Skip to content
Snippets Groups Projects
Commit 7a624a05 authored by Carlos Vega's avatar Carlos Vega
Browse files

Merge branch 'master' into feature/dummy_data_pdp

parents 5e114166 bbe5d91b
No related branches found
No related tags found
1 merge request!172Feature/dummy data pdp
Pipeline #7083 passed
Showing
with 309 additions and 10 deletions
...@@ -19,4 +19,4 @@ test: ...@@ -19,4 +19,4 @@ test:
- cd smash - cd smash
- python manage.py makemigrations web && python manage.py migrate - python manage.py makemigrations web && python manage.py migrate
- coverage run --source web manage.py test - coverage run --source web manage.py test
- coverage report -m --omit="*/test*,*/migrations*" - coverage report -m --omit="*/test*,*/migrations*,*debug_utils*"
pandas==0.23.4
numpy==1.15.2
matplotlib==2.2.3
Django==1.11.5 Django==1.11.5
gunicorn==19.6.0 gunicorn==19.6.0
Pillow==3.4.2 Pillow==3.4.2
......
...@@ -67,6 +67,9 @@ urlpatterns = [ ...@@ -67,6 +67,9 @@ urlpatterns = [
url(r'^availabilities/(?P<date>\d{4}-\d{2}-\d{2})/$', daily_planning.availabilities, name='web.api.availabilities'), url(r'^availabilities/(?P<date>\d{4}-\d{2}-\d{2})/$', daily_planning.availabilities, name='web.api.availabilities'),
url(r'^events_persist$', daily_planning.events_persist, name='web.api.events_persist'), url(r'^events_persist$', daily_planning.events_persist, name='web.api.events_persist'),
#worker availability
url(r'^worker_availability/$', worker.get_worker_availability, name='web.api.get_worker_availability'),
# worker data # worker data
url(r'^redcap/missing_subjects/(?P<missing_subject_id>\d+):ignore$', redcap.ignore_missing_subject, url(r'^redcap/missing_subjects/(?P<missing_subject_id>\d+):ignore$', redcap.ignore_missing_subject,
name='web.api.redcap.ignore_missing_subject'), name='web.api.redcap.ignore_missing_subject'),
......
...@@ -57,6 +57,7 @@ def get_holidays(worker, date): ...@@ -57,6 +57,7 @@ def get_holidays(worker, date):
'link_when': start_date, 'link_when': start_date,
'link_who': worker.id, 'link_who': worker.id,
'link_end': end_date, 'link_end': end_date,
'kind': holiday.kind
} }
result.append(event) result.append(event)
...@@ -246,8 +247,9 @@ def events(request, date): ...@@ -246,8 +247,9 @@ def events(request, date):
subject = { subject = {
'name': unicode(appointment_subject), 'name': unicode(appointment_subject),
'id': appointment_subject.id, 'id': appointment_subject.id,
'appointment_id': appointment.id,
'color': RANDOM_COLORS[i], 'color': RANDOM_COLORS[i],
'start': appointment.datetime_when.replace(tzinfo=None).strftime("%H:%M:00"), 'start': appointment.datetime_when.replace(tzinfo=None).strftime("%H:%M"),
# this indicates only location of the first appointment # this indicates only location of the first appointment
# (there is small chance to have two appointments in two different places at the same day) # (there is small chance to have two appointments in two different places at the same day)
'location': str(appointment.location), 'location': str(appointment.location),
......
import datetime import datetime
import logging
from django.http import JsonResponse import json
from django.http import JsonResponse, HttpResponse
from django.utils import timezone from django.utils import timezone
from django.shortcuts import get_object_or_404
from web.models.constants import GLOBAL_STUDY_ID from web.models.constants import GLOBAL_STUDY_ID
from web.api_views.daily_planning import get_workers_for_daily_planning, get_availabilities from web.api_views.daily_planning import get_workers_for_daily_planning, get_availabilities
from ..models import Worker from ..models import Worker
logger = logging.getLogger(__name__)
def specializations(request): def specializations(request):
workers = Worker.objects.filter(specialization__isnull=False).values_list('specialization').distinct() workers = Worker.objects.filter(specialization__isnull=False).values_list('specialization').distinct()
...@@ -21,14 +24,18 @@ def units(request): ...@@ -21,14 +24,18 @@ def units(request):
"units": [x[0] for x in workers] "units": [x[0] for x in workers]
}) })
def workers_for_daily_planning(request): def workers_for_daily_planning(request):
start_date = request.GET.get('start_date')
workers = get_workers_for_daily_planning(request) workers = get_workers_for_daily_planning(request)
workers_list_for_json = [] workers_list_for_json = []
if start_date is not None:
today = timezone.now()
start_date=datetime.datetime.strptime(start_date, '%Y-%m-%d').replace(tzinfo=today.tzinfo)
for worker in workers: for worker in workers:
role = unicode(worker.roles.filter(study_id=GLOBAL_STUDY_ID)[0].role) role = unicode(worker.roles.filter(study_id=GLOBAL_STUDY_ID)[0].role)
worker_dict_for_json = { worker_dict_for_json = {
'id': worker.id, 'id': worker.id,
'availability': worker.availability_percentage(start_date=start_date),
'title': u"{} ({})".format(unicode(worker), role[:1].upper()), 'title': u"{} ({})".format(unicode(worker), role[:1].upper()),
'role': role 'role': role
} }
...@@ -86,3 +93,31 @@ def serialize_worker(worker): ...@@ -86,3 +93,31 @@ def serialize_worker(worker):
"id": worker.id, "id": worker.id,
} }
return result return result
def get_worker_availability(request):
start_str_date = request.GET.get("start_date")
end_str_date = request.GET.get("end_date")
worker_id = request.GET.get("worker_id")
if start_str_date is None or worker_id is None:
context = {
'status': '400', 'reason': 'Either start_date, worker_id or both are invalid.'
}
response = HttpResponse(json.dumps(context), content_type='application/json')
response.status_code = 400
return response
start_date = datetime.datetime.strptime(start_str_date, "%Y-%m-%d-%H-%M").replace(tzinfo=timezone.now().tzinfo)
if end_str_date is None or end_str_date == start_str_date:
start_date = start_date.replace(hour=0, minute=0, second=0)
end_date = start_date + datetime.timedelta(days=1)
else:
end_date = datetime.datetime.strptime(end_str_date, "%Y-%m-%d-%H-%M").replace(tzinfo=timezone.now().tzinfo)
worker = get_object_or_404(Worker, id=int(worker_id))
result = {
'start_date': start_date,
'end_date': end_date,
'availability': round(worker.availability_percentage(start_date=start_date, end_date=end_date), 0)
}
return JsonResponse(result)
\ No newline at end of file
# coding=utf-8
import time
def timeit(method):
'''
Debug decorator to measure the execution time of some method or function
'''
def timed(*args, **kw):
ts = time.time()
result = method(*args, **kw)
te = time.time()
if 'log_time' in kw:
name = kw.get('log_name', method.__name__.upper())
kw['log_time'][name] = int((te - ts) * 1000)
else:
print '%r %2.2f ms' % \
(method.__name__, (te - ts) * 1000)
return result
return timed
\ No newline at end of file
...@@ -115,6 +115,10 @@ class AppointmentAddForm(AppointmentForm): ...@@ -115,6 +115,10 @@ class AppointmentAddForm(AppointmentForm):
widget=forms.CheckboxSelectMultiple, widget=forms.CheckboxSelectMultiple,
queryset=AppointmentType.objects.all(), queryset=AppointmentType.objects.all(),
) )
fields['worker_assigned'].widget.attrs = {'class': 'search_worker_availability'}
fields['datetime_when'].widget.attrs = {'class': 'start_date'}
fields['length'].widget.attrs = {'class': 'appointment_duration'}
self.fields = fields self.fields = fields
self.fields['location'].queryset = get_filter_locations(self.user) self.fields['location'].queryset = get_filter_locations(self.user)
......
...@@ -123,6 +123,10 @@ class StatisticsForm(Form): ...@@ -123,6 +123,10 @@ class StatisticsForm(Form):
class AvailabilityAddForm(ModelForm): class AvailabilityAddForm(ModelForm):
def __init__(self, *args, **kwargs):
super(AvailabilityAddForm, self).__init__(*args, **kwargs)
self.fields['person'].widget.attrs['readonly'] = True
available_from = forms.TimeField(label="Available from", available_from = forms.TimeField(label="Available from",
widget=forms.TimeInput(TIMEPICKER_DATE_ATTRS), widget=forms.TimeInput(TIMEPICKER_DATE_ATTRS),
initial="8:00", initial="8:00",
...@@ -204,6 +208,10 @@ class FlyingTeamEditForm(ModelForm): ...@@ -204,6 +208,10 @@ class FlyingTeamEditForm(ModelForm):
class HolidayAddForm(ModelForm): class HolidayAddForm(ModelForm):
def __init__(self, *args, **kwargs):
super(HolidayAddForm, self).__init__(*args, **kwargs)
self.fields['person'].widget.attrs['readonly'] = True
datetime_start = forms.DateTimeField(widget=forms.DateTimeInput(DATETIMEPICKER_DATE_ATTRS), datetime_start = forms.DateTimeField(widget=forms.DateTimeInput(DATETIMEPICKER_DATE_ATTRS),
initial=datetime.datetime.now().replace(hour=8, minute=0), initial=datetime.datetime.now().replace(hour=8, minute=0),
) )
......
# -*- coding: utf-8 -*-
# Generated by Django 1.11.5 on 2018-10-02 09:08
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('web', '0118_voucher_activity_type'),
]
operations = [
migrations.AlterField(
model_name='workerstudyrole',
name='role',
field=models.CharField(choices=[(b'DOCTOR', b'Doctor'), (b'NURSE', b'Nurse'), (b'PSYCHOLOGIST', b'Psychologist'), (b'TECHNICIAN', b'Technician'), (b'SECRETARY', b'Secretary'), (b'PROJECT MANAGER', b'Project Manager')], max_length=20, verbose_name=b'Role'),
),
]
# -*- coding: utf-8 -*-
# Generated by Django 1.11.5 on 2018-10-15 13:24
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('web', '0118_voucher_activity_type'),
]
operations = [
migrations.AlterField(
model_name='studyvisitlist',
name='type',
field=models.CharField(choices=[(b'UNFINISHED', b'Unfinished visits'), (b'APPROACHING_WITHOUT_APPOINTMENTS', b'Approaching visits'), (b'APPROACHING_FOR_MAIL_CONTACT', b'Post mail for approaching visits'), (b'GENERIC', b'Generic visit list'), (b'MISSING_APPOINTMENTS', b'Visits with missing appointments'), (b'EXCEEDED_TIME', b'Exceeded visit time')], max_length=50, verbose_name=b'Type of list'),
),
migrations.AlterField(
model_name='workerstudyrole',
name='role',
field=models.CharField(choices=[(b'DOCTOR', b'Doctor'), (b'NURSE', b'Nurse'), (b'PSYCHOLOGIST', b'Psychologist'), (b'TECHNICIAN', b'Technician'), (b'SECRETARY', b'Secretary'), (b'PROJECT MANAGER', b'Project Manager')], max_length=20, verbose_name=b'Role'),
),
]
# -*- coding: utf-8 -*-
# Generated by Django 1.11.5 on 2018-10-03 09:11
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('web', '0119_auto_20181002_0908'),
]
operations = [
migrations.AddField(
model_name='holiday',
name='kind',
field=models.CharField(choices=[(b'H', b'Holiday'), (b'X', b'Extra Availability')], default=b'H', max_length=1),
),
]
# -*- coding: utf-8 -*-
# Generated by Django 1.11.5 on 2018-10-03 12:56
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('web', '0120_holiday_kind'),
]
operations = [
migrations.AlterField(
model_name='holiday',
name='kind',
field=models.CharField(choices=[(b'H', b'Holiday'), (b'X', b'Extra Availability')], default=b'H', help_text=b'Defines the kind of availability. Either Holiday or Extra Availability.', max_length=1),
),
]
# -*- coding: utf-8 -*-
# Generated by Django 1.11.5 on 2018-10-10 12:29
from __future__ import unicode_literals
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('web', '0121_auto_20181003_1256'),
]
operations = [
migrations.RemoveField(
model_name='worker',
name='appointments',
),
]
# -*- coding: utf-8 -*-
# Generated by Django 1.11.5 on 2018-10-17 15:32
from __future__ import unicode_literals
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('web', '0119_auto_20181015_1324'),
('web', '0122_remove_worker_appointments'),
]
operations = [
]
# -*- coding: utf-8 -*-
# Generated by Django 1.11.5 on 2018-10-17 15:32
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('web', '0123_merge_20181017_1532'),
]
operations = [
migrations.AlterField(
model_name='studyvisitlist',
name='type',
field=models.CharField(choices=[(b'UNFINISHED', b'unfinished visits'), (b'APPROACHING_WITHOUT_APPOINTMENTS', b'approaching visits'), (b'APPROACHING_FOR_MAIL_CONTACT', b'post mail for approaching visits'), (b'GENERIC', b'Generic visit list'), (b'MISSING_APPOINTMENTS', b'visits with missing appointments'), (b'EXCEEDED_TIME', b'exceeded visit time')], max_length=50, verbose_name=b'Type of list'),
),
]
# -*- coding: utf-8 -*-
# Generated by Django 1.11.5 on 2018-10-25 07:45
from __future__ import unicode_literals
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('web', '0124_auto_20181017_1532'),
('web', '0121_auto_20181024_1859'),
]
operations = [
]
...@@ -78,6 +78,14 @@ WEEKDAY_CHOICES = ( ...@@ -78,6 +78,14 @@ WEEKDAY_CHOICES = (
(SUNDAY_AS_DAY_OF_WEEK, 'SUNDAY'), (SUNDAY_AS_DAY_OF_WEEK, 'SUNDAY'),
) )
AVAILABILITY_HOLIDAY = 'H'
AVAILABILITY_EXTRA = 'X'
AVAILABILITY_CHOICES = (
(AVAILABILITY_HOLIDAY, 'Holiday'),
(AVAILABILITY_EXTRA, 'Extra Availability'),
)
REDCAP_TOKEN_CONFIGURATION_TYPE = "REDCAP_TOKEN_CONFIGURATION_TYPE" REDCAP_TOKEN_CONFIGURATION_TYPE = "REDCAP_TOKEN_CONFIGURATION_TYPE"
REDCAP_BASE_URL_CONFIGURATION_TYPE = "REDCAP_BASE_URL_CONFIGURATION_TYPE" REDCAP_BASE_URL_CONFIGURATION_TYPE = "REDCAP_BASE_URL_CONFIGURATION_TYPE"
......
# coding=utf-8 # coding=utf-8
from django.db import models from django.db import models
from constants import AVAILABILITY_CHOICES, AVAILABILITY_HOLIDAY
class Holiday(models.Model): class Holiday(models.Model):
class Meta: class Meta:
...@@ -21,6 +22,8 @@ class Holiday(models.Model): ...@@ -21,6 +22,8 @@ class Holiday(models.Model):
verbose_name='Comments' verbose_name='Comments'
) )
kind = models.CharField(max_length=1, choices=AVAILABILITY_CHOICES, default=AVAILABILITY_HOLIDAY, help_text='Defines the kind of availability. Either Holiday or Extra Availability.')
def __str__(self): def __str__(self):
return "%s %s" % (self.person.first_name, self.person.last_name) return "%s %s" % (self.person.first_name, self.person.last_name)
......
...@@ -41,6 +41,14 @@ class Study(models.Model): ...@@ -41,6 +41,14 @@ class Study(models.Model):
def __unicode__(self): def __unicode__(self):
return "%s" % self.name return "%s" % self.name
@property
def has_voucher_types(self):
return self.columns.voucher_types
@property
def has_vouchers(self):
return self.columns.vouchers
@staticmethod @staticmethod
def get_by_id(study_id): def get_by_id(study_id):
study = Study.objects.filter(id=study_id) study = Study.objects.filter(id=study_id)
......
...@@ -2,13 +2,23 @@ ...@@ -2,13 +2,23 @@
import datetime import datetime
import logging import logging
from web.utils import get_today_midnight_date
from django.contrib.auth.models import User, AnonymousUser from django.contrib.auth.models import User, AnonymousUser
from django.db import models from django.db import models
from web.models.constants import GLOBAL_STUDY_ID, COUNTRY_OTHER_ID from web.models.constants import GLOBAL_STUDY_ID, COUNTRY_OTHER_ID, AVAILABILITY_HOLIDAY, AVAILABILITY_EXTRA
from web.models.worker_study_role import STUDY_ROLE_CHOICES, HEALTH_PARTNER_ROLE_CHOICES, \ from web.models.worker_study_role import STUDY_ROLE_CHOICES, HEALTH_PARTNER_ROLE_CHOICES, \
VOUCHER_PARTNER_ROLE_CHOICES, WORKER_STAFF, WORKER_HEALTH_PARTNER, WORKER_VOUCHER_PARTNER, ROLE_CHOICES VOUCHER_PARTNER_ROLE_CHOICES, WORKER_STAFF, WORKER_HEALTH_PARTNER, WORKER_VOUCHER_PARTNER, ROLE_CHOICES
from web.utils import get_weekdays_in_period
from web.officeAvailability import OfficeAvailability
from django.db.models import Q
from web.models.holiday import Holiday
from web.models.availability import Availability
from web.models.appointment import Appointment
from web.models.appointment_type_link import AppointmentTypeLink
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
...@@ -61,9 +71,6 @@ class Worker(models.Model): ...@@ -61,9 +71,6 @@ class Worker(models.Model):
verbose_name='Locations', verbose_name='Locations',
blank=True blank=True
) )
appointments = models.ManyToManyField('web.Appointment', blank=True,
verbose_name='Appointments'
)
user = models.OneToOneField(User, blank=True, null=True, user = models.OneToOneField(User, blank=True, null=True,
verbose_name='Username' verbose_name='Username'
) )
...@@ -141,7 +148,8 @@ class Worker(models.Model): ...@@ -141,7 +148,8 @@ class Worker(models.Model):
def is_on_leave(self): def is_on_leave(self):
if len(self.holiday_set.filter(datetime_end__gt=datetime.datetime.now(), if len(self.holiday_set.filter(datetime_end__gt=datetime.datetime.now(),
datetime_start__lt=datetime.datetime.now())): datetime_start__lt=datetime.datetime.now(),
kind=AVAILABILITY_HOLIDAY)):
return True return True
return False return False
...@@ -161,6 +169,48 @@ class Worker(models.Model): ...@@ -161,6 +169,48 @@ class Worker(models.Model):
else: else:
return False return False
def is_available(self, start_date=None, end_date=None):
self.availability_percentage(start_date=start_date, end_date=end_date) > 50.0
def availability_percentage(self, start_date=None, end_date=None):
'''
start_date: defaults to None and then is set to today's midnight date
end_date: defaults to None and then is set to today's midnight date + 24 hours
'''
today_midnight = get_today_midnight_date()
if start_date is None:
start_date = today_midnight
if end_date is None:
start_date = start_date.replace(hour=0, minute=0, second=0)
end_date = start_date + datetime.timedelta(days=1)
office_availability = OfficeAvailability('{} {}'.format(self.first_name, self.last_name), start=start_date, end=end_date)
#Appointments
subject_appointments = AppointmentTypeLink.objects.filter(worker=self.id, date_when__gte=start_date, date_when__lte=end_date)
general_appointments = Appointment.objects.filter(worker_assigned=self.id, datetime_when__gte=start_date, datetime_when__lte=end_date)
#Holidays and extra availabilities.
holidays_and_extra_availabilities = self.holiday_set.filter(datetime_start__gte=start_date, datetime_end__lt=end_date).order_by('-datetime_start')
#Availability
weekdays = get_weekdays_in_period(start_date, end_date)
weekdayQ = Q() #create a filter for each weekday in the selected period
for weekday in weekdays:
weekdayQ = weekdayQ | Q(day_number=weekday)
availabilities = self.availability_set.filter(person=self.id).filter(weekdayQ).order_by('day_number', 'available_from')
things = []
things.extend(availabilities)
things.extend(holidays_and_extra_availabilities)
things.extend(subject_appointments)
things.extend(general_appointments)
for thing in things:
office_availability.consider_this(thing, only_working_hours=True)
return office_availability.get_availability_percentage(only_working_hours=True)
@property @property
def role(self): def role(self):
roles = self.roles.filter(study=GLOBAL_STUDY_ID) roles = self.roles.filter(study=GLOBAL_STUDY_ID)
......
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