CONT: fixed the previous changes.

This commit is contained in:
2019-01-30 16:35:37 +01:00
parent 7667277862
commit f0e225c5fd
11 changed files with 79 additions and 59 deletions

View File

@@ -3,12 +3,12 @@ from __future__ import unicode_literals
from django.db import migrations
from dav_events.workflow import DEFAULT_EVENT_STATI
from dav_events.models.eventstatus import get_or_create_event_status
def create_stati(apps, schema_editor):
l = ('draft', 'submitted', 'accepted', 'publishing', 'published', 'expired')
for c in l:
for c in DEFAULT_EVENT_STATI.keys():
get_or_create_event_status(c)

View File

@@ -250,7 +250,6 @@ class Event(models.Model):
verbose_name = _(u'Veranstaltung')
verbose_name_plural = _(u'Veranstaltungen')
ordering = ['first_day']
# default_permissions = ('view', 'edit', 'delete')
def __unicode__(self):
return u'{number} - {title} ({date})'.format(number=self.get_number(),
@@ -498,6 +497,7 @@ class Event(models.Model):
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,
@@ -510,7 +510,7 @@ class EventFlag(models.Model):
related_name='+')
class Meta:
ordering = ['event', 'status', 'timestamp']
ordering = ['event', 'timestamp', 'status']
def __unicode__(self):
s = u'{status} - {timestamp}'

View File

@@ -1,5 +1,4 @@
from django.db import models
from django.utils.html import format_html
from django.utils.translation import ugettext_lazy as _
from ..validators import IdStringValidator
@@ -26,18 +25,10 @@ class EventStatus(models.Model):
ordering = ['severity']
def __unicode__(self):
return u'{label} ({severity} - {code})'.format(code=self.code,
return u'{severity} - {code} ({label})'.format(code=self.code,
severity=self.severity,
label=self.label)
def get_bootstrap_label(self):
context = self.bootstrap_context or 'default'
return format_html(u'<span class="label label-{context}">{label}</span>',
context=context,
label=self.label)
#return u'<span class="label label-{context}">{label}</span>'.format(context=context,
# label=self.label)
def get_or_create_event_status(code):
try:

View File

@@ -4,7 +4,7 @@
<div class="panel panel-default">
<div class="panel-heading">
<div class="pull-right">
{% render_event_status event show_void='False' %}
{% render_event_status event show_void=False %}
</div>
<span class="panel-title">{{ number }} - {{ title }}</span>
</div>

View File

@@ -23,7 +23,7 @@
</p>
</div>
<div class="modal-footer">
<a class="btn btn-success" href="{% url 'dav_events:confirmstatus' event.pk 'submitted' %}">
<a class="btn btn-success" href="{% url 'dav_events:updatestatus' event.pk 'submitted' %}">
{% bootstrap_icon 'ok' %}&thinsp;
{% trans 'Ja, alles klar!' %}
</a>
@@ -51,7 +51,7 @@
</p>
</div>
<div class="modal-footer">
<a class="btn btn-success" href="{% url 'dav_events:confirmstatus' event.pk 'accepted' %}">
<a class="btn btn-success" href="{% url 'dav_events:updatestatus' event.pk 'accepted' %}">
{% bootstrap_icon 'ok' %}&thinsp;
{% trans 'Ja, passt schon!' %}
</a>
@@ -84,15 +84,15 @@
</p>
</div>
<div class="modal-footer">
<a class="btn btn-info" href="{% if event.planned_publication_date %}{% url 'dav_events:confirmstatus' event.pk 'publishing_facebook' %}{% else %}{% url 'dav_events:confirmstatus' event.pk 'published' %}{% endif %}">
<a class="btn btn-info" href="{% url 'dav_events:updatestatus' event.pk 'publishing_facebook' %}">
{% bootstrap_icon 'ok' %}&thinsp;
{% trans 'Facebook' %}
</a>
<a class="btn btn-primary" href="{% if event.planned_publication_date %}{% url 'dav_events:confirmstatus' event.pk 'publishing_web' %}{% else %}{% url 'dav_events:confirmstatus' event.pk 'published' %}{% endif %}">
<a class="btn btn-primary" href="{% url 'dav_events:updatestatus' event.pk 'publishing_web' %}">
{% bootstrap_icon 'ok' %}&thinsp;
{% trans 'Webseite' %}
</a>
<a class="btn btn-success" href="{% if event.planned_publication_date %}{% url 'dav_events:confirmstatus' event.pk 'publishing' %}{% else %}{% url 'dav_events:confirmstatus' event.pk 'published' %}{% endif %}">
<a class="btn btn-success" href="{% url 'dav_events:updatestatus' event.pk 'publishing' %}">
{% bootstrap_icon 'ok' %}&thinsp;
{% trans 'Überall' %}
</a>
@@ -132,9 +132,9 @@
</a>
{% endif %}
{% if has_permission_publish %}
<a class="btn {% if is_accepted and not is_publishing %}btn-success{% else %}btn-default disabled{% endif %}"
<a class="btn {% if is_accepted and not is_publishing and not is_published %}btn-success{% else %}btn-default disabled{% endif %}"
data-toggle="modal" data-target="#modal-confirmpublication-dialog">
{% if is_publishing %}
{% if is_publishing_any %}
{% bootstrap_icon 'check' %}&thinsp;
{% else %}
{% bootstrap_icon 'unchecked' %}&thinsp;
@@ -171,7 +171,7 @@
<div class="panel panel-default">
<div class="panel-body">
<div class="row">
<div class="col-sm-5">
<div class="col-sm-7">
<h5>Status-Log</h5>
{% for flag in event.flags.all %}
<div class="row">
@@ -186,17 +186,15 @@
</div>
{% endfor %}
</div>
<div class="col-sm-3">
<h5>{% trans 'Veröffentlichen' %}</h5>
<div class="col-sm-5">
<h5>{% trans 'Veröffentlichung' %}</h5>
{% if event.planned_publication_date %}
{{ event.planned_publication_date|date:'l, d. F Y' }}
{% else %}
{% trans 'Unverzüglich' %}
{% endif %}
</div>
<div class="col-sm-4">
{% if event.internal_note %}
<h5>{% trans 'Bearbeitungshinweis' %}</h5>
<h5 style="margin-top: 1em;">{% trans 'Bearbeitungshinweis' %}</h5>
<div class="well well-sm"><small>{{ event.internal_note|linebreaksbr }}</small></div>
{% endif %}
</div>

View File

@@ -26,7 +26,7 @@
<button type="button" class="close" data-dismiss="alert" aria-label="Close"><span aria-hidden="true">&times;</span></button>
{% trans 'Diese Veranstaltung ist bereits ausgelaufen.' %}
</div>
{% elif is_publishing %}
{% elif is_publishing_any %}
<div class="alert alert-danger alert-dismissible" role="alert">
<button type="button" class="close" data-dismiss="alert" aria-label="Close"><span aria-hidden="true">&times;</span></button>
<strong>{% trans 'Achtung!' %}</strong> {% trans 'Diese Veranstaltung wird/wurde bereits veröffentlicht.' %}

View File

@@ -1,13 +1,16 @@
from django import template
from django.utils.html import format_html
from django.utils.safestring import mark_safe
from ..models.eventstatus import get_or_create_event_status
from ..models.eventstatus import EventStatus, get_or_create_event_status
register = template.Library()
@register.simple_tag
def render_event_status(event, show_void=True):
label_html = u'<span class="label label-{context}">{label}</span> '
html = u''
status_list = event.workflow.get_status_list()
@@ -15,6 +18,15 @@ def render_event_status(event, show_void=True):
status_list = [get_or_create_event_status('void')]
for status in status_list:
html += status.get_bootstrap_label()
if isinstance(status, EventStatus):
label = status.label
context = status.bootstrap_context
else:
label = status.get('label')
context = status.get('bootstrap_context', 'default')
html += format_html(label_html,
label=label,
context=context)
return mark_safe(html)

View File

@@ -7,8 +7,8 @@ urlpatterns = [
url(r'^$', views.events.EventListView.as_view(), name='list'),
url(r'^export$', views.events.EventListExportView.as_view(), name='list_export'),
url(r'^create$', views.events.EventCreateView.as_view(), name='create'),
url(r'^(?P<pk>\d+)/confirm/(?P<status>[a-z0-9][a-z0-9]*)',
views.events.EventConfirmStatusView.as_view(), name='confirmstatus'),
url(r'^(?P<pk>\d+)/status/(?P<status>[a-z0-9._-][a-z0-9._-]*)',
views.events.EventUpdateStatusView.as_view(), name='updatestatus'),
url(r'^(?P<pk>\d+)/edit', views.events.EventUpdateView.as_view(), name='update'),
url(r'^(?P<pk>\d+)/', views.events.EventDetailView.as_view(), name='detail'),
url(r'^action/(?P<pk>[a-fA-F0-9]{8}-([a-fA-F0-9]{4}-){3}[a-fA-F0-9]{12})/',

View File

@@ -7,6 +7,8 @@ from django.db.models import Q
app_config = apps.get_containing_app_config(__package__)
logger = logging.getLogger(__name__)
# TODO: most of the functions here are auth stuff.
def get_system_user():
return get_user_model().objects.get_or_create(username='-system-')[0]

View File

@@ -148,6 +148,8 @@ class EventDetailView(EventPermissionMixin, generic.DetailView):
context['is_submitted'] = obj.workflow.has_reached_status('submitted')
context['is_accepted'] = obj.workflow.has_reached_status('accepted')
context['is_publishing'] = obj.workflow.has_reached_status('publishing')
context['is_publishing_any'] = obj.workflow.has_reached_status('publishing*')
context['is_published'] = obj.workflow.has_reached_status('published')
return context
@method_decorator(login_required)
@@ -155,7 +157,7 @@ class EventDetailView(EventPermissionMixin, generic.DetailView):
return super(EventDetailView, self).dispatch(request, *args, **kwargs)
class EventConfirmStatusView(EventPermissionMixin, generic.DetailView):
class EventUpdateStatusView(EventPermissionMixin, generic.DetailView):
model = models.Event
def get(self, request, *args, **kwargs):
@@ -217,6 +219,7 @@ class EventUpdateView(EventPermissionMixin, generic.UpdateView):
context['has_permission_publish'] = self.has_permission('publish', obj)
context['is_expired'] = obj.workflow.has_reached_status('expired')
context['is_publishing'] = obj.workflow.has_reached_status('publishing')
context['is_publishing_any'] = obj.workflow.has_reached_status('publishing*')
context['is_accepted'] = obj.workflow.has_reached_status('accepted')
return context

View File

@@ -18,11 +18,11 @@ DEFAULT_EVENT_STATI = {
'draft': (10, _(u'Entwurf'), 'info'),
'submitted': (30, _(u'Eingereicht'), 'danger'),
'accepted': (50, _(u'Freigegeben'), 'warning'),
'publishing_facebook': (68, _(u'Veröffentlichung Facebook am {planned_publication_date}'), 'warning'),
'publishing_web': (69, _(u'Veröffentlichung Web am {planned_publication_date}'), 'warning'),
'publishing': (70, _(u'Veröffentlichung am {planned_publication_date}'), 'warning'),
'published_facebook': (78, _(u'Veröffentlicht Facebook'), 'success'),
'published_web': (79, _(u'Veröffentlicht Web'), 'success'),
'publishing_facebook': (68, _(u'Veröffentlichung (Facebook)'), 'warning'),
'publishing_web': (69, _(u'Veröffentlichung (Web)'), 'warning'),
'publishing': (70, _(u'Veröffentlichung'), 'warning'),
'published_facebook': (78, _(u'Veröffentlicht (Facebook)'), 'success'),
'published_web': (79, _(u'Veröffentlicht (Web)'), 'success'),
'published': (80, _(u'Veröffentlicht'), 'success'),
'expired': (100, _(u'Ausgelaufen'), None),
}
@@ -49,10 +49,10 @@ class BasicWorkflow(object):
year = event.first_day.year
year_begin = datetime.date(year, 1, 1)
year_end = datetime.date(year, 12, 31)
qs = event.objects.filter(number__isnull=False,
sport=event.sport,
first_day__gte=year_begin,
first_day__lte=year_end).order_by('number')
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<sport>[A-Z])(?P<count>[0-9][0-9]*)/(?P<year>[0-9][0-9]*)', last.number)
@@ -65,6 +65,8 @@ class BasicWorkflow(object):
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):
event = self._event
if not event.id:
@@ -104,6 +106,7 @@ class BasicWorkflow(object):
'published_web',
'published')).order_by('timestamp')
publishing = pub_flags.filter(status__code='publishing')
publishing_web = pub_flags.filter(status__code='publishing_web')
publishing_facebook = pub_flags.filter(status__code='publishing_facebook')
published_web = pub_flags.filter(status__code='published_web')
@@ -112,7 +115,7 @@ class BasicWorkflow(object):
if not event.planned_publication_date or event.planned_publication_date <= today:
# Event is due to be published.
# Timestamp of the detected action flag. No very good.
# Timestamp of the detected action flag. No very good. TODO
if event.planned_publication_date:
timestamp = timezone.make_aware(datetime.datetime.combine(
event.planned_publication_date,
@@ -157,12 +160,13 @@ class BasicWorkflow(object):
timestamp = pub_flags.last().timestamp
logger.info('Detected general published state of Event %s', event)
event.set_flag(status='published', timestamp=timestamp)
elif publishing_web.exists() and publishing_facebook.exists():
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,
# but all publishers have confirmed the publication date,
# so we set a general publishing flag.
logger.info('Detected publishing state of Event %s', event)
flags = event.flags.filter(status_code__in=('publishing_web', 'publishing_facebook'))
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)
@@ -190,9 +194,9 @@ class BasicWorkflow(object):
def has_reached_status(self, code):
self.check_status(code)
if code == 'publishing':
if code == 'publishing*':
codes = ['publishing', 'publishing_web', 'publishing_facebook']
elif code == 'published':
elif code == 'published*':
codes = ['published', 'published_web', 'published_facebook']
else:
codes = [code]
@@ -235,7 +239,7 @@ class BasicWorkflow(object):
if flag:
return flag
valid, return_code, message = self.validate_status_update(code, user)
valid, return_code, message = self.validate_status_update(code)
if not valid:
logger.warning(u'Invalid status update to \'%s\': %s Event: %s', code, message, event)
@@ -251,10 +255,10 @@ class BasicWorkflow(object):
status_list = []
event.workflow.check_status()
last_flag = event.flags.last()
if last_flag:
flags = [last_flag]
last_status = last_flag.status
heaviest_flag = event.flags.order_by('status').last()
if heaviest_flag:
flags = []
last_status = heaviest_flag.status
if last_status.code.startswith('publishing_'):
flags += event.flags.filter(status__code='accepted')
elif last_status.code.startswith('published_'):
@@ -262,12 +266,20 @@ class BasicWorkflow(object):
flags += event.flags.filter(status__code='publishing')
else:
flags += event.flags.filter(status__code='accepted')
flags.append(heaviest_flag)
deferred_publishing = event.planned_publication_date and event.planned_publication_date > today
add_publishing_date = False
for flag in flags:
format_kwargs = event.get_template_context()
label = flag.status.label.format(**format_kwargs)
flag.status.label = label
status_list.append(flag.status)
if deferred_publishing and flag.status.code.startswith('publishing'):
add_publishing_date = True
if add_publishing_date:
date_str = event.planned_publication_date.strftime('%d.%m.%Y')
label = _(u'Veröffentlichung am {date}').format(date=date_str)
status_list.append({'label': label})
return status_list
@@ -313,6 +325,7 @@ class BasicWorkflow(object):
return True
return False
# TODO: is a class method a good idea?
@classmethod
def has_global_permission(cls, user, permission):
if user.is_superuser:
@@ -325,6 +338,8 @@ class BasicWorkflow(object):
#
# Signal handlers
#
# TODO: the signal handlers should not be part of the workflow class,
# but call a method from the workflow of the particular event.
@classmethod
def send_emails_on_event_update(cls, sender, **kwargs):
event = kwargs.get('event')
@@ -344,7 +359,6 @@ class BasicWorkflow(object):
recipients = [event.owner]
if event.workflow.has_reached_status('submitted'):
# If the event is already submitted, add managers to the recipients.
recipients += get_users_by_role('manager_super')
recipients += get_users_by_role('manager_{}'.format(event.sport.lower()))
if event.workflow.has_reached_status('accepted'):
# If the event is already published, add publishers to the recipients.
@@ -375,8 +389,7 @@ class BasicWorkflow(object):
# 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('manager_super')
recipients += get_users_by_role('manager_{}'.format(event.sport.lower()))
recipients = get_users_by_role('manager_{}'.format(event.sport.lower()))
OneClickAction = app_config.get_model('OneClickAction')
for recipient in recipients:
if recipient.email:
@@ -429,6 +442,7 @@ class BasicWorkflow(object):
#
# Misc logic
#
# TODO: is a class method a good idea?
@classmethod
def plan_publication(cls, first_day, deadline=None):
app_config = apps.get_containing_app_config(__package__)