diff --git a/.gitignore b/.gitignore
index 4996b376b436862040d84b8cd8b7aa059d67db9f..e0934d5bb8aa607ffdb9744cea0969892cf32c5c 100644
--- a/.gitignore
+++ b/.gitignore
@@ -16,6 +16,6 @@ local_settings.py
 #tmp files
 appointment-import/testFiles/~*
 appointment-import/tmp.sql
-.idea
 *.iml
 out
+.idea
diff --git a/smash/web/forms.py b/smash/web/forms.py
index 514d9ac9f2a080c3908e756236e404a9b591e5eb..a461805b1a2f5c4ebe7e7e7676943971a784986c 100644
--- a/smash/web/forms.py
+++ b/smash/web/forms.py
@@ -2,6 +2,7 @@ from datetime import datetime
 
 from django import forms
 from django.forms import ModelForm, Form
+from django.utils.dates import MONTHS
 
 from models import Subject, Worker, Appointment, Visit
 
@@ -21,6 +22,7 @@ DATETIMEPICKER_DATE_ATTRS = {
     'class': 'datetimepicker',
     'data-date-format': 'Y-MM-DD HH:mm',
 }
+START_YEAR_STATISTICS = 2015
 
 
 def validate_subject_nd_number(self):
@@ -221,3 +223,23 @@ class KitRequestForm(Form):
                                widget=forms.DateInput(DATEPICKER_DATE_ATTRS, "%Y-%m-%d"),
                                required=False
                                )
+
+
+class StatisticsForm(Form):
+    def __init__(self, *args, **kwargs):
+        super(StatisticsForm, self).__init__(*args)
+        visit_choices = kwargs['visit_choices']
+        month = kwargs['month']
+        year = kwargs['year']
+        now = datetime.now()
+        year_now = now.year
+        number_of_years_for_statistics = year_now - START_YEAR_STATISTICS + 2
+
+        year_choices = [(START_YEAR_STATISTICS + i, START_YEAR_STATISTICS + i) for i in
+                        range(0, number_of_years_for_statistics + 1)]
+        self.fields['month'] = forms.ChoiceField(choices=MONTHS.items(), initial=month)
+        self.fields['year'] = forms.ChoiceField(choices=year_choices, initial=year)
+        choices = [(-1, "all")]
+        choices.extend(Subject.SUBJECT_TYPE_CHOICES.items())
+        self.fields['subject_type'] = forms.ChoiceField(choices=choices, initial="-1")
+        self.fields['visit'] = forms.ChoiceField(choices=visit_choices, initial="-1")
diff --git a/smash/web/models.py b/smash/web/models.py
index 03f784fc8399a99ea3c8f92339eff5cbf43bdafe..56811396e49cb1b461671646cd84a363813e1913 100644
--- a/smash/web/models.py
+++ b/smash/web/models.py
@@ -50,10 +50,10 @@ class Subject(models.Model):
     )
 
     SUBJECT_TYPE_CHOICES_CONTROL = 'C'
-    SUBJECT_TYPE_CHOICES = (
-        (SUBJECT_TYPE_CHOICES_CONTROL, 'CONTROL'),
-        ('P', 'PATIENT'),
-    )
+    SUBJECT_TYPE_CHOICES = {
+        SUBJECT_TYPE_CHOICES_CONTROL: 'CONTROL',
+        'P': 'PATIENT',
+    }
 
     def finish_all_visits(self):
         visits = Visit.objects.filter(subject=self, is_finished=False)
@@ -95,7 +95,7 @@ class Subject(models.Model):
         verbose_name='Contact on',
     )
     type = models.CharField(max_length=1,
-                            choices=SUBJECT_TYPE_CHOICES,
+                            choices=SUBJECT_TYPE_CHOICES.items(),
                             verbose_name='Type'
                             )
 
@@ -545,12 +545,12 @@ class Appointment(models.Model):
     APPOINTMENT_STATUS_FINISHED = 'FINISHED'
     APPOINTMENT_STATUS_CANCELLED = 'CANCELLED'
     APPOINTMENT_STATUS_NO_SHOW = 'NO_SHOW'
-    APPOINTMENT_STATUS_CHOICES = (
-        (APPOINTMENT_STATUS_SCHEDULED, 'Scheduled'),
-        (APPOINTMENT_STATUS_FINISHED, 'Finished'),
-        (APPOINTMENT_STATUS_CANCELLED, 'Cancelled'),
-        (APPOINTMENT_STATUS_NO_SHOW, 'No Show'),
-    )
+    APPOINTMENT_STATUS_CHOICES = {
+        APPOINTMENT_STATUS_SCHEDULED: 'Scheduled',
+        APPOINTMENT_STATUS_FINISHED: 'Finished',
+        APPOINTMENT_STATUS_CANCELLED: 'Cancelled',
+        APPOINTMENT_STATUS_NO_SHOW: 'No Show',
+    }
 
     flying_team = models.ForeignKey(FlyingTeam,
                                     verbose_name='Flying team (if applicable)',
@@ -588,7 +588,7 @@ class Appointment(models.Model):
         verbose_name='Appointment length (in minutes)'
     )  # Potentially redundant; but can be used to manually adjust appointment's length
 
