|
| 1 | +from datetime import date, time |
| 2 | + |
1 | 3 | from schedule.tests.factories import ( |
| 4 | + DayFactory, |
2 | 5 | ScheduleItemAdditionalSpeakerFactory, |
3 | 6 | ScheduleItemFactory, |
| 7 | + SlotFactory, |
4 | 8 | ) |
5 | 9 | from submissions.tests.factories import SubmissionFactory |
6 | 10 | from conferences.tests.factories import ( |
@@ -645,6 +649,130 @@ def test_video_uploaded_path_matcher( |
645 | 649 | ) |
646 | 650 |
|
647 | 651 |
|
| 652 | +def test_video_uploaded_path_matcher_with_day_signal(rf, mocker): |
| 653 | + conference = ConferenceFactory(code="pycon2026") |
| 654 | + # workshops only on the first day: videos are numbered by talk day, |
| 655 | + # so D1 in the uploaded files is the second conference day |
| 656 | + workshops_day = DayFactory(conference=conference, day=date(2026, 5, 27)) |
| 657 | + day_1 = DayFactory(conference=conference, day=date(2026, 5, 28)) |
| 658 | + day_2 = DayFactory(conference=conference, day=date(2026, 5, 29)) |
| 659 | + day_3 = DayFactory(conference=conference, day=date(2026, 5, 30)) |
| 660 | + |
| 661 | + marco = UserFactory(name="Marco", full_name="Marco Santoni") |
| 662 | + igor = UserFactory(name="Igor", full_name="Igor Saggese") |
| 663 | + marc_andre = UserFactory(name="Marc-André", full_name="Marc-André Lemburg") |
| 664 | + trainer = UserFactory(name="Trainer", full_name="Some Trainer") |
| 665 | + |
| 666 | + mocker.patch( |
| 667 | + "conferences.admin.conference.walk_conference_videos_folder", |
| 668 | + return_value=[ |
| 669 | + "conference-videos/pycon2026/SPAGHETTI/D1/02 - igor-saggese.mp4", |
| 670 | + "conference-videos/pycon2026/SPAGHETTI/D2/03 - igor-saggese.mp4", |
| 671 | + "conference-videos/pycon2026/TORTELLINI/D1/05 - lightning_talks_day_1.mp4", |
| 672 | + "conference-videos/pycon2026/TORTELLINI/D2/04 - lightning_talks_day_2.mp4", |
| 673 | + "conference-videos/pycon2026/TORTELLINI/D3/05 - lightning_talks_day_3.mp4", |
| 674 | + "conference-videos/pycon2026/TORTELLINI/D3/01 - marc-andre-lemburg.mp4", |
| 675 | + ], |
| 676 | + ) |
| 677 | + |
| 678 | + def slot_for(day): |
| 679 | + return SlotFactory(day=day, hour=time(10, 0), duration=45) |
| 680 | + |
| 681 | + workshop_event = ScheduleItemFactory( |
| 682 | + conference=conference, |
| 683 | + title="Workshop", |
| 684 | + type=ScheduleItem.TYPES.training, |
| 685 | + submission__speaker=trainer, |
| 686 | + slot=slot_for(workshops_day), |
| 687 | + ) |
| 688 | + |
| 689 | + multi_speaker_event = ScheduleItemFactory( |
| 690 | + conference=conference, |
| 691 | + title="Talk with two speakers", |
| 692 | + type=ScheduleItem.TYPES.talk, |
| 693 | + submission__speaker=marco, |
| 694 | + slot=slot_for(day_1), |
| 695 | + ) |
| 696 | + ScheduleItemAdditionalSpeakerFactory(scheduleitem=multi_speaker_event, user=igor) |
| 697 | + |
| 698 | + igor_solo_event = ScheduleItemFactory( |
| 699 | + conference=conference, |
| 700 | + title="Igor solo talk", |
| 701 | + type=ScheduleItem.TYPES.talk, |
| 702 | + submission__speaker=igor, |
| 703 | + slot=slot_for(day_2), |
| 704 | + ) |
| 705 | + |
| 706 | + lightning_talks_events = [ |
| 707 | + ScheduleItemFactory( |
| 708 | + conference=conference, |
| 709 | + title="Lightning Talks", |
| 710 | + type=ScheduleItem.TYPES.custom, |
| 711 | + submission=None, |
| 712 | + slot=slot_for(day), |
| 713 | + ) |
| 714 | + for day in (day_1, day_2, day_3) |
| 715 | + ] |
| 716 | + |
| 717 | + marc_andre_event = ScheduleItemFactory( |
| 718 | + conference=conference, |
| 719 | + title="Accented speaker talk", |
| 720 | + type=ScheduleItem.TYPES.talk, |
| 721 | + submission__speaker=marc_andre, |
| 722 | + slot=slot_for(day_3), |
| 723 | + ) |
| 724 | + |
| 725 | + admin = ConferenceAdmin( |
| 726 | + model=conference.__class__, |
| 727 | + admin_site=AdminSite(), |
| 728 | + ) |
| 729 | + admin.message_user = mocker.Mock() |
| 730 | + |
| 731 | + request = rf.post("/", data={"run_matcher": "1"}) |
| 732 | + ret = admin.map_videos(request, conference.id) |
| 733 | + |
| 734 | + assert ret.status_code == 302 |
| 735 | + |
| 736 | + workshop_event.refresh_from_db() |
| 737 | + multi_speaker_event.refresh_from_db() |
| 738 | + igor_solo_event.refresh_from_db() |
| 739 | + marc_andre_event.refresh_from_db() |
| 740 | + for event in lightning_talks_events: |
| 741 | + event.refresh_from_db() |
| 742 | + |
| 743 | + assert workshop_event.video_uploaded_path == "" |
| 744 | + # the file is named after only one of the two speakers, |
| 745 | + # the day disambiguates it from igor's solo talk |
| 746 | + assert ( |
| 747 | + multi_speaker_event.video_uploaded_path |
| 748 | + == "conference-videos/pycon2026/SPAGHETTI/D1/02 - igor-saggese.mp4" |
| 749 | + ) |
| 750 | + assert ( |
| 751 | + igor_solo_event.video_uploaded_path |
| 752 | + == "conference-videos/pycon2026/SPAGHETTI/D2/03 - igor-saggese.mp4" |
| 753 | + ) |
| 754 | + assert ( |
| 755 | + lightning_talks_events[0].video_uploaded_path |
| 756 | + == "conference-videos/pycon2026/TORTELLINI/D1/05 - lightning_talks_day_1.mp4" |
| 757 | + ) |
| 758 | + assert ( |
| 759 | + lightning_talks_events[1].video_uploaded_path |
| 760 | + == "conference-videos/pycon2026/TORTELLINI/D2/04 - lightning_talks_day_2.mp4" |
| 761 | + ) |
| 762 | + assert ( |
| 763 | + lightning_talks_events[2].video_uploaded_path |
| 764 | + == "conference-videos/pycon2026/TORTELLINI/D3/05 - lightning_talks_day_3.mp4" |
| 765 | + ) |
| 766 | + assert ( |
| 767 | + marc_andre_event.video_uploaded_path |
| 768 | + == "conference-videos/pycon2026/TORTELLINI/D3/01 - marc-andre-lemburg.mp4" |
| 769 | + ) |
| 770 | + |
| 771 | + assert admin.message_user.mock_calls[0].args[1] == "Matched 6 videos to events." |
| 772 | + # every file was matched exactly once: no reuse or unused warnings |
| 773 | + assert admin.message_user.call_count == 1 |
| 774 | + |
| 775 | + |
648 | 776 | def test_storage_walk_conference_videos_folder(mocker): |
649 | 777 | mock_storage = mocker.Mock() |
650 | 778 | mock_storage.listdir.side_effect = [ |
|
0 commit comments