From 59ccefa911f537554eda73e4b8d231da22e8bdf5 Mon Sep 17 00:00:00 2001 From: Daniel Grams Date: Wed, 8 Dec 2021 15:25:34 +0100 Subject: [PATCH] Veranstaltungslisten #341 --- cypress/integration/event.js | 19 +- cypress/integration/event_list.js | 69 +++++ cypress/integration/manage.js | 30 +- cypress/support/commands.js | 9 + messages.pot | 211 +++++++------- migrations/versions/eba21922b9b7_.py | 65 +++++ package-lock.json | 266 +++++------------- package.json | 2 +- project/api/__init__.py | 2 + project/api/event/schemas.py | 5 + project/api/event_list/__init__.py | 0 project/api/event_list/resources.py | 157 +++++++++++ project/api/event_list/schemas.py | 112 ++++++++ project/api/organization/resources.py | 72 +++++ project/cli/test.py | 35 +++ project/forms/event_date.py | 1 + project/i10n.py | 1 + project/jinja_filters.py | 2 + project/models.py | 30 ++ project/services/admin_unit.py | 33 +++ project/services/event.py | 10 + project/services/event_search.py | 20 +- .../static/vue/event-lists/add-event.vue.js | 128 +++++++++ project/static/vue/event-lists/create.vue.js | 78 +++++ .../static/vue/event-lists/event-list.vue.js | 121 ++++++++ project/static/vue/event-lists/list.vue.js | 137 +++++++++ project/static/vue/event-lists/read.vue.js | 42 +++ project/static/vue/event-lists/update.vue.js | 104 +++++++ .../update.vue.js | 2 +- project/templates/_macros.html | 47 ++++ project/templates/event/actions.html | 32 ++- project/templates/event/create.html | 24 +- project/templates/event/update.html | 25 +- project/templates/layout.html | 2 + project/templates/layout_vue.html | 35 ++- project/templates/manage/event_lists.html | 42 +++ project/templates/manage/events.html | 31 +- .../translations/de/LC_MESSAGES/messages.mo | Bin 33307 -> 33656 bytes .../translations/de/LC_MESSAGES/messages.po | 214 +++++++------- .../translations/en/LC_MESSAGES/messages.mo | Bin 3489 -> 3565 bytes .../translations/en/LC_MESSAGES/messages.po | 214 +++++++------- project/views/manage.py | 13 + tests/api/test_event_date.py | 7 + tests/api/test_event_list.py | 103 +++++++ tests/api/test_organization.py | 58 +++- tests/seeder.py | 28 ++ tests/test_models.py | 42 +++ tests/utils.py | 8 +- tests/views/test_manage.py | 7 + 49 files changed, 2133 insertions(+), 562 deletions(-) create mode 100644 cypress/integration/event_list.js create mode 100644 migrations/versions/eba21922b9b7_.py create mode 100644 project/api/event_list/__init__.py create mode 100644 project/api/event_list/resources.py create mode 100644 project/api/event_list/schemas.py create mode 100644 project/static/vue/event-lists/add-event.vue.js create mode 100644 project/static/vue/event-lists/create.vue.js create mode 100644 project/static/vue/event-lists/event-list.vue.js create mode 100644 project/static/vue/event-lists/list.vue.js create mode 100644 project/static/vue/event-lists/read.vue.js create mode 100644 project/static/vue/event-lists/update.vue.js create mode 100644 project/templates/manage/event_lists.html create mode 100644 tests/api/test_event_list.py diff --git a/cypress/integration/event.js b/cypress/integration/event.js index a045b4e..1f3694c 100644 --- a/cypress/integration/event.js +++ b/cypress/integration/event.js @@ -83,13 +83,24 @@ describe("Event", () => { }); it("read and actions", () => { + cy.login(); cy.createAdminUnit().then(function (adminUnitId) { cy.createEvent(adminUnitId).then(function (eventId) { - cy.visit("/event/" + eventId); - cy.screenshot("read"); + cy.createEventList(adminUnitId).then(function (eventListId) { + cy.visit("/event/" + eventId); + cy.screenshot("read"); - cy.visit("/event/" + eventId + "/actions"); - cy.screenshot("actions"); + cy.visit("/event/" + eventId + "/actions"); + cy.screenshot("actions"); + + cy.wait(1000); // Wait for Vue to load + cy.get("a:contains(Zu Liste)").click(); + cy.get(".btn:contains(OK)").should("be.visible"); + cy.screenshot("lists"); + + cy.get(".btn:contains(OK)").click(); + cy.get(".btn:contains(OK)").should("not.exist"); + }); }); }); }); diff --git a/cypress/integration/event_list.js b/cypress/integration/event_list.js new file mode 100644 index 0000000..cc392f6 --- /dev/null +++ b/cypress/integration/event_list.js @@ -0,0 +1,69 @@ +describe("Event lists", () => { + it("list", () => { + cy.login(); + cy.createAdminUnit().then(function (adminUnitId) { + cy.visit("/manage/admin_unit/" + adminUnitId + "/event-lists"); + cy.screenshot("list"); + }); + }); + + it("create", () => { + cy.login(); + cy.createAdminUnit().then(function (adminUnitId) { + cy.visit("/manage/admin_unit/" + adminUnitId + "/event-lists"); + cy.visit("/manage/admin_unit/" + adminUnitId + "/event-lists/create"); + + cy.get('input[name=name]').type("Sehr gute Liste"); + cy.screenshot("create"); + cy.get("button[type=submit]").click(); + + cy.url().should( + "not.include", + "/create" + ); + + cy.get('button:contains(Sehr)'); + cy.screenshot("list-filled"); + }); + }); + + it("read", () => { + cy.login(); + cy.createAdminUnit().then(function (adminUnitId) { + cy.createEventList(adminUnitId).then(function (eventListId) { + cy.visit("/manage/admin_unit/" + adminUnitId + "/event-lists/" + eventListId); + cy.screenshot("read"); + }); + }); + }); + + it("updates", () => { + cy.login(); + cy.createAdminUnit().then(function (adminUnitId) { + cy.createEventList(adminUnitId).then(function (eventListId) { + cy.visit("/manage/admin_unit/" + adminUnitId + "/event-lists"); + cy.visit("/manage/admin_unit/" + adminUnitId + "/event-lists/" + eventListId + "/update"); + cy.screenshot("update"); + cy.get("button[type=submit]").click(); + cy.url().should( + "not.include", + "/update" + ); + }); + }); + }); + + it("deletes", () => { + cy.login(); + cy.createAdminUnit().then(function (adminUnitId) { + cy.createEventList(adminUnitId).then(function (eventListId) { + cy.visit("/manage/admin_unit/" + adminUnitId + "/event-lists"); + + cy.get('.dropdown-toggle.btn-link').click(); + cy.get('.b-dropdown.show li:last').click(); + + cy.get('.dropdown-toggle.btn-link').should('not.exist'); + }); + }); + }); +}); diff --git a/cypress/integration/manage.js b/cypress/integration/manage.js index 55bf146..888cdbc 100644 --- a/cypress/integration/manage.js +++ b/cypress/integration/manage.js @@ -23,17 +23,27 @@ describe("Manage", () => { cy.login(); cy.createAdminUnit().then(function (adminUnitId) { cy.createEvent(adminUnitId).then(function (eventId) { - cy.visit("/manage/admin_unit/" + adminUnitId); - cy.url().should( - "include", - "/manage/admin_unit/" + adminUnitId + "/events" - ); - cy.screenshot("events"); + cy.createEventList(adminUnitId).then(function (eventListId) { + cy.visit("/manage/admin_unit/" + adminUnitId); + cy.url().should( + "include", + "/manage/admin_unit/" + adminUnitId + "/events" + ); + cy.screenshot("events"); - cy.get("#toggle-search-btn").click(); - cy.screenshot("search-form"); - cy.screenshotDatepicker("#date_from-user"); - cy.get("#toggle-search-btn").click(); + cy.get("#toggle-search-btn").click(); + cy.screenshot("search-form"); + cy.screenshotDatepicker("#date_from-user"); + cy.get("#toggle-search-btn").click(); + + cy.get('.dropdown-toggle.btn-link').click(); + cy.get('a:contains(Zu Liste)').click(); + cy.get(".btn:contains(OK)").should("be.visible"); + cy.screenshot("lists"); + + cy.get(".btn:contains(OK)").click(); + cy.get(".btn:contains(OK)").should("not.exist"); + }); }); }); }); diff --git a/cypress/support/commands.js b/cypress/support/commands.js index fc6ce26..213e1ac 100644 --- a/cypress/support/commands.js +++ b/cypress/support/commands.js @@ -150,6 +150,15 @@ Cypress.Commands.add("createAdminUnitOrganizationInvitation", (adminUnitId, emai }); }); +Cypress.Commands.add("createEventList", (adminUnitId) => { + return cy + .logexec("flask test event-list-create " + adminUnitId) + .then(function (result) { + let json = JSON.parse(result.stdout); + return json.event_list_id; + }); +}); + Cypress.Commands.add("createSuggestion", (adminUnitId) => { return cy .logexec("flask test suggestion-create " + adminUnitId) diff --git a/messages.pot b/messages.pot index 5057df0..f93be78 100644 --- a/messages.pot +++ b/messages.pot @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: PROJECT VERSION\n" "Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" -"POT-Creation-Date: 2021-11-17 01:07+0100\n" +"POT-Creation-Date: 2021-12-08 13:32+0100\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -166,14 +166,18 @@ msgid "Scope_event:write" msgstr "" #: project/i10n.py:50 -msgid "Scope_organization:read" +msgid "Scope_eventlist:write" msgstr "" #: project/i10n.py:51 -msgid "Scope_organization:write" +msgid "Scope_organization:read" msgstr "" #: project/i10n.py:52 +msgid "Scope_organization:write" +msgstr "" + +#: project/i10n.py:53 msgid "There must be no self-reference." msgstr "" @@ -189,29 +193,29 @@ msgstr "" msgid "message" msgstr "" -#: project/api/organization/resources.py:355 +#: project/api/organization/resources.py:364 #: project/views/admin_unit_member_invitation.py:85 msgid "You have received an invitation" msgstr "" -#: project/forms/admin.py:10 project/templates/layout.html:309 +#: project/forms/admin.py:10 project/templates/layout.html:311 #: project/views/root.py:37 msgid "Terms of service" msgstr "" -#: project/forms/admin.py:11 project/templates/layout.html:313 +#: project/forms/admin.py:11 project/templates/layout.html:315 #: project/views/root.py:45 msgid "Legal notice" msgstr "" #: project/forms/admin.py:12 project/templates/_macros.html:1392 -#: project/templates/layout.html:317 +#: project/templates/layout.html:319 #: project/templates/widget/event_suggestion/create.html:204 #: project/views/admin_unit.py:73 project/views/root.py:53 msgid "Contact" msgstr "" -#: project/forms/admin.py:13 project/templates/layout.html:321 +#: project/forms/admin.py:13 project/templates/layout.html:323 #: project/views/root.py:61 msgid "Privacy" msgstr "" @@ -723,8 +727,8 @@ msgstr "" #: project/forms/event.py:255 project/forms/reference.py:14 #: project/forms/reference.py:27 project/forms/reference_request.py:76 -#: project/templates/event/create.html:379 -#: project/templates/event/update.html:235 +#: project/templates/event/create.html:359 +#: project/templates/event/update.html:216 msgid "Rating" msgstr "" @@ -752,8 +756,8 @@ msgstr "" #: project/forms/event.py:286 project/forms/event.py:295 #: project/forms/event.py:368 project/forms/event_suggestion.py:50 #: project/templates/_macros.html:436 project/templates/_macros.html:596 -#: project/templates/event/create.html:304 -#: project/templates/event/update.html:185 +#: project/templates/event/create.html:284 +#: project/templates/event/update.html:166 #: project/templates/event_place/create.html:31 #: project/templates/event_place/delete.html:13 #: project/templates/event_place/update.html:31 @@ -771,8 +775,8 @@ msgstr "" #: project/forms/event.py:302 project/forms/event.py:311 #: project/forms/event.py:376 project/forms/event.py:439 #: project/forms/event_suggestion.py:60 project/templates/_macros.html:473 -#: project/templates/_macros.html:633 project/templates/event/create.html:273 -#: project/templates/event/update.html:175 +#: project/templates/_macros.html:633 project/templates/event/create.html:253 +#: project/templates/event/update.html:156 #: project/templates/organizer/create.html:27 #: project/templates/organizer/delete.html:13 #: project/templates/organizer/update.html:27 @@ -815,7 +819,7 @@ msgid "" " Organizers." msgstr "" -#: project/forms/event.py:385 project/templates/event/update.html:164 +#: project/forms/event.py:385 project/templates/event/update.html:145 #: project/templates/oauth2_token/list.html:21 msgid "Status" msgstr "" @@ -865,12 +869,12 @@ msgid "Select the public status of the event." msgstr "" #: project/forms/event.py:407 project/templates/event/update.html:5 -#: project/templates/event/update.html:132 +#: project/templates/event/update.html:113 msgid "Update event" msgstr "" #: project/forms/event.py:423 project/templates/_macros.html:1221 -#: project/templates/event/actions.html:47 +#: project/templates/event/actions.html:66 #: project/templates/event/delete.html:6 msgid "Delete event" msgstr "" @@ -913,8 +917,8 @@ msgstr "" msgid "Distance" msgstr "" -#: project/forms/event_date.py:32 project/forms/planing.py:36 -#: project/templates/widget/event_date/list.html:61 +#: project/forms/event_date.py:33 project/forms/planing.py:36 +#: project/templates/widget/event_date/list.html:82 msgid "Find" msgstr "" @@ -954,13 +958,13 @@ msgstr "" msgid "I would like to be notified by email after the review" msgstr "" -#: project/forms/event_suggestion.py:52 project/templates/event/create.html:309 +#: project/forms/event_suggestion.py:52 project/templates/event/create.html:289 msgid "" "Choose where the event takes place. If the venue is not yet in the list, " "just enter it." msgstr "" -#: project/forms/event_suggestion.py:62 project/templates/event/create.html:277 +#: project/forms/event_suggestion.py:62 project/templates/event/create.html:257 msgid "" "Select the organizer. If the organizer is not yet on the list, just enter" " it." @@ -1045,7 +1049,7 @@ msgstr "" #: project/templates/_macros.html:489 project/templates/_macros.html:649 #: project/templates/admin_unit/create.html:28 #: project/templates/admin_unit/update.html:29 -#: project/templates/layout.html:258 +#: project/templates/layout.html:260 msgid "Organization" msgstr "" @@ -1165,9 +1169,9 @@ msgid "Last updated at %(updated_at)s." msgstr "" #: project/templates/_macros.html:385 project/templates/_macros.html:555 -#: project/templates/event/actions.html:12 -#: project/templates/event/create.html:250 -#: project/templates/event/update.html:141 +#: project/templates/event/actions.html:25 +#: project/templates/event/create.html:230 +#: project/templates/event/update.html:122 #: project/templates/widget/event_suggestion/create.html:229 msgid "Event" msgstr "" @@ -1177,7 +1181,7 @@ msgid "Date" msgstr "" #: project/templates/_macros.html:418 project/templates/_macros.html:577 -#: project/templates/_macros.html:1473 project/templates/event/actions.html:32 +#: project/templates/_macros.html:1473 project/templates/event/actions.html:51 msgid "Share" msgstr "" @@ -1274,11 +1278,11 @@ msgstr "" msgid "Choose image file" msgstr "" -#: project/templates/_macros.html:1220 project/templates/event/actions.html:46 +#: project/templates/_macros.html:1220 project/templates/event/actions.html:65 msgid "Edit event" msgstr "" -#: project/templates/_macros.html:1223 project/templates/manage/events.html:45 +#: project/templates/_macros.html:1223 project/templates/manage/events.html:65 msgid "More" msgstr "" @@ -1323,6 +1327,16 @@ msgstr "" msgid "Remove event date" msgstr "" +#: project/templates/_macros.html:1738 project/templates/event/create.html:176 +#: project/templates/event/update.html:99 +#: project/templates/widget/event_suggestion/create.html:129 +msgid "Enter organizer" +msgstr "" + +#: project/templates/_macros.html:1762 +msgid "Enter list name" +msgstr "" + #: project/templates/home.html:28 msgid "Manage" msgstr "" @@ -1334,7 +1348,7 @@ msgstr "" #: project/templates/layout.html:175 project/templates/layout.html:219 #: project/templates/manage/events.html:6 -#: project/templates/manage/events.html:24 +#: project/templates/manage/events.html:41 msgid "Events" msgstr "" @@ -1352,7 +1366,7 @@ msgstr "" msgid "Planing" msgstr "" -#: project/templates/layout.html:187 project/templates/layout.html:271 +#: project/templates/layout.html:187 project/templates/layout.html:273 #: project/templates/oauth2_client/list.html:10 #: project/templates/oauth2_client/read.html:10 #: project/templates/oauth2_token/list.html:10 project/templates/profile.html:4 @@ -1375,64 +1389,69 @@ msgid "Show events" msgstr "" #: project/templates/event/create.html:5 -#: project/templates/event/create.html:241 project/templates/layout.html:226 -#: project/templates/manage/events.html:27 +#: project/templates/event/create.html:221 project/templates/layout.html:226 +#: project/templates/manage/events.html:44 #: project/templates/manage/organizers.html:21 msgid "Create event" msgstr "" -#: project/templates/layout.html:229 +#: project/templates/layout.html:228 +#: project/templates/manage/event_lists.html:4 +msgid "Event lists" +msgstr "" + +#: project/templates/layout.html:231 msgid "Review suggestions" msgstr "" -#: project/templates/layout.html:239 +#: project/templates/layout.html:241 #: project/templates/manage/references_incoming.html:5 #: project/templates/manage/references_outgoing.html:5 msgid "References" msgstr "" -#: project/templates/layout.html:245 +#: project/templates/layout.html:247 #: project/templates/manage/references_incoming.html:9 msgid "Incoming references" msgstr "" -#: project/templates/layout.html:246 +#: project/templates/layout.html:248 #: project/templates/manage/references_outgoing.html:9 msgid "Outgoing references" msgstr "" -#: project/templates/layout.html:248 +#: project/templates/layout.html:250 #: project/templates/manage/reference_requests_incoming.html:9 msgid "Incoming reference requests" msgstr "" -#: project/templates/layout.html:253 +#: project/templates/layout.html:255 #: project/templates/manage/reference_requests_outgoing.html:9 msgid "Outgoing reference requests" msgstr "" -#: project/templates/layout.html:261 project/templates/manage/organizers.html:5 +#: project/templates/layout.html:263 project/templates/manage/organizers.html:5 #: project/templates/manage/organizers.html:9 msgid "Organizers" msgstr "" #: project/templates/event_place/list.html:3 -#: project/templates/event_place/list.html:7 project/templates/layout.html:262 +#: project/templates/event_place/list.html:7 project/templates/layout.html:264 #: project/templates/manage/places.html:5 #: project/templates/manage/places.html:9 msgid "Places" msgstr "" -#: project/templates/layout.html:264 project/templates/manage/members.html:5 +#: project/templates/layout.html:266 project/templates/manage/members.html:5 #: project/templates/manage/members.html:28 msgid "Members" msgstr "" -#: project/templates/layout.html:265 project/templates/manage/relations.html:4 +#: project/templates/layout.html:267 project/templates/manage/relations.html:4 msgid "Relations" msgstr "" -#: project/templates/layout.html:267 +#: project/templates/layout.html:269 #: project/templates/manage/admin_units.html:17 #: project/templates/manage/organization_invitations.html:4 #: project/templates/user/organization_invitations.html:4 @@ -1444,22 +1463,22 @@ msgstr "" #: project/templates/admin/settings.html:8 #: project/templates/admin_unit/update.html:6 #: project/templates/admin_unit/update.html:23 -#: project/templates/layout.html:269 project/templates/manage/widgets.html:12 -#: project/templates/profile.html:19 +#: project/templates/layout.html:271 project/templates/manage/widgets.html:11 +#: project/templates/manage/widgets.html:15 project/templates/profile.html:19 msgid "Settings" msgstr "" -#: project/templates/layout.html:270 project/templates/manage/reviews.html:10 +#: project/templates/layout.html:272 project/templates/manage/reviews.html:10 #: project/templates/manage/widgets.html:5 #: project/templates/manage/widgets.html:9 msgid "Widgets" msgstr "" -#: project/templates/layout.html:281 +#: project/templates/layout.html:283 msgid "Switch organization" msgstr "" -#: project/templates/developer/read.html:4 project/templates/layout.html:331 +#: project/templates/developer/read.html:4 project/templates/layout.html:333 #: project/templates/profile.html:29 msgid "Developer" msgstr "" @@ -1482,7 +1501,7 @@ msgstr "" #: project/templates/admin/admin_units.html:27 #: project/templates/admin/users.html:27 -#: project/templates/manage/events.html:42 +#: project/templates/manage/events.html:59 #: project/templates/manage/members.html:35 #: project/templates/manage/organizers.html:22 #: project/templates/manage/places.html:27 @@ -1494,8 +1513,8 @@ msgstr "" #: project/templates/admin_unit/create.html:58 #: project/templates/admin_unit/update.html:59 -#: project/templates/event/create.html:367 -#: project/templates/event/update.html:223 +#: project/templates/event/create.html:347 +#: project/templates/event/update.html:204 #: project/templates/event_place/create.html:57 #: project/templates/event_place/update.html:57 #: project/templates/organizer/create.html:56 @@ -1597,83 +1616,89 @@ msgstr "" msgid "The review status of your event has been updated." msgstr "" -#: project/templates/event/actions.html:4 -#: project/templates/event/actions.html:9 +#: project/templates/event/actions.html:5 +#: project/templates/event/actions.html:22 msgid "Actions for event" msgstr "" -#: project/templates/event/actions.html:19 +#: project/templates/event/actions.html:32 msgid "Request reference" msgstr "" -#: project/templates/event/actions.html:20 +#: project/templates/event/actions.html:33 #: project/templates/event/reference_request.html:10 msgid "Ask another organization to reference your event on their calendar." msgstr "" -#: project/templates/event/actions.html:25 +#: project/templates/event/actions.html:38 #: project/templates/manage/references_incoming.html:10 msgid "Reference event" msgstr "" -#: project/templates/event/actions.html:26 +#: project/templates/event/actions.html:39 #: project/templates/event/reference.html:9 msgid "Reference this event on your organization's calendar." msgstr "" -#: project/templates/event/actions.html:39 +#: project/templates/event/actions.html:44 +#: project/templates/manage/events.html:63 +msgid "Add to list" +msgstr "" + +#: project/templates/event/actions.html:45 +msgid "Add this event to a list of your organization." +msgstr "" + +#: project/templates/event/actions.html:58 msgid "Duplicate event" msgstr "" -#: project/templates/event/actions.html:42 +#: project/templates/event/actions.html:61 #, python-format msgid "Create another event for %(admin_unit_name)s" msgstr "" -#: project/templates/event/actions.html:43 +#: project/templates/event/actions.html:62 #, python-format msgid "List all events of %(admin_unit_name)s" msgstr "" +#: project/templates/event/actions.html:74 +#: project/templates/manage/events.html:78 +msgid "Add event to list" +msgstr "" + #: project/templates/event/create.html:90 #: project/templates/event/update.html:76 #: project/templates/widget/event_suggestion/create.html:104 msgid "Enter place or address" msgstr "" -#: project/templates/event/create.html:176 -#: project/templates/event/create.html:233 -#: project/templates/event/update.html:99 -#: project/templates/event/update.html:125 -#: project/templates/widget/event_suggestion/create.html:129 -msgid "Enter organizer" -msgstr "" - -#: project/templates/event/create.html:260 -#: project/templates/event/update.html:151 +#: project/templates/event/create.html:240 +#: project/templates/event/update.html:132 msgid "Event dates" msgstr "" -#: project/templates/event/create.html:267 -#: project/templates/event/update.html:158 +#: project/templates/event/create.html:247 +#: project/templates/event/update.html:139 msgid "Add event date" msgstr "" -#: project/templates/event/create.html:294 +#: project/templates/event/create.html:274 msgid "Switch to organizer search" msgstr "" -#: project/templates/event/create.html:329 +#: project/templates/event/create.html:309 msgid "Switch to place search" msgstr "" -#: project/templates/event/create.html:340 -#: project/templates/event/update.html:196 +#: project/templates/event/create.html:320 +#: project/templates/event/update.html:177 msgid "Access" msgstr "" -#: project/templates/event/create.html:354 -#: project/templates/event/update.html:210 +#: project/templates/event/create.html:334 +#: project/templates/event/update.html:191 msgid "Target group" msgstr "" @@ -1744,19 +1769,19 @@ msgstr "" msgid "Member" msgstr "" -#: project/templates/manage/events.html:20 +#: project/templates/manage/events.html:37 msgid "" "The organization is not verified. Events are therefore not publicly " "visible." msgstr "" -#: project/templates/manage/events.html:41 +#: project/templates/manage/events.html:58 #: project/templates/manage/references_incoming.html:19 #: project/templates/manage/references_outgoing.html:19 msgid "View" msgstr "" -#: project/templates/manage/events.html:43 +#: project/templates/manage/events.html:60 #: project/templates/manage/members.html:21 #: project/templates/manage/members.html:36 #: project/templates/manage/organizers.html:23 @@ -1766,7 +1791,7 @@ msgstr "" msgid "Delete" msgstr "" -#: project/templates/manage/events.html:44 +#: project/templates/manage/events.html:61 msgid "Duplicate" msgstr "" @@ -1797,11 +1822,7 @@ msgstr "" msgid "Reviews" msgstr "" -#: project/templates/manage/widgets.html:25 -msgid "Veranstaltungen als iFrame einbetten" -msgstr "" - -#: project/templates/manage/widgets.html:33 +#: project/templates/manage/widgets.html:87 msgid "Link, um Veranstaltungen vorzuschlagen" msgstr "" @@ -1904,7 +1925,7 @@ msgstr "" msgid "Widget" msgstr "" -#: project/templates/widget/event_date/list.html:128 +#: project/templates/widget/event_date/list.html:149 msgid "Print" msgstr "" @@ -1924,7 +1945,7 @@ msgstr "" msgid "Organization successfully updated" msgstr "" -#: project/views/admin.py:68 project/views/manage.py:318 +#: project/views/admin.py:68 project/views/manage.py:331 msgid "Settings successfully updated" msgstr "" @@ -1982,27 +2003,27 @@ msgstr "" msgid "Invitation successfully deleted" msgstr "" -#: project/views/event.py:178 +#: project/views/event.py:179 msgid "Event successfully published" msgstr "" -#: project/views/event.py:180 +#: project/views/event.py:181 msgid "Draft successfully saved" msgstr "" -#: project/views/event.py:223 +#: project/views/event.py:224 msgid "Event successfully updated" msgstr "" -#: project/views/event.py:249 +#: project/views/event.py:250 msgid "Event successfully deleted" msgstr "" -#: project/views/event.py:408 +#: project/views/event.py:409 msgid "Referenced event changed" msgstr "" -#: project/views/event.py:431 +#: project/views/event.py:432 msgid "New event report" msgstr "" diff --git a/migrations/versions/eba21922b9b7_.py b/migrations/versions/eba21922b9b7_.py new file mode 100644 index 0000000..5cfaa60 --- /dev/null +++ b/migrations/versions/eba21922b9b7_.py @@ -0,0 +1,65 @@ +"""empty message + +Revision ID: eba21922b9b7 +Revises: 12aac790ed5e +Create Date: 2021-12-05 14:32:12.165882 + +""" +import sqlalchemy as sa +import sqlalchemy_utils +from alembic import op + +from project import dbtypes + +# revision identifiers, used by Alembic. +revision = "eba21922b9b7" +down_revision = "12aac790ed5e" +branch_labels = None +depends_on = None + + +def upgrade(): + op.create_table( + "eventlist", + sa.Column("id", sa.Integer(), nullable=False), + sa.Column("name", sa.Unicode(length=255), nullable=True), + sa.Column("admin_unit_id", sa.Integer(), nullable=False), + sa.Column("created_at", sa.DateTime(), nullable=True), + sa.Column("updated_at", sa.DateTime(), nullable=True), + sa.Column("created_by_id", sa.Integer(), nullable=True), + sa.Column("updated_by_id", sa.Integer(), nullable=True), + sa.ForeignKeyConstraint( + ["admin_unit_id"], + ["adminunit.id"], + ), + sa.ForeignKeyConstraint( + ["created_by_id"], + ["user.id"], + ), + sa.ForeignKeyConstraint( + ["updated_by_id"], + ["user.id"], + ), + sa.PrimaryKeyConstraint("id"), + ) + op.create_table( + "event_eventlists", + sa.Column("id", sa.Integer(), nullable=False), + sa.Column("event_id", sa.Integer(), nullable=False), + sa.Column("list_id", sa.Integer(), nullable=False), + sa.ForeignKeyConstraint( + ["event_id"], + ["event.id"], + ), + sa.ForeignKeyConstraint( + ["list_id"], + ["eventlist.id"], + ), + sa.PrimaryKeyConstraint("id"), + sa.UniqueConstraint("event_id", "list_id"), + ) + + +def downgrade(): + op.drop_table("event_eventlists") + op.drop_table("eventlist") diff --git a/package-lock.json b/package-lock.json index 6628045..30c2257 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,14 +9,14 @@ "version": "1.0.0", "license": "ISC", "devDependencies": { - "cypress": "^8.5.0", + "cypress": "^9.1.1", "cypress-fail-on-console-error": "^2.1.2" } }, "node_modules/@cypress/request": { - "version": "2.88.6", - "resolved": "https://registry.npmjs.org/@cypress/request/-/request-2.88.6.tgz", - "integrity": "sha512-z0UxBE/+qaESAHY9p9sM2h8Y4XqtsbDCt0/DPOrqA/RZgKi4PkxdpXyK4wCCnSk1xHqWHZZAE+gV6aDAR6+caQ==", + "version": "2.88.10", + "resolved": "https://registry.npmjs.org/@cypress/request/-/request-2.88.10.tgz", + "integrity": "sha512-Zp7F+R93N0yZyG34GutyTNr+okam7s/Fzc1+i3kcqOP8vk6OuajuE9qZJ6Rs+10/1JFtXFYMdyarnU1rZuJesg==", "dev": true, "dependencies": { "aws-sign2": "~0.7.0", @@ -26,8 +26,7 @@ "extend": "~3.0.2", "forever-agent": "~0.6.1", "form-data": "~2.3.2", - "har-validator": "~5.1.3", - "http-signature": "~1.2.0", + "http-signature": "~1.3.6", "is-typedarray": "~1.0.0", "isstream": "~0.1.2", "json-stringify-safe": "~5.0.1", @@ -138,22 +137,6 @@ "node": ">=8" } }, - "node_modules/ajv": { - "version": "6.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", - "dev": true, - "dependencies": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, "node_modules/ansi-colors": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz", @@ -223,9 +206,9 @@ ] }, "node_modules/asn1": { - "version": "0.2.4", - "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.4.tgz", - "integrity": "sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg==", + "version": "0.2.6", + "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.6.tgz", + "integrity": "sha512-ix/FxPn0MDjeyJ7i/yoHGFt/EX6LyNbxSEhPPXODPL+KB0VPk86UYfL0lMdy+KCnv+fmvIzySwaK5COwqVbWTQ==", "dev": true, "dependencies": { "safer-buffer": "~2.1.0" @@ -568,20 +551,20 @@ } }, "node_modules/cypress": { - "version": "8.5.0", - "resolved": "https://registry.npmjs.org/cypress/-/cypress-8.5.0.tgz", - "integrity": "sha512-MMkXIS+Ro2KETn4gAlG3tIc/7FiljuuCZP0zpd9QsRG6MZSyZW/l1J3D4iQM6WHsVxuX4rFChn5jPFlC2tNSvQ==", + "version": "9.1.1", + "resolved": "https://registry.npmjs.org/cypress/-/cypress-9.1.1.tgz", + "integrity": "sha512-yWcYD8SEQ8F3okFbRPqSDj5V0xhrZBT5QRIH+P1J2vYvtEmZ4KGciHE7LCcZZLILOrs7pg4WNCqkj/XRvReQlQ==", "dev": true, "hasInstallScript": true, "dependencies": { - "@cypress/request": "^2.88.6", + "@cypress/request": "^2.88.10", "@cypress/xvfb": "^1.2.4", "@types/node": "^14.14.31", "@types/sinonjs__fake-timers": "^6.0.2", "@types/sizzle": "^2.3.2", "arch": "^2.2.0", "blob-util": "^2.0.2", - "bluebird": "^3.7.2", + "bluebird": "3.7.2", "cachedir": "^2.3.0", "chalk": "^4.1.0", "check-more-types": "^2.24.0", @@ -609,7 +592,6 @@ "ospath": "^1.2.2", "pretty-bytes": "^5.6.0", "proxy-from-env": "1.0.0", - "ramda": "~0.27.1", "request-progress": "^3.0.0", "supports-color": "^8.1.1", "tmp": "~0.2.1", @@ -822,18 +804,6 @@ "node >=0.6.0" ] }, - "node_modules/fast-deep-equal": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", - "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", - "dev": true - }, - "node_modules/fast-json-stable-stringify": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", - "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", - "dev": true - }, "node_modules/fd-slicer": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.1.0.tgz", @@ -985,29 +955,6 @@ "integrity": "sha512-nTnJ528pbqxYanhpDYsi4Rd8MAeaBA67+RZ10CM1m3bTAVFEDcd5AuA4a6W5YkGZ1iNXHzZz8T6TBKLeBuNriQ==", "dev": true }, - "node_modules/har-schema": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz", - "integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI=", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/har-validator": { - "version": "5.1.5", - "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.5.tgz", - "integrity": "sha512-nmT2T0lljbxdQZfspsno9hgrG3Uir6Ks5afism62poxqBM6sDnMEuPmzTq8XN0OEwqKLLdh1jQI3qyE66Nzb3w==", - "deprecated": "this library is no longer supported", - "dev": true, - "dependencies": { - "ajv": "^6.12.3", - "har-schema": "^2.0.0" - }, - "engines": { - "node": ">=6" - } - }, "node_modules/has-flag": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", @@ -1018,18 +965,17 @@ } }, "node_modules/http-signature": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", - "integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=", + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.3.6.tgz", + "integrity": "sha512-3adrsD6zqo4GsTqtO7FyrejHNv+NgiIfAfv68+jVlFmSr9OGy7zrxONceFRLKvnnZA5jbxQBX1u9PpB6Wi32Gw==", "dev": true, "dependencies": { "assert-plus": "^1.0.0", - "jsprim": "^1.2.2", - "sshpk": "^1.7.0" + "jsprim": "^2.0.2", + "sshpk": "^1.14.1" }, "engines": { - "node": ">=0.8", - "npm": ">=1.3.7" + "node": ">=0.10" } }, "node_modules/human-signals": { @@ -1176,15 +1122,9 @@ "dev": true }, "node_modules/json-schema": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz", - "integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM=", - "dev": true - }, - "node_modules/json-schema-traverse": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.4.0.tgz", + "integrity": "sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA==", "dev": true }, "node_modules/json-stringify-safe": { @@ -1206,9 +1146,9 @@ } }, "node_modules/jsprim": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz", - "integrity": "sha1-MT5mvB5cwG5Di8G3SZwuXFastqI=", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-2.0.2.tgz", + "integrity": "sha512-gqXddjPqQ6G40VdnI6T6yObEC+pDNvyP95wdQhkWkg7crHH3km5qP1FsOXEkzEQwnz6gz5qGTn1c2Y52wP3OyQ==", "dev": true, "engines": [ "node >=0.6.0" @@ -1216,7 +1156,7 @@ "dependencies": { "assert-plus": "1.0.0", "extsprintf": "1.3.0", - "json-schema": "0.2.3", + "json-schema": "0.4.0", "verror": "1.10.0" } }, @@ -1346,21 +1286,21 @@ "dev": true }, "node_modules/mime-db": { - "version": "1.49.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.49.0.tgz", - "integrity": "sha512-CIc8j9URtOVApSFCQIF+VBkX1RwXp/oMMOrqdyXSBXq5RWNEsRfyj1kiRnQgmNXmHxPoFIxOroKA3zcU9P+nAA==", + "version": "1.51.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.51.0.tgz", + "integrity": "sha512-5y8A56jg7XVQx2mbv1lu49NR4dokRnhZYTtL+KGfaa27uq4pSTXkwQkFJl4pkRMyNFz/EtYDSkiiEHx3F7UN6g==", "dev": true, "engines": { "node": ">= 0.6" } }, "node_modules/mime-types": { - "version": "2.1.32", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.32.tgz", - "integrity": "sha512-hJGaVS4G4c9TSMYh2n6SQAGrC4RnfU+daP8G7cSCmaqNjiOoUY0VHCMS42pxnQmVF1GWwFhbHWn3RIxCqTmZ9A==", + "version": "2.1.34", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.34.tgz", + "integrity": "sha512-6cP692WwGIs9XXdOO4++N+7qjqv0rqxxVvJ3VHPh/Sc9mVZcQP+ZGhkKiTvWMQRr2tbHkJP/Yn7Y0npb3ZBs4A==", "dev": true, "dependencies": { - "mime-db": "1.49.0" + "mime-db": "1.51.0" }, "engines": { "node": ">= 0.6" @@ -1597,12 +1537,6 @@ "node": ">=0.4.x" } }, - "node_modules/ramda": { - "version": "0.27.1", - "resolved": "https://registry.npmjs.org/ramda/-/ramda-0.27.1.tgz", - "integrity": "sha512-PgIdVpn5y5Yns8vqb8FzBUEYn98V3xcPgawAkkgj0YJ0qDsnHCiNmZYfOGMgOvoB0eWFLpYbhxUR3mxfDIMvpw==", - "dev": true - }, "node_modules/request-progress": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/request-progress/-/request-progress-3.0.0.tgz", @@ -1934,15 +1868,6 @@ "node": ">=8" } }, - "node_modules/uri-js": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", - "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", - "dev": true, - "dependencies": { - "punycode": "^2.1.0" - } - }, "node_modules/url": { "version": "0.11.0", "resolved": "https://registry.npmjs.org/url/-/url-0.11.0.tgz", @@ -2033,9 +1958,9 @@ }, "dependencies": { "@cypress/request": { - "version": "2.88.6", - "resolved": "https://registry.npmjs.org/@cypress/request/-/request-2.88.6.tgz", - "integrity": "sha512-z0UxBE/+qaESAHY9p9sM2h8Y4XqtsbDCt0/DPOrqA/RZgKi4PkxdpXyK4wCCnSk1xHqWHZZAE+gV6aDAR6+caQ==", + "version": "2.88.10", + "resolved": "https://registry.npmjs.org/@cypress/request/-/request-2.88.10.tgz", + "integrity": "sha512-Zp7F+R93N0yZyG34GutyTNr+okam7s/Fzc1+i3kcqOP8vk6OuajuE9qZJ6Rs+10/1JFtXFYMdyarnU1rZuJesg==", "dev": true, "requires": { "aws-sign2": "~0.7.0", @@ -2045,8 +1970,7 @@ "extend": "~3.0.2", "forever-agent": "~0.6.1", "form-data": "~2.3.2", - "har-validator": "~5.1.3", - "http-signature": "~1.2.0", + "http-signature": "~1.3.6", "is-typedarray": "~1.0.0", "isstream": "~0.1.2", "json-stringify-safe": "~5.0.1", @@ -2153,18 +2077,6 @@ "indent-string": "^4.0.0" } }, - "ajv": { - "version": "6.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", - "dev": true, - "requires": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" - } - }, "ansi-colors": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz", @@ -2202,9 +2114,9 @@ "dev": true }, "asn1": { - "version": "0.2.4", - "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.4.tgz", - "integrity": "sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg==", + "version": "0.2.6", + "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.6.tgz", + "integrity": "sha512-ix/FxPn0MDjeyJ7i/yoHGFt/EX6LyNbxSEhPPXODPL+KB0VPk86UYfL0lMdy+KCnv+fmvIzySwaK5COwqVbWTQ==", "dev": true, "requires": { "safer-buffer": "~2.1.0" @@ -2475,19 +2387,19 @@ } }, "cypress": { - "version": "8.5.0", - "resolved": "https://registry.npmjs.org/cypress/-/cypress-8.5.0.tgz", - "integrity": "sha512-MMkXIS+Ro2KETn4gAlG3tIc/7FiljuuCZP0zpd9QsRG6MZSyZW/l1J3D4iQM6WHsVxuX4rFChn5jPFlC2tNSvQ==", + "version": "9.1.1", + "resolved": "https://registry.npmjs.org/cypress/-/cypress-9.1.1.tgz", + "integrity": "sha512-yWcYD8SEQ8F3okFbRPqSDj5V0xhrZBT5QRIH+P1J2vYvtEmZ4KGciHE7LCcZZLILOrs7pg4WNCqkj/XRvReQlQ==", "dev": true, "requires": { - "@cypress/request": "^2.88.6", + "@cypress/request": "^2.88.10", "@cypress/xvfb": "^1.2.4", "@types/node": "^14.14.31", "@types/sinonjs__fake-timers": "^6.0.2", "@types/sizzle": "^2.3.2", "arch": "^2.2.0", "blob-util": "^2.0.2", - "bluebird": "^3.7.2", + "bluebird": "3.7.2", "cachedir": "^2.3.0", "chalk": "^4.1.0", "check-more-types": "^2.24.0", @@ -2515,7 +2427,6 @@ "ospath": "^1.2.2", "pretty-bytes": "^5.6.0", "proxy-from-env": "1.0.0", - "ramda": "~0.27.1", "request-progress": "^3.0.0", "supports-color": "^8.1.1", "tmp": "~0.2.1", @@ -2676,18 +2587,6 @@ "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=", "dev": true }, - "fast-deep-equal": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", - "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", - "dev": true - }, - "fast-json-stable-stringify": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", - "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", - "dev": true - }, "fd-slicer": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.1.0.tgz", @@ -2803,22 +2702,6 @@ "integrity": "sha512-nTnJ528pbqxYanhpDYsi4Rd8MAeaBA67+RZ10CM1m3bTAVFEDcd5AuA4a6W5YkGZ1iNXHzZz8T6TBKLeBuNriQ==", "dev": true }, - "har-schema": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz", - "integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI=", - "dev": true - }, - "har-validator": { - "version": "5.1.5", - "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.5.tgz", - "integrity": "sha512-nmT2T0lljbxdQZfspsno9hgrG3Uir6Ks5afism62poxqBM6sDnMEuPmzTq8XN0OEwqKLLdh1jQI3qyE66Nzb3w==", - "dev": true, - "requires": { - "ajv": "^6.12.3", - "har-schema": "^2.0.0" - } - }, "has-flag": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", @@ -2826,14 +2709,14 @@ "dev": true }, "http-signature": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", - "integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=", + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.3.6.tgz", + "integrity": "sha512-3adrsD6zqo4GsTqtO7FyrejHNv+NgiIfAfv68+jVlFmSr9OGy7zrxONceFRLKvnnZA5jbxQBX1u9PpB6Wi32Gw==", "dev": true, "requires": { "assert-plus": "^1.0.0", - "jsprim": "^1.2.2", - "sshpk": "^1.7.0" + "jsprim": "^2.0.2", + "sshpk": "^1.14.1" } }, "human-signals": { @@ -2944,15 +2827,9 @@ "dev": true }, "json-schema": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz", - "integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM=", - "dev": true - }, - "json-schema-traverse": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.4.0.tgz", + "integrity": "sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA==", "dev": true }, "json-stringify-safe": { @@ -2972,14 +2849,14 @@ } }, "jsprim": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz", - "integrity": "sha1-MT5mvB5cwG5Di8G3SZwuXFastqI=", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-2.0.2.tgz", + "integrity": "sha512-gqXddjPqQ6G40VdnI6T6yObEC+pDNvyP95wdQhkWkg7crHH3km5qP1FsOXEkzEQwnz6gz5qGTn1c2Y52wP3OyQ==", "dev": true, "requires": { "assert-plus": "1.0.0", "extsprintf": "1.3.0", - "json-schema": "0.2.3", + "json-schema": "0.4.0", "verror": "1.10.0" } }, @@ -3081,18 +2958,18 @@ "dev": true }, "mime-db": { - "version": "1.49.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.49.0.tgz", - "integrity": "sha512-CIc8j9URtOVApSFCQIF+VBkX1RwXp/oMMOrqdyXSBXq5RWNEsRfyj1kiRnQgmNXmHxPoFIxOroKA3zcU9P+nAA==", + "version": "1.51.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.51.0.tgz", + "integrity": "sha512-5y8A56jg7XVQx2mbv1lu49NR4dokRnhZYTtL+KGfaa27uq4pSTXkwQkFJl4pkRMyNFz/EtYDSkiiEHx3F7UN6g==", "dev": true }, "mime-types": { - "version": "2.1.32", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.32.tgz", - "integrity": "sha512-hJGaVS4G4c9TSMYh2n6SQAGrC4RnfU+daP8G7cSCmaqNjiOoUY0VHCMS42pxnQmVF1GWwFhbHWn3RIxCqTmZ9A==", + "version": "2.1.34", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.34.tgz", + "integrity": "sha512-6cP692WwGIs9XXdOO4++N+7qjqv0rqxxVvJ3VHPh/Sc9mVZcQP+ZGhkKiTvWMQRr2tbHkJP/Yn7Y0npb3ZBs4A==", "dev": true, "requires": { - "mime-db": "1.49.0" + "mime-db": "1.51.0" } }, "mimic-fn": { @@ -3274,12 +3151,6 @@ "integrity": "sha1-sgmEkgO7Jd+CDadW50cAWHhSFiA=", "dev": true }, - "ramda": { - "version": "0.27.1", - "resolved": "https://registry.npmjs.org/ramda/-/ramda-0.27.1.tgz", - "integrity": "sha512-PgIdVpn5y5Yns8vqb8FzBUEYn98V3xcPgawAkkgj0YJ0qDsnHCiNmZYfOGMgOvoB0eWFLpYbhxUR3mxfDIMvpw==", - "dev": true - }, "request-progress": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/request-progress/-/request-progress-3.0.0.tgz", @@ -3521,15 +3392,6 @@ "integrity": "sha512-KK8xQ1mkzZeg9inewmFVDNkg3l5LUhoq9kN6iWYB/CC9YMG8HA+c1Q8HwDe6dEX7kErrEVNVBO3fWsVq5iDgtw==", "dev": true }, - "uri-js": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", - "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", - "dev": true, - "requires": { - "punycode": "^2.1.0" - } - }, "url": { "version": "0.11.0", "resolved": "https://registry.npmjs.org/url/-/url-0.11.0.tgz", diff --git a/package.json b/package.json index 4f0ffa5..67ea0f2 100644 --- a/package.json +++ b/package.json @@ -22,7 +22,7 @@ }, "homepage": "https://github.com/DanielGrams/gsevpt#readme", "devDependencies": { - "cypress": "^8.5.0", + "cypress": "^9.1.1", "cypress-fail-on-console-error": "^2.1.2" } } diff --git a/project/api/__init__.py b/project/api/__init__.py index f85e194..14ef9fd 100644 --- a/project/api/__init__.py +++ b/project/api/__init__.py @@ -124,6 +124,7 @@ scope_list = [ "event:write", "organization:read", "organization:write", + "eventlist:write", ] scopes = {k: get_localized_scope(k) for v, k in enumerate(scope_list)} @@ -191,6 +192,7 @@ import project.api.dump.resources import project.api.event.resources import project.api.event_category.resources import project.api.event_date.resources +import project.api.event_list.resources import project.api.event_reference.resources import project.api.organization.resources import project.api.organization_invitation.resources diff --git a/project/api/event/schemas.py b/project/api/event/schemas.py index 9e71fe0..17fa1fe 100644 --- a/project/api/event/schemas.py +++ b/project/api/event/schemas.py @@ -37,6 +37,7 @@ from project.api.schemas import ( PaginationResponseSchema, SQLAlchemyBaseSchema, TrackableSchemaMixin, + WriteIdSchemaMixin, ) from project.models import ( Event, @@ -57,6 +58,10 @@ class EventIdSchema(EventModelSchema, IdSchemaMixin): pass +class EventWriteIdSchema(EventModelSchema, WriteIdSchemaMixin): + pass + + class EventBaseSchemaMixin(TrackableSchemaMixin): name = marshmallow.auto_field( required=True, diff --git a/project/api/event_list/__init__.py b/project/api/event_list/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/project/api/event_list/resources.py b/project/api/event_list/resources.py new file mode 100644 index 0000000..cf05a2a --- /dev/null +++ b/project/api/event_list/resources.py @@ -0,0 +1,157 @@ +from flask import make_response +from flask_apispec import doc, marshal_with, use_kwargs + +from project import db +from project.access import access_or_401, login_api_user_or_401 +from project.api import add_api_resource +from project.api.event.schemas import EventListRequestSchema, EventListResponseSchema +from project.api.event_list.schemas import ( + EventListPatchRequestSchema, + EventListSchema, + EventListUpdateRequestSchema, +) +from project.api.resources import BaseResource, require_api_access +from project.models import Event, EventList +from project.services.event import get_events_query +from project.services.event_search import EventSearchParams + + +class EventListModelResource(BaseResource): + @doc(summary="Get event list", tags=["Event Lists"]) + @marshal_with(EventListSchema) + def get(self, id): + return EventList.query.get_or_404(id) + + @doc( + summary="Update event list", + tags=["Event Lists"], + security=[{"oauth2": ["eventlist:write"]}], + ) + @use_kwargs(EventListUpdateRequestSchema, location="json", apply=False) + @marshal_with(None, 204) + @require_api_access("eventlist:write") + def put(self, id): + login_api_user_or_401() + event_list = EventList.query.get_or_404(id) + access_or_401(event_list.adminunit, "admin_unit:update") + + event_list = self.update_instance( + EventListUpdateRequestSchema, instance=event_list + ) + db.session.commit() + + return make_response("", 204) + + @doc( + summary="Patch event list", + tags=["Event Lists"], + security=[{"oauth2": ["eventlist:write"]}], + ) + @use_kwargs(EventListPatchRequestSchema, location="json", apply=False) + @marshal_with(None, 204) + @require_api_access("eventlist:write") + def patch(self, id): + login_api_user_or_401() + event_list = EventList.query.get_or_404(id) + access_or_401(event_list.adminunit, "admin_unit:update") + + event_list = self.update_instance( + EventListPatchRequestSchema, instance=event_list + ) + db.session.commit() + + return make_response("", 204) + + @doc( + summary="Delete event list", + tags=["Event Lists"], + security=[{"oauth2": ["eventlist:write"]}], + ) + @marshal_with(None, 204) + @require_api_access("eventlist:write") + def delete(self, id): + login_api_user_or_401() + event_list = EventList.query.get_or_404(id) + access_or_401(event_list.adminunit, "admin_unit:update") + + db.session.delete(event_list) + db.session.commit() + + return make_response("", 204) + + +class EventListEventListResource(BaseResource): + @doc( + summary="List events of event lists", + tags=["Event Lists", "Events"], + ) + @use_kwargs(EventListRequestSchema, location=("query")) + @marshal_with(EventListResponseSchema) + def get(self, id, **kwargs): + params = EventSearchParams() + params.event_list_id = id + pagination = get_events_query(params).paginate() + return pagination + + +class EventListEventListWriteResource(BaseResource): + @doc( + summary="Add event", + tags=["Event Lists", "Events"], + security=[{"oauth2": ["eventlist:write"]}], + ) + @marshal_with(None, 204) + @require_api_access("eventlist:write") + def put(self, id, event_id): + login_api_user_or_401() + event_list = EventList.query.get_or_404(id) + access_or_401(event_list.adminunit, "admin_unit:update") + event = Event.query.get_or_404(event_id) + + exists = ( + Event.query.with_parent(event_list).filter(Event.id == event_id).first() + ) + if not exists: + event_list.events.append(event) + db.session.commit() + + return make_response("", 204) + + @doc( + summary="Remove event", + tags=["Event Lists", "Events"], + security=[{"oauth2": ["eventlist:write"]}], + ) + @marshal_with(None, 204) + @require_api_access("eventlist:write") + def delete(self, id, event_id): + login_api_user_or_401() + event_list = EventList.query.get_or_404(id) + access_or_401(event_list.adminunit, "admin_unit:update") + event = Event.query.get_or_404(event_id) + + exists = ( + Event.query.with_parent(event_list).filter(Event.id == event_id).first() + ) + if exists: + event_list.events.remove(event) + db.session.commit() + + return make_response("", 204) + + +add_api_resource( + EventListModelResource, "/event-lists/", "api_v1_event_list_model" +) + +add_api_resource( + EventListEventListResource, + "/event-lists//events", + "api_v1_event_list_event_list", +) + +add_api_resource( + EventListEventListWriteResource, + "/event-lists//events/", + "api_v1_event_list_event_list_write", +) diff --git a/project/api/event_list/schemas.py b/project/api/event_list/schemas.py new file mode 100644 index 0000000..6fb6a51 --- /dev/null +++ b/project/api/event_list/schemas.py @@ -0,0 +1,112 @@ +from marshmallow import fields, pre_dump, validate + +from project.api import marshmallow +from project.api.event.schemas import EventWriteIdSchema +from project.api.organization.schemas import OrganizationRefSchema +from project.api.schemas import ( + IdSchemaMixin, + PaginationRequestSchema, + PaginationResponseSchema, + SQLAlchemyBaseSchema, + TrackableSchemaMixin, + WriteIdSchemaMixin, +) +from project.models import EventList + + +class EventListModelSchema(SQLAlchemyBaseSchema): + class Meta: + model = EventList + load_instance = True + + +class EventListIdSchema(EventListModelSchema, IdSchemaMixin): + pass + + +class EventListWriteIdSchema(EventListModelSchema, WriteIdSchemaMixin): + pass + + +class EventListBaseSchemaMixin(TrackableSchemaMixin): + name = marshmallow.auto_field( + required=True, validate=validate.Length(min=3, max=255) + ) + + +class EventListSchema(EventListIdSchema, EventListBaseSchemaMixin): + organization = fields.Nested(OrganizationRefSchema, attribute="adminunit") + + +class EventListRefSchema(EventListIdSchema): + name = marshmallow.auto_field() + + +class EventListListRequestSchema(PaginationRequestSchema): + name = fields.Str( + metadata={"description": "Looks for name."}, + ) + + +class EventListListResponseSchema(PaginationResponseSchema): + items = fields.List( + fields.Nested(EventListRefSchema), metadata={"description": "Event lists"} + ) + + +class EventListStatusSchema(marshmallow.Schema): + event_list = fields.Nested(EventListRefSchema) + contains_event = fields.Boolean( + required=True, metadata={"description": "True if list contains event."} + ) + + @pre_dump(pass_many=True) + def unwrap_tuple(self, data, many, **kwargs): + return {"event_list": data[0], "contains_event": data[1] > 0} + + +class EventListStatusListResponseSchema(PaginationResponseSchema): + items = fields.List( + fields.Nested(EventListStatusSchema), + metadata={"description": "Event list stati"}, + ) + + +class EventListWriteSchemaMixin(object): + pass + + +class EventListCreateRequestSchema( + EventListModelSchema, + EventListBaseSchemaMixin, + EventListWriteSchemaMixin, +): + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + self.make_post_schema() + + +class EventListUpdateRequestSchema( + EventListModelSchema, + EventListBaseSchemaMixin, +): + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + self.make_post_schema() + + +class EventListPatchRequestSchema( + EventListModelSchema, + EventListBaseSchemaMixin, +): + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + self.make_patch_schema() + + +class EventListEventRequestSchema(marshmallow.Schema): + event = fields.Nested( + EventWriteIdSchema, + required=True, + metadata={"description": "Event."}, + ) diff --git a/project/api/organization/resources.py b/project/api/organization/resources.py index ae7ba5f..59c489f 100644 --- a/project/api/organization/resources.py +++ b/project/api/organization/resources.py @@ -24,6 +24,13 @@ from project.api.event_date.schemas import ( EventDateSearchRequestSchema, EventDateSearchResponseSchema, ) +from project.api.event_list.schemas import ( + EventListCreateRequestSchema, + EventListIdSchema, + EventListListRequestSchema, + EventListListResponseSchema, + EventListStatusListResponseSchema, +) from project.api.event_reference.schemas import ( EventReferenceListRequestSchema, EventReferenceListResponseSchema, @@ -63,6 +70,8 @@ from project.oauth2 import require_oauth from project.services.admin_unit import ( get_admin_unit_invitation_query, get_admin_unit_query, + get_event_list_query, + get_event_list_status_query, get_organizer_query, get_place_query, ) @@ -360,6 +369,59 @@ class OrganizationOrganizationInvitationListResource(BaseResource): return invitation, 201 +class OrganizationEventListListResource(BaseResource): + @doc( + summary="List event lists of organization", + tags=["Organizations", "Event Lists"], + ) + @use_kwargs(EventListListRequestSchema, location=("query")) + @marshal_with(EventListListResponseSchema) + def get(self, id, **kwargs): + admin_unit = AdminUnit.query.get_or_404(id) + name = kwargs["name"] if "name" in kwargs else None + + pagination = get_event_list_query(admin_unit.id, name).paginate() + return pagination + + @doc( + summary="Add new event list", + tags=["Organizations", "Event Lists"], + security=[{"oauth2": ["eventlist:write"]}], + ) + @use_kwargs(EventListCreateRequestSchema, location="json", apply=False) + @marshal_with(EventListIdSchema, 201) + @require_api_access("eventlist:write") + def post(self, id): + login_api_user_or_401() + admin_unit = get_admin_unit_for_manage_or_404(id) + access_or_401(admin_unit, "admin_unit:update") + + event_list = self.create_instance( + EventListCreateRequestSchema, admin_unit_id=admin_unit.id + ) + db.session.add(event_list) + db.session.commit() + + return event_list, 201 + + +class OrganizationEventListStatusListResource(BaseResource): + @doc( + summary="List event lists of organization with status", + tags=["Organizations", "Event Lists"], + ) + @use_kwargs(EventListListRequestSchema, location=("query")) + @marshal_with(EventListStatusListResponseSchema) + def get(self, id, event_id, **kwargs): + admin_unit = AdminUnit.query.get_or_404(id) + name = kwargs["name"] if "name" in kwargs else None + + pagination = get_event_list_status_query( + admin_unit.id, event_id, name + ).paginate() + return pagination + + add_api_resource(OrganizationResource, "/organizations/", "api_v1_organization") add_api_resource( OrganizationEventDateSearchResource, @@ -376,6 +438,16 @@ add_api_resource( "/organizations//events", "api_v1_organization_event_list", ) +add_api_resource( + OrganizationEventListListResource, + "/organizations//event-lists", + "api_v1_organization_event_list_list", +) +add_api_resource( + OrganizationEventListStatusListResource, + "/organizations//event-lists/status/", + "api_v1_organization_event_list_status_list", +) add_api_resource(OrganizationListResource, "/organizations", "api_v1_organization_list") add_api_resource( OrganizationOrganizerListResource, diff --git a/project/cli/test.py b/project/cli/test.py index 0e63e10..b95515d 100644 --- a/project/cli/test.py +++ b/project/cli/test.py @@ -15,6 +15,7 @@ from project.models import ( Event, EventAttendanceMode, EventDateDefinition, + EventList, EventReference, EventReferenceRequest, EventReferenceRequestReviewStatus, @@ -442,4 +443,38 @@ def create_event_suggestion(admin_unit_id, freetext): click.echo(json.dumps(result)) +def _add_event_to_list(event_list_id, event_id): + event = Event.query.get(event_id) + event_list = EventList.query.get(event_list_id) + event_list.events.append(event) + db.session.commit() + + +def _create_event_list(admin_unit_id, event_ids=list(), name="My list"): + event_list = EventList() + event_list.name = name + event_list.admin_unit_id = admin_unit_id + db.session.add(event_list) + db.session.commit() + event_list_id = event_list.id + + if type(event_ids) is not list: + event_ids = [event_ids] + + for event_id in event_ids: + _add_event_to_list(event_list_id, event_id) + + return event_list_id + + +@test_cli.command("event-list-create") +@click.argument("admin_unit_id") +def create_event_list(admin_unit_id): + event_list_id = _create_event_list(admin_unit_id) + result = { + "event_list_id": event_list_id, + } + click.echo(json.dumps(result)) + + app.cli.add_command(test_cli) diff --git a/project/forms/event_date.py b/project/forms/event_date.py index 8a8d9f7..57ce4c1 100644 --- a/project/forms/event_date.py +++ b/project/forms/event_date.py @@ -28,6 +28,7 @@ class FindEventDateForm(FlaskForm): coerce=int, choices=distance_choices, ) + event_list_id = HiddenField(validators=[Optional()]) submit = SubmitField(lazy_gettext("Find")) diff --git a/project/i10n.py b/project/i10n.py index 0ad2c07..676b46c 100644 --- a/project/i10n.py +++ b/project/i10n.py @@ -47,6 +47,7 @@ def print_dynamic_texts(): gettext("Scope_organizer:write") gettext("Scope_place:write") gettext("Scope_event:write") + gettext("Scope_eventlist:write") gettext("Scope_organization:read") gettext("Scope_organization:write") gettext("There must be no self-reference.") diff --git a/project/jinja_filters.py b/project/jinja_filters.py index 06c0bae..5e61af1 100644 --- a/project/jinja_filters.py +++ b/project/jinja_filters.py @@ -60,6 +60,7 @@ app.jinja_env.globals.update( @app.context_processor def get_context_processors(): + from project.access import has_access from project.views.utils import get_current_admin_unit def get_manage_menu_options(admin_unit): @@ -85,4 +86,5 @@ def get_context_processors(): return dict( current_admin_unit=get_current_admin_unit(), get_manage_menu_options=get_manage_menu_options, + has_access=has_access, ) diff --git a/project/models.py b/project/models.py index f1d770c..7dfaff3 100644 --- a/project/models.py +++ b/project/models.py @@ -438,6 +438,11 @@ class AdminUnit(db.Model, TrackableMixin): cascade="all, delete-orphan", backref=backref("adminunit", lazy=True), ) + event_lists = relationship( + "EventList", + cascade="all, delete-orphan", + backref=backref("adminunit", lazy=True), + ) location_id = deferred(db.Column(db.Integer, db.ForeignKey("location.id"))) location = db.relationship( "Location", uselist=False, single_parent=True, cascade="all, delete-orphan" @@ -900,6 +905,11 @@ class Event(db.Model, TrackableMixin, EventMixin): secondary="event_coorganizers", backref=backref("co_organized_events", lazy=True), ) + event_lists = relationship( + "EventList", + secondary="event_eventlists", + backref=backref("events", lazy=True), + ) public_status = Column( IntegerEnum(PublicStatus), @@ -1104,6 +1114,26 @@ class EventCoOrganizers(db.Model): ) +class EventList(db.Model, TrackableMixin): + __tablename__ = "eventlist" + __table_args__ = ( + UniqueConstraint( + "name", "admin_unit_id", name="eventreference_name_admin_unit_id" + ), + ) + id = Column(Integer(), primary_key=True) + name = Column(Unicode(255)) + admin_unit_id = db.Column(db.Integer, db.ForeignKey("adminunit.id"), nullable=False) + + +class EventEventLists(db.Model): + __tablename__ = "event_eventlists" + __table_args__ = (UniqueConstraint("event_id", "list_id"),) + id = Column(Integer(), primary_key=True) + event_id = db.Column(db.Integer, db.ForeignKey("event.id"), nullable=False) + list_id = db.Column(db.Integer, db.ForeignKey("eventlist.id"), nullable=False) + + class Analytics(db.Model): __tablename__ = "analytics" id = Column(Integer(), primary_key=True) diff --git a/project/services/admin_unit.py b/project/services/admin_unit.py index ddab9d0..a3026a8 100644 --- a/project/services/admin_unit.py +++ b/project/services/admin_unit.py @@ -8,6 +8,8 @@ from project.models import ( AdminUnitMemberInvitation, AdminUnitMemberRole, AdminUnitRelation, + EventEventLists, + EventList, EventOrganizer, EventPlace, Location, @@ -203,6 +205,37 @@ def get_place_query(admin_unit_id, name=None): return query.order_by(func.lower(EventPlace.name)) +def get_event_list_query(admin_unit_id, name=None, event_id=None): + query = EventList.query.filter(EventList.admin_unit_id == admin_unit_id) + + if name: + like_name = "%" + name + "%" + query = query.filter(EventList.name.ilike(like_name)) + + return query.order_by(func.lower(EventList.name)) + + +def get_event_list_status_query(admin_unit_id, event_id, name=None): + event_count = ( + db.session.query(func.count(EventEventLists.id)) + .filter( + EventEventLists.event_id == event_id, + EventEventLists.list_id == EventList.id, + ) + .label("event_count") + ) + + query = db.session.query(EventList, event_count).filter( + EventList.admin_unit_id == admin_unit_id + ) + + if name: + like_name = "%" + name + "%" + query = query.filter(EventList.name.ilike(like_name)) + + return query.group_by(EventList.id).order_by(func.lower(EventList.name)) + + def insert_admin_unit_relation(source_admin_unit_id: int, target_admin_unit_id: int): result = AdminUnitRelation( source_admin_unit_id=source_admin_unit_id, diff --git a/project/services/event.py b/project/services/event.py index 025afdd..3142450 100644 --- a/project/services/event.py +++ b/project/services/event.py @@ -24,6 +24,7 @@ from project.models import ( EventAttendanceMode, EventCategory, EventDate, + EventList, EventOrganizer, EventPlace, EventReference, @@ -67,6 +68,15 @@ def fill_event_filter(event_filter, params): event_filter, Event.categories.any(EventCategory.id.in_(category_ids)) ) + if params.event_list_id: + if type(params.event_list_id) is list: + event_list_ids = params.event_list_id + else: + event_list_ids = [params.event_list_id] + event_filter = and_( + event_filter, Event.event_lists.any(EventList.id.in_(event_list_ids)) + ) + if params.organizer_id: event_filter = and_(event_filter, Event.organizer_id == params.organizer_id) diff --git a/project/services/event_search.py b/project/services/event_search.py index 3f249aa..d8ac067 100644 --- a/project/services/event_search.py +++ b/project/services/event_search.py @@ -24,6 +24,7 @@ class EventSearchParams(object): self.distance = None self.category_id = None self.organizer_id = None + self.event_list_id = None self.weekday = None self.sort = None @@ -86,6 +87,16 @@ class EventSearchParams(object): self.date_from = today self.date_to = date_set_end_of_day(today + relativedelta(months=3)) + def load_list_param(self, param: str): + item_ids = request.args.getlist(param) + + if "0" in item_ids: + item_ids.remove("0") + + if len(item_ids) > 0: + return item_ids + return None + def load_from_request(self): if "date_from" in request.args: self.date_from_str = request.args["date_from"] @@ -103,11 +114,7 @@ class EventSearchParams(object): self.distance = request.args["distance"] if "category_id" in request.args: - category_ids = request.args.getlist("category_id") - if "0" in category_ids: - category_ids.remove("0") - if len(category_ids) > 0: - self.category_id = category_ids + self.category_id = self.load_list_param("category_id") if "weekday" in request.args: self.weekday = request.args.getlist("weekday") @@ -115,5 +122,8 @@ class EventSearchParams(object): if "organizer_id" in request.args: self.organizer_id = request.args["organizer_id"] + if "event_list_id" in request.args: + self.event_list_id = self.load_list_param("event_list_id") + if "sort" in request.args: self.sort = request.args["sort"] diff --git a/project/static/vue/event-lists/add-event.vue.js b/project/static/vue/event-lists/add-event.vue.js new file mode 100644 index 0000000..fb8b2be --- /dev/null +++ b/project/static/vue/event-lists/add-event.vue.js @@ -0,0 +1,128 @@ +const EventListAddEvent = { + template: ` +
+

{{ $t("comp.instruction") }}

+ + + + +
+ `, + i18n: { + messages: { + en: { + comp: { + instruction: "You can add and modify lists at Events > Event list.", + addedMessage: "Event was added to list", + removedMessage: "Event was removed list", + }, + }, + de: { + comp: { + instruction: "Du kannst Listen unter Veranstaltungen > Veranstaltungslisten hinzufügen und ändern.", + addedMessage: "Veranstaltung wurde der Liste hinzugefügt", + removedMessage: "Veranstaltung wurde von der Liste entfernt", + }, + }, + }, + }, + props: { + eventId: { + type: String + }, + organizationId: { + type: String + } + }, + data: () => ({ + errorMessage: null, + fields: [ + { + key: "name", + label: i18n.t("shared.models.eventList.name"), + }, + ], + totalRows: 0, + currentPage: 1, + perPage: 10, + searchResult: { + items: [], + }, + }), + methods: { + loadTableData(ctx, callback) { + const vm = this; + axios + .get(`/api/v1/organizations/${this.organizationId}/event-lists/status/${this.eventId}`, { + params: { + page: ctx.currentPage, + per_page: ctx.perPage, + }, + withCredentials: true, + handler: this, + }) + .then((response) => { + vm.totalRows = response.data.total; + callback(response.data.items); + }) + .catch(() => { + callback([]); + }); + return null; + }, + refreshTableData() { + this.$refs.table.refresh(); + }, + handleRequestStart() { + this.errorMessage = null; + }, + handleRequestError(error, message) { + this.errorMessage = message; + }, + rowClicked(status, index) { + const eventList = status.event_list; + const url = `/api/v1/event-lists/${eventList.id}/events/${this.eventId}`; + + if (status.contains_event) { + axios.delete(url, { + withCredentials: true, + }) + .then(() => { + this.$root.makeSuccessToast(this.$t("comp.removedMessage")); + status.contains_event = false; + }); + } else { + axios.put(url, { + withCredentials: true, + }) + .then(() => { + this.$root.makeSuccessToast(this.$t("comp.addedMessage")); + status.contains_event = true; + }); + } + }, + }, +}; diff --git a/project/static/vue/event-lists/create.vue.js b/project/static/vue/event-lists/create.vue.js new file mode 100644 index 0000000..ec58cdc --- /dev/null +++ b/project/static/vue/event-lists/create.vue.js @@ -0,0 +1,78 @@ +const EventListCreate = { + template: ` +
+

{{ $t("comp.title") }}

+ + + + {{ $t("shared.cancel") }} + + + {{ $t("shared.submit") }} + + + +
+ `, + i18n: { + messages: { + en: { + comp: { + title: "Add list", + successMessage: "List successfully created", + }, + }, + de: { + comp: { + title: "Liste hinzufügen", + successMessage: "Liste erfolgreich erstellt", + }, + }, + }, + }, + data: () => ({ + isSubmitting: false, + form: { + name: null, + }, + }), + computed: { + adminUnitId() { + return this.$route.params.admin_unit_id + }, + }, + mounted() { + this.form = { + name: null, + } + }, + methods: { + submitForm() { + let data = { + 'name': this.form.name, + }; + + axios + .post(`/api/v1/organizations/${this.adminUnitId}/event-lists`, + data, + { + withCredentials: true, + handleLoading: this.handleSubmitting, + }) + .then(() => { + this.$root.makeSuccessToast(this.$t("comp.successMessage")) + this.goBack() + }) + }, + handleSubmitting(isLoading) { + this.isSubmitting = isLoading; + }, + goBack() { + this.$root.goBack(`/manage/admin_unit/${this.adminUnitId}/event-lists`) + }, + } +}; diff --git a/project/static/vue/event-lists/event-list.vue.js b/project/static/vue/event-lists/event-list.vue.js new file mode 100644 index 0000000..5400f3a --- /dev/null +++ b/project/static/vue/event-lists/event-list.vue.js @@ -0,0 +1,121 @@ +const EventListEventList = { + template: ` +
+ + + + + + +
+ `, + i18n: { + messages: { + en: { + comp: { + removedMessage: "Event successfully removed", + removeConfirmation: "Do you really want to remove the event?", + }, + }, + de: { + comp: { + removedMessage: "Veranstaltung erfolgreich entfernt", + removeConfirmation: "Möchtest du die Veranstaltung wirklich entfernen?", + }, + }, + }, + }, + data: () => ({ + errorMessage: null, + fields: [ + { + key: "name", + label: i18n.t("shared.models.event.name"), + }, + ], + totalRows: 0, + currentPage: 1, + perPage: 10, + searchResult: { + items: [], + }, + }), + computed: { + eventListId() { + return this.$route.params.event_list_id; + }, + }, + methods: { + loadTableData(ctx, callback) { + const vm = this; + axios + .get(`/api/v1/event-lists/${this.eventListId}/events`, { + params: { + page: ctx.currentPage, + per_page: ctx.perPage, + }, + withCredentials: true, + handler: this, + }) + .then((response) => { + vm.totalRows = response.data.total; + callback(response.data.items); + }) + .catch(() => { + callback([]); + }); + return null; + }, + refreshTableData() { + this.$refs.table.refresh(); + }, + handleRequestStart() { + this.errorMessage = null; + }, + handleRequestError(error, message) { + this.errorMessage = message; + }, + viewItem(id) { + window.location.href = `/event/${id}`; + }, + removeItem(id) { + if (confirm(this.$t("comp.removeConfirmation"))) { + axios + .delete(`/api/v1/event-lists/${this.eventListId}/events/${id}`, { + withCredentials: true, + }) + .then(() => { + this.$root.makeSuccessToast(this.$t("comp.removedMessage")); + this.refreshTableData(); + }); + } + }, + }, +}; diff --git a/project/static/vue/event-lists/list.vue.js b/project/static/vue/event-lists/list.vue.js new file mode 100644 index 0000000..e49ef8d --- /dev/null +++ b/project/static/vue/event-lists/list.vue.js @@ -0,0 +1,137 @@ +const EventListList = { + template: ` +
+

{{ $t("shared.models.eventList.listName") }}

+ +
+ {{ $t("comp.addTitle") }} +
+ + + + + + + +
+ `, + i18n: { + messages: { + en: { + comp: { + addTitle: "Add list", + deletedMessage: "List successfully deleted", + deleteConfirmation: "Do you really want to delete the list?", + }, + }, + de: { + comp: { + addTitle: "Liste hinzufügen", + deletedMessage: "Liste erfolgreich gelöscht", + deleteConfirmation: "Möchtest du die Liste wirklich löschen?", + }, + }, + }, + }, + data: () => ({ + errorMessage: null, + fields: [ + { + key: "name", + label: i18n.t("shared.models.eventList.name"), + }, + ], + totalRows: 0, + currentPage: 1, + perPage: 10, + searchResult: { + items: [], + }, + }), + computed: { + adminUnitId() { + return this.$route.params.admin_unit_id; + }, + }, + methods: { + loadTableData(ctx, callback) { + const vm = this; + axios + .get(`/api/v1/organizations/${this.adminUnitId}/event-lists`, { + params: { + page: ctx.currentPage, + per_page: ctx.perPage, + }, + withCredentials: true, + handler: this, + }) + .then((response) => { + vm.totalRows = response.data.total; + callback(response.data.items); + }) + .catch(() => { + callback([]); + }); + return null; + }, + refreshTableData() { + this.$refs.table.refresh(); + }, + handleRequestStart() { + this.errorMessage = null; + }, + handleRequestError(error, message) { + this.errorMessage = message; + }, + viewItem(id) { + this.$router.push({ + path: `/manage/admin_unit/${this.adminUnitId}/event-lists/${id}`, + }); + }, + editItem(id) { + this.$router.push({ + path: `/manage/admin_unit/${this.adminUnitId}/event-lists/${id}/update`, + }); + }, + deleteItem(id) { + if (confirm(this.$t("comp.deleteConfirmation"))) { + axios + .delete(`/api/v1/event-lists/${id}`, { + withCredentials: true, + }) + .then(() => { + this.$root.makeSuccessToast(this.$t("comp.deletedMessage")); + this.refreshTableData(); + }); + } + }, + }, +}; diff --git a/project/static/vue/event-lists/read.vue.js b/project/static/vue/event-lists/read.vue.js new file mode 100644 index 0000000..2cd7822 --- /dev/null +++ b/project/static/vue/event-lists/read.vue.js @@ -0,0 +1,42 @@ +const EventListRead = { + template: ` +
+ +
+

{{ eventList.name }}

+
+

{{ $t("shared.models.event.listName") }}

+ +
+
+ `, + data: () => ({ + isLoading: false, + eventList: null, + }), + computed: { + eventListId() { + return this.$route.params.event_list_id; + }, + }, + mounted() { + this.isLoading = false; + this.eventList = null; + this.loadData(); + }, + methods: { + loadData() { + axios + .get(`/api/v1/event-lists/${this.eventListId}`, { + withCredentials: true, + handleLoading: this.handleLoading, + }) + .then((response) => { + this.eventList = response.data; + }); + }, + handleLoading(isLoading) { + this.isLoading = isLoading; + }, + }, +}; diff --git a/project/static/vue/event-lists/update.vue.js b/project/static/vue/event-lists/update.vue.js new file mode 100644 index 0000000..a437e00 --- /dev/null +++ b/project/static/vue/event-lists/update.vue.js @@ -0,0 +1,104 @@ +const EventListUpdate = { + template: ` +
+

{{ $t("comp.title") }}

+ +
+ + + + {{ $t("shared.cancel") }} + + + {{ $t("shared.submit") }} + + + +
+
+
+ `, + i18n: { + messages: { + en: { + comp: { + title: "Update list", + successMessage: "List successfully updated", + }, + }, + de: { + comp: { + title: "Liste aktualisieren", + successMessage: "Liste erfolgreich aktualisiert", + }, + }, + }, + }, + data: () => ({ + isLoading: false, + isSubmitting: false, + eventList: null, + form: { + name: null, + }, + }), + computed: { + adminUnitId() { + return this.$route.params.admin_unit_id; + }, + eventListId() { + return this.$route.params.event_list_id; + }, + }, + mounted() { + this.isLoading = false; + this.eventList = null; + this.form = { + name: null, + }; + this.loadFormData(); + }, + methods: { + loadFormData() { + axios + .get(`/api/v1/event-lists/${this.eventListId}`, { + withCredentials: true, + handleLoading: this.handleLoading, + }) + .then((response) => { + this.eventList = response.data; + this.form = { + name: response.data.name, + }; + }); + }, + handleLoading(isLoading) { + this.isLoading = isLoading; + }, + submitForm() { + let data = { + 'name': this.form.name, + }; + + axios + .put(`/api/v1/event-lists/${this.eventListId}`, data, { + withCredentials: true, + handleLoading: this.handleSubmitting, + }) + .then(() => { + this.$root.makeSuccessToast(this.$t("comp.successMessage")); + this.goBack(); + }); + }, + handleSubmitting(isLoading) { + this.isSubmitting = isLoading; + }, + goBack() { + this.$root.goBack(`/manage/admin_unit/${this.adminUnitId}/event-lists`); + }, + }, +}; diff --git a/project/static/vue/organization-organization-invitations/update.vue.js b/project/static/vue/organization-organization-invitations/update.vue.js index ee2528d..04d7ccd 100644 --- a/project/static/vue/organization-organization-invitations/update.vue.js +++ b/project/static/vue/organization-organization-invitations/update.vue.js @@ -41,7 +41,7 @@ const OrganizationOrganizationInvitationUpdate = { en: { comp: { title: "Update invitation", - successMessage: "Relation successfully updated", + successMessage: "Invitation successfully updated", }, }, de: { diff --git a/project/templates/_macros.html b/project/templates/_macros.html index 46eef2d..60d9fbf 100644 --- a/project/templates/_macros.html +++ b/project/templates/_macros.html @@ -1715,3 +1715,50 @@ $('#allday').on('change', function() { {% endmacro %} +{% macro render_co_organizer_select2(admin_unit_id) %} +$('#co_organizer_ids').select2({ + width: '100%', + ajax: { + url: "{{ url_for('api_v1_organization_organizer_list', id=admin_unit_id) }}", + dataType: 'json', + delay: 250, + cache: true, + data: function (params) { + return { + name: params.term, + per_page: 5 + }; + }, + processResults: function (data) { + return { + results: data.items.map(p => ({"id": p.id, "text": p.name})) + }; + } + }, + placeholder: "{{ _('Enter organizer') }}" +}); +{% endmacro %} + +{% macro render_event_list_select2(admin_unit_id) %} +$('#event_list_ids').select2({ + width: '100%', + ajax: { + url: "{{ url_for('api_v1_organization_event_list_list', id=admin_unit_id) }}", + dataType: 'json', + delay: 250, + cache: true, + data: function (params) { + return { + name: params.term, + per_page: 5 + }; + }, + processResults: function (data) { + return { + results: data.items.map(p => ({"id": p.id, "text": p.name})) + }; + } + }, + placeholder: "{{ _('Enter list name') }}" +}); +{% endmacro %} \ No newline at end of file diff --git a/project/templates/event/actions.html b/project/templates/event/actions.html index edb559f..677730d 100644 --- a/project/templates/event/actions.html +++ b/project/templates/event/actions.html @@ -1,10 +1,23 @@ -{% extends "layout.html" %} +{% extends "layout_vue.html" %} {% from "_macros.html" import render_share_modal %} + {%- block title -%} {{ _('Actions for event') }} {%- endblock -%} -{% block content %} +{% block component_scripts %} + +{% endblock %} + +{% block component_definitions %} +Vue.component("EventListAddEvent", EventListAddEvent); +{% endblock %} + +{% block vue_container %} +{% endblock %} + +{% block content %} + {% set can_add_to_list = current_admin_unit and has_access(current_admin_unit, "admin_unit:update")%}
{{ _('Actions for event') }}
@@ -26,6 +39,12 @@ {{ _('Reference this event on your organization\'s calendar.') }} {% endif %} + {% if can_add_to_list %} + +

{{ _('Add to list') }}

+ {{ _('Add this event to a list of your organization.') }} +
+ {% endif %}
@@ -50,4 +69,13 @@
+
+ {% if can_add_to_list %} + + + + {% endif %} +
+ {{ super() }} + {% endblock %} \ No newline at end of file diff --git a/project/templates/event/create.html b/project/templates/event/create.html index b47c87a..52c8950 100644 --- a/project/templates/event/create.html +++ b/project/templates/event/create.html @@ -1,5 +1,5 @@ {% extends "layout.html" %} -{% from "_macros.html" import render_event_date_defintion_code, render_date_definition_container, render_manage_form_styles, render_manage_form_scripts, render_cropper_header, render_end_container_handling, render_jquery_steps_header, render_cropper_header, render_cropper_code, render_crop_image_form_section, render_radio_buttons, render_field_with_errors, render_field %} +{% from "_macros.html" import render_co_organizer_select2, render_event_date_defintion_code, render_date_definition_container, render_manage_form_styles, render_manage_form_scripts, render_cropper_header, render_end_container_handling, render_jquery_steps_header, render_cropper_header, render_cropper_code, render_crop_image_form_section, render_radio_buttons, render_field_with_errors, render_field %} {%- block title -%} {{ _('Create event') }} @@ -211,27 +211,7 @@ $( function() { update_organizer_container($('input[type=radio][name=organizer_choice]:checked').val()); - $('#co_organizer_ids').select2({ - width: '100%', - ajax: { - url: "{{ url_for('api_v1_organization_organizer_list', id=admin_unit.id) }}", - dataType: 'json', - delay: 250, - cache: true, - data: function (params) { - return { - name: params.term, - per_page: 5 - }; - }, - processResults: function (data) { - return { - results: data.items.map(p => ({"id": p.id, "text": p.name})) - }; - } - }, - placeholder: "{{ _('Enter organizer') }}" - }); + {{ render_co_organizer_select2(admin_unit.id) }} }); diff --git a/project/templates/event/update.html b/project/templates/event/update.html index eea6e78..43082ca 100644 --- a/project/templates/event/update.html +++ b/project/templates/event/update.html @@ -1,5 +1,5 @@ {% extends "layout.html" %} -{% from "_macros.html" import render_event_date_defintion_code, render_date_definition_container, render_manage_form_styles, render_manage_form_scripts, render_cropper_header, render_end_container_handling, render_jquery_steps_header, render_cropper_code, render_crop_image_form_section, render_radio_buttons, render_field_with_errors, render_field %} +{% from "_macros.html" import render_co_organizer_select2, render_event_date_defintion_code, render_date_definition_container, render_manage_form_styles, render_manage_form_scripts, render_cropper_header, render_end_container_handling, render_jquery_steps_header, render_cropper_code, render_crop_image_form_section, render_radio_buttons, render_field_with_errors, render_field %} {%- block title -%} {{ _('Update event') }} @@ -103,27 +103,8 @@ $(this).valid(); }); - $('#co_organizer_ids').select2({ - width: '100%', - ajax: { - url: "{{ url_for('api_v1_organization_organizer_list', id=event.admin_unit.id) }}", - dataType: 'json', - delay: 250, - cache: true, - data: function (params) { - return { - name: params.term, - per_page: 5 - }; - }, - processResults: function (data) { - return { - results: data.items.map(p => ({"id": p.id, "text": p.name})) - }; - } - }, - placeholder: "{{ _('Enter organizer') }}" - }); + {{ render_co_organizer_select2(event.admin_unit.id) }} + }); {% endblock %} diff --git a/project/templates/layout.html b/project/templates/layout.html index c06738c..46a35e1 100644 --- a/project/templates/layout.html +++ b/project/templates/layout.html @@ -224,6 +224,8 @@ @@ -53,4 +73,13 @@
{{ render_pagination(pagination) }}
+
+ {% if can_add_to_list %} + + + + {% endif %} +
+{{ super() }} + {% endblock %} \ No newline at end of file diff --git a/project/translations/de/LC_MESSAGES/messages.mo b/project/translations/de/LC_MESSAGES/messages.mo index c9d109b980c481de9523ac2e06886532dc7b859f..7415cdd34d465014459629c47ce9110491e0c4fc 100644 GIT binary patch delta 7956 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 U@rtNDS8=52|IzQxg-1O93vRBRhyVZp diff --git a/project/translations/de/LC_MESSAGES/messages.po b/project/translations/de/LC_MESSAGES/messages.po index 1bea18e..fb53f73 100644 --- a/project/translations/de/LC_MESSAGES/messages.po +++ b/project/translations/de/LC_MESSAGES/messages.po @@ -7,7 +7,7 @@ msgid "" msgstr "" "Project-Id-Version: PROJECT VERSION\n" "Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" -"POT-Creation-Date: 2021-11-17 01:07+0100\n" +"POT-Creation-Date: 2021-12-08 13:32+0100\n" "PO-Revision-Date: 2020-06-07 18:51+0200\n" "Last-Translator: FULL NAME \n" "Language: de\n" @@ -167,14 +167,18 @@ msgid "Scope_event:write" msgstr "Veranstaltungen anlegen, ändern und löschen" #: project/i10n.py:50 +msgid "Scope_eventlist:write" +msgstr "Veranstaltungslisten anlegen, ändern und löschen" + +#: project/i10n.py:51 msgid "Scope_organization:read" msgstr "Organisationen lesen" -#: project/i10n.py:51 +#: project/i10n.py:52 msgid "Scope_organization:write" msgstr "Organisationen anlegen, ändern und löschen" -#: project/i10n.py:52 +#: project/i10n.py:53 msgid "There must be no self-reference." msgstr "Es darf keine Selbstreferenz geben." @@ -190,29 +194,29 @@ msgstr "." msgid "message" msgstr "message" -#: project/api/organization/resources.py:355 +#: project/api/organization/resources.py:364 #: project/views/admin_unit_member_invitation.py:85 msgid "You have received an invitation" msgstr "Du hast eine Einladung erhalten" -#: project/forms/admin.py:10 project/templates/layout.html:309 +#: project/forms/admin.py:10 project/templates/layout.html:311 #: project/views/root.py:37 msgid "Terms of service" msgstr "Nutzungsbedingungen" -#: project/forms/admin.py:11 project/templates/layout.html:313 +#: project/forms/admin.py:11 project/templates/layout.html:315 #: project/views/root.py:45 msgid "Legal notice" msgstr "Impressum" #: project/forms/admin.py:12 project/templates/_macros.html:1392 -#: project/templates/layout.html:317 +#: project/templates/layout.html:319 #: project/templates/widget/event_suggestion/create.html:204 #: project/views/admin_unit.py:73 project/views/root.py:53 msgid "Contact" msgstr "Kontakt" -#: project/forms/admin.py:13 project/templates/layout.html:321 +#: project/forms/admin.py:13 project/templates/layout.html:323 #: project/views/root.py:61 msgid "Privacy" msgstr "Datenschutz" @@ -755,8 +759,8 @@ msgstr "Wähle Kategorien, die zur Veranstaltung passen." #: project/forms/event.py:255 project/forms/reference.py:14 #: project/forms/reference.py:27 project/forms/reference_request.py:76 -#: project/templates/event/create.html:379 -#: project/templates/event/update.html:235 +#: project/templates/event/create.html:359 +#: project/templates/event/update.html:216 msgid "Rating" msgstr "Bewertung" @@ -788,8 +792,8 @@ msgstr "Ungültiger Mitveranstalter." #: project/forms/event.py:286 project/forms/event.py:295 #: project/forms/event.py:368 project/forms/event_suggestion.py:50 #: project/templates/_macros.html:436 project/templates/_macros.html:596 -#: project/templates/event/create.html:304 -#: project/templates/event/update.html:185 +#: project/templates/event/create.html:284 +#: project/templates/event/update.html:166 #: project/templates/event_place/create.html:31 #: project/templates/event_place/delete.html:13 #: project/templates/event_place/update.html:31 @@ -807,8 +811,8 @@ msgstr "Neuen Ort eingeben" #: project/forms/event.py:302 project/forms/event.py:311 #: project/forms/event.py:376 project/forms/event.py:439 #: project/forms/event_suggestion.py:60 project/templates/_macros.html:473 -#: project/templates/_macros.html:633 project/templates/event/create.html:273 -#: project/templates/event/update.html:175 +#: project/templates/_macros.html:633 project/templates/event/create.html:253 +#: project/templates/event/update.html:156 #: project/templates/organizer/create.html:27 #: project/templates/organizer/delete.html:13 #: project/templates/organizer/update.html:27 @@ -855,7 +859,7 @@ msgstr "" "Wähle den Veranstalter. Du kannst Veranstalter unter Organisation > " "Veranstalter hinzufügen und ändern." -#: project/forms/event.py:385 project/templates/event/update.html:164 +#: project/forms/event.py:385 project/templates/event/update.html:145 #: project/templates/oauth2_token/list.html:21 msgid "Status" msgstr "Status" @@ -905,12 +909,12 @@ msgid "Select the public status of the event." msgstr "Wähle den öffentlichen Status der Veranstaltung." #: project/forms/event.py:407 project/templates/event/update.html:5 -#: project/templates/event/update.html:132 +#: project/templates/event/update.html:113 msgid "Update event" msgstr "Veranstaltung aktualisieren" #: project/forms/event.py:423 project/templates/_macros.html:1221 -#: project/templates/event/actions.html:47 +#: project/templates/event/actions.html:66 #: project/templates/event/delete.html:6 msgid "Delete event" msgstr "Veranstaltung löschen" @@ -953,8 +957,8 @@ msgstr "Standort" msgid "Distance" msgstr "Distanz" -#: project/forms/event_date.py:32 project/forms/planing.py:36 -#: project/templates/widget/event_date/list.html:61 +#: project/forms/event_date.py:33 project/forms/planing.py:36 +#: project/templates/widget/event_date/list.html:82 msgid "Find" msgstr "Finden" @@ -994,7 +998,7 @@ msgstr "Bitte gib deine Email-Adresse oder deine Telefonnummer für die Prüfung msgid "I would like to be notified by email after the review" msgstr "Ich möchte per Email benachrichtigt werden nach der Prüfung" -#: project/forms/event_suggestion.py:52 project/templates/event/create.html:309 +#: project/forms/event_suggestion.py:52 project/templates/event/create.html:289 msgid "" "Choose where the event takes place. If the venue is not yet in the list, " "just enter it." @@ -1002,7 +1006,7 @@ msgstr "" "Wähle aus, wo die Veranstaltung stattfindet. Ist der Veranstaltungsort " "noch nicht in der Liste, trage ihn einfach ein." -#: project/forms/event_suggestion.py:62 project/templates/event/create.html:277 +#: project/forms/event_suggestion.py:62 project/templates/event/create.html:257 msgid "" "Select the organizer. If the organizer is not yet on the list, just enter" " it." @@ -1089,7 +1093,7 @@ msgstr "Wochentage" #: project/templates/_macros.html:489 project/templates/_macros.html:649 #: project/templates/admin_unit/create.html:28 #: project/templates/admin_unit/update.html:29 -#: project/templates/layout.html:258 +#: project/templates/layout.html:260 msgid "Organization" msgstr "Organisation" @@ -1209,9 +1213,9 @@ msgid "Last updated at %(updated_at)s." msgstr "Zuletzt aktualisiert am %(updated_at)s." #: project/templates/_macros.html:385 project/templates/_macros.html:555 -#: project/templates/event/actions.html:12 -#: project/templates/event/create.html:250 -#: project/templates/event/update.html:141 +#: project/templates/event/actions.html:25 +#: project/templates/event/create.html:230 +#: project/templates/event/update.html:122 #: project/templates/widget/event_suggestion/create.html:229 msgid "Event" msgstr "Veranstaltung" @@ -1221,7 +1225,7 @@ msgid "Date" msgstr "Datum" #: project/templates/_macros.html:418 project/templates/_macros.html:577 -#: project/templates/_macros.html:1473 project/templates/event/actions.html:32 +#: project/templates/_macros.html:1473 project/templates/event/actions.html:51 msgid "Share" msgstr "Teilen" @@ -1320,11 +1324,11 @@ msgstr "OK" msgid "Choose image file" msgstr "Bild-Datei auswählen" -#: project/templates/_macros.html:1220 project/templates/event/actions.html:46 +#: project/templates/_macros.html:1220 project/templates/event/actions.html:65 msgid "Edit event" msgstr "Veranstaltung bearbeiten" -#: project/templates/_macros.html:1223 project/templates/manage/events.html:45 +#: project/templates/_macros.html:1223 project/templates/manage/events.html:65 msgid "More" msgstr "Mehr" @@ -1369,6 +1373,16 @@ msgstr "Anderer Kalender" msgid "Remove event date" msgstr "Termin entfernen" +#: project/templates/_macros.html:1738 project/templates/event/create.html:176 +#: project/templates/event/update.html:99 +#: project/templates/widget/event_suggestion/create.html:129 +msgid "Enter organizer" +msgstr "Veranstalter eingeben" + +#: project/templates/_macros.html:1762 +msgid "Enter list name" +msgstr "Listenname eingeben" + #: project/templates/home.html:28 msgid "Manage" msgstr "Verwaltung" @@ -1380,7 +1394,7 @@ msgstr "Kostenlos registrieren" #: project/templates/layout.html:175 project/templates/layout.html:219 #: project/templates/manage/events.html:6 -#: project/templates/manage/events.html:24 +#: project/templates/manage/events.html:41 msgid "Events" msgstr "Veranstaltungen" @@ -1398,7 +1412,7 @@ msgstr "Organisationen" msgid "Planing" msgstr "Planung" -#: project/templates/layout.html:187 project/templates/layout.html:271 +#: project/templates/layout.html:187 project/templates/layout.html:273 #: project/templates/oauth2_client/list.html:10 #: project/templates/oauth2_client/read.html:10 #: project/templates/oauth2_token/list.html:10 project/templates/profile.html:4 @@ -1421,64 +1435,69 @@ msgid "Show events" msgstr "Veranstaltungen anzeigen" #: project/templates/event/create.html:5 -#: project/templates/event/create.html:241 project/templates/layout.html:226 -#: project/templates/manage/events.html:27 +#: project/templates/event/create.html:221 project/templates/layout.html:226 +#: project/templates/manage/events.html:44 #: project/templates/manage/organizers.html:21 msgid "Create event" msgstr "Veranstaltung erstellen" -#: project/templates/layout.html:229 +#: project/templates/layout.html:228 +#: project/templates/manage/event_lists.html:4 +msgid "Event lists" +msgstr "Veranstaltungslisten" + +#: project/templates/layout.html:231 msgid "Review suggestions" msgstr "Vorschläge prüfen" -#: project/templates/layout.html:239 +#: project/templates/layout.html:241 #: project/templates/manage/references_incoming.html:5 #: project/templates/manage/references_outgoing.html:5 msgid "References" msgstr "Empfehlungen" -#: project/templates/layout.html:245 +#: project/templates/layout.html:247 #: project/templates/manage/references_incoming.html:9 msgid "Incoming references" msgstr "Eingehende Empfehlungen" -#: project/templates/layout.html:246 +#: project/templates/layout.html:248 #: project/templates/manage/references_outgoing.html:9 msgid "Outgoing references" msgstr "Ausgehende Empfehlungen" -#: project/templates/layout.html:248 +#: project/templates/layout.html:250 #: project/templates/manage/reference_requests_incoming.html:9 msgid "Incoming reference requests" msgstr "Eingehende Empfehlungsanfragen" -#: project/templates/layout.html:253 +#: project/templates/layout.html:255 #: project/templates/manage/reference_requests_outgoing.html:9 msgid "Outgoing reference requests" msgstr "Ausgehende Empfehlungsanfragen" -#: project/templates/layout.html:261 project/templates/manage/organizers.html:5 +#: project/templates/layout.html:263 project/templates/manage/organizers.html:5 #: project/templates/manage/organizers.html:9 msgid "Organizers" msgstr "Veranstalter" #: project/templates/event_place/list.html:3 -#: project/templates/event_place/list.html:7 project/templates/layout.html:262 +#: project/templates/event_place/list.html:7 project/templates/layout.html:264 #: project/templates/manage/places.html:5 #: project/templates/manage/places.html:9 msgid "Places" msgstr "Orte" -#: project/templates/layout.html:264 project/templates/manage/members.html:5 +#: project/templates/layout.html:266 project/templates/manage/members.html:5 #: project/templates/manage/members.html:28 msgid "Members" msgstr "Mitglieder" -#: project/templates/layout.html:265 project/templates/manage/relations.html:4 +#: project/templates/layout.html:267 project/templates/manage/relations.html:4 msgid "Relations" msgstr "Beziehungen" -#: project/templates/layout.html:267 +#: project/templates/layout.html:269 #: project/templates/manage/admin_units.html:17 #: project/templates/manage/organization_invitations.html:4 #: project/templates/user/organization_invitations.html:4 @@ -1490,22 +1509,22 @@ msgstr "Organisationseinladungen" #: project/templates/admin/settings.html:8 #: project/templates/admin_unit/update.html:6 #: project/templates/admin_unit/update.html:23 -#: project/templates/layout.html:269 project/templates/manage/widgets.html:12 -#: project/templates/profile.html:19 +#: project/templates/layout.html:271 project/templates/manage/widgets.html:11 +#: project/templates/manage/widgets.html:15 project/templates/profile.html:19 msgid "Settings" msgstr "Einstellungen" -#: project/templates/layout.html:270 project/templates/manage/reviews.html:10 +#: project/templates/layout.html:272 project/templates/manage/reviews.html:10 #: project/templates/manage/widgets.html:5 #: project/templates/manage/widgets.html:9 msgid "Widgets" msgstr "Widgets" -#: project/templates/layout.html:281 +#: project/templates/layout.html:283 msgid "Switch organization" msgstr "Organisation wechseln" -#: project/templates/developer/read.html:4 project/templates/layout.html:331 +#: project/templates/developer/read.html:4 project/templates/layout.html:333 #: project/templates/profile.html:29 msgid "Developer" msgstr "Entwickler" @@ -1528,7 +1547,7 @@ msgstr "Benutzer" #: project/templates/admin/admin_units.html:27 #: project/templates/admin/users.html:27 -#: project/templates/manage/events.html:42 +#: project/templates/manage/events.html:59 #: project/templates/manage/members.html:35 #: project/templates/manage/organizers.html:22 #: project/templates/manage/places.html:27 @@ -1540,8 +1559,8 @@ msgstr "Bearbeiten" #: project/templates/admin_unit/create.html:58 #: project/templates/admin_unit/update.html:59 -#: project/templates/event/create.html:367 -#: project/templates/event/update.html:223 +#: project/templates/event/create.html:347 +#: project/templates/event/update.html:204 #: project/templates/event_place/create.html:57 #: project/templates/event_place/update.html:57 #: project/templates/organizer/create.html:56 @@ -1645,85 +1664,91 @@ msgstr "Klicke hier, um den Vorschlag zu prüfen" msgid "The review status of your event has been updated." msgstr "Der Prüfungsstatus deiner Veranstaltung wurde aktualisiert" -#: project/templates/event/actions.html:4 -#: project/templates/event/actions.html:9 +#: project/templates/event/actions.html:5 +#: project/templates/event/actions.html:22 msgid "Actions for event" msgstr "Aktionen für die Veranstaltung" -#: project/templates/event/actions.html:19 +#: project/templates/event/actions.html:32 msgid "Request reference" msgstr "Empfehlung anfragen" -#: project/templates/event/actions.html:20 +#: project/templates/event/actions.html:33 #: project/templates/event/reference_request.html:10 msgid "Ask another organization to reference your event on their calendar." msgstr "" "Bitte eine andere Organisation, deine Veranstaltung in ihrem Kalender zu " "empfehlen." -#: project/templates/event/actions.html:25 +#: project/templates/event/actions.html:38 #: project/templates/manage/references_incoming.html:10 msgid "Reference event" msgstr "Veranstaltung empfehlen" -#: project/templates/event/actions.html:26 +#: project/templates/event/actions.html:39 #: project/templates/event/reference.html:9 msgid "Reference this event on your organization's calendar." msgstr "Empfehle diese Veranstaltung im Kalender deiner Organisation." -#: project/templates/event/actions.html:39 +#: project/templates/event/actions.html:44 +#: project/templates/manage/events.html:63 +msgid "Add to list" +msgstr "Zu Liste hinzufügen" + +#: project/templates/event/actions.html:45 +msgid "Add this event to a list of your organization." +msgstr "Füge diese Veranstaltung zu einer Liste deiner Organisation hinzu." + +#: project/templates/event/actions.html:58 msgid "Duplicate event" msgstr "Veranstaltung duplizieren" -#: project/templates/event/actions.html:42 +#: project/templates/event/actions.html:61 #, python-format msgid "Create another event for %(admin_unit_name)s" msgstr "Erstelle eine weitere Veranstaltung für %(admin_unit_name)s" -#: project/templates/event/actions.html:43 +#: project/templates/event/actions.html:62 #, python-format msgid "List all events of %(admin_unit_name)s" msgstr "Zeige alle Veranstaltungen von %(admin_unit_name)s" +#: project/templates/event/actions.html:74 +#: project/templates/manage/events.html:78 +msgid "Add event to list" +msgstr "Veranstaltung zu Liste hinzufügen" + #: project/templates/event/create.html:90 #: project/templates/event/update.html:76 #: project/templates/widget/event_suggestion/create.html:104 msgid "Enter place or address" msgstr "Orte oder Adresse eingeben" -#: project/templates/event/create.html:176 -#: project/templates/event/create.html:233 -#: project/templates/event/update.html:99 -#: project/templates/event/update.html:125 -#: project/templates/widget/event_suggestion/create.html:129 -msgid "Enter organizer" -msgstr "Veranstalter eingeben" - -#: project/templates/event/create.html:260 -#: project/templates/event/update.html:151 +#: project/templates/event/create.html:240 +#: project/templates/event/update.html:132 msgid "Event dates" msgstr "Termine" -#: project/templates/event/create.html:267 -#: project/templates/event/update.html:158 +#: project/templates/event/create.html:247 +#: project/templates/event/update.html:139 msgid "Add event date" msgstr "Termin hinzufügen" -#: project/templates/event/create.html:294 +#: project/templates/event/create.html:274 msgid "Switch to organizer search" msgstr "Zur Veranstaltersuche wechseln" -#: project/templates/event/create.html:329 +#: project/templates/event/create.html:309 msgid "Switch to place search" msgstr "Zur Ortssuche wechseln" -#: project/templates/event/create.html:340 -#: project/templates/event/update.html:196 +#: project/templates/event/create.html:320 +#: project/templates/event/update.html:177 msgid "Access" msgstr "Zugang" -#: project/templates/event/create.html:354 -#: project/templates/event/update.html:210 +#: project/templates/event/create.html:334 +#: project/templates/event/update.html:191 msgid "Target group" msgstr "Zielgruppe" @@ -1794,7 +1819,7 @@ msgstr "Organisation einladen" msgid "Member" msgstr "Mitglied" -#: project/templates/manage/events.html:20 +#: project/templates/manage/events.html:37 msgid "" "The organization is not verified. Events are therefore not publicly " "visible." @@ -1802,13 +1827,13 @@ msgstr "" "Die Organisation ist nicht verifiziert. Veranstaltungen sind daher nicht " "öffentlich sichtbar." -#: project/templates/manage/events.html:41 +#: project/templates/manage/events.html:58 #: project/templates/manage/references_incoming.html:19 #: project/templates/manage/references_outgoing.html:19 msgid "View" msgstr "Anzeigen" -#: project/templates/manage/events.html:43 +#: project/templates/manage/events.html:60 #: project/templates/manage/members.html:21 #: project/templates/manage/members.html:36 #: project/templates/manage/organizers.html:23 @@ -1818,7 +1843,7 @@ msgstr "Anzeigen" msgid "Delete" msgstr "Löschen" -#: project/templates/manage/events.html:44 +#: project/templates/manage/events.html:61 msgid "Duplicate" msgstr "Duplizieren" @@ -1849,11 +1874,7 @@ msgstr "Prüfungsstatus anzeigen" msgid "Reviews" msgstr "Prüfungen" -#: project/templates/manage/widgets.html:25 -msgid "Veranstaltungen als iFrame einbetten" -msgstr "Veranstaltungen als iFrame einbetten" - -#: project/templates/manage/widgets.html:33 +#: project/templates/manage/widgets.html:87 msgid "Link, um Veranstaltungen vorzuschlagen" msgstr "Link, um Veranstaltungen vorzuschlagen" @@ -1956,7 +1977,7 @@ msgstr "Du hast noch keinen Account? Kein Problem!" msgid "Widget" msgstr "Widget" -#: project/templates/widget/event_date/list.html:128 +#: project/templates/widget/event_date/list.html:149 msgid "Print" msgstr "Drucken" @@ -1976,7 +1997,7 @@ msgstr "Vorschau" msgid "Organization successfully updated" msgstr "Organisation erfolgreich aktualisiert" -#: project/views/admin.py:68 project/views/manage.py:318 +#: project/views/admin.py:68 project/views/manage.py:331 msgid "Settings successfully updated" msgstr "Einstellungen erfolgreich aktualisiert" @@ -2037,27 +2058,27 @@ msgstr "Die eingegebene Email passt nicht zur Email der Einladung" msgid "Invitation successfully deleted" msgstr "Einladung erfolgreich gelöscht" -#: project/views/event.py:178 +#: project/views/event.py:179 msgid "Event successfully published" msgstr "Veranstaltung erfolgreich veröffentlicht" -#: project/views/event.py:180 +#: project/views/event.py:181 msgid "Draft successfully saved" msgstr "Entwurf erfolgreich gespeichert" -#: project/views/event.py:223 +#: project/views/event.py:224 msgid "Event successfully updated" msgstr "Veranstaltung erfolgreich aktualisiert" -#: project/views/event.py:249 +#: project/views/event.py:250 msgid "Event successfully deleted" msgstr "Veranstaltung erfolgreich gelöscht" -#: project/views/event.py:408 +#: project/views/event.py:409 msgid "Referenced event changed" msgstr "Empfohlene Veranstaltung wurde geändert" -#: project/views/event.py:431 +#: project/views/event.py:432 msgid "New event report" msgstr "Neue Meldung zu einer Veranstaltung" @@ -2301,3 +2322,6 @@ msgstr "Neue Veranstaltung zu prüfen" #~ msgid "Entered name does not match event name" #~ msgstr "Der eingegebene Name entspricht nicht dem Namen der Veranstaltung" +#~ msgid "Veranstaltungen als iFrame einbetten" +#~ msgstr "Veranstaltungen als iFrame einbetten" + diff --git a/project/translations/en/LC_MESSAGES/messages.mo b/project/translations/en/LC_MESSAGES/messages.mo index 2f2f716a9c500e3bf3eddacd37dfcab52b508e5f..2f422ea8edcb3779c26aa32e67a159c290aafa1b 100644 GIT binary patch delta 1082 zcmYMyOK4M35XSMzqc&-7TTLysjp)@F3dZpiqQT5o|;u7RC5jRD&Qc zM2jLSw1W89D6Y)LwNNeU&W#IE5CugGU38&#G&<< zGr(^pzj##9|Ig2uStsEfX0S1C)`eNzh&ylt9>+~Mg#~<#Mf`=`m~Sv!kNe#VxXP?* zj~Qs=SzL=>u>%uJ{%sVx{n$=^7&Tw^_>#v{9zR1hI*Y6E3$l^L8_klK#U?CZ!2Z_9 zU@3_qY{p?s;aI)E?1ays_jnRpn4d<~KXIS?{7cmOckU-_CH{e`U-W!3L7x4soq-;h z!vK3x4;Vx(*n{eL1ef6v)J>d2)lZ^sWExjs&F9~u>OZ5Nvw&(O$~EY~X{@#}$TGMV zG24b|;u&^R!F%NP>^m}L^GNgd2l-l*E|qUaZP0=IdzM4?vb@`isvAJ9--W8*o8tbp zVAKl_yJggZ)2I_xJ-&*3?Y74cP%qC~cTgSQ z!w?@~4rj3b5~2R0MeN2F?r|M%#SlkOH!y*!yN;@Rh->f-+R&NOcxA9sDUFZSzQr!b r6D1|e$4=D_CDM_mVqtS%xTR-%Pq8+U%*JlM2)dH>iclqYgNDdo7hGca delta 1028 zcmYMyPe@cz6vy#1&Wx!u{+$NP(HhOLKmM5!Vj^@?F&h^VgdrBVhzO!6s6`OYu7%+) z7r7B#L_5I&k#MCu7rIb4I<^xva}fnrB>Mg30f#qy-nsXjch9}=?Ip$&xvw>$5#!gz zzn=fUfTELMA!v4%a03&#k98OenYCj(w&FwVz#(kIFW8CO*n)|$*%iFyKF4!rIs3?s z7M{T-T*X@ajasOx_}Hr$C(fegA9(!A;~|eHP>s&ud0asjvR_z=u~TMc*nm;iw@chq zF>npbF^dtr_dmgGz~`TNJct#{zenYdx}SZ18dX2sXbE#oq zj8&8{g`AenqmFC=Y1USd%L+ceg<9YT^0w>{d8UQwURI#$Yf<%$sQk;Q{7i)NS7J8< zGK(s>gW6%A$B&WAUU>W#^=-Vv(>RXW(KPaX+5+<3*&=Eq%SdmwhH7LJ)xcha^H;)8 zPxy<9!>q1`qNswn+kl$ChX?A diff --git a/project/translations/en/LC_MESSAGES/messages.po b/project/translations/en/LC_MESSAGES/messages.po index 55ab71c..13a4a4e 100644 --- a/project/translations/en/LC_MESSAGES/messages.po +++ b/project/translations/en/LC_MESSAGES/messages.po @@ -7,7 +7,7 @@ msgid "" msgstr "" "Project-Id-Version: PROJECT VERSION\n" "Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" -"POT-Creation-Date: 2021-11-17 01:07+0100\n" +"POT-Creation-Date: 2021-12-08 13:32+0100\n" "PO-Revision-Date: 2021-04-30 15:04+0200\n" "Last-Translator: FULL NAME \n" "Language: en\n" @@ -167,14 +167,18 @@ msgid "Scope_event:write" msgstr "Create, update and delete events" #: project/i10n.py:50 +msgid "Scope_eventlist:write" +msgstr "Create, update and delete event lists" + +#: project/i10n.py:51 msgid "Scope_organization:read" msgstr "Read organizations" -#: project/i10n.py:51 +#: project/i10n.py:52 msgid "Scope_organization:write" msgstr "Create, update and delete organizations" -#: project/i10n.py:52 +#: project/i10n.py:53 msgid "There must be no self-reference." msgstr "" @@ -190,29 +194,29 @@ msgstr "" msgid "message" msgstr "" -#: project/api/organization/resources.py:355 +#: project/api/organization/resources.py:364 #: project/views/admin_unit_member_invitation.py:85 msgid "You have received an invitation" msgstr "" -#: project/forms/admin.py:10 project/templates/layout.html:309 +#: project/forms/admin.py:10 project/templates/layout.html:311 #: project/views/root.py:37 msgid "Terms of service" msgstr "" -#: project/forms/admin.py:11 project/templates/layout.html:313 +#: project/forms/admin.py:11 project/templates/layout.html:315 #: project/views/root.py:45 msgid "Legal notice" msgstr "" #: project/forms/admin.py:12 project/templates/_macros.html:1392 -#: project/templates/layout.html:317 +#: project/templates/layout.html:319 #: project/templates/widget/event_suggestion/create.html:204 #: project/views/admin_unit.py:73 project/views/root.py:53 msgid "Contact" msgstr "" -#: project/forms/admin.py:13 project/templates/layout.html:321 +#: project/forms/admin.py:13 project/templates/layout.html:323 #: project/views/root.py:61 msgid "Privacy" msgstr "" @@ -724,8 +728,8 @@ msgstr "" #: project/forms/event.py:255 project/forms/reference.py:14 #: project/forms/reference.py:27 project/forms/reference_request.py:76 -#: project/templates/event/create.html:379 -#: project/templates/event/update.html:235 +#: project/templates/event/create.html:359 +#: project/templates/event/update.html:216 msgid "Rating" msgstr "" @@ -753,8 +757,8 @@ msgstr "" #: project/forms/event.py:286 project/forms/event.py:295 #: project/forms/event.py:368 project/forms/event_suggestion.py:50 #: project/templates/_macros.html:436 project/templates/_macros.html:596 -#: project/templates/event/create.html:304 -#: project/templates/event/update.html:185 +#: project/templates/event/create.html:284 +#: project/templates/event/update.html:166 #: project/templates/event_place/create.html:31 #: project/templates/event_place/delete.html:13 #: project/templates/event_place/update.html:31 @@ -772,8 +776,8 @@ msgstr "" #: project/forms/event.py:302 project/forms/event.py:311 #: project/forms/event.py:376 project/forms/event.py:439 #: project/forms/event_suggestion.py:60 project/templates/_macros.html:473 -#: project/templates/_macros.html:633 project/templates/event/create.html:273 -#: project/templates/event/update.html:175 +#: project/templates/_macros.html:633 project/templates/event/create.html:253 +#: project/templates/event/update.html:156 #: project/templates/organizer/create.html:27 #: project/templates/organizer/delete.html:13 #: project/templates/organizer/update.html:27 @@ -816,7 +820,7 @@ msgid "" " Organizers." msgstr "" -#: project/forms/event.py:385 project/templates/event/update.html:164 +#: project/forms/event.py:385 project/templates/event/update.html:145 #: project/templates/oauth2_token/list.html:21 msgid "Status" msgstr "" @@ -866,12 +870,12 @@ msgid "Select the public status of the event." msgstr "" #: project/forms/event.py:407 project/templates/event/update.html:5 -#: project/templates/event/update.html:132 +#: project/templates/event/update.html:113 msgid "Update event" msgstr "" #: project/forms/event.py:423 project/templates/_macros.html:1221 -#: project/templates/event/actions.html:47 +#: project/templates/event/actions.html:66 #: project/templates/event/delete.html:6 msgid "Delete event" msgstr "" @@ -914,8 +918,8 @@ msgstr "" msgid "Distance" msgstr "" -#: project/forms/event_date.py:32 project/forms/planing.py:36 -#: project/templates/widget/event_date/list.html:61 +#: project/forms/event_date.py:33 project/forms/planing.py:36 +#: project/templates/widget/event_date/list.html:82 msgid "Find" msgstr "" @@ -955,13 +959,13 @@ msgstr "" msgid "I would like to be notified by email after the review" msgstr "" -#: project/forms/event_suggestion.py:52 project/templates/event/create.html:309 +#: project/forms/event_suggestion.py:52 project/templates/event/create.html:289 msgid "" "Choose where the event takes place. If the venue is not yet in the list, " "just enter it." msgstr "" -#: project/forms/event_suggestion.py:62 project/templates/event/create.html:277 +#: project/forms/event_suggestion.py:62 project/templates/event/create.html:257 msgid "" "Select the organizer. If the organizer is not yet on the list, just enter" " it." @@ -1046,7 +1050,7 @@ msgstr "" #: project/templates/_macros.html:489 project/templates/_macros.html:649 #: project/templates/admin_unit/create.html:28 #: project/templates/admin_unit/update.html:29 -#: project/templates/layout.html:258 +#: project/templates/layout.html:260 msgid "Organization" msgstr "" @@ -1166,9 +1170,9 @@ msgid "Last updated at %(updated_at)s." msgstr "" #: project/templates/_macros.html:385 project/templates/_macros.html:555 -#: project/templates/event/actions.html:12 -#: project/templates/event/create.html:250 -#: project/templates/event/update.html:141 +#: project/templates/event/actions.html:25 +#: project/templates/event/create.html:230 +#: project/templates/event/update.html:122 #: project/templates/widget/event_suggestion/create.html:229 msgid "Event" msgstr "" @@ -1178,7 +1182,7 @@ msgid "Date" msgstr "" #: project/templates/_macros.html:418 project/templates/_macros.html:577 -#: project/templates/_macros.html:1473 project/templates/event/actions.html:32 +#: project/templates/_macros.html:1473 project/templates/event/actions.html:51 msgid "Share" msgstr "" @@ -1275,11 +1279,11 @@ msgstr "" msgid "Choose image file" msgstr "" -#: project/templates/_macros.html:1220 project/templates/event/actions.html:46 +#: project/templates/_macros.html:1220 project/templates/event/actions.html:65 msgid "Edit event" msgstr "" -#: project/templates/_macros.html:1223 project/templates/manage/events.html:45 +#: project/templates/_macros.html:1223 project/templates/manage/events.html:65 msgid "More" msgstr "" @@ -1324,6 +1328,16 @@ msgstr "" msgid "Remove event date" msgstr "" +#: project/templates/_macros.html:1738 project/templates/event/create.html:176 +#: project/templates/event/update.html:99 +#: project/templates/widget/event_suggestion/create.html:129 +msgid "Enter organizer" +msgstr "" + +#: project/templates/_macros.html:1762 +msgid "Enter list name" +msgstr "" + #: project/templates/home.html:28 msgid "Manage" msgstr "" @@ -1335,7 +1349,7 @@ msgstr "" #: project/templates/layout.html:175 project/templates/layout.html:219 #: project/templates/manage/events.html:6 -#: project/templates/manage/events.html:24 +#: project/templates/manage/events.html:41 msgid "Events" msgstr "" @@ -1353,7 +1367,7 @@ msgstr "" msgid "Planing" msgstr "" -#: project/templates/layout.html:187 project/templates/layout.html:271 +#: project/templates/layout.html:187 project/templates/layout.html:273 #: project/templates/oauth2_client/list.html:10 #: project/templates/oauth2_client/read.html:10 #: project/templates/oauth2_token/list.html:10 project/templates/profile.html:4 @@ -1376,64 +1390,69 @@ msgid "Show events" msgstr "" #: project/templates/event/create.html:5 -#: project/templates/event/create.html:241 project/templates/layout.html:226 -#: project/templates/manage/events.html:27 +#: project/templates/event/create.html:221 project/templates/layout.html:226 +#: project/templates/manage/events.html:44 #: project/templates/manage/organizers.html:21 msgid "Create event" msgstr "" -#: project/templates/layout.html:229 +#: project/templates/layout.html:228 +#: project/templates/manage/event_lists.html:4 +msgid "Event lists" +msgstr "" + +#: project/templates/layout.html:231 msgid "Review suggestions" msgstr "" -#: project/templates/layout.html:239 +#: project/templates/layout.html:241 #: project/templates/manage/references_incoming.html:5 #: project/templates/manage/references_outgoing.html:5 msgid "References" msgstr "" -#: project/templates/layout.html:245 +#: project/templates/layout.html:247 #: project/templates/manage/references_incoming.html:9 msgid "Incoming references" msgstr "" -#: project/templates/layout.html:246 +#: project/templates/layout.html:248 #: project/templates/manage/references_outgoing.html:9 msgid "Outgoing references" msgstr "" -#: project/templates/layout.html:248 +#: project/templates/layout.html:250 #: project/templates/manage/reference_requests_incoming.html:9 msgid "Incoming reference requests" msgstr "" -#: project/templates/layout.html:253 +#: project/templates/layout.html:255 #: project/templates/manage/reference_requests_outgoing.html:9 msgid "Outgoing reference requests" msgstr "" -#: project/templates/layout.html:261 project/templates/manage/organizers.html:5 +#: project/templates/layout.html:263 project/templates/manage/organizers.html:5 #: project/templates/manage/organizers.html:9 msgid "Organizers" msgstr "" #: project/templates/event_place/list.html:3 -#: project/templates/event_place/list.html:7 project/templates/layout.html:262 +#: project/templates/event_place/list.html:7 project/templates/layout.html:264 #: project/templates/manage/places.html:5 #: project/templates/manage/places.html:9 msgid "Places" msgstr "" -#: project/templates/layout.html:264 project/templates/manage/members.html:5 +#: project/templates/layout.html:266 project/templates/manage/members.html:5 #: project/templates/manage/members.html:28 msgid "Members" msgstr "" -#: project/templates/layout.html:265 project/templates/manage/relations.html:4 +#: project/templates/layout.html:267 project/templates/manage/relations.html:4 msgid "Relations" msgstr "" -#: project/templates/layout.html:267 +#: project/templates/layout.html:269 #: project/templates/manage/admin_units.html:17 #: project/templates/manage/organization_invitations.html:4 #: project/templates/user/organization_invitations.html:4 @@ -1445,22 +1464,22 @@ msgstr "" #: project/templates/admin/settings.html:8 #: project/templates/admin_unit/update.html:6 #: project/templates/admin_unit/update.html:23 -#: project/templates/layout.html:269 project/templates/manage/widgets.html:12 -#: project/templates/profile.html:19 +#: project/templates/layout.html:271 project/templates/manage/widgets.html:11 +#: project/templates/manage/widgets.html:15 project/templates/profile.html:19 msgid "Settings" msgstr "" -#: project/templates/layout.html:270 project/templates/manage/reviews.html:10 +#: project/templates/layout.html:272 project/templates/manage/reviews.html:10 #: project/templates/manage/widgets.html:5 #: project/templates/manage/widgets.html:9 msgid "Widgets" msgstr "" -#: project/templates/layout.html:281 +#: project/templates/layout.html:283 msgid "Switch organization" msgstr "" -#: project/templates/developer/read.html:4 project/templates/layout.html:331 +#: project/templates/developer/read.html:4 project/templates/layout.html:333 #: project/templates/profile.html:29 msgid "Developer" msgstr "" @@ -1483,7 +1502,7 @@ msgstr "" #: project/templates/admin/admin_units.html:27 #: project/templates/admin/users.html:27 -#: project/templates/manage/events.html:42 +#: project/templates/manage/events.html:59 #: project/templates/manage/members.html:35 #: project/templates/manage/organizers.html:22 #: project/templates/manage/places.html:27 @@ -1495,8 +1514,8 @@ msgstr "" #: project/templates/admin_unit/create.html:58 #: project/templates/admin_unit/update.html:59 -#: project/templates/event/create.html:367 -#: project/templates/event/update.html:223 +#: project/templates/event/create.html:347 +#: project/templates/event/update.html:204 #: project/templates/event_place/create.html:57 #: project/templates/event_place/update.html:57 #: project/templates/organizer/create.html:56 @@ -1598,83 +1617,89 @@ msgstr "" msgid "The review status of your event has been updated." msgstr "" -#: project/templates/event/actions.html:4 -#: project/templates/event/actions.html:9 +#: project/templates/event/actions.html:5 +#: project/templates/event/actions.html:22 msgid "Actions for event" msgstr "" -#: project/templates/event/actions.html:19 +#: project/templates/event/actions.html:32 msgid "Request reference" msgstr "" -#: project/templates/event/actions.html:20 +#: project/templates/event/actions.html:33 #: project/templates/event/reference_request.html:10 msgid "Ask another organization to reference your event on their calendar." msgstr "" -#: project/templates/event/actions.html:25 +#: project/templates/event/actions.html:38 #: project/templates/manage/references_incoming.html:10 msgid "Reference event" msgstr "" -#: project/templates/event/actions.html:26 +#: project/templates/event/actions.html:39 #: project/templates/event/reference.html:9 msgid "Reference this event on your organization's calendar." msgstr "" -#: project/templates/event/actions.html:39 +#: project/templates/event/actions.html:44 +#: project/templates/manage/events.html:63 +msgid "Add to list" +msgstr "" + +#: project/templates/event/actions.html:45 +msgid "Add this event to a list of your organization." +msgstr "" + +#: project/templates/event/actions.html:58 msgid "Duplicate event" msgstr "" -#: project/templates/event/actions.html:42 +#: project/templates/event/actions.html:61 #, python-format msgid "Create another event for %(admin_unit_name)s" msgstr "" -#: project/templates/event/actions.html:43 +#: project/templates/event/actions.html:62 #, python-format msgid "List all events of %(admin_unit_name)s" msgstr "" +#: project/templates/event/actions.html:74 +#: project/templates/manage/events.html:78 +msgid "Add event to list" +msgstr "" + #: project/templates/event/create.html:90 #: project/templates/event/update.html:76 #: project/templates/widget/event_suggestion/create.html:104 msgid "Enter place or address" msgstr "" -#: project/templates/event/create.html:176 -#: project/templates/event/create.html:233 -#: project/templates/event/update.html:99 -#: project/templates/event/update.html:125 -#: project/templates/widget/event_suggestion/create.html:129 -msgid "Enter organizer" -msgstr "" - -#: project/templates/event/create.html:260 -#: project/templates/event/update.html:151 +#: project/templates/event/create.html:240 +#: project/templates/event/update.html:132 msgid "Event dates" msgstr "" -#: project/templates/event/create.html:267 -#: project/templates/event/update.html:158 +#: project/templates/event/create.html:247 +#: project/templates/event/update.html:139 msgid "Add event date" msgstr "" -#: project/templates/event/create.html:294 +#: project/templates/event/create.html:274 msgid "Switch to organizer search" msgstr "" -#: project/templates/event/create.html:329 +#: project/templates/event/create.html:309 msgid "Switch to place search" msgstr "" -#: project/templates/event/create.html:340 -#: project/templates/event/update.html:196 +#: project/templates/event/create.html:320 +#: project/templates/event/update.html:177 msgid "Access" msgstr "" -#: project/templates/event/create.html:354 -#: project/templates/event/update.html:210 +#: project/templates/event/create.html:334 +#: project/templates/event/update.html:191 msgid "Target group" msgstr "" @@ -1745,19 +1770,19 @@ msgstr "" msgid "Member" msgstr "" -#: project/templates/manage/events.html:20 +#: project/templates/manage/events.html:37 msgid "" "The organization is not verified. Events are therefore not publicly " "visible." msgstr "" -#: project/templates/manage/events.html:41 +#: project/templates/manage/events.html:58 #: project/templates/manage/references_incoming.html:19 #: project/templates/manage/references_outgoing.html:19 msgid "View" msgstr "" -#: project/templates/manage/events.html:43 +#: project/templates/manage/events.html:60 #: project/templates/manage/members.html:21 #: project/templates/manage/members.html:36 #: project/templates/manage/organizers.html:23 @@ -1767,7 +1792,7 @@ msgstr "" msgid "Delete" msgstr "" -#: project/templates/manage/events.html:44 +#: project/templates/manage/events.html:61 msgid "Duplicate" msgstr "" @@ -1798,11 +1823,7 @@ msgstr "" msgid "Reviews" msgstr "" -#: project/templates/manage/widgets.html:25 -msgid "Veranstaltungen als iFrame einbetten" -msgstr "" - -#: project/templates/manage/widgets.html:33 +#: project/templates/manage/widgets.html:87 msgid "Link, um Veranstaltungen vorzuschlagen" msgstr "" @@ -1905,7 +1926,7 @@ msgstr "" msgid "Widget" msgstr "" -#: project/templates/widget/event_date/list.html:128 +#: project/templates/widget/event_date/list.html:149 msgid "Print" msgstr "" @@ -1925,7 +1946,7 @@ msgstr "" msgid "Organization successfully updated" msgstr "" -#: project/views/admin.py:68 project/views/manage.py:318 +#: project/views/admin.py:68 project/views/manage.py:331 msgid "Settings successfully updated" msgstr "" @@ -1983,27 +2004,27 @@ msgstr "" msgid "Invitation successfully deleted" msgstr "" -#: project/views/event.py:178 +#: project/views/event.py:179 msgid "Event successfully published" msgstr "" -#: project/views/event.py:180 +#: project/views/event.py:181 msgid "Draft successfully saved" msgstr "" -#: project/views/event.py:223 +#: project/views/event.py:224 msgid "Event successfully updated" msgstr "" -#: project/views/event.py:249 +#: project/views/event.py:250 msgid "Event successfully deleted" msgstr "" -#: project/views/event.py:408 +#: project/views/event.py:409 msgid "Referenced event changed" msgstr "" -#: project/views/event.py:431 +#: project/views/event.py:432 msgid "New event report" msgstr "" @@ -2232,3 +2253,6 @@ msgstr "" #~ msgid "Add event date with a different time" #~ msgstr "" +#~ msgid "Veranstaltungen als iFrame einbetten" +#~ msgstr "" + diff --git a/project/views/manage.py b/project/views/manage.py index 6fa9376..de71e45 100644 --- a/project/views/manage.py +++ b/project/views/manage.py @@ -278,6 +278,19 @@ def manage_admin_unit_organization_invitations(id, path=None): ) +@app.route("/manage/admin_unit//event-lists") +@app.route("/manage/admin_unit//event-lists/") +@auth_required() +def manage_admin_unit_event_lists(id, path=None): + admin_unit = get_admin_unit_for_manage_or_404(id) + set_current_admin_unit(admin_unit) + + return render_template( + "manage/event_lists.html", + admin_unit=admin_unit, + ) + + @app.route("/manage/admin_unit//widgets", methods=("GET", "POST")) @auth_required() def manage_admin_unit_widgets(id): diff --git a/tests/api/test_event_date.py b/tests/api/test_event_date.py index 0920000..993b958 100644 --- a/tests/api/test_event_date.py +++ b/tests/api/test_event_date.py @@ -81,6 +81,13 @@ def test_search(client, seeder, utils): url = utils.get_url("api_v1_event_date_search", organizer_id=organizer_id) response = utils.get_ok(url) + listed_event_id = seeder.create_event(admin_unit_id) + event_list_id = seeder.create_event_list(admin_unit_id, listed_event_id) + url = utils.get_url("api_v1_event_date_search", event_list_id=event_list_id) + response = utils.get_ok(url) + assert len(response.json["items"]) == 1 + assert response.json["items"][0]["event"]["id"] == listed_event_id + def test_search_oneDay(client, seeder, utils): from project.dateutils import create_berlin_date diff --git a/tests/api/test_event_list.py b/tests/api/test_event_list.py new file mode 100644 index 0000000..2662ff1 --- /dev/null +++ b/tests/api/test_event_list.py @@ -0,0 +1,103 @@ +def test_read(client, app, db, seeder, utils): + _, admin_unit_id = seeder.setup_base() + event_list_id = seeder.create_event_list(admin_unit_id) + + url = utils.get_url("api_v1_event_list_model", id=event_list_id) + response = utils.get_ok(url) + assert response.json["id"] == event_list_id + + +def test_put(client, seeder, utils, app): + _, admin_unit_id = seeder.setup_api_access() + event_list_id = seeder.create_event_list(admin_unit_id) + + url = utils.get_url("api_v1_event_list_model", id=event_list_id) + response = utils.put_json(url, {"name": "Neuer Name"}) + utils.assert_response_no_content(response) + + with app.app_context(): + from project.models import EventList + + event_list = EventList.query.get(event_list_id) + assert event_list.name == "Neuer Name" + + +def test_patch(client, seeder, utils, app): + _, admin_unit_id = seeder.setup_api_access() + event_list_id = seeder.create_event_list(admin_unit_id) + + url = utils.get_url("api_v1_event_list_model", id=event_list_id) + response = utils.patch_json(url, {"name": "Neuer Name"}) + utils.assert_response_no_content(response) + + with app.app_context(): + from project.models import EventList + + event_list = EventList.query.get(event_list_id) + assert event_list.name == "Neuer Name" + + +def test_delete(client, seeder, utils, app): + _, admin_unit_id = seeder.setup_api_access() + event_list_id = seeder.create_event_list(admin_unit_id) + + url = utils.get_url("api_v1_event_list_model", id=event_list_id) + response = utils.delete(url) + utils.assert_response_no_content(response) + + with app.app_context(): + from project.models import EventList + + event_list = EventList.query.get(event_list_id) + assert event_list is None + + +def test_events(client, seeder, utils): + _, admin_unit_id = seeder.setup_base() + event_id = seeder.create_event(admin_unit_id) + event_list_id = seeder.create_event_list(admin_unit_id, event_id) + + url = utils.get_url("api_v1_event_list_event_list", id=event_list_id) + response = utils.get_ok(url) + assert len(response.json["items"]) == 1 + assert response.json["items"][0]["id"] == event_id + + +def test_events_put(client, seeder, utils, app): + _, admin_unit_id = seeder.setup_api_access() + event_id = seeder.create_event(admin_unit_id) + event_list_id = seeder.create_event_list(admin_unit_id) + + url = utils.get_url( + "api_v1_event_list_event_list_write", id=event_list_id, event_id=event_id + ) + response = utils.put_json(url) + utils.assert_response_no_content(response) + + with app.app_context(): + from project.models import EventList + + event_list = EventList.query.get(event_list_id) + assert len(event_list.events) == 1 + assert event_list.events[0].id == event_id + + +def test_events_delete(client, seeder, utils, app): + _, admin_unit_id = seeder.setup_api_access() + event_id = seeder.create_event(admin_unit_id) + event_list_id = seeder.create_event_list(admin_unit_id, event_id) + + url = utils.get_url( + "api_v1_event_list_event_list_write", id=event_list_id, event_id=event_id + ) + response = utils.delete(url) + utils.assert_response_no_content(response) + + with app.app_context(): + from project.models import Event, EventList + + event_list = EventList.query.get(event_list_id) + assert len(event_list.events) == 0 + + event = Event.query.get(event_id) + assert event is not None diff --git a/tests/api/test_organization.py b/tests/api/test_organization.py index 71260b3..31110e1 100644 --- a/tests/api/test_organization.py +++ b/tests/api/test_organization.py @@ -278,7 +278,7 @@ def test_places(client, seeder, utils): def test_places_post(client, seeder, utils, app): user_id, admin_unit_id = seeder.setup_api_access() - url = utils.get_url("api_v1_organization_place_list", id=admin_unit_id, name="crew") + url = utils.get_url("api_v1_organization_place_list", id=admin_unit_id) response = utils.post_json( url, { @@ -304,6 +304,62 @@ def test_places_post(client, seeder, utils, app): assert place.location.city == "Goslar" +def test_event_lists(client, seeder, utils): + _, admin_unit_id = seeder.setup_base() + event_list_id = seeder.create_event_list(admin_unit_id, name="Meine Liste") + + url = utils.get_url( + "api_v1_organization_event_list_list", id=admin_unit_id, name="meine" + ) + response = utils.get_ok(url) + assert len(response.json["items"]) == 1 + assert response.json["items"][0]["id"] == event_list_id + + +def test_event_lists_post(client, seeder, utils, app): + _, admin_unit_id = seeder.setup_api_access() + + url = utils.get_url("api_v1_organization_event_list_list", id=admin_unit_id) + response = utils.post_json( + url, + { + "name": "Neue Liste", + }, + ) + utils.assert_response_created(response) + assert "id" in response.json + + with app.app_context(): + from project.models import EventList + + event_list = ( + EventList.query.filter(EventList.admin_unit_id == admin_unit_id) + .filter(EventList.name == "Neue Liste") + .first() + ) + assert event_list is not None + assert event_list.name == "Neue Liste" + + +def test_event_lists_status(client, seeder, utils): + _, admin_unit_id = seeder.setup_base() + event_id = seeder.create_event(admin_unit_id) + event_list_id = seeder.create_event_list( + admin_unit_id, event_id, name="Meine Liste" + ) + + url = utils.get_url( + "api_v1_organization_event_list_status_list", + id=admin_unit_id, + event_id=event_id, + name="meine", + ) + response = utils.get_ok(url) + assert len(response.json["items"]) == 1 + assert response.json["items"][0]["event_list"]["id"] == event_list_id + assert response.json["items"][0]["contains_event"] + + def test_references_incoming(client, seeder, utils): user_id, admin_unit_id = seeder.setup_base() ( diff --git a/tests/seeder.py b/tests/seeder.py index a4a726d..2cde408 100644 --- a/tests/seeder.py +++ b/tests/seeder.py @@ -473,6 +473,34 @@ class Seeder(object): suggestion_id = suggestion.id return suggestion_id + def add_event_to_list(self, event_list_id, event_id): + from project.models import Event, EventList + + with self._app.app_context(): + event = Event.query.get(event_id) + event_list = EventList.query.get(event_list_id) + event_list.events.append(event) + self._db.session.commit() + + def create_event_list(self, admin_unit_id, event_ids=list(), name="My list"): + from project.models import EventList + + with self._app.app_context(): + event_list = EventList() + event_list.name = name + event_list.admin_unit_id = admin_unit_id + self._db.session.add(event_list) + self._db.session.commit() + event_list_id = event_list.id + + if type(event_ids) is not list: + event_ids = [event_ids] + + for event_id in event_ids: + self.add_event_to_list(event_list_id, event_id) + + return event_list_id + def create_reference(self, event_id, admin_unit_id): from project.models import EventReference diff --git a/tests/test_models.py b/tests/test_models.py index 8b2636f..9edf5dd 100644 --- a/tests/test_models.py +++ b/tests/test_models.py @@ -169,6 +169,7 @@ def test_admin_unit_deletion(client, app, db, seeder): event_place_id = seeder.upsert_default_event_place(admin_unit_id) organizer_id = seeder.upsert_default_event_organizer(admin_unit_id) invitation_id = seeder.create_invitation(admin_unit_id, "newbie@domain.com") + event_list_id = seeder.create_event_list(admin_unit_id, my_event_id) other_user_id = seeder.create_user("other@test.de") other_admin_unit_id = seeder.create_admin_unit(other_user_id, "Other Crew") @@ -196,6 +197,7 @@ def test_admin_unit_deletion(client, app, db, seeder): Event, EventDate, EventDateDefinition, + EventList, EventOrganizer, EventPlace, EventReference, @@ -227,6 +229,7 @@ def test_admin_unit_deletion(client, app, db, seeder): assert EventPlace.query.get(event_place_id) is None assert EventOrganizer.query.get(organizer_id) is None assert AdminUnitMemberInvitation.query.get(invitation_id) is None + assert EventList.query.get(event_list_id) is None assert AdminUnit.query.get(other_admin_unit_id) is not None assert Event.query.get(other_event_id) is not None @@ -315,3 +318,42 @@ def test_admin_unit_invitations(client, app, db, seeder): assert len(admin_unit.admin_unit_invitations) == 0 invitation = AdminUnitInvitation.query.get(invitation_id) assert invitation is None + + +def test_event_list_deletion(client, app, db, seeder): + _, admin_unit_id = seeder.setup_base(log_in=False) + event_id = seeder.create_event(admin_unit_id) + event_list_a_id = seeder.create_event_list(admin_unit_id, event_id, "List A") + event_list_b_id = seeder.create_event_list(admin_unit_id, event_id, "List B") + + with app.app_context(): + from project.models import Event, EventList + + event_list_a = EventList.query.get(event_list_a_id) + assert len(event_list_a.events) == 1 + assert event_list_a.events[0].id == event_id + + event_list_b = EventList.query.get(event_list_b_id) + assert len(event_list_b.events) == 1 + assert event_list_b.events[0].id == event_id + + event = Event.query.get(event_id) + assert len(event.event_lists) == 2 + assert event.event_lists[0].id == event_list_a_id + assert event.event_lists[1].id == event_list_b_id + + event_list_a = EventList.query.get(event_list_a_id) + db.session.delete(event_list_a) + db.session.commit() + assert len(event.event_lists) == 1 + assert event.event_lists[0].id == event_list_b_id + + event_list_b = EventList.query.get(event_list_b_id) + assert len(event_list_b.events) == 1 + assert event_list_b.events[0].id == event_id + + db.session.delete(event) + db.session.commit() + + event_list_b = EventList.query.get(event_list_b_id) + assert len(event_list_b.events) == 0 diff --git a/tests/utils.py b/tests/utils.py index 75e8d38..596888f 100644 --- a/tests/utils.py +++ b/tests/utils.py @@ -142,9 +142,11 @@ class UtilActions(object): def log_request(self, url): print(url) - def log_json_request(self, url, data: dict): + def log_json_request(self, url, data: dict = None): self.log_request(url) - print(data) + + if data: + print(data) def log_response(self, response): print(response.status_code) @@ -163,7 +165,7 @@ class UtilActions(object): self.log_response(response) return response - def put_json(self, url, data: dict): + def put_json(self, url, data: dict = None): self.log_json_request(url, data) response = self._client.put(url, json=data, headers=self.get_headers()) self.log_response(response) diff --git a/tests/views/test_manage.py b/tests/views/test_manage.py index 5221143..c3e1b07 100644 --- a/tests/views/test_manage.py +++ b/tests/views/test_manage.py @@ -195,3 +195,10 @@ def test_admin_unit_organization_invitations(client, seeder, utils): url = utils.get_url("manage_admin_unit_organization_invitations", id=admin_unit_id) utils.get_ok(url) + + +def test_admin_unit_event_lists(client, seeder, utils): + user_id, admin_unit_id = seeder.setup_base() + + url = utils.get_url("manage_admin_unit_event_lists", id=admin_unit_id) + utils.get_ok(url)