OpenSlides/openslides/application/models.py

403 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):
"""
Generates a poll object for the application
"""
from poll.models import Poll
poll = Poll(optiondecision=True, \
application=self)
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