-    status = models.CharField(max_length=20, choices=APPOINTMENT_STATUS_CHOICES,
+    status = models.CharField(max_length=20, choices=APPOINTMENT_STATUS_CHOICES.items(),
                               verbose_name='Status',
                               editable=False,
                               default=APPOINTMENT_STATUS_SCHEDULED
diff --git a/smash/web/statistics.py b/smash/web/statistics.py
new file mode 100644
index 0000000000000000000000000000000000000000..0de80c539f380d7c016101fd7520a09c1ac4b8d8
--- /dev/null
+++ b/smash/web/statistics.py
@@ -0,0 +1,212 @@
+# coding=utf-8
+import copy
+import datetime
+from collections import defaultdict
+from operator import attrgetter
+
+from django.db import connection
+from django.db.models import Q, Count
+
+from .models import AppointmentType, Appointment, Visit
+
+__author__ = 'Valentin Grouès'
+
+QUERY_VISITS_RANKS = """
+SELECT DISTINCT(rank() OVER (PARTITION BY subject_id ORDER BY datetime_begin)) AS rank FROM web_visit
+"""
+
+QUERY_APPOINTMENTS_COUNT = """
+SELECT count(*) FROM web_appointment LEFT JOIN (SELECT id, rank()
+OVER (PARTITION BY subject_id ORDER BY datetime_begin) AS rnk FROM web_visit)
+a ON a.id = web_appointment.visit_id WHERE a.rnk = %s
+AND EXTRACT(MONTH FROM web_appointment.datetime_when) = %s
+AND EXTRACT(YEAR FROM web_appointment.datetime_when) = %s
+"""
+
+QUERY_VISITS_ENDED_COUNT = """
+SELECT count(*) FROM  (SELECT id, datetime_begin, datetime_end, rank()
+OVER (PARTITION BY subject_id ORDER BY datetime_begin) AS rnk FROM web_visit) a
+WHERE a.rnk = %s AND
+EXTRACT(MONTH FROM a.datetime_end) = %s AND
+EXTRACT(YEAR FROM a.datetime_end) = %s
+"""
+
+QUERY_VISITS_STARTED_COUNT = """
+SELECT count(*) FROM  (SELECT id, datetime_begin, datetime_end, rank()
+OVER (PARTITION BY subject_id ORDER BY datetime_begin) AS rnk FROM web_visit) a
+WHERE a.rnk = %s AND
+EXTRACT(MONTH FROM a.datetime_begin) = %s AND
+EXTRACT(YEAR FROM a.datetime_begin) = %s
+"""
+
+QUERY_APPOINTMENTS = """
+SELECT types.appointmenttype_id, web_appointment.status, count(*) FROM web_appointment
+LEFT JOIN (SELECT id, rank() OVER (PARTITION BY subject_id ORDER BY datetime_begin) AS rnk
+FROM web_visit) a ON a.id = web_appointment.visit_id LEFT JOIN web_appointment_appointment_types types
+ON types.appointment_id = web_appointment.id WHERE a.rnk = %s
+AND EXTRACT(MONTH FROM web_appointment.datetime_when) = %s
+AND EXTRACT(YEAR FROM web_appointment.datetime_when) = %s
+GROUP BY types.appointmenttype_id,  web_appointment.status
+"""
+
+
+class StatisticsManager(object):
+    def __init__(self):
+        self.appointment_types = {appointment_type.id: appointment_type for appointment_type in
+                                  AppointmentType.objects.all()}
+        self.visits_ranks = self._get_visits_ranks()
+        self.statuses_list = Appointment.objects.filter().values_list('status', flat=True).distinct().order_by(
+            'status').all()
+        self.statuses_labels = [Appointment.APPOINTMENT_STATUS_CHOICES.get(status, status.title()) for status in
+                                self.statuses_list]
+
+    def get_statistics_for_month(self, month, year, subject_type=None, visit=None):
+        """
+        Build dict with statistics for a given month of a given year.
+        Statistics include:
+         - number of appointments,
+         - number of visits ended,
+         - number of visits started
+         - number of appointements per type and per status
+        :param month: the month number [1;12]
+        :type month: int
+        :param year: the year (4 digits)
+        :type year: int
+        :param subject_type: the type of subject (patient or control or None for all)
+        :type subject_type: basestring
+        :param visit: the visit number or None for all
+        :type visit: basestring
+        :return: a dictionary containing the statistics
+        :rtype: dict
+        """
+        results = {}
+        general_results = {}
+
+        filters_month_year_appointments, filters_month_year_visits_ended, filters_month_year_visits_started = self._build_filters(
+            month, subject_type, year)
+
+        number_of_appointments = self._get_number_of_appointments(filters_month_year_appointments, visit, month, year)
+        number_of_visits_started = self._get_number_visits_started(filters_month_year_visits_started, visit, month,
+                                                                   year)
+        number_of_visits_ended = self._get_number_visits_ended(filters_month_year_visits_ended, visit, month, year)
+
+        general_results["appointments"] = number_of_appointments
+        general_results["visits_started"] = number_of_visits_started
+        general_results["visits_ended"] = number_of_visits_ended
+
+        results["general"] = general_results
+
+        results_appointments = self.get_appointments_per_type_and_status(filters_month_year_appointments, month,
+                                                                         self.statuses_list, visit, year)
+        results["appointments"] = results_appointments
+        results["statuses_list"] = self.statuses_labels
+        appointment_types_values = map(attrgetter('code'), self.appointment_types.values())
+        sorted_appointment_types_values = sorted(appointment_types_values)
+        results["appointments_types_list"] = sorted_appointment_types_values
+        return results
+
+    def get_appointments_per_type_and_status(self, filters_month_year_appointments, month, statuses_list, visit, year):
+        if not visit:
+            results_appointments = {}
+            for appointment_type in self.appointment_types.values():
+                appointment_type_filters = copy.deepcopy(filters_month_year_appointments)
+                appointment_type_filters.add(Q(appointment_types=appointment_type), Q.AND)
+                results_appointment_set = Appointment.objects.filter(appointment_type_filters).values(
+                    'status').order_by(
+                    'status').annotate(
+                    Count('status'))
+                results_appointment = [Appointment.objects.filter(appointment_type_filters).count()]
+                results_appointment_per_status = {result['status']: result['status__count'] for result in
+                                                  results_appointment_set}
+
+                results_appointment.extend([results_appointment_per_status.get(status, 0) for status in statuses_list])
+                results_appointments[appointment_type.code] = results_appointment
+        else:
+            results_appointment_set = defaultdict(dict)
+            with connection.cursor() as cursor:
+                cursor.execute(QUERY_APPOINTMENTS, [visit, month, year])
+                rows = cursor.fetchall()
+                for row in rows:
+                    appointment_type_id, status, count = row
+                    results_appointment_set[appointment_type_id][status] = int(count)
+            results_appointments = {}
+            for appointment_type in self.appointment_types.values():
+                if appointment_type.id not in results_appointment_set:
+                    results_appointments[appointment_type.code] = [0 * i for i in range(0, len(statuses_list) + 1)]
+                    continue
+                results_appointment_set_for_type = results_appointment_set[appointment_type.id]
+                total = [sum(results_appointment_set_for_type.values())]
+                total.extend([results_appointment_set_for_type.get(status, 0) for status in statuses_list])
+                results_appointments[appointment_type.code] = total
+        return results_appointments
+
+    @staticmethod
+    def _get_count_from_filters_or_sql(model, filters, query, visit, month, year):
+        if visit:
+            with connection.cursor() as cursor:
+                cursor.execute(
+                    query,
+                    [visit, month, year])
+                row = cursor.fetchone()
+                count = int(row[0])
+        else:
+            count = model.objects.filter(filters).count()
+        return count
+
+    def _get_number_visits_started(self, filters_month_year_visits_started, visit, month, year):
+        return self._get_count_from_filters_or_sql(Visit, filters_month_year_visits_started, QUERY_VISITS_STARTED_COUNT,
+                                                   visit, month, year)
+
+    def _get_number_visits_ended(self, filters_month_year_visits_ended, visit, month, year):
+        return self._get_count_from_filters_or_sql(Visit, filters_month_year_visits_ended, QUERY_VISITS_ENDED_COUNT,
+                                                   visit, month, year)
+
+    def _get_number_of_appointments(self, filters, visit, month, year):
+        return self._get_count_from_filters_or_sql(Appointment, filters, QUERY_APPOINTMENTS_COUNT, visit, month, year)
+
+    @staticmethod
+    def _build_filters(month, subject_type, year):
+        filters_month_year_appointments = Q()
+        filters_month_year_appointments.add(Q(datetime_when__month=month), Q.AND)
+        filters_month_year_appointments.add(Q(datetime_when__year=year), Q.AND)
+
+        filters_month_year_visits_started = Q()
+        filters_month_year_visits_started.add(Q(datetime_begin__month=month), Q.AND)
+        filters_month_year_visits_started.add(Q(datetime_begin__year=year), Q.AND)
+
+        filters_month_year_visits_ended = Q()
+        filters_month_year_visits_ended.add(Q(datetime_end__month=month), Q.AND)
+        filters_month_year_visits_ended.add(Q(datetime_end__year=year), Q.AND)
+
+        if subject_type is not None:
+            subject_type_filter = Q(subject__type=subject_type)
+            filters_month_year_visits_started.add(subject_type_filter, Q.AND)
+            filters_month_year_visits_ended.add(subject_type_filter, Q.AND)
+            subject_type_filter_appointements = Q(visit__subject__type=subject_type)
+            filters_month_year_appointments.add(subject_type_filter_appointements, Q.AND)
+        return filters_month_year_appointments, filters_month_year_visits_ended, filters_month_year_visits_started
+
+    @staticmethod
+    def _get_visits_ranks():
+        with connection.cursor() as cursor:
+            cursor.execute(
+                QUERY_VISITS_RANKS,
+                [])
+            rows = cursor.fetchall()
+
+        return [r[0] for r in rows]
+
+
+def get_previous_year_and_month():
+    now = datetime.datetime.now()
+    return get_previous_year_and_month_for_date(now)
+
+
+def get_previous_year_and_month_for_date(now):
+    previous_month = now.month - 1 or 12
+    year_now = now.year
+    if previous_month == 12:
+        year_previous_month = year_now - 1
+    else:
+        year_previous_month = year_now
+    return year_previous_month, previous_month
diff --git a/smash/web/templates/_base.html b/smash/web/templates/_base.html
index 685769f28a16609d7ec2f345e91e5ecc64703178..feceaeed1ec4f054a4670257224e0743880e5dfa 100644
--- a/smash/web/templates/_base.html
+++ b/smash/web/templates/_base.html
@@ -218,106 +218,7 @@ desired effect
 
         <!-- sidebar: style can be found in sidebar.less -->
         <section class="sidebar">
-
-            {% comment "Uncomment if needed" %}
-      <!-- Sidebar user panel (optional) -->
-      <div class="user-panel">
-        <div class="pull-left image">
-          <!--
-			  <img src="dist/img/user2-160x160.jpg" class="img-circle" alt="User Image">
-		  -->
-		  <i class="fa fa-user-circle-o fa-3x white" style="color: rgba(255,255,255,0.8)"></i>
-        </div>
-        <div class="pull-left info">
-          <p>Prenom Nom</p>
-          <!-- Status -->
-          <a href="#"><i class="fa fa-circle text-success"></i> Online</a>
-        </div>
-      </div>
-      {% endcomment %}
-
-            {% comment "Uncomment if necessary' %}
-      <!-- search form (Optional) -->
-      <form action="#" method="get" class="sidebar-form">
-        <div class="input-group">
-          <input type="text" name="q" class="form-control" placeholder="Search...">
-              <span class="input-group-btn">
-                <button type="submit" name="search" id="search-btn" class="btn btn-flat"><i class="fa fa-search"></i>
-                </button>
-              </span>
-        </div>
-      </form>
-      <!-- /.search form -->
-      {% endcomment %}
-
-            <!-- Sidebar Menu -->
-            <ul class="sidebar-menu">
-                <li class="header">Pages</li>
-
-                <li data-desc="subjects">
-                    <a href="{% url 'web.views.subjects' %}">
-                        <i class="fa fa-users"></i>
-                        <span>Subjects</span>
-                    </a>
-                </li>
-
-                <li data-desc="visits">
-                    <a href="{% url 'web.views.visits' %}">
-                        <i class="fa fa-id-card-o"></i>
-                        <span>Visits</span>
-                    </a>
-                </li>
-
-                <li data-desc="appointments">
-                    <a href="{% url 'web.views.appointments' %}">
-                        <i class="fa fa-calendar"></i>
-                        <span>Appointments</span>
-                    </a>
-                </li>
-
-                <li data-desc="workers">
-                    <a href="{% url 'web.views.doctors' %}">
-                        <i class="fa fa-user-md"></i>
-                        <span>Workers</span>
-                    </a>
-                </li>
-
-                <li data-desc="equipment_and_rooms">
-                    <a href="{% url 'web.views.equipment_and_rooms' %}">
-                        <i class="fa fa-building-o"></i>
-                        <span>Equipment&amp;rooms</span>
-                    </a>
-                </li>
-
-                <li data-desc="mail_templates">
-                    <a href="{% url 'web.views.mail_templates' %}">
-                        <i class="fa fa-envelope-o"></i>
-                        <span>Mail templates</span>
-                    </a>
-                </li>
-
-                <li data-desc="export">
-                    <a href="{% url 'web.views.export' %}">
-                        <i class="fa fa-file-excel-o"></i>
-                        <span>Export</span>
-                    </a>
-                </li>
-
-                {% comment "Multi-level" %}
-        <li class="treeview">
-          <a href="#"><i class="fa fa-link"></i> <span>Multilevel</span>
-            <span class="pull-right-container">
-              <i class="fa fa-angle-left pull-right"></i>
-            </span>
-          </a>
-          <ul class="treeview-menu">
-            <li><a href="#">Link in level 2</a></li>
-            <li><a href="#">Link in level 2</a></li>
-          </ul>
-        </li>
-        {% endcomment %}
-            </ul>
-            <!-- /.sidebar-menu -->
+            {% include "sidebar.html" %}
         </section>
         <!-- /.sidebar -->
     </aside>
diff --git a/smash/web/templates/sidebar.html b/smash/web/templates/sidebar.html
new file mode 100644
index 0000000000000000000000000000000000000000..7dbbeb016be68b380ca0b7516eea1f9b56dea4c3
--- /dev/null
+++ b/smash/web/templates/sidebar.html
@@ -0,0 +1,61 @@
+<!-- Sidebar Menu -->
+<ul class="sidebar-menu">
+    <li class="header">Pages</li>
+
+    <li data-desc="subjects">
+        <a href="{% url 'web.views.subjects' %}">
+            <i class="fa fa-users"></i>
+            <span>Subjects</span>
+        </a>
+    </li>
+
+    <li data-desc="visits">
+        <a href="{% url 'web.views.visits' %}">
+            <i class="fa fa-id-card-o"></i>
+            <span>Visits</span>
+        </a>
+    </li>
+
+    <li data-desc="appointments">
+        <a href="{% url 'web.views.appointments' %}">
+            <i class="fa fa-calendar"></i>
+            <span>Appointments</span>
+        </a>
+    </li>
+
+    <li data-desc="workers">
+        <a href="{% url 'web.views.doctors' %}">
+            <i class="fa fa-user-md"></i>
+            <span>Workers</span>
+        </a>
+    </li>
+
+    <li data-desc="equipment_and_rooms">
+        <a href="{% url 'web.views.equipment_and_rooms' %}">
+            <i class="fa fa-building-o"></i>
+            <span>Equipment&amp;rooms</span>
+        </a>
+    </li>
+
+    <li data-desc="statistics">
+        <a href="{% url 'web.views.statistics' %}">
+            <i class="fa fa-bar-chart" aria-hidden="true"></i>
+            <span>Statistics</span>
+        </a>
+    </li>
+
+    <li data-desc="mail_templates">
+        <a href="{% url 'web.views.mail_templates' %}">
+            <i class="fa fa-envelope-o"></i>
+            <span>Mail templates</span>
+        </a>
+    </li>
+
+    <li data-desc="export">
+        <a href="{% url 'web.views.export' %}">
+            <i class="fa fa-file-excel-o"></i>
+            <span>Export</span>
+        </a>
+    </li>
+
+</ul>
\ No newline at end of file
diff --git a/smash/web/templates/statistics/breadcrumb.html b/smash/web/templates/statistics/breadcrumb.html
new file mode 100644
index 0000000000000000000000000000000000000000..2470896f37cfd00aec1456cbff23b37c441a7e8d
--- /dev/null
+++ b/smash/web/templates/statistics/breadcrumb.html
@@ -0,0 +1,2 @@
+<li><a href="{% url 'web.views.appointments' %}"><i class="fa fa-dashboard"></i> Dashboard</a></li>
+<li class="active"><a href="{% url 'web.views.statistics' %}">Statistics</a></li>
\ No newline at end of file
diff --git a/smash/web/templates/statistics/index.html b/smash/web/templates/statistics/index.html
new file mode 100644
index 0000000000000000000000000000000000000000..15c67f4ff7cead7d673008993d1f38bd9761443f
--- /dev/null
+++ b/smash/web/templates/statistics/index.html
@@ -0,0 +1,110 @@
+{% extends "_base.html" %}
+{% load static %}
+{% load filters %}
+
+{% block ui_active_tab %}'statistics'{% endblock ui_active_tab %}
+{% block page_header %}Monthly statistics{% endblock page_header %}
+{% block page_description %}{% endblock page_description %}
+
+{% block breadcrumb %}
+    {% include "mail_templates/breadcrumb.html" %}
+{% endblock breadcrumb %}
+
+{% block styles %}
+    {{ block.super }}
+    <style type="text/css">
+        #appointments-table {
+            table-layout: fixed !important;
+        }
+
+        #appointments-table tr td {
+            width: 80px;
+            text-align: center;
+        }
+
+        #appointments-table tr td:first-child {
+            text-align: left;
+        }
+
+        #appointments-table thead tr td {
+            background-color: #eee;
+        }
+
+        #appointments-table tbody tr td:first-child {
+            background-color: #eee;
+        }
+
+        #form-month {
+            margin-bottom: 50px;
+        }
+    </style>
+{% endblock %}
+
+
+{% block maincontent %}
+
+    <div class="row">
+        <div class="col-md-8">
+            <form id="form-month" method="get" class="form-inline">
+                {#            {% csrf_token %}#}
+                {% for field in form %}
+                    <div class="form-group">
+                        <label class="control-label">{{ field.label }}</label>
+                        {{ field | add_class:'form-control' }}
+                    </div>
+                {% endfor %}
+            </form>
+        </div>
+    </div>
+    <div class="row">
+        {% include 'statistics/small_box.html' with value=monthly_statistics.general.appointments label="Appointments" color="aqua" icon="calendar" %}
+        {% include 'statistics/small_box.html' with value=monthly_statistics.general.visits_started label="Visits started" color="green" icon="hourglass-start" %}
+        {% include 'statistics/small_box.html' with value=monthly_statistics.general.visits_ended label="Visits ended" color="red" icon="hourglass-end" %}
+    </div>
+    <div class="row">
+        <div class="col-lg-9 col-md-12">
+            <div class="box">
+                <div class="box-header with-border">
+                    <h3 class="box-title"><i class="fa fa-calendar"></i> Appointments</h3>
+                </div>
+                <div class="box-body">
+                    <table id="appointments-table" class="table table-bordered table-striped table-hover">
+                        <thead>
+                        <tr>
+                            <td>Type \ Status</td>
+                            <td>Total</td>
+                            {% for status in monthly_statistics.statuses_list %}
+                                <td>{{ status }}</td>
+                            {% endfor %}
+                        </tr>
+                        </thead>
+                        <tbody>
+                        {% for appointment_type in monthly_statistics.appointments_types_list %}
+                            <tr>
+                                <td>{{ appointment_type }}</td>
+                                {{ monthly_statistics.appointments | render_appointments:appointment_type }}
+                            </tr>
+                        {% endfor %}
+                        </tbody>
+                    </table>
+                </div>
+                <div class="box-footer clearfix">
+                </div>
+            </div>
+        </div>
+    </div>
+
+{% endblock maincontent %}
+
+{% block scripts %}
+    {{ block.super }}
+
+
+    <script>
+        $(document).ready(function () {
+            $('#form-month select').change(function () {
+                $('#form-month').submit();
+            });
+        });
+    </script>
+{% endblock scripts %}
diff --git a/smash/web/templates/statistics/small_box.html b/smash/web/templates/statistics/small_box.html
new file mode 100644
index 0000000000000000000000000000000000000000..e5b6e1f91a3dea620452471e990b1eed3caf882c
--- /dev/null
+++ b/smash/web/templates/statistics/small_box.html
@@ -0,0 +1,11 @@
+<div class="col-md-4 col-lg-3">
+    <!-- small box -->
+    <div class="info-box ">
+        <span class="info-box-icon bg-{{ color }}"><i class="fa fa-{{ icon }}"></i></span>
+
+        <div class="info-box-content">
+            <span class="info-box-text">{{ label }}</span>
+            <span class="info-box-number">{{ value }}</span>
+        </div>
+    </div>
+</div>
\ No newline at end of file
diff --git a/smash/web/templatetags/filters.py b/smash/web/templatetags/filters.py
index 2df497f0ddaaefa14b4fcc94294fdee4913176b1..45f03ee0f9e2a18ca2eb368001e24a63d4c7b75a 100644
--- a/smash/web/templatetags/filters.py
+++ b/smash/web/templatetags/filters.py
@@ -1,5 +1,6 @@
 # See: http://stackoverflow.com/a/18962481
 from django import template
+from django.utils.safestring import mark_safe
 
 register = template.Library()
 
@@ -21,3 +22,11 @@ def add_class(value, arg):
 def disable(value):
     value.field.widget.attrs['disabled'] = 'disabled'
     return value
+
+
+@register.filter(name="render_appointments")
+def render_appointments(statistics, appointment_type):
+    html = ""
+    for status_count in statistics.get(appointment_type, []):
+        html += '<td>{}</td>'.format(status_count)
+    return mark_safe(html)
diff --git a/smash/web/tests/functions.py b/smash/web/tests/functions.py
index e5b5f0e43d30486f76c00fad1bbf31832bac1d88..728bf683282c458aa9d22b482ae3334244880fa4 100644
--- a/smash/web/tests/functions.py
+++ b/smash/web/tests/functions.py
@@ -31,7 +31,8 @@ def create_subject():
         first_name="Piotr",
         last_name="Gawron",
         default_location=get_test_location(),
-        sex=Subject.SEX_CHOICES_MALE)
+        sex=Subject.SEX_CHOICES_MALE,
+        type=Subject.SUBJECT_TYPE_CHOICES_CONTROL)
 
 
 def create_user():
@@ -62,10 +63,11 @@ def create_visit(subject=None):
                                 is_finished=False)
 
 
-def create_appointment(visit=None):
+def create_appointment(visit=None, when=None):
     if visit is None:
         visit = create_visit()
     return Appointment.objects.create(
         visit=visit,
         length=30,
-        location=get_test_location())
+        location=get_test_location(),
+        datetime_when=when)
diff --git a/smash/web/tests/test_statistics.py b/smash/web/tests/test_statistics.py
new file mode 100644
index 0000000000000000000000000000000000000000..3c6286cebb821a52293a041401f062737dfdade1
--- /dev/null
+++ b/smash/web/tests/test_statistics.py
@@ -0,0 +1,83 @@
+# coding=utf-8
+import datetime
+
+from django.test import TestCase
+
+from web.models import Visit
+from web.statistics import get_previous_year_and_month_for_date, StatisticsManager
+from web.tests.functions import create_appointment, create_appointment_type
+
+__author__ = 'Valentin Grouès'
+
+
+class TestStatistics(TestCase):
+    def setUp(self):
+        self.now = datetime.datetime.now()
+        self.appointment_type = create_appointment_type()
+        appointment = create_appointment(when=self.now)
+        appointment.appointment_types = [self.appointment_type]
+        appointment.save()
+        self.subject = appointment.visit.subject
+        self.statistics_manager = StatisticsManager()
+
+    def test_get_previous_year_and_month_for_date(self):
+        test_date = datetime.datetime(year=2014, month=10, day=13)
+        previous_year, previous_month = get_previous_year_and_month_for_date(test_date)
+        self.assertEqual(2014, previous_year)
+        self.assertEqual(9, previous_month)
+        test_date = datetime.datetime(year=2014, month=1, day=13)
+        previous_year, previous_month = get_previous_year_and_month_for_date(test_date)
+        self.assertEqual(2013, previous_year)
+        self.assertEqual(12, previous_month)
+
+    def test_get_statistics_for_month_one_appointment(self):
+        statistics = self.statistics_manager.get_statistics_for_month(self.now.month - 1, self.now.year)
+        self.check_statistics(statistics, 1, 0, 0, {"C": [0, 0]}, ['Scheduled'])
+
+        statistics = self.statistics_manager.get_statistics_for_month(self.now.month, self.now.year)
+        self.check_statistics(statistics, 0, 0, 1, {"C": [1, 1]}, ['Scheduled'])
+
+        statistics = self.statistics_manager.get_statistics_for_month(self.now.month + 1, self.now.year)
+        self.check_statistics(statistics, 0, 1, 0, {"C": [0, 0]}, ['Scheduled'])
+
+    def test_get_statistics_for_month_one_appointment_visit(self):
+        statistics = self.statistics_manager.get_statistics_for_month(self.now.month, self.now.year, visit="1")
+        self.check_statistics(statistics, 0, 0, 1, {"C": [1, 1]}, ['Scheduled'])
+
+        statistics = self.statistics_manager.get_statistics_for_month(self.now.month, self.now.year, visit="2")
+        self.check_statistics(statistics, 0, 0, 0, {"C": [0, 0]}, ['Scheduled'])
+
+    def test_get_statistics_for_month_one_appointment_subject_type(self):
+        statistics = self.statistics_manager.get_statistics_for_month(self.now.month, self.now.year, subject_type="C")
+        self.check_statistics(statistics, 0, 0, 1, {"C": [1, 1]}, ['Scheduled'])
+
+        statistics = self.statistics_manager.get_statistics_for_month(self.now.month, self.now.year, subject_type="P")
+        self.check_statistics(statistics, 0, 0, 0, {"C": [0, 0]}, ['Scheduled'])
+
+    def test_get_statistics_for_month_multiple_visits(self):
+        second_visit = Visit.objects.create(datetime_begin=self.now + datetime.timedelta(days=-32),
+                                            datetime_end=self.now + datetime.timedelta(days=31),
+                                            subject=self.subject,
+                                            is_finished=False)
+        second_appointment = create_appointment(second_visit, when=self.now)
+        second_appointment.appointment_types = [self.appointment_type]
+        second_appointment.status = "Cancelled"
+        second_appointment.save()
+        self.statistics_manager = StatisticsManager()
+
+        statistics = self.statistics_manager.get_statistics_for_month(self.now.month, self.now.year)
+        self.check_statistics(statistics, 0, 0, 2, {"C": [2, 1, 1]}, ['Cancelled', 'Scheduled'])
+
+        statistics = self.statistics_manager.get_statistics_for_month(self.now.month, self.now.year, visit="1")
+        self.check_statistics(statistics, 0, 0, 1, {"C": [1, 1, 0]}, ['Cancelled', 'Scheduled'])
+
+        statistics = self.statistics_manager.get_statistics_for_month(self.now.month, self.now.year, visit="2")
+        self.check_statistics(statistics, 0, 0, 1, {"C": [1, 0, 1]}, ['Cancelled', 'Scheduled'])
+
+    def check_statistics(self, statistics, expected_visits_started, expected_visits_ended, expected_appointments_count,
+                         expected_appointments_details, expected_statuses):
+        self.assertEqual(expected_visits_started, statistics['general']['visits_started'])
+        self.assertEqual(expected_visits_ended, statistics['general']['visits_ended'])
+        self.assertEqual(expected_statuses, statistics['statuses_list'])
+        self.assertEqual(expected_appointments_count, statistics['general']['appointments'])
+        self.assertEqual(expected_appointments_details, statistics['appointments'])
diff --git a/smash/web/tests/test_view_statistics.py b/smash/web/tests/test_view_statistics.py
new file mode 100644
index 0000000000000000000000000000000000000000..b070ca19c432aa222731847af288cb49ffc7c22a
--- /dev/null
+++ b/smash/web/tests/test_view_statistics.py
@@ -0,0 +1,30 @@
+# coding=utf-8
+from datetime import datetime
+
+from django.contrib.auth.models import User
+from django.test import Client
+from django.test import TestCase
+from django.urls import reverse
+
+__author__ = 'Valentin Grouès'
+
+
+class TestStatisticsView(TestCase):
+    def setUp(self):
+        self.client = Client()
+        username = 'piotr'
+        password = 'top_secret'
+        self.user = User.objects.create_user(
+            username=username, email='jacob@bla', password=password)
+        self.client.login(username=username, password=password)
+
+    def test_statistics_request(self):
+        url = reverse('web.views.statistics')
+        response = self.client.get(url)
+        self.assertEqual(response.status_code, 200)
+        current_month = datetime.now().month - 1 or 12
+        content = response.content
+        self.assertIn('<option value="{}" selected="selected">'.format(current_month), content)
+        response = self.client.get(url, {"month": 10, "year": 2017, "subject_type": -1, "visit": -1})
+        content = response.content
+        self.assertIn('<option value="10" selected="selected">October', content)
diff --git a/smash/web/urls.py b/smash/web/urls.py
index 4712179e99e4667e9911d93afa9e1d2df3c7ce49..6cbc1fcefde1c3ad82ea887069b1d56693b3dee1 100644
--- a/smash/web/urls.py
+++ b/smash/web/urls.py
@@ -71,6 +71,7 @@ urlpatterns = [
         views.kit_requests_send_mail, name='web.views.kit_requests_send_mail'),
 
     url(r'^mail_templates$', views.mail_templates, name='web.views.mail_templates'),
