diff --git a/smash/web/api_views/daily_planning.py b/smash/web/api_views/daily_planning.py index 773131ec056af9b6b55c919d2bcdcdb44e13c090..d7a64bfb3676beae78641f8d9b85b0abf8acc190 100644 --- a/smash/web/api_views/daily_planning.py +++ b/smash/web/api_views/daily_planning.py @@ -43,15 +43,21 @@ def build_duration(duration): def get_holidays(worker, date): - today_start = datetime.datetime.strptime(date, '%Y-%m-%d').replace(hour=0, minute=0) - today_end = datetime.datetime.strptime(date, '%Y-%m-%d').replace(hour=23, minute=59) - holidays = Holiday.objects.filter(person=worker, datetime_start__lte=today_end, datetime_end__gte=today_start) + today_start = datetime.datetime.strptime( + date, '%Y-%m-%d').replace(hour=0, minute=0) + today_end = datetime.datetime.strptime( + date, '%Y-%m-%d').replace(hour=23, minute=59) + holidays = Holiday.objects.filter( + person=worker, datetime_start__lte=today_end, datetime_end__gte=today_start) result = [] for holiday in holidays: - minutes = int((holiday.datetime_end - holiday.datetime_start).total_seconds() / 60) + minutes = int( + (holiday.datetime_end - holiday.datetime_start).total_seconds() / 60) # hack for time zones - start_date = datetime.datetime.combine(today_start, holiday.datetime_start.time()) - end_date = datetime.datetime.combine(today_start, holiday.datetime_end.time()) + start_date = datetime.datetime.combine( + today_start, holiday.datetime_start.time()) + end_date = datetime.datetime.combine( + today_start, holiday.datetime_end.time()) event = { 'duration': build_duration(minutes), 'link_when': start_date, @@ -65,14 +71,16 @@ def get_holidays(worker, date): def remove_holidays(availability, worker, date): - today_start = datetime.datetime.strptime(date, '%Y-%m-%d').replace(hour=0, minute=0) - today_end = datetime.datetime.strptime(date, '%Y-%m-%d').replace(hour=23, minute=59) - holidays_starting_before = Holiday.objects.filter(person=worker, datetime_start__lte=today_start, - datetime_end__gte=today_start) - holidays_starting_today = Holiday.objects.filter(person=worker, datetime_start__lte=today_end, - datetime_start__gte=today_start) - holidays_ending_today = Holiday.objects.filter(person=worker, datetime_end__lte=today_end, - datetime_end__gte=today_start) + today_start = datetime.datetime.strptime( + date, '%Y-%m-%d').replace(hour=0, minute=0) + today_end = datetime.datetime.strptime( + date, '%Y-%m-%d').replace(hour=23, minute=59) + holidays_starting_before = Holiday.objects.filter( + person=worker, datetime_start__lte=today_start, datetime_end__gte=today_start) + holidays_starting_today = Holiday.objects.filter( + person=worker, datetime_start__lte=today_end, datetime_start__gte=today_start) + holidays_ending_today = Holiday.objects.filter( + person=worker, datetime_end__lte=today_end, datetime_end__gte=today_start) result = [] timestamps = [{ "time": availability.available_from, @@ -124,13 +132,17 @@ def get_availabilities(worker, date): result = [] today = datetime.datetime.strptime(date, '%Y-%m-%d') weekday = today.weekday() + 1 - availabilities = Availability.objects.filter(person=worker, day_number=weekday) + availabilities = Availability.objects.filter( + person=worker, day_number=weekday) for availability in availabilities: - availabilities_without_holidays = remove_holidays(availability, worker, date) + availabilities_without_holidays = remove_holidays( + availability, worker, date) for availability_without_holiday in availabilities_without_holidays: - start_date = datetime.datetime.combine(today, availability_without_holiday.available_from) - end_date = datetime.datetime.combine(today, availability_without_holiday.available_till) + start_date = datetime.datetime.combine( + today, availability_without_holiday.available_from) + end_date = datetime.datetime.combine( + today, availability_without_holiday.available_till) delta = end_date - start_date minutes = int(delta.total_seconds() / 60) @@ -156,7 +168,8 @@ def get_conflicts(worker, date): if appointment.worker_assigned is not None: link_when = appointment.datetime_when link_when = link_when.replace(tzinfo=None) - link_end = link_when + datetime.timedelta(minutes=appointment.length) + link_end = link_when + \ + datetime.timedelta(minutes=appointment.length) event = { 'title': appointment.title(), 'duration': appointment.length, @@ -168,12 +181,15 @@ def get_conflicts(worker, date): } result.append(event) - links = AppointmentTypeLink.objects.filter(appointment=appointment).all() + links = AppointmentTypeLink.objects.filter( + appointment=appointment).all() for j, link in enumerate(links): link_when = link.date_when if link_when is not None: link_when = link_when.replace(tzinfo=None) - link_end = link_when + datetime.timedelta(minutes=link.appointment_type.default_duration) + link_end = link_when + \ + datetime.timedelta( + minutes=link.appointment_type.default_duration) event = { 'title': link.appointment_type.description, 'duration': build_duration(link.appointment_type.default_duration), @@ -212,7 +228,8 @@ def get_generic_appointment_events(request, date): link_end = None if link_when is not None: link_when = link_when.replace(tzinfo=None) - link_end = link_when + datetime.timedelta(minutes=appointment.length) + link_end = link_when + \ + datetime.timedelta(minutes=appointment.length) event = { 'title': appointment.title(), 'short_title': appointment.title(), @@ -250,6 +267,7 @@ def events(request, date): 'appointment_id': appointment.id, 'color': RANDOM_COLORS[i], 'start': appointment.datetime_when.replace(tzinfo=None).strftime("%H:%M"), + 'flags': [language.image.url for language in appointment_subject.subject.languages.all()], # this indicates only location of the first appointment # (there is small chance to have two appointments in two different places at the same day) 'location': str(appointment.location), @@ -257,13 +275,16 @@ def events(request, date): } subjects[appointment_subject.id] = subject - links = AppointmentTypeLink.objects.filter(appointment=appointment).all() + links = AppointmentTypeLink.objects.filter( + appointment=appointment).all() for j, link in enumerate(links): link_when = link.date_when link_end = None if link_when is not None: link_when = link_when.replace(tzinfo=None) - link_end = link_when + datetime.timedelta(minutes=link.appointment_type.default_duration) + link_end = link_when + \ + datetime.timedelta( + minutes=link.appointment_type.default_duration) event = { 'title': link.appointment_type.description, 'short_title': link.appointment_type.code, @@ -330,13 +351,16 @@ def events_persist(request): event_links = json.loads(request.POST.get('events_to_persist')) for event_link in event_links: try: - when = datetime.datetime.strptime(event_link['start'], '%Y-%m-%dT%H:%M:00') + when = datetime.datetime.strptime( + event_link['start'], '%Y-%m-%dT%H:%M:00') except ValueError: - when = datetime.datetime.strptime(event_link['start'][0:-6], '%Y-%m-%dT%H:%M:00') + when = datetime.datetime.strptime( + event_link['start'][0:-6], '%Y-%m-%dT%H:%M:00') worker_id = event_link['link_who'] if 'link_id' in event_link: link_id = event_link['link_id'] - appointment_type_link = get_object_or_404(AppointmentTypeLink, pk=link_id) + appointment_type_link = get_object_or_404( + AppointmentTypeLink, pk=link_id) appointment_type_link.worker_id = worker_id appointment_type_link.date_when = when appointment_type_link.save() @@ -349,11 +373,13 @@ def events_persist(request): appointment.save() events_to_clear = json.loads(request.POST.get('events_to_clear')) for link_id in events_to_clear: - appointment_type_link = get_object_or_404(AppointmentTypeLink, pk=link_id) + appointment_type_link = get_object_or_404( + AppointmentTypeLink, pk=link_id) appointment_type_link.worker_id = None appointment_type_link.date_when = None appointment_type_link.save() - appointments_to_clear = json.loads(request.POST.get('appointments_to_clear')) + appointments_to_clear = json.loads( + request.POST.get('appointments_to_clear')) for appointment_id in appointments_to_clear: appointment = get_object_or_404(Appointment, pk=appointment_id) appointment.worker_assigned_id = None diff --git a/smash/web/api_views/worker.py b/smash/web/api_views/worker.py index 836fa83d720fdcee65b532d1727f2621682d5608..afc5e6c7b1a18eb025b52ee67cb00bff18c6e46a 100644 --- a/smash/web/api_views/worker.py +++ b/smash/web/api_views/worker.py @@ -35,6 +35,7 @@ def workers_for_daily_planning(request): role = unicode(worker.roles.filter(study_id=GLOBAL_STUDY_ID)[0].role) worker_dict_for_json = { 'id': worker.id, + 'flags' : [language.image.url for language in worker.languages.all()], 'availability': worker.availability_percentage(start_date=start_date), 'title': u"{} ({})".format(unicode(worker), role[:1].upper()), 'role': role diff --git a/smash/web/static/js/daily_planning.js b/smash/web/static/js/daily_planning.js index 9ae40d26537594521ff1412d448d72862255ad31..4241e1e233f25a53a8ae26e4ccd353452323c334 100644 --- a/smash/web/static/js/daily_planning.js +++ b/smash/web/static/js/daily_planning.js @@ -125,7 +125,25 @@ function get_subjects_events(day) { var boxBody = $("<div class='box-body' id='subject_" + subject.id + "'>"); var boxHeader = $("<div class='box-header with-border'/>"); - var title_subject = $(`<h4>${subject.name} (${subject.start}) <span style='float:right;padding-right:5px;'>${subject.location}</span> <span style='float:left;padding-right:5px;'><a title="Edit appointment" target="_blank" href="/appointments/edit/${subject.appointment_id}"><i class="fa fa-pencil-square"></i></a></span></h4>`); + //flags + var flagTooltip = ''; + if('flags' in subject){ + for(i=0; i<subject.flags.length; i++){ + var url = subject.flags[i]; + flagTooltip+= `<img class='tooltip_image' src='${url}'/>`; + } + var title_subject = `<h4 class="subject_title" data-toggle="tooltip" data-html="true" title="${flagTooltip}">${subject.name} (${subject.start})</h4>`; + }else{ + var title_subject = `<h4 class="subject_title">${subject.name} (${subject.start})</h4>`; //wrap content + } + + title_subject = $(`${title_subject} + <span style='padding-right:5px;'>${subject.location}</span> + <span style='padding-right:5px;'> + <a title="Edit appointment" target="_blank" href="/appointments/edit/${subject.appointment_id}"> + <i class="fa fa-pencil-square"></i> + </a> + </span>`); boxHeader.append(title_subject); title_subject.find('a[title]').tooltip(); $.each(subject.events, function (index_event, event) { @@ -160,12 +178,17 @@ function get_subjects_events(day) { }); }); var location_based_general_appointments = data.generic; - $.each(location_based_general_appointments, function (index, location) { + $.each(location_based_general_appointments, function (index, location) { //Subject Appointment List var divSubject = $("<div class='col-md-4 col-lg-3 col-sm-12 subjects-events'/>"); var boxSubject = $("<div class='box box-primary'/>").css('border-top-color', location.color); var boxBody = $("<div class='box-body' id='location_" + location.id + "'>"); var boxHeader = $("<div class='box-header with-border'/>"); - var title_location = $(`<h4>${location.name}<span style='float:right;padding-right:5px;'>${location.location}</span><span style='float:left;padding-right:5px;'><a title="Edit appointment" target="_blank" href="/appointments/edit/${location.id}"><i class="fa fa-pencil-square"></i></a></span></h4>`); + var title_location = $(`<h4>${location.name}<span style='padding-right:5px;'>${location.location}</span></h4> + <span style='padding-right:5px;'> + <a title="Edit Appointment" target="_blank" href="/appointments/edit/${location.id}"> + <i class="fa fa-pencil-square"></i> + </a> + </span>`); boxHeader.append(title_location); title_location.find('a[title]').tooltip(); $.each(location.events, function (index_event, event) { @@ -371,6 +394,31 @@ $(document).ready(function () { resourceLabelText: 'Workers', refetchResourcesOnNavigate: true, resourceOrder: '-availability', + resourceRender: function(resourceObj, labelTds, bodyTds) { //Calendar Columns + var title = $(labelTds).text() //get content + $(labelTds).text('') //empty content + + var flagTooltip = ''; + if('flags' in resourceObj){ + for(i=0; i<resourceObj.flags.length; i++){ + var url = resourceObj.flags[i]; + flagTooltip+= `<img class='tooltip_image' src='${url}'/>`; + } + $(labelTds).prepend(`<span class="column_title" data-toggle="tooltip" data-html="true" title="${flagTooltip}">${title}</span>`) //wrap content + }else{ + $(labelTds).prepend(`<span class="column_title">${title}</span>`) //wrap content + } + + //buttons + var worker_id = resourceObj.id; + var span = $(`<div style='display:block;'></div>`); + var add_extra_availability_link = `<a style="padding-right:5px;" title="Add Extra Availability or Holiday" target="_blank" href="/doctors/${worker_id}/holiday/add"><i class="fa fa-plus-square-o" aria-hidden="true"></i></a>`; + var edit_worker_link = `<a style="padding-right:5px;" title="Edit Worker" target="_blank" href="/doctors/edit/${worker_id}"><i class="fa fa-pencil-square" aria-hidden="true"></i></a>`; + $(span).append($(add_extra_availability_link)); + $(span).append($(edit_worker_link)); + $(labelTds).append(span); + $(labelTds).find('a[title]').tooltip(); + }, resources: function(callback){ setTimeout(function(){ var view = $('#calendar').fullCalendar('getView'); @@ -382,6 +430,7 @@ $(document).ready(function () { start_date: view.start.format('YYYY-MM-DD'), } }).then(function(resources){ + //Filter out roles var checked_roles = $('.role_list_item > input:checked').map( (i,e) => e.value).toArray(); resources = resources.filter(resource => checked_roles.includes(resource.role)); callback(resources) @@ -432,21 +481,6 @@ $(document).ready(function () { resizeCalendarColumns(); }; - //ADD EDIT BUTTONS - $('.fc-resource-cell').not('.anchored').each(function(resourceColumn){ - $(this).addClass('anchored'); - var worker_id = $(this).data('resource-id'); - var span = $(`<div style='display:block;'></div>`); - var add_extra_availability_link = `<a style="padding-right:5px;" title="Add Extra Availability or Holiday" target="_blank" href="/doctors/${worker_id}/holiday/add"><i class="fa fa-plus-square-o" aria-hidden="true"></i></a>`; - var edit_worker_link = `<a style="padding-right:5px;" title="Edit Worker" target="_blank" href="/doctors/edit/${worker_id}"><i class="fa fa-pencil-square" aria-hidden="true"></i></a>`; - - $(span).append($(add_extra_availability_link)); - $(span).append($(edit_worker_link)); - $(this).append(span); - //$(this).css('padding-left', span.width()); - $(this).find('a[title]').tooltip(); - }); - } }); }) diff --git a/smash/web/templates/daily_planning.html b/smash/web/templates/daily_planning.html index c28fb0192e2eaf4f53d787b3ba30d111ec7b6c8b..63b552701589c09f7ce5b04d23c9e827277bc131 100644 --- a/smash/web/templates/daily_planning.html +++ b/smash/web/templates/daily_planning.html @@ -26,6 +26,17 @@ list-style: none; list-style-type:none; } + .tooltip_image{ + width: 20px !important; + margin: 5px; + } + .subject_title{ + padding: 0 !important; + } + .subject_title + .tooltip > .tooltip-inner, .column_title + .tooltip > .tooltip-inner { + background-color: #fafafa; + border: solid 1px #ccc; + } </style> {% include "includes/datepicker.css.html" %} {% endblock styles %}