summaryrefslogtreecommitdiff
path: root/ishtar_common/models.py
diff options
context:
space:
mode:
Diffstat (limited to 'ishtar_common/models.py')
-rw-r--r--ishtar_common/models.py168
1 files changed, 96 insertions, 72 deletions
diff --git a/ishtar_common/models.py b/ishtar_common/models.py
index f1de8c60a..5912c8377 100644
--- a/ishtar_common/models.py
+++ b/ishtar_common/models.py
@@ -31,6 +31,7 @@ import os
import re
import shutil
import tempfile
+import time
import unicodecsv
import zipfile
@@ -55,7 +56,7 @@ from django.template.defaultfilters import slugify
from django.contrib.auth.models import User, Group
from django.contrib.contenttypes.models import ContentType
-from django.contrib.contenttypes import generic
+from django.contrib.contenttypes.fields import GenericForeignKey
from django.contrib.gis.db import models
from simple_history.models import HistoricalRecords as BaseHistoricalRecords
@@ -74,19 +75,12 @@ logger = logging.getLogger(__name__)
def post_save_user(sender, **kwargs):
user = kwargs['instance']
- try:
- q = IshtarUser.objects.filter(username=user.username)
- if not q.count():
- ishtaruser = IshtarUser.create_from_user(user)
- else:
- ishtaruser = q.all()[0]
- administrator, created = PersonType.objects.get_or_create(
- txt_idx='administrator')
- if ishtaruser.is_superuser \
- and not ishtaruser.has_right('administrator'):
- ishtaruser.person.person_types.add(administrator)
- except DatabaseError: # manage when db is not synced
- pass
+ if kwargs["created"]:
+ try:
+ IshtarUser.create_from_user(user)
+ except DatabaseError: # manage when db is not synced
+ pass
+ IshtarUser.set_superuser(user)
post_save.connect(post_save_user, sender=User)
@@ -130,7 +124,7 @@ def check_model_access_control(request, model, available_perms=None):
class Imported(models.Model):
imports = models.ManyToManyField(
- 'Import', blank=True, null=True,
+ 'Import', blank=True,
related_name="imported_%(app_label)s_%(class)s")
class Meta:
@@ -185,7 +179,7 @@ class HistoricalRecords(BaseHistoricalRecords):
history_modifier = getattr(instance, 'history_modifier', None)
assert history_modifier
except (User.DoesNotExist, AssertionError):
- # on batch removing of users, user could have disapeared
+ # on batch removing of users, user could have disappeared
return
manager = getattr(instance, self.manager_name)
attrs = {}
@@ -195,7 +189,8 @@ class HistoricalRecords(BaseHistoricalRecords):
.filter(history_modifier_id=history_modifier.pk)\
.order_by('-history_date', '-history_id')
if not q_history.count():
- manager.create(history_type=type, **attrs)
+ manager.create(history_type=type,
+ history_date=datetime.datetime.now(), **attrs)
return
old_instance = q_history.all()[0]
# multiple saving by the same user in a very short time are generaly
@@ -208,6 +203,8 @@ class HistoricalRecords(BaseHistoricalRecords):
if q.count():
return
+ if 'history_date' not in attrs or not attrs['history_date']:
+ attrs['history_date'] = datetime.datetime.now()
# record a new version only if data have been changed
for field in instance._meta.fields:
if getattr(old_instance, field.attname) != attrs[field.attname]:
@@ -251,7 +248,7 @@ def is_unique(cls, field):
return func
-class OwnPerms:
+class OwnPerms(object):
"""
Manage special permissions for object's owner
"""
@@ -313,13 +310,13 @@ class OwnPerms:
"""
Get Own items
"""
- if isinstance(user, User):
- user = IshtarUser.objects.get(user_ptr=user)
- if user.is_anonymous():
+ if hasattr(user, 'is_authenticated') and not user.is_authenticated():
returned = cls.objects.filter(pk__isnull=True)
if values:
returned = []
return returned
+ if isinstance(user, User):
+ user = IshtarUser.objects.get(user_ptr=user)
items = []
if hasattr(cls, 'BASKET_MODEL'):
items = list(cls.BASKET_MODEL.objects.filter(user=user).all())
@@ -772,7 +769,7 @@ class ItemKey(models.Model):
key = models.CharField(_(u"Key"), max_length=100)
content_type = models.ForeignKey(ContentType)
object_id = models.PositiveIntegerField()
- content_object = generic.GenericForeignKey('content_type', 'object_id')
+ content_object = GenericForeignKey('content_type', 'object_id')
importer = models.ForeignKey(
'Import', null=True, blank=True,
help_text=_(u"Specific key to an import"))
@@ -867,6 +864,28 @@ class HistoryError(Exception):
PRIVATE_FIELDS = ('id', 'history_modifier', 'order')
+class BulkUpdatedItem(object):
+ @classmethod
+ def bulk_recursion(cls, transaction_id, extra_args):
+ """
+ Prevent infinite recursion. Should not happen but wrong manipulation
+ in the database or messy imports can generate circular relations
+
+ :param transaction_id: current transaction ID (unix time) - if null
+ a transaction ID is generated
+ :param extra_args: arguments dealing with
+ :return: (transaction ID, is a recursion)
+ """
+ if not transaction_id:
+ transaction_id = unicode(time.time())
+ args = ['cached_label_bulk_update', transaction_id] + extra_args
+ key, val = get_cache(cls, args)
+ if val:
+ return transaction_id, True
+ cache.set(key, 1, settings.CACHE_SMALLTIMEOUT)
+ return transaction_id, False
+
+
class BaseHistorizedItem(Imported):
IS_BASKET = False
history_modifier = models.ForeignKey(
@@ -1159,30 +1178,30 @@ class IshtarSiteProfile(models.Model, Cached):
description = models.TextField(_(u"Description"), null=True, blank=True)
base_color = models.CharField(
_(u"CSS color code for base module"),
- default='rgba(0, 0, 0, 0)', max_length=200)
+ default=u'rgba(0, 0, 0, 0)', max_length=200)
files = models.BooleanField(_(u"Files module"), default=False)
files_color = models.CharField(
_(u"CSS color code for files module"),
- default='rgba(0, 32, 210, 0.1)', max_length=200)
+ default=u'rgba(0, 32, 210, 0.1)', max_length=200)
context_record = models.BooleanField(_(u"Context records module"),
default=False)
context_record_color = models.CharField(
_(u"CSS color code for context record module"),
- default='rgba(210,200,0,0.2)', max_length=200)
+ default=u'rgba(210,200,0,0.2)', max_length=200)
find = models.BooleanField(_(u"Finds module"), default=False,
help_text=_(u"Need context records module"))
find_color = models.CharField(
_(u"CSS color code for find module"),
- default='rgba(210,0,0,0.15)', max_length=200)
+ default=u'rgba(210,0,0,0.15)', max_length=200)
warehouse = models.BooleanField(
_(u"Warehouses module"), default=False,
help_text=_(u"Need finds module"))
warehouse_color = models.CharField(
- _(u"CSS code for warehouse module"), default='rgba(10,20,200,0.15)',
+ _(u"CSS code for warehouse module"), default=u'rgba(10,20,200,0.15)',
max_length=200)
mapping = models.BooleanField(_(u"Mapping module"), default=False)
mapping_color = models.CharField(
- _(u"CSS code for mapping module"), default='rgba(72, 236, 0, 0.15)',
+ _(u"CSS code for mapping module"), default=u'rgba(72, 236, 0, 0.15)',
max_length=200)
homepage = models.TextField(
_(u"Home page"), null=True, blank=True,
@@ -1191,57 +1210,57 @@ class IshtarSiteProfile(models.Model, Cached):
u"can be used to display a random image."))
file_external_id = models.TextField(
_(u"File external id"),
- default="{year}-{numeric_reference}",
+ default=u"{year}-{numeric_reference}",
help_text=_(u"Formula to manage file external ID. "
u"Change this with care. With incorrect formula, the "
u"application might be unusable and import of external "
u"data can be destructive."))
parcel_external_id = models.TextField(
_(u"Parcel external id"),
- default="{associated_file__external_id}{operation__code_patriarche}-"
- "{town__numero_insee}-{section}{parcel_number}",
+ default=u"{associated_file__external_id}{operation__code_patriarche}-"
+ u"{town__numero_insee}-{section}{parcel_number}",
help_text=_(u"Formula to manage parcel external ID. "
u"Change this with care. With incorrect formula, the "
u"application might be unusable and import of external "
u"data can be destructive."))
context_record_external_id = models.TextField(
_(u"Context record external id"),
- default="{parcel__external_id}-{label}",
+ default=u"{parcel__external_id}-{label}",
help_text=_(u"Formula to manage context record external ID. "
u"Change this with care. With incorrect formula, the "
u"application might be unusable and import of external "
u"data can be destructive."))
base_find_external_id = models.TextField(
_(u"Base find external id"),
- default="{context_record__external_id}-{label}",
+ default=u"{context_record__external_id}-{label}",
help_text=_(u"Formula to manage base find external ID. "
u"Change this with care. With incorrect formula, the "
u"application might be unusable and import of external "
u"data can be destructive."))
find_external_id = models.TextField(
_(u"Find external id"),
- default="{get_first_base_find__context_record__external_id}-{label}",
+ default=u"{get_first_base_find__context_record__external_id}-{label}",
help_text=_(u"Formula to manage find external ID. "
u"Change this with care. With incorrect formula, the "
u"application might be unusable and import of external "
u"data can be destructive."))
container_external_id = models.TextField(
_(u"Container external id"),
- default="{responsible__external_id}-{index}",
+ default=u"{responsible__external_id}-{index}",
help_text=_(u"Formula to manage container external ID. "
u"Change this with care. With incorrect formula, the "
u"application might be unusable and import of external "
u"data can be destructive."))
warehouse_external_id = models.TextField(
_(u"Warehouse external id"),
- default="{name|slug}",
+ default=u"{name|slug}",
help_text=_(u"Formula to manage warehouse external ID. "
u"Change this with care. With incorrect formula, the "
u"application might be unusable and import of external "
u"data can be destructive."))
person_raw_name = models.TextField(
_(u"Raw name for person"),
- default="{name|upper} {surname}",
+ default=u"{name|upper} {surname}",
help_text=_(u"Formula to manage person raw_name. "
u"Change this with care. With incorrect formula, the "
u"application might be unusable and import of external "
@@ -1695,10 +1714,8 @@ class Address(BaseHistorizedItem):
class Merge(models.Model):
merge_key = models.TextField(_("Merge key"), blank=True, null=True)
- merge_candidate = models.ManyToManyField("self",
- blank=True, null=True)
- merge_exclusion = models.ManyToManyField("self",
- blank=True, null=True)
+ merge_candidate = models.ManyToManyField("self", blank=True)
+ merge_exclusion = models.ManyToManyField("self", blank=True)
archived = models.NullBooleanField(default=False,
blank=True, null=True)
# 1 for one word similarity, 2 for two word similarity, etc.
@@ -1837,13 +1854,13 @@ class ImporterType(models.Model):
description = models.CharField(_(u"Description"), blank=True, null=True,
max_length=500)
users = models.ManyToManyField('IshtarUser', verbose_name=_(u"Users"),
- blank=True, null=True)
+ blank=True)
associated_models = models.ForeignKey(
ImporterModel, verbose_name=_(u"Associated model"),
related_name='+', blank=True, null=True)
created_models = models.ManyToManyField(
ImporterModel, verbose_name=_(u"Models that can accept new items"),
- blank=True, null=True, help_text=_(u"Leave blank for no restrictions"),
+ blank=True, help_text=_(u"Leave blank for no restrictions"),
related_name='+')
is_template = models.BooleanField(_(u"Is template"), default=False)
unicity_keys = models.CharField(_(u"Unicity keys (separator \";\")"),
@@ -2355,7 +2372,7 @@ class Import(models.Model):
_(u"Associated images (zip file)"), upload_to="upload/imports/",
blank=True, null=True, max_length=220)
encoding = models.CharField(_(u"Encoding"), choices=ENCODINGS,
- default='utf-8', max_length=15)
+ default=u'utf-8', max_length=15)
skip_lines = models.IntegerField(_(u"Skip lines"), default=1)
error_file = models.FileField(_(u"Error file"),
upload_to="upload/imports/",
@@ -2367,7 +2384,7 @@ class Import(models.Model):
upload_to="upload/imports/",
blank=True, null=True, max_length=255)
state = models.CharField(_(u"State"), max_length=2, choices=IMPORT_STATE,
- default='C')
+ default=u'C')
conservative_import = models.BooleanField(
_(u"Conservative import"), default=False,
help_text='If set to true, do not overload existing values')
@@ -2603,7 +2620,7 @@ class Organization(Address, Merge, OwnPerms, ValueGetter):
class PersonType(GeneralType):
# rights = models.ManyToManyField(WizardStep, verbose_name=_(u"Rights"))
groups = models.ManyToManyField(Group, verbose_name=_(u"Groups"),
- blank=True, null=True)
+ blank=True)
class Meta:
verbose_name = _(u"Person type")
@@ -2770,21 +2787,21 @@ class Person(Address, Merge, OwnPerms, ValueGetter):
txt_idx__in=right_name).count()) or \
bool(self.person_types.filter(
groups__permissions__codename__in=right_name).count()) or\
- bool(self.ishtaruser.filter(
- groups__permissions__codename__in=right_name
+ bool(self.ishtaruser.user_ptr.groups.filter(
+ permissions__codename__in=right_name
).count()) or\
- bool(self.ishtaruser.filter(
- user_permissions__codename__in=right_name).count())
+ bool(self.ishtaruser.user_ptr.user_permissions.filter(
+ codename__in=right_name).count())
# or self.person_types.filter(wizard__url_name__in=right_name).count())
else:
res = bool(self.person_types.filter(txt_idx=right_name).count()) or \
bool(self.person_types.filter(
groups__permissions__codename=right_name).count()) or \
- bool(self.ishtaruser.filter(
- groups__permissions__codename__in=[right_name]
- ).count()) or\
- bool(self.ishtaruser.filter(
- user_permissions__codename__in=[right_name]).count())
+ bool(self.ishtaruser.user_ptr.groups.filter(
+ permissions__codename__in=[right_name]
+ ).count()) or \
+ bool(self.ishtaruser.user_ptr.user_permissions.filter(
+ codename__in=[right_name]).count())
# or self.person_types.filter(wizard__url_name=right_name).count())
if session:
cache.set(cache_key, res, settings.CACHE_SMALLTIMEOUT)
@@ -2851,7 +2868,7 @@ class Person(Address, Merge, OwnPerms, ValueGetter):
=user.ishtaruser)
-class IshtarUser(User):
+class IshtarUser(models.Model):
TABLE_COLS = ('username', 'person__name', 'person__surname',
'person__email', 'person__person_types_list',
'person__attached_to')
@@ -2868,8 +2885,10 @@ class IshtarUser(User):
}
# fields
- person = models.ForeignKey(Person, verbose_name=_(u"Person"), unique=True,
- related_name='ishtaruser')
+ user_ptr = models.OneToOneField(User, primary_key=True,
+ related_name='ishtaruser')
+ person = models.OneToOneField(Person, verbose_name=_(u"Person"),
+ related_name='ishtaruser')
advanced_shortcut_menu = models.BooleanField(
_(u"Advanced shortcut menu"), default=False)
@@ -2878,6 +2897,20 @@ class IshtarUser(User):
verbose_name_plural = _(u"Ishtar users")
@classmethod
+ def set_superuser(cls, user):
+ q = cls.objects.filter(user_ptr=user)
+ if not q.count():
+ return
+ ishtaruser = q.all()[0]
+ admin, created = PersonType.objects.get_or_create(
+ txt_idx='administrator')
+ person = ishtaruser.person
+ if user.is_superuser:
+ person.person_types.add(admin)
+ elif admin in person.person_types.all():
+ person.person_types.remove(admin)
+
+ @classmethod
def create_from_user(cls, user):
default = user.username
surname = user.first_name or default
@@ -2886,13 +2919,7 @@ class IshtarUser(User):
person = Person.objects.create(surname=surname,
name=name, email=email,
history_modifier=user)
- if user.is_superuser:
- person_type, created = PersonType.objects.get_or_create(
- txt_idx='administrator')
- person.person_types.add(person_type)
- password = user.password
- isht_user = IshtarUser.objects.create(
- user_ptr=user, username=default, person=person, password=password)
+ isht_user = cls.objects.create(user_ptr=user, person=person)
return isht_user
def has_right(self, right_name, session=None):
@@ -2903,20 +2930,17 @@ class IshtarUser(User):
def has_perm(self, perm, model=None, session=None, obj=None):
if not session:
- return super(IshtarUser, self).has_perm(perm, model)
+ return self.user_ptr.has_perm(perm, model)
cache_key = 'usersession-{}-{}-{}-{}'.format(
session.session_key, perm, model.__name__ if model else 'no',
obj.pk if obj else 'no')
res = cache.get(cache_key)
if res in (True, False):
return res
- res = super(IshtarUser, self).has_perm(perm, model)
+ res = self.user_ptr.has_perm(perm, model)
cache.set(cache_key, res, settings.CACHE_SMALLTIMEOUT)
return res
-IshtarUser._meta.get_field('password').help_text = _(
- u"To modify the password use the form in Auth > User")
-
class AuthorType(GeneralType):
order = models.IntegerField(_(u"Order"), default=1)
@@ -3006,7 +3030,7 @@ class Source(OwnPerms, ImageModel, models.Model):
authors = models.ManyToManyField(Author, verbose_name=_(u"Authors"),
related_name="%(class)s_related")
associated_url = models.URLField(
- verify_exists=False, blank=True, null=True,
+ blank=True, null=True,
verbose_name=_(u"Numerical ressource (web address)"))
receipt_date = models.DateField(blank=True, null=True,
verbose_name=_(u"Receipt date"))
@@ -3145,7 +3169,7 @@ post_delete.connect(post_save_cache, sender=OperationType)
class SpatialReferenceSystem(GeneralType):
order = models.IntegerField(_(u"Order"), default=10)
auth_name = models.CharField(
- _(u"Authority name"), default='EPSG', max_length=256)
+ _(u"Authority name"), default=u'EPSG', max_length=256)
srid = models.IntegerField(_(u"Authority SRID"))
class Meta: