Skip to content

Commit

Permalink
Merge pull request #1553 from RomainFayolle/feature-export_retreat_pa…
Browse files Browse the repository at this point in the history
…rticipant_room

Feature export retreat participant room #rm4787
  • Loading branch information
RignonNoel authored Sep 9, 2022
2 parents 5fdfa59 + 1b533d5 commit 2e87a2b
Show file tree
Hide file tree
Showing 7 changed files with 612 additions and 9 deletions.
36 changes: 34 additions & 2 deletions blitz_api/factories.py
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand Down Expand Up @@ -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
Expand All @@ -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
2 changes: 2 additions & 0 deletions blitz_api/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -680,13 +680,15 @@ 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')),
(EXPORT_OTHER, _('Other')),
(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(
Expand Down
16 changes: 14 additions & 2 deletions retirement/admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -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()

Expand Down Expand Up @@ -122,6 +125,14 @@ def export_retreat_sales(self, request, queryset):
export_retreat_sales.short_description = 'export_retreat_sales'


def export_retreat_room_distribution(self, request, queryset):
for retreat in queryset:
generate_retreat_room_distribution.delay(request.user.id, retreat)


export_retreat_room_distribution.short_description = 'export_retreat_room_distribution'


class RetreatAdmin(SimpleHistoryAdmin,
ExportActionModelAdmin,
SafeDeleteAdmin,
Expand Down Expand Up @@ -156,7 +167,8 @@ class RetreatAdmin(SimpleHistoryAdmin,
'export_admin_action',
make_reservation_not_refundable,
make_reservation_refundable,
export_retreat_sales
export_retreat_sales,
export_retreat_room_distribution
]


Expand Down
48 changes: 48 additions & 0 deletions retirement/exports.py
Original file line number Diff line number Diff line change
@@ -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
Expand Down Expand Up @@ -45,3 +47,49 @@ def generate_retreat_sales(
ContentFile(output_stream.getvalue().encode()))

return new_export


@shared_task()
def generate_retreat_room_distribution(
admin_id,
retreat
):
"""
For given retreats, generate a csv file for room distribution
:params admin_id: id of admin doing the request
:params retreat: django retreat object
"""

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()
181 changes: 181 additions & 0 deletions retirement/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import json
import traceback
from datetime import timedelta
from operator import itemgetter

import requests
from django.conf import settings
Expand All @@ -23,6 +24,8 @@
Membership,
OrderLine,
BaseProduct,
OrderLineBaseProduct,
OptionProduct,
Coupon,
Refund,
)
Expand Down Expand Up @@ -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):

Expand Down
Loading

0 comments on commit 2e87a2b

Please sign in to comment.