# coding=utf-8 import datetime import logging from web.utils import get_today_midnight_date from django.contrib.auth.models import User, AnonymousUser from django.db import models 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, \ 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__) def roles_by_worker_type(worker_type): role_choices = role_choices_by_worker_type(worker_type) roles = [] for role_type, role_name in role_choices: roles.append(role_type) return roles def role_choices_by_worker_type(worker_type): if worker_type == WORKER_STAFF: return STUDY_ROLE_CHOICES elif worker_type == WORKER_HEALTH_PARTNER: return HEALTH_PARTNER_ROLE_CHOICES elif worker_type == WORKER_VOUCHER_PARTNER: return VOUCHER_PARTNER_ROLE_CHOICES else: raise TypeError("Unknown worker type") def worker_type_by_worker(worker): roles = worker.roles.filter(study=GLOBAL_STUDY_ID) if roles.count() == 0: return WORKER_STAFF role = roles[0].role for role_type, role_name in STUDY_ROLE_CHOICES: if role_type == role: return WORKER_STAFF for role_type, role_name in HEALTH_PARTNER_ROLE_CHOICES: if role_type == role: return WORKER_HEALTH_PARTNER for role_type, role_name in VOUCHER_PARTNER_ROLE_CHOICES: if role_type == role: return WORKER_VOUCHER_PARTNER raise TypeError("Unknown worker role") class Worker(models.Model): class Meta: app_label = 'web' languages = models.ManyToManyField("web.Language", verbose_name='Known languages', blank=True ) locations = models.ManyToManyField("web.Location", verbose_name='Locations', blank=True ) user = models.OneToOneField(User, blank=True, null=True, verbose_name='Username' ) first_name = models.CharField(max_length=50, verbose_name='First name', blank=True, ) last_name = models.CharField(max_length=50, verbose_name='Last name', blank=True, ) name = models.CharField(max_length=50, verbose_name='Name', default='', blank=True, null=False ) phone_number = models.CharField(max_length=20, verbose_name='Phone number', blank=True ) phone_number_2 = models.CharField(max_length=20, verbose_name='Phone number 2', blank=True ) fax_number = models.CharField(max_length=20, verbose_name='Fax number', blank=True ) address = models.CharField(max_length=255, blank=True, verbose_name='Address' ) postal_code = models.CharField(max_length=7, blank=True, verbose_name='Postal code' ) city = models.CharField(max_length=50, blank=True, verbose_name='City' ) country = models.ForeignKey('web.Country', null=False, blank=False, default=COUNTRY_OTHER_ID, verbose_name='Country' ) voucher_types = models.ManyToManyField("web.VoucherType", verbose_name='Voucher types', blank=True ) unit = models.CharField(max_length=50, verbose_name='Unit', blank=True ) email = models.EmailField( verbose_name='E-mail', blank=True ) specialization = models.CharField(max_length=20, verbose_name='Specialization', blank=True ) voucher_partner_code = models.CharField(max_length=10, verbose_name='Code', blank=True ) comment = models.TextField(max_length=1024, verbose_name='Comment', null=True, blank=True ) def is_on_leave(self): if len(self.holiday_set.filter(datetime_end__gt=datetime.datetime.now(), datetime_start__lt=datetime.datetime.now(), kind=AVAILABILITY_HOLIDAY)): return True return False def disable(self): if self.user is not None: self.user.is_active = False self.user.save() logger.info("'" + self.user.username + "' account has been disabled") return True else: logger.warn("Cannot disable account for user '" + self.first_name + " " + self.last_name + "'") return False def is_active(self): if self.user is not None: return self.user.is_active else: 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(u'{} {}'.format(self.first_name, self.last_name), start=start_date, end=end_date) #Subject Appointments old_events = Q(date_when__gt=start_date) & Q(date_when__gt=end_date) future_events = Q(date_when__lt=start_date) & Q(date_when__lt=end_date) non_overlap_events = old_events | future_events overlap_events = ~non_overlap_events query = Q(worker=self.id) & overlap_events subject_appointments = AppointmentTypeLink.objects.filter(query) #General Appointments old_events = Q(datetime_when__gt=start_date) & Q(datetime_when__gt=end_date) future_events = Q(datetime_when__lt=start_date) & Q(datetime_when__lt=end_date) non_overlap_events = old_events | future_events overlap_events = ~non_overlap_events query = Q(worker_assigned=self.id) & overlap_events general_appointments = Appointment.objects.filter(query) #Holidays and extra availabilities. old_events = Q(datetime_start__gt=start_date) & Q(datetime_start__gt=end_date) future_events = Q(datetime_end__lt=start_date) & Q(datetime_end__lt=end_date) non_overlap_events = old_events | future_events overlap_events = ~non_overlap_events holidays_and_extra_availabilities = self.holiday_set.filter(overlap_events).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 def role(self): roles = self.roles.filter(study=GLOBAL_STUDY_ID) if roles.count() == 0: return WORKER_STAFF role = roles[0].role if role not in [role_type for role_type, _ in ROLE_CHOICES]: raise TypeError("Unknown worker role") return role @staticmethod def get_by_user(the_user): if isinstance(the_user, User): workers = Worker.objects.filter(user=the_user) if len(workers) > 0: return workers[0] else: return None elif isinstance(the_user, Worker): return the_user elif isinstance(the_user, AnonymousUser): return None elif the_user is not None: raise TypeError("Unknown class type: " + the_user.__class__.__name__) else: return None @staticmethod def get_workers_by_worker_type(worker_type, study_id=GLOBAL_STUDY_ID): return Worker.objects.filter(roles__study_id=study_id, roles__role__in=roles_by_worker_type(worker_type)) def __str__(self): if self.name != '': if self.first_name == '': return self.name else: return "%s %s %s" % (self.name, self.first_name, self.last_name) else: return "%s %s" % (self.first_name, self.last_name) def __unicode__(self): if self.name != '': if self.first_name == '': return self.name else: return "%s %s %s" % (self.name, self.first_name, self.last_name) else: return "%s %s" % (self.first_name, self.last_name) def initials(self): result = "" if len(self.first_name) > 0: result += self.first_name[0] if len(self.last_name) > 0: result += self.last_name[0] return result