Skip to content

Commit

Permalink
FSI: Course Subscribtions
Browse files Browse the repository at this point in the history
Ensure selected course is at least 6 years in the future from the last course the attendee subscribed to

TYPE: Feature
LINK: OGC-1912
  • Loading branch information
BreathingFlesh authored Nov 28, 2024
1 parent 8d665ca commit ddf9e27
Show file tree
Hide file tree
Showing 5 changed files with 118 additions and 16 deletions.
2 changes: 1 addition & 1 deletion .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ repos:
additional_dependencies:
- eslint-react
- repo: https://github.com/PyCQA/bandit
rev: 1.7.10
rev: 1.8.0
hooks:
- id: bandit
args: ["-c", "pyproject.toml", "--quiet"]
Expand Down
48 changes: 39 additions & 9 deletions src/onegov/fsi/forms/subscription.py
Original file line number Diff line number Diff line change
@@ -1,27 +1,31 @@
from datetime import datetime
import pytz
from sqlalchemy import desc
from onegov.form import Form
from onegov.form.fields import ChosenSelectField
from onegov.fsi import _
from onegov.fsi.collections.attendee import CourseAttendeeCollection
from onegov.fsi.collections.course_event import CourseEventCollection
from wtforms.fields import StringField
from wtforms.validators import InputRequired
from onegov.fsi.models import CourseEvent, CourseSubscription


from typing import TYPE_CHECKING
if TYPE_CHECKING:
from onegov.fsi.models import (
CourseAttendee, CourseEvent, CourseSubscription)
CourseAttendee)
from onegov.fsi.request import FsiRequest
from wtforms.fields.choices import _Choice


class SubscriptionFormMixin:

model: 'CourseSubscription'
model: CourseSubscription
request: 'FsiRequest'

@property
def event(self) -> 'CourseEvent':
def event(self) -> CourseEvent:
return self.model.course_event

@property
Expand All @@ -37,7 +41,7 @@ def event_collection(self) -> CourseEventCollection:
def attendee(self) -> 'CourseAttendee | None':
return self.model.attendee

def event_choice(self, event: 'CourseEvent') -> tuple[str, str]:
def event_choice(self, event: CourseEvent) -> tuple[str, str]:
return str(event.id), str(event)

def attendee_choice(
Expand Down Expand Up @@ -125,7 +129,7 @@ def on_request(self) -> None:
self.course_event_id.choices = self.get_event_choices()

@property
def event_from_form(self) -> 'CourseEvent | None':
def event_from_form(self) -> CourseEvent | None:
return self.course_event_id.data and CourseEventCollection(
self.request.session,
show_hidden=True,
Expand All @@ -151,6 +155,32 @@ def ensure_no_other_subscriptions(self) -> bool:
return False
return True

def ensure_6_year_time_interval(self) -> bool:
if self.attendee_id.data and self.course_event_id.data:
last_subscribed_event = self.request.session.query(
CourseEvent).join(CourseSubscription).filter(
CourseSubscription.attendee_id == self.attendee_id.data
).order_by(desc(CourseEvent.start)).first()
if last_subscribed_event and self.event_from_form and (
# Chosen event needs to start at least 6 years after the last
# subscribed event
self.event_from_form.start < datetime(
last_subscribed_event.start.year + 6, 1, 1,
tzinfo=pytz.utc)
):
assert isinstance(self.course_event_id.errors, list)
self.course_event_id.errors.append(
_('The selected course must take place at least 6 years '
'after the last course for which the attendee was '
'registered. The last course for this attendee was '
'on ${date}.', mapping={
'date': last_subscribed_event.start.strftime(
'%d.%m.%Y')}
)
)
return False
return True

def ensure_can_book_if_locked(self) -> bool:
if self.attendee_id.data and self.course_event_id.data:
event = self.event_from_form
Expand Down Expand Up @@ -235,11 +265,11 @@ def attendee_collection(self) -> CourseAttendeeCollection:
the whole list of attendees"""
return CourseAttendeeCollection(self.request.session)

def update_model(self, model: 'CourseSubscription') -> None:
def update_model(self, model: CourseSubscription) -> None:
model.attendee_id = self.attendee_id.data
model.course_event_id = self.course_event_id.data

def apply_model(self, model: 'CourseSubscription') -> None:
def apply_model(self, model: CourseSubscription) -> None:
self.course_event_id.data = model.course_event_id
self.attendee_id.data = model.attendee_id

Expand Down Expand Up @@ -273,7 +303,7 @@ class EditFsiPlaceholderSubscriptionForm(Form, SubscriptionFormMixin):
label=_('Placeholder Description (optional)'),
)

def update_model(self, model: 'CourseSubscription') -> None:
def update_model(self, model: CourseSubscription) -> None:
desc = self.dummy_desc.data
if not desc:
default_desc = self.request.translate(
Expand All @@ -282,7 +312,7 @@ def update_model(self, model: 'CourseSubscription') -> None:
model.course_event_id = self.course_event_id.data
model.dummy_desc = desc

def apply_model(self, model: 'CourseSubscription') -> None:
def apply_model(self, model: CourseSubscription) -> None:
self.course_event_id.data = model.course_event_id
self.dummy_desc.data = model.dummy_desc

Expand Down
17 changes: 15 additions & 2 deletions src/onegov/fsi/locale/de_CH/LC_MESSAGES/onegov.fsi.po
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
msgid ""
msgstr ""
"Project-Id-Version: PACKAGE 1.0\n"
"POT-Creation-Date: 2024-08-13 09:41+0200\n"
"POT-Creation-Date: 2024-11-28 10:06+0100\n"
"PO-Revision-Date: 2020-03-05 14:36+0100\n"
"Last-Translator: Denis Krienbühl <[email protected]>\n"
"Language-Team: German\n"
Expand Down Expand Up @@ -194,8 +194,18 @@ msgstr ""
msgid "There are other subscriptions for the same course in this year"
msgstr "Für dieses Jahr gibt es bereits andere Anmeldungen für diesen Kurs"

#, python-format
msgid ""
"The selected course must take place at least 6 years after the last course "
"for which the attendee was registered. The last course for this attendee was "
"on ${date}."
msgstr ""
"Die gewählte Kursdurchführung muss mindestens 6 Jahre nach der letzten "
"Kursdurchführung, zu welcher der Teilnehmer angemeldet wurde, stattfinden. "
"Die letzte Kursdurchführung für diesen Teilnehmer war am ${date}."

msgid "This course event can't be booked (anymore)."
msgstr "Diese Durchführung kann (nicht) mehr gebucht werden."
msgstr "Diese Durchführung kann nicht (mehr) gebucht werden."

msgid "Placeholder Description (optional)"
msgstr "Platzhalter-Beschreibung (optional)"
Expand Down Expand Up @@ -618,6 +628,9 @@ msgstr "Rot"
msgid "Attendee should have attended the course already, but didn't."
msgstr "Der nächste Kursbesuch ist ausstehend."

msgid "Logo"
msgstr "Logo"

msgid "Dear ${first_name} ${last_name}"
msgstr "Sehr geehrte(r) ${first_name} ${last_name}"

Expand Down
16 changes: 16 additions & 0 deletions src/onegov/fsi/models/course_attendee.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import datetime
import pytz
from onegov.core.orm import Base
from onegov.core.orm.types import UUID, JSON
from sqlalchemy import Boolean
Expand Down Expand Up @@ -255,8 +257,22 @@ def possible_course_events(
excl = session.query(CourseEvent.id).join(CourseSubscription)
excl = excl.filter(CourseSubscription.attendee_id == self.id)
excl = excl.subquery('excl')

last_subscribed_event = session.query(
CourseEvent).join(CourseSubscription).filter(
CourseSubscription.attendee_id == self.id).order_by(
desc(CourseEvent.start)).first()

result = session.query(CourseEvent).filter(CourseEvent.id.notin_(excl))
result = result.filter(CourseEvent.start > utcnow())
if last_subscribed_event:
# Suggested events need to start at least 6 years after the last
# subscribed event
result = result.filter(
CourseEvent.start > datetime.datetime(
last_subscribed_event.start.year + 6, 1, 1,
tzinfo=pytz.utc))

if not show_hidden:
result = result.filter(CourseEvent.hidden_from_public == False)
if not show_locked:
Expand Down
51 changes: 47 additions & 4 deletions tests/onegov/fsi/test_views_subscription.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,8 @@ def test_locked_course_event_reservations(client_with_db):
page.form['presenter_company'] = 'Presenter'
page.form['presenter_email'] = '[email protected]'
page.form['locked_for_subscriptions'] = True
page.form['start'] = '2050-10-04 10:00'
page.form['end'] = '2050-10-04 12:00'
page.form['start'] = '2056-10-04 10:00'
page.form['end'] = '2056-10-04 12:00'
page.form['location'] = 'location'
page.form['max_attendees'] = 20
# goes to the event created
Expand All @@ -28,13 +28,56 @@ def test_locked_course_event_reservations(client_with_db):
# Hinzufügen - Teilnehmer als editor
add_subscription = new.click('Teilnehmer', href='reservations', index=0)
page = add_subscription.form.submit()
assert 'Diese Durchführung kann (nicht) mehr gebucht werden.' in page
assert 'Diese Durchführung kann nicht (mehr) gebucht werden.' in page

client.login_admin()
add_subscription = new.click('Teilnehmer', href='reservations', index=0)
page = add_subscription.form.submit().follow()
assert 'Neue Anmeldung wurde hinzugefügt' in page
assert 'Diese Durchführung kann (nicht) mehr gebucht werden.' not in page
assert 'Diese Durchführung kann nicht (mehr) gebucht werden.' not in page


def test_subscription_to_a_course_event(client_with_db):
client = client_with_db
client.login_admin()
session = client.app.session()
course = session.query(Course).first()

attendee = session.query(CourseAttendee).first()
assert attendee.user_id == session.query(User).filter_by(
role='member').first().id
assert attendee.organisation == 'ORG'

# Add a new course event
page = client.get(f'/fsi/events/add?course_id={course.id}')
page.form['presenter_name'] = 'Presenter'
page.form['presenter_company'] = 'Presenter'
page.form['presenter_email'] = '[email protected]'
page.form['locked_for_subscriptions'] = True
page.form['start'] = '2054-10-04 10:00'
page.form['end'] = '2054-10-04 12:00'
page.form['location'] = 'location'
page.form['max_attendees'] = 20
# goes to the event created
new = page.form.submit().follow()
assert 'Eine neue Durchführung wurde hinzugefügt' in new

coll = CourseEventCollection(session, upcoming_only=True)
events = coll.query().all()
assert len(events) == 3

form = client.get('/fsi/reservations/add')
form.form['attendee_id'] = str(attendee.id)
form.form['course_event_id'] = str(events[1].id)
page = form.form.submit()
assert 'Die gewählte Kursdurchführung muss mindestens 6 Jahre' in page
assert 'Für dieses Jahr gibt es bereits andere Anmeldungen ' not in page

form = client.get('/fsi/reservations/add')
form.form['attendee_id'] = str(attendee.id)
form.form['course_event_id'] = str(events[2].id)
page = form.form.submit().follow()
assert 'Neue Anmeldung wurde hinzugefügt' in page


def test_reservation_collection_view(client_with_db):
Expand Down

0 comments on commit ddf9e27

Please sign in to comment.