summaryrefslogtreecommitdiff
path: root/archaeological_finds/models.py
diff options
context:
space:
mode:
Diffstat (limited to 'archaeological_finds/models.py')
-rw-r--r--archaeological_finds/models.py215
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: