Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Sends email notifications for directory entries with publication date… #1642

Merged
8 changes: 7 additions & 1 deletion src/onegov/directory/models/directory_entry.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
from datetime import datetime

from onegov.core.orm import Base
from onegov.core.orm.mixins import ContentMixin
from onegov.core.orm.mixins import TimestampMixin
from onegov.core.orm.mixins import UTCPublicationMixin
from onegov.core.orm.types import UUID
from onegov.core.orm.types import UUID, UTCDateTime
from onegov.file import AssociatedFiles
from onegov.gis import CoordinatesMixin
from onegov.search import SearchableContent
Expand Down Expand Up @@ -75,6 +77,10 @@ def es_public(self) -> bool:
#: Describes the entry briefly
lead: 'Column[str | None]' = Column(Text, nullable=True)

#: Marks the entry if publication notifications have been sent
notification_sent: 'Column[datetime | None]' = Column(UTCDateTime,
default=None)

#: All keywords defined for this entry (indexed)
_keywords: 'Column[dict[str, str] | None]' = Column( # type:ignore
MutableDict.as_mutable(HSTORE),
Expand Down
28 changes: 27 additions & 1 deletion src/onegov/directory/upgrade.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,13 @@
upgraded on the server. See :class:`onegov.core.upgrade.upgrade_task`.

"""
from sedate import utcnow
from sqlalchemy import Column, Integer

from onegov.core.orm.types import UTCDateTime
from onegov.core.upgrade import upgrade_task, UpgradeContext
from onegov.directory import Directory
from sqlalchemy import Column, Integer
from onegov.org.models import ExtendedDirectory


@upgrade_task('Add entries count')
Expand Down Expand Up @@ -67,3 +70,26 @@ def make_directory_models_polymorphic_type_non_nullable(
""")

context.operations.alter_column(table, 'type', nullable=False)


@upgrade_task('Directory entries add notification_sent column')
def add_notification_sent_column(context: UpgradeContext) -> None:
if not context.has_column('directory_entries', 'notification_sent'):
context.operations.add_column(
'directory_entries',
Column(
'notification_sent',
UTCDateTime,
nullable=True,
default=None
)
)

context.session.flush()

for directory in context.session.query(ExtendedDirectory):
for entry in directory.entries:
if entry.publication_started:
# prevent sending notifications for exiting entries with
# publication start date in the past
entry.notification_sent = utcnow()
34 changes: 34 additions & 0 deletions src/onegov/org/cronjobs.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
from onegov.core.orm import find_models
from onegov.core.orm.mixins.publication import UTCPublicationMixin
from onegov.core.templates import render_template
from onegov.directory import DirectoryCollection
from onegov.event import Occurrence, Event
from onegov.file import FileCollection
from onegov.form import FormSubmission, parse_form
Expand All @@ -17,10 +18,14 @@
from onegov.org.layout import DefaultMailLayout
from onegov.org.models import (
ResourceRecipient, ResourceRecipientCollection, TANAccess, News)
from onegov.org.models.directory import ExtendedDirectoryEntryCollection, \
ExtendedDirectory
from onegov.org.models.extensions import (
GeneralFileLinkExtension, DeletableContentExtension)
from onegov.org.models.ticket import ReservationHandler
from onegov.org.views.allocation import handle_rules_cronjob
from onegov.org.views.directory import \
send_email_notification_for_directory_entry
from onegov.org.views.newsletter import send_newsletter
from onegov.org.views.ticket import delete_tickets_and_related_data
from onegov.reservation import Reservation, Resource, ResourceCollection
Expand Down Expand Up @@ -69,6 +74,7 @@ def hourly_maintenance_tasks(request: 'OrgRequest') -> None:
publish_files(request)
reindex_published_models(request)
send_scheduled_newsletter(request)
send_email_notification_for_recent_directory_entry_publications(request)
delete_old_tans(request)
delete_old_tan_accesses(request)

Expand All @@ -84,6 +90,34 @@ def send_scheduled_newsletter(request: 'OrgRequest') -> None:
newsletter.scheduled = None


def send_email_notification_for_recent_directory_entry_publications(
request: 'OrgRequest'
) -> None:
"""
Sends notifications to users about recently published `DirectoryEntry`.

"""

now = utcnow()

for directory in DirectoryCollection(request.session).query().filter(
ExtendedDirectory.enable_update_notifications == True):

directory_entries = ExtendedDirectoryEntryCollection(
directory).query().filter(
now >= ExtendedDirectoryEntry.publication_start,
now < ExtendedDirectoryEntry.publication_end,
# ExtendedDirectoryEntry.published == True,
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I got confused as ExtendedDirectoryEntry.published == True did not pass the tests. Any idea why?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good question, not sure.

ExtendedDirectoryEntry.notification_sent == None,
)

for entry in directory_entries:
assert entry.published
send_email_notification_for_directory_entry(
directory, entry, request)
entry.notification_sent = now


def publish_files(request: 'OrgRequest') -> None:
FileCollection(request.session).publish_files()

Expand Down
101 changes: 52 additions & 49 deletions src/onegov/org/views/directory.py
Original file line number Diff line number Diff line change
Expand Up @@ -574,6 +574,56 @@ def as_dict(entry: Any) -> dict[str, Any]:
return tuple(as_dict(e) for e in entries)


def send_email_notification_for_directory_entry(
directory: ExtendedDirectory,
entry: ExtendedDirectoryEntry,
request: 'OrgRequest'
) -> None:
title = request.translate(_(
'${org}: New Entry in "${directory}"',
mapping={'org': request.app.org.title,
'directory': directory.title},
))
entry_link = request.link(entry)
recipients = EntryRecipientCollection(request.session).query(
).filter_by(directory_id=directory.id).filter_by(
confirmed=True).all()

def email_iter() -> 'Iterator[EmailJsonDict]':
for recipient in recipients:
unsubscribe = request.link(
recipient.subscription, 'unsubscribe')
content = render_template(
'mail_new_directory_entry.pt',
request,
{
'layout': DefaultMailLayout(object(), request),
'title': title,
'directory': directory,
'entry_title': entry.title,
'entry_link': entry_link,
'unsubscribe': unsubscribe
},
)
plaintext = html_to_text(content)
print('*** tschupre sending notification to', recipient.address)
yield request.app.prepare_email(
receivers=(recipient.address,),
subject=title,
content=content,
plaintext=plaintext,
category='transactional',
attachments=(),
headers={
'List-Unsubscribe': f'<{unsubscribe}>',
'List-Unsubscribe-Post': (
'List-Unsubscribe=One-Click')
}
)

request.app.send_transactional_email_batch(email_iter())


@OrgApp.form(
model=ExtendedDirectoryEntryCollection,
permission=Private,
Expand Down Expand Up @@ -601,59 +651,12 @@ def handle_new_directory_entry(
transaction.abort()
return request.redirect(request.link(self))

# FIXME: if this entry is not yet published we will need to send
# a notification using some kind of cronjob, but we need
# to take care to only send it once, so we probably need
# to add a marker to entries to indicate that notifications
# have already been sent.
if self.directory.enable_update_notifications and entry.access in (
'public',
'mtan'
) and entry.published:
title = request.translate(_(
'${org}: New Entry in "${directory}"',
mapping={'org': request.app.org.title,
'directory': self.directory.title},
))

entry_link = request.link(entry)

recipients = EntryRecipientCollection(request.session).query(
).filter_by(directory_id=self.directory.id).filter_by(
confirmed=True).all()

def email_iter() -> 'Iterator[EmailJsonDict]':
for recipient in recipients:
unsubscribe = request.link(
recipient.subscription, 'unsubscribe')
content = render_template(
'mail_new_directory_entry.pt',
request,
{
'layout': DefaultMailLayout(object(), request),
'title': title,
'directory': self.directory,
'entry_title': entry.title,
'entry_link': entry_link,
'unsubscribe': unsubscribe
},
)
plaintext = html_to_text(content)
yield request.app.prepare_email(
receivers=(recipient.address,),
subject=title,
content=content,
plaintext=plaintext,
category='transactional',
attachments=(),
headers={
'List-Unsubscribe': f'<{unsubscribe}>',
'List-Unsubscribe-Post': (
'List-Unsubscribe=One-Click')
}
)

request.app.send_transactional_email_batch(email_iter())
send_email_notification_for_directory_entry(
self.directory, entry, request)

request.success(_('Added a new directory entry'))
return request.redirect(request.link(entry))
Expand Down
Loading
Loading