import datetime import logging from django.apps import apps from django.utils import timezone from . import emails from .utils import get_users_by_role logger = logging.getLogger(__name__) class BasicWorkflow(object): # # Status updates # @classmethod def validate_status_code_update(cls, code, event, callback=None, *args, **kwargs): valid = True return_code = 'OK' message = u'OK' if code == 'accepted': if not event.is_flagged('submitted'): valid = False return_code = 'not-submitted' message = u'Event is not submitted.' elif code == 'publishing' or code == 'published': if not event.is_flagged('accepted'): valid = False return_code = 'not-accepted' message = u'Event is not accepted.' if callback is not None: callback(valid, return_code, message, *args, **kwargs) return valid, return_code, message @classmethod def status_code_update(cls, event, code=None): if not event.id: return today = datetime.date.today() midnight = datetime.time(00, 00, 00) if code in (None, 'draft'): # Check if event has a draft flag. if not event.flags.filter(status__code='draft').exists(): logger.info('Detected draft state of Event %s', event) event.set_flag(status='draft', timestamp=event.created_at) if code in (None, 'accepted'): # Check if event with accepted flag has a number. if event.flags.filter(status__code='accepted').exists(): if not event.number: event.set_next_number() logger.info('Setting number on Event %s', event) if code in (None, 'published', 'publishing'): # Check if event with publishing flag should now have a published flag also. if (event.flags.filter(status__code='publishing').exists() and not event.flags.filter(status__code='published').exists()): if not event.planned_publication_date: logger.info('Detected published state of Event %s', event) flag = event.flags.filter(status__code='publishing').last() event.set_flag(status='published', timestamp=flag.timestamp) elif event.planned_publication_date <= today: logger.info('Detected published state of Event %s', event) timestamp = timezone.make_aware(datetime.datetime.combine(event.planned_publication_date, midnight)) event.set_flag(status='published', timestamp=timestamp) if code in (None, 'expired'): # Check if event is expired now and need a expired flag. if not event.flags.filter(status__code='expired').exists(): expired_at = None if event.alt_last_day: if event.alt_last_day < today: expired_at = event.alt_last_day elif event.last_day: if event.last_day < today: expired_at = event.last_day elif event.alt_first_day: if event.alt_first_day < today: expired_at = event.alt_first_day elif event.first_day and event.first_day < today: expired_at = event.first_day if expired_at: logger.info('Detected expired state of Event %s', event) timestamp = timezone.make_aware(datetime.datetime.combine(expired_at, midnight)) event.set_flag(status='expired', timestamp=timestamp) @classmethod def get_number(cls, event): if event.number and event.flags.filter(status__code='accepted').exists(): return event.number else: return None # # Permissions # @classmethod def has_permission(cls, event, user, permission): raise NotImplementedError('not ready yet') if user.is_superuser: return True if permission == 'view': if user == event.owner: return True raise Exception('must check roles') elif permission == 'submit': if user == event.owner: return True elif permission == 'accept': raise Exception('must check roles') elif permission == 'publish': raise Exception('must check roles') elif permission == 'update': raise Exception('must check roles') return False # # Signal handlers # @classmethod def send_emails_on_event_update(cls, sender, **kwargs): event = kwargs.get('event') diff = kwargs.get('diff') updater = kwargs.get('user') app_config = apps.get_containing_app_config(__package__) if not app_config.settings.enable_email_on_update: return if len(diff) < 1: logger.debug('send_emails_on_event_update(): No diff data -> Skip sending mails.') return diff_text = '\n'.join(diff[3:]) # Who should be informed about the update? recipients = [event.owner] if event.is_flagged('submitted'): # If the event is already submitted, add managers to the recipients. recipients += get_users_by_role('manage_all') recipients += get_users_by_role('manage_{}'.format(event.sport.lower())) if event.is_flagged('accepted'): # If the event is already published, add publishers to the recipients. recipients += get_users_by_role('publish_incremental') for recipient in recipients: if recipient.email and recipient.email != updater.email: email = emails.EventUpdatedMail(recipient=recipient, event=event, editor=updater, diff=diff_text) email.send() @classmethod def send_emails_on_event_status_update(cls, sender, **kwargs): event = kwargs.get('event') flag = kwargs.get('flag') updater = flag.user app_config = apps.get_containing_app_config(__package__) if not app_config.settings.enable_email_on_status_update: return if flag.status.code == 'submitted': # Inform event owner about his event (so he can keep the mail as a reminder for the event). if event.owner.email: email = emails.EventSubmittedMail(recipient=event.owner, event=event) email.send() # Inform managers that they have to accept the event. # Also create OneClickActions for all of them and add the link to the mail, # so they can accept the event with a click into the mail. recipients = get_users_by_role('manage_all') recipients += get_users_by_role('manage_{}'.format(event.sport.lower())) OneClickAction = app_config.get_model('OneClickAction') for recipient in recipients: if recipient.email: action = OneClickAction(command='EA') action.parameters = '{event},{user}'.format(event=event.id, user=recipient.id) action.save() email = emails.EventToAcceptMail(recipient=recipient, event=event, accept_action=action) email.send() elif flag.status.code == 'accepted': # Inform event owner about the acceptance of his event. if event.owner.email: email = emails.EventAcceptedMail(recipient=event.owner, event=event, editor=updater) email.send() # Inform publishers that they have to publish the event. # Also create OneClickActions for all of them and add the link to the mail, # so they can confirm the publication with a click into the mail. recipients = get_users_by_role('publish_incremental') OneClickAction = app_config.get_model('OneClickAction') for recipient in recipients: if recipient.email: action = OneClickAction(command='EP') action.parameters = '{event},{user}'.format(event=event.id, user=recipient.id) action.save() email = emails.EventToPublishMail(recipient=recipient, event=event, editor=updater, confirm_publication_action=action) email.send() # # Misc logic # @classmethod def plan_publication(cls, first_day, deadline=None): app_config = apps.get_containing_app_config(__package__) if deadline: publication_deadline = deadline - datetime.timedelta(app_config.settings.publish_before_deadline_days) else: publication_deadline = first_day - datetime.timedelta(app_config.settings.publish_before_begin_days) today = datetime.date.today() for year in (today.year, today.year + 1): for issue in app_config.settings.publish_issues: if not ('issue' in issue and 'release' in issue and 'deadline' in issue): logger.error('workflow.plan_publication(): invalid configured issue.') continue issue_release = datetime.date(year, issue['release'][1], issue['release'][0]) if issue_release < today: continue issue_deadline = datetime.date(year, issue['deadline'][1], issue['deadline'][0]) if issue_deadline > issue_release: issue_deadline = datetime.date(year - 1, issue['deadline'][1], issue['deadline'][0]) if publication_deadline > issue_release and today <= issue_deadline: return issue_release, u'%s/%s' % (issue['issue'], year) return None, None workflow = BasicWorkflow