summaryrefslogtreecommitdiff
path: root/ishtar/ishtar_base/models.py
diff options
context:
space:
mode:
Diffstat (limited to 'ishtar/ishtar_base/models.py')
-rw-r--r--ishtar/ishtar_base/models.py1204
1 files changed, 1204 insertions, 0 deletions
diff --git a/ishtar/ishtar_base/models.py b/ishtar/ishtar_base/models.py
new file mode 100644
index 000000000..7de7b239b
--- /dev/null
+++ b/ishtar/ishtar_base/models.py
@@ -0,0 +1,1204 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+# Copyright (C) 2010-2011 Étienne Loks <etienne.loks_AT_peacefrogsDOTnet>
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as
+# published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+# See the file COPYING for details.
+
+"""
+Models description
+"""
+import datetime
+
+from django.core.exceptions import ObjectDoesNotExist, ValidationError
+from django.core.validators import validate_slug
+from django.utils.translation import ugettext_lazy as _, ugettext
+from django.db.utils import DatabaseError
+from django.utils.safestring import SafeUnicode, mark_safe
+from django.db.models import Q, Max
+from django.db.models.signals import m2m_changed
+
+from django.contrib.auth.models import User
+from django.contrib.gis.db import models
+from django.contrib import admin
+
+from simple_history.models import HistoricalRecords as BaseHistoricalRecords
+
+from ishtar import settings
+
+JOINT = u" - "
+
+# HistoricalRecords enhancement: don't save identical versions
+class HistoricalRecords(BaseHistoricalRecords):
+ def create_historical_record(self, instance, type):
+ manager = getattr(instance, self.manager_name)
+ attrs = {}
+ for field in instance._meta.fields:
+ attrs[field.attname] = getattr(instance, field.attname)
+ history = instance.history.all()
+ if not history:
+ manager.create(history_type=type, **attrs)
+ return
+ old_instance = history[0]
+ for field in instance._meta.fields:
+ if getattr(old_instance, field.attname) != attrs[field.attname]:
+ manager.create(history_type=type, **attrs)
+ return
+
+# valid ID validator for models
+def valid_id(cls):
+ def func(value):
+ try:
+ cls.objects.get(pk=value)
+ except ObjectDoesNotExist:
+ raise ValidationError(_(u"Not a valid item."))
+ return func
+
+# unique validator for models
+def is_unique(cls, field):
+ def func(value):
+ query = {field:value}
+ try:
+ assert cls.objects.filter(**query).count() == 0
+ except AssertionError:
+ raise ValidationError(_(u"This item already exist."))
+ return func
+
+class OwnPerms:
+ """
+ Manage special permissions for object's owner
+ """
+ @classmethod
+ def get_query_owns(cls, user):
+ """
+ Query object to get own items
+ """
+ return None # implement for each object
+
+ def is_own(self, user):
+ """
+ Check if the current object is owned by the user
+ """
+ query = self.get_query_owns(user)
+ if not query:
+ return False
+ query = query & Q(pk=self.pk)
+ return cls.objects.filter(query).count()
+
+ @classmethod
+ def has_item_of(cls, user):
+ """
+ Check if the user own some items
+ """
+ query = cls.get_query_owns(user)
+ if not query:
+ return False
+ return cls.objects.filter(query).count()
+
+ @classmethod
+ def get_owns(cls, user):
+ """
+ Get Own items
+ """
+ if isinstance(user, User):
+ user = IshtarUser.objects.get(user_ptr=user)
+ if user.is_anonymous():
+ return []
+ query = cls.get_query_owns(user)
+ if not query:
+ return []
+ return cls.objects.filter(query).order_by(*cls._meta.ordering).all()
+
+class GeneralType(models.Model):
+ """
+ Abstract class for "types"
+ """
+ label = models.CharField(_(u"Label"), max_length=100)
+ txt_idx = models.CharField(_(u"Textual ID"),
+ validators=[validate_slug], max_length=30, unique=True)
+ comment = models.TextField(_(u"Comment"), blank=True, null=True)
+ available = models.BooleanField(_(u"Available"))
+ HELP_TEXT = u""
+
+ class Meta:
+ abstract = True
+
+ def __unicode__(self):
+ return self.label
+
+ @classmethod
+ def get_help(cls, dct={}):
+ help_text = cls.HELP_TEXT
+ c_rank = -1
+ help_items = u"\n"
+ for item in cls.get_types(dct=dct, instances=True):
+ if not item.comment:
+ continue
+ if c_rank > item.rank:
+ help_items += u"</dl>\n"
+ elif c_rank < item.rank:
+ help_items += u"<dl>\n"
+ c_rank = item.rank
+ help_items += u"<dt>%s</dt><dd>%s</dd>" % (item.label,
+ u"<br/>".join(item.comment.split('\n')))
+ c_rank += 1
+ if c_rank:
+ help_items += c_rank*u"</dl>"
+ return mark_safe(help_text + help_items)
+
+ @classmethod
+ def get_types(cls, dct={}, instances=False):
+ base_dct = dct.copy()
+ if hasattr(cls, 'parent'):
+ return cls._get_parent_types(base_dct, instances)
+ return cls._get_types(base_dct, instances)
+
+ @classmethod
+ def _get_types(cls, dct={}, instances=False):
+ dct['available'] = True
+ if not instances:
+ yield ('', '--')
+ for item in cls.objects.filter(**dct).all():
+ if instances:
+ item.rank = 0
+ yield item
+ else:
+ yield (item.pk, _(unicode(item)))
+
+ PREFIX = "&rsaquo; "
+
+ @classmethod
+ def _get_childs(cls, item, dct, prefix=0, instances=False):
+ prefix += 1
+ dct['parent'] = item
+ childs = cls.objects.filter(**dct)
+ if hasattr(cls, 'order'):
+ childs = childs.order_by('order')
+ for child in childs.all():
+ if instances:
+ child.rank = prefix
+ yield child
+ else:
+ yield (child.pk, SafeUnicode(prefix*cls.PREFIX + \
+ unicode(_(unicode(child))) ))
+ for sub_child in cls._get_childs(child, dct, prefix, instances):
+ yield sub_child
+
+ @classmethod
+ def _get_parent_types(cls, dct={}, instances=False):
+ dct['available'] = True
+ if not instances:
+ yield ('', '--')
+ dct['parent'] = None
+ items = cls.objects.filter(**dct)
+ if hasattr(cls, 'order'):
+ items = items.order_by('order')
+ for item in items.all():
+ if instances:
+ item.rank = 0
+ yield item
+ else:
+ yield (item.pk, unicode(item))
+ for child in cls._get_childs(item, dct, instances):
+ yield child
+
+class HistoryError(Exception):
+ def __init__(self, value):
+ self.value = value
+ def __str__(self):
+ return repr(self.value)
+
+class BaseHistorizedItem(models.Model):
+ history_modifier = models.ForeignKey(User, related_name='+',
+ verbose_name=_(u"Last modifier"))
+ class Meta:
+ abstract = True
+
+ def save(self, *args, **kwargs):
+ assert hasattr(self, 'history_modifier') == True
+ super(BaseHistorizedItem, self).save(*args, **kwargs)
+ return True
+
+ def get_previous(self, step=None, date=None, strict=True):
+ """
+ Get a "step" previous state of the item
+ """
+ assert step or date
+ historized = self.history.all()
+ item = None
+ if step:
+ assert len(historized) > step
+ item = historized[step]
+ else:
+ for step, item in enumerate(historized):
+ if item.history_date == date:
+ break
+ # ended with no match
+ if item.history_date != date:
+ return
+ item._step = step
+ if len(historized) != (step + 1):
+ item._previous = historized[step + 1].history_date
+ else:
+ item._previous = None
+ if step > 0:
+ item._next = historized[step - 1].history_date
+ else:
+ 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]
+ if hasattr(field, 'rel') and field.rel:
+ if not hasattr(item, k+'_id'):
+ setattr(item, k, getattr(self, k))
+ continue
+ val = getattr(item, k+'_id')
+ if not val:
+ setattr(item, k, None)
+ continue
+ try:
+ val = field.rel.to.objects.get(pk=val)
+ setattr(item, k, val)
+ except ObjectDoesNotExist:
+ if strict:
+ raise HistoryError(u"The class %s has no pk %d" % (
+ unicode(field.rel.to), val))
+ setattr(item, k, None)
+ item.pk = self.pk
+ return item
+
+ def rollback(self, date):
+ """
+ Rollback to a previous state
+ """
+ to_del, new_item = [], None
+ for item in self.history.all():
+ to_del.append(item)
+ if item.history_date == date:
+ new_item = item
+ break
+ if not new_item:
+ raise HistoryError(u"The date to rollback to doesn't exist.")
+ try:
+ for f in self._meta.fields:
+ k = f.name
+ if k != 'id' and hasattr(self, k):
+ if not hasattr(new_item, k):
+ k = k + "_id"
+ setattr(self, k, getattr(new_item, k))
+ self.save()
+ except:
+ raise HistoryError(u"The rollback has failed.")
+ # clean the obsolete history
+ for historized_item in to_del:
+ historized_item.delete()
+
+ def values(self):
+ values = {}
+ for f in self._meta.fields:
+ k = f.name
+ if k != 'id':
+ values[k] = getattr(self, k)
+ return values
+
+class LightHistorizedItem(BaseHistorizedItem):
+ history_date = models.DateTimeField(default=datetime.datetime.now)
+ class Meta:
+ abstract = True
+
+class Departement(models.Model):
+ label = models.CharField(_(u"Label"), max_length=30)
+ number = models.CharField(_(u"Number"), unique=True, max_length=3)
+
+ class Meta:
+ verbose_name = _(u"Departement")
+ verbose_name_plural = _(u"Departements")
+ ordering = ['number']
+
+ def __unicode__(self):
+ return unicode(self.number) + JOINT + self.label
+
+class Address(BaseHistorizedItem):
+ address = models.TextField(_(u"Address"), null=True, blank=True)
+ address_complement = models.TextField(_(u"Address complement"), null=True,
+ blank=True)
+ postal_code = models.CharField(_(u"Postal code"), max_length=10, null=True,
+ blank=True)
+ town = models.CharField(_(u"Town"), max_length=30, null=True, blank=True)
+ country = models.CharField(_(u"Country"), max_length=30, null=True,
+ blank=True)
+ phone = models.CharField(_(u"Phone"), max_length=18, null=True, blank=True)
+ mobile_phone = models.CharField(_(u"Mobile phone"), max_length=18,
+ null=True, blank=True)
+ history = HistoricalRecords()
+
+ class Meta:
+ abstract = True
+
+class OrganizationType(GeneralType):
+ class Meta:
+ verbose_name = _(u"Organization type")
+ verbose_name_plural = _(u"Organization types")
+
+class Organization(Address, OwnPerms):
+ name = models.CharField(_(u"Name"), max_length=100)
+ organization_type = models.ForeignKey(OrganizationType,
+ verbose_name=_(u"Type"))
+ history = HistoricalRecords()
+ class Meta:
+ verbose_name = _(u"Organization")
+ verbose_name_plural = _(u"Organizations")
+ permissions = (
+ ("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")),
+ )
+
+ def __unicode__(self):
+ return self.name
+
+class PersonType(GeneralType):
+ class Meta:
+ verbose_name = _(u"Person type")
+ verbose_name_plural = _(u"Person types")
+
+class Person(Address, OwnPerms) :
+ TYPE = (('Mr', _(u'Mr')),
+ ('Ms', _(u'Miss')),
+ ('Md', _(u'Mrs')),
+ ('Dr', _(u'Doctor')),
+ )
+ title = models.CharField(_(u"Title"), max_length=2, choices=TYPE)
+ surname = models.CharField(_(u"Surname"), max_length=20)
+ name = models.CharField(_(u"Name"), max_length=30)
+ email = models.CharField(_(u"Email"), max_length=40, blank=True, null=True)
+ person_type = models.ForeignKey(PersonType, verbose_name=_(u"Type"))
+ attached_to = models.ForeignKey('Organization',
+ verbose_name=_(u"Is attached to"), blank=True, null=True)
+ is_author = models.NullBooleanField(_(u"Is an author?"), blank=True,
+ null=True)
+ in_charge_storage = models.NullBooleanField(_(u"In charge of a storage?"),
+ blank=True, null=True)
+
+ class Meta:
+ verbose_name = _(u"Person")
+ verbose_name_plural = _(u"Persons")
+ permissions = (
+ ("view_person", ugettext(u"Can view Person")),
+ ("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")),
+ )
+
+ def __unicode__(self):
+ lbl = u"%s %s" % (self.name, self.surname)
+ lbl += JOINT
+ if self.attached_to:
+ lbl += unicode(self.attached_to)
+ elif self.email:
+ lbl += self.email
+ return lbl
+
+ def full_label(self):
+ return u" ".join([unicode(getattr(self, attr))
+ for attr in ('title', 'surname', 'name', 'attached_to')
+ if getattr(self, attr)])
+
+class IshtarUser(User):
+ person = models.ForeignKey(Person, verbose_name=_(u"Person"), unique=True)
+
+ class Meta:
+ verbose_name = _(u"Ishtar user")
+ verbose_name_plural = _(u"Ishtar users")
+
+class AuthorType(GeneralType):
+ class Meta:
+ verbose_name = _(u"Author type")
+ verbose_name_plural = _(u"Author types")
+
+class Author(models.Model):
+ person = models.ForeignKey(Person, verbose_name=_(u"Person"))
+ author_type = models.ForeignKey(AuthorType, verbose_name=_(u"Author type"))
+
+ class Meta:
+ verbose_name = _(u"Author")
+ verbose_name_plural = _(u"Authors")
+
+ def __unicode__(self):
+ return unicode(self.person) + JOINT + unicode(self.author_type)
+
+class SourceType(GeneralType):
+ class Meta:
+ verbose_name = _(u"Source type")
+ verbose_name_plural = _(u"Source types")
+
+class Source(models.Model):
+ title = models.CharField(_(u"Title"), max_length=200)
+ source_type = models.ForeignKey(SourceType, verbose_name=_(u"Type"))
+ authors = models.ManyToManyField(Author, verbose_name=_(u"Authors"))
+
+ class Meta:
+ abstract = True
+
+ def __unicode__(self):
+ return self.title
+
+class FileType(GeneralType):
+ class Meta:
+ verbose_name = _(u"Archaeological file type")
+ verbose_name_plural = _(u"Archaeological file types")
+
+ @classmethod
+ def is_preventive(cls, file_type_id, key=''):
+ key = key or 'preventive'
+ try:
+ preventive = FileType.objects.get(txt_idx=key).pk
+ return file_type_id == preventive
+ except ObjectDoesNotExist:
+ return False
+
+class PermitType(GeneralType):
+ class Meta:
+ verbose_name = _(u"Permit type")
+ verbose_name_plural = _(u"Permit types")
+
+if settings.COUNTRY == 'fr':
+ class SaisineType(GeneralType):
+ delay = models.IntegerField(_(u"Delay (in days)"))
+ class Meta:
+ verbose_name = u"Type Saisine"
+ verbose_name_plural = u"Types Saisine"
+
+class File(BaseHistorizedItem, OwnPerms):
+ TABLE_COLS = ['numeric_reference', 'year', 'internal_reference',
+ 'file_type', 'saisine_type', 'towns', ]
+ year = models.IntegerField(_(u"Year"),
+ default=lambda:datetime.datetime.now().year)
+ numeric_reference = models.IntegerField(_(u"Numeric reference"))
+ internal_reference = models.CharField(_(u"Internal reference"),
+ max_length=60, unique=True)
+ file_type = models.ForeignKey(FileType, verbose_name=_(u"File type"))
+ in_charge = models.ForeignKey(Person, related_name='+',
+ verbose_name=_(u"Person in charge"))
+ general_contractor = models.ForeignKey(Person, related_name='+',
+ verbose_name=_(u"General contractor"), blank=True, null=True)
+ town_planning_service = models.ForeignKey(Organization, related_name='+',
+ verbose_name=_(u"Town planning service"), blank=True, null=True)
+ permit_type = models.ForeignKey(PermitType, verbose_name=_(u"Permit type"),
+ blank=True, null=True)
+ permit_reference = models.CharField(_(u"Permit reference"),
+ max_length=60, blank=True, null=True)
+ is_active = models.BooleanField(_(u"Is active?"), default=True)
+ towns = models.ManyToManyField("Town", verbose_name=_(u"Towns"))
+ creation_date = models.DateField(_(u"Creation date"),
+ default=datetime.date.today)
+ reception_date = models.DateField(_(u'Reception date'), blank=True,
+ null=True)
+ related_file = models.ForeignKey("File", verbose_name=_(u"Related file"),
+ blank=True, null=True)
+ if settings.COUNTRY == 'fr':
+ saisine_type = models.ForeignKey(SaisineType, blank=True, null=True,
+ verbose_name= u"Type de saisine")
+ reference_number = models.IntegerField(_(u"Reference number"),
+ blank=True, null=True)
+ total_surface = models.IntegerField(_(u"Total surface (m²)"),
+ blank=True, null=True)
+ total_developed_surface = models.IntegerField(
+ _(u"Total developed surface (m²)"), blank=True, null=True)
+ address = models.TextField(_(u"Main address"), null=True, blank=True)
+ address_complement = models.TextField(_(u"Main address - complement"),
+ null=True, blank=True)
+ postal_code = models.CharField(_(u"Main address - postal code"),
+ max_length=10, null=True, blank=True)
+ comment = models.TextField(_(u"Comment"))
+ history = HistoricalRecords()
+
+ class Meta:
+ verbose_name = _(u"Archaeological file")
+ verbose_name_plural = _(u"Archaeological files")
+ permissions = (
+ ("view_own_file", ugettext(u"Can view own Archaelogical file")),
+ ("add_own_file", ugettext(u"Can add own Archaelogical file")),
+ ("change_own_file", ugettext(u"Can change own Archaelogical file")),
+ ("delete_own_file", ugettext(u"Can delete own Archaelogical file")),
+ )
+ ordering = ['-year', '-numeric_reference']
+
+ def __unicode__(self):
+ items = [unicode(_('Intercommunal'))]
+ if self.towns.count() == 1:
+ items[0] = unicode(self.towns.all()[0])
+ items.append("-".join((unicode(self.year),
+ unicode(self.numeric_reference))))
+ items += [unicode(getattr(self, k))[:36]
+ for k in ['internal_reference',] if getattr(self, k)]
+ return JOINT.join(items)
+
+ @classmethod
+ def get_query_owns(cls, user):
+ return Q(history_modifier=user) & Q(is_active=True)
+
+ def closing(self):
+ if self.is_active:
+ return
+ for item in self.history.all():
+ if item.is_active():
+ break
+ closing_item = item
+ return {'date':item.history_date, 'user':item.history_modifier}
+
+ def total_surface_ha(self):
+ if self.total_surface:
+ return self.total_surface/10000.0
+
+ def total_developed_surface_ha(self):
+ if self.total_developed_surface:
+ return self.total_developed_surface/10000.0
+
+ def operation_acts(self):
+ acts = []
+ for ope in self.operations.all():
+ for act in ope.administrative_act.all():
+ acts.append(act)
+ return acts
+
+ def is_preventive(self):
+ return FileType.is_preventive(self.file_type.pk)
+
+class OperationType(GeneralType):
+ class Meta:
+ verbose_name = _(u"Operation type")
+ verbose_name_plural = _(u"Operation types")
+
+ @classmethod
+ def is_preventive(cls, ope_type_id, key=''):
+ key = key or 'prev_excavation'
+ try:
+ preventive = OperationType.objects.get(txt_idx=key).pk
+ return ope_type_id == preventive
+ except ObjectDoesNotExist:
+ return False
+
+class RemainType(GeneralType):
+ class Meta:
+ verbose_name = _(u"Remain type")
+ verbose_name_plural = _(u"Remain types")
+
+class Operation(BaseHistorizedItem, OwnPerms):
+ TABLE_COLS = ['operation_code', 'year', 'operation_type',
+ 'remains', 'towns', 'associated_file', 'start_date']
+ start_date = models.DateField(_(u"Start date"), null=True, blank=True)
+ end_date = models.DateField(_(u"Closing date"), null=True, blank=True)
+ in_charge = models.ForeignKey('Person', related_name='+', null=True,
+ blank=True, verbose_name=_(u"In charge"))
+ year = models.IntegerField(_(u"Year"))
+ operation_code = models.IntegerField(_(u"Operation code"))
+ associated_file = models.ForeignKey(File, related_name='operations',
+ verbose_name=_(u"File"), blank=True, null=True)
+ operation_type = models.ForeignKey(OperationType, related_name='+',
+ verbose_name=_(u"Operation type"))
+ surface = models.IntegerField(_(u"Surface (m²)"), blank=True, null=True)
+ remains = models.ManyToManyField("RemainType", verbose_name=_(u'Remains'))
+ towns = models.ManyToManyField("Town", verbose_name=_(u"Towns"))
+ cost = models.IntegerField(_(u"Cost (€)"), blank=True, null=True)
+ periods = models.ManyToManyField('Period', verbose_name=_(u"Periods"))
+ if settings.COUNTRY == 'fr':
+ code_patriarche = models.IntegerField(u"Code PATRIARCHE", null=True,
+ blank=True)
+ code_dracar = models.CharField(u"Code DRACAR", max_length=10, null=True,
+ blank=True)
+ fnap_financing = models.FloatField(u"Financement FNAP",
+ blank=True, null=True)
+ TABLE_COLS += ["code_patriarche"]
+ zoning_prescription = models.NullBooleanField(
+ _(u"Prescription on zoning"), blank=True, null=True)
+ large_area_prescription = models.NullBooleanField(
+ _(u"Prescription on large area"), blank=True, null=True)
+ geoarchaeological_context_prescription = models.NullBooleanField(
+ _(u"Prescription on geoarchaeological context"), blank=True, null=True)
+ comment = models.TextField(_(u"Comment"), null=True, blank=True)
+ history = HistoricalRecords()
+
+ class Meta:
+ verbose_name = _(u"Operation")
+ verbose_name_plural = _(u"Operations")
+ permissions = (
+ ("view_own_operation", ugettext(u"Can view own Operation")),
+ ("add_own_operation", ugettext(u"Can add own Operation")),
+ ("change_own_operation", ugettext(u"Can change own Operation")),
+ ("delete_own_operation", ugettext(u"Can delete own Operation")),
+ )
+
+ def __unicode__(self):
+ items = [unicode(_('Intercommunal'))]
+ if self.towns.count() == 1:
+ items[0] = unicode(self.towns.all()[0])
+ items.append("-".join((unicode(self.year),
+ unicode(self.operation_code))))
+ return JOINT.join(items)
+
+ def is_own(self, person):
+ return False
+
+ @classmethod
+ def get_query_owns(cls, user):
+ return Q(in_charge=user.person)|Q(history_modifier=user)\
+ & Q(end_date__isnull=True)
+
+ def is_active(self):
+ return not bool(self.end_date)
+
+ def closing(self):
+ if self.is_active():
+ return
+ for item in self.history.all():
+ if not item.end_date:
+ break
+ return {'date':item.history_date,
+ 'user':IshtarUser.objects.get(pk=item.history_modifier_id)}
+
+class OperationSource(Source):
+ class Meta:
+ verbose_name = _(u"Operation documentation")
+ verbose_name_plural = _(u"Operation documentations")
+ operation = models.ForeignKey(Operation, verbose_name=_(u"Operation"),
+ related_name="source")
+
+class Parcel(LightHistorizedItem):
+ associated_file = models.ForeignKey(File, related_name='parcels',
+ blank=True, null=True, verbose_name=_(u"File"))
+ operation = models.ForeignKey(Operation, related_name='parcels', blank=True,
+ null=True, verbose_name=_(u"Operation"))
+ year = models.IntegerField(_(u"Year"),
+ default=lambda:datetime.datetime.now().year)
+ town = models.ForeignKey("Town", related_name='parcels',
+ verbose_name=_(u"Town"))
+ section = models.CharField(_(u"Section"), max_length=4)
+ parcel_number = models.CharField(_(u"Parcel number"), max_length=6)
+
+ class Meta:
+ verbose_name = _(u"Parcel")
+ verbose_name_plural = _(u"Parcels")
+
+ def short_label(self):
+ return JOINT.join([unicode(item) for item in [self.section,
+ self.parcel_number] if item])
+
+ def __unicode__(self):
+ return self.short_label()
+
+ def long_label(self):
+ return JOINT.join([unicode(item) for item in \
+ [self.associated_file, self.operation, self.section, self.parcel_number]
+ if item])
+
+class Period(GeneralType) :
+ order = models.IntegerField(_(u"Order"))
+ start_date = models.IntegerField(_(u"Start date"))
+ end_date = models.IntegerField(_(u"End date"))
+ parent = models.ForeignKey("Period", verbose_name=_(u"Parent period"),
+ blank=True, null=True)
+
+ class Meta:
+ verbose_name = _(u"Type Period")
+ verbose_name_plural = _(u"Types Period")
+
+ def __unicode__(self):
+ return self.label
+
+class DatingType(GeneralType):
+ class Meta:
+ verbose_name = _(u"Dating type")
+ verbose_name_plural = _(u"Dating types")
+
+class DatingQuality(GeneralType):
+ class Meta:
+ verbose_name = _(u"Dating quality")
+ verbose_name_plural = _(u"Dating qualities")
+
+class Dating(models.Model):
+ period = models.ForeignKey(Period, verbose_name=_(u"Period"))
+ start_date = models.IntegerField(_(u"Start date"), blank=True, null=True)
+ end_date = models.IntegerField(_(u"End date"), blank=True, null=True)
+ dating_type = models.ForeignKey(DatingType, verbose_name=_(u"Dating type"),
+ blank=True, null=True)
+ quality = models.ForeignKey(DatingQuality, verbose_name=_(u"Quality"),
+ blank=True, null=True)
+
+ class Meta:
+ verbose_name = _(u"Dating")
+ verbose_name_plural = _(u"Datings")
+
+ def __unicode__(self):
+ start_date = self.start_date and unicode(self.start_date) or u""
+ end_date = self.end_date and unicode(self.end_date) or u""
+ if not start_date and not end_date:
+ return unicode(self.period)
+ return u"%s (%s-%s)" % (self.period, start_date, end_date)
+
+class Unit(GeneralType):
+ order = models.IntegerField(_(u"Order"))
+ parent = models.ForeignKey("Unit", verbose_name=_(u"Parent unit"),
+ blank=True, null=True)
+
+ class Meta:
+ verbose_name = _(u"Type Unit")
+ verbose_name_plural = _(u"Types Unit")
+
+ def __unicode__(self):
+ return self.label
+
+class ActivityType(GeneralType):
+ order = models.IntegerField(_(u"Order"))
+
+ class Meta:
+ verbose_name = _(u"Type Activity")
+ verbose_name_plural = _(u"Types Activity")
+
+ def __unicode__(self):
+ return self.label
+
+class IdentificationType(GeneralType):
+ order = models.IntegerField(_(u"Order"))
+ class Meta:
+ verbose_name = _(u"Type Identification")
+ verbose_name_plural = _(u"Types Identification")
+
+ def __unicode__(self):
+ return self.label
+
+class ContextRecord(BaseHistorizedItem, OwnPerms):
+ TABLE_COLS = ['parcel.town', 'parcel.operation.year',
+ 'parcel.operation.operation_code',
+ 'label', 'unit']
+ if settings.COUNTRY == 'fr':
+ TABLE_COLS.insert(1, 'parcel.operation.code_patriarche')
+ parcel = models.ForeignKey(Parcel, verbose_name=_(u"Parcel"),
+ related_name='context_record')
+ operation = models.ForeignKey(Operation, verbose_name=_(u"Operation"),
+ related_name='context_record')
+ label = models.CharField(_(u"ID"), max_length=200)
+ description = models.TextField(_("Description"), blank=True, null=True)
+ length = models.IntegerField(_(u"Length (cm)"), blank=True, null=True)
+ width = models.IntegerField(_(u"Width (cm)"), blank=True, null=True)
+ thickness = models.IntegerField(_(u"Thickness (cm)"), blank=True, null=True)
+ depth = models.IntegerField(_(u"Depth (cm)"), blank=True, null=True)
+ location = models.CharField(_(u"Location"), blank=True, null=True,
+ max_length=200,
+ help_text=_(u"A short description of the location of the context record"))
+ datings = models.ManyToManyField(Dating)
+ unit = models.ForeignKey(Unit, verbose_name=_(u"Unit"), related_name='+',
+ blank=True, null=True)
+ has_furniture = models.NullBooleanField(u"Has furniture?", blank=True,
+ null=True)
+ filling = models.TextField(_(u"Filling"), blank=True, null=True)
+ interpretation = models.TextField(_(u"Interpretation"), blank=True,
+ null=True)
+ taq = models.IntegerField(_(u"TAQ"), blank=True, null=True,
+ help_text=_("\"Terminus Ante Quem\" the context record can't have been "
+ "created after this date"))
+ taq_estimated = models.IntegerField(_(u"Estimated TAQ"), blank=True,
+ null=True, help_text=_("Estimation of a \"Terminus Ante Quem\""))
+ tpq = models.IntegerField(_(u"TPQ"), blank=True, null=True,
+ help_text=_("\"Terminus Post Quem\" the context record can't have been "
+ " created before this date"))
+ tpq_estimated = models.IntegerField(_(u"Estimated TPQ"), blank=True,
+ null=True, help_text=_("Estimation of a \"Terminus Post Quem\""))
+ identification = models.ForeignKey(IdentificationType, blank=True,
+ null=True, verbose_name=_(u"Identification"),)
+ activity = models.ForeignKey(ActivityType,blank=True, null=True,
+ verbose_name=_(u"Activity"),)
+ history = HistoricalRecords()
+
+ class Meta:
+ verbose_name = _(u"Context Record")
+ verbose_name_plural = _(u"Context Record")
+ permissions = (
+ ("view_own_contextrecord", ugettext(u"Can view own Context Record")),
+ ("add_own_contextrecord", ugettext(u"Can add own Context Record")),
+ ("change_own_contextrecord", ugettext(u"Can change own Context Record")),
+ ("delete_own_contextrecord", ugettext(u"Can delete own Context Record")),
+ )
+
+ def __unicode__(self):
+ return JOINT.join((unicode(self.parcel), self.label))
+
+ def full_label(self):
+ if not self.parcel.operation:
+ return unicode(self)
+ return self._real_label() or self._temp_label()
+
+ def _real_label(self):
+ if not self.parcel.operation.code_patriarche:
+ return
+ return JOINT.join((self.parcel.operation.code_patriarche,
+ self.label))
+
+ def _temp_label(self):
+ if self.parcel.operation.code_patriarche:
+ return
+ return JOINT.join([unicode(lbl) for lbl in [self.parcel.operation.year,
+ self.parcel.operation.operation_code,
+ self.label] if lbl])
+class ContextRecordSource(Source):
+ class Meta:
+ verbose_name = _(u"Context record documentation")
+ verbose_name_plural = _(u"Context record documentations")
+ context_record = models.ForeignKey(ContextRecord,
+ verbose_name=_(u"Context record"), related_name="source")
+
+class MaterialType(GeneralType):
+ recommendation = models.TextField(_(u"Recommendation"))
+ parent = models.ForeignKey("MaterialType", blank=True, null=True,
+ verbose_name=_(u"Parent material"))
+
+ class Meta:
+ verbose_name = _(u"Material type")
+ verbose_name_plural = _(u"Material types")
+
+class BaseItem(BaseHistorizedItem, OwnPerms):
+ label = models.CharField(_(u"ID"), max_length=60)
+ description = models.TextField(_(u"Description"))
+ context_record = models.ForeignKey(ContextRecord,
+ related_name='base_items', verbose_name=_(u"Context Record"))
+ is_isolated = models.NullBooleanField(_(u"Is isolated?"), blank=True,
+ null=True)
+ index = models.IntegerField(u"Index", default=0)
+ material_index = models.IntegerField(u"Material index", default=0)
+ history = HistoricalRecords()
+
+ class Meta:
+ verbose_name = _(u"Base item")
+ verbose_name_plural = _(u"Base items")
+ permissions = (
+ ("view_own_baseitem", ugettext(u"Can view own Base item")),
+ ("add_own_baseitem", ugettext(u"Can add own Base item")),
+ ("change_own_baseitem", ugettext(u"Can change own Base item")),
+ ("delete_own_baseitem", ugettext(u"Can delete own Base item")),
+ )
+
+ def __unicode__(self):
+ return self.label
+
+ def get_last_item(self):
+ #TODO: manage virtuals - property(last_item) ?
+ items = self.item.filter().order_by("-order").all()
+ return items and items[0]
+
+ def full_label(self):
+ return self._real_label() or self._temp_label()
+
+ def material_type_label(self):
+ item = self.get_last_item()
+ lbl = item and (unicode(item.material_type) + unicode(_(":"))) or ''
+ if self.context_record.parcel.operation.code_patriarche:
+ return lbl + JOINT.join([unicode(it) for it in (
+ self.context_record.parcel.operation.code_patriarche,
+ self.context_record.label,
+ self.material_index,
+ self.label)])
+ return lbl + JOINT.join([unicode(it) for it in (
+ self.context_record.parcel.year,
+ self.index,
+ self.context_record.label,
+ self.material_index,
+ self.label)])
+
+
+ def _real_label(self):
+ if not self.context_record.parcel.operation.code_patriarche:
+ return
+ return JOINT.join([unicode(it) for it in (
+ self.context_record.parcel.operation.code_patriarche,
+ self.context_record.label,
+ self.label)])
+
+ def _temp_label(self):
+ if self.context_record.parcel.operation.code_patriarche:
+ return
+ return JOINT.join([unicode(it) for it in (
+ self.context_record.parcel.year,
+ self.index,
+ self.context_record.label,
+ self.label)])
+class Item(BaseHistorizedItem, OwnPerms):
+ TABLE_COLS = ['base_items.context_record.parcel.town',
+ 'base_items.context_record.parcel.operation.year',
+ 'base_items.context_record.parcel.operation.operation_code',
+ 'label', 'material_type', 'dating.period',
+ 'base_items.is_isolated']
+ if settings.COUNTRY == 'fr':
+ TABLE_COLS.insert(1,
+ 'base_items.context_record.parcel.operation.code_patriarche')
+ base_items = models.ManyToManyField(BaseItem, verbose_name=_(u"Base item"),
+ related_name='item')
+ order = models.IntegerField(_(u"Order"))
+ label = models.CharField(_(u"ID"), max_length=60)
+ description = models.TextField(_(u"Description"), blank=True, null=True)
+ material_type = models.ForeignKey(MaterialType,
+ verbose_name = _(u"Material type"))
+ volume = models.FloatField(_(u"Volume (l)"), blank=True, null=True)
+ weight = models.FloatField(_(u"Weight (g)"), blank=True, null=True)
+ item_number = models.IntegerField(_("Item number"), blank=True, null=True)
+ upstream_treatment = models.ForeignKey("Treatment", blank=True, null=True,
+ related_name='downstream_treatment', verbose_name=_("Upstream treatment"))
+ downstream_treatment = models.ForeignKey("Treatment", blank=True, null=True,
+ related_name='upstream_treatment', verbose_name=_("Downstream treatment"))
+ dating = models.ForeignKey(Dating, verbose_name=_(u"Dating"))
+ history = HistoricalRecords()
+
+ class Meta:
+ verbose_name = _(u"Item")
+ verbose_name_plural = _(u"Items")
+ permissions = (
+ ("view_own_item", ugettext(u"Can view own Item")),
+ ("add_own_item", ugettext(u"Can add own Item")),
+ ("change_own_item", ugettext(u"Can change own Item")),
+ ("delete_own_item", ugettext(u"Can delete own Item")),
+ )
+
+ def __unicode__(self):
+ return self.label
+
+ def save(self, *args, **kwargs):
+ if not self.pk:
+ super(Item, self).save(*args, **kwargs)
+ for base_item in self.base_items.all():
+ if not base_item.index:
+ idx = BaseItem.objects.filter(context_record=\
+ base_item.context_record).aggregate(Max('index'))
+ base_item.index = idx and idx['index__max'] + 1 or 1
+ if not base_item.material_index:
+ idx = BaseItem.objects.filter(context_record=\
+ base_item.context_record,
+ item__material_type=self.material_type).aggregate(
+ Max('material_index'))
+ base_item.material_index = idx and \
+ idx['material_index__max'] + 1 or 1
+ base_item.save()
+ super(Item, self).save(*args, **kwargs)
+
+class ItemSource(Source):
+ class Meta:
+ verbose_name = _(u"Item documentation")
+ verbose_name_plural = _(u"Item documentations")
+ item = models.ForeignKey(Item, verbose_name=_(u"Item"),
+ related_name="source")
+
+class ParcelOwner(LightHistorizedItem):
+ owner = models.ForeignKey(Person, verbose_name=_(u"Owner"))
+ parcel = models.ForeignKey(Parcel, verbose_name=_(u"Parcel"))
+ start_date = models.DateField(_(u"Start date"))
+ end_date = models.DateField(_(u"End date"))
+
+ class Meta:
+ verbose_name = _(u"Parcel owner")
+ verbose_name_plural = _(u"Parcel owners")
+
+ def __unicode__(self):
+ return self.owner + JOINT + self.parcel
+
+class WarehouseType(GeneralType):
+ class Meta:
+ verbose_name = _(u"Warehouse type")
+ verbose_name_plural = _(u"Warehouse types")
+
+class Warehouse(Address, OwnPerms):
+ name = models.CharField(_(u"Name"), max_length=40)
+ warehouse_type = models.ForeignKey(WarehouseType,
+ verbose_name=_(u"Warehouse type"))
+ person_in_charge = models.ForeignKey(Person,
+ verbose_name=_(u"Person in charge"), null=True, blank=True)
+ comment = models.TextField(_(u"Comment"), null=True, blank=True)
+
+ class Meta:
+ verbose_name = _(u"Warehouse")
+ verbose_name_plural = _(u"Warehouses")
+ permissions = (
+ ("view_own_warehouse", ugettext(u"Can view own Warehouse")),
+ ("add_own_warehouse", ugettext(u"Can add own Warehouse")),
+ ("change_own_warehouse", ugettext(u"Can change own Warehouse")),
+ ("delete_own_warehouse", ugettext(u"Can delete own Warehouse")),
+ )
+
+ def __unicode__(self):
+ return u"%s (%s)" % (self.name, unicode(self.warehouse_type))
+
+class ActType(GeneralType):
+ TYPE = (('F', _(u'Archaelogical file')),
+ ('O', _(u'Operation')),
+ )
+ intented_to = models.CharField(_(u"Intended to"), max_length=1,
+ choices=TYPE)
+ class Meta:
+ verbose_name = _(u"Act type")
+ verbose_name_plural = _(u"Act types")
+
+class AdministrativeAct(BaseHistorizedItem, OwnPerms):
+ TABLE_COLS = ['act_type', 'associated_file', 'operation',
+ 'associated_file.towns', 'operation.towns']
+ TABLE_COLS_FILE = ['act_type', 'associated_file', 'associated_file.towns',]
+ TABLE_COLS_OPE = ['act_type', 'operation', 'operation.towns']
+ act_type = models.ForeignKey(ActType, verbose_name=_(u"Act type"))
+ in_charge = models.ForeignKey(Person, blank=True, null=True,
+ related_name='+', verbose_name=_(u"Person in charge of the operation"))
+ operator = models.ForeignKey(Organization, blank=True, null=True,
+ verbose_name=_(u"Archaeological preventive operator"))
+ scientific = models.ForeignKey(Person, blank=True, null=True,
+related_name='+', verbose_name=_(u"Person in charge of the scientific part"))
+ signatory = models.ForeignKey(Person, blank=True, null=True,
+ related_name='+', verbose_name=_(u"Signatory"))
+ operation = models.ForeignKey(Operation, blank=True, null=True,
+ related_name='administrative_act', verbose_name=_(u"Operation"))
+ associated_file = models.ForeignKey(File, blank=True, null=True,
+ related_name='administrative_act', verbose_name=_(u"Archaelogical file"))
+ signature_date = models.DateField(_(u"Signature date"))
+ act_object = models.CharField(_(u"Object"), max_length=200)
+ if settings.COUNTRY == 'fr':
+ ref_sra = models.CharField(u"Référence SRA", max_length=15)
+ history = HistoricalRecords()
+
+ class Meta:
+ verbose_name = _(u"Administrative act")
+ verbose_name_plural = _(u"Administrative acts")
+ permissions = (
+("view_own_administrativeact", ugettext(u"Can view own Administrative act")),
+("add_own_administrativeact", ugettext(u"Can add own Administrative act")),
+("change_own_administrativeact", ugettext(u"Can change own Administrative act")),
+("delete_own_administrativeact", ugettext(u"Can delete own Administrative act")),
+ )
+
+ def __unicode__(self):
+ return JOINT.join([unicode(item)
+ for item in [self.operation, self.associated_file, self.act_object]
+ if item])
+
+class ContainerType(GeneralType):
+ length = models.IntegerField(_(u"Length (mm)"))
+ width = models.IntegerField(_(u"Width (mm)"))
+ height = models.IntegerField(_(u"Height (mm)"))
+ volume = models.IntegerField(_(u"Volume (l)"))
+ reference = models.CharField(_(u"Reference"), max_length=30)
+
+ class Meta:
+ verbose_name = _(u"Container type")
+ verbose_name_plural = _(u"Container types")
+
+class Container(LightHistorizedItem):
+ location = models.ForeignKey(Warehouse, verbose_name=_(u"Location"))
+ container_type = models.ForeignKey(ContainerType,
+ verbose_name=_("Container type"))
+ reference = models.CharField(_(u"Reference"), max_length=40)
+ comment = models.TextField(_(u"Comment"))
+
+ class Meta:
+ verbose_name = _(u"Container")
+ verbose_name_plural = _(u"Containers")
+
+if settings.COUNTRY == 'fr':
+ class Arrondissement(models.Model):
+ name = models.CharField(u"Nom", max_length=30)
+ department = models.ForeignKey(Departement, verbose_name=u"Département")
+
+ def __unicode__(self):
+ return JOINT.join((self.name, unicode(self.department)))
+
+ class Canton(models.Model):
+ name = models.CharField(u"Nom", max_length=30)
+ arrondissement = models.ForeignKey(Arrondissement,
+ verbose_name=u"Arrondissement")
+ def __unicode__(self):
+ return JOINT.join((self.name, unicode(self.arrondissement)))
+
+class Town(models.Model):
+ name = models.CharField(_(u"Name"), max_length=100)
+ surface = models.IntegerField(_(u"Surface (m²)"), blank=True, null=True)
+ center = models.PointField(_(u"Localisation"), srid=settings.SRID,
+ blank=True, null=True)
+ if settings.COUNTRY == 'fr':
+ numero_insee = models.CharField(u"Numéro INSEE", max_length=6,
+ unique=True)
+ departement = models.ForeignKey(Departement, verbose_name=u"Département",
+ null=True, blank=True)
+ canton = models.ForeignKey(Canton, verbose_name=u"Canton", null=True,
+ blank=True)
+ objects = models.GeoManager()
+
+ class Meta:
+ verbose_name = _(u"Town")
+ verbose_name_plural = _(u"Towns")
+ if settings.COUNTRY == 'fr':
+ ordering = ['numero_insee']
+
+ def __unicode__(self):
+ if settings.COUNTRY == "fr":
+ return u"%s (%s)" % (self.name, self.numero_insee)
+ return self.name
+
+class TreatmentType(GeneralType):
+ virtual = models.BooleanField(_(u"Virtual"))
+ class Meta:
+ verbose_name = _(u"Treatment type")
+ verbose_name_plural = _(u"Treatment types")
+
+class Treatment(BaseHistorizedItem, OwnPerms):
+ container = models.ForeignKey(Container, verbose_name=_(u"Container"),
+ blank=True, null=True)
+ description = models.TextField(_(u"Description"), 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)
+ person = models.ForeignKey(Person, verbose_name=_(u"Person"),
+ blank=True, null=True)
+ start_date = models.DateField(_(u"Start date"), blank=True, null=True)
+ end_date = models.DateField(_(u"End date"), blank=True, null=True)
+ history = HistoricalRecords()
+
+ class Meta:
+ verbose_name = _(u"Treatment")
+ verbose_name_plural = _(u"Treatments")
+ permissions = (
+ ("view_own_treatment", ugettext(u"Can view own Treatment")),
+ ("add_own_treatment", ugettext(u"Can add own Treatment")),
+ ("change_own_treatment", ugettext(u"Can change own Treatment")),
+ ("delete_own_treatment", ugettext(u"Can delete own Treatment")),
+ )
+
+class TreatmentSource(Source):
+ class Meta:
+ verbose_name = _(u"Treatment documentation")
+ verbose_name_plural = _(u"Treament documentations")
+ treatment = models.ForeignKey(Treatment, verbose_name=_(u"Treatment"),
+ related_name="source")
+
+class Property(LightHistorizedItem):
+ item = models.ForeignKey(Item, verbose_name=_(u"Item"))
+ administrative_act = models.ForeignKey(AdministrativeAct,
+ verbose_name=_(u"Administrative act"))
+ person = models.ForeignKey(Person, verbose_name=_(u"Person"))
+ start_date = models.DateField(_(u"Start date"))
+ end_date = models.DateField(_(u"End date"))
+
+ class Meta:
+ verbose_name = _(u"Property")
+ verbose_name_plural = _(u"Properties")
+
+ def __unicode__(self):
+ return self.person + JOINT + self.item
+