From 64f8bfdb2d7bec61f8ef5b625b8465fc08d37cc0 Mon Sep 17 00:00:00 2001 From: RomainFayolle Date: Fri, 9 Sep 2022 08:58:36 -0400 Subject: [PATCH 1/4] Logic for room distribution --- retirement/models.py | 181 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 181 insertions(+) diff --git a/retirement/models.py b/retirement/models.py index 9cec2c28..5474351d 100644 --- a/retirement/models.py +++ b/retirement/models.py @@ -3,6 +3,7 @@ import json import traceback from datetime import timedelta +from operator import itemgetter import requests from django.conf import settings @@ -23,6 +24,8 @@ Membership, OrderLine, BaseProduct, + OrderLineBaseProduct, + OptionProduct, Coupon, Refund, ) @@ -671,6 +674,184 @@ def activate(self): self.is_active = True self.save() + @staticmethod + def _set_participant_room(participant_data, room_number): + participant_data['room_number'] = room_number + participant_data['placed'] = True + return participant_data + + def get_retreat_room_distribution(self): + """ + Generate room distribution for a retreat, matching people with their friends or their preferred gender. + Return a list of dict with data and room number for each participant + """ + active_reservations = self.reservations.filter(is_active=True) + room_pool = {} + single_pool = {} + friend_pool = {} + mixed_pool = {} + + current_man_room = None + current_woman_room = None + current_non_binary_room = None + current_mixte_room = None + + retreat_room_distribution = [] + room_number = 0 + + for reservation in active_reservations: + participant_data = { + 'first_name': reservation.user.first_name, + 'last_name': reservation.user.last_name, + 'email': reservation.user.email, + 'room_option': 'single', + 'gender_preference': 'NA', + 'share_with': 'NA', + 'room_number': 0, + 'placed': False + } + room_options = OptionProduct.objects.filter(is_room_option=True) + participant_order_detail = OrderLineBaseProduct.objects.get( + order_line=reservation.order_line, + option__in=room_options + ) + current_option = OptionProduct.objects.get(id=participant_order_detail.option_id) + + if current_option.type == OptionProduct.METADATA_SHARED_ROOM: + participant_data['gender_preference'] = participant_order_detail.metadata[ + 'share_with_preferred_gender'] + participant_data['room_option'] = 'shared' + if participant_order_detail.metadata['share_with_member']: + participant_data['share_with'] = participant_order_detail.metadata['share_with_member'] + friend_pool[reservation.user.email] = participant_data + continue + elif participant_data['gender_preference'] == 'mixte': + mixed_pool[reservation.user.email] = participant_data + continue + room_pool[reservation.user.email] = participant_data + else: + single_pool[reservation.user.email] = participant_data + + # Handling single pool + for key, value in single_pool.items(): + if not value['placed']: + room_number += 1 + retreat_room_distribution.append(self._set_participant_room(value, room_number)) + + # Handling friend pool + for key, value in friend_pool.items(): + if not value['placed']: + if value['share_with'] in friend_pool: + if friend_pool[value['share_with']]['share_with'] == key: + room_number += 1 + retreat_room_distribution.append(self._set_participant_room( + value, room_number)) + retreat_room_distribution.append(self._set_participant_room( + friend_pool[value['share_with']], room_number)) + continue + # Pairing not found, putting in other pool + if value['gender_preference'] == 'mixte': + mixed_pool[key] = value + else: + room_pool[key] = value + + # Handling main pool + for key, value in room_pool.items(): + if not value['placed']: + if value['gender_preference'] == 'man': + if current_man_room: + room_number += 1 + retreat_room_distribution.append(self._set_participant_room( + value, room_number)) + retreat_room_distribution.append(self._set_participant_room( + room_pool[current_man_room], room_number)) + current_man_room = None + else: + current_man_room = key + elif value['gender_preference'] == 'woman': + if current_woman_room: + room_number += 1 + retreat_room_distribution.append(self._set_participant_room( + value, room_number)) + retreat_room_distribution.append(self._set_participant_room( + room_pool[current_woman_room], room_number)) + current_woman_room = None + else: + current_woman_room = key + elif value['gender_preference'] == 'non-binary': + if current_non_binary_room: + room_number += 1 + retreat_room_distribution.append(self._set_participant_room( + value, room_number)) + retreat_room_distribution.append(self._set_participant_room( + room_pool[current_non_binary_room], room_number)) + current_non_binary_room = None + else: + current_non_binary_room = key + + # Handling shared rooms for mixte + for key, value in mixed_pool.items(): + # fill the rooms or create mixte room + if current_man_room: + room_number += 1 + retreat_room_distribution.append(self._set_participant_room( + value, room_number)) + retreat_room_distribution.append(self._set_participant_room( + room_pool[current_man_room], room_number)) + current_man_room = None + elif current_woman_room: + room_number += 1 + retreat_room_distribution.append(self._set_participant_room( + value, room_number)) + retreat_room_distribution.append(self._set_participant_room( + room_pool[current_woman_room], room_number)) + current_woman_room = None + elif current_non_binary_room: + room_number += 1 + retreat_room_distribution.append(self._set_participant_room( + value, room_number)) + retreat_room_distribution.append(self._set_participant_room( + room_pool[current_non_binary_room], room_number)) + current_non_binary_room = None + elif current_mixte_room: + room_number += 1 + retreat_room_distribution.append(self._set_participant_room( + value, room_number)) + retreat_room_distribution.append(self._set_participant_room( + mixed_pool[current_mixte_room], room_number)) + current_mixte_room = None + else: + current_mixte_room = key + + # Handling unpaired participants + if current_mixte_room: + room_number += 1 + retreat_room_distribution.append(self._set_participant_room( + mixed_pool[current_mixte_room], room_number)) + else: + if current_man_room and current_woman_room and current_non_binary_room: + room_number += 1 + retreat_room_distribution.append(self._set_participant_room( + room_pool[current_man_room], room_number)) + retreat_room_distribution.append(self._set_participant_room( + room_pool[current_woman_room], room_number)) + room_number += 1 + retreat_room_distribution.append(self._set_participant_room( + room_pool[current_non_binary_room], room_number)) + elif current_man_room or current_woman_room or current_non_binary_room: + room_number += 1 + if current_man_room: + retreat_room_distribution.append(self._set_participant_room( + room_pool[current_man_room], room_number)) + if current_woman_room: + retreat_room_distribution.append(self._set_participant_room( + room_pool[current_woman_room], room_number)) + if current_non_binary_room: + retreat_room_distribution.append(self._set_participant_room( + room_pool[current_non_binary_room], room_number)) + + return sorted(retreat_room_distribution, key=itemgetter('room_number')) + class RetreatDate(models.Model): From e14266c413f72dce1a416e845d453814dc090481 Mon Sep 17 00:00:00 2001 From: RomainFayolle Date: Fri, 9 Sep 2022 08:59:15 -0400 Subject: [PATCH 2/4] Export function and admin function Can export on several retreat at once --- blitz_api/models.py | 2 ++ retirement/admin.py | 15 +++++++++++-- retirement/exports.py | 49 +++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 64 insertions(+), 2 deletions(-) diff --git a/blitz_api/models.py b/blitz_api/models.py index db4a177b..6de8084c 100644 --- a/blitz_api/models.py +++ b/blitz_api/models.py @@ -680,6 +680,7 @@ class ExportMedia(models.Model): EXPORT_RETREAT_SALES = 'RETREAT SALES' EXPORT_RETREAT_PARTICIPATION = 'RETREAT PARTICIPATION' EXPORT_RETREAT_OPTIONS = 'RETREAT OPTIONS' + EXPORT_RETREAT_ROOM_DISTRIBUTION = 'RETREAT ROOM DISTRIBUTION' EXPORT_CHOICES = ( (EXPORT_ANONYMOUS_CHRONO_DATA, _('Anonymous Chrono data')), @@ -687,6 +688,7 @@ class ExportMedia(models.Model): (EXPORT_RETREAT_SALES, _('Retreat sales')), (EXPORT_RETREAT_PARTICIPATION, _('Retreat participation')), (EXPORT_RETREAT_OPTIONS, _('Retreat options')), + (EXPORT_RETREAT_ROOM_DISTRIBUTION, _('Retreat room distribution')), ) file = models.FileField( diff --git a/retirement/admin.py b/retirement/admin.py index cd602db3..f98755dc 100644 --- a/retirement/admin.py +++ b/retirement/admin.py @@ -33,7 +33,10 @@ WaitQueueResource ) -from .exports import generate_retreat_sales +from .exports import ( + generate_retreat_sales, + generate_retreat_room_distribution +) User = get_user_model() @@ -122,6 +125,13 @@ def export_retreat_sales(self, request, queryset): export_retreat_sales.short_description = 'export_retreat_sales' +def export_retreat_room_distribution(self, request, queryset): + generate_retreat_room_distribution.delay(request.user.id, queryset) + + +export_retreat_room_distribution.short_description = 'export_retreat_room_distribution' + + class RetreatAdmin(SimpleHistoryAdmin, ExportActionModelAdmin, SafeDeleteAdmin, @@ -156,7 +166,8 @@ class RetreatAdmin(SimpleHistoryAdmin, 'export_admin_action', make_reservation_not_refundable, make_reservation_refundable, - export_retreat_sales + export_retreat_sales, + export_retreat_room_distribution ] diff --git a/retirement/exports.py b/retirement/exports.py index ba65f908..64aea7d5 100644 --- a/retirement/exports.py +++ b/retirement/exports.py @@ -1,6 +1,8 @@ import csv import io +from celery import shared_task + from django.core.files.base import ContentFile from django.db.models import QuerySet, Sum from django.utils import timezone @@ -45,3 +47,50 @@ def generate_retreat_sales( ContentFile(output_stream.getvalue().encode())) return new_export + + +@shared_task() +def generate_retreat_room_distribution( + admin_id, + queryset: QuerySet +): + """ + For given retreats, generate a csv file for room distribution + :params admin_id: id of admin doing the request + :params queryset: django queryset of retreat objects + """ + + for retreat in queryset: + output_stream = io.StringIO() + writer = csv.writer(output_stream) + + header = [ + 'Nom', 'Prénom', 'email', 'Option de chambre', 'Préférence de genre', + 'Souhaite partager avec', 'Numéro de chambre' + ] + + writer.writerow(header) + participants_distribution_data = retreat.get_retreat_room_distribution() + + for p in participants_distribution_data: + line_array = [None] * 7 + line_array[0] = p['last_name'] + line_array[1] = p['first_name'] + line_array[2] = p['email'] + line_array[3] = p['room_option'] + line_array[4] = p['gender_preference'] + line_array[5] = p['share_with'] + line_array[6] = p['room_number'] + + writer.writerow(line_array) + + file_name = f'export_{retreat.name}_retreat_room_distribution.csv' + new_export = ExportMedia.objects.create( + name=file_name, + author_id=admin_id, + type=ExportMedia.EXPORT_RETREAT_ROOM_DISTRIBUTION + ) + new_export.file.save( + file_name, + ContentFile(output_stream.getvalue().encode())) + new_export.send_confirmation_email() From 8b099c353ef69b0853bc52d2a15248d337737cd8 Mon Sep 17 00:00:00 2001 From: RomainFayolle Date: Fri, 9 Sep 2022 08:59:26 -0400 Subject: [PATCH 3/4] switch loop on admin instead of in task --- blitz_api/factories.py | 36 ++- retirement/admin.py | 3 +- retirement/exports.py | 71 +++--- .../test_export_retreat_room_distribution.py | 90 +++++++ retirement/tests/tests_model_Retreat.py | 240 +++++++++++++++++- 5 files changed, 396 insertions(+), 44 deletions(-) create mode 100644 retirement/tests/test_export_retreat_room_distribution.py diff --git a/blitz_api/factories.py b/blitz_api/factories.py index 7563580b..b7d4cfb2 100644 --- a/blitz_api/factories.py +++ b/blitz_api/factories.py @@ -11,8 +11,18 @@ from blitz_api.models import Organization, AcademicLevel, AcademicField, \ Address -from retirement.models import Retreat, RetreatDate, RetreatType -from store.models import Order, OptionProduct +from retirement.models import ( + Retreat, + RetreatDate, + RetreatType, + Reservation, +) +from store.models import ( + Order, + OrderLine, + OptionProduct, + OrderLineBaseProduct +) from faker import Faker User = get_user_model() @@ -130,6 +140,14 @@ class Meta: settlement_id = 1 +class ReservationFactory(DjangoModelFactory): + + class Meta: + model = Reservation + + is_active = True + + class OptionProductFactory(DjangoModelFactory): class Meta: model = OptionProduct @@ -139,3 +157,17 @@ class Meta: available = True price = 50 max_quantity = 100 + + +class OrderLineFactory(DjangoModelFactory): + class Meta: + model = OrderLine + + quantity = 1 + + +class OrderLineBaseProductFactory(DjangoModelFactory): + class Meta: + model = OrderLineBaseProduct + + quantity = 1 diff --git a/retirement/admin.py b/retirement/admin.py index f98755dc..45b7dec6 100644 --- a/retirement/admin.py +++ b/retirement/admin.py @@ -126,7 +126,8 @@ def export_retreat_sales(self, request, queryset): def export_retreat_room_distribution(self, request, queryset): - generate_retreat_room_distribution.delay(request.user.id, queryset) + for retreat in queryset: + generate_retreat_room_distribution.delay(request.user.id, retreat) export_retreat_room_distribution.short_description = 'export_retreat_room_distribution' diff --git a/retirement/exports.py b/retirement/exports.py index 64aea7d5..2e24af1b 100644 --- a/retirement/exports.py +++ b/retirement/exports.py @@ -52,45 +52,44 @@ def generate_retreat_sales( @shared_task() def generate_retreat_room_distribution( admin_id, - queryset: QuerySet + retreat ): """ For given retreats, generate a csv file for room distribution :params admin_id: id of admin doing the request - :params queryset: django queryset of retreat objects + :params retreat: django retreat object """ - for retreat in queryset: - output_stream = io.StringIO() - writer = csv.writer(output_stream) - - header = [ - 'Nom', 'Prénom', 'email', 'Option de chambre', 'Préférence de genre', - 'Souhaite partager avec', 'Numéro de chambre' - ] - - writer.writerow(header) - participants_distribution_data = retreat.get_retreat_room_distribution() - - for p in participants_distribution_data: - line_array = [None] * 7 - line_array[0] = p['last_name'] - line_array[1] = p['first_name'] - line_array[2] = p['email'] - line_array[3] = p['room_option'] - line_array[4] = p['gender_preference'] - line_array[5] = p['share_with'] - line_array[6] = p['room_number'] - - writer.writerow(line_array) - - file_name = f'export_{retreat.name}_retreat_room_distribution.csv' - new_export = ExportMedia.objects.create( - name=file_name, - author_id=admin_id, - type=ExportMedia.EXPORT_RETREAT_ROOM_DISTRIBUTION - ) - new_export.file.save( - file_name, - ContentFile(output_stream.getvalue().encode())) - new_export.send_confirmation_email() + output_stream = io.StringIO() + writer = csv.writer(output_stream) + + header = [ + 'Nom', 'Prénom', 'email', 'Option de chambre', 'Préférence de genre', + 'Souhaite partager avec', 'Numéro de chambre' + ] + + writer.writerow(header) + participants_distribution_data = retreat.get_retreat_room_distribution() + + for p in participants_distribution_data: + line_array = [None] * 7 + line_array[0] = p['last_name'] + line_array[1] = p['first_name'] + line_array[2] = p['email'] + line_array[3] = p['room_option'] + line_array[4] = p['gender_preference'] + line_array[5] = p['share_with'] + line_array[6] = p['room_number'] + + writer.writerow(line_array) + + file_name = f'export_{retreat.name}_retreat_room_distribution.csv' + new_export = ExportMedia.objects.create( + name=file_name, + author_id=admin_id, + type=ExportMedia.EXPORT_RETREAT_ROOM_DISTRIBUTION + ) + new_export.file.save( + file_name, + ContentFile(output_stream.getvalue().encode())) + new_export.send_confirmation_email() diff --git a/retirement/tests/test_export_retreat_room_distribution.py b/retirement/tests/test_export_retreat_room_distribution.py new file mode 100644 index 00000000..ab0eb85e --- /dev/null +++ b/retirement/tests/test_export_retreat_room_distribution.py @@ -0,0 +1,90 @@ +from unittest import mock +from django.test import TestCase +from datetime import datetime + +from django.contrib.contenttypes.models import ContentType + +import pytz +from django.conf import settings +from retirement.models import ( + RetreatType, + RetreatDate, + Retreat +) +from retirement.exports import generate_retreat_room_distribution +from blitz_api.models import ExportMedia +from blitz_api.factories import AdminFactory + +LOCAL_TIMEZONE = pytz.timezone(settings.TIME_ZONE) + + +class TestExportAnonymousChronoDataTask(TestCase): + + def setUp(self): + self.admin = AdminFactory() + self.retreat_type = ContentType.objects.get_for_model(Retreat) + self.retreatType = RetreatType.objects.create( + name="Type 1", + minutes_before_display_link=10, + number_of_tomatoes=4, + ) + self.retreat = Retreat.objects.create( + name="mega_retreat", + details="This is a description of the mega retreat.", + seats=400, + address_line1="123 random street", + postal_code="123 456", + state_province="Random state", + country="Random country", + price=199, + min_day_refund=7, + min_day_exchange=7, + refund_rate=50, + accessibility=True, + form_url="example.com", + carpool_url='example2.com', + review_url='example3.com', + has_shared_rooms=True, + toilet_gendered=False, + room_type=Retreat.SINGLE_OCCUPATION, + display_start_time=LOCAL_TIMEZONE.localize( + datetime(2130, 1, 15, 8) + ), + type=self.retreatType, + ) + RetreatDate.objects.create( + start_time=LOCAL_TIMEZONE.localize(datetime(2130, 1, 15, 8)), + end_time=LOCAL_TIMEZONE.localize(datetime(2130, 1, 17, 12)), + retreat=self.retreat, + ) + self.retreat.activate() + + @mock.patch('retirement.models.Retreat.get_retreat_room_distribution') + @mock.patch('blitz_api.models.ExportMedia.send_confirmation_email') + def test_export_retreat_room_distribution(self, mock_email, mock_room_distribution): + """ + """ + mock_email.return_value = None + mock_room_distribution.return_value = [ + {'first_name': 'Joshua', 'last_name': 'Berry', 'email': '16@test.ca', 'room_option': 'single', + 'gender_preference': 'NA', 'share_with': 'NA', 'room_number': 1, 'placed': True}, + {'first_name': 'Sarah', 'last_name': 'Hancock', 'email': '17@test.ca', 'room_option': 'single', + 'gender_preference': 'NA', 'share_with': 'NA', 'room_number': 2, 'placed': True}, + {'first_name': 'Lisa', 'last_name': 'Wright', 'email': '1@test.ca', 'room_option': 'shared', + 'gender_preference': 'mixte', 'share_with': '14@test.ca', 'room_number': 3, 'placed': True}, + {'first_name': 'Matthew', 'last_name': 'Ross', 'email': '14@test.ca', 'room_option': 'shared', + 'gender_preference': 'woman', 'share_with': '1@test.ca', 'room_number': 3, 'placed': True}, + {'first_name': 'Robert', 'last_name': 'Haas', 'email': '2@test.ca', 'room_option': 'shared', + 'gender_preference': 'man', 'share_with': '11@test.ca', 'room_number': 4, 'placed': True}, + {'first_name': 'Justin', 'last_name': 'Martin', 'email': '11@test.ca', 'room_option': 'shared', + 'gender_preference': 'woman', 'share_with': '2@test.ca', 'room_number': 4, 'placed': True} + ] + retreat = Retreat.objects.get(pk=self.retreat.id) + generate_retreat_room_distribution(self.admin.id, retreat) + self.assertEqual( + ExportMedia.objects.all().count(), + 1) + export = ExportMedia.objects.all().first() + self.assertEqual( + export.type, + ExportMedia.EXPORT_RETREAT_ROOM_DISTRIBUTION) diff --git a/retirement/tests/tests_model_Retreat.py b/retirement/tests/tests_model_Retreat.py index 45ada955..cafe4622 100644 --- a/retirement/tests/tests_model_Retreat.py +++ b/retirement/tests/tests_model_Retreat.py @@ -1,24 +1,36 @@ from datetime import datetime +from django.contrib.contenttypes.models import ContentType + import pytz from django.conf import settings from rest_framework.test import APITestCase +from blitz_api.factories import ( + UserFactory, + OrderFactory, + ReservationFactory, + OptionProductFactory, + OrderLineFactory, + OrderLineBaseProductFactory +) + from retirement.models import ( Retreat, RetreatDate, RetreatType, ) +from store.models import ( + OptionProduct +) LOCAL_TIMEZONE = pytz.timezone(settings.TIME_ZONE) class RetreatTests(APITestCase): - def test_create(self): - """ - Ensure that we can create a retreat. - """ + def setUp(self): + self.retreat_type = ContentType.objects.get_for_model(Retreat) self.retreatType = RetreatType.objects.create( name="Type 1", minutes_before_display_link=10, @@ -55,4 +67,222 @@ def test_create(self): ) self.retreat.activate() - self.assertEqual(self.retreat.__str__(), "mega_retreat") + self.shared_room_option = OptionProductFactory( + is_room_option=True, + type=OptionProduct.METADATA_SHARED_ROOM + + ) + self.shared_room_option.available_on_products.add(self.retreat) + self.shared_room_option.save() + + self.single_room_option = OptionProductFactory( + is_room_option=True, + type=OptionProduct.METADATA_NONE + ) + self.single_room_option.available_on_products.add(self.retreat) + self.single_room_option.save() + + def test_create(self): + """ + Ensure that we can create a retreat. + """ + self.retreatType2 = RetreatType.objects.create( + name="Type 1", + minutes_before_display_link=10, + number_of_tomatoes=4, + ) + self.retreat2 = Retreat.objects.create( + name="mega_retreat", + details="This is a description of the mega retreat.", + seats=400, + address_line1="123 random street", + postal_code="123 456", + state_province="Random state", + country="Random country", + price=199, + min_day_refund=7, + min_day_exchange=7, + refund_rate=50, + accessibility=True, + form_url="example.com", + carpool_url='example2.com', + review_url='example3.com', + has_shared_rooms=True, + toilet_gendered=False, + room_type=Retreat.SINGLE_OCCUPATION, + display_start_time=LOCAL_TIMEZONE.localize( + datetime(2130, 1, 15, 8) + ), + type=self.retreatType2, + ) + RetreatDate.objects.create( + start_time=LOCAL_TIMEZONE.localize(datetime(2130, 1, 15, 8)), + end_time=LOCAL_TIMEZONE.localize(datetime(2130, 1, 17, 12)), + retreat=self.retreat2, + ) + self.retreat2.activate() + + self.assertEqual(self.retreat2.__str__(), "mega_retreat") + + def test_get_retreat_room_distribution(self): + """ + Test the room distribution for a retreat + """ + user_1 = UserFactory(email='1@test.ca') + user_2 = UserFactory(email='2@test.ca') + user_3 = UserFactory(email='3@test.ca') + user_4 = UserFactory(email='4@test.ca') + user_5 = UserFactory(email='5@test.ca') + user_6 = UserFactory(email='6@test.ca') + user_7 = UserFactory(email='7@test.ca') + user_8 = UserFactory(email='8@test.ca') + user_9 = UserFactory(email='9@test.ca') + user_10 = UserFactory(email='10@test.ca') + user_11 = UserFactory(email='11@test.ca') + user_12 = UserFactory(email='12@test.ca') # will be non-active + user_13 = UserFactory(email='13@test.ca') + user_14 = UserFactory(email='14@test.ca') + user_15 = UserFactory(email='15@test.ca') + user_16 = UserFactory(email='16@test.ca') + user_17 = UserFactory(email='17@test.ca') + + order_1 = OrderFactory(user=user_1) + order_2 = OrderFactory(user=user_2) + order_3 = OrderFactory(user=user_3) + order_4 = OrderFactory(user=user_4) + order_5 = OrderFactory(user=user_5) + order_6 = OrderFactory(user=user_6) + order_7 = OrderFactory(user=user_7) + order_8 = OrderFactory(user=user_8) + order_9 = OrderFactory(user=user_9) + order_10 = OrderFactory(user=user_10) + order_11 = OrderFactory(user=user_11) + order_12 = OrderFactory(user=user_12) + order_13 = OrderFactory(user=user_13) + order_14 = OrderFactory(user=user_14) + order_15 = OrderFactory(user=user_15) + order_16 = OrderFactory(user=user_16) + order_17 = OrderFactory(user=user_17) + + order_line_1 = OrderLineFactory(content_type=self.retreat_type, object_id=self.retreat.id, order=order_1) + order_line_2 = OrderLineFactory(content_type=self.retreat_type, object_id=self.retreat.id, order=order_2) + order_line_3 = OrderLineFactory(content_type=self.retreat_type, object_id=self.retreat.id, order=order_3) + order_line_4 = OrderLineFactory(content_type=self.retreat_type, object_id=self.retreat.id, order=order_4) + order_line_5 = OrderLineFactory(content_type=self.retreat_type, object_id=self.retreat.id, order=order_5) + order_line_6 = OrderLineFactory(content_type=self.retreat_type, object_id=self.retreat.id, order=order_6) + order_line_7 = OrderLineFactory(content_type=self.retreat_type, object_id=self.retreat.id, order=order_7) + order_line_8 = OrderLineFactory(content_type=self.retreat_type, object_id=self.retreat.id, order=order_8) + order_line_9 = OrderLineFactory(content_type=self.retreat_type, object_id=self.retreat.id, order=order_9) + order_line_10 = OrderLineFactory(content_type=self.retreat_type, object_id=self.retreat.id, order=order_10) + order_line_11 = OrderLineFactory(content_type=self.retreat_type, object_id=self.retreat.id, order=order_11) + order_line_12 = OrderLineFactory(content_type=self.retreat_type, object_id=self.retreat.id, order=order_12) + order_line_13 = OrderLineFactory(content_type=self.retreat_type, object_id=self.retreat.id, order=order_13) + order_line_14 = OrderLineFactory(content_type=self.retreat_type, object_id=self.retreat.id, order=order_14) + order_line_15 = OrderLineFactory(content_type=self.retreat_type, object_id=self.retreat.id, order=order_15) + order_line_16 = OrderLineFactory(content_type=self.retreat_type, object_id=self.retreat.id, order=order_16) + order_line_17 = OrderLineFactory(content_type=self.retreat_type, object_id=self.retreat.id, order=order_17) + + reservation_1 = ReservationFactory(user=user_1, retreat=self.retreat, order_line=order_line_1) + reservation_2 = ReservationFactory(user=user_2, retreat=self.retreat, order_line=order_line_2) + reservation_3 = ReservationFactory(user=user_3, retreat=self.retreat, order_line=order_line_3) + reservation_4 = ReservationFactory(user=user_4, retreat=self.retreat, order_line=order_line_4) + reservation_5 = ReservationFactory(user=user_5, retreat=self.retreat, order_line=order_line_5) + reservation_6 = ReservationFactory(user=user_6, retreat=self.retreat, order_line=order_line_6) + reservation_7 = ReservationFactory(user=user_7, retreat=self.retreat, order_line=order_line_7) + reservation_8 = ReservationFactory(user=user_8, retreat=self.retreat, order_line=order_line_8) + reservation_9 = ReservationFactory(user=user_9, retreat=self.retreat, order_line=order_line_9) + reservation_10 = ReservationFactory(user=user_10, retreat=self.retreat, order_line=order_line_10) + reservation_11 = ReservationFactory(user=user_11, retreat=self.retreat, order_line=order_line_11) + reservation_12 = ReservationFactory( + user=user_12, retreat=self.retreat, order_line=order_line_12, is_active=False) + reservation_13 = ReservationFactory(user=user_13, retreat=self.retreat, order_line=order_line_13) + reservation_14 = ReservationFactory(user=user_14, retreat=self.retreat, order_line=order_line_14) + reservation_15 = ReservationFactory(user=user_15, retreat=self.retreat, order_line=order_line_15) + reservation_16 = ReservationFactory(user=user_16, retreat=self.retreat, order_line=order_line_16) + reservation_17 = ReservationFactory(user=user_17, retreat=self.retreat, order_line=order_line_17) + + metadata_1 = {"share_with_member": "14@test.ca", "share_with_preferred_gender": "mixte"} + metadata_2 = {"share_with_member": "11@test.ca", "share_with_preferred_gender": "man"} + metadata_3 = {"share_with_member": "", "share_with_preferred_gender": "woman"} + metadata_4 = {"share_with_member": "", "share_with_preferred_gender": "non-binary"} + metadata_5 = {"share_with_member": "", "share_with_preferred_gender": "non-binary"} + metadata_6 = {"share_with_member": "", "share_with_preferred_gender": "woman"} + metadata_7 = {"share_with_member": "", "share_with_preferred_gender": "non-binary"} + metadata_8 = {"share_with_member": "", "share_with_preferred_gender": "man"} + metadata_9 = {"share_with_member": "", "share_with_preferred_gender": "man"} + metadata_10 = {"share_with_member": "", "share_with_preferred_gender": "man"} + metadata_11 = {"share_with_member": "2@test.ca", "share_with_preferred_gender": "woman"} + metadata_12 = {"share_with_member": "", "share_with_preferred_gender": "woman"} + metadata_13 = {"share_with_member": "", "share_with_preferred_gender": "woman"} + metadata_14 = {"share_with_member": "1@test.ca", "share_with_preferred_gender": "woman"} + metadata_15 = {"share_with_member": "not@found.ca", "share_with_preferred_gender": "mixte"} + + option_1 = OrderLineBaseProductFactory( + order_line=order_line_1, option=self.shared_room_option, metadata=metadata_1) + option_2 = OrderLineBaseProductFactory( + order_line=order_line_2, option=self.shared_room_option, metadata=metadata_2) + option_3 = OrderLineBaseProductFactory( + order_line=order_line_3, option=self.shared_room_option, metadata=metadata_3) + option_4 = OrderLineBaseProductFactory( + order_line=order_line_4, option=self.shared_room_option, metadata=metadata_4) + option_5 = OrderLineBaseProductFactory( + order_line=order_line_5, option=self.shared_room_option, metadata=metadata_5) + option_6 = OrderLineBaseProductFactory( + order_line=order_line_6, option=self.shared_room_option, metadata=metadata_6) + option_7 = OrderLineBaseProductFactory( + order_line=order_line_7, option=self.shared_room_option, metadata=metadata_7) + option_8 = OrderLineBaseProductFactory( + order_line=order_line_8, option=self.shared_room_option, metadata=metadata_8) + option_9 = OrderLineBaseProductFactory( + order_line=order_line_9, option=self.shared_room_option, metadata=metadata_9) + option_10 = OrderLineBaseProductFactory( + order_line=order_line_10, option=self.shared_room_option, metadata=metadata_10) + option_11 = OrderLineBaseProductFactory( + order_line=order_line_11, option=self.shared_room_option, metadata=metadata_11) + option_12 = OrderLineBaseProductFactory( + order_line=order_line_12, option=self.shared_room_option, metadata=metadata_12) + option_13 = OrderLineBaseProductFactory( + order_line=order_line_13, option=self.shared_room_option, metadata=metadata_13) + option_14 = OrderLineBaseProductFactory( + order_line=order_line_14, option=self.shared_room_option, metadata=metadata_14) + option_15 = OrderLineBaseProductFactory( + order_line=order_line_15, option=self.shared_room_option, metadata=metadata_15) + option_16 = OrderLineBaseProductFactory( + order_line=order_line_16, option=self.single_room_option) + option_17 = OrderLineBaseProductFactory( + order_line=order_line_17, option=self.single_room_option) + + distribution = self.retreat.get_retreat_room_distribution() + self.assertEqual(len(distribution), 16) + room_set = set() + for user in distribution: + room_set.add(user['room_number']) + self.assertEqual(len(room_set), 9) + # Single room + self.assertEqual(distribution[0]['email'], user_16.email) + self.assertEqual(distribution[1]['email'], user_17.email) + # Friend room + self.assertEqual(distribution[2]['email'], distribution[3]['share_with']) + self.assertEqual(distribution[3]['email'], distribution[2]['share_with']) + self.assertEqual(distribution[2]['room_number'], distribution[3]['room_number']) + + self.assertEqual(distribution[4]['email'], distribution[5]['share_with']) + self.assertEqual(distribution[5]['email'], distribution[4]['share_with']) + self.assertEqual(distribution[4]['room_number'], distribution[5]['room_number']) + + # Match room + self.assertEqual(distribution[6]['gender_preference'], distribution[7]['gender_preference']) + self.assertEqual(distribution[6]['room_number'], distribution[7]['room_number']) + + self.assertEqual(distribution[8]['gender_preference'], distribution[9]['gender_preference']) + self.assertEqual(distribution[8]['room_number'], distribution[9]['room_number']) + + self.assertEqual(distribution[10]['gender_preference'], distribution[11]['gender_preference']) + self.assertEqual(distribution[10]['room_number'], distribution[11]['room_number']) + + # Match as possible + self.assertEqual(distribution[12]['gender_preference'], metadata_15['share_with_preferred_gender']) + self.assertEqual(distribution[12]['share_with'], metadata_15['share_with_member']) + self.assertEqual(distribution[12]['room_number'], distribution[13]['room_number']) + + self.assertEqual(distribution[14]['room_number'], distribution[15]['room_number']) From 1b533d565a2e486c19082b9ddd9b64372fa2f4da Mon Sep 17 00:00:00 2001 From: RomainFayolle Date: Fri, 9 Sep 2022 11:58:12 -0400 Subject: [PATCH 4/4] update test to cover full comparison --- retirement/tests/tests_model_Retreat.py | 98 +++++++++++++------------ 1 file changed, 53 insertions(+), 45 deletions(-) diff --git a/retirement/tests/tests_model_Retreat.py b/retirement/tests/tests_model_Retreat.py index cafe4622..d38ff6b0 100644 --- a/retirement/tests/tests_model_Retreat.py +++ b/retirement/tests/tests_model_Retreat.py @@ -128,23 +128,23 @@ def test_get_retreat_room_distribution(self): """ Test the room distribution for a retreat """ - user_1 = UserFactory(email='1@test.ca') - user_2 = UserFactory(email='2@test.ca') - user_3 = UserFactory(email='3@test.ca') - user_4 = UserFactory(email='4@test.ca') - user_5 = UserFactory(email='5@test.ca') - user_6 = UserFactory(email='6@test.ca') - user_7 = UserFactory(email='7@test.ca') - user_8 = UserFactory(email='8@test.ca') - user_9 = UserFactory(email='9@test.ca') - user_10 = UserFactory(email='10@test.ca') - user_11 = UserFactory(email='11@test.ca') - user_12 = UserFactory(email='12@test.ca') # will be non-active - user_13 = UserFactory(email='13@test.ca') - user_14 = UserFactory(email='14@test.ca') - user_15 = UserFactory(email='15@test.ca') - user_16 = UserFactory(email='16@test.ca') - user_17 = UserFactory(email='17@test.ca') + user_1 = UserFactory(first_name='x', last_name='y', email='1@test.ca') + user_2 = UserFactory(first_name='x', last_name='y', email='2@test.ca') + user_3 = UserFactory(first_name='x', last_name='y', email='3@test.ca') + user_4 = UserFactory(first_name='x', last_name='y', email='4@test.ca') + user_5 = UserFactory(first_name='x', last_name='y', email='5@test.ca') + user_6 = UserFactory(first_name='x', last_name='y', email='6@test.ca') + user_7 = UserFactory(first_name='x', last_name='y', email='7@test.ca') + user_8 = UserFactory(first_name='x', last_name='y', email='8@test.ca') + user_9 = UserFactory(first_name='x', last_name='y', email='9@test.ca') + user_10 = UserFactory(first_name='x', last_name='y', email='10@test.ca') + user_11 = UserFactory(first_name='x', last_name='y', email='11@test.ca') + user_12 = UserFactory(first_name='x', last_name='y', email='12@test.ca') # will be non-active + user_13 = UserFactory(first_name='x', last_name='y', email='13@test.ca') + user_14 = UserFactory(first_name='x', last_name='y', email='14@test.ca') + user_15 = UserFactory(first_name='x', last_name='y', email='15@test.ca') + user_16 = UserFactory(first_name='x', last_name='y', email='16@test.ca') + user_17 = UserFactory(first_name='x', last_name='y', email='17@test.ca') order_1 = OrderFactory(user=user_1) order_2 = OrderFactory(user=user_2) @@ -253,36 +253,44 @@ def test_get_retreat_room_distribution(self): order_line=order_line_17, option=self.single_room_option) distribution = self.retreat.get_retreat_room_distribution() + expected_distribution = [ + {'first_name': 'x', 'last_name': 'y', 'email': '16@test.ca', 'room_option': 'single', + 'gender_preference': 'NA', 'share_with': 'NA', 'room_number': 1, 'placed': True}, + {'first_name': 'x', 'last_name': 'y', 'email': '17@test.ca', 'room_option': 'single', + 'gender_preference': 'NA', 'share_with': 'NA', 'room_number': 2, 'placed': True}, + {'first_name': 'x', 'last_name': 'y', 'email': '1@test.ca', 'room_option': 'shared', + 'gender_preference': 'mixte', 'share_with': '14@test.ca', 'room_number': 3, 'placed': True}, + {'first_name': 'x', 'last_name': 'y', 'email': '14@test.ca', 'room_option': 'shared', + 'gender_preference': 'woman', 'share_with': '1@test.ca', 'room_number': 3, 'placed': True}, + {'first_name': 'x', 'last_name': 'y', 'email': '2@test.ca', 'room_option': 'shared', + 'gender_preference': 'man', 'share_with': '11@test.ca', 'room_number': 4, 'placed': True}, + {'first_name': 'x', 'last_name': 'y', 'email': '11@test.ca', 'room_option': 'shared', + 'gender_preference': 'woman', 'share_with': '2@test.ca', 'room_number': 4, 'placed': True}, + {'first_name': 'x', 'last_name': 'y', 'email': '5@test.ca', 'room_option': 'shared', + 'gender_preference': 'non-binary', 'share_with': 'NA', 'room_number': 5, 'placed': True}, + {'first_name': 'x', 'last_name': 'y', 'email': '4@test.ca', 'room_option': 'shared', + 'gender_preference': 'non-binary', 'share_with': 'NA', 'room_number': 5, 'placed': True}, + {'first_name': 'x', 'last_name': 'y', 'email': '6@test.ca', 'room_option': 'shared', + 'gender_preference': 'woman', 'share_with': 'NA', 'room_number': 6, 'placed': True}, + {'first_name': 'x', 'last_name': 'y', 'email': '3@test.ca', 'room_option': 'shared', + 'gender_preference': 'woman', 'share_with': 'NA', 'room_number': 6, 'placed': True}, + {'first_name': 'x', 'last_name': 'y', 'email': '9@test.ca', 'room_option': 'shared', + 'gender_preference': 'man', 'share_with': 'NA', 'room_number': 7, 'placed': True}, + {'first_name': 'x', 'last_name': 'y', 'email': '8@test.ca', 'room_option': 'shared', + 'gender_preference': 'man', 'share_with': 'NA', 'room_number': 7, 'placed': True}, + {'first_name': 'x', 'last_name': 'y', 'email': '15@test.ca', 'room_option': 'shared', + 'gender_preference': 'mixte', 'share_with': 'not@found.ca', 'room_number': 8, 'placed': True}, + {'first_name': 'x', 'last_name': 'y', 'email': '10@test.ca', 'room_option': 'shared', + 'gender_preference': 'man', 'share_with': 'NA', 'room_number': 8, 'placed': True}, + {'first_name': 'x', 'last_name': 'y', 'email': '13@test.ca', 'room_option': 'shared', + 'gender_preference': 'woman', 'share_with': 'NA', 'room_number': 9, 'placed': True}, + {'first_name': 'x', 'last_name': 'y', 'email': '7@test.ca', 'room_option': 'shared', + 'gender_preference': 'non-binary', 'share_with': 'NA', 'room_number': 9, 'placed': True} + ] + self.assertEqual(len(distribution), 16) + self.assertEqual(distribution, expected_distribution) room_set = set() for user in distribution: room_set.add(user['room_number']) self.assertEqual(len(room_set), 9) - # Single room - self.assertEqual(distribution[0]['email'], user_16.email) - self.assertEqual(distribution[1]['email'], user_17.email) - # Friend room - self.assertEqual(distribution[2]['email'], distribution[3]['share_with']) - self.assertEqual(distribution[3]['email'], distribution[2]['share_with']) - self.assertEqual(distribution[2]['room_number'], distribution[3]['room_number']) - - self.assertEqual(distribution[4]['email'], distribution[5]['share_with']) - self.assertEqual(distribution[5]['email'], distribution[4]['share_with']) - self.assertEqual(distribution[4]['room_number'], distribution[5]['room_number']) - - # Match room - self.assertEqual(distribution[6]['gender_preference'], distribution[7]['gender_preference']) - self.assertEqual(distribution[6]['room_number'], distribution[7]['room_number']) - - self.assertEqual(distribution[8]['gender_preference'], distribution[9]['gender_preference']) - self.assertEqual(distribution[8]['room_number'], distribution[9]['room_number']) - - self.assertEqual(distribution[10]['gender_preference'], distribution[11]['gender_preference']) - self.assertEqual(distribution[10]['room_number'], distribution[11]['room_number']) - - # Match as possible - self.assertEqual(distribution[12]['gender_preference'], metadata_15['share_with_preferred_gender']) - self.assertEqual(distribution[12]['share_with'], metadata_15['share_with_member']) - self.assertEqual(distribution[12]['room_number'], distribution[13]['room_number']) - - self.assertEqual(distribution[14]['room_number'], distribution[15]['room_number'])