diff options
Diffstat (limited to 'archaeological_finds/models.py')
| -rw-r--r-- | archaeological_finds/models.py | 215 | 
1 files changed, 184 insertions, 31 deletions
diff --git a/archaeological_finds/models.py b/archaeological_finds/models.py index b52aabbf2..dc626feee 100644 --- a/archaeological_finds/models.py +++ b/archaeological_finds/models.py @@ -23,15 +23,17 @@ from django.conf import settings  from django.contrib.gis.db import models  from django.core.urlresolvers import reverse  from django.db.models import Max, Q +from django.db.models.signals import m2m_changed  from django.utils.translation import ugettext_lazy as _, ugettext  from ishtar_common.models import GeneralType, ImageModel, BaseHistorizedItem, \      ShortMenuItem, LightHistorizedItem, HistoricalRecords, OwnPerms, Source, \ -    Person +    Person, Basket, get_external_id  from archaeological_operations.models import AdministrativeAct  from archaeological_context_records.models import ContextRecord, Dating +from ishtar_common.models import PRIVATE_FIELDS  from archaeological_warehouse.models import Warehouse, Container @@ -102,8 +104,9 @@ IS_ISOLATED_CHOICES = (  class BaseFind(BaseHistorizedItem, OwnPerms):      IS_ISOLATED_DICT = dict(IS_ISOLATED_CHOICES)      label = models.TextField(_(u"Free ID")) -    external_id = models.CharField(_(u"External ID"), blank=True, null=True, -                                   max_length=120) +    external_id = models.TextField(_(u"External ID"), blank=True, null=True) +    auto_external_id = models.BooleanField( +        _(u"External ID is set automatically"), default=False)      description = models.TextField(_(u"Description"), blank=True, null=True)      comment = models.TextField(_(u"Comment"), blank=True, null=True)      topographic_localisation = models.CharField( @@ -118,7 +121,10 @@ class BaseFind(BaseHistorizedItem, OwnPerms):      batch = models.CharField(_(u"Batch/object"), max_length=1, default="U",                               choices=IS_ISOLATED_CHOICES)      index = models.IntegerField(u"Index", default=0) -    material_index = models.IntegerField(u"Material index", default=0) +    material_index = models.IntegerField(_(u"Material index"), default=0) +    point = models.PointField(_(u"Point"), blank=True, null=True, dim=3) +    line = models.LineStringField(_(u"Line"), blank=True, null=True) +    polygon = models.PolygonField(_(u"Polygon"), blank=True, null=True)      cache_short_id = models.TextField(          _(u"Short ID"), blank=True, null=True,          help_text=_(u"Cached value - do not edit")) @@ -231,6 +237,20 @@ class BaseFind(BaseHistorizedItem, OwnPerms):                  fields['find'] = field.related.model          return fields +    def save(self, *args, **kwargs): +        returned = super(BaseFind, self).save(*args, **kwargs) + +        updated = False +        if not self.external_id or self.auto_external_id: +            external_id = get_external_id('base_find_external_id', self) +            if external_id != self.external_id: +                updated = True +                self.auto_external_id = True +                self.external_id = external_id +        if updated: +            self.save() +        return returned +  WEIGHT_UNIT = (('g', _(u"g")),                 ('kg', _(u"kg")),) @@ -240,6 +260,11 @@ CHECK_CHOICES = (('NC', _(u"Not checked")),                   ) +class FindBasket(Basket): +    items = models.ManyToManyField('Find', blank=True, null=True, +                                   related_name='basket') + +  class Find(BaseHistorizedItem, ImageModel, OwnPerms, ShortMenuItem):      CHECK_DICT = dict(CHECK_CHOICES)      SHOW_URL = 'show-find' @@ -278,8 +303,9 @@ class Find(BaseHistorizedItem, ImageModel, OwnPerms, ShortMenuItem):      ATTRS_EQUIV = {'get_first_base_find': 'base_finds'}      base_finds = models.ManyToManyField(BaseFind, verbose_name=_(u"Base find"),                                          related_name='find') -    external_id = models.CharField(_(u"External ID"), blank=True, null=True, -                                   max_length=120) +    external_id = models.TextField(_(u"External ID"), blank=True, null=True) +    auto_external_id = models.BooleanField( +        _(u"External ID is set automatically"), default=False)      order = models.IntegerField(_(u"Order"), default=1)      label = models.TextField(_(u"Free ID"))      description = models.TextField(_(u"Description"), blank=True, null=True) @@ -288,6 +314,8 @@ class Find(BaseHistorizedItem, ImageModel, OwnPerms, ShortMenuItem):      conservatory_state = models.ForeignKey(          ConservatoryState, verbose_name=_(u"Conservatory state"), blank=True,          null=True) +    conservatory_comment = models.TextField(_(u"Conservatory comment"), +                                            blank=True, null=True)      preservation_to_considers = models.ManyToManyField(          PreservationType, verbose_name=_(u"Type of preservation to consider"),          related_name='finds') @@ -298,10 +326,10 @@ class Find(BaseHistorizedItem, ImageModel, OwnPerms, ShortMenuItem):      find_number = models.IntegerField(_("Find number"), blank=True, null=True)      upstream_treatment = models.ForeignKey(          "Treatment", blank=True, null=True, -        related_name='downstream_treatment', +        related_name='downstream',          verbose_name=_("Upstream treatment"))      downstream_treatment = models.ForeignKey( -        "Treatment", blank=True, null=True, related_name='upstream_treatment', +        "Treatment", blank=True, null=True, related_name='upstream',          verbose_name=_("Downstream treatment"))      datings = models.ManyToManyField(Dating, verbose_name=_(u"Dating"),                                       related_name='find') @@ -318,6 +346,8 @@ class Find(BaseHistorizedItem, ImageModel, OwnPerms, ShortMenuItem):      width = models.FloatField(_(u"Width (cm)"), blank=True, null=True)      height = models.FloatField(_(u"Height (cm)"), blank=True, null=True)      diameter = models.FloatField(_(u"Diameter (cm)"), blank=True, null=True) +    dimensions_comment = models.TextField(_(u"Dimensions comment"), +                                          blank=True, null=True)      mark = models.TextField(_(u"Mark"), blank=True, null=True)      comment = models.TextField(_(u"Comment"), blank=True, null=True)      dating_comment = models.TextField(_(u"Comment on dating"), blank=True, @@ -329,6 +359,7 @@ class Find(BaseHistorizedItem, ImageModel, OwnPerms, ShortMenuItem):      check_date = models.DateField(_(u"Check date"),                                    default=datetime.date.today)      history = HistoricalRecords() +    BASKET_MODEL = FindBasket      def __init__(self, *args, **kwargs):          super(Find, self).__init__(*args, **kwargs) @@ -372,6 +403,18 @@ class Find(BaseHistorizedItem, ImageModel, OwnPerms, ShortMenuItem):          return u" - ".join([base_find.name                              for base_find in self.base_finds.all()]) +    @property +    def full_label(self): +        lbl = u" - ".join([ +            getattr(self, attr) +            for attr in ('label', 'administrative_index') +            if getattr(self, attr)]) +        base = u" - ".join([base_find.complete_id() +                            for base_find in self.base_finds.all()]) +        if base: +            lbl += u' ({})'.format(base) +        return lbl +      def get_first_base_find(self):          q = self.base_finds          if not q.count(): @@ -394,6 +437,44 @@ class Find(BaseHistorizedItem, ImageModel, OwnPerms, ShortMenuItem):              bf.context_record.operation.get_reference(),              self.index) +    def upstream_treatments(self): +        treatments = [] +        base_finds = [bf.pk for bf in self.base_finds.all()] +        if self.upstream_treatment and \ +                self.upstream_treatment.pk not in treatments: +            treatments.append( +                (self.upstream_treatment.upstream.distinct( +                ).order_by('label').all(), self.upstream_treatment)) +            for upstream in self.upstream_treatment.upstream.all(): +                if upstream.pk != self.pk and not [ +                        bf.pk for bf in upstream.base_finds.all() +                        if bf.pk in base_finds]: +                    continue +                for items, treatment in upstream.upstream_treatments(): +                    if treatment.pk not in treatments: +                        treatments.append((treatment.upstream.order_by( +                            'label').all(), treatment)) +        return treatments + +    def downstream_treatments(self): +        treatments = [] +        base_finds = [bf.pk for bf in self.base_finds.all()] +        if self.downstream_treatment and \ +                self.downstream_treatment.pk not in treatments: +            treatments.append( +                (self.downstream_treatment.downstream.distinct( +                ).order_by('label').all(), self.downstream_treatment)) +            for downstream in self.downstream_treatment.downstream.all(): +                if downstream.pk != self.pk and not [ +                        bf.pk for bf in downstream.base_finds.all() +                        if bf.pk in base_finds]: +                    continue +                for items, treatment in downstream.downstream_treatments(): +                    if treatment.pk not in treatments: +                        treatments.append((treatment.downstream.order_by( +                            'label').all(), treatment)) +        return treatments +      def get_department(self):          bf = self.get_first_base_find()          if not bf: @@ -460,19 +541,24 @@ class Find(BaseHistorizedItem, ImageModel, OwnPerms, ShortMenuItem):          return q.filter(downstream_treatment__isnull=True).count()      def duplicate(self, user): -        # TODO -        raise +        model = self.__class__ +        # base fields +        table_cols = [field.name for field in model._meta.fields +                      if field.name not in PRIVATE_FIELDS or +                      field.name == 'order']          dct = dict([(attr, getattr(self, attr)) for attr in -                    ('order', 'label', 'description', -                     'volume', 'weight', 'find_number', 'dating', -                     'conservatory_state', 'preservation_to_consider', -                     'weight_unit', )]) +                    table_cols])          dct['order'] += 1          dct['history_modifier'] = user          new = self.__class__(**dct)          new.save() -        for base_find in self.base_finds.all(): -            new.base_finds.add(base_find) + +        # m2m fields +        m2m = [field.name for field in model._meta.many_to_many +               if field.name not in PRIVATE_FIELDS] +        for field in m2m: +            for val in getattr(self, field).all(): +                getattr(new, field).add(val)          return new      @classmethod @@ -485,21 +571,36 @@ class Find(BaseHistorizedItem, ImageModel, OwnPerms, ShortMenuItem):      def save(self, *args, **kwargs):          super(Find, self).save(*args, **kwargs) + +        updated = False +        if not self.external_id or self.auto_external_id: +            external_id = get_external_id('find_external_id', self) +            if external_id != self.external_id: +                updated = True +                self.auto_external_id = True +                self.external_id = external_id +        if updated: +            self.save() +            return +          q = self.base_finds          if not self.index and q.count(): -            operation = q.order_by( -                '-context_record__operation__start_date')\ -                .all()[0].context_record.operation -            q = Find.objects\ -                .filter(base_finds__context_record__operation=operation) -            if self.pk: -                q = q.exclude(pk=self.pk) -            if q.count(): -                self.index = q.aggregate(Max('index'))['index__max'] + 1 -            else: -                self.index = 1 -            self.save() -        for base_find in self.base_finds.all(): +            operation = q.filter( +                context_record__operation__pk__isnull=False).order_by( +                '-context_record__operation__start_date') +            if operation.count(): +                operation = operation.all()[0].context_record.operation +                q = Find.objects\ +                    .filter(base_finds__context_record__operation=operation) +                if self.pk: +                    q = q.exclude(pk=self.pk) +                if q.count(): +                    self.index = q.aggregate(Max('index'))['index__max'] + 1 +                else: +                    self.index = 1 +                self.save() +        for base_find in self.base_finds.filter( +                context_record__operation__pk__isnull=False).all():              modified = False              if not base_find.index:                  modified = True @@ -528,8 +629,18 @@ class Find(BaseHistorizedItem, ImageModel, OwnPerms, ShortMenuItem):              #        idx and idx['material_index__max'] + 1 or 1 +def base_find_find_changed(sender, **kwargs): +    obj = kwargs.get('instance', None) +    if not obj: +        return +    # recalculate complete id and external id +    obj.save() + +m2m_changed.connect(base_find_find_changed, sender=Find.base_finds.through) +  class FindSource(Source):      SHOW_URL = 'show-findsource' +    MODIFY_URL = 'find_source_modify'      class Meta:          verbose_name = _(u"Find documentation") @@ -544,6 +655,16 @@ class FindSource(Source):  class TreatmentType(GeneralType):      virtual = models.BooleanField(_(u"Virtual")) +    upstream_is_many = models.BooleanField( +        _(u"Upstream is many"), default=False, +        help_text=_( +            u"Check this if for this treatment from many finds you'll get " +            u"one.")) +    downstream_is_many = models.BooleanField( +        _(u"Downstream is many"), default=False, +        help_text=_( +            u"Check this if for this treatment from one find you'll get " +            u"many."))      class Meta:          verbose_name = _(u"Treatment type") @@ -560,8 +681,11 @@ class Treatment(BaseHistorizedItem, OwnPerms):      comment = models.TextField(_(u"Comment"), blank=True, null=True)      treatment_type = models.ForeignKey(TreatmentType,                                         verbose_name=_(u"Treatment type")) -    location = models.ForeignKey(Warehouse, verbose_name=_(u"Location"), -                                 blank=True, null=True) +    location = models.ForeignKey( +        Warehouse, verbose_name=_(u"Location"), blank=True, null=True, +        help_text=_( +            u"Location where the treatment is done. Target warehouse for " +            u"a move."))      other_location = models.CharField(_(u"Other location"), max_length=200,                                        blank=True, null=True)      person = models.ForeignKey( @@ -588,6 +712,35 @@ class Treatment(BaseHistorizedItem, OwnPerms):              lbl += u" %s %s" % (_(u"by"), unicode(self.person))          return lbl +    def save(self, *args, **kwargs): +        items, user, extra_args_for_new = [], None, [] +        if "items" in kwargs: +            items = kwargs.pop('items') +        if "user" in kwargs: +            user = kwargs.pop('user') +        if "extra_args_for_new" in kwargs: +            extra_args_for_new = kwargs.pop('extra_args_for_new') +        is_new = self.pk is None +        super(Treatment, self).save(*args, **kwargs) +        if not is_new or not items: +            return +        basket = None +        if hasattr(items, "items"): +            basket = items +            items = basket.items.all() +        for item in items: +            new = item.duplicate(user) +            item.downstream_treatment = self +            item.save() +            new.upstream_treatment = self +            for k in extra_args_for_new: +                setattr(new, k, extra_args_for_new[k]) +            new.save() +            # update baskets +            for basket in FindBasket.objects.filter(items__pk=item.pk).all(): +                basket.items.remove(item) +                basket.items.add(new) +  class TreatmentSource(Source):      class Meta:  | 
