Skip to content
Snippets Groups Projects
Commit 0d1146f2 authored by Piotr Gawron's avatar Piotr Gawron
Browse files

Merge branch 'master' into 66-appointment-type-list

parents b3c6ebaa bec1de36
No related branches found
No related tags found
1 merge request!16Resolve "Appointment type list"
Pipeline #
......@@ -16,6 +16,6 @@ local_settings.py
#tmp files
appointment-import/testFiles/~*
appointment-import/tmp.sql
.idea
*.iml
out
.idea
......@@ -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, AppointmentType
......@@ -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):
......@@ -229,3 +231,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")
......@@ -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'
)
......@@ -548,12 +548,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)',
......@@ -591,7 +591,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
......
# 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
......@@ -215,106 +215,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>
......
<!-- 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
<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
{% 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 %}
<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
# See: http://stackoverflow.com/a/18962481
from django import template
from django.forms import CheckboxSelectMultiple
from django.utils.safestring import mark_safe
register = template.Library()
......@@ -29,3 +30,10 @@ def disable(value):
@register.filter(name='is_checkbox')
def is_checkbox(value):
return isinstance(value.field.widget, CheckboxSelectMultiple)
@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)
......@@ -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)
# 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'])
# 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)
......@@ -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'),
......
......@@ -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
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment