diff options
Diffstat (limited to 'ishtar_common/models.py')
-rw-r--r-- | ishtar_common/models.py | 260 |
1 files changed, 164 insertions, 96 deletions
diff --git a/ishtar_common/models.py b/ishtar_common/models.py index 7f79df72f..01b46b7ae 100644 --- a/ishtar_common/models.py +++ b/ishtar_common/models.py @@ -30,9 +30,11 @@ import logging import os from os.path import isfile, join import re +from secretary import Renderer as SecretaryRenderer import shutil from subprocess import Popen, PIPE import tempfile +import time import unicodecsv import zipfile @@ -57,38 +59,29 @@ 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 -from ishtar_common.ooo_replace import ooo_replace from ishtar_common.model_merging import merge_model_objects from ishtar_common.utils import get_cache, disable_for_loaddata from ishtar_common.data_importer import Importer, ImportFormater, \ IntegerFormater, FloatFormater, UnicodeFormater, DateFormater, \ TypeFormater, YearFormater, StrToBoolean, FileFormater - 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) @@ -132,7 +125,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: @@ -158,6 +151,15 @@ class ValueGetter(object): values[prefix + field_name] = value for extra_field in self.GET_VALUES_EXTRA: values[prefix + extra_field] = getattr(self, extra_field) or '' + for key in values.keys(): + val = values[key] + if val is None: + val = '' + else: + val = unicode(val) + if val.endswith('.None'): + val = '' + values[key] = val values['KEYS'] = u'\n'.join(values.keys()) value_list = [] for key in values.keys(): @@ -187,7 +189,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 = {} @@ -197,7 +199,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 @@ -210,6 +213,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]: @@ -253,7 +258,7 @@ def is_unique(cls, field): return func -class OwnPerms: +class OwnPerms(object): """ Manage special permissions for object's owner """ @@ -315,13 +320,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()) @@ -774,7 +779,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")) @@ -869,6 +874,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( @@ -1163,16 +1190,16 @@ 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_index = models.CharField( @@ -1182,16 +1209,16 @@ class IshtarSiteProfile(models.Model, Cached): u"only if there is no find in the database")) 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, @@ -1200,57 +1227,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 " @@ -1584,7 +1611,50 @@ class DocumentTemplate(models.Model): datetime.date.today().strftime('%Y-%m-%d') +\ u"." + self.template.name.split('.')[-1] values = c_object.get_values() + engine = SecretaryRenderer() + result = engine.render(self.template, **values) + output = open(output_name, 'wb') + output.write(result) + return output_name + + def convert_from_v1(self): + """ + Convert the current template from v1 to v2. + """ + from old.ooo_replace import ooo_replace + from archaeological_operations.models import AdministrativeAct + + old_dir = settings.MEDIA_ROOT + "/upload/templates/v1/" + if not os.path.exists(old_dir): + os.makedirs(old_dir) + shutil.copy(settings.MEDIA_ROOT + self.template.name, old_dir) + + tempdir = tempfile.mkdtemp("-ishtardocs") + output_name = tempdir + os.path.sep + self.template.name.split( + os.sep)[-1] + + objects = [] + filters = [ + {'operation__isnull': False}, + {'associated_file__isnull': False}, + {'treatment_file__isnull': False}, + {'treatment__isnull': False}, + ] + for filtr in filters: + q = AdministrativeAct.objects.filter(**filtr) + if q.count(): + objects.append(q.all()[0]) + + if not objects: + return + values = {} + for obj in objects: + values.update(obj.get_values()) + for key in values: + values[key] = "{{ " + key + " }}" + ooo_replace(self.template, output_name, values) + shutil.move(output_name, settings.MEDIA_ROOT + self.template.name) return output_name @@ -1704,10 +1774,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. @@ -1846,13 +1914,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 \";\")"), @@ -2366,7 +2434,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/", @@ -2378,7 +2446,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') @@ -2572,13 +2640,11 @@ class Organization(Address, Merge, OwnPerms, ValueGetter): verbose_name = _(u"Organization") verbose_name_plural = _(u"Organizations") permissions = ( - ("view_organization", ugettext(u"Can view all Organizations")), - ("view_own_organization", ugettext(u"Can view own Organization")), - ("add_own_organization", ugettext(u"Can add own Organization")), - ("change_own_organization", - ugettext(u"Can change own Organization")), - ("delete_own_organization", - ugettext(u"Can delete own Organization")), + ("view_organization", u"Can view all Organizations"), + ("view_own_organization", u"Can view own Organization"), + ("add_own_organization", u"Can add own Organization"), + ("change_own_organization", u"Can change own Organization"), + ("delete_own_organization", u"Can delete own Organization"), ) def simple_lbl(self): @@ -2614,7 +2680,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") @@ -2689,11 +2755,11 @@ class Person(Address, Merge, OwnPerms, ValueGetter): verbose_name = _(u"Person") verbose_name_plural = _(u"Persons") permissions = ( - ("view_person", ugettext(u"Can view all Persons")), - ("view_own_person", ugettext(u"Can view own Person")), - ("add_own_person", ugettext(u"Can add own Person")), - ("change_own_person", ugettext(u"Can change own Person")), - ("delete_own_person", ugettext(u"Can delete own Person")), + ("view_person", u"Can view all Persons"), + ("view_own_person", u"Can view own Person"), + ("add_own_person", u"Can add own Person"), + ("change_own_person", u"Can change own Person"), + ("delete_own_person", u"Can delete own Person"), ) @property @@ -2781,21 +2847,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) @@ -2862,7 +2928,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') @@ -2879,8 +2945,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) @@ -2889,6 +2957,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 @@ -2897,13 +2979,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): @@ -2914,20 +2990,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) @@ -2950,16 +3023,11 @@ class Author(models.Model): verbose_name_plural = _(u"Authors") ordering = ('author_type__order', 'person__name') permissions = ( - ("view_author", - ugettext(u"Can view all Authors")), - ("view_own_author", - ugettext(u"Can view own Author")), - ("add_own_author", - ugettext(u"Can add own Author")), - ("change_own_author", - ugettext(u"Can change own Author")), - ("delete_own_author", - ugettext(u"Can delete own Author")), + ("view_author", u"Can view all Authors"), + ("view_own_author", u"Can view own Author"), + ("add_own_author", u"Can add own Author"), + ("change_own_author", u"Can change own Author"), + ("delete_own_author", u"Can delete own Author"), ) def __unicode__(self): @@ -3017,7 +3085,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")) @@ -3156,7 +3224,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: |