@@ -469,13 +469,13 @@ def test_filter_events_calendar(
469
469
"by_day" : ["MO" , "FR" ],
470
470
"schedule" : schedule ,
471
471
}
472
-
473
472
on_call_shift = make_on_call_shift (
474
473
organization = organization , shift_type = CustomOnCallShift .TYPE_RECURRENT_EVENT , ** data
475
474
)
476
475
on_call_shift .users .add (user )
477
476
478
477
url = reverse ("api-internal:schedule-filter-events" , kwargs = {"pk" : schedule .public_primary_key })
478
+ url += "?type=rotation"
479
479
response = client .get (url , format = "json" , ** make_user_auth_headers (user , token ))
480
480
481
481
# current week events are expected
@@ -525,6 +525,7 @@ def test_filter_events_calendar(
525
525
@pytest .mark .django_db
526
526
def test_filter_events_range_calendar (
527
527
make_organization_and_user_with_plugin_token ,
528
+ make_user_for_organization ,
528
529
make_user_auth_headers ,
529
530
make_schedule ,
530
531
make_on_call_shift ,
@@ -540,6 +541,9 @@ def test_filter_events_range_calendar(
540
541
541
542
now = timezone .now ().replace (microsecond = 0 )
542
543
start_date = now - timezone .timedelta (days = 7 )
544
+ mon_start = now - timezone .timedelta (days = start_date .weekday ())
545
+ request_date = mon_start + timezone .timedelta (days = 2 )
546
+
543
547
data = {
544
548
"start" : start_date ,
545
549
"rotation_start" : start_date ,
@@ -549,17 +553,27 @@ def test_filter_events_range_calendar(
549
553
"by_day" : ["MO" , "FR" ],
550
554
"schedule" : schedule ,
551
555
}
552
-
553
556
on_call_shift = make_on_call_shift (
554
557
organization = organization , shift_type = CustomOnCallShift .TYPE_RECURRENT_EVENT , ** data
555
558
)
556
559
on_call_shift .users .add (user )
557
560
558
- mon_start = now - timezone .timedelta (days = start_date .weekday ())
559
- request_date = mon_start + timezone .timedelta (days = 2 )
561
+ # add override shift
562
+ override_start = request_date + timezone .timedelta (seconds = 3600 )
563
+ override_data = {
564
+ "start" : override_start ,
565
+ "rotation_start" : override_start ,
566
+ "duration" : timezone .timedelta (seconds = 3600 ),
567
+ "schedule" : schedule ,
568
+ }
569
+ override = make_on_call_shift (
570
+ organization = organization , shift_type = CustomOnCallShift .TYPE_OVERRIDE , ** override_data
571
+ )
572
+ other_user = make_user_for_organization (organization )
573
+ override .users .add (other_user )
560
574
561
575
url = reverse ("api-internal:schedule-filter-events" , kwargs = {"pk" : schedule .public_primary_key })
562
- url += "?date={}&days=3" .format (request_date .strftime ("%Y-%m-%d" ))
576
+ url += "?date={}&days=3&type=rotation " .format (request_date .strftime ("%Y-%m-%d" ))
563
577
response = client .get (url , format = "json" , ** make_user_auth_headers (user , token ))
564
578
565
579
# only friday occurrence is expected
@@ -590,6 +604,215 @@ def test_filter_events_range_calendar(
590
604
assert response .data == expected_result
591
605
592
606
607
+ @pytest .mark .django_db
608
+ def test_filter_events_overrides (
609
+ make_organization_and_user_with_plugin_token ,
610
+ make_user_for_organization ,
611
+ make_user_auth_headers ,
612
+ make_schedule ,
613
+ make_on_call_shift ,
614
+ ):
615
+ organization , user , token = make_organization_and_user_with_plugin_token ()
616
+ client = APIClient ()
617
+
618
+ schedule = make_schedule (
619
+ organization ,
620
+ schedule_class = OnCallScheduleWeb ,
621
+ name = "test_web_schedule" ,
622
+ )
623
+
624
+ now = timezone .now ().replace (microsecond = 0 )
625
+ start_date = now - timezone .timedelta (days = 7 )
626
+ mon_start = now - timezone .timedelta (days = start_date .weekday ())
627
+ request_date = mon_start + timezone .timedelta (days = 2 )
628
+
629
+ data = {
630
+ "start" : start_date ,
631
+ "rotation_start" : start_date ,
632
+ "duration" : timezone .timedelta (seconds = 7200 ),
633
+ "priority_level" : 1 ,
634
+ "frequency" : CustomOnCallShift .FREQUENCY_WEEKLY ,
635
+ "by_day" : ["MO" , "FR" ],
636
+ "schedule" : schedule ,
637
+ }
638
+ on_call_shift = make_on_call_shift (
639
+ organization = organization , shift_type = CustomOnCallShift .TYPE_RECURRENT_EVENT , ** data
640
+ )
641
+ on_call_shift .users .add (user )
642
+
643
+ # add override shift
644
+ override_start = request_date + timezone .timedelta (seconds = 3600 )
645
+ override_data = {
646
+ "start" : override_start ,
647
+ "rotation_start" : override_start ,
648
+ "duration" : timezone .timedelta (seconds = 3600 ),
649
+ "schedule" : schedule ,
650
+ }
651
+ override = make_on_call_shift (
652
+ organization = organization , shift_type = CustomOnCallShift .TYPE_OVERRIDE , ** override_data
653
+ )
654
+ other_user = make_user_for_organization (organization )
655
+ override .add_rolling_users ([[other_user ]])
656
+
657
+ url = reverse ("api-internal:schedule-filter-events" , kwargs = {"pk" : schedule .public_primary_key })
658
+ url += "?date={}&days=3&type=override" .format (request_date .strftime ("%Y-%m-%d" ))
659
+ response = client .get (url , format = "json" , ** make_user_auth_headers (user , token ))
660
+
661
+ # only override occurrence is expected
662
+ expected_result = {
663
+ "id" : schedule .public_primary_key ,
664
+ "name" : "test_web_schedule" ,
665
+ "type" : 2 ,
666
+ "events" : [
667
+ {
668
+ "all_day" : False ,
669
+ "start" : override_start ,
670
+ "end" : override_start + override .duration ,
671
+ "users" : [{"display_name" : other_user .username , "pk" : other_user .public_primary_key }],
672
+ "missing_users" : [],
673
+ "priority_level" : None ,
674
+ "source" : "api" ,
675
+ "calendar_type" : OnCallSchedule .OVERRIDES ,
676
+ "is_empty" : False ,
677
+ "is_gap" : False ,
678
+ "shift" : {
679
+ "pk" : override .public_primary_key ,
680
+ },
681
+ }
682
+ ],
683
+ }
684
+ assert response .status_code == status .HTTP_200_OK
685
+ assert response .data == expected_result
686
+
687
+
688
+ @pytest .mark .django_db
689
+ def test_filter_events_final_schedule (
690
+ make_organization_and_user_with_plugin_token ,
691
+ make_user_for_organization ,
692
+ make_user_auth_headers ,
693
+ make_schedule ,
694
+ make_on_call_shift ,
695
+ ):
696
+ organization , user , token = make_organization_and_user_with_plugin_token ()
697
+ client = APIClient ()
698
+
699
+ schedule = make_schedule (
700
+ organization ,
701
+ schedule_class = OnCallScheduleWeb ,
702
+ name = "test_web_schedule" ,
703
+ )
704
+
705
+ now = timezone .now ().replace (hour = 0 , minute = 0 , second = 0 , microsecond = 0 )
706
+ start_date = now - timezone .timedelta (days = 7 )
707
+ request_date = start_date
708
+
709
+ user_a , user_b , user_c , user_d , user_e = (make_user_for_organization (organization , username = i ) for i in "ABCDE" )
710
+
711
+ shifts = (
712
+ # user, priority, start time (h), duration (hs)
713
+ (user_a , 1 , 10 , 5 ), # r1-1: 10-15 / A
714
+ (user_b , 1 , 11 , 2 ), # r1-2: 11-13 / B
715
+ (user_a , 1 , 16 , 3 ), # r1-3: 16-19 / A
716
+ (user_a , 1 , 21 , 1 ), # r1-4: 21-22 / A
717
+ (user_b , 1 , 22 , 2 ), # r1-5: 22-00 / B
718
+ (user_c , 2 , 12 , 2 ), # r2-1: 12-14 / C
719
+ (user_d , 2 , 14 , 1 ), # r2-2: 14-15 / D
720
+ (user_d , 2 , 17 , 1 ), # r2-3: 17-18 / D
721
+ (user_d , 2 , 20 , 3 ), # r2-4: 20-23 / D
722
+ )
723
+ for user , priority , start_h , duration in shifts :
724
+ data = {
725
+ "start" : start_date + timezone .timedelta (hours = start_h ),
726
+ "rotation_start" : start_date ,
727
+ "duration" : timezone .timedelta (hours = duration ),
728
+ "priority_level" : priority ,
729
+ "frequency" : CustomOnCallShift .FREQUENCY_DAILY ,
730
+ "schedule" : schedule ,
731
+ }
732
+ on_call_shift = make_on_call_shift (
733
+ organization = organization , shift_type = CustomOnCallShift .TYPE_RECURRENT_EVENT , ** data
734
+ )
735
+ on_call_shift .users .add (user )
736
+
737
+ # override: 22-23 / E
738
+ override_data = {
739
+ "start" : start_date + timezone .timedelta (hours = 22 ),
740
+ "rotation_start" : start_date ,
741
+ "duration" : timezone .timedelta (hours = 1 ),
742
+ "schedule" : schedule ,
743
+ }
744
+ override = make_on_call_shift (
745
+ organization = organization , shift_type = CustomOnCallShift .TYPE_OVERRIDE , ** override_data
746
+ )
747
+ override .add_rolling_users ([[user_e ]])
748
+
749
+ url = reverse ("api-internal:schedule-filter-events" , kwargs = {"pk" : schedule .public_primary_key })
750
+ url += "?date={}&days=1" .format (request_date .strftime ("%Y-%m-%d" ))
751
+ response = client .get (url , format = "json" , ** make_user_auth_headers (user , token ))
752
+ assert response .status_code == status .HTTP_200_OK
753
+
754
+ expected = (
755
+ # start (h), duration (H), user, priority, is_gap, is_override
756
+ (0 , 10 , None , None , True , False ), # 0-10 gap
757
+ (10 , 2 , "A" , 1 , False , False ), # 10-12 A
758
+ (11 , 1 , "B" , 1 , False , False ), # 11-12 B
759
+ (12 , 2 , "C" , 2 , False , False ), # 12-14 C
760
+ (14 , 1 , "D" , 2 , False , False ), # 14-15 D
761
+ (15 , 1 , None , None , True , False ), # 15-16 gap
762
+ (16 , 1 , "A" , 1 , False , False ), # 16-17 A
763
+ (17 , 1 , "D" , 2 , False , False ), # 17-18 D
764
+ (18 , 1 , "A" , 1 , False , False ), # 18-19 A
765
+ (19 , 1 , None , None , True , False ), # 19-20 gap
766
+ (20 , 2 , "D" , 2 , False , False ), # 20-22 D
767
+ (22 , 1 , "E" , None , False , True ), # 22-23 E (override)
768
+ (23 , 1 , "B" , 1 , False , False ), # 23-00 B
769
+ )
770
+ expected_events = [
771
+ {
772
+ "calendar_type" : 1 if is_override else None if is_gap else 0 ,
773
+ "end" : start_date + timezone .timedelta (hours = start + duration ),
774
+ "is_gap" : is_gap ,
775
+ "priority_level" : priority ,
776
+ "start" : start_date + timezone .timedelta (hours = start , milliseconds = 1 if start == 0 else 0 ),
777
+ "user" : user ,
778
+ }
779
+ for start , duration , user , priority , is_gap , is_override in expected
780
+ ]
781
+ returned_events = [
782
+ {
783
+ "calendar_type" : e ["calendar_type" ],
784
+ "end" : e ["end" ],
785
+ "is_gap" : e ["is_gap" ],
786
+ "priority_level" : e ["priority_level" ],
787
+ "start" : e ["start" ],
788
+ "user" : e ["users" ][0 ]["display_name" ] if e ["users" ] else None ,
789
+ }
790
+ for e in response .data ["events" ]
791
+ ]
792
+ assert returned_events == expected_events
793
+
794
+
795
+ @pytest .mark .django_db
796
+ def test_filter_events_invalid_type (
797
+ make_organization_and_user_with_plugin_token ,
798
+ make_user_auth_headers ,
799
+ make_schedule ,
800
+ ):
801
+ organization , user , token = make_organization_and_user_with_plugin_token ()
802
+ client = APIClient ()
803
+
804
+ schedule = make_schedule (
805
+ organization ,
806
+ schedule_class = OnCallScheduleWeb ,
807
+ name = "test_web_schedule" ,
808
+ )
809
+
810
+ url = reverse ("api-internal:schedule-filter-events" , kwargs = {"pk" : schedule .public_primary_key })
811
+ url += "?type=invalid"
812
+ response = client .get (url , format = "json" , ** make_user_auth_headers (user , token ))
813
+ assert response .status_code == status .HTTP_400_BAD_REQUEST
814
+
815
+
593
816
@pytest .mark .django_db
594
817
@pytest .mark .parametrize (
595
818
"role,expected_status" ,
0 commit comments