+    url(r'^statistics$', views.statistics, name='web.views.statistics'),
 
     url(r'^export$', views.export, name='web.views.export'),
     url(r'^export/(?P<type>[A-z]+)$', views.export_to_csv2, name='web.views.export_to_csv2'),
diff --git a/smash/web/views.py b/smash/web/views.py
index 30ea696fdd5247fc3213c04cd9b3eeef6c38c021..3197798bbc00555ea3cb291dafc256aa17f9707b 100644
--- a/smash/web/views.py
+++ b/smash/web/views.py
@@ -16,8 +16,11 @@ from django.utils.dateparse import parse_datetime
 
 from auth import do_logout, do_login
 from forms import SubjectDetailForm, WorkerEditForm, WorkerDetailForm, AppointmentDetailForm, AppointmentAddForm, \
-    AppointmentEditForm, KitRequestForm, SubjectEditForm, SubjectAddForm, VisitAddForm, WorkerAddForm, VisitDetailForm
+    AppointmentEditForm, KitRequestForm, SubjectEditForm, SubjectAddForm, VisitAddForm, WorkerAddForm, VisitDetailForm, \
+    StatisticsForm
 from models import Worker, Location, Visit, Subject, Appointment, Avaibility, Item, AppointmentType
+from statistics import StatisticsManager
+from statistics import get_previous_year_and_month
 
 handler404 = 'web.views.e404_page_not_found'
 handler500 = 'web.views.e500_error'
@@ -863,3 +866,26 @@ def kit_requests(request):
 def kit_requests_send_mail(request, start_date, end_date=None):
     return wrap_response(request, 'equipment_and_rooms/kit_requests_send_mail.html',
                          get_kit_requests_data(request, start_date, end_date))
+
+def statistics(request):
+    statistics_manager = StatisticsManager()
+    visit_choices = [("-1", "all")]
+    visit_choices.extend([(rank, rank) for rank in statistics_manager.visits_ranks])
+    year_previous_month, previous_month = get_previous_year_and_month()
+
+    form = StatisticsForm(request.GET, visit_choices=visit_choices, month=previous_month, year=year_previous_month)
+    if not form.is_valid():
+        form.is_bound = False
+    month = form.data.get('month', previous_month)
+    year = form.data.get('year', year_previous_month)
+    subject_type = form.data.get('subject_type', "-1")
+    visit = form.data.get('visit', "-1")
+    if subject_type == "-1":
+        subject_type = None
+    if visit == "-1":
+        visit = None
+    monthly_statistics = statistics_manager.get_statistics_for_month(month, year, subject_type, visit)
+    return wrap_response(request, 'statistics/index.html', {
+        'form': form,
+        'monthly_statistics': monthly_statistics
+    })
\ No newline at end of file