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.py1235
1 files changed, 290 insertions, 945 deletions
diff --git a/ishtar_common/models.py b/ishtar_common/models.py
index 7f79df72f..0c06f0b4d 100644
--- a/ishtar_common/models.py
+++ b/ishtar_common/models.py
@@ -22,53 +22,54 @@ Models description
"""
from cStringIO import StringIO
import copy
-import csv
import datetime
from PIL import Image
-from importlib import import_module
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 unicodecsv
-import zipfile
+import time
from django.conf import settings
from django.core.cache import cache
-from django.core.exceptions import ObjectDoesNotExist, ValidationError, \
- SuspiciousOperation
-from django.core.files import File
+from django.core.exceptions import ObjectDoesNotExist, ValidationError
from django.core.files.uploadedfile import SimpleUploadedFile
from django.core.validators import validate_slug
from django.core.urlresolvers import reverse, NoReverseMatch
from django.db.utils import DatabaseError
from django.db.models import Q, Max, Count
-from django.db.models.base import ModelBase
-from django.db.models.signals import post_save, pre_delete, post_delete
+from django.db.models.signals import post_save, post_delete
from django.utils.functional import lazy
-from django.utils.translation import ugettext_lazy as _, ugettext, \
- pgettext_lazy
+from django.utils.translation import ugettext_lazy as _
from django.utils.safestring import SafeUnicode, mark_safe
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
-
+from ishtar_common.utils import get_cache, disable_for_loaddata, create_slug,\
+ get_all_field_names
+
+from ishtar_common.models_imports import ImporterModel, ImporterType, \
+ ImporterDefault, ImporterDefaultValues, ImporterColumn, \
+ ImporterDuplicateField, Regexp, ImportTarget, TargetKey, FormaterType, \
+ Import
+
+__all__ = [
+ 'ImporterModel', 'ImporterType', 'ImporterDefault', 'ImporterDefaultValues',
+ 'ImporterColumn', 'ImporterDuplicateField', 'Regexp', 'ImportTarget',
+ 'TargetKey', 'FormaterType', 'Import'
+]
logger = logging.getLogger(__name__)
@@ -76,19 +77,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,15 +124,6 @@ def check_model_access_control(request, model, available_perms=None):
return allowed, own
-class Imported(models.Model):
- imports = models.ManyToManyField(
- 'Import', blank=True, null=True,
- related_name="imported_%(app_label)s_%(class)s")
-
- class Meta:
- abstract = True
-
-
class ValueGetter(object):
_prefix = ""
GET_VALUES_EXTRA = []
@@ -148,7 +133,7 @@ class ValueGetter(object):
if not prefix:
prefix = self._prefix
values = {}
- for field_name in self._meta.get_all_field_names():
+ for field_name in get_all_field_names(self):
if not hasattr(self, field_name):
continue
value = getattr(self, field_name)
@@ -158,6 +143,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():
@@ -176,7 +170,7 @@ class ValueGetter(object):
if not prefix:
prefix = cls._prefix
values = {}
- for field_name in cls._meta.get_all_field_names():
+ for field_name in get_all_field_names(cls):
values[prefix + field_name] = ''
return values
@@ -187,7 +181,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 +191,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 +205,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 +250,7 @@ def is_unique(cls, field):
return func
-class OwnPerms:
+class OwnPerms(object):
"""
Manage special permissions for object's owner
"""
@@ -315,13 +312,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())
@@ -410,6 +407,16 @@ def post_save_cache(sender, **kwargs):
sender.refresh_cache()
+class SlugModelManager(models.Manager):
+ def get_by_natural_key(self, slug):
+ return self.get(slug=slug)
+
+
+class TypeManager(models.Manager):
+ def get_by_natural_key(self, txt_idx):
+ return self.get(txt_idx=txt_idx)
+
+
class GeneralType(Cached, models.Model):
"""
Abstract class for "types"
@@ -417,18 +424,25 @@ class GeneralType(Cached, models.Model):
label = models.CharField(_(u"Label"), max_length=100)
txt_idx = models.CharField(
_(u"Textual ID"), validators=[validate_slug], max_length=100,
- unique=True)
+ unique=True,
+ help_text=_(
+ u"The slug is the standardized version of the name. It contains "
+ u"only lowercase letters, numbers and hyphens. Each slug must "
+ u"be unique."))
comment = models.TextField(_(u"Comment"), blank=True, null=True)
available = models.BooleanField(_(u"Available"), default=True)
HELP_TEXT = u""
+ objects = TypeManager()
class Meta:
abstract = True
- unique_together = (('txt_idx', 'available'),)
def __unicode__(self):
return self.label
+ def natural_key(self):
+ return (self.txt_idx, )
+
@classmethod
def create_default_for_test(cls):
return [cls.objects.create(label='Test %d' % i) for i in range(5)]
@@ -738,55 +752,27 @@ class GeneralType(Cached, models.Model):
item.generate_key()
-class Basket(models.Model):
- """
- Abstract class for a basket
- Subclass must be defined with an "items" ManyToManyField
- """
- IS_BASKET = True
- label = models.CharField(_(u"Label"), max_length=1000)
- comment = models.TextField(_(u"Comment"), blank=True, null=True)
- user = models.ForeignKey('IshtarUser', blank=True, null=True)
- available = models.BooleanField(_(u"Available"), default=True)
-
- class Meta:
- abstract = True
- unique_together = (('label', 'user'),)
-
- def __unicode__(self):
- return self.label
-
- @property
- def cached_label(self):
- return unicode(self)
-
- @classmethod
- def get_short_menu_class(cls, pk):
- return 'basket'
-
- @property
- def associated_filename(self):
- return "{}-{}".format(datetime.date.today().strftime(
- "%Y-%m-%d"), slugify(self.label))
-
-
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,
+ Import, null=True, blank=True,
help_text=_(u"Specific key to an import"))
def __unicode__(self):
return self.key
+def get_image_path(instance, filename):
+ return instance._get_image_path(filename)
+
+
class ImageModel(models.Model):
- image = models.ImageField(upload_to="upload/", blank=True, null=True,
+ image = models.ImageField(upload_to=get_image_path, blank=True, null=True,
max_length=255)
- thumbnail = models.ImageField(upload_to='upload/thumbs/', blank=True,
+ thumbnail = models.ImageField(upload_to=get_image_path, blank=True,
null=True, max_length=255)
IMAGE_MAX_SIZE = settings.IMAGE_MAX_SIZE
THUMB_MAX_SIZE = settings.THUMB_MAX_SIZE
@@ -795,15 +781,11 @@ class ImageModel(models.Model):
class Meta:
abstract = True
- def __init__(self, *args, **kwargs):
- super(ImageModel, self).__init__(*args, **kwargs)
- image = self._meta.get_field_by_name("image")[0]
- IMAGE_PREFIX = self.IMAGE_PREFIX
- if not IMAGE_PREFIX.endswith('/'):
- IMAGE_PREFIX += u'/'
- image.upload_to = IMAGE_PREFIX
- thumbnail = self._meta.get_field_by_name("thumbnail")[0]
- thumbnail.upload_to = IMAGE_PREFIX + "thumbs/"
+ def _get_image_path(self, filename):
+ return u"{}/{}".format(self._get_base_image_path(), filename)
+
+ def _get_base_image_path(self):
+ return u"upload"
def has_changed(self, field):
if not self.pk:
@@ -850,8 +832,12 @@ class ImageModel(models.Model):
pass
# save the thumbnail
+ splited = filename.split('.')
+ thumb_filename = u"{}-thumb.{}".format(
+ u".".join(splited[:-1]), splited[-1]
+ )
self.thumbnail.save(
- '_%s' % filename,
+ thumb_filename,
self.create_thumb(image, self.THUMB_MAX_SIZE),
save=False)
except IOError:
@@ -869,6 +855,37 @@ 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 Imported(models.Model):
+ imports = models.ManyToManyField(
+ Import, blank=True,
+ related_name="imported_%(app_label)s_%(class)s")
+
+ class Meta:
+ abstract = True
+
+
class BaseHistorizedItem(Imported):
IS_BASKET = False
history_modifier = models.ForeignKey(
@@ -916,8 +933,8 @@ class BaseHistorizedItem(Imported):
item._next = None
item.history_date = historized[step].history_date
model = self.__class__
- for k in model._meta.get_all_field_names():
- field = model._meta.get_field_by_name(k)[0]
+ for k in get_all_field_names(model):
+ field = model._meta.get_field(k)
if hasattr(field, 'rel') and field.rel:
if not hasattr(item, k + '_id'):
setattr(item, k, getattr(self, k))
@@ -941,14 +958,14 @@ class BaseHistorizedItem(Imported):
def last_edition_date(self):
try:
return self.history.order_by('-history_date').all()[0].history_date
- except IndexError:
+ except (AttributeError, IndexError):
return
@property
def history_creation_date(self):
try:
return self.history.order_by('history_date').all()[0].history_date
- except IndexError:
+ except (AttributeError, IndexError):
return
def rollback(self, date):
@@ -1022,10 +1039,9 @@ class GeneralRelationType(GeneralType):
symmetrical = models.BooleanField(_(u"Symmetrical"))
tiny_label = models.CharField(_(u"Tiny label"), max_length=50,
blank=True, null=True)
- # # an inverse must be set
- # inverse_relation = models.ForeignKey(
- # 'RelationType', verbose_name=_(u"Inverse relation"), blank=True,
- # null=True)
+ inverse_relation = models.ForeignKey(
+ 'self', verbose_name=_(u"Inverse relation"), blank=True,
+ null=True)
class Meta:
abstract = True
@@ -1163,16 +1179,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 +1198,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,64 +1216,64 @@ 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 "
u"data can be destructive."))
active = models.BooleanField(_(u"Current active"), default=False)
currency = models.CharField(_(u"Currency"), default=u"€",
- choices=CURRENCY, max_length='5')
+ choices=CURRENCY, max_length=5)
class Meta:
verbose_name = _(u"Ishtar site profile")
@@ -1397,7 +1413,8 @@ class DashboardFormItem(object):
q = cls.objects.filter(**{date_var + '__isnull': False})
if fltr:
q = q.filter(**fltr)
- return q.filter(**{date_var + '__year': year}).distinct('pk')
+ return q.filter(
+ **{date_var + '__year': year}).order_by('pk').distinct('pk')
@classmethod
def get_by_month(cls, year, month, fltr={}, date_source='creation'):
@@ -1407,14 +1424,14 @@ class DashboardFormItem(object):
q = q.filter(**fltr)
q = q.filter(
**{date_var + '__year': year, date_var + '__month': month})
- return q.distinct('pk')
+ return q.order_by('pk').distinct('pk')
@classmethod
def get_total_number(cls, fltr={}):
q = cls.objects
if fltr:
q = q.filter(**fltr)
- return q.distinct('pk').count()
+ return q.order_by('pk').distinct('pk').count()
class Dashboard(object):
@@ -1555,10 +1572,14 @@ class DocumentTemplate(models.Model):
CLASSNAMES = (('archaeological_operations.models.AdministrativeAct',
_(u"Administrative Act")),)
name = models.CharField(_(u"Name"), max_length=100)
- template = models.FileField(_(u"Template"), upload_to="upload/templates/")
+ slug = models.SlugField(_(u"Slug"), blank=True, null=True, max_length=100,
+ unique=True)
+ template = models.FileField(
+ _(u"Template"), upload_to="templates/%Y/")
associated_object_name = models.CharField(
_(u"Associated object"), max_length=100, choices=CLASSNAMES)
available = models.BooleanField(_(u"Available"), default=True)
+ objects = SlugModelManager()
class Meta:
verbose_name = _(u"Document template")
@@ -1568,6 +1589,14 @@ class DocumentTemplate(models.Model):
def __unicode__(self):
return self.name
+ def natural_key(self):
+ return (self.slug, )
+
+ def save(self, *args, **kwargs):
+ if not self.slug:
+ self.slug = create_slug(DocumentTemplate, self.name)
+ return super(DocumentTemplate, self).save(*args, **kwargs)
+
@classmethod
def get_tuples(cls, dct={}, empty_first=True):
dct['available'] = True
@@ -1584,7 +1613,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 + "/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 +1776,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.
@@ -1785,772 +1855,6 @@ post_delete.connect(post_save_cache, sender=OrganizationType)
organization_type_pk_lazy = lazy(OrganizationType.get_or_create_pk, unicode)
organization_type_pks_lazy = lazy(OrganizationType.get_or_create_pks, unicode)
-IMPORTER_CLASSES = {}
-
-IMPORTER_CLASSES.update({
- 'sra-pdl-files':
- 'archaeological_files.data_importer.FileImporterSraPdL'})
-
-
-def get_model_fields(model):
- """
- Return a dict of fields from model
- To be replace in Django 1.8 with get_fields, get_field
- """
- fields = {}
- options = model._meta
- for field in sorted(options.fields + options.many_to_many):
- fields[field.name] = field
- if hasattr(model, 'get_extra_fields'):
- fields.update(model.get_extra_fields())
- return fields
-
-
-def import_class(full_path_classname):
- """
- Return the model class from the full path
- TODO: add a white list for more security
- """
- mods = full_path_classname.split('.')
- if len(mods) == 1:
- mods = ['ishtar_common', 'models', mods[0]]
- elif 'models' not in mods and 'models_finds' not in mods \
- and 'models_treatments' not in mods:
- raise SuspiciousOperation(
- u"Try to import a non model from a string")
- module = import_module('.'.join(mods[:-1]))
- return getattr(module, mods[-1])
-
-
-class ImporterModel(models.Model):
- name = models.CharField(_(u"Name"), max_length=200)
- klass = models.CharField(_(u"Class name"), max_length=200)
-
- class Meta:
- verbose_name = _(u"Importer - Model")
- verbose_name_plural = _(u"Importer - Models")
- ordering = ('name',)
-
- def __unicode__(self):
- return self.name
-
-
-class ImporterType(models.Model):
- """
- Description of a table to be mapped with ishtar database
- """
- name = models.CharField(_(u"Name"), blank=True, null=True,
- max_length=100)
- slug = models.SlugField(_(u"Slug"), unique=True, blank=True, null=True,
- max_length=100)
- description = models.CharField(_(u"Description"), blank=True, null=True,
- max_length=500)
- users = models.ManyToManyField('IshtarUser', verbose_name=_(u"Users"),
- blank=True, null=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"),
- related_name='+')
- is_template = models.BooleanField(_(u"Is template"), default=False)
- unicity_keys = models.CharField(_(u"Unicity keys (separator \";\")"),
- blank=True, null=True, max_length=500)
-
- class Meta:
- verbose_name = _(u"Importer - Type")
- verbose_name_plural = _(u"Importer - Types")
- ordering = ('name',)
-
- def __unicode__(self):
- return self.name
-
- def get_importer_class(self, import_instance=None):
- if self.slug and self.slug in IMPORTER_CLASSES:
- cls = import_class(IMPORTER_CLASSES[self.slug])
- return cls
- OBJECT_CLS = import_class(self.associated_models.klass)
- DEFAULTS = dict([(default.keys, default.values)
- for default in self.defaults.all()])
- LINE_FORMAT = []
- idx = 0
- for column in self.columns.order_by('col_number').all():
- idx += 1
- while column.col_number > idx:
- LINE_FORMAT.append(None)
- idx += 1
- targets = []
- formater_types = []
- nb = column.targets.count()
- if not nb:
- LINE_FORMAT.append(None)
- continue
- force_news = []
- concat_str = []
- for target in column.targets.all():
- ft = target.formater_type.get_formater_type(
- target, import_instance=import_instance)
- if not ft:
- continue
- formater_types.append(ft)
- targets.append(target.target)
- concat_str.append(target.concat_str)
- force_news.append(target.force_new)
- formater_kwargs = {}
- if column.regexp_pre_filter:
- formater_kwargs['regexp'] = re.compile(
- column.regexp_pre_filter.regexp)
- formater_kwargs['concat_str'] = concat_str
- formater_kwargs['duplicate_fields'] = [
- (field.field_name, field.force_new, field.concat,
- field.concat_str)
- for field in column.duplicate_fields.all()]
- formater_kwargs['label'] = column.label
- formater_kwargs['required'] = column.required
- formater_kwargs['force_new'] = force_news
- if column.export_field_name:
- formater_kwargs['export_field_name'] = [
- column.export_field_name]
- formater = ImportFormater(targets, formater_types,
- **formater_kwargs)
- LINE_FORMAT.append(formater)
- UNICITY_KEYS = []
- if self.unicity_keys:
- UNICITY_KEYS = [un.strip() for un in self.unicity_keys.split(';')]
- MODEL_CREATION_LIMIT = []
- for modls in self.created_models.all():
- MODEL_CREATION_LIMIT.append(import_class(modls.klass))
- args = {'OBJECT_CLS': OBJECT_CLS, 'DESC': self.description,
- 'DEFAULTS': DEFAULTS, 'LINE_FORMAT': LINE_FORMAT,
- 'UNICITY_KEYS': UNICITY_KEYS,
- 'MODEL_CREATION_LIMIT': MODEL_CREATION_LIMIT}
- name = str(''.join(
- x for x in slugify(self.name).replace('-', ' ').title()
- if not x.isspace()))
- newclass = type(name, (Importer,), args)
- return newclass
-
-
-def get_associated_model(parent_model, keys):
- model = None
- if isinstance(parent_model, unicode) or \
- isinstance(parent_model, str):
- OBJECT_CLS = import_class(parent_model)
- else:
- OBJECT_CLS = parent_model
- for idx, item in enumerate(keys):
- if not idx:
- field = get_model_fields(OBJECT_CLS)[item]
- if hasattr(field, 'rel') and hasattr(field.rel, 'to'):
- model = field.rel.to
- if type(field) == ModelBase:
- model = field
- else:
- return get_associated_model(model, keys[1:])
- return model
-
-
-class ImporterDefault(models.Model):
- """
- Targets of default values in an import
- """
- importer_type = models.ForeignKey(ImporterType, related_name='defaults')
- target = models.CharField(u"Target", max_length=500)
-
- class Meta:
- verbose_name = _(u"Importer - Default")
- verbose_name_plural = _(u"Importer - Defaults")
-
- def __unicode__(self):
- return u"{} - {}".format(self.importer_type, self.target)
-
- @property
- def keys(self):
- return tuple(self.target.split('__'))
-
- @property
- def associated_model(self):
- return get_associated_model(self.importer_type.associated_models.klass,
- self.keys)
-
- @property
- def values(self):
- values = {}
- for default_value in self.default_values.all():
- values[default_value.target] = default_value.get_value()
- return values
-
-
-class ImporterDefaultValues(models.Model):
- """
- Default values in an import
- """
- default_target = models.ForeignKey(ImporterDefault,
- related_name='default_values')
- target = models.CharField(u"Target", max_length=500)
- value = models.CharField(u"Value", max_length=500)
-
- def __unicode__(self):
- return u"{} - {}".format(self.default_target, self.target, self.value)
-
- class Meta:
- verbose_name = _(u"Importer - Default value")
- verbose_name_plural = _(u"Importer - Default values")
-
- def get_value(self):
- parent_model = self.default_target.associated_model
- if not parent_model:
- return self.value
- fields = get_model_fields(parent_model)
- target = self.target.strip()
- if target not in fields:
- return
- field = fields[target]
- if not hasattr(field, 'rel') or not hasattr(field.rel, 'to'):
- return
- model = field.rel.to
- # if value is an id
- try:
- return model.objects.get(pk=int(self.value))
- except (ValueError, model.DoesNotExist):
- pass
- # try with txt_idx
- try:
- return model.objects.get(txt_idx=self.value)
- except (ValueError, model.DoesNotExist):
- pass
- return ""
-
-
-class ImporterColumn(models.Model):
- """
- Import file column description
- """
- label = models.CharField(_(u"Label"), blank=True, null=True,
- max_length=200)
- importer_type = models.ForeignKey(ImporterType, related_name='columns')
- col_number = models.IntegerField(_(u"Column number"), default=1)
- description = models.TextField(_("Description"), blank=True, null=True)
- regexp_pre_filter = models.ForeignKey("Regexp", blank=True, null=True)
- required = models.BooleanField(_(u"Required"), default=False)
- export_field_name = models.CharField(
- _(u"Export field name"), blank=True, null=True, max_length=200,
- help_text=_(u"Fill this field if the field name is ambiguous for "
- u"export. For instance: concatenated fields.")
- )
-
- class Meta:
- verbose_name = _(u"Importer - Column")
- verbose_name_plural = _(u"Importer - Columns")
- ordering = ('importer_type', 'col_number')
- unique_together = ('importer_type', 'col_number')
-
- def __unicode__(self):
- return u"{} - {}".format(self.importer_type, self.col_number)
-
- def targets_lbl(self):
- return u', '.join([target.target for target in self.targets.all()])
-
- def duplicate_fields_lbl(self):
- return u', '.join([dp.field_name
- for dp in self.duplicate_fields.all()])
-
-
-class ImporterDuplicateField(models.Model):
- """
- Direct copy of result in other fields
- """
- column = models.ForeignKey(ImporterColumn, related_name='duplicate_fields')
- field_name = models.CharField(_(u"Field name"), blank=True, null=True,
- max_length=200)
- force_new = models.BooleanField(_(u"Force creation of new items"),
- default=False)
- concat = models.BooleanField(_(u"Concatenate with existing"),
- default=False)
- concat_str = models.CharField(_(u"Concatenate character"), max_length=5,
- blank=True, null=True)
-
- class Meta:
- verbose_name = _(u"Importer - Duplicate field")
- verbose_name_plural = _(u"Importer - Duplicate fields")
-
-
-class Regexp(models.Model):
- name = models.CharField(_(u"Name"), max_length=100)
- description = models.CharField(_(u"Description"), blank=True, null=True,
- max_length=500)
- regexp = models.CharField(_(u"Regular expression"), max_length=500)
-
- class Meta:
- verbose_name = _(u"Importer - Regular expression")
- verbose_name_plural = _(u"Importer - Regular expressions")
-
- def __unicode__(self):
- return self.name
-
-
-class ImportTarget(models.Model):
- """
- Ishtar database target for a column
- """
- column = models.ForeignKey(ImporterColumn, related_name='targets')
- target = models.CharField(u"Target", max_length=500)
- regexp_filter = models.ForeignKey("Regexp", blank=True, null=True)
- formater_type = models.ForeignKey("FormaterType")
- force_new = models.BooleanField(_(u"Force creation of new items"),
- default=False)
- concat = models.BooleanField(_(u"Concatenate with existing"),
- default=False)
- concat_str = models.CharField(_(u"Concatenate character"), max_length=5,
- blank=True, null=True)
- comment = models.TextField(_(u"Comment"), blank=True, null=True)
-
- class Meta:
- verbose_name = _(u"Importer - Target")
- verbose_name_plural = _(u"Importer - Targets")
-
- def __unicode__(self):
- return self.target[:50] if self.target else self.comment
-
- @property
- def associated_model(self):
- try:
- return get_associated_model(
- self.column.importer_type.associated_models.klass,
- self.target.split('__'))
- except KeyError:
- return
-
- def get_choices(self):
- if self.formater_type.formater_type == 'UnknowType' \
- and self.column.importer_type.slug:
- cls = self.column.importer_type.get_importer_class()
- formt = cls().line_format[self.column.col_number - 1]
- if hasattr(formt.formater, 'choices'):
- return [('', '--' * 8)] + list(formt.formater.choices)
- return [('', '--' * 8)]
- if self.formater_type.formater_type == 'StrToBoolean':
- return [('', '--' * 8),
- ('True', _(u"True")),
- ('False', _(u"False"))]
- if not self.associated_model or not hasattr(self.associated_model,
- 'get_types'):
- return []
- return self.associated_model.get_types()
-
-
-class TargetKey(models.Model):
- """
- User's link between import source and ishtar database.
- Also temporary used for GeneralType to point missing link before adding
- them in ItemKey table.
- A targetkey connection can be create to be applied to on particular
- import (associated_import), one particular user (associated_user) or to all
- imports (associated_import and associated_user are empty).
- """
- target = models.ForeignKey(ImportTarget, related_name='keys')
- key = models.TextField(_(u"Key"))
- value = models.TextField(_(u"Value"), blank=True, null=True)
- is_set = models.BooleanField(_(u"Is set"), default=False)
- associated_import = models.ForeignKey('Import', blank=True, null=True)
- associated_user = models.ForeignKey('IshtarUser', blank=True, null=True)
-
- class Meta:
- unique_together = ('target', 'key', 'associated_user',
- 'associated_import')
- verbose_name = _(u"Importer - Target key")
- verbose_name_plural = _(u"Importer - Targets keys")
-
- def __unicode__(self):
- return u" - ".join([unicode(self.target), self.key[:50]])
-
- def column_nb(self):
- # for the admin
- return self.target.column.col_number
-
- def importer_type(self):
- # for the admin
- return self.target.column.importer_type.name
-
- def format(self):
- if not self.is_set:
- return None
- if self.target.formater_type.formater_type == 'StrToBoolean':
- if self.value in ('False', '0'):
- return False
- elif self.value:
- return True
- return
- return self.value
-
- def save(self, *args, **kwargs):
- obj = super(TargetKey, self).save(*args, **kwargs)
- if not self.value:
- return obj
- associated_model = self.target.associated_model
- if associated_model and hasattr(self.target.associated_model,
- "add_key"):
- v = None
- # pk is given
- try:
- v = self.target.associated_model.objects.get(
- pk=unicode(int(self.value)))
- except (ValueError, self.target.associated_model.DoesNotExist):
- # try with txt_idx
- try:
- v = self.target.associated_model.objects.get(
- txt_idx=unicode(self.value))
- except self.target.associated_model.DoesNotExist:
- pass
- if v:
- v.add_key(self.key, importer=self.associated_import)
- return obj
-
-TARGET_MODELS = [
- ('OrganizationType', _(u"Organization type")),
- ('TitleType', _(u"Title")),
- ('SourceType', _(u"Source type")),
- ('AuthorType', _(u"Author type")),
- ('Format', _(u"Format")),
- ('archaeological_operations.models.OperationType', _(u"Operation type")),
- ('archaeological_operations.models.Period', _(u"Period")),
- ('archaeological_operations.models.ReportState', _(u"Report state")),
- ('archaeological_operations.models.RemainType', _(u"Remain type")),
- ('archaeological_context_records.models.Unit', _(u"Unit")),
- ('archaeological_context_records.models.ActivityType',
- _(u"Activity type")),
- ('archaeological_context_records.models.DocumentationType',
- _(u"Documentation type")),
- ('archaeological_finds.models.MaterialType', _(u"Material")),
- ('archaeological_finds.models.ConservatoryState',
- _(u"Conservatory state")),
- ('archaeological_warehouse.models.ContainerType', _(u"Container type")),
- ('archaeological_finds.models.PreservationType', _(u"Preservation type")),
- ('archaeological_finds.models.ObjectType', _(u"Object type")),
- ('archaeological_finds.models.IntegrityType', _(u"Integrity type")),
- ('archaeological_finds.models.RemarkabilityType',
- _(u"Remarkability type")),
- ('archaeological_finds.models.BatchType', _(u"Batch type")),
- ('archaeological_context_records.models.IdentificationType',
- _("Identification type")),
- ('archaeological_context_records.models.RelationType',
- _(u"Context record relation type")),
- ('SpatialReferenceSystem', _(u"Spatial reference system")),
- ('SupportType', _(u"Support type")),
- ('TitleType', _(u"Title type")),
-]
-
-TARGET_MODELS_KEYS = [tm[0] for tm in TARGET_MODELS]
-
-IMPORTER_TYPES = (
- ('IntegerFormater', _(u"Integer")),
- ('FloatFormater', _(u"Float")),
- ('UnicodeFormater', _(u"String")),
- ('DateFormater', _(u"Date")),
- ('TypeFormater', _(u"Type")),
- ('YearFormater', _(u"Year")),
- ('StrToBoolean', _(u"String to boolean")),
- ('FileFormater', pgettext_lazy("filesystem", u"File")),
- ('UnknowType', _(u"Unknow type"))
-)
-
-IMPORTER_TYPES_DCT = {
- 'IntegerFormater': IntegerFormater,
- 'FloatFormater': FloatFormater,
- 'UnicodeFormater': UnicodeFormater,
- 'DateFormater': DateFormater,
- 'TypeFormater': TypeFormater,
- 'YearFormater': YearFormater,
- 'StrToBoolean': StrToBoolean,
- 'FileFormater': FileFormater,
- 'UnknowType': None,
-}
-
-DATE_FORMATS = (
- ('%Y', _(u"4 digit year. e.g.: \"2015\"")),
- ('%Y/%m/%d', _(u"4 digit year/month/day. e.g.: \"2015/02/04\"")),
- ('%d/%m/%Y', _(u"Day/month/4 digit year. e.g.: \"04/02/2015\"")),
-)
-
-IMPORTER_TYPES_CHOICES = {'TypeFormater': TARGET_MODELS,
- 'DateFormater': DATE_FORMATS}
-
-
-class FormaterType(models.Model):
- formater_type = models.CharField(u"Formater type", max_length=20,
- choices=IMPORTER_TYPES)
- options = models.CharField(_(u"Options"), max_length=500, blank=True,
- null=True)
- many_split = models.CharField(_(u"Split character(s)"), max_length=10,
- blank=True, null=True)
-
- class Meta:
- verbose_name = _(u"Importer - Formater type")
- verbose_name_plural = _(u"Importer - Formater types")
- unique_together = ('formater_type', 'options', 'many_split')
- ordering = ('formater_type', 'options')
-
- def __unicode__(self):
- return u" - ".join(
- [unicode(dict(IMPORTER_TYPES)[self.formater_type])
- if self.formater_type in IMPORTER_TYPES_DCT else ''] +
- [getattr(self, k) for k in ('options', 'many_split')
- if getattr(self, k)])
-
- def get_choices(self):
- if self.format_type in IMPORTER_TYPES_CHOICES:
- return IMPORTER_TYPES_CHOICES[self.format_type]
-
- def get_formater_type(self, target, import_instance=None):
- if self.formater_type not in IMPORTER_TYPES_DCT.keys():
- return
- kwargs = {'db_target': target, 'import_instance': import_instance}
- if self.many_split:
- kwargs['many_split'] = self.many_split
- if self.formater_type == 'TypeFormater':
- if self.options not in TARGET_MODELS_KEYS:
- logger.warning(
- "**WARN FormaterType.get_formater_type**: {} "
- "is not in TARGET_MODELS_KEYS".format(self.options))
- return
- model = None
- if self.options in dir():
- model = dir()[self.options]
- else:
- model = import_class(self.options)
- return TypeFormater(model, **kwargs)
- elif self.formater_type == 'UnicodeFormater':
- if self.options:
- try:
- return UnicodeFormater(int(self.options.strip()), **kwargs)
- except ValueError:
- pass
- return UnicodeFormater(**kwargs)
- elif self.formater_type == 'DateFormater':
- date_formats = self.options
- if self.many_split:
- date_formats = self.options.split(kwargs.pop('many_split'))
- return DateFormater(date_formats, **kwargs)
- elif self.formater_type == 'StrToBoolean':
- return StrToBoolean(**kwargs)
- elif self.formater_type == 'UnknowType':
- return
- else:
- return IMPORTER_TYPES_DCT[self.formater_type](**kwargs)
-
-IMPORT_STATE = (("C", _(u"Created")),
- ("AP", _(u"Analyse in progress")),
- ("A", _(u"Analysed")),
- ("P", _(u"Import pending")),
- ("IP", _(u"Import in progress")),
- ("FE", _(u"Finished with errors")),
- ("F", _(u"Finished")),
- ("AC", _(u"Archived")),
- )
-
-IMPORT_STATE_DCT = dict(IMPORT_STATE)
-ENCODINGS = [(settings.ENCODING, settings.ENCODING),
- (settings.ALT_ENCODING, settings.ALT_ENCODING),
- ('utf-8', 'utf-8')]
-
-
-class Import(models.Model):
- user = models.ForeignKey('IshtarUser')
- name = models.CharField(_(u"Name"), max_length=500,
- blank=True, null=True)
- importer_type = models.ForeignKey(ImporterType)
- imported_file = models.FileField(
- _(u"Imported file"), upload_to="upload/imports/", max_length=220)
- imported_images = models.FileField(
- _(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)
- skip_lines = models.IntegerField(_(u"Skip lines"), default=1)
- error_file = models.FileField(_(u"Error file"),
- upload_to="upload/imports/",
- blank=True, null=True, max_length=255)
- result_file = models.FileField(_(u"Result file"),
- upload_to="upload/imports/",
- blank=True, null=True, max_length=255)
- match_file = models.FileField(_(u"Match file"),
- upload_to="upload/imports/",
- blank=True, null=True, max_length=255)
- state = models.CharField(_(u"State"), max_length=2, choices=IMPORT_STATE,
- default='C')
- conservative_import = models.BooleanField(
- _(u"Conservative import"), default=False,
- help_text='If set to true, do not overload existing values')
- creation_date = models.DateTimeField(
- _(u"Creation date"), auto_now_add=True, blank=True, null=True)
- end_date = models.DateTimeField(_(u"End date"), blank=True,
- null=True, editable=False)
- seconds_remaining = models.IntegerField(
- _(u"Remaining seconds"), blank=True, null=True, editable=False)
-
- class Meta:
- verbose_name = _(u"Import")
- verbose_name_plural = _(u"Imports")
-
- def __unicode__(self):
- return u"{} | {}".format(self.name or u"-", self.importer_type)
-
- def need_matching(self):
- return bool(TargetKey.objects.filter(associated_import=self,
- is_set=False).count())
-
- @property
- def errors(self):
- if not self.error_file:
- return []
- errors = []
- with open(self.error_file.path, 'rb') as csvfile:
- reader = csv.DictReader(csvfile, fieldnames=['line', 'column',
- 'error'])
- reader.next() # pass the header
- for row in reader:
- errors.append(row)
- return errors
-
- def get_actions(self):
- """
- Get available action relevant with the current status
- """
- actions = []
- if self.state == 'C':
- actions.append(('A', _(u"Analyse")))
- if self.state == 'A':
- actions.append(('A', _(u"Re-analyse")))
- actions.append(('I', _(u"Launch import")))
- if self.state in ('F', 'FE'):
- actions.append(('A', _(u"Re-analyse")))
- actions.append(('I', _(u"Re-import")))
- actions.append(('AC', _(u"Archive")))
- if self.state == 'AC':
- actions.append(('A', _(u"Unarchive")))
- actions.append(('D', _(u"Delete")))
- return actions
-
- @property
- def imported_filename(self):
- return self.imported_file.name.split(os.sep)[-1]
-
- @property
- def status(self):
- if self.state not in IMPORT_STATE_DCT:
- return ""
- return IMPORT_STATE_DCT[self.state]
-
- def get_importer_instance(self):
- return self.importer_type.get_importer_class(import_instance=self)(
- skip_lines=self.skip_lines, import_instance=self,
- conservative_import=self.conservative_import)
-
- @property
- def data_table(self):
- imported_file = self.imported_file.path
- tmpdir = None
- if zipfile.is_zipfile(imported_file):
- z = zipfile.ZipFile(imported_file)
- filename = None
- for name in z.namelist():
- # get first CSV file found
- if name.endswith('.csv'):
- filename = name
- break
- if not filename:
- return []
- tmpdir = tempfile.mkdtemp(prefix='tmp-ishtar-')
- imported_file = z.extract(filename, tmpdir)
-
- encodings = [self.encoding]
- encodings += [coding for coding, c in ENCODINGS
- if coding != self.encoding]
- for encoding in encodings:
- try:
- with open(imported_file) as csv_file:
- vals = [line
- for line in unicodecsv.reader(csv_file,
- encoding=encoding)]
- if tmpdir:
- shutil.rmtree(tmpdir)
- return vals
- except UnicodeDecodeError:
- pass # try the next encoding
- if tmpdir:
- shutil.rmtree(tmpdir)
- return []
-
- def initialize(self):
- self.state = 'AP'
- self.save()
- self.get_importer_instance().initialize(self.data_table, output='db')
- self.state = 'A'
- self.save()
-
- def importation(self):
- self.state = 'IP'
- self.save()
- importer = self.get_importer_instance()
- importer.importation(self.data_table)
- # result file
- filename = slugify(self.importer_type.name)
- now = datetime.datetime.now().isoformat('-').replace(':', '')
- result_file = filename + "_result_%s.csv" % now
- result_file = os.sep.join([self.result_file.storage.location,
- result_file])
- with open(result_file, 'w') as fle:
- fle.write(importer.get_csv_result().encode('utf-8'))
- self.result_file = File(open(fle.name))
- if importer.errors:
- self.state = 'FE'
- error_file = filename + "_errors_%s.csv" % now
- error_file = os.sep.join([self.error_file.storage.location,
- error_file])
- with open(error_file, 'w') as fle:
- fle.write(importer.get_csv_errors().encode('utf-8'))
- self.error_file = File(open(fle.name))
- else:
- self.state = 'F'
- self.error_file = None
- if importer.match_table:
- match_file = filename + "_match_%s.csv" % now
- match_file = os.sep.join([self.match_file.storage.location,
- match_file])
- with open(match_file, 'w') as fle:
- fle.write(importer.get_csv_matches().encode('utf-8'))
- self.match_file = File(open(fle.name))
- self.save()
-
- def archive(self):
- self.state = 'AC'
- self.save()
-
- def get_all_imported(self):
- imported = []
- for related, zorg in \
- self._meta.get_all_related_m2m_objects_with_model():
- accessor = related.get_accessor_name()
- imported += [(accessor, obj)
- for obj in getattr(self, accessor).all()]
- return imported
-
-
-def pre_delete_import(sender, **kwargs):
- # deleted imported items when an import is delete
- instance = kwargs.get('instance')
- if not instance:
- return
- to_delete = []
- for accessor, imported in instance.get_all_imported():
- to_delete.append(imported)
- for item in to_delete:
- item.delete()
-
-
-pre_delete.connect(pre_delete_import, sender=Import)
-
class Organization(Address, Merge, OwnPerms, ValueGetter):
TABLE_COLS = ('name', 'organization_type', 'town')
@@ -2572,13 +1876,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 +1916,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 +1991,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,24 +2083,24 @@ 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 \
+ 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)
+ cache.set(cache_key, res, settings.CACHE_TIMEOUT)
return res
def full_label(self):
@@ -2862,7 +2164,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 +2181,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)
@@ -2888,6 +2192,23 @@ class IshtarUser(User):
verbose_name = _(u"Ishtar user")
verbose_name_plural = _(u"Ishtar users")
+ def __unicode__(self):
+ return unicode(self.person)
+
+ @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
@@ -2897,13 +2218,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,19 +2229,48 @@ 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 Basket(models.Model):
+ """
+ Abstract class for a basket
+ Subclass must be defined with an "items" ManyToManyField
+ """
+ IS_BASKET = True
+ label = models.CharField(_(u"Label"), max_length=1000)
+ comment = models.TextField(_(u"Comment"), blank=True, null=True)
+ user = models.ForeignKey(IshtarUser, blank=True, null=True)
+ available = models.BooleanField(_(u"Available"), default=True)
+
+ class Meta:
+ abstract = True
+ unique_together = (('label', 'user'),)
+
+ def __unicode__(self):
+ return self.label
+
+ @property
+ def cached_label(self):
+ return unicode(self)
+
+ @classmethod
+ def get_short_menu_class(cls, pk):
+ return 'basket'
+
+ @property
+ def associated_filename(self):
+ return "{}-{}".format(datetime.date.today().strftime(
+ "%Y-%m-%d"), slugify(self.label))
class AuthorType(GeneralType):
@@ -2950,16 +2294,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 +2356,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"))
@@ -3038,7 +2377,6 @@ class Source(OwnPerms, ImageModel, models.Model):
duplicate = models.BooleanField(_(u"Has a duplicate"), default=False)
TABLE_COLS = ['title', 'source_type', 'authors', 'associated_url']
COL_LINK = ['associated_url']
- IMAGE_PREFIX = 'sources'
class Meta:
abstract = True
@@ -3046,6 +2384,13 @@ class Source(OwnPerms, ImageModel, models.Model):
def __unicode__(self):
return self.title
+ def get_associated_operation(self):
+ raise NotImplementedError()
+
+ def _get_base_image_path(self):
+ base = self.owner._get_base_image_path()
+ return u"{}/sources".format(base)
+
@property
def associated_filename(self):
values = [unicode(getattr(self, attr))
@@ -3156,7 +2501,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: