From bba67c7dbea48c4ff79379749056cdfe35273d6c Mon Sep 17 00:00:00 2001 From: Thomas Steen Rasmussen Date: Wed, 25 Aug 2021 02:19:28 +0200 Subject: [PATCH 1/4] needs moderation --- src/villages/urls.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/villages/urls.py b/src/villages/urls.py index 581178cc..772499f0 100644 --- a/src/villages/urls.py +++ b/src/villages/urls.py @@ -1,7 +1,6 @@ from django.urls import path from .views import ( - VillageCreateView, VillageDeleteView, VillageDetailView, VillageListView, @@ -12,7 +11,7 @@ app_name = "villages" urlpatterns = [ path("", VillageListView.as_view(), name="list"), - path("create/", VillageCreateView.as_view(), name="create"), + # path("create/", VillageCreateView.as_view(), name="create"), path("/delete/", VillageDeleteView.as_view(), name="delete"), path("/edit/", VillageUpdateView.as_view(), name="update"), path("/", VillageDetailView.as_view(), name="detail"), -- 2.43.4 From 3420394a0f51605eef2e26a4f9b511ac712be0d2 Mon Sep 17 00:00:00 2001 From: Thomas Steen Rasmussen Date: Wed, 25 Aug 2021 02:36:26 +0200 Subject: [PATCH 2/4] needs moderation --- src/bornhack/urls.py | 6 ------ 1 file changed, 6 deletions(-) diff --git a/src/bornhack/urls.py b/src/bornhack/urls.py index f4c9b7b9..16a70392 100644 --- a/src/bornhack/urls.py +++ b/src/bornhack/urls.py @@ -13,7 +13,6 @@ from info.views import CampInfoView from people.views import PeopleView from sponsors.views import SponsorsView from villages.views import ( - VillageCreateView, VillageDeleteView, VillageDetailView, VillageListView, @@ -118,11 +117,6 @@ urlpatterns = [ include( [ path("", VillageListView.as_view(), name="village_list"), - path( - "create/", - VillageCreateView.as_view(), - name="village_create", - ), path( "/delete/", VillageDeleteView.as_view(), -- 2.43.4 From b3f127f197cf0a8803e946cdde967d1c0cd93257 Mon Sep 17 00:00:00 2001 From: Thomas Steen Rasmussen Date: Wed, 25 Aug 2021 02:39:04 +0200 Subject: [PATCH 3/4] needs moderation --- src/villages/templates/village_list.html | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/villages/templates/village_list.html b/src/villages/templates/village_list.html index b8e93c1c..06621ea5 100644 --- a/src/villages/templates/village_list.html +++ b/src/villages/templates/village_list.html @@ -16,10 +16,6 @@ Villages | {{ block.super }} tent, chairs and tables in the shop!

-{% if user.is_authenticated and not camp.read_only %} -Create a new {{ camp.title }} village -{% endif %} -
{% if villages %} -- 2.43.4 From 005431d6f6e07ebcba9321c71a3cc45a02bf09c8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Reynir=20Bj=C3=B6rnsson?= Date: Tue, 24 Aug 2021 18:07:58 +0200 Subject: [PATCH 4/4] team shifts: implement marking shifts for sale A common occurence is that not enough people volunteer for shifts at first, and then the few volunteers take all the shifts (too many). Then late comers aren't able to take any shifts. This commit implements a third state for shift assignment: for sale. A shift marked for sale means the team member is able to take the shift if need be, but would rather someone else takes the shift. Another team member will be able to take shifts at the same time even if the required number of people are met. The current semantics is for-sale shifts are replaced eagerly. Another possible implementation would be to only replace for-sale shifts if the number of required people is met. --- src/teams/models.py | 20 +++++++++++++++++++- src/teams/templates/team_shift_list.html | 8 ++++++-- src/teams/templates/team_user_shifts.html | 6 +++++- src/teams/urls.py | 5 +++++ src/teams/views/shifts.py | 20 ++++++++++++++++++++ 5 files changed, 55 insertions(+), 4 deletions(-) diff --git a/src/teams/models.py b/src/teams/models.py index 6e0862f4..3366926f 100644 --- a/src/teams/models.py +++ b/src/teams/models.py @@ -401,7 +401,7 @@ class TeamShift(CampRelatedModel): shift_range = DateTimeRangeField() - team_members = models.ManyToManyField(TeamMember, blank=True) + team_members = models.ManyToManyField(TeamMember, blank=True, through=TeamShiftAssignment) people_required = models.IntegerField(default=1) @@ -420,3 +420,21 @@ class TeamShift(CampRelatedModel): @property def users(self): return [member.user for member in self.team_members.all()] + +class TeamShiftAssignment(CampRelatedModel): + team_shift = models.ForeignKey( + "teams.TeamShift", + on_delete=models.CASCADE, + help_text="The shift", + ) + + team_member = models.ForeignKey( + "teams.TeamMember", + on_delete=models.CASCADE, + help_text="The team member on shift", + ) + + for_sale = models.BooleanField( + default=False, + help_text="Is the shift assignment for sale?", + ) diff --git a/src/teams/templates/team_shift_list.html b/src/teams/templates/team_shift_list.html index 360e66a9..e8bbf025 100644 --- a/src/teams/templates/team_shift_list.html +++ b/src/teams/templates/team_shift_list.html @@ -50,7 +50,7 @@ Shifts | {{ block.super }} {{ shift.people_required }} +
{% for member in shift.team_members.all %} - {{ member.user.profile.get_public_credit_name }}{% if not forloop.last %},{% endif %} + {{ member.user.profile.get_public_credit_name }}{% if member.for_sale %} (for sale!){% endif %}{% if not forloop.last %},{% endif %} {% empty %} None! {% endfor %} @@ -71,7 +71,11 @@ Shifts | {{ block.super }} href="{% url 'teams:shift_member_drop' camp_slug=camp.slug team_slug=team.slug pk=shift.pk %}"> Unassign me - {% elif shift.people_required > shift.team_members.count %} + + Sell shift + + {% elif shift.people_required > shift.team_members.filter(for_sale=False).count %} Assign me diff --git a/src/teams/templates/team_user_shifts.html b/src/teams/templates/team_user_shifts.html index 745fd2bb..e3311b90 100644 --- a/src/teams/templates/team_user_shifts.html +++ b/src/teams/templates/team_user_shifts.html @@ -34,6 +34,10 @@ Your shifts | {{ block.super }} {{ shift.shift_range.upper|date:'H:i' }} + + Sell shift @@ -45,4 +49,4 @@ Your shifts | {{ block.super }}
-{% endblock %} \ No newline at end of file +{% endblock %} diff --git a/src/teams/urls.py b/src/teams/urls.py index 92ef4e03..33bdc7c7 100644 --- a/src/teams/urls.py +++ b/src/teams/urls.py @@ -179,6 +179,11 @@ urlpatterns = [ MemberDropsShift.as_view(), name="shift_member_drop", ), + path( + "sell", + MemberSellsShift.as_view(), + name="shift_member_sell", + ), ] ), ), diff --git a/src/teams/views/shifts.py b/src/teams/views/shifts.py index eaa44fc8..72d0ede9 100644 --- a/src/teams/views/shifts.py +++ b/src/teams/views/shifts.py @@ -274,6 +274,9 @@ class MemberTakesShift(LoginRequiredMixin, CampViewMixin, View): request, template.render(Context({"shifts": overlapping_shifts})) ) else: + # Remove at most one shift assignment for sale + for shift_assignment in shift.team_members.filter(for_sale=True)[:1]: + shift_assignment.delete() shift.team_members.add(team_member) kwargs.pop("pk") @@ -297,6 +300,23 @@ class MemberDropsShift(LoginRequiredMixin, CampViewMixin, View): return HttpResponseRedirect(reverse("teams:shifts", kwargs=kwargs)) +class MemberSellsShift(LoginRequiredMixin, CampViewMixin, View): + + http_methods = ["get"] + + def get(self, request, **kwargs): + shift = TeamShift.objects.get(id=kwargs["pk"]) + team = Team.objects.get(camp=self.camp, slug=kwargs["team_slug"]) + + team_member = TeamMember.objects.get(team=team, user=request.user) + + shift_assignment = shift.team_members.get(team_member = team_member.pk) + shift_assignment.for_sale = True + shift_assignment.save() + + kwargs.pop("pk") + + return HttpResponseRedirect(reverse("teams:shifts", kwargs=kwargs)) class UserShifts(CampViewMixin, TemplateView): template_name = "team_user_shifts.html" -- 2.43.4