diff --git a/dav_events/migrations/0020_auto_20180704_1202.py b/dav_events/migrations/0020_auto_20180704_1202.py index be25515..f6e15cc 100644 --- a/dav_events/migrations/0020_auto_20180704_1202.py +++ b/dav_events/migrations/0020_auto_20180704_1202.py @@ -52,6 +52,6 @@ class Migration(migrations.Migration): migrations.AddField( model_name='eventflag', name='user', - field=models.ForeignKey(default=dav_events.models.event.get_system_user_id, on_delete=models.SET(dav_events.utils.get_ghost_user), related_name='+', to=settings.AUTH_USER_MODEL), + field=models.ForeignKey(default=dav_events.models.eventflag.get_system_user_id, on_delete=models.SET(dav_events.utils.get_ghost_user), related_name='+', to=settings.AUTH_USER_MODEL), ), ] diff --git a/dav_events/models/__init__.py b/dav_events/models/__init__.py index e2f3afe..e8f44d2 100644 --- a/dav_events/models/__init__.py +++ b/dav_events/models/__init__.py @@ -1,3 +1,4 @@ -from .event import Event, EventFlag +from .event import Event +from .eventflag import EventFlag from .eventstatus import EventStatus from .oneclickaction import OneClickAction diff --git a/dav_events/models/event.py b/dav_events/models/event.py index ad54ddc..89112f0 100644 --- a/dav_events/models/event.py +++ b/dav_events/models/event.py @@ -11,26 +11,22 @@ from django.contrib.auth import get_user_model from django.core.urlresolvers import reverse from django.db import models from django.template.loader import get_template -from django.utils import timezone from django.utils.translation import get_language, ugettext_lazy as _ from django_countries.fields import CountryField from .. import choices from .. import config from .. import signals -from ..utils import get_ghost_user, get_system_user +from ..utils import get_ghost_user from ..workflow import DefaultWorkflow +from .eventflag import EventFlag from .eventstatus import EventStatus, get_or_create_event_status logger = logging.getLogger(__name__) -def get_system_user_id(): - return get_system_user().id - - class Event(models.Model): # Metadata owner = models.ForeignKey(settings.AUTH_USER_MODEL, @@ -314,17 +310,6 @@ class Event(models.Model): logger.info('Event updated: %s', self) signals.event_updated.send(sender=self.__class__, event=self, diff=diff_lines, user=self.editor) - # TODO: move this into workflow - def set_flag(self, status, **kwargs): - if not isinstance(status, EventStatus): - status = get_or_create_event_status(status) - kwargs['event'] = self - kwargs['status'] = status - flag = EventFlag(**kwargs) - flag.save() - logger.info('Flagging status \'%s\' for %s', status.code, self) - return flag - def get_number(self): return self.workflow.get_number() @@ -495,26 +480,3 @@ class Event(models.Model): template_name = os.path.join('dav_events', 'event', 'default.html') template = get_template(template_name) return template.render(self.get_template_context()) - - -# TODO: can we put this into a separated file? -class EventFlag(models.Model): - event = models.ForeignKey(Event, related_name='flags') - status = models.ForeignKey(EventStatus, - on_delete=models.PROTECT, - related_name='+') - timestamp = models.DateTimeField(default=timezone.now) - user = models.ForeignKey(settings.AUTH_USER_MODEL, - default=get_system_user_id, - on_delete=models.SET(get_ghost_user), - related_name='+') - - class Meta: - ordering = ['event', 'timestamp', 'status'] - - def __unicode__(self): - s = u'{status} - {timestamp}' - if self.user: - s += u' by user {user}' - return s.format(status=self.status, timestamp=self.timestamp.strftime('%d.%m.%Y %H:%M:%S'), - user=self.user) diff --git a/dav_events/models/eventflag.py b/dav_events/models/eventflag.py new file mode 100644 index 0000000..471c69d --- /dev/null +++ b/dav_events/models/eventflag.py @@ -0,0 +1,31 @@ +from django.conf import settings +from django.db import models +from django.utils import timezone + +from ..utils import get_ghost_user, get_system_user + + +def get_system_user_id(): + return get_system_user().id + + +class EventFlag(models.Model): + event = models.ForeignKey('dav_events.Event', related_name='flags') + status = models.ForeignKey('dav_events.EventStatus', + on_delete=models.PROTECT, + related_name='+') + timestamp = models.DateTimeField(default=timezone.now) + user = models.ForeignKey(settings.AUTH_USER_MODEL, + default=get_system_user_id, + on_delete=models.SET(get_ghost_user), + related_name='+') + + class Meta: + ordering = ['event', 'timestamp', 'status'] + + def __unicode__(self): + s = u'{status} - {timestamp}' + if self.user: + s += u' by user {user}' + return s.format(status=self.status, timestamp=self.timestamp.strftime('%d.%m.%Y %H:%M:%S'), + user=self.user) diff --git a/dav_events/workflow.py b/dav_events/workflow.py index ffb7d7d..c339760 100644 --- a/dav_events/workflow.py +++ b/dav_events/workflow.py @@ -8,6 +8,8 @@ from django.utils.translation import ugettext_lazy as _ from . import emails from . import signals +from .models.eventflag import EventFlag +from .models.eventstatus import get_or_create_event_status from .utils import get_users_by_role, has_role logger = logging.getLogger(__name__) @@ -35,39 +37,20 @@ class BasicWorkflow(object): # # Status changes # - def get_number(self): + + # We use EventFlags to store the status information + def _add_flag(self, code, **kwargs): event = self._event - if event.number and event.flags.filter(status__code='accepted').exists(): - return event.number - else: - return '%s**/%d' % (event.sport, event.first_day.year % 100) + status = get_or_create_event_status(code) + kwargs['event'] = event + kwargs['status'] = status + flag = EventFlag(**kwargs) + flag.save() + logger.info('Flagging status \'%s\' for %s', status.code, event) + return flag - def set_number(self): - event = self._event - counter = 0 - - year = event.first_day.year - year_begin = datetime.date(year, 1, 1) - year_end = datetime.date(year, 12, 31) - qs = event.__class__.objects.filter(number__isnull=False, - sport=event.sport, - first_day__gte=year_begin, - first_day__lte=year_end).order_by('number') - last = qs.last() - if last: - match = re.match(r'^(?P[A-Z])(?P[0-9][0-9]*)/(?P[0-9][0-9]*)', last.number) - if match: - gd = match.groupdict() - counter = int(gd['count']) - - counter += 1 - event.number = '%s%02d/%d' % (event.sport, counter, year % 100) - event.save(implicit_update=True) - return event.number - - # TODO: the name/intention of this method is unclear. - # Could we make it obsolete? At least it should be private - def check_status(self, code=None): + # TODO: the name/intention of this method is unclear. Could we make it obsolete? + def _check_status(self, code=None): event = self._event if not event.id: return @@ -78,7 +61,7 @@ class BasicWorkflow(object): # 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) + self._add_flag('draft', timestamp=event.created_at) if code in (None, 'accepted'): # Check if event with accepted flag has a number. @@ -130,7 +113,7 @@ class BasicWorkflow(object): if not timestamp: timestamp = event.flags.filter(status__code='publishing').last().timestamp logger.info('Detected published state of Event %s', event) - event.set_flag(status='published', timestamp=timestamp) + self._add_flag('published', timestamp=timestamp) elif ((publishing_web.exists() or published_web.exists()) and (publishing_facebook.exists() or published_facebook.exists())): # All publishers have confirmed the publication date or have published already @@ -138,7 +121,7 @@ class BasicWorkflow(object): if not timestamp: timestamp = pub_flags.last().timestamp logger.info('Detected general published state of Event %s', event) - event.set_flag(status='published', timestamp=timestamp) + self._add_flag('published', timestamp=timestamp) else: if publishing_web.exists() and not published_web.exists(): # One publisher has confirmed the publication date, @@ -146,20 +129,20 @@ class BasicWorkflow(object): if not timestamp: timestamp = event.flags.filter(status__code='publishing_web').last().timestamp logger.info('Detected published_web state of Event %s', event) - event.set_flag(status='published_web', timestamp=timestamp) + self._add_flag('published_web', timestamp=timestamp) if publishing_facebook.exists() and not published_facebook.exists(): # One publisher has confirmed the publication date, # so we can flag, that he/she has published. if not timestamp: timestamp = event.flags.filter(status__code='publishing_facebook').last().timestamp logger.info('Detected published_facebook state of Event %s', event) - event.set_flag(status='published_facebook', timestamp=timestamp) + self._add_flag('published_facebook', timestamp=timestamp) if published_web.exists() and published_facebook.exists(): # All publishers have published, # so we can flag the complete published state. timestamp = pub_flags.last().timestamp logger.info('Detected general published state of Event %s', event) - event.set_flag(status='published', timestamp=timestamp) + self._add_flag('published', timestamp=timestamp) elif not publishing.exists() and publishing_web.exists() and publishing_facebook.exists(): # Event is not due to be published yet, # does not have a general publishing flag, @@ -168,7 +151,7 @@ class BasicWorkflow(object): logger.info('Detected publishing state of Event %s', event) flags = event.flags.filter(status__code__in=('publishing_web', 'publishing_facebook')) timestamp = flags.order_by('timestamp').last().timestamp - event.set_flag(status='publishing', timestamp=timestamp) + self._add_flag('publishing', timestamp=timestamp) if code in (None, 'expired'): # Check if event is expired now and need a expired flag. @@ -190,10 +173,10 @@ class BasicWorkflow(object): 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) + self._add_flag('expired', timestamp=timestamp) def has_reached_status(self, code): - self.check_status(code) + self._check_status(code) if code == 'publishing*': codes = ['publishing', 'publishing_web', 'publishing_facebook'] elif code == 'published*': @@ -243,8 +226,8 @@ class BasicWorkflow(object): if not valid: logger.warning(u'Invalid status update to \'%s\': %s Event: %s', code, message, event) - flag = event.set_flag(status=code, user=user) - self.check_status(code) + flag = self._add_flag(code, user=user) + self._check_status(code) signals.event_status_updated.send(sender=self.__class__, event=event, flag=flag) return flag @@ -253,7 +236,7 @@ class BasicWorkflow(object): event = self._event status_list = [] - event.workflow.check_status() + self._check_status() heaviest_flag = event.flags.order_by('status').last() if heaviest_flag: @@ -283,6 +266,39 @@ class BasicWorkflow(object): return status_list + # + # Status related event properties + # + def get_number(self): + event = self._event + if event.number and event.flags.filter(status__code='accepted').exists(): + return event.number + else: + return '%s**/%d' % (event.sport, event.first_day.year % 100) + + def set_number(self): + event = self._event + counter = 0 + + year = event.first_day.year + year_begin = datetime.date(year, 1, 1) + year_end = datetime.date(year, 12, 31) + qs = event.__class__.objects.filter(number__isnull=False, + sport=event.sport, + first_day__gte=year_begin, + first_day__lte=year_end).order_by('number') + last = qs.last() + if last: + match = re.match(r'^(?P[A-Z])(?P[0-9][0-9]*)/(?P[0-9][0-9]*)', last.number) + if match: + gd = match.groupdict() + counter = int(gd['count']) + + counter += 1 + event.number = '%s%02d/%d' % (event.sport, counter, year % 100) + event.save(implicit_update=True) + return event.number + # # Notifications (loose coupled via signals) # @@ -381,7 +397,7 @@ class BasicWorkflow(object): email.send() # - # Permission + # Permissions # def has_permission(self, user, permission): if user.is_superuser: