-
Notifications
You must be signed in to change notification settings - Fork 161
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
11 changed files
with
413 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
- model: tutorialv2.EventCategory | ||
pk: 1 | ||
fields: | ||
name: beta | ||
- model: tutorialv2.EventCategory | ||
pk: 2 | ||
fields: | ||
name: validation | ||
- model: tutorialv2.EventCategory | ||
pk: 3 | ||
fields: | ||
name: author |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,71 @@ | ||
{% extends "tutorialv2/base.html" %} | ||
{% load profile %} | ||
{% load thumbnail %} | ||
{% load date %} | ||
{% load i18n %} | ||
{% load captureas %} | ||
|
||
|
||
{% block title %} | ||
{% blocktrans with title=content.title %} | ||
Historique de "{{ title }}" | ||
{% endblocktrans %} | ||
{% endblock %} | ||
|
||
|
||
|
||
{% block breadcrumb %} | ||
<li><a href="{{ content.get_absolute_url }}">{{ content.title }}</a></li> | ||
<li>{% trans "Historique des événements" %}</li> | ||
{% endblock %} | ||
|
||
|
||
|
||
{% block headline %} | ||
{% if content.licence %} | ||
<p class="license"> | ||
{{ content.licence }} | ||
</p> | ||
{% endif %} | ||
|
||
<h1 {% if content.image %}class="illu"{% endif %}> | ||
{% if content.image %} | ||
<img src="{{ content.image.physical.tutorial_illu.url }}" alt=""> | ||
{% endif %} | ||
{% blocktrans with title=content.title %} | ||
Historique de "{{ title }}" | ||
{% endblocktrans %} | ||
</h1> | ||
|
||
{% if content.description %} | ||
<h2 class="subtitle"> | ||
{{ content.description }} | ||
</h2> | ||
{% endif %} | ||
|
||
{% include 'tutorialv2/includes/tags_authors.part.html' with content=content online=False %} | ||
{% endblock %} | ||
|
||
|
||
|
||
{% block content %} | ||
{% include "misc/paginator.html" with position="top" %} | ||
|
||
<table class="fullwidth commits-list"> | ||
<thead> | ||
<tr> | ||
<th width="20%">{% trans "Date" %}</th> | ||
<th>{% trans "Description" %}</th> | ||
</tr> | ||
</thead> | ||
<tbody> | ||
{% for e in events %} | ||
<tr> | ||
<td>{{ e.date | format_date:True }}</td> | ||
<td>{{ e.description | safe }}</td> | ||
</tr> | ||
{% endfor %} | ||
</tbody> | ||
</table> | ||
{% include "misc/paginator.html" with position="bottom" %} | ||
{% endblock %} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
# Generated by Django 2.2.16 on 2020-10-08 20:02 | ||
|
||
from django.conf import settings | ||
from django.db import migrations, models | ||
import django.db.models.deletion | ||
|
||
|
||
class Migration(migrations.Migration): | ||
|
||
dependencies = [ | ||
migrations.swappable_dependency(settings.AUTH_USER_MODEL), | ||
('tutorialv2', '0031_source_is_url'), | ||
] | ||
|
||
operations = [ | ||
migrations.CreateModel( | ||
name='EventCategory', | ||
fields=[ | ||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), | ||
('name', models.CharField(max_length=100, unique=True)), | ||
], | ||
options={ | ||
'verbose_name': "Catégorie d'événement", | ||
'verbose_name_plural': "Catégories d'événement", | ||
}, | ||
), | ||
migrations.CreateModel( | ||
name='Event', | ||
fields=[ | ||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), | ||
('date', models.DateTimeField(auto_now_add=True)), | ||
('type', models.CharField(max_length=100)), | ||
('actor', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, to=settings.AUTH_USER_MODEL)), | ||
('author', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='event_author', to=settings.AUTH_USER_MODEL)), | ||
('content', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='tutorialv2.PublishableContent')), | ||
], | ||
options={ | ||
'verbose_name': 'Événement sur un contenu', | ||
'verbose_name_plural': 'Événements sur un contenu', | ||
}, | ||
), | ||
] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,195 @@ | ||
from django.db import models | ||
from django.contrib.auth.models import User | ||
from django.dispatch import receiver | ||
from django.urls import reverse | ||
from django.utils.translation import ugettext_lazy as _ | ||
|
||
from zds.tutorialv2.models.database import PublishableContent | ||
from zds.tutorialv2 import signals | ||
from zds.tutorialv2.views.authors import AddAuthorToContent, RemoveAuthorFromContent | ||
from zds.tutorialv2.views.beta import ManageBetaContent | ||
from zds.tutorialv2.views.validations_contents import (ReserveValidation, AskValidationForContent, CancelValidation, | ||
RejectValidation, AcceptValidation, RevokeValidation) | ||
|
||
# General architecture | ||
# - Event are assigned to categories (e.g. for filtering purposes) | ||
# - Events have a type | ||
# - Signals map to types | ||
# - In turns, types are stored in database and used for displaying the event | ||
|
||
# Notes on addition/deletion/update of managed signals | ||
# - addition: | ||
# 1. Add a signal->type mapping, | ||
# 2. Write the corresponding descriptor, | ||
# 3. Map the type to the descriptor and voilà ! | ||
# - deletion | ||
# 1. Remove the signal->type mapping and the correspondong receiver | ||
# This will make it impossible to record new events coming from this signal. | ||
# 2. Do nothing more. | ||
# Events in database should be displayed properly, so keep the display logic (e.g. descriptor). | ||
# - update | ||
# 1. If a type name was to be updated for some reason, the database should also be updated (update all records) | ||
# to match this change. | ||
|
||
|
||
# Signals known to the module | ||
signal_to_types = { | ||
signals.author_added: 'author_added', | ||
signals.author_removed: 'author_removed', | ||
signals.beta_activated: 'beta_activated', | ||
signals.beta_deactivated: 'beta_deactivated', | ||
signals.validation_requested: 'validation_requested', | ||
signals.validation_canceled: 'validation_canceled', | ||
signals.validation_accepted: 'validation_accepted', | ||
signals.validation_rejected: 'validation_rejected', | ||
signals.validation_revoked: 'validation_revoked', | ||
signals.validation_reserved: 'validation_reserved', | ||
signals.validation_unreserved: 'validation_unreserved', | ||
} | ||
|
||
|
||
class EventCategory(models.Model): | ||
class Meta: | ||
verbose_name = "Catégorie d'événement" | ||
verbose_name_plural = "Catégories d'événement" | ||
|
||
name = models.CharField(unique=True, max_length=100) | ||
|
||
|
||
class Event(models.Model): | ||
class Meta: | ||
verbose_name = 'Événement sur un contenu' | ||
verbose_name_plural = 'Événements sur un contenu' | ||
|
||
# Base fields | ||
date = models.DateTimeField(auto_now_add=True) | ||
content = models.ForeignKey(PublishableContent, on_delete=models.CASCADE) | ||
actor = models.ForeignKey(User, on_delete=models.SET_NULL, null=True) | ||
type = models.CharField(max_length=100) | ||
|
||
# Field used by author events | ||
author = models.ForeignKey(User, related_name='event_author', on_delete=models.SET_NULL, null=True) | ||
|
||
# Content version | ||
# TODO version of the content concerned by the event | ||
|
||
@property | ||
def description(self): | ||
try: | ||
return descriptors[self.type.__str__()](self) | ||
except KeyError: | ||
return describe_generic(self) | ||
|
||
|
||
# Event descriptors | ||
|
||
def describe_generic(event): | ||
return _('{} a déclenché un événement inconnu.').format(event.actor) | ||
|
||
|
||
def describe_author_added(event): | ||
return _('<a href="{}">{}</a> a ajouté <a href="{}">{}</a> à la liste des auteurs.').format( | ||
reverse('member-detail', args=[event.actor.username]), event.actor, | ||
reverse('member-detail', args=[event.author.username]), event.author) | ||
|
||
|
||
def describe_author_removed(event): | ||
return _('<a href="{}">{}</a> a supprimé <a href="{}">{}</a> de la liste des auteurs.').format( | ||
reverse('member-detail', args=[event.actor.username]), event.actor, | ||
reverse('member-detail', args=[event.author.username]), event.author) | ||
|
||
|
||
def describe_beta_deactivated(event): | ||
return _('<a href="{}">{}</a> a désactivé la bêta.').format( | ||
reverse('member-detail', args=[event.actor.username]), event.actor) | ||
|
||
|
||
def describe_beta_activated(event): | ||
return _('<a href="{}">{}</a> a activé la bêta.').format( | ||
reverse('member-detail', args=[event.actor.username]), event.actor) | ||
|
||
|
||
def describe_beta_updated(event): | ||
return _('<a href="{}">{}</a> a mis à jour la bêta.').format( | ||
reverse('member-detail', args=[event.actor.username]), event.actor) | ||
|
||
|
||
def describe_validation_reserved(event): | ||
return _('<a href="{}">{}</a> a réservé le contenu pour validation.').format( | ||
reverse('member-detail', args=[event.actor.username]), event.actor) | ||
|
||
|
||
def describe_validation_unreserved(event): | ||
return _('<a href="{}">{}</a> a annulé la réservation du contenu pour validation.').format( | ||
reverse('member-detail', args=[event.actor.username]), event.actor) | ||
|
||
|
||
def describe_validation_requested(event): | ||
return _('<a href="{}">{}</a> a demandé la validation du contenu.').format( | ||
reverse('member-detail', args=[event.actor.username]), event.actor) | ||
|
||
|
||
def describe_validation_canceled(event): | ||
return _('<a href="{}">{}</a> a annulé la demande de validation du contenu.').format( | ||
reverse('member-detail', args=[event.actor.username]), event.actor) | ||
|
||
|
||
def describe_validation_accepted(event): | ||
return _('<a href="{}">{}</a> a accepté le contenu pour publication.').format( | ||
reverse('member-detail', args=[event.actor.username]), event.actor) | ||
|
||
|
||
def describe_validation_rejected(event): | ||
return _('<a href="{}">{}</a> a refusé le contenu pour publication.').format( | ||
reverse('member-detail', args=[event.actor.username]), event.actor) | ||
|
||
|
||
def describe_validation_revoked(event): | ||
return _('<a href="{}">{}</a> a dépublié le contenu.').format( | ||
reverse('member-detail', args=[event.actor.username]), event.actor) | ||
|
||
|
||
descriptors = { | ||
'author_added': describe_author_added, | ||
'author_removed': describe_author_removed, | ||
'beta_activated': describe_beta_activated, | ||
'beta_deactivated': describe_beta_deactivated, | ||
'validation_requested': describe_validation_requested, | ||
'validation_canceled': describe_validation_canceled, | ||
'validation_accepted': describe_validation_accepted, | ||
'validation_rejected': describe_validation_rejected, | ||
'validation_revoked': describe_validation_revoked, | ||
'validation_reserved': describe_validation_reserved, | ||
'validation_unreserved': describe_validation_unreserved, | ||
} | ||
|
||
|
||
@receiver(signals.beta_activated, sender=ManageBetaContent) | ||
@receiver(signals.beta_deactivated, sender=ManageBetaContent) | ||
def record_event_beta_management(sender, **kwargs): | ||
print(kwargs) | ||
Event(content=kwargs['content'], | ||
actor=kwargs['actor'], | ||
type=signal_to_types[kwargs['signal']]).save() | ||
|
||
|
||
@receiver(signals.author_added, sender=AddAuthorToContent) | ||
@receiver(signals.author_removed, sender=RemoveAuthorFromContent) | ||
def record_event_author_management(sender, *_, **kwargs): | ||
Event(content=kwargs['content'], | ||
actor=kwargs['actor'], | ||
author=kwargs['author'], | ||
type=signal_to_types[kwargs['signal']]).save() | ||
|
||
|
||
@receiver(signals.validation_requested, sender=AskValidationForContent) | ||
@receiver(signals.validation_canceled, sender=CancelValidation) | ||
@receiver(signals.validation_accepted, sender=AcceptValidation) | ||
@receiver(signals.validation_rejected, sender=RejectValidation) | ||
@receiver(signals.validation_revoked, sender=RevokeValidation) | ||
@receiver(signals.validation_reserved, sender=ReserveValidation) | ||
@receiver(signals.validation_unreserved, sender=ReserveValidation) | ||
def record_event_validation_management(sender, **kwargs): | ||
Event(content=kwargs['content'], | ||
actor=kwargs['actor'], | ||
type=signal_to_types[kwargs['signal']]).save() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.