|
11 | 11 | import io |
12 | 12 | import itertools |
13 | 13 | import json |
| 14 | +import math |
14 | 15 | import os |
15 | 16 | import pytz |
16 | 17 | import re |
@@ -456,14 +457,8 @@ def edit_meeting_schedule(request, num=None, owner=None, name=None): |
456 | 457 |
|
457 | 458 | assignments = get_all_assignments_from_schedule(schedule) |
458 | 459 |
|
459 | | - # FIXME |
460 | | - #areas = get_areas() |
461 | | - #ads = find_ads_for_meeting(meeting) |
462 | | - |
463 | | - css_ems_per_hour = 1.5 |
464 | | - |
465 | 460 | rooms = meeting.room_set.filter(session_types__slug='regular').distinct().order_by("capacity") |
466 | | - timeslots_qs = meeting.timeslot_set.filter(type='regular').prefetch_related('type', 'sessions').order_by('time', 'location', 'name') |
| 461 | + timeslots_qs = meeting.timeslot_set.filter(type='regular').prefetch_related('type', 'sessions').order_by('location', 'time', 'name') |
467 | 462 |
|
468 | 463 | sessions = add_event_info_to_session_qs( |
469 | 464 | Session.objects.filter( |
@@ -511,25 +506,106 @@ def edit_meeting_schedule(request, num=None, owner=None, name=None): |
511 | 506 | for a in assignments: |
512 | 507 | assignments_by_session[a.session_id].append(a) |
513 | 508 |
|
514 | | - # prepare timeslot matrix |
515 | | - times = {} # start time -> end time |
516 | | - timeslots = {} |
517 | | - timeslots_by_pk = {} |
518 | | - for ts in timeslots_qs: |
519 | | - ts_end_time = ts.end_time() |
520 | | - if ts_end_time < times.get(ts.time, datetime.datetime.max): |
521 | | - times[ts.time] = ts_end_time |
| 509 | + # Prepare timeslot layout. We arrange time slots in columns per |
| 510 | + # room where everything inside is grouped by day. Things inside |
| 511 | + # the days are then layouted proportionally to the actual time of |
| 512 | + # day, to ensure that everything lines up, even if the time slots |
| 513 | + # are not the same in the different rooms. |
522 | 514 |
|
523 | | - timeslots[(ts.location_id, ts.time)] = ts |
524 | | - timeslots_by_pk[ts.pk] = ts |
525 | | - ts.session_assignments = [] |
| 515 | + def timedelta_to_css_ems(timedelta): |
| 516 | + css_ems_per_hour = 1.8 |
| 517 | + return timedelta.seconds / 60.0 / 60.0 * css_ems_per_hour |
| 518 | + |
| 519 | + # time labels column |
| 520 | + timeslots_by_day = defaultdict(list) |
| 521 | + for t in timeslots_qs: |
| 522 | + timeslots_by_day[t.time.date()].append(t) |
| 523 | + |
| 524 | + day_min_max = [] |
| 525 | + for day, timeslots in sorted(timeslots_by_day.iteritems()): |
| 526 | + day_min_max.append((day, min(t.time for t in timeslots), max(t.end_time() for t in timeslots))) |
| 527 | + |
| 528 | + time_labels = [] |
| 529 | + for day, day_min_time, day_max_time in day_min_max: |
| 530 | + day_labels = [] |
| 531 | + |
| 532 | + hourly_delta = 2 |
| 533 | + |
| 534 | + first_hour = int(math.ceil((day_min_time.hour + day_min_time.minute / 60.0) / hourly_delta) * hourly_delta) |
| 535 | + t = day_min_time.replace(hour=first_hour, minute=0, second=0, microsecond=0) |
| 536 | + |
| 537 | + last_hour = int(math.floor((day_max_time.hour + day_max_time.minute / 60.0) / hourly_delta) * hourly_delta) |
| 538 | + end = day_max_time.replace(hour=last_hour, minute=0, second=0, microsecond=0) |
526 | 539 |
|
527 | | - timeslot_matrix = [ |
528 | | - (start_time, end_time, (end_time - start_time).seconds / 60.0 / 60.0 * css_ems_per_hour, [(r, timeslots.get((r.pk, start_time))) for r in rooms]) |
529 | | - for start_time, end_time in sorted(times.items()) |
530 | | - ] |
| 540 | + while t <= end: |
| 541 | + day_labels.append((t, 'top', timedelta_to_css_ems(t - day_min_time), 'left')) |
| 542 | + t += datetime.timedelta(seconds=hourly_delta * 60 * 60) |
| 543 | + |
| 544 | + if not day_labels: |
| 545 | + day_labels.append((day_min_time, 'top', 0, 'left')) |
| 546 | + |
| 547 | + time_labels.append({ |
| 548 | + 'day': day, |
| 549 | + 'height': timedelta_to_css_ems(day_max_time - day_min_time), |
| 550 | + 'labels': day_labels, |
| 551 | + }) |
| 552 | + |
| 553 | + # room columns |
| 554 | + timeslots_by_room_and_day = defaultdict(list) |
| 555 | + for t in timeslots_qs: |
| 556 | + timeslots_by_room_and_day[(t.location_id, t.time.date())].append(t) |
| 557 | + |
| 558 | + room_columns = [] |
| 559 | + for r in rooms: |
| 560 | + room_days = [] |
| 561 | + |
| 562 | + for day, day_min_time, day_max_time in day_min_max: |
| 563 | + day_timeslots = [] |
| 564 | + for t in timeslots_by_room_and_day.get((r.pk, day), []): |
| 565 | + day_timeslots.append({ |
| 566 | + 'timeslot': t, |
| 567 | + 'offset': timedelta_to_css_ems(t.time - day_min_time), |
| 568 | + 'height': timedelta_to_css_ems(t.end_time() - t.time), |
| 569 | + }) |
| 570 | + |
| 571 | + room_days.append({ |
| 572 | + 'day': day, |
| 573 | + 'timeslots': day_timeslots, |
| 574 | + 'height': timedelta_to_css_ems(day_max_time - day_min_time), |
| 575 | + }) |
| 576 | + |
| 577 | + if any(d['timeslots'] for d in room_days): |
| 578 | + room_columns.append({ |
| 579 | + 'room': r, |
| 580 | + 'days': room_days, |
| 581 | + }) |
531 | 582 |
|
532 | 583 | # prepare sessions |
| 584 | + for ts in timeslots_qs: |
| 585 | + ts.session_assignments = [] |
| 586 | + timeslots_by_pk = {ts.pk: ts for ts in timeslots_qs} |
| 587 | + |
| 588 | + def cubehelix(i, total, hue=1.2, start_angle=0.5): |
| 589 | + # https://arxiv.org/pdf/1108.5083.pdf |
| 590 | + rotations = total // 4 |
| 591 | + x = float(i + 1) / (total + 1) |
| 592 | + phi = 2 * math.pi * (start_angle / 3 + rotations * x) |
| 593 | + a = hue * x * (1 - x) / 2.0 |
| 594 | + |
| 595 | + return ( |
| 596 | + max(0, min(x + a * (-0.14861 * math.cos(phi) + 1.78277 * math.sin(phi)), 1)), |
| 597 | + max(0, min(x + a * (-0.29227 * math.cos(phi) + -0.90649 * math.sin(phi)), 1)), |
| 598 | + max(0, min(x + a * (1.97294 * math.cos(phi)), 1)), |
| 599 | + ) |
| 600 | + |
| 601 | + session_parents = sorted(set( |
| 602 | + s.group.parent for s in sessions |
| 603 | + if s.group and s.group.parent and s.group.parent.type_id == 'area' or s.group.parent.acronym == 'irtf' |
| 604 | + ), key=lambda p: p.acronym) |
| 605 | + for i, p in enumerate(session_parents): |
| 606 | + rgb_color = cubehelix(i, len(session_parents)) |
| 607 | + p.scheduling_color = "#" + "".join(chr(int(round(x * 255))).encode('hex') for x in rgb_color) |
| 608 | + |
533 | 609 | unassigned_sessions = [] |
534 | 610 | session_data = [] |
535 | 611 | for s in sessions: |
@@ -573,7 +649,8 @@ def edit_meeting_schedule(request, num=None, owner=None, name=None): |
573 | 649 | d['comments'] = s.comments |
574 | 650 |
|
575 | 651 | s.requested_duration_in_hours = s.requested_duration.seconds / 60.0 / 60.0 |
576 | | - s.scheduling_height = s.requested_duration_in_hours * css_ems_per_hour |
| 652 | + s.layout_height = timedelta_to_css_ems(s.requested_duration) |
| 653 | + s.parent_acronym = s.group.parent.acronym if s.group and s.group.parent else "" |
577 | 654 |
|
578 | 655 | scheduled = False |
579 | 656 |
|
@@ -602,11 +679,11 @@ def edit_meeting_schedule(request, num=None, owner=None, name=None): |
602 | 679 | 'schedule': schedule, |
603 | 680 | 'can_edit': can_edit, |
604 | 681 | 'schedule_data': json.dumps(schedule_data, indent=2), |
| 682 | + 'time_labels': time_labels, |
605 | 683 | 'rooms': rooms, |
606 | | - 'timeslot_matrix': timeslot_matrix, |
| 684 | + 'room_columns': room_columns, |
607 | 685 | 'unassigned_sessions': unassigned_sessions, |
608 | | - 'timeslot_width': (100.0 - 10) / len(rooms), |
609 | | - #'areas': areas, |
| 686 | + 'session_parents': session_parents, |
610 | 687 | 'hide_menu': True, |
611 | 688 | }) |
612 | 689 |
|
|
0 commit comments