409 lines
13 KiB
Python
409 lines
13 KiB
Python
![]() |
#!/usr/bin/env python
|
||
|
# -*- coding: utf-8 -*-
|
||
|
"""
|
||
|
openslides.application.models
|
||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||
|
|
||
|
Models for the application app.
|
||
|
|
||
|
:copyright: 2011 by the OpenSlides team, see AUTHORS.
|
||
|
:license: GNU GPL, see LICENSE for more details.
|
||
|
"""
|
||
|
|
||
|
from datetime import datetime
|
||
|
|
||
|
from django.db import models
|
||
|
from django.db.models import Max
|
||
|
from django.contrib.auth.models import User
|
||
|
from django.utils.translation import ugettext as _
|
||
|
|
||
|
from openslides.system.api import config_get
|
||
|
|
||
|
|
||
|
class Application(models.Model):
|
||
|
STATUS = (
|
||
|
('pub', _('Published')),
|
||
|
('per', _('Permitted')),
|
||
|
('acc', _('Accepted')),
|
||
|
('rej', _('Rejected')),
|
||
|
('wit', _('Withdrawed')),
|
||
|
('adj', _('Adjourned')),
|
||
|
('noc', _('Not Concerned')),
|
||
|
('com', _('Commited a bill')),
|
||
|
('nop', _('Rejected (not permitted)')),
|
||
|
#additional actions:
|
||
|
# edit
|
||
|
# delete
|
||
|
# setnumber
|
||
|
# support
|
||
|
# unsupport
|
||
|
# createitem
|
||
|
# genpoll
|
||
|
)
|
||
|
|
||
|
submitter = models.ForeignKey(User, verbose_name=_("Submitter"))
|
||
|
supporter = models.ManyToManyField(User, related_name='supporter', \
|
||
|
null=True, blank=True, verbose_name=_("Supporters"))
|
||
|
number = models.PositiveSmallIntegerField(blank=True, null=True,
|
||
|
unique=True)
|
||
|
status = models.CharField(max_length=3, choices=STATUS, default='pub')
|
||
|
permitted = models.ForeignKey('AVersion', related_name='permitted', \
|
||
|
null=True, blank=True)
|
||
|
log = models.TextField(blank=True, null=True)
|
||
|
|
||
|
@property
|
||
|
def last_version(self):
|
||
|
"""
|
||
|
Return last version of the application.
|
||
|
"""
|
||
|
try:
|
||
|
return AVersion.objects.filter(application=self).order_by('id') \
|
||
|
.reverse()[0]
|
||
|
except IndexError:
|
||
|
return None
|
||
|
|
||
|
@property
|
||
|
def versions(self):
|
||
|
"""
|
||
|
Return a list of all versions of the application.
|
||
|
"""
|
||
|
return AVersion.objects.filter(application=self)
|
||
|
|
||
|
@property
|
||
|
def creation_time(self):
|
||
|
"""
|
||
|
Return the time of the creation of the application.
|
||
|
"""
|
||
|
try:
|
||
|
return self.versions[0].time
|
||
|
except IndexError:
|
||
|
return None
|
||
|
|
||
|
@property
|
||
|
def notes(self):
|
||
|
"""
|
||
|
Return some information of the application.
|
||
|
"""
|
||
|
note = []
|
||
|
if self.status == "pub" and not self.enough_supporters:
|
||
|
note.append(_("Searching for supporters."))
|
||
|
if self.status == "pub" and self.permitted is None:
|
||
|
note.append(_("Not yet permitted."))
|
||
|
elif self.unpermitted_changes and self.permitted:
|
||
|
note.append(_("Not yet permitted changes."))
|
||
|
return note
|
||
|
|
||
|
@property
|
||
|
def unpermitted_changes(self):
|
||
|
"""
|
||
|
Return True if the application has unpermitted changes.
|
||
|
|
||
|
The application has unpermitted changes, if the permitted-version
|
||
|
is not the lastone and the lastone is not abjected.
|
||
|
TODO: rename the property in unchecked__changes
|
||
|
"""
|
||
|
if (self.last_version != self.permitted
|
||
|
and not self.last_version.abjected):
|
||
|
return True
|
||
|
else:
|
||
|
return False
|
||
|
|
||
|
@property
|
||
|
def enough_supporters(self):
|
||
|
"""
|
||
|
Return True, if the application has enough supporters
|
||
|
"""
|
||
|
min_supporters = int(config_get('application_min_supporters'))
|
||
|
return self.supporter.count() >= min_supporters
|
||
|
|
||
|
def save(self, user=None):
|
||
|
"""
|
||
|
Save the Application, and create a new AVersion if necessary
|
||
|
"""
|
||
|
super(Application, self).save()
|
||
|
last_version = self.last_version
|
||
|
if last_version is not None:
|
||
|
if (last_version.text == self.text
|
||
|
and last_version.title == self.title
|
||
|
and last_version.reason == self.reason):
|
||
|
return # No changes
|
||
|
try:
|
||
|
if self.title != "":
|
||
|
version = AVersion(title=getattr(self, 'title', ''),
|
||
|
text=getattr(self, 'text', ''),
|
||
|
reason=getattr(self, 'reason', ''),
|
||
|
application=self)
|
||
|
version.save()
|
||
|
self.writelog(_("Version %s created") % version.aid, user)
|
||
|
is_manager = user.has_perm('application.can_manage_application')
|
||
|
except AttributeError:
|
||
|
is_manager = False
|
||
|
|
||
|
if (self.status == "pub"
|
||
|
and self.supporter.exists()
|
||
|
and not is_manager):
|
||
|
self.supporter.clear()
|
||
|
self.writelog(_("Supporters removed"), user)
|
||
|
|
||
|
def reset(self, user):
|
||
|
"""
|
||
|
Reset the application.
|
||
|
"""
|
||
|
self.status = "pub"
|
||
|
self.permitted = None
|
||
|
self.save()
|
||
|
self.writelog(_("Status reseted to: %s") % (self.get_status_display()), user)
|
||
|
|
||
|
def support(self, user):
|
||
|
"""
|
||
|
Add a Supporter to the list of supporters of the application.
|
||
|
"""
|
||
|
if user == self.submitter:
|
||
|
raise NameError('Supporter can not be the submitter of a ' \
|
||
|
'application.')
|
||
|
if self.permitted is not None:
|
||
|
raise NameError('This application is already permitted.')
|
||
|
if user not in self.supporter.all():
|
||
|
self.supporter.add(user)
|
||
|
self.writelog(_("Supporter: +%s") % (user))
|
||
|
|
||
|
def unsupport(self, user):
|
||
|
"""
|
||
|
remove a supporter from the list of supporters of the application
|
||
|
"""
|
||
|
if self.permitted is not None:
|
||
|
raise NameError('This application is already permitted.')
|
||
|
if user in self.supporter.all():
|
||
|
self.supporter.remove(user)
|
||
|
self.writelog(_("Supporter: -%s") % (user))
|
||
|
|
||
|
def set_number(self, number=None, user=None):
|
||
|
"""
|
||
|
Set a number for ths application.
|
||
|
"""
|
||
|
if self.number is not None:
|
||
|
raise NameError('This application has already a number.')
|
||
|
if number is None:
|
||
|
try:
|
||
|
number = Application.objects.aggregate(Max('number')) \
|
||
|
['number__max'] + 1
|
||
|
except TypeError:
|
||
|
number = 1
|
||
|
self.number = number
|
||
|
self.save()
|
||
|
self.writelog(_("Number set: %s") % (self.number), user)
|
||
|
return self.number
|
||
|
|
||
|
def permit(self, user=None):
|
||
|
"""
|
||
|
Change the status of this application to permit.
|
||
|
"""
|
||
|
self.set_status(user, "per")
|
||
|
aversion = self.last_version
|
||
|
if self.number is None:
|
||
|
self.set_number()
|
||
|
self.permitted = aversion
|
||
|
self.save()
|
||
|
self.writelog(_("Version %s permitted") % (aversion.aid), user)
|
||
|
return self.permitted
|
||
|
|
||
|
def notpermit(self, user=None):
|
||
|
"""
|
||
|
Change the status of this application to 'not permitted (rejected)'.
|
||
|
"""
|
||
|
self.set_status(user, "nop")
|
||
|
#TODO: reject last version
|
||
|
aversion = self.last_version
|
||
|
#self.permitted = aversion
|
||
|
if self.number is None:
|
||
|
self.set_number()
|
||
|
self.save()
|
||
|
self.writelog(_("Version %s not permitted") % (self.last_version.aid), user)
|
||
|
|
||
|
def set_status(self, user, status, force=False):
|
||
|
"""
|
||
|
Set the status of the application.
|
||
|
"""
|
||
|
error = True
|
||
|
for a, b in Application.STATUS:
|
||
|
if status == a:
|
||
|
error = False
|
||
|
break
|
||
|
if error:
|
||
|
raise NameError('%s is not a valid status.' % status)
|
||
|
if self.status == status:
|
||
|
raise NameError('The application status is already %s.' \
|
||
|
% self.status)
|
||
|
|
||
|
actions = []
|
||
|
actions = self.get_allowed_actions(user)
|
||
|
if status not in actions and not force:
|
||
|
raise NameError('The application status is: %s. You can not set' \
|
||
|
' the status to %s.' % (self.status, status))
|
||
|
|
||
|
oldstatus = self.get_status_display()
|
||
|
self.status = status
|
||
|
self.save()
|
||
|
self.writelog(_("Status modified")+": %s -> %s" \
|
||
|
% (oldstatus, self.get_status_display()), user)
|
||
|
|
||
|
def get_allowed_actions(self, user=None):
|
||
|
"""
|
||
|
Return a list of all the allowed status.
|
||
|
"""
|
||
|
actions = []
|
||
|
|
||
|
# check if user allowed to withdraw an application
|
||
|
if (self.status == "pub"
|
||
|
and self.number \
|
||
|
and user == self.submitter) \
|
||
|
or (self.status == "pub" \
|
||
|
and self.number \
|
||
|
and user.has_perm("application.can_manage_application")) \
|
||
|
or (self.status == "per" \
|
||
|
and user == self.submitter) \
|
||
|
or (self.status == "per" \
|
||
|
and user.has_perm("application.can_manage_application")):
|
||
|
actions.append("wit")
|
||
|
|
||
|
# Check if the user can support and unspoort the application
|
||
|
if self.status == "pub" \
|
||
|
and user != self.submitter \
|
||
|
and user not in self.supporter.all():
|
||
|
actions.append("support")
|
||
|
|
||
|
if self.status == "pub" and user in self.supporter.all():
|
||
|
actions.append("unsupport")
|
||
|
|
||
|
#Check if the user can edit the application
|
||
|
if user == self.submitter \
|
||
|
or user.has_perm("application.can_manage_application"):
|
||
|
actions.append("edit")
|
||
|
|
||
|
#Check if the user can delete the application
|
||
|
if self.number is None \
|
||
|
and self.status == "pub" \
|
||
|
and (self.submitter == user \
|
||
|
or user.has_perm("application.can_manage_application")):
|
||
|
actions.append("delete")
|
||
|
|
||
|
#For the rest, all actions need the manage permission
|
||
|
if not user.has_perm("application.can_manage_application"):
|
||
|
return actions
|
||
|
|
||
|
if self.status == "pub":
|
||
|
actions.append("nop")
|
||
|
actions.append("per")
|
||
|
if self.number == None:
|
||
|
actions.append("setnumber")
|
||
|
|
||
|
if self.status == "per":
|
||
|
actions.append("acc")
|
||
|
actions.append("rej")
|
||
|
actions.append("adj")
|
||
|
actions.append("noc")
|
||
|
actions.append("com")
|
||
|
actions.append("genpoll")
|
||
|
if self.unpermitted_changes:
|
||
|
actions.append("permitversion")
|
||
|
actions.append("rejectversion")
|
||
|
|
||
|
if self.number and not self.itemapplication_set.all():
|
||
|
actions.append("createitem")
|
||
|
|
||
|
return actions
|
||
|
|
||
|
def delete(self, force=False):
|
||
|
"""
|
||
|
Delete the application. It is not possible, if the application has
|
||
|
allready a number
|
||
|
"""
|
||
|
if self.number and not force:
|
||
|
raise NameError('The application has already a number. ' \
|
||
|
'You can not delete it.')
|
||
|
super(Application, self).delete()
|
||
|
|
||
|
def writelog(self, text, user=None):
|
||
|
if not self.log:
|
||
|
self.log = ""
|
||
|
self.log += u"%s | %s" % (datetime.now().strftime("%d.%m.%Y %H:%M:%S"), text)
|
||
|
if user is not None:
|
||
|
self.log += u" (by %s)" % (user.username)
|
||
|
self.log += "\n"
|
||
|
self.save()
|
||
|
|
||
|
def __getattr__(self, name):
|
||
|
"""
|
||
|
if name is title, text, reason or time,
|
||
|
Return this attribute from the newest version of the application
|
||
|
"""
|
||
|
if name in ('title', 'text', 'reason', 'time'):
|
||
|
try:
|
||
|
return self.last_version.__dict__[name]
|
||
|
except TypeError:
|
||
|
raise AttributeError(name)
|
||
|
except AttributeError:
|
||
|
raise AttributeError(name)
|
||
|
raise AttributeError(name)
|
||
|
|
||
|
def gen_poll(self, user=None, pollcount=None):
|
||
|
"""
|
||
|
Generates a poll object for the application
|
||
|
"""
|
||
|
from poll.models import Poll
|
||
|
if pollcount > 1:
|
||
|
description = _("%s. poll") % pollcount
|
||
|
else:
|
||
|
description = _("Poll")
|
||
|
poll = Poll(title=_("Vote on application #%s") % self.number, \
|
||
|
optiondecision=True, \
|
||
|
application=self,
|
||
|
description=description)
|
||
|
poll.save()
|
||
|
poll.add_option(self)
|
||
|
self.writelog(_("Poll created"), user)
|
||
|
return poll
|
||
|
|
||
|
@models.permalink
|
||
|
def get_absolute_url(self, link='view'):
|
||
|
if link == 'view':
|
||
|
return ('application_view', [str(self.id)])
|
||
|
if link == 'delete':
|
||
|
return ('application_delete', [str(self.id)])
|
||
|
|
||
|
def __unicode__(self):
|
||
|
try:
|
||
|
return self.last_version.title
|
||
|
except AttributeError:
|
||
|
return "no title jet"
|
||
|
|
||
|
class Meta:
|
||
|
permissions = (
|
||
|
('can_view_application', _("Can see applications")),
|
||
|
('can_insert_application', _("Can insert new applications")),
|
||
|
('can_support_application', _("Can support applications")),
|
||
|
('can_manage_application', _("Can manage applications")),
|
||
|
)
|
||
|
|
||
|
|
||
|
class AVersion(models.Model):
|
||
|
title = models.CharField(max_length=100)
|
||
|
text = models.TextField()
|
||
|
reason = models.TextField(null=True, blank=True)
|
||
|
abjected = models.BooleanField()
|
||
|
time = models.DateTimeField(auto_now=True)
|
||
|
application = models.ForeignKey(Application)
|
||
|
|
||
|
def __unicode__(self):
|
||
|
return "%s %s" % (self.id, self.title)
|
||
|
|
||
|
@property
|
||
|
def aid(self):
|
||
|
try:
|
||
|
return self._aid
|
||
|
except AttributeError:
|
||
|
self._aid = AVersion.objects \
|
||
|
.filter(application=self.application) \
|
||
|
.filter(id__lte=self.id).count()
|
||
|
return self._aid
|