255 lines
7.5 KiB
Python
255 lines
7.5 KiB
Python
# -*- coding: utf-8 -*-
|
|
|
|
from datetime import timedelta
|
|
|
|
from django.contrib.contenttypes.models import ContentType
|
|
from django.db import models
|
|
from django.conf import settings
|
|
from django.db.models.query import Q
|
|
from django.utils.timezone import now
|
|
from django.core.urlresolvers import reverse
|
|
from django.core.files.storage import default_storage
|
|
|
|
from django.contrib.contenttypes.fields import GenericForeignKey
|
|
|
|
from sorl.thumbnail import get_thumbnail
|
|
|
|
|
|
class Journal(models.Model):
|
|
timestamp = models.DateTimeField(
|
|
verbose_name=u'Horodatage',
|
|
db_index=True,
|
|
auto_now_add=True)
|
|
actor = models.ForeignKey(
|
|
settings.AUTH_USER_MODEL,
|
|
verbose_name=u'Auteur',
|
|
related_name='actor_journal')
|
|
subject = models.ForeignKey(
|
|
settings.AUTH_USER_MODEL,
|
|
verbose_name='Sujet',
|
|
null=True,
|
|
related_name='subject_journal')
|
|
message = models.TextField(
|
|
verbose_name='Message')
|
|
|
|
class Meta:
|
|
verbose_name = 'historique'
|
|
verbose_name_plural = 'historiques'
|
|
ordering = ('-timestamp', '-id')
|
|
|
|
|
|
class ValidationRequestManager(models.Manager):
|
|
def for_origin(self, origin):
|
|
ct = ContentType.objects.get_for_model(origin)
|
|
pk = origin.pk
|
|
return self.filter(origin_ct=ct, origin_id=pk)
|
|
|
|
def next_request(self, user, after=None):
|
|
qs = self.select_for_update()
|
|
qs = qs.order_by('pk')
|
|
# la demande est nouvelle
|
|
# la demande est en cours de traitement mais
|
|
# aucune date de prise en main n'est posée
|
|
# la demande est prise depuis plus d'une heure
|
|
# la demande est prise par <user>
|
|
qs = qs.filter(
|
|
Q(status=ValidationRequest.STATUS_RECEIVED)
|
|
& (Q(taken__isnull=True)
|
|
| Q(taken__lt=now() - timedelta(seconds=self.model.TAKEN_DELAY))
|
|
| Q(taken_by=user)
|
|
)
|
|
)
|
|
# on veut la prochaine demande à traiter
|
|
if after:
|
|
qs = qs.filter(pk__gt=after.pk)
|
|
validation_request = qs[:1].first()
|
|
if validation_request:
|
|
# la demande est associée à l'utilisateur en cours
|
|
validation_request.taken_by = user
|
|
validation_request.taken = now()
|
|
validation_request.save()
|
|
return validation_request
|
|
|
|
def received(self):
|
|
return self.filter(status=ValidationRequest.STATUS_RECEIVED)
|
|
|
|
|
|
class ValidationRequest(models.Model):
|
|
TAKEN_DELAY = 3600
|
|
|
|
STATUS_RECEIVED = 'received'
|
|
STATUS_ACCEPTED = 'accepted'
|
|
STATUS_REFUSED = 'refused'
|
|
|
|
STATUS_CHOICES = (
|
|
(STATUS_RECEIVED, u'reçue'),
|
|
(STATUS_ACCEPTED, u'acceptée'),
|
|
(STATUS_REFUSED, u'refusée'),
|
|
)
|
|
|
|
REASON_UNREADABLE = 'unreadable'
|
|
REASON_INVALID = 'invalid'
|
|
REASON_UNDERAGED = 'underaged'
|
|
REASON_CHOICES = (
|
|
(REASON_UNREADABLE, u'Pièce(s) illisible(s)'),
|
|
(REASON_INVALID, u'Pièce(s) invalides(s)'),
|
|
(REASON_UNDERAGED, u'Invividu mineur'),
|
|
)
|
|
|
|
created = models.DateTimeField(
|
|
verbose_name=u'Date de création',
|
|
db_index=True,
|
|
auto_now_add=True)
|
|
origin_ct = models.ForeignKey(
|
|
'contenttypes.ContentType',
|
|
verbose_name='origin ct')
|
|
origin_id = models.PositiveIntegerField(
|
|
verbose_name='origin id')
|
|
origin = GenericForeignKey('origin_ct', 'origin_id')
|
|
user = models.ForeignKey(
|
|
settings.AUTH_USER_MODEL,
|
|
verbose_name=u'Utilisateur',
|
|
related_name='validation_requests')
|
|
status = models.CharField(
|
|
max_length=16,
|
|
choices=STATUS_CHOICES,
|
|
verbose_name=u'Statut',
|
|
default=STATUS_RECEIVED)
|
|
reason = models.TextField(
|
|
choices=REASON_CHOICES,
|
|
blank=True,
|
|
verbose_name=u'Raison du refus')
|
|
validated = models.DateTimeField(
|
|
null=True,
|
|
blank=True,
|
|
verbose_name=u'Date de validation')
|
|
validated_by = models.ForeignKey(
|
|
settings.AUTH_USER_MODEL,
|
|
verbose_name=u'Validé/refusé par',
|
|
null=True,
|
|
blank=True,
|
|
related_name='validation_requests_validated')
|
|
external_id = models.TextField(
|
|
blank=True,
|
|
null=True,
|
|
verbose_name=u'Identifiant externe')
|
|
taken = models.DateTimeField(
|
|
verbose_name=u'En cours',
|
|
null=True)
|
|
taken_by = models.ForeignKey(
|
|
settings.AUTH_USER_MODEL,
|
|
verbose_name=u'Traité par',
|
|
null=True,
|
|
related_name='validation_requests_taken')
|
|
|
|
objects = ValidationRequestManager()
|
|
|
|
@property
|
|
def is_taken(self):
|
|
return self.taken and now() - self.taken < timedelta(seconds=self.TAKEN_DELAY)
|
|
|
|
def get_taken(self):
|
|
return self.taken if self.is_taken else None
|
|
|
|
def get_taken_by(self):
|
|
return self.taken_by if self.is_taken else None
|
|
|
|
@property
|
|
def human_status(self):
|
|
if self.status == self.STATUS_REFUSED:
|
|
return u'refusé'
|
|
elif self.status == self.STATUS_ACCEPTED:
|
|
return u'accepté'
|
|
elif self.status == self.STATUS_RECEIVED:
|
|
if self.is_taken:
|
|
return u'en traitement'
|
|
else:
|
|
return u'reçu'
|
|
else:
|
|
raise NotImplementedError
|
|
|
|
@property
|
|
def human_status_who(self):
|
|
if self.status == self.STATUS_REFUSED:
|
|
return self.validated_by
|
|
elif self.status == self.STATUS_ACCEPTED:
|
|
return self.validated_by
|
|
elif self.status == self.STATUS_RECEIVED:
|
|
if self.is_taken:
|
|
return self.taken_by
|
|
|
|
def accept(self, user):
|
|
self.status = self.STATUS_ACCEPTED
|
|
self.validated = now()
|
|
self.validated_by = user
|
|
self.save()
|
|
|
|
class Meta:
|
|
verbose_name = u'Requête de validation'
|
|
verbose_name_plural = u'Requêtes de validation'
|
|
ordering = ('-created',)
|
|
unique_together = (
|
|
('origin_ct', 'origin_id', 'user', 'external_id'),
|
|
)
|
|
|
|
|
|
class ValidationRequestAttachment(models.Model):
|
|
validation_request = models.ForeignKey(
|
|
ValidationRequest,
|
|
verbose_name=u'requête de validation',
|
|
related_name='attachments')
|
|
|
|
image = models.ImageField(
|
|
verbose_name='contenu')
|
|
|
|
@property
|
|
def extension(self):
|
|
return self.image.name.rsplit('.', 1)[-1]
|
|
|
|
@property
|
|
def url(self):
|
|
return reverse(
|
|
'cut-manager-user-validation-attachment',
|
|
kwargs={
|
|
'pk': self.pk,
|
|
'filename': self.image.name.rsplit('/', 1)[-1]
|
|
})
|
|
|
|
@property
|
|
def thumbnail(self):
|
|
try:
|
|
thumbnail = get_thumbnail(self.image, '200x200')
|
|
except Exception:
|
|
raise
|
|
return None
|
|
try:
|
|
# check file exists and is readable
|
|
with default_storage.open(thumbnail.name):
|
|
pass
|
|
return thumbnail
|
|
except IOError:
|
|
pass
|
|
return None
|
|
|
|
@property
|
|
def thumbnail_image(self):
|
|
thumbnail = self.thumbnail
|
|
if thumbnail:
|
|
return {
|
|
'src': reverse(
|
|
'cut-manager-user-validation-attachment-thumbnail',
|
|
kwargs={
|
|
'pk': self.pk,
|
|
'filename': self.image.name.rsplit('/', 1)[-1]
|
|
}
|
|
),
|
|
'height': thumbnail.height,
|
|
'width': thumbnail.width,
|
|
}
|
|
return {}
|
|
|
|
class Meta:
|
|
ordering = ('pk',)
|
|
verbose_name = u'Pièce jointe'
|
|
verbose_name_plural = u'Pièces jointes'
|