diff options
Diffstat (limited to 'ishtar/ishtar_base/models.py')
-rw-r--r-- | ishtar/ishtar_base/models.py | 1204 |
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 = "› " + + @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 + |