Skip to content
This repository has been archived by the owner on Sep 5, 2019. It is now read-only.

Commit

Permalink
Rework notifications
Browse files Browse the repository at this point in the history
  • Loading branch information
msom committed Sep 23, 2016
1 parent 64a2255 commit 3c5f058
Show file tree
Hide file tree
Showing 6 changed files with 120 additions and 93 deletions.
54 changes: 26 additions & 28 deletions onegov/election_day/collection.py
Original file line number Diff line number Diff line change
@@ -1,44 +1,42 @@
from onegov.ballot.models import Election, Vote
from onegov.election_day.models import Notification
from onegov.election_day.models import Notification, WebhookNotification


class NotificationCollection(object):

def __init__(self, session):
self.session = session

def add(self, url, last_change, election_or_vote):
""" Adds a new notification. """

notification = Notification()
notification.url = url
notification.last_change = last_change
if isinstance(election_or_vote, Election):
notification.election_id = election_or_vote.id
if isinstance(election_or_vote, Vote):
notification.vote_id = election_or_vote.id

self.session.add(notification)
self.session.flush()

return notification

def query(self):
return self.session.query(Notification)

def by_election(self, election, url, last_change):
""" Returns the notification specified by given parameters. """
def by_election(self, election):
""" Returns the notification for the given election and its
modification times.
"""

return self.query().filter(
Notification.election_id == election.id,
Notification.url == url,
Notification.last_change == last_change
).first()
Notification.last_change == election.last_result_change
).all()

def by_vote(self, vote):
""" Returns the notification for the given vote and its modification
time.
def by_vote(self, vote, url, last_change):
""" Returns the notification specified by given parameters. """
"""

return self.query().filter(
Notification.vote_id == vote.id,
Notification.url == url,
Notification.last_change == last_change
).first()
Notification.last_change == vote.last_result_change
).all()

def trigger(self, request, model):
""" Triggers and adds all notifications. """

if request.app.principal.webhooks:
notification = WebhookNotification()
notification.trigger(request, model)
self.session.add(notification)

self.session.flush()
10 changes: 8 additions & 2 deletions onegov/election_day/models/__init__.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,12 @@
from onegov.election_day.models.archive import Archive
from onegov.election_day.models.principal import Principal
from onegov.election_day.models.notification import Notification
from onegov.election_day.models.notification import WebhookNotification
from onegov.election_day.models.principal import Principal


__all__ = ['Archive', 'Principal', 'Notification']
__all__ = [
'Archive',
'Notification',
'Principal',
'WebhookNotification'
]
71 changes: 68 additions & 3 deletions onegov/election_day/models/notification.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,22 @@
import json
import logging
import urllib.request

from _thread import start_new_thread
from onegov.ballot.models import Election, Vote
from onegov.core.orm import Base
from onegov.core.orm.mixins import TimestampMixin
from onegov.core.orm.types import UTCDateTime
from onegov.core.orm.types import UUID
from sqlalchemy import Text
from onegov.election_day.utils import get_summary
from sqlalchemy import Column, ForeignKey
from sqlalchemy import Text
from uuid import uuid4


log = logging.getLogger('onegov.election_day') # noqa


class Notification(Base, TimestampMixin):
""" Stores triggered notifications. """

Expand All @@ -16,8 +25,8 @@ class Notification(Base, TimestampMixin):
#: Identifies the notification
id = Column(UUID, primary_key=True, default=uuid4)

#: The URL to call
url = Column(Text, nullable=False)
#: The action made (e.g. the URL called)
action = Column(Text, nullable=False)

#: The corresponding election
election_id = Column(Text, ForeignKey(Election.id), nullable=True)
Expand All @@ -27,3 +36,59 @@ class Notification(Base, TimestampMixin):

#: The last update of the corresponding election/vote
last_change = Column(UTCDateTime)

def update_from_model(self, model):
""" Copy """
self.last_change = model.last_result_change
if isinstance(model, Election):
self.election_id = model.id
if isinstance(model, Vote):
self.vote_id = model.id

def trigger(self, request, model):
""" Trigger the custom actions. """

raise NotImplementedError


def send_post_request(url, data, headers, timeout=30):
""" """
try:
request = urllib.request.Request(url)
for header in headers:
request.add_header(header[0], header[1])
urllib.request.urlopen(request, data, timeout)
except Exception as e:
log.error(
'Error while sending a POST request to {}: {}'.format(
url, str(e)
)
)


class WebhookNotification(Notification):

def trigger(self, request, model):
""" Posts the summary of the given vote or election to the webhook
URL defined for this principal.
This only works for external URL. If posting to server itself is
needed, use a process instead of the thread:
process = Process(target=send_post_request, args=(urls, data))
process.start()
"""
urls = request.app.principal.webhooks
if urls:
self.update_from_model(model)
self.action = 'webhooks'

summary = get_summary(model, request)
data = json.dumps(summary).encode('utf-8')
headers = (
('Content-Type', 'application/json; charset=utf-8'),
('Content-Length', len(data))
)
for url in urls:
start_new_thread(send_post_request, (url, data, headers))
30 changes: 0 additions & 30 deletions onegov/election_day/utils.py
Original file line number Diff line number Diff line change
@@ -1,14 +1,6 @@
import _thread
import json
import logging
import urllib.request

from onegov.ballot import Election, Vote


log = logging.getLogger('onegov.election_day') # noqa


def add_last_modified_header(response, last_modified):
""" Adds the give date to the response as Last-Modified header. """

Expand Down Expand Up @@ -98,25 +90,3 @@ def get_archive_links(archive, request):
str(year): request.link(archive.for_date(year))
for year in archive.get_years()
}


def _post_to(url, data, timeout=30):
try:
data = json.dumps(data).encode('utf-8')
request = urllib.request.Request(url)
request.add_header('Content-Type', 'application/json; charset=utf-8')
request.add_header('Content-Length', len(data))
urllib.request.urlopen(request, data, timeout)
except Exception as e:
log.error(
'Error while sending a POST request to {}: {}'.format(url, e.msg)
)


def post_to(url, data):
try:
_thread.start_new_thread(_post_to, (url, data))
except:
return False

return True
23 changes: 9 additions & 14 deletions onegov/election_day/views/manage_elections.py
Original file line number Diff line number Diff line change
Expand Up @@ -105,28 +105,23 @@ def trigger_notifications(self, request, form):
layout = ManageElectionsLayout(self, request)

if form.submitted(request):
for url in request.app.principal.webhooks:
notifications.add(url, self.last_result_change, self)
notifications.trigger(request, self)
return morepath.redirect(layout.manage_model_link)

callout = None
message = ''
title = _("Trigger notifications")
button_class = 'primary'

for url in request.app.principal.webhooks:
existing = notifications.by_election(
self, url, self.last_result_change
if notifications.by_election(self):
callout = _(
"There are no changes since the last time the notifications "
"have been triggered!"
)
if existing is not None:
callout = _(
"There are no changes since the last time the notifications "
"have been triggered!"
)
message = _(
"Do you really want to retrigger the notfications?",
)
button_class = 'alert'
message = _(
"Do you really want to retrigger the notfications?",
)
button_class = 'alert'

return {
'message': message,
Expand Down
25 changes: 9 additions & 16 deletions onegov/election_day/views/manage_votes.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@
from onegov.election_day.forms import TriggerNotificationForm
from onegov.election_day.forms import VoteForm
from onegov.election_day.layout import ManageVotesLayout
from onegov.election_day.utils import get_vote_summary, post_to


@ElectionDayApp.html(model=VoteCollection, template='manage_votes.pt',
Expand Down Expand Up @@ -105,29 +104,23 @@ def trigger_notifications(self, request, form):
layout = ManageVotesLayout(self, request)

if form.submitted(request):
for url in request.app.principal.webhooks:
if post_to(url, get_vote_summary(self, request)):
notifications.add(url, self.last_result_change, self)
notifications.trigger(request, self)
return morepath.redirect(layout.manage_model_link)

callout = None
message = ''
title = _("Trigger notifications")
button_class = 'primary'

for url in request.app.principal.webhooks:
existing = notifications.by_vote(
self, url, self.last_result_change
if notifications.by_vote(self):
callout = _(
"There are no changes since the last time the notifications "
"have been triggered!"
)
if existing is not None:
callout = _(
"There are no changes since the last time the notifications "
"have been triggered!"
)
message = _(
"Do you really want to retrigger the notfications?",
)
button_class = 'alert'
message = _(
"Do you really want to retrigger the notfications?",
)
button_class = 'alert'

return {
'message': message,
Expand Down

0 comments on commit 3c5f058

Please sign in to comment.