#!/usr/bin/env python # -*- coding: utf-8 -*- # Copyright (C) 2010-2011 Étienne Loks # 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 . # 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"\n" elif c_rank < item.rank: help_items += u"
\n" c_rank = item.rank help_items += u"
%s
%s
" % (item.label, u"
".join(item.comment.split('\n'))) c_rank += 1 if c_rank: help_items += c_rank*u"
" 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")) TABLE_COLS = ['title', 'source_type', '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") TABLE_COLS = ['operation.year', 'operation.operation_code'] + \ Source.TABLE_COLS 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', 'operation.year', '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