From 86f26d1a98c5d451ed5f051efea3a6d72a8c6609 Mon Sep 17 00:00:00 2001 From: Vidya Ramakrishnan Date: Mon, 9 Oct 2023 10:50:23 +0530 Subject: [PATCH 1/5] schema for adding to google calendar --- funnel/transports/email/send.py | 41 +++++++++++++++++++ .../views/notifications/rsvp_notification.py | 7 +++- 2 files changed, 46 insertions(+), 2 deletions(-) diff --git a/funnel/transports/email/send.py b/funnel/transports/email/send.py index 8ed054bb3..0ce3c7187 100644 --- a/funnel/transports/email/send.py +++ b/funnel/transports/email/send.py @@ -23,6 +23,7 @@ __all__ = [ 'EmailAttachment', 'jsonld_confirm_action', + 'jsonld_register_action', 'jsonld_view_action', 'process_recipient', 'send_email', @@ -55,6 +56,46 @@ def jsonld_view_action(description: str, url: str, title: str) -> dict[str, obje } +def jsonld_register_action(description: str, url: str, title: str, start_date: str, venue: dict, fullname:str) -> dict[str, object]: + location = { + "@type": "Place", + "name": "Undecided" + } + if venue: + location["name"] = venue.title + if venue.address1: + postal_address = { + "@type": "PostalAddress", + "streetAddress": venue.address1, + "addressLocality": venue.city, + "addressRegion": venue.state, + "postalCode": venue.postcode, + "addressCountry": venue.country, + } + location["address"] = postal_address + return { + "@context": "http://schema.org", + "@type": "EventReservation", + "reservationStatus": "http://schema.org/Confirmed", + "underName": { + "@type": "Person", + "name": fullname, + }, + "reservationFor": { + "@type": "Event", + "name": title, + "url": url, + "performer": { + "@type": "Organization", + "name": description, + }, + "startDate": start_date, + "location": location, + }, + "numSeats": "1", + } + + def jsonld_confirm_action(description: str, url: str, title: str) -> dict[str, object]: return { "@context": "http://schema.org", diff --git a/funnel/views/notifications/rsvp_notification.py b/funnel/views/notifications/rsvp_notification.py index 81fface2b..d6be6f79f 100644 --- a/funnel/views/notifications/rsvp_notification.py +++ b/funnel/views/notifications/rsvp_notification.py @@ -120,10 +120,13 @@ def email_content(self): return render_template( 'notifications/rsvp_yes_email.html.jinja2', view=self, - jsonld=email.jsonld_view_action( + jsonld=email.jsonld_register_action( self.rsvp.project.joined_title, self.rsvp.project.url_for(_external=True), - _("View project"), + self.rsvp.project.title, + self.rsvp.project.start_at, + self.rsvp.project.primary_venue, + self.rsvp.participant.fullname ), ) From d076f4b189c8a43eb2d0f5d9f9c361c33f5c3190 Mon Sep 17 00:00:00 2001 From: Vidya Ramakrishnan Date: Mon, 9 Oct 2023 12:07:07 +0530 Subject: [PATCH 2/5] Use project.location --- funnel/transports/email/send.py | 4 ++-- funnel/views/notifications/rsvp_notification.py | 1 + 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/funnel/transports/email/send.py b/funnel/transports/email/send.py index 0ce3c7187..e58f63c2b 100644 --- a/funnel/transports/email/send.py +++ b/funnel/transports/email/send.py @@ -56,10 +56,10 @@ def jsonld_view_action(description: str, url: str, title: str) -> dict[str, obje } -def jsonld_register_action(description: str, url: str, title: str, start_date: str, venue: dict, fullname:str) -> dict[str, object]: +def jsonld_register_action(description: str, url: str, title: str, start_date: str, location: str, venue: dict, fullname:str) -> dict[str, object]: location = { "@type": "Place", - "name": "Undecided" + "name": location, } if venue: location["name"] = venue.title diff --git a/funnel/views/notifications/rsvp_notification.py b/funnel/views/notifications/rsvp_notification.py index d6be6f79f..44242f7e8 100644 --- a/funnel/views/notifications/rsvp_notification.py +++ b/funnel/views/notifications/rsvp_notification.py @@ -125,6 +125,7 @@ def email_content(self): self.rsvp.project.url_for(_external=True), self.rsvp.project.title, self.rsvp.project.start_at, + self.rsvp.project.location, self.rsvp.project.primary_venue, self.rsvp.participant.fullname ), From 81f07bd4ea043f61325ad79fc4355c106865d509 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 9 Oct 2023 06:48:33 +0000 Subject: [PATCH 3/5] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- funnel/transports/email/send.py | 10 +++++++++- funnel/views/notifications/rsvp_notification.py | 2 +- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/funnel/transports/email/send.py b/funnel/transports/email/send.py index e58f63c2b..8530afae4 100644 --- a/funnel/transports/email/send.py +++ b/funnel/transports/email/send.py @@ -56,7 +56,15 @@ def jsonld_view_action(description: str, url: str, title: str) -> dict[str, obje } -def jsonld_register_action(description: str, url: str, title: str, start_date: str, location: str, venue: dict, fullname:str) -> dict[str, object]: +def jsonld_register_action( + description: str, + url: str, + title: str, + start_date: str, + location: str, + venue: dict, + fullname: str, +) -> dict[str, object]: location = { "@type": "Place", "name": location, diff --git a/funnel/views/notifications/rsvp_notification.py b/funnel/views/notifications/rsvp_notification.py index 44242f7e8..07a9d03c5 100644 --- a/funnel/views/notifications/rsvp_notification.py +++ b/funnel/views/notifications/rsvp_notification.py @@ -127,7 +127,7 @@ def email_content(self): self.rsvp.project.start_at, self.rsvp.project.location, self.rsvp.project.primary_venue, - self.rsvp.participant.fullname + self.rsvp.participant.fullname, ), ) From 1781df3c613f04ed569b67026bd7789b2ec88d32 Mon Sep 17 00:00:00 2001 From: Kiran Jonnalagadda Date: Tue, 10 Oct 2023 16:54:53 +0530 Subject: [PATCH 4/5] Use single quotes, fix venue type --- funnel/transports/email/send.py | 94 +++++++++---------- .../views/notifications/rsvp_notification.py | 2 +- 2 files changed, 48 insertions(+), 48 deletions(-) diff --git a/funnel/transports/email/send.py b/funnel/transports/email/send.py index 8530afae4..619b07ed9 100644 --- a/funnel/transports/email/send.py +++ b/funnel/transports/email/send.py @@ -17,13 +17,13 @@ from baseframe import _, statsd from ... import app -from ...models import Account, EmailAddress, EmailAddressBlockedError +from ...models import Account, EmailAddress, EmailAddressBlockedError, Venue from ..exc import TransportRecipientError __all__ = [ 'EmailAttachment', 'jsonld_confirm_action', - 'jsonld_register_action', + 'jsonld_event_reservation', 'jsonld_view_action', 'process_recipient', 'send_email', @@ -44,75 +44,75 @@ class EmailAttachment: def jsonld_view_action(description: str, url: str, title: str) -> dict[str, object]: return { - "@context": "http://schema.org", - "@type": "EmailMessage", - "description": description, - "potentialAction": {"@type": "ViewAction", "name": title, "url": url}, - "publisher": { - "@type": "Organization", - "name": current_app.config['SITE_TITLE'], - "url": 'https://' + current_app.config['DEFAULT_DOMAIN'] + '/', + '@context': 'http://schema.org', + '@type': 'EmailMessage', + 'description': description, + 'potentialAction': {'@type': 'ViewAction', 'name': title, 'url': url}, + 'publisher': { + '@type': 'Organization', + 'name': current_app.config['SITE_TITLE'], + 'url': 'https://' + current_app.config['DEFAULT_DOMAIN'] + '/', }, } -def jsonld_register_action( +def jsonld_event_reservation( description: str, url: str, title: str, start_date: str, location: str, - venue: dict, + venue: Venue | None, fullname: str, ) -> dict[str, object]: - location = { - "@type": "Place", - "name": location, + location_schema: dict[str, object] = { + '@type': 'Place', + 'name': location, } - if venue: - location["name"] = venue.title + if venue is not None: + location_schema['name'] = venue.title if venue.address1: postal_address = { - "@type": "PostalAddress", - "streetAddress": venue.address1, - "addressLocality": venue.city, - "addressRegion": venue.state, - "postalCode": venue.postcode, - "addressCountry": venue.country, + '@type': 'PostalAddress', + 'streetAddress': venue.address1, + 'addressLocality': venue.city, + 'addressRegion': venue.state, + 'postalCode': venue.postcode, + 'addressCountry': venue.country, } - location["address"] = postal_address + location_schema['address'] = postal_address return { - "@context": "http://schema.org", - "@type": "EventReservation", - "reservationStatus": "http://schema.org/Confirmed", - "underName": { - "@type": "Person", - "name": fullname, + '@context': 'http://schema.org', + '@type': 'EventReservation', + 'reservationStatus': 'http://schema.org/Confirmed', + 'underName': { + '@type': 'Person', + 'name': fullname, }, - "reservationFor": { - "@type": "Event", - "name": title, - "url": url, - "performer": { - "@type": "Organization", - "name": description, + 'reservationFor': { + '@type': 'Event', + 'name': title, + 'url': url, + 'performer': { + '@type': 'Organization', + 'name': description, }, - "startDate": start_date, - "location": location, + 'startDate': start_date, + 'location': location_schema, }, - "numSeats": "1", + 'numSeats': '1', } def jsonld_confirm_action(description: str, url: str, title: str) -> dict[str, object]: return { - "@context": "http://schema.org", - "@type": "EmailMessage", - "description": description, - "potentialAction": { - "@type": "ConfirmAction", - "name": title, - "handler": {"@type": "HttpActionHandler", "url": url}, + '@context': 'http://schema.org', + '@type': 'EmailMessage', + 'description': description, + 'potentialAction': { + '@type': 'ConfirmAction', + 'name': title, + 'handler': {'@type': 'HttpActionHandler', 'url': url}, }, } diff --git a/funnel/views/notifications/rsvp_notification.py b/funnel/views/notifications/rsvp_notification.py index 07a9d03c5..ebc0a2d85 100644 --- a/funnel/views/notifications/rsvp_notification.py +++ b/funnel/views/notifications/rsvp_notification.py @@ -120,7 +120,7 @@ def email_content(self): return render_template( 'notifications/rsvp_yes_email.html.jinja2', view=self, - jsonld=email.jsonld_register_action( + jsonld=email.jsonld_event_reservation( self.rsvp.project.joined_title, self.rsvp.project.url_for(_external=True), self.rsvp.project.title, From 672b5a86b6b18fb83dd69646964e93ce8eb2d239 Mon Sep 17 00:00:00 2001 From: Kiran Jonnalagadda Date: Tue, 10 Oct 2023 17:22:28 +0530 Subject: [PATCH 5/5] Use Rsvp for EventRegistration schema --- funnel/models/rsvp.py | 4 +- funnel/transports/email/send.py | 79 ++++++++++--------- .../views/notifications/rsvp_notification.py | 10 +-- 3 files changed, 45 insertions(+), 48 deletions(-) diff --git a/funnel/models/rsvp.py b/funnel/models/rsvp.py index a5bd82361..ac6390686 100644 --- a/funnel/models/rsvp.py +++ b/funnel/models/rsvp.py @@ -45,7 +45,7 @@ class Rsvp(UuidMixin, NoIdMixin, Model): project_id = sa.orm.mapped_column( sa.Integer, sa.ForeignKey('project.id'), nullable=False, primary_key=True ) - project = with_roles( + project: Mapped[Project] = with_roles( relationship(Project, backref=backref('rsvps', cascade='all', lazy='dynamic')), read={'owner', 'project_promoter'}, grants_via={None: project_child_role_map}, @@ -54,7 +54,7 @@ class Rsvp(UuidMixin, NoIdMixin, Model): participant_id: Mapped[int] = sa.orm.mapped_column( sa.ForeignKey('account.id'), nullable=False, primary_key=True ) - participant = with_roles( + participant: Mapped[Account] = with_roles( relationship(Account, backref=backref('rsvps', cascade='all', lazy='dynamic')), read={'owner', 'project_promoter'}, grants={'owner'}, diff --git a/funnel/transports/email/send.py b/funnel/transports/email/send.py index 619b07ed9..f31343c0c 100644 --- a/funnel/transports/email/send.py +++ b/funnel/transports/email/send.py @@ -17,7 +17,7 @@ from baseframe import _, statsd from ... import app -from ...models import Account, EmailAddress, EmailAddressBlockedError, Venue +from ...models import Account, EmailAddress, EmailAddressBlockedError, Rsvp from ..exc import TransportRecipientError __all__ = [ @@ -43,8 +43,9 @@ class EmailAttachment: def jsonld_view_action(description: str, url: str, title: str) -> dict[str, object]: + """Schema.org JSON-LD markup for an email view action.""" return { - '@context': 'http://schema.org', + '@context': 'https://schema.org', '@type': 'EmailMessage', 'description': description, 'potentialAction': {'@type': 'ViewAction', 'name': title, 'url': url}, @@ -56,21 +57,29 @@ def jsonld_view_action(description: str, url: str, title: str) -> dict[str, obje } -def jsonld_event_reservation( - description: str, - url: str, - title: str, - start_date: str, - location: str, - venue: Venue | None, - fullname: str, -) -> dict[str, object]: - location_schema: dict[str, object] = { - '@type': 'Place', - 'name': location, +def jsonld_confirm_action(description: str, url: str, title: str) -> dict[str, object]: + """Schema.org JSON-LD markup for an email confirmation action.""" + return { + '@context': 'https://schema.org', + '@type': 'EmailMessage', + 'description': description, + 'potentialAction': { + '@type': 'ConfirmAction', + 'name': title, + 'handler': {'@type': 'HttpActionHandler', 'url': url}, + }, } + + +def jsonld_event_reservation(rsvp: Rsvp) -> dict[str, object]: + """Schema.org JSON-LD markup for an event reservation.""" + location: str | dict[str, object] + venue = rsvp.project.primary_venue if venue is not None: - location_schema['name'] = venue.title + location = { + '@type': 'Place', + 'name': venue.title, + } if venue.address1: postal_address = { '@type': 'PostalAddress', @@ -80,43 +89,39 @@ def jsonld_event_reservation( 'postalCode': venue.postcode, 'addressCountry': venue.country, } - location_schema['address'] = postal_address + location['address'] = postal_address + else: + location = rsvp.project.location return { - '@context': 'http://schema.org', + '@context': 'https://schema.org', '@type': 'EventReservation', - 'reservationStatus': 'http://schema.org/Confirmed', + 'reservationNumber': rsvp.uuid_b58, + 'reservationStatus': ( + 'https://schema.org/ReservationConfirmed' + if rsvp.state.YES + else 'https://schema.org/ReservationCancelled' + if rsvp.state.NO + else 'https://schema.org/ReservationPending' + ), 'underName': { '@type': 'Person', - 'name': fullname, + 'name': rsvp.participant.fullname, }, 'reservationFor': { '@type': 'Event', - 'name': title, - 'url': url, + 'name': rsvp.project.joined_title, + 'url': rsvp.project.absolute_url, + 'startDate': rsvp.project.start_at, + 'location': location, 'performer': { '@type': 'Organization', - 'name': description, + 'name': rsvp.project.account.title, }, - 'startDate': start_date, - 'location': location_schema, }, 'numSeats': '1', } -def jsonld_confirm_action(description: str, url: str, title: str) -> dict[str, object]: - return { - '@context': 'http://schema.org', - '@type': 'EmailMessage', - 'description': description, - 'potentialAction': { - '@type': 'ConfirmAction', - 'name': title, - 'handler': {'@type': 'HttpActionHandler', 'url': url}, - }, - } - - def process_recipient(recipient: EmailRecipient) -> str: """ Process recipient in any of the given input formats. diff --git a/funnel/views/notifications/rsvp_notification.py b/funnel/views/notifications/rsvp_notification.py index ebc0a2d85..65fe69c24 100644 --- a/funnel/views/notifications/rsvp_notification.py +++ b/funnel/views/notifications/rsvp_notification.py @@ -120,15 +120,7 @@ def email_content(self): return render_template( 'notifications/rsvp_yes_email.html.jinja2', view=self, - jsonld=email.jsonld_event_reservation( - self.rsvp.project.joined_title, - self.rsvp.project.url_for(_external=True), - self.rsvp.project.title, - self.rsvp.project.start_at, - self.rsvp.project.location, - self.rsvp.project.primary_venue, - self.rsvp.participant.fullname, - ), + jsonld=email.jsonld_event_reservation(self.rsvp), ) def sms(