Skip to content

Commit

Permalink
Org: Make editing rule for allocations possible.
Browse files Browse the repository at this point in the history
TYPE: Feature
LINK: OGC-1397
  • Loading branch information
cyrillkuettel authored Oct 7, 2024
1 parent 1b5993f commit 3f9655c
Show file tree
Hide file tree
Showing 7 changed files with 194 additions and 5 deletions.
7 changes: 6 additions & 1 deletion src/onegov/event/collections/events.py
Original file line number Diff line number Diff line change
Expand Up @@ -435,7 +435,12 @@ def from_ical(
if not hasattr(tags, '__iter__'):
tags = [tags]

tags = [str(c) for tag in tags for c in tag.cats if c]
# Filter out strings or invalid objects without 'cats'
tags = [str(c) for tag in tags
if not isinstance(tag, str) and hasattr(tag, 'cats')
for c in tag.cats
if c
]

uid = str(vevent.get('uid', ''))
title = str(escape(vevent.get('summary', '')))
Expand Down
17 changes: 17 additions & 0 deletions src/onegov/org/locale/de_CH/LC_MESSAGES/onegov.org.po
Original file line number Diff line number Diff line change
Expand Up @@ -4849,6 +4849,15 @@ msgstr ""
"Regeln stellen sicher dass die Einteilungen zwischen Start und Ende bestehen "
"und dass sie im gewählten Interval verlängert werden."

msgid "Rule not found"
msgstr "Regel nicht gefunden"

msgid "Rule updated"
msgstr "Regel aktualisiert"

msgid "Edit Rule"
msgstr "Regel bearbeiten"

msgid "The rule was stopped"
msgstr "Die Regel wurde gestoppt"

Expand Down Expand Up @@ -6146,6 +6155,14 @@ msgstr "Ein Konto wurde für Sie erstellt"
msgid "The user was created successfully"
msgstr "Der Benutzer wurde erfolgreich erstellt"

#~ msgid ""
#~ "Rules ensure that the allocations between start/end exist and that they "
#~ "are extended beyond those dates at the given intervals. Editing a rule "
#~ "may create new allocations."
#~ msgstr ""
#~ "Regeln stellen sicher dass die Einteilungen zwischen Start und Ende "
#~ "bestehen und dass sie im gewählten Interval verlängert werden."

#~ msgid "Directory change rejected"
#~ msgstr "Verzeichnisänderung abgelehnt"

Expand Down
9 changes: 9 additions & 0 deletions src/onegov/org/locale/fr_CH/LC_MESSAGES/onegov.org.po
Original file line number Diff line number Diff line change
Expand Up @@ -4863,6 +4863,15 @@ msgstr ""
"existent et qu'elles sont prolongées au-delà de ces dates à des intervalles "
"donnés."

msgid "Rule not found"
msgstr "Règle non trouvée"

msgid "Rule updated"
msgstr "Règle mise à jour"

msgid "Edit Rule"
msgstr "Modifier la règle"

msgid "The rule was stopped"
msgstr "La règle a été arrêtée"

Expand Down
9 changes: 9 additions & 0 deletions src/onegov/org/locale/it_CH/LC_MESSAGES/onegov.org.po
Original file line number Diff line number Diff line change
Expand Up @@ -4854,6 +4854,15 @@ msgstr ""
"Le regole assicurano che le allocazioni tra inizio/fine esistano e che siano "
"estese oltre tali date agli intervalli indicati. "

msgid "Rule not found"
msgstr "Regola non trovata"

msgid "Rule updated"
msgstr "Regola aggiornata"

msgid "Edit Rule"
msgstr "Modifica regola"

msgid "The rule was stopped"
msgstr "La regola è stata arrestata"

Expand Down
96 changes: 96 additions & 0 deletions src/onegov/org/views/allocation.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
from datetime import timedelta
from libres.db.models import ReservedSlot
from libres.modules.errors import LibresError

from onegov.core.security import Public, Private, Secret
from onegov.core.utils import is_uuid
from onegov.form import merge_forms
Expand Down Expand Up @@ -163,6 +164,10 @@ def actions_for_rule(rule: dict[str, Any]) -> 'Iterator[Link]':
)
)
)
yield Link(
text=_('Edit'),
url=link_for_rule(rule, 'edit-rule'),
)

def rules_with_actions() -> 'Iterator[RenderData]':
form_class = get_allocation_rule_form_class(self, request)
Expand Down Expand Up @@ -431,6 +436,96 @@ def handle_allocation_rule(
}


@OrgApp.form(model=Resource, template='form.pt', name='edit-rule',
permission=Private, form=get_allocation_rule_form_class)
def handle_edit_rule(
self: Resource, request: 'OrgRequest', form: AllocationRuleForm,
layout: AllocationRulesLayout | None = None
) -> 'RenderData | Response':
request.assert_valid_csrf_token()
layout = layout or AllocationRulesLayout(self, request)

rule_id = rule_id_from_request(request)

if form.submitted(request):

# all the slots
slots = self.scheduler.managed_reserved_slots()
slots = slots.with_entities(ReservedSlot.allocation_id)

# all the reservations
reservations = self.scheduler.managed_reservations()
reservations = reservations.with_entities(Reservation.target)

candidates = self.scheduler.managed_allocations()
candidates = candidates.filter(
func.json_extract_path_text(
func.cast(Allocation.data, JSON), 'rule'
) == rule_id
)
# .. without the ones with slots
candidates = candidates.filter(
not_(Allocation.id.in_(slots.subquery())))

# .. without the ones with reservations
candidates = candidates.filter(
not_(Allocation.group.in_(reservations.subquery())))

# delete the allocations
deleted_count = candidates.delete('fetch')

# Update the rule itself
rules = self.content.get('rules', [])
for i, rule in enumerate(rules):
if rule['id'] == rule_id:
updated_rule = form.rule
updated_rule['last_run'] = utcnow() # Reset last_run
updated_rule['iteration'] = 0 # Reset iteration count
rules[i] = updated_rule
break
self.content['rules'] = rules

# Apply the updated rule
new_allocations_count = form.apply(self)

request.success(
_(
'Rule updated. ${deleted} allocations removed, '
'${created} new allocations created.',
mapping={
'deleted': deleted_count,
'created': new_allocations_count,
},
)
)
return request.redirect(request.link(self, name='rules'))

# Pre-populate the form with existing rule data
existing_rule = next(
(
rule
for rule in self.content.get('rules', [])
if rule['id'] == rule_id
),
None,
)
if existing_rule is None:
request.message(_('Rule not found'), 'warning')
return request.redirect(request.link(self, name='rules'))

form.rule = existing_rule
return {
'layout': layout,
'title': _('Edit Rule'),
'form': form,
'helptext': _(
'Rules ensure that the allocations between start/end exist and '
'that they are extended beyond those dates at the given '
'intervals. '
)
}


def rule_id_from_request(request: 'OrgRequest') -> str:
""" Returns the rule_id from the request params, ensuring that
an actual uuid is returned.
Expand Down Expand Up @@ -566,3 +661,4 @@ def handle_delete_rule(self: Resource, request: 'OrgRequest') -> None:
'n': count
})
)

14 changes: 13 additions & 1 deletion src/onegov/town6/views/allocation.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@
from onegov.org.views.allocation import (
get_new_allocation_form_class, get_edit_allocation_form_class,
get_allocation_rule_form_class, view_allocation_rules,
handle_new_allocation, handle_edit_allocation, handle_allocation_rule)
handle_new_allocation, handle_edit_allocation, handle_allocation_rule,
handle_edit_rule)
from onegov.town6 import TownApp
from onegov.reservation import Allocation
from onegov.reservation import Resource
Expand Down Expand Up @@ -62,3 +63,14 @@ def town_handle_allocation_rule(
) -> 'RenderData | Response':
return handle_allocation_rule(
self, request, form, AllocationRulesLayout(self, request))


@TownApp.form(model=Resource, template='form.pt', name='edit-rule',
permission=Private, form=get_allocation_rule_form_class)
def town_handle_edit_rule(
self: Resource,
request: 'TownRequest',
form: 'AllocationRuleForm',
) -> 'RenderData | Response':
return handle_edit_rule(
self, request, form, AllocationRulesLayout(self, request))
47 changes: 44 additions & 3 deletions tests/onegov/org/test_views_resources.py
Original file line number Diff line number Diff line change
Expand Up @@ -2260,10 +2260,51 @@ def run_cronjob():

assert count_allocations() == 7

page = client.get('/resource/room').click("Regeln")
page.click('Löschen')

assert count_allocations() == 1

def test_allocation_rules_edit(client):
client.login_admin()

resources = client.get('/resources')

page = resources.click('Raum')
page.form['title'] = 'Room'
page.form.submit()

def count_allocations():
s = '2000-01-01'
e = '2050-01-31'

return len(client.get(f'/resource/room/slots?start={s}&end={e}').json)

def run_cronjob():
client.get('/resource/room/process-rules')

page = client.get('/resource/room').click("Regeln").click("Regel")
page.form['title'] = 'Täglich'
page.form['extend'] = 'daily'
page.form['start'] = '2019-01-01'
page.form['end'] = '2019-01-02'
page.form['as_whole_day'] = 'yes'

page.select_checkbox('except_for', "Sa")
page.select_checkbox('except_for', "So")

page = page.form.submit().follow()

assert 'Regel aktiv, 2 Einteilungen erstellt' in page
assert count_allocations() == 2

# Modifying the rule applies changes where possible, but
# existing reserved slots remain unaffected.
edit_page = client.get('/resource/room')
edit_page = edit_page.click('Regeln').click('Bearbeiten')
form = edit_page.form
form['title'] = 'Renamed room'

edit_page = form.submit().follow()

assert 'Renamed room' in edit_page


def test_allocation_rules_on_daypasses(client):
Expand Down

0 comments on commit 3f9655c

Please sign in to comment.