diff --git a/mobile/coach_views.py b/mobile/coach_views.py index 076bd88..d4374b2 100644 --- a/mobile/coach_views.py +++ b/mobile/coach_views.py @@ -440,18 +440,17 @@ class CoachCreateEventView(CoachScopeMixin, LoginRequiredMixin, TemplateView): # one of the four tiles COACH_EVENT_KINDS offers below. instance = Event(club=self.request.club, created_by=self.me, kind=Event.EventKind.TRAINING) form = EventForm(data, club=self.request.club, user=self.request.user, editing=False, instance=instance) - # max_referees/external_game_id have no use on a coach-created event -- # max_referees has a model default (2) but no blank=True, so the field - # is required despite it; construct_instance skips a deleted field - # entirely, leaving the instance's own default/blank. + # is required despite it -- delete it rather than render a referee- + # count control this screen has no use for; construct_instance skips + # a deleted field entirely, leaving the instance's own default. del form.fields["max_referees"] - del form.fields["external_game_id"] # Narrowed to the four kinds the tile picker actually offers -- social/ # other don't get their own tile, and this keeps a tampered request from # setting one anyway (the desktop form still offers the full list). form.fields["kind"].choices = [choice for choice in form.fields["kind"].choices if choice[0] in COACH_EVENT_KINDS] self._scope_shared_fields(form) - for field_name in ("start", "deadline", "competition"): + for field_name in ("start", "gathering", "deadline", "competition", "external_game_id"): form.fields[field_name].widget.attrs["class"] = _INPUT_CLASSES return form @@ -462,9 +461,6 @@ class CoachCreateEventView(CoachScopeMixin, LoginRequiredMixin, TemplateView): # friendly frequency/interval/weekdays fields below cover the common # weekly/monthly cases this screen is for. del form.fields["advanced_rrule"] - # Not offered here: neither maps to a "how long since kickoff" a coach - # thinks in the way duration_hours/minutes below does. - del form.fields["gathering_minutes_before"] form.fields["kind"].choices = [choice for choice in form.fields["kind"].choices if choice[0] in COACH_EVENT_KINDS] self._scope_shared_fields(form) # SelectMultiple relies on the desktop's searchable-select JS (not loaded @@ -478,7 +474,7 @@ class CoachCreateEventView(CoachScopeMixin, LoginRequiredMixin, TemplateView): # that (same trap _scope_shared_fields' own comment covers, just the # plain-ChoiceField shape of it). form.fields["weekdays"].widget = forms.CheckboxSelectMultiple(attrs={"class": "sr-only"}, choices=form.fields["weekdays"].choices) - for field_name in ("dtstart", "until", "frequency", "interval", "duration_hours", "duration_minutes", "deadline_minutes_before"): + for field_name in ("dtstart", "until", "frequency", "interval", "duration_hours", "duration_minutes", "gathering_minutes_before", "deadline_minutes_before"): form.fields[field_name].widget.attrs["class"] = _INPUT_CLASSES return form diff --git a/mobile/templates/mobile/coach/event_form.html b/mobile/templates/mobile/coach/event_form.html index 824a48f..2d58e78 100644 --- a/mobile/templates/mobile/coach/event_form.html +++ b/mobile/templates/mobile/coach/event_form.html @@ -70,6 +70,11 @@ {{ form.start }} {% for error in form.start.errors %}

{{ error }}

{% endfor %} +
+ + {{ form.gathering }} +

{% trans "Leave blank if there's no separate meet-up time." %}

+ {% for error in form.gathering.errors %}

{{ error }}

{% endfor %}
@@ -77,6 +82,11 @@ {{ series_form.dtstart }} {% for error in series_form.dtstart.errors %}

{{ error }}

{% endfor %} +
+ + {{ series_form.gathering_minutes_before }} +

{% trans "Leave blank if there's no separate meet-up time." %}

+ {% for error in series_form.gathering_minutes_before.errors %}

{{ error }}

{% endfor %}
@@ -89,9 +99,18 @@
{% include "mobile/coach/_opponent_picker.html" %}
-
- - {{ form.competition }} +
+
+ + {{ form.competition }} +
+
+
+ + {{ form.external_game_id }} +

{% trans "This game's id in the competition's own data source, if it has one." %}

+ {% for error in form.external_game_id.errors %}

{{ error }}

{% endfor %} +
diff --git a/mobile/tests.py b/mobile/tests.py index d0f7c34..fc2684e 100644 --- a/mobile/tests.py +++ b/mobile/tests.py @@ -3700,6 +3700,65 @@ class CoachCreateEventViewTests(TestCase): self.assertEqual(event.opponent, opponent) self.assertEqual(event.competition, "Regional League") + def test_can_set_a_competition_id_for_a_game(self): + self.client.force_login(self.user) + + self._post(kind="game", title="Away game", external_game_id="4460") + + event = Event.objects.get(title="Away game") + self.assertEqual(event.external_game_id, "4460") + + def test_competition_id_is_optional(self): + self.client.force_login(self.user) + + self._post(title="Plain practice") + + event = Event.objects.get(title="Plain practice") + self.assertEqual(event.external_game_id, "") + + def test_can_set_a_gathering_time(self): + self.client.force_login(self.user) + gathering = timezone.localtime(timezone.now() + datetime.timedelta(days=5, hours=-1)).strftime("%Y-%m-%dT%H:%M") + + self._post(title="Early gather practice", gathering=gathering) + + event = Event.objects.get(title="Early gather practice") + self.assertIsNotNone(event.gathering) + + def test_gathering_time_is_optional(self): + self.client.force_login(self.user) + + self._post(title="No gather practice") + + event = Event.objects.get(title="No gather practice") + self.assertIsNone(event.gathering) + + def test_recurring_can_set_a_gathering_offset(self): + self.client.force_login(self.user) + dtstart = timezone.localtime(timezone.now() + datetime.timedelta(days=1)).strftime("%Y-%m-%dT%H:%M") + + self.client.post( + reverse("mobile:coach_create_event"), + { + "is_recurring": "on", + "kind": "training", + "title": "Gathering series", + "teams": [str(self.team.pk)], + "dtstart": dtstart, + "frequency": "weekly", + "interval": "1", + "weekdays": ["MO"], + "gathering_minutes_before": "30", + }, + HTTP_HOST="ajax-united.rosterchief.app", + ) + + series = EventSeries.objects.get(title="Gathering series") + self.assertEqual(series.gathering_offset, datetime.timedelta(minutes=30)) + occurrence = series.occurrences.first() + self.assertIsNotNone(occurrence) + self.assertEqual(occurrence.gathering, occurrence.start - datetime.timedelta(minutes=30)) + def test_invited_members_pool_excludes_the_current_roster(self): on_roster = Member.objects.create(first_name="On", last_name="Roster") TeamMembership.objects.create(team=self.team, member=on_roster, season=self.season)