diff --git a/.gitignore b/.gitignore index 11244fb8da25c2eaac6b2ab2ed6627247d2bbcc8..527ddfa78241279c95ec4eadee3d00677402779d 100644 --- a/.gitignore +++ b/.gitignore @@ -16,8 +16,8 @@ appointment-import/tmp.sql *.iml out .idea +<<<<<<< HEAD -#coverage tool .coverage -smash/htmlcov/* +smash/htmlcov/ diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index c4967b07aa79b548e3c7cb0de5f3cec260178378..fd2748cea54a607609c5c9468f6aa2ee75c9e208 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -17,6 +17,6 @@ test: script: - cp "local_settings_ci.py" "smash/smash/local_settings.py" - cd smash - - python manage.py makemigrations && python manage.py migrate + - python manage.py makemigrations web && python manage.py migrate - coverage run --source web manage.py test - coverage report -m diff --git a/smash/web/admin.py b/smash/web/admin.py index 6f5e40c96a085f56ae91ef72deccd9b90f15140e..5fdcc64ec02827b9b594f08b09513dfa6b27b86d 100644 --- a/smash/web/admin.py +++ b/smash/web/admin.py @@ -1,6 +1,6 @@ from django.contrib import admin -from models import Subject, Item, Room, AppointmentType, Language, Location, Worker, FlyingTeam, Avaibility, Holiday, \ +from models import Subject, Item, Room, AppointmentType, Language, Location, Worker, FlyingTeam, Availability, Holiday, \ Visit, Appointment @@ -18,6 +18,6 @@ admin.site.register(Language, LanguageAdmin) admin.site.register(Location) admin.site.register(Worker) admin.site.register(FlyingTeam) -admin.site.register(Avaibility) +admin.site.register(Availability) admin.site.register(Holiday) admin.site.register(Appointment) diff --git a/smash/web/forms.py b/smash/web/forms.py index 80e00359544498c0bdcc726ca6e59067381e3958..b5d6da32b6eabcfbfff044f17d34135352f3a77a 100644 --- a/smash/web/forms.py +++ b/smash/web/forms.py @@ -1,17 +1,17 @@ -from datetime import 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 +from models import Subject, Worker, Appointment, Visit, AppointmentType, ContactAttempt from models.constants import SUBJECT_TYPE_CHOICES """ Possible redundancy, but if need arises, contents of forms can be easily customized """ -CURRENT_YEAR = datetime.now().year +CURRENT_YEAR = datetime.datetime.now().year YEAR_CHOICES = tuple(range(CURRENT_YEAR, CURRENT_YEAR - 120, -1)) FUTURE_YEAR_CHOICES = tuple(range(CURRENT_YEAR, CURRENT_YEAR + 5, 1)) DATEPICKER_DATE_ATTRS = { @@ -79,6 +79,7 @@ class SubjectAddForm(ModelForm): validate_subject_nd_number(self) + def get_new_screening_number(screening_number_prefix): result_number = 0 subjects = Subject.objects.filter(screening_number__contains=screening_number_prefix) @@ -95,12 +96,14 @@ def get_new_screening_number(screening_number_prefix): return screening_number_prefix + str(result_number + 1).zfill(3) + def get_prefix_screening_number(user): prefix_screening_number = '' if (user is not None) and (user.screening_number_prefix is not None) and (user.screening_number_prefix != ""): prefix_screening_number = user.screening_number_prefix + "-" return prefix_screening_number + class SubjectDetailForm(ModelForm): class Meta: model = Subject @@ -268,6 +271,29 @@ class VisitAddForm(ModelForm): self.add_error('datetime_end', "End date must be after start date") +class ContactAttemptForm(ModelForm): + datetime_when = forms.DateTimeField(label='Contact on (YYYY-MM-DD HH:MM)', + widget=forms.DateTimeInput(DATETIMEPICKER_DATE_ATTRS) + ) + + class Meta: + model = ContactAttempt + fields = '__all__' + + def __init__(self, *args, **kwargs): + user = kwargs.pop('user', None) + if user is None: + raise TypeError("User not defined") + self.user = Worker.get_by_user(user) + if self.user is None: + raise TypeError("Worker not defined for: " + user.username) + subject = kwargs.pop('subject', None) + super(ContactAttemptForm, self).__init__(*args, **kwargs) + self.fields['subject'].initial = subject.id + self.fields['subject'].disabled = True + self.fields['worker'].initial = self.user + + class KitRequestForm(Form): start_date = forms.DateField(label="From date", widget=forms.DateInput(DATEPICKER_DATE_ATTRS, "%Y-%m-%d"), @@ -286,7 +312,7 @@ class StatisticsForm(Form): visit_choices = kwargs['visit_choices'] month = kwargs['month'] year = kwargs['year'] - now = datetime.now() + now = datetime.datetime.now() year_now = now.year number_of_years_for_statistics = year_now - START_YEAR_STATISTICS + 2 diff --git a/smash/web/migrations/__init__.py b/smash/web/migrations/__init__.py deleted file mode 100644 index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..0000000000000000000000000000000000000000 diff --git a/smash/web/models/__init__.py b/smash/web/models/__init__.py index 8b5de969d74df68db00fccace5627dfc877fe210..0f14c5c3518e834baad80edae36fe72c80e15dbe 100644 --- a/smash/web/models/__init__.py +++ b/smash/web/models/__init__.py @@ -12,16 +12,17 @@ from visit import Visit from worker import Worker from appointment import Appointment from appointment_type import AppointmentType -from avaibility import Avaibility +from availability import Availability from holiday import Holiday from item import Item from language import Language from subject import Subject +from contact_attempt import ContactAttempt def get_current_year(): return datetime.datetime.now().year -__all__ = [FlyingTeam, Appointment, AppointmentType, Avaibility, Holiday, Item, Language, Location, Room, Subject, - Visit, Worker] +__all__ = [FlyingTeam, Appointment, AppointmentType, Availability, Holiday, Item, Language, Location, Room, Subject, + Visit, Worker, ContactAttempt] diff --git a/smash/web/models/avaibility.py b/smash/web/models/availability.py similarity index 87% rename from smash/web/models/avaibility.py rename to smash/web/models/availability.py index d448758776af6766186a930de235b2aaf3bedee9..8323a6ab311756145b1a307d4d17ba83f32cf2bd 100644 --- a/smash/web/models/avaibility.py +++ b/smash/web/models/availability.py @@ -2,7 +2,7 @@ from django.db import models -class Avaibility(models.Model): +class Availability(models.Model): class Meta: app_label = 'web' @@ -13,10 +13,10 @@ class Avaibility(models.Model): verbose_name='Day of the week' ) available_from = models.TimeField( - verbose_name='Avaible since' + verbose_name='Available since' ) available_till = models.TimeField( - verbose_name='Avaible until' + verbose_name='Available until' ) is_current = models.BooleanField( verbose_name='Is current?', diff --git a/smash/web/models/constants.py b/smash/web/models/constants.py index 19d93238b48f0c5a1ab6c0acd63c7b643035e7b2..333f0baa2a66ea3c4e2e7c1b0437e5ac3f566200 100644 --- a/smash/web/models/constants.py +++ b/smash/web/models/constants.py @@ -13,3 +13,17 @@ SUBJECT_TYPE_CHOICES = { } APPOINTMENT_TYPE_DEFAULT_COLOR = '#cfc600' APPOINTMENT_TYPE_DEFAULT_FONT_COLOR = '#00000' + +CONTACT_TYPES_EMAIL = 'E' +CONTACT_TYPES_PHONE = 'P' +CONTACT_TYPES_SMS = 'S' +CONTACT_TYPES_FAX = 'X' +CONTACT_TYPES_FACE2FACE = 'F' + +CONTACT_TYPES_CHOICES = ( + (CONTACT_TYPES_EMAIL, 'Email'), + (CONTACT_TYPES_FACE2FACE, 'Face to face'), + (CONTACT_TYPES_FAX, 'Fax'), + (CONTACT_TYPES_PHONE, 'Phone'), + (CONTACT_TYPES_SMS, 'SMS'), +) diff --git a/smash/web/models/contact_attempt.py b/smash/web/models/contact_attempt.py new file mode 100644 index 0000000000000000000000000000000000000000..796b1ce5e68bb97b24278b60fcbb4ce1032d9db9 --- /dev/null +++ b/smash/web/models/contact_attempt.py @@ -0,0 +1,28 @@ +# coding=utf-8 +from django.db import models + +from constants import CONTACT_TYPES_CHOICES, CONTACT_TYPES_PHONE + +__author__ = 'Valentin Grouès' + + +class ContactAttempt(models.Model): + subject = models.ForeignKey("web.Subject", + verbose_name='Subject' + ) + worker = models.ForeignKey("web.Worker", null=True, + verbose_name='Worker' + ) + type = models.CharField(max_length=2, default=CONTACT_TYPES_PHONE, choices=CONTACT_TYPES_CHOICES) + + datetime_when = models.DateTimeField(verbose_name="Contact on", help_text='When did the contact occurred?') + + success = models.BooleanField(default=False) + + comment = models.TextField(max_length=1024, null=True, blank=True) + + def __str__(self): + return "%s %s" % (self.subject, self.worker) + + def __unicode__(self): + return "%s %s" % (self.subject, self.worker) diff --git a/smash/web/templates/appointments/add.html b/smash/web/templates/appointments/add.html index 63c8d3b182292cad498193ccb36351d0d061c410..419e28dc1ab5e1cdbf1f6c24c5b952e8985fd0f3 100644 --- a/smash/web/templates/appointments/add.html +++ b/smash/web/templates/appointments/add.html @@ -18,7 +18,7 @@ {% block page_header %}New appointment{% endblock page_header %} {% block page_description %}{% endblock page_description %} -{% block title %}{{ block.super }} - Add new appoitnment{% endblock %} +{% block title %}{{ block.super }} - Add new appointment{% endblock %} {% block breadcrumb %} {% include "appointments/breadcrumb.html" %} diff --git a/smash/web/templates/contact_attempt/add.html b/smash/web/templates/contact_attempt/add.html new file mode 100644 index 0000000000000000000000000000000000000000..624407a50eed1ebad2e300bfc7ab8d6507b7473d --- /dev/null +++ b/smash/web/templates/contact_attempt/add.html @@ -0,0 +1,81 @@ +{% extends "_base.html" %} +{% load static %} +{% load filters %} + +{% block styles %} + {{ block.super }} + <link rel="stylesheet" href="{% static 'AdminLTE/plugins/awesomplete/awesomplete.css' %}"/> + + {% include "includes/datepicker.css.html" %} +{% endblock styles %} + +{% block ui_active_tab %}'subjects'{% endblock ui_active_tab %} +{% block page_header %}New contact attempt{% endblock page_header %} +{% block page_description %}{% endblock page_description %} + +{% block title %}{{ block.super }} - Add new contact attempt{% endblock %} + +{% block breadcrumb %} + {% include "subjects/breadcrumb.html" %} +{% endblock breadcrumb %} + +{% block maincontent %} + + {% block content %} + <div class="row"> + <div class="col-md-12"> + <div class="box box-success"> + <div class="box-header with-border"> + <h3 class="box-title">Enter contact attempt details</h3> + </div> + + + <form method="post" action="" class="form-horizontal"> + {% csrf_token %} + + <div class="box-body"> + {% for field in form %} + <div class="form-group {% if field.errors %}has-error{% endif %}"> + <label class="col-sm-4 col-lg-offset-1 col-lg-2 control-label"> + {{ field.label }} + </label> + + <div class="col-sm-8 col-lg-4"> + {{ field|add_class:'form-control' }} + </div> + + {% if field.errors %} + <span class="help-block"> + {{ field.errors }} + </span> + {% endif %} + </div> + {% endfor %} + </div><!-- /.box-body --> + <div class="box-footer"> + <div class="col-sm-6"> + <button type="submit" class="btn btn-block btn-success">Add</button> + </div> + <div class="col-sm-6"> + <a href="{% url 'web.views.subject_edit' subject_id %}" + class="btn btn-block btn-default">Cancel</a> + </div> + </div><!-- /.box-footer --> + </form> + </div> + + </div> + </div> + + {% endblock %} + + +{% endblock maincontent %} + +{% block scripts %} + {{ block.super }} + + <script src="{% static 'AdminLTE/plugins/awesomplete/awesomplete.min.js' %}"></script> + + {% include "includes/datetimepicker.js.html" %} +{% endblock scripts %} diff --git a/smash/web/templates/doctors/availability_delete.html b/smash/web/templates/doctors/availability_delete.html index 06163f25086be9245d7d420fa404fdc634d54990..64ee01760ab783c4e024d39d890e547e1db0b41a 100644 --- a/smash/web/templates/doctors/availability_delete.html +++ b/smash/web/templates/doctors/availability_delete.html @@ -9,7 +9,7 @@ {% block ui_active_tab %}'workers'{% endblock ui_active_tab %} {% block page_header %}Workers{% endblock page_header %} -{% block page_description %}avaibility{% endblock page_description %} +{% block page_description %}availability{% endblock page_description %} {% block breadcrumb %} {% include "doctors/breadcrumb.html" %} @@ -45,13 +45,13 @@ {% else %} - <p>No avaibilities on Monday.</p> + <p>No availabilities on Monday.</p> {% endif %} <div> <a href="{% url 'web.views.doctor_add' %}" class="btn btn-app"> <i class="fa fa-plus"></i> - Add new avaibility</a> + Add new availability</a> </div> <h3>Tuesday</h3> @@ -80,13 +80,13 @@ {% else %} - <p>No avaibilities on Tuesday.</p> + <p>No availabilities on Tuesday.</p> {% endif %} <div> <a href="{% url 'web.views.doctor_add' %}" class="btn btn-app"> <i class="fa fa-plus"></i> - Add new avaibility</a> + Add new availability</a> </div> <h3>Wednesday</h3> @@ -115,13 +115,13 @@ {% else %} - <p>No avaibilities on Wednesday.</p> + <p>No availabilities on Wednesday.</p> {% endif %} <div> <a href="{% url 'web.views.doctor_add' %}" class="btn btn-app"> <i class="fa fa-plus"></i> - Add new avaibility</a> + Add new availability</a> </div> <h3>Thursday</h3> @@ -150,13 +150,13 @@ {% else %} - <p>No avaibilities on Thursday.</p> + <p>No availabilities on Thursday.</p> {% endif %} <div> <a href="{% url 'web.views.doctor_add' %}" class="btn btn-app"> <i class="fa fa-plus"></i> - Add new avaibility</a> + Add new availability</a> </div> <h3>Friday</h3> @@ -185,13 +185,13 @@ {% else %} - <p>No avaibilities on Friday.</p> + <p>No availabilities on Friday.</p> {% endif %} <div> <a href="{% url 'web.views.doctor_add' %}" class="btn btn-app"> <i class="fa fa-plus"></i> - Add new avaibility</a> + Add new availability</a> </div> <h3>Saturday</h3> @@ -220,13 +220,13 @@ {% else %} - <p>No avaibilities on Saturday.</p> + <p>No availabilities on Saturday.</p> {% endif %} <div> <a href="{% url 'web.views.doctor_add' %}" class="btn btn-app"> <i class="fa fa-plus"></i> - Add new avaibility</a> + Add new availability</a> </div> </div> diff --git a/smash/web/templates/doctors/availability_index.html b/smash/web/templates/doctors/availability_index.html index 770b20a8c6ab6c006cc119b7cbfc8ab63637a5fa..9e09791f50552035e6d4f534189855cbddcbfd2c 100644 --- a/smash/web/templates/doctors/availability_index.html +++ b/smash/web/templates/doctors/availability_index.html @@ -9,7 +9,7 @@ {% block ui_active_tab %}'workers'{% endblock ui_active_tab %} {% block page_header %}Workers{% endblock page_header %} -{% block page_description %}avaibility{% endblock page_description %} +{% block page_description %}availability{% endblock page_description %} {% block breadcrumb %} {% include "doctors/breadcrumb.html" %} @@ -42,7 +42,7 @@ <div class="new-availability"> <a href="#" class="btn btn-app"> <i class="fa fa-plus"></i> - Add new avaibility</a> + Add new availability</a> </div> {% if avmon %} @@ -69,7 +69,7 @@ </table> {% else %} - <p>No avaibilities on Monday.</p> + <p>No availabilities on Monday.</p> {% endif %} </div> @@ -81,7 +81,7 @@ <div class="new-availability"> <a href="#" class="btn btn-app"> <i class="fa fa-plus"></i> - Add new avaibility</a> + Add new availability</a> </div> {% if avtue %} @@ -108,7 +108,7 @@ </table> {% else %} - <p>No avaibilities on Tuesday.</p> + <p>No availabilities on Tuesday.</p> {% endif %} </div> @@ -120,7 +120,7 @@ <div class="new-availability"> <a href="#" class="btn btn-app"> <i class="fa fa-plus"></i> - Add new avaibility</a> + Add new availability</a> </div> {% if avwed %} @@ -147,7 +147,7 @@ </table> {% else %} - <p>No avaibilities on Wednesday.</p> + <p>No availabilities on Wednesday.</p> {% endif %} </div> @@ -159,7 +159,7 @@ <div class="new-availability"> <a href="#" class="btn btn-app"> <i class="fa fa-plus"></i> - Add new avaibility</a> + Add new availability</a> </div> {% if avthu %} @@ -186,7 +186,7 @@ </table> {% else %} - <p>No avaibilities on Thursday.</p> + <p>No availabilities on Thursday.</p> {% endif %} </div> @@ -198,7 +198,7 @@ <div class="new-availability"> <a href="#" class="btn btn-app"> <i class="fa fa-plus"></i> - Add new avaibility</a> + Add new availability</a> </div> {% if avfri %} @@ -225,7 +225,7 @@ </table> {% else %} - <p>No avaibilities on Friday.</p> + <p>No availabilities on Friday.</p> {% endif %} </div> @@ -237,7 +237,7 @@ <div class="new-availability"> <a href="#" class="btn btn-app"> <i class="fa fa-plus"></i> - Add new avaibility</a> + Add new availability</a> </div> {% if avsat %} @@ -264,7 +264,7 @@ </table> {% else %} - <p>No avaibilities on Saturday.</p> + <p>No availabilities on Saturday.</p> {% endif %} </div> @@ -274,7 +274,7 @@ <div class="new-availability"> <a href="#" class="btn btn-app"> <i class="fa fa-plus"></i> - Add new avaibility</a> + Add new availability</a> </div> {% if avsun %} @@ -301,7 +301,7 @@ </table> {% else %} - <p>No avaibilities on Sunday.</p> + <p>No availabilities on Sunday.</p> {% endif %} </div> diff --git a/smash/web/templates/subjects/edit.html b/smash/web/templates/subjects/edit.html index 0c8b2f5705b0de274e1cb6149ea7e249e3537b9b..9356646e39ad8ec8af3f11885f73cd4a0cb939db 100644 --- a/smash/web/templates/subjects/edit.html +++ b/smash/web/templates/subjects/edit.html @@ -23,53 +23,103 @@ {% block maincontent %} {% block content %} - <div class="box box-info"> - <div class="box-header with-border"> - <a href="{% url 'web.views.subjects' %}" class="btn btn-block btn-default" onclick="history.back()">Go - back (without changes)</a> + <div class="row"> + <p class="col-lg-3 pull-left"> + <a href="{% url 'web.views.subjects' %}" class="btn btn-block btn-default" + onclick="history.back()">Go + back (discard changes)</a> + </p> + <p class="col-md-2 pull-right"> <a href="{% url 'web.views.subject_visit_details' subject.id %}" type="button" class="btn btn-block btn-default">Subject's visits</a> - </div> - - {% comment %} <div class="box-header with-border"> - <h3 class="box-title">Details of subject</h3> - </div>{% endcomment %} - - <form method="post" action="" class="form-horizontal"> - {% csrf_token %} - - <div class="box-body"> - <div class="col-md-12"> - {% for field in form %} - <div class="col-md-6 form-group {% if field.errors %}has-error{% endif %}"> - <label for="{# TODO #}" class="col-sm-4 control-label"> - {{ field.label }} - </label> - - <div class="col-sm-8"> - {{ field|add_class:'form-control' }} - </div> - - {% if field.errors %} - <span class="help-block"> {{ field.errors }} </span> - {% endif %} - </div> - {% endfor %} + </p> + </div> + <div class="row"> + <div class="col-md-12"> + <div class="box box-success"> + <div class="box-header with-border"> + <h3>Subject details</h3> </div> - </div><!-- /.box-body --> - - <div class="box-footer"> - <div class="col-sm-6"> - <button type="submit" class="btn btn-block btn-success">Save</button> + <div class="box-body"> + <div class="col-md-12"> + <form method="post" action="" class="form-horizontal"> + {% csrf_token %} + + {% for field in form %} + <div class="col-md-6 form-group {% if field.errors %}has-error{% endif %}"> + <label for="{# TODO #}" class="col-sm-4 control-label"> + {{ field.label }} + </label> + + <div class="col-sm-8"> + {{ field|add_class:'form-control' }} + </div> + + {% if field.errors %} + <span class="help-block"> {{ field.errors }} </span> + {% endif %} + </div> + {% endfor %} + + + </form> + </div> + </div><!-- /.box-body --> + + + <div class="box-footer"> + <div class="col-sm-6"> + <button type="submit" class="btn btn-block btn-success">Save</button> + </div> + <div class="col-sm-6"> + <a href="{% url 'web.views.subjects' %}" class="btn btn-block btn-default" + onclick="history.back()">Cancel</a> + </div> + </div><!-- /.box-footer --> + </div><!-- /.box --> + </div><!-- /.col-md-12 --> + </div><!-- /.row --> + <div class="row"> + <div class="col-lg-12"> + <div class="box box-success"> + <div class="box-header with-border"> + <h3>Contact attempts <a title="add a new contact attempt" + id="add-contact-attempt" + href="{% url 'web.views.contact_add' subject.id %}" class="text-primary" + ><i class="fa fa-plus-circle text-success"></i></a></h3> </div> - <div class="col-sm-6"> - <a href="{% url 'web.views.subjects' %}" class="btn btn-block btn-default" - onclick="history.back()">Cancel</a> + <div class="box-body"> + <table class="table table-bordered table-striped"> + <thead> + <tr> + + <th class="text-center">When</th> + <th class="text-center">Who</th> + <th class="text-center">Type</th> + <th class="text-center">Success</th> + <th class="text-center">Comment</th> + </tr> + </thead> + <tbody> + {% for contact_attempt in contact_attempts %} + <tr> + <td>{{ contact_attempt.datetime_when }}</td> + <td>{{ contact_attempt.worker }}</td> + <td class="text-center">{{ contact_attempt.get_type_display }}</td> + <td class="text-center"> + <i class="fa {% if contact_attempt.success %}fa-check text-success{% else %}fa-times text-danger{% endif %}"></i> + </td> + <td>{{ contact_attempt.comment }}</td> + </tr> + {% endfor %} + </tbody> + </table> </div> - </div><!-- /.box-footer --> - </form> + </div> + + </div> </div> <div class="modal modal-danger fade" id="confirm-dead-resigned-mark-dialog" tabindex="-1" role="dialog"> diff --git a/smash/web/tests/__init__.py b/smash/web/tests/__init__.py index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..72b9f9adc3193fb034ac7656aea51eae348acddc 100644 --- a/smash/web/tests/__init__.py +++ b/smash/web/tests/__init__.py @@ -0,0 +1,21 @@ +from django.contrib.auth.models import User +from django.test import Client +from django.test import TestCase + +from functions import create_worker + + +class LoggedInTestCase(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) + + +class LoggedInWithWorkerTestCase(LoggedInTestCase): + def setUp(self): + super(LoggedInWithWorkerTestCase, self).setUp() + self.worker = create_worker(self.user) diff --git a/smash/web/tests/test_api.py b/smash/web/tests/test_api.py index 7772b9a2b8d3466a57fd66f921eae7a47f7968ec..f7e2b6c0c243180a669ca533b247ddf087de3abe 100644 --- a/smash/web/tests/test_api.py +++ b/smash/web/tests/test_api.py @@ -1,29 +1,18 @@ # coding=utf-8 -import datetime import json -from django.contrib.auth.models import User -from django.test import TestCase -from django.test import Client from django.urls import reverse -from web.models import Visit -from web.api_views import cities -from web.tests.functions import create_subject, create_worker, create_appointment_type +from web.tests.functions import create_subject, create_appointment_type +from . import LoggedInWithWorkerTestCase __author__ = 'Piotr Gawron' -class TestApi(TestCase): +class TestApi(LoggedInWithWorkerTestCase): def setUp(self): + super(TestApi, self).setUp() self.subject = create_subject() - self.client = Client() - username = 'piotr' - password = 'top_secret' - self.user = User.objects.create_user( - username=username, email='jacob@bla', password=password) - self.worker = create_worker(self.user) - self.client.login(username=username, password=password) def test_cities(self): city_name = "some city" @@ -130,7 +119,7 @@ class TestApi(TestCase): found = False for type in appointment_types: - if type['type']==type_name: + if type['type'] == type_name: found = True self.assertTrue(found) diff --git a/smash/web/tests/test_model_appointment.py b/smash/web/tests/test_model_appointment.py index 252c807c128a7a93b8621ab1059d2cb6ff6c27c2..36525f581b4dba2ebd12f93c49db85e1dbf4397d 100644 --- a/smash/web/tests/test_model_appointment.py +++ b/smash/web/tests/test_model_appointment.py @@ -1,9 +1,6 @@ from django.test import TestCase -from functions import create_subject, create_appointment -from functions import create_visit -from web.models import Appointment -from web.models import Visit +from functions import create_appointment class AppointmentModelTests(TestCase): diff --git a/smash/web/tests/test_model_subject.py b/smash/web/tests/test_model_subject.py index 6401a89e5f16dad0b2696671a0cd0a74a053a5b5..5397e82b5edabfdc58b81dc0993986707d3bfcf0 100644 --- a/smash/web/tests/test_model_subject.py +++ b/smash/web/tests/test_model_subject.py @@ -27,8 +27,8 @@ class SubjectModelTests(TestCase): subject.mark_as_resigned() appointment_status = Appointment.objects.filter(id=appointment.id)[0].status - visit_finsihed = Visit.objects.filter(id=visit.id)[0].is_finished + visit_finished = Visit.objects.filter(id=visit.id)[0].is_finished self.assertTrue(subject.resigned) - self.assertTrue(visit_finsihed) + self.assertTrue(visit_finished) self.assertEquals(Appointment.APPOINTMENT_STATUS_CANCELLED, appointment_status) diff --git a/smash/web/tests/test_view_appointments.py b/smash/web/tests/test_view_appointments.py index 3e7c2d53f5c8c9181ae3db7ed632063456173d8f..52c7e442009f0b3e8fdbdc00ee8b206a84d3ba8a 100644 --- a/smash/web/tests/test_view_appointments.py +++ b/smash/web/tests/test_view_appointments.py @@ -1,24 +1,14 @@ import datetime -from django.contrib.auth.models import User -from django.test import Client -from django.test import TestCase from django.urls import reverse from functions import create_subject, create_visit, create_appointment, create_worker from web.forms import AppointmentEditForm, SubjectEditForm from web.models import Appointment, Subject +from . import LoggedInTestCase -class AppointmentsViewTests(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) - +class AppointmentsViewTests(LoggedInTestCase): def test_appointments_list_request(self): response = self.client.get(reverse('web.views.appointments')) self.assertEqual(response.status_code, 200) diff --git a/smash/web/tests/test_view_contact_attempt.py b/smash/web/tests/test_view_contact_attempt.py new file mode 100644 index 0000000000000000000000000000000000000000..872bfa8fd631f411c53b60c65c26d2982c1b6480 --- /dev/null +++ b/smash/web/tests/test_view_contact_attempt.py @@ -0,0 +1,53 @@ +import datetime + +from django.urls import reverse +from django.utils import timezone + +from functions import create_subject +from web.models import ContactAttempt +from web.models.constants import CONTACT_TYPES_EMAIL +from . import LoggedInWithWorkerTestCase + + +class ContactAttemptViewTests(LoggedInWithWorkerTestCase): + def test_contact_attempt_add_get(self): + subject = create_subject() + response = self.client.get(reverse('web.views.contact_add', kwargs={'subject_id': subject.id})) + self.assertContains(response, 'selected">{}'.format(self.worker), 1) + self.assertContains(response, 'selected">{}'.format(subject), 1) + + def test_contact_attempt_add_post_valid(self): + subject = create_subject() + self.assertEqual(0, ContactAttempt.objects.filter(subject=subject).count()) + now = datetime.datetime.now() + now_aware = timezone.make_aware(now, timezone.get_default_timezone()) + contact_type = CONTACT_TYPES_EMAIL + comment = "this is a comment" + form_data = {'datetime_when': now, 'worker': self.worker.id, 'type': contact_type, 'comment': comment} + response = self.client.post( + reverse('web.views.contact_add', kwargs={'subject_id': subject.id}), data=form_data) + # check correct redirection to suject edit page + self.assertRedirects(response, reverse('web.views.subject_edit', kwargs={'id': subject.id})) + contact_attempts = ContactAttempt.objects.filter(subject=subject).all() + self.assertEqual(1, len(contact_attempts)) + contact_attempt = contact_attempts[0] + self.assertEqual(now_aware, contact_attempt.datetime_when) + self.assertEqual(contact_type, contact_attempt.type) + self.assertEqual(subject, contact_attempt.subject) + self.assertEqual(self.worker, contact_attempt.worker) + self.assertEqual(comment, contact_attempt.comment) + self.assertFalse(contact_attempt.success) + # follow redirect to check if the new contact attempt is correctly listed + response = self.client.get(response.url) + self.assertContains(response, comment, 1) + + def test_contact_attempt_add_post_invalid(self): + subject = create_subject() + self.assertEqual(0, ContactAttempt.objects.filter(subject=subject).count()) + contact_type = CONTACT_TYPES_EMAIL + comment = "this is a comment" + form_data = {'type': contact_type, 'comment': comment} + response = self.client.post( + reverse('web.views.contact_add', kwargs={'subject_id': subject.id}), data=form_data) + self.assertContains(response, "This field is required", 2) + self.assertEqual(0, ContactAttempt.objects.filter(subject=subject).count()) diff --git a/smash/web/tests/test_view_kit_request.py b/smash/web/tests/test_view_kit_request.py index 556308fd0066b8449868cf91215fc6ca154fba10..3aa380bc1fafc878f5b10d74e94e0b9f8d948e6c 100644 --- a/smash/web/tests/test_view_kit_request.py +++ b/smash/web/tests/test_view_kit_request.py @@ -1,23 +1,16 @@ import datetime -from django.test import TestCase, RequestFactory from django.urls import reverse -from functions import create_user, create_appointment_type, create_appointment +from functions import create_appointment_type, create_appointment from web.models import Item, Appointment -from web.views.kit import kit_requests from web.views.notifications import get_today_midnight_date +from . import LoggedInTestCase -class ViewFunctionsTests(TestCase): - def setUp(self): - self.factory = RequestFactory() - self.user = create_user() - +class ViewFunctionsTests(LoggedInTestCase): def test_kit_requests(self): - request = self.factory.get(reverse('web.views.kit_requests')) - request.user = self.user - response = kit_requests(request) + response = self.client.get(reverse('web.views.kit_requests')) self.assertEqual(response.status_code, 200) def test_kit_requests_2(self): @@ -32,9 +25,7 @@ class ViewFunctionsTests(TestCase): appointment.appointment_types.add(appointment_type) appointment.save() - request = self.factory.get(reverse('web.views.kit_requests')) - request.user = self.user - response = kit_requests(request) + response = self.client.get(reverse('web.views.kit_requests')) self.assertEqual(response.status_code, 200) self.assertTrue(item_name in response.content) @@ -52,9 +43,7 @@ class ViewFunctionsTests(TestCase): appointment.status = Appointment.APPOINTMENT_STATUS_CANCELLED appointment.save() - request = self.factory.get(reverse('web.views.kit_requests')) - request.user = self.user - response = kit_requests(request) + response = self.client.get(reverse('web.views.kit_requests')) self.assertEqual(response.status_code, 200) self.assertFalse(item_name in response.content) @@ -71,9 +60,7 @@ class ViewFunctionsTests(TestCase): appointment.appointment_types.add(appointment_type) appointment.save() - request = self.factory.get(reverse('web.views.kit_requests')) - request.user = self.user - response = kit_requests(request) + response = self.client.get(reverse('web.views.kit_requests')) self.assertEqual(response.status_code, 200) self.assertTrue(item_name in response.content) diff --git a/smash/web/tests/test_view_login.py b/smash/web/tests/test_view_login.py index 4b5970a9390218cdf8e928a95e197b638bb19898..4e662d9de2085340c9998676081c7f4dcece35f2 100644 --- a/smash/web/tests/test_view_login.py +++ b/smash/web/tests/test_view_login.py @@ -1,4 +1,5 @@ # coding=utf-8 +from django.contrib import auth as django_auth from django.test import Client from django.test import TestCase from django.urls import reverse @@ -14,11 +15,28 @@ class TestLoginView(TestCase): password = 'top_secret' username = user.username login_url = reverse('web.views.login') + self.assertFalse(django_auth.get_user(self.client).is_authenticated()) response = self.client.post(login_url, data={'username': username, 'password': password}, follow=True) self.assertEqual(200, response.status_code) + self.assertTrue(django_auth.get_user(self.client).is_authenticated()) worker = Worker.get_by_user(user) self.assertIsNotNone(worker) worker.last_name = 'Grouès' worker.save() response = self.client.post(login_url, data={'username': username, 'password': password}, follow=True) self.assertEqual(200, response.status_code) + + def test_login_failed(self): + self.client = Client() + user = create_user() + username = user.username + login_url = reverse('web.views.login') + response = self.client.post(login_url, data={'username': username, 'password': 'wrong_password'}, follow=False) + self.assertEqual(302, response.status_code) + self.assertEqual('/login?error=login_failed', response.url) + self.assertFalse(django_auth.get_user(self.client).is_authenticated()) + + def test_logout(self): + self.test_login() + self.client.get(reverse('web.views.logout')) + self.assertFalse(django_auth.get_user(self.client).is_authenticated()) diff --git a/smash/web/tests/test_view_notifications.py b/smash/web/tests/test_view_notifications.py index 817ec07cb60733cefdd4b41da16c009d3c16d06f..e4d4142d17de2bcf71e1c2c8a51bd577bc0619fb 100644 --- a/smash/web/tests/test_view_notifications.py +++ b/smash/web/tests/test_view_notifications.py @@ -1,8 +1,5 @@ import datetime -from django.contrib.auth.models import User -from django.test import TestCase, RequestFactory - from functions import create_appointment, create_location, create_worker, create_appointment_type from functions import create_subject from functions import create_visit @@ -21,16 +18,11 @@ from web.views.notifications import \ get_today_midnight_date, \ get_unfinished_appointments, \ get_unfinished_appointments_count, \ - get_unfinished_visits, get_active_visits_with_missing_appointments - + get_unfinished_visits +from . import LoggedInTestCase -class NotificationViewTests(TestCase): - def setUp(self): - # Every test needs access to the request factory. - self.factory = RequestFactory() - self.user = User.objects.create_user( - username='piotr', email='jacob@bla', password='top_secret') +class NotificationViewTests(LoggedInTestCase): def test_get_exceeded_visit_notifications_count(self): original_notification = get_visits_without_appointments_count(self.user) @@ -85,7 +77,7 @@ class NotificationViewTests(TestCase): appointment = create_appointment(visit) appointment.appointment_types.add(appointment_type) - appointment.status=Appointment.APPOINTMENT_STATUS_FINISHED + appointment.status = Appointment.APPOINTMENT_STATUS_FINISHED appointment.save() notification = get_visits_with_missing_appointments_count(self.user) diff --git a/smash/web/tests/test_view_statistics.py b/smash/web/tests/test_view_statistics.py index b070ca19c432aa222731847af288cb49ffc7c22a..eac349b67a9893e701d39d75606647b797e60592 100644 --- a/smash/web/tests/test_view_statistics.py +++ b/smash/web/tests/test_view_statistics.py @@ -1,23 +1,14 @@ # 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' +from . import LoggedInTestCase +__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) +class TestStatisticsView(LoggedInTestCase): def test_statistics_request(self): url = reverse('web.views.statistics') response = self.client.get(url) diff --git a/smash/web/tests/test_view_visit.py b/smash/web/tests/test_view_visit.py index 21c16290d0c03e4a68b4d60c52c9be11bfaf8144..b8c0a28696966ba193af9e1bba78da8d8a5e56ba 100644 --- a/smash/web/tests/test_view_visit.py +++ b/smash/web/tests/test_view_visit.py @@ -1,29 +1,15 @@ import datetime -from django.test import Client -from django.test import TestCase from django.urls import reverse -from functions import \ - create_appointment, \ - create_appointment_type, \ - create_subject, \ - create_visit, \ - create_user +from functions import create_subject, create_visit, create_appointment, create_appointment_type from web.forms import VisitDetailForm, VisitAddForm -from web.models import Subject, Visit +from web.models import Visit from web.views.notifications import get_today_midnight_date +from . import LoggedInTestCase -class VisitViewTests(TestCase): - def setUp(self): - username = 'piotr' - password = 'top_secret' - - self.client = Client() - self.user = create_user(username, password) - self.client.login(username=username, password=password) - +class VisitViewTests(LoggedInTestCase): def test_visit_details_request(self): visit = create_visit() create_appointment(visit) @@ -47,7 +33,7 @@ class VisitViewTests(TestCase): response = self.client.post( reverse('web.views.visit_details', kwargs={'id': visit.id}), data=form_data) self.assertEqual(response.status_code, 200) - self.assertFalse("error" in response.content) + self.assertNotContains(response, "error") def test_render_add_visit(self): subject = create_subject() @@ -133,6 +119,5 @@ class VisitViewTests(TestCase): visit = create_visit() visit.datetime_begin = get_today_midnight_date() + datetime.timedelta(days=-10) visit.save() - response = self.client.get(reverse("web.views.unfinished_visits")) self.assertEqual(response.status_code, 200) diff --git a/smash/web/urls.py b/smash/web/urls.py index 6bfa4151411ba8b0711be7ade684a21885d91b36..0a9c9be7130d5a27ae2d6e313a5c93858f312551 100644 --- a/smash/web/urls.py +++ b/smash/web/urls.py @@ -20,6 +20,11 @@ from django.conf.urls import url from web import views urlpatterns = [ + + #################### + # APPOINTMENTS # + #################### + url(r'^appointments$', views.appointment.appointments, name='web.views.appointments'), url(r'^appointments/unfinished$', views.appointment.unfinished_appointments, name='web.views.unfinished_appointments'), @@ -29,6 +34,10 @@ urlpatterns = [ url(r'^appointments/add/general$', views.appointment.appointment_add, name='web.views.appointment_add_general'), url(r'^appointments/edit/(?P<id>\d+)$', views.appointment.appointment_edit, name='web.views.appointment_edit'), + #################### + # VISITS # + #################### + url(r'^visits$', views.visit.visits, name='web.views.visits'), url(r'^visits/exceeded$', views.visit.exceeded_visits, name='web.views.exceeded_visits'), url(r'^visits/unfinished$', views.visit.unfinished_visits, name='web.views.unfinished_visits'), @@ -43,6 +52,10 @@ urlpatterns = [ url(r'^visits/add/(?P<subject_id>\d+)$', views.visit.visit_add, name='web.views.visit_add'), url(r'^visit/mark/(?P<id>\d+)/(?P<as_what>[A-z]+)$', views.visit.visit_mark, name='web.views.visit_mark'), + #################### + # SUBJECTS # + #################### + url(r'^subjects$', views.subject.subjects, name='web.views.subjects'), url(r'^subjects/no_visit$', views.subject.subject_no_visits, name='web.views.subject_no_visits'), url(r'^subjects/equire_contact$', views.subject.subject_require_contact, name='web.views.subject_require_contact'), @@ -51,6 +64,16 @@ urlpatterns = [ name='web.views.subject_visit_details'), url(r'^subjects/edit/(?P<id>\d+)$', views.subject.subject_edit, name='web.views.subject_edit'), + #################### + # CONTACTS # + #################### + + url(r'^subjects/(?P<subject_id>\d+)/contacts/add$', views.contact_attempt.contact_add, name='web.views.contact_add'), + + #################### + # DOCTORS # + #################### + url(r'^doctors$', views.doctor.doctors, name='web.views.doctors'), url(r'^doctors/add$', views.doctor.doctor_add, name='web.views.doctor_add'), url(r'^doctors/details/(?P<doctor_id>\d+)$', views.doctor.doctor_details, name='web.views.doctor_details'), @@ -61,6 +84,10 @@ urlpatterns = [ views.doctor.doctor_availability_delete, name='web.views.doctor_availability_delete'), + #################### + # EQUIPMENT # + #################### + url(r'^equipment_and_rooms$', views.equipment.equipment_and_rooms, name='web.views.equipment_and_rooms'), url(r'^equipment_and_rooms/eqdef$', views.equipment.equipment_def, name='web.views.equipment_def'), url(r'^equipment_and_rooms/kit_requests$', views.kit.kit_requests, name='web.views.kit_requests'), @@ -69,12 +96,29 @@ urlpatterns = [ url(r'^equipment_and_rooms/kit_requests/(?P<start_date>[\w-]+)/(?P<end_date>[\w-]+)/$', views.kit.kit_requests_send_mail, name='web.views.kit_requests_send_mail'), + #################### + # MAIL # + #################### + url(r'^mail_templates$', views.mails.mail_templates, name='web.views.mail_templates'), + + #################### + # STATISTICS # + #################### + url(r'^statistics$', views.statistics.statistics, name='web.views.statistics'), + #################### + # EXPORT # + #################### + url(r'^export$', views.export.export, name='web.views.export'), url(r'^export/(?P<type>[A-z]+)$', views.export.export_to_csv2, name='web.views.export_to_csv2'), + #################### + # AUTH # + #################### + url(r'^login$', views.auth.login, name='web.views.login'), url(r'^logout$', views.auth.logout, name='web.views.logout'), diff --git a/smash/web/views/__init__.py b/smash/web/views/__init__.py index 1ae7ae1c0faf72106b8061582409264355a45fea..49820e91e6c47e45ffee4bc5d23f646f0c8364fd 100644 --- a/smash/web/views/__init__.py +++ b/smash/web/views/__init__.py @@ -60,3 +60,4 @@ import kit import mails import statistics import export +import contact_attempt diff --git a/smash/web/views/contact_attempt.py b/smash/web/views/contact_attempt.py new file mode 100644 index 0000000000000000000000000000000000000000..229e11f8744863c69432aa18b0d7a51a55f21023 --- /dev/null +++ b/smash/web/views/contact_attempt.py @@ -0,0 +1,20 @@ +from django.shortcuts import redirect, get_object_or_404 + +from . import wrap_response +from ..forms import ContactAttemptForm +from ..models import Subject + + +def contact_add(request, subject_id): + subject = get_object_or_404(Subject, id=subject_id) + if request.method == 'POST': + form = ContactAttemptForm(request.POST, user=request.user, subject=subject) + form.instance.subject_id = subject_id + if form.is_valid(): + form.save() + return redirect('web.views.subject_edit', id=subject_id) + else: + form = ContactAttemptForm(user=request.user, subject=subject) + + return wrap_response(request, 'contact_attempt/add.html', + {'form': form, 'subject_id': subject_id}) diff --git a/smash/web/views/doctor.py b/smash/web/views/doctor.py index 1aa0e538f9996d71c3a4a5b52494e6271ee3c8e2..e8b6ade7c47f53e4c4fa535ad1a9be1c849c16ac 100644 --- a/smash/web/views/doctor.py +++ b/smash/web/views/doctor.py @@ -3,7 +3,7 @@ from django.shortcuts import redirect, get_object_or_404 from . import wrap_response from ..forms import WorkerAddForm, WorkerEditForm, WorkerDetailForm -from ..models import Worker, Avaibility +from ..models import Worker, Availability def doctors(request): @@ -47,7 +47,7 @@ def doctor_details(request, doctor_id): def doctor_availability(request, doctor_id): - avall = Avaibility.objects.filter(person=doctor_id) + avall = Availability.objects.filter(person=doctor_id) avmon = avall.filter(day_number=1) avtue = avall.filter(day_number=2) @@ -72,7 +72,7 @@ def doctor_availability(request, doctor_id): def doctor_availability_delete(request, doctor_id, availability_id): - availibility = Avaibility.objects.filter(id=availability_id) + availibility = Availability.objects.filter(id=availability_id) if len(availibility) > 0: availibility.delete() return redirect(doctoravail, id=doctor_id) # FIXME doctoravail doesn't exist diff --git a/smash/web/views/subject.py b/smash/web/views/subject.py index 37732d54a789e0ca7dd86a0b27273ea299b8f6ad..325e2eca7ab89c51ccb0d6fe221a1d1ceeea00d3 100644 --- a/smash/web/views/subject.py +++ b/smash/web/views/subject.py @@ -50,6 +50,7 @@ def subject_require_contact(request): def subject_edit(request, id): the_subject = get_object_or_404(Subject, id=id) + contact_attempts = the_subject.contactattempt_set.order_by('-datetime_when').all() was_dead = the_subject.dead was_resigned = the_subject.resigned if request.method == 'POST': @@ -68,7 +69,8 @@ def subject_edit(request, id): return wrap_response(request, 'subjects/edit.html', { 'form': form, - 'subject': the_subject + 'subject': the_subject, + 'contact_attempts': contact_attempts })