diff options
42 files changed, 2147 insertions, 1607 deletions
diff --git a/archaeological_context_records/admin.py b/archaeological_context_records/admin.py new file mode 100644 index 000000000..5985f4462 --- /dev/null +++ b/archaeological_context_records/admin.py @@ -0,0 +1,56 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# Copyright (C) 2012 É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. + +from django.conf import settings +from django.contrib import admin + +from ishtar_common.admin import HistorizedObjectAdmin + +import models + +class DatingAdmin(admin.ModelAdmin): + list_display = ('period', 'start_date', 'end_date', 'dating_type', + 'quality') + list_filter = ("period", 'dating_type', 'quality') + model = models.Dating + +admin.site.register(models.Dating, DatingAdmin) + +class ContextRecordAdmin(HistorizedObjectAdmin): + list_display = ('label', 'length', 'width', + 'thickness', 'depth') + list_filter = ('has_furniture',) + search_fields = ('parcel__operation__name', "datings__period__label") + model = models.ContextRecord + +admin.site.register(models.ContextRecord, ContextRecordAdmin) + +class ContextRecordSourceAdmin(admin.ModelAdmin): + list_display = ('context_record', 'title', 'source_type',) + list_filter = ('source_type',) + search_fields = ('title', ) + model = models.ContextRecordSource + +admin.site.register(models.ContextRecordSource, ContextRecordSourceAdmin) + +basic_models = [models.DatingType, models.DatingQuality, + models.Unit, models.ActivityType, models.IdentificationType] + +for model in basic_models: + admin.site.register(model) diff --git a/ishtar_common/forms_context_records.py b/archaeological_context_records/forms.py index 816782bd8..816782bd8 100644 --- a/ishtar_common/forms_context_records.py +++ b/archaeological_context_records/forms.py diff --git a/archaeological_context_records/ishtar_menu.py b/archaeological_context_records/ishtar_menu.py new file mode 100644 index 000000000..c471a75a8 --- /dev/null +++ b/archaeological_context_records/ishtar_menu.py @@ -0,0 +1,67 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# Copyright (C) 2010-2012 É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. + +from django.utils.translation import ugettext_lazy as _ + +from archaeological_operations.models import Operation +from ishtar_common.menu_base import SectionItem, MenuItem + +import models + +MENU_SECTIONS = [ + (40, SectionItem('record_management', _(u"Context record"), + childs=[ + MenuItem('record_search', _(u"Search"), + model=models.ContextRecord, + access_controls=['view_contextrecord', + 'view_own_contextrecord']), + MenuItem('record_creation', _(u"Creation"), + model=models.ContextRecord, + access_controls=['add_contextrecord', + 'add_own_contextrecord']), + MenuItem('record_modification', _(u"Modification"), + model=models.ContextRecord, + access_controls=['change_contextrecord', + 'change_own_contextrecord']), + MenuItem('record_deletion', _(u"Deletion"), + model=models.ContextRecord, + access_controls=['delete_contextrecord', + 'delete_own_contextrecord']), + SectionItem('record_source', _(u"Documentation"), + childs=[ + MenuItem('record_source_creation', + _(u"Add"), + model=models.ContextRecordSource, + access_controls=['change_contextrecord', + 'change_own_contextrecord']), + MenuItem('record_source_modification', + _(u"Modification"), + model=models.ContextRecordSource, + access_controls=['change_contextrecord', + 'change_own_contextrecord']), + MenuItem('record_source_deletion', + _(u"Deletion"), + model=models.ContextRecordSource, + access_controls=['change_contextrecord', + 'change_own_contextrecord']), + ]) + ]) + ) +] + diff --git a/archaeological_context_records/migrations/0001_initial.py b/archaeological_context_records/migrations/0001_initial.py index 09ee30efe..599a6d4f7 100644 --- a/archaeological_context_records/migrations/0001_initial.py +++ b/archaeological_context_records/migrations/0001_initial.py @@ -8,32 +8,6 @@ from django.db import models class Migration(SchemaMigration): def forwards(self, orm): - # Adding model 'Parcel' - db.create_table('archaeological_context_records_parcel', ( - ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)), - ('history_modifier', self.gf('django.db.models.fields.related.ForeignKey')(related_name='+', to=orm['auth.User'])), - ('history_date', self.gf('django.db.models.fields.DateTimeField')(default=datetime.datetime.now)), - ('associated_file', self.gf('django.db.models.fields.related.ForeignKey')(blank=True, related_name='parcels', null=True, to=orm['archaeological_files.File'])), - ('operation', self.gf('django.db.models.fields.related.ForeignKey')(blank=True, related_name='parcels', null=True, to=orm['archaeological_operations.Operation'])), - ('year', self.gf('django.db.models.fields.IntegerField')(null=True, blank=True)), - ('town', self.gf('django.db.models.fields.related.ForeignKey')(related_name='parcels', to=orm['ishtar_common.Town'])), - ('section', self.gf('django.db.models.fields.CharField')(max_length=4)), - ('parcel_number', self.gf('django.db.models.fields.CharField')(max_length=6)), - )) - db.send_create_signal('archaeological_context_records', ['Parcel']) - - # Adding model 'ParcelOwner' - db.create_table('archaeological_context_records_parcelowner', ( - ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)), - ('history_modifier', self.gf('django.db.models.fields.related.ForeignKey')(related_name='+', to=orm['auth.User'])), - ('history_date', self.gf('django.db.models.fields.DateTimeField')(default=datetime.datetime.now)), - ('owner', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['ishtar_common.Person'])), - ('parcel', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['archaeological_context_records.Parcel'])), - ('start_date', self.gf('django.db.models.fields.DateField')()), - ('end_date', self.gf('django.db.models.fields.DateField')()), - )) - db.send_create_signal('archaeological_context_records', ['ParcelOwner']) - # Adding model 'DatingType' db.create_table('archaeological_context_records_datingtype', ( ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)), @@ -133,7 +107,7 @@ class Migration(SchemaMigration): db.create_table('archaeological_context_records_contextrecord', ( ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)), ('history_modifier', self.gf('django.db.models.fields.related.ForeignKey')(related_name='+', to=orm['auth.User'])), - ('parcel', self.gf('django.db.models.fields.related.ForeignKey')(related_name='context_record', to=orm['archaeological_context_records.Parcel'])), + ('parcel', self.gf('django.db.models.fields.related.ForeignKey')(related_name='context_record', to=orm['archaeological_operations.Parcel'])), ('operation', self.gf('django.db.models.fields.related.ForeignKey')(related_name='context_record', to=orm['archaeological_operations.Operation'])), ('label', self.gf('django.db.models.fields.CharField')(max_length=200)), ('description', self.gf('django.db.models.fields.TextField')(null=True, blank=True)), @@ -185,12 +159,6 @@ class Migration(SchemaMigration): def backwards(self, orm): - # Deleting model 'Parcel' - db.delete_table('archaeological_context_records_parcel') - - # Deleting model 'ParcelOwner' - db.delete_table('archaeological_context_records_parcelowner') - # Deleting model 'DatingType' db.delete_table('archaeological_context_records_datingtype') @@ -251,7 +219,7 @@ class Migration(SchemaMigration): 'length': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}), 'location': ('django.db.models.fields.CharField', [], {'max_length': '200', 'null': 'True', 'blank': 'True'}), 'operation': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'context_record'", 'to': "orm['archaeological_operations.Operation']"}), - 'parcel': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'context_record'", 'to': "orm['archaeological_context_records.Parcel']"}), + 'parcel': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'context_record'", 'to': "orm['archaeological_operations.Parcel']"}), 'taq': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}), 'taq_estimated': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}), 'thickness': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}), @@ -333,28 +301,6 @@ class Migration(SchemaMigration): 'order': ('django.db.models.fields.IntegerField', [], {}), 'txt_idx': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'}) }, - 'archaeological_context_records.parcel': { - 'Meta': {'object_name': 'Parcel'}, - 'associated_file': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'parcels'", 'null': 'True', 'to': "orm['archaeological_files.File']"}), - 'history_date': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), - 'history_modifier': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'+'", 'to': "orm['auth.User']"}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'operation': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'parcels'", 'null': 'True', 'to': "orm['archaeological_operations.Operation']"}), - 'parcel_number': ('django.db.models.fields.CharField', [], {'max_length': '6'}), - 'section': ('django.db.models.fields.CharField', [], {'max_length': '4'}), - 'town': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'parcels'", 'to': "orm['ishtar_common.Town']"}), - 'year': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}) - }, - 'archaeological_context_records.parcelowner': { - 'Meta': {'object_name': 'ParcelOwner'}, - 'end_date': ('django.db.models.fields.DateField', [], {}), - 'history_date': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), - 'history_modifier': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'+'", 'to': "orm['auth.User']"}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'owner': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['ishtar_common.Person']"}), - 'parcel': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['archaeological_context_records.Parcel']"}), - 'start_date': ('django.db.models.fields.DateField', [], {}) - }, 'archaeological_context_records.unit': { 'Meta': {'object_name': 'Unit'}, 'available': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), @@ -456,6 +402,18 @@ class Migration(SchemaMigration): 'label': ('django.db.models.fields.CharField', [], {'max_length': '100'}), 'txt_idx': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'}) }, + 'archaeological_operations.parcel': { + 'Meta': {'object_name': 'Parcel'}, + 'associated_file': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'parcels'", 'null': 'True', 'to': "orm['archaeological_files.File']"}), + 'history_date': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'history_modifier': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'+'", 'to': "orm['auth.User']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'operation': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'parcels'", 'null': 'True', 'to': "orm['archaeological_operations.Operation']"}), + 'parcel_number': ('django.db.models.fields.CharField', [], {'max_length': '6'}), + 'section': ('django.db.models.fields.CharField', [], {'max_length': '4'}), + 'town': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'parcels'", 'to': "orm['ishtar_common.Town']"}), + 'year': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}) + }, 'archaeological_operations.period': { 'Meta': {'object_name': 'Period'}, 'available': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), diff --git a/archaeological_context_records/models.py b/archaeological_context_records/models.py index c47fd3354..75653e78e 100644 --- a/archaeological_context_records/models.py +++ b/archaeological_context_records/models.py @@ -22,53 +22,8 @@ from django.contrib.gis.db import models from django.utils.translation import ugettext_lazy as _, ugettext from ishtar_common.models import GeneralType, BaseHistorizedItem, \ - LightHistorizedItem, HistoricalRecords, OwnPerms, Town, Person, Source -FILES_AVAILABLE = 'archaeological_files' in settings.INSTALLED_APPS -if FILES_AVAILABLE: - from archaeological_files.models import File -from archaeological_operations.models import Operation, Period - -class Parcel(LightHistorizedItem): - if FILES_AVAILABLE: - 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"), blank=True, null=True) - 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): - items = [unicode(self.operation or self.associated_file)] - items += [unicode(item) for item in [self.section, self.parcel_number] - if item] - return JOINT.join(items) - -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 + HistoricalRecords, OwnPerms, Town, Person, Source +from archaeological_operations.models import Operation, Period, Parcel class DatingType(GeneralType): class Meta: diff --git a/archaeological_context_records/urls.py b/archaeological_context_records/urls.py new file mode 100644 index 000000000..c42ae2b02 --- /dev/null +++ b/archaeological_context_records/urls.py @@ -0,0 +1,57 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# Copyright (C) 2010-2012 É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. + +from django.conf.urls.defaults import * +""" +import forms + +# forms +urlpatterns = patterns('', + # Context records + url(r'record_search/(?P<step>.+)$', + forms.record_search_wizard, name='record_search'), + url(r'record_creation/(?P<step>.+)$', + forms.record_creation_wizard, name='record_creation'), + url(r'record_modification/(?P<step>.+)$', + forms.record_modification_wizard, name='record_modification'), + url(r'record_deletion/(?P<step>.+)$', + forms.record_deletion_wizard, name='record_deletion'), + url(r'record_source_creation/(?P<step>.+)$', + forms.record_source_creation_wizard, + name='record_source_creation'), + url(r'record_source_modification/(?P<step>.+)$', + forms.record_source_modification_wizard, + name='record_source_modification'), + url(r'record_source_deletion/(?P<step>.+)$', + forms.record_source_deletion_wizard, + name='record_source_deletion'), +) + +urlpatterns += patterns('archaeological_context_records.views', + url(r'show-contextrecord/(?P<pk>.+)?/(?P<type>.+)?$', + 'show_contextrecord', name='show-contextrecord'), + url(r'get-contextrecord/(?P<type>.+)?$', 'get_contextrecord', + name='get-contextrecord'), + url(r'get-contextrecord-full/(?P<type>.+)?$', + 'get_contextrecord', name='get-contextrecord-full', + kwargs={'full':True}), + url(r'get-contexrecordsource/(?P<type>.+)?$', + 'get_contextrecordsource', name='get-contextrecordsource'), +) +""" diff --git a/archaeological_context_records/views.py b/archaeological_context_records/views.py new file mode 100644 index 000000000..89a45482b --- /dev/null +++ b/archaeological_context_records/views.py @@ -0,0 +1,36 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# Copyright (C) 2010-2012 É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. + +from ishtar_common.views import get_item, show_item, revert_item +import models + +show_contextrecord = show_item(models.ContextRecord, 'contextrecord') +get_contextrecord = get_item(models.ContextRecord, + 'get_contextrecord', 'contextrecord', + extra_request_keys={'parcel__town':'parcel__town__pk', + 'operation__year':'operation__year__contains', + 'datings__period':'datings__period__pk'},) +get_contextrecordsource = get_item(models.ContextRecordSource, + 'get_contextrecordsource', 'contextrecordsource', + extra_request_keys={ + 'context_record__parcel__town':'context_record__parcel__town__pk', + 'context_record__operation__year':'context_record__operation__year', + 'context_record__datings__period':'context_record__datings__period__pk', + 'context_record__unit':'context_record__unit__pk', + }) diff --git a/archaeological_files/admin.py b/archaeological_files/admin.py new file mode 100644 index 000000000..339b19661 --- /dev/null +++ b/archaeological_files/admin.py @@ -0,0 +1,42 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# Copyright (C) 2012 É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. + +from django.conf import settings +from django.contrib import admin + +from ishtar_common.admin import HistorizedObjectAdmin + +import models + +class FileAdmin(HistorizedObjectAdmin): + list_display = ['year', 'numeric_reference', 'internal_reference', + 'end_date', 'file_type', 'general_contractor',] + if settings.COUNTRY == 'fr': + list_display += ['saisine_type', 'reference_number'] + list_filter = ("file_type", "year",) + search_fields = ('towns__name',) + model = models.File + +admin.site.register(models.File, FileAdmin) + +basic_models = [models.FileType, models.PermitType] +if settings.COUNTRY == 'fr': + basic_models.append(models.SaisineType) +for model in basic_models: + admin.site.register(model) diff --git a/ishtar_common/forms_files.py b/archaeological_files/forms.py index c4c460cee..368c57843 100644 --- a/ishtar_common/forms_files.py +++ b/archaeological_files/forms.py @@ -1,6 +1,6 @@ #!/usr/bin/env python # -*- coding: utf-8 -*- -# Copyright (C) 2010-2011 Étienne Loks <etienne.loks_AT_peacefrogsDOTnet> +# Copyright (C) 2010-2012 É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 @@ -23,25 +23,27 @@ Files forms definitions import datetime from django import forms -from django.shortcuts import render_to_response -from django.template import RequestContext +from django.conf import settings from django.core import validators from django.core.exceptions import ObjectDoesNotExist from django.db.models import Max +from django.shortcuts import render_to_response +from django.template import RequestContext from django.utils.translation import ugettext_lazy as _ -from ishtar import settings - +from ishtar_common.models import Town import models -import widgets -from forms import Wizard, FinalForm, FormSet, ClosingWizard, \ +from ishtar_common.views import Wizard +from ishtar_common.forms import FinalForm, FormSet, ClosingWizard, \ ClosingDateFormSelection, SearchWizard, formset_factory, get_now, \ reverse_lazy -from forms_common import TownFormset, ParcelFormSet, \ +from ishtar_common.forms_common import TownFormset, ParcelFormSet, \ get_town_field, get_person_field -from forms_operations import OperationAdministrativeActWizard, \ -AdministrativeActOpeForm, AdministrativeActOpeFormSelection, \ -AdministrativeActDeletionWizard, FinalAdministrativeActDeleteForm, is_preventive +from archaeological_operations.forms import OperationAdministrativeActWizard, \ + AdministrativeActOpeForm, AdministrativeActOpeFormSelection, \ + AdministrativeActDeletionWizard, FinalAdministrativeActDeleteForm, \ + is_preventive +from ishtar_common import widgets class FileWizard(Wizard): model = models.File @@ -67,7 +69,7 @@ class FileWizard(Wizard): for k in qdict.keys(): if k.endswith("town") and qdict[k]: try: - town = models.Town.objects.get(pk=int(qdict[k])) + town = Town.objects.get(pk=int(qdict[k])) towns.append((town.pk, unicode(town))) except (ObjectDoesNotExist, ValueError): pass diff --git a/archaeological_files/ishtar_menu.py b/archaeological_files/ishtar_menu.py index 370320f69..398b43f4b 100644 --- a/archaeological_files/ishtar_menu.py +++ b/archaeological_files/ishtar_menu.py @@ -21,14 +21,13 @@ from django.utils.translation import ugettext_lazy as _ from archaeological_operations.models import Operation from ishtar_common.menu_base import SectionItem, MenuItem -from ishtar_common.models import AdministrativeAct -import models +from archaeological_operations.models import AdministrativeAct -ORDER = 20 +import models MENU_SECTIONS = [ - SectionItem('file_management', _(u"Archaeological file"), + (20, SectionItem('file_management', _(u"Archaeological file"), childs=[ MenuItem('file_search', _(u"Search"), model=models.File, @@ -62,4 +61,5 @@ MENU_SECTIONS = [ access_controls=['delete_file', 'delete_own_file']), ],), ]), + ) ] diff --git a/archaeological_files/models.py b/archaeological_files/models.py index 68a65f6de..90f60fe64 100644 --- a/archaeological_files/models.py +++ b/archaeological_files/models.py @@ -24,7 +24,8 @@ from django.contrib.gis.db import models from django.utils.translation import ugettext_lazy as _, ugettext from ishtar_common.models import GeneralType, BaseHistorizedItem, \ - HistoricalRecords, OwnPerms, Person, Organization, Department, Town + HistoricalRecords, OwnPerms, Person, Organization, Department, Town, \ + Dashboard class FileType(GeneralType): class Meta: @@ -183,3 +184,124 @@ class FileByDepartment(models.Model): class Meta: managed = False db_table = 'file_department' + +class FileDashboard: + def __init__(self): + main_dashboard = Dashboard(File) + + self.total_number = main_dashboard.total_number + + types = File.objects.values('file_type', 'file_type__label') + self.types = types.annotate(number=Count('pk')).order_by('file_type') + + by_year = File.objects.extra( + {'date':"date_trunc('year', creation_date)"}) + self.by_year = by_year.values('date')\ + .annotate(number=Count('pk')).order_by('-date') + + now = datetime.date.today() + limit = datetime.date(now.year, now.month, 1) - datetime.timedelta(365) + by_month = File.objects.filter(creation_date__gt=limit).extra( + {'date':"date_trunc('month', creation_date)"}) + self.by_month = by_month.values('date')\ + .annotate(number=Count('pk')).order_by('-date') + + # research + self.research = {} + prog_type = FileType.objects.get(txt_idx='prog') + researchs = File.objects.filter(file_type=prog_type) + self.research['total_number'] = researchs.count() + by_year = researchs.extra({'date':"date_trunc('year', creation_date)"}) + self.research['by_year'] = by_year.values('date')\ + .annotate(number=Count('pk'))\ + .order_by('-date') + by_month = researchs.filter(creation_date__gt=limit)\ + .extra({'date':"date_trunc('month', creation_date)"}) + self.research['by_month'] = by_month.values('date')\ + .annotate(number=Count('pk'))\ + .order_by('-date') + + self.research['by_dpt'] = FileByDepartment.objects\ + .filter(file__file_type=prog_type, + department__isnull=False)\ + .values('department__label')\ + .annotate(number=Count('file'))\ + .order_by('department__label') + FileTown = File.towns.through + self.research['towns'] = FileTown.objects\ + .filter(file__file_type=prog_type)\ + .values('town__name')\ + .annotate(number=Count('file'))\ + .order_by('-number','town__name')[:10] + + # rescue + rescue_type = FileType.objects.get(txt_idx='preventive') + rescues = File.objects.filter(file_type=rescue_type) + self.rescue = {} + self.rescue['total_number'] = rescues.count() + self.rescue['saisine'] = rescues.values('saisine_type__label')\ + .annotate(number=Count('pk'))\ + .order_by('saisine_type__label') + self.rescue['administrative_act'] = AdministrativeAct.objects\ + .filter(associated_file__isnull=False)\ + .values('act_type__label')\ + .annotate(number=Count('pk'))\ + .order_by('act_type__pk') + + by_year = rescues.extra({'date':"date_trunc('year', creation_date)"}) + self.rescue['by_year'] = by_year.values('date')\ + .annotate(number=Count('pk'))\ + .order_by('-date') + by_month = rescues.filter(creation_date__gt=limit)\ + .extra({'date':"date_trunc('month', creation_date)"}) + self.rescue['by_month'] = by_month.values('date')\ + .annotate(number=Count('pk'))\ + .order_by('-date') + + self.rescue['by_dpt'] = FileByDepartment.objects\ + .filter(file__file_type=rescue_type, + department__isnull=False)\ + .values('department__label')\ + .annotate(number=Count('file'))\ + .order_by('department__label') + self.rescue['towns'] = FileTown.objects\ + .filter(file__file_type=rescue_type)\ + .values('town__name')\ + .annotate(number=Count('file'))\ + .order_by('-number','town__name')[:10] + + self.rescue['with_associated_operation'] = rescues\ + .filter(operations__isnull=False).count() + + self.rescue['with_associated_operation_percent'] = round( + float(self.rescue['with_associated_operation'])\ + /self.rescue['total_number']*100, 2) + + by_year_operationnal = rescues.filter(operations__isnull=False)\ + .extra({'date':"date_trunc('year', creation_date)"}) + by_year_operationnal = by_year_operationnal.values('date')\ + .annotate(number=Count('pk'))\ + .order_by('-date') + percents, idx = [], 0 + for dct in self.rescue['by_year']: + if idx > len(by_year_operationnal): + break + if by_year_operationnal[idx]['date'] != dct['date'] or\ + not dct['number']: + continue + val = round(float(by_year_operationnal[idx]['number'])/\ + dct['number']*100, 2) + percents.append({'date':dct['date'], 'number':val}) + self.rescue['operational_by_year'] = percents + + self.rescue['surface_by_town'] = FileTown.objects\ + .filter(file__file_type=rescue_type)\ + .values('town__name')\ + .annotate(number=Sum('file__total_surface'))\ + .order_by('-number','town__name')[:10] + self.rescue['surface_by_dpt'] = FileByDepartment.objects\ + .filter(file__file_type=rescue_type, + department__isnull=False)\ + .values('department__label')\ + .annotate(number=Sum('file__total_surface'))\ + .order_by('department__label') diff --git a/archaeological_files/urls.py b/archaeological_files/urls.py new file mode 100644 index 000000000..e32ab8294 --- /dev/null +++ b/archaeological_files/urls.py @@ -0,0 +1,64 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# Copyright (C) 2010-2012 É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. + +from django.conf.urls.defaults import * + +""" +import forms + +# forms +urlpatterns = patterns('', + url(r'file_search/(?P<step>.+)$', + forms.file_search_wizard, name='file_search'), + url(r'file_creation/(?P<step>.+)$', + forms.file_creation_wizard, name='file_creation'), + url(r'file_modification/(?P<step>.+)$', + forms.file_modification_wizard, name='file_modification'), + url(r'file_closing/(?P<step>.+)$', + forms.file_closing_wizard, name='file_closing'), + url(r'file_deletion/(?P<step>.+)$', + forms.file_deletion_wizard, name='file_deletion'), + url(r'file_administrativeactfile/(?P<step>.+)$', + forms.file_administrativeactfile_wizard, + name='file_administrativeactfile'), + url(r'file_administrativeactfile_modification/(?P<step>.+)$', + forms.file_administrativeactfile_modification_wizard, + name='file_administrativeactfile_modification'), + url(r'file_administrativeactfile_deletion/(?P<step>.+)$', + forms.file_administrativeactfile_deletion_wizard, + name='file_administrativeactfile_deletion'), +) + +urlpatterns += patterns('archaeological_files.views', + url(r'autocomplete-file/$', 'autocomplete_file', + name='autocomplete-file'), + url(r'get-file/(?P<type>.+)?$', 'get_file', + name='get-file'), + url(r'get-file-full/(?P<type>.+)?$', 'get_file', + name='get-file-full', kwargs={'full':True}), + url(r'get-administrativeactfile/(?P<type>.+)?$', + 'get_administrativeactfile', name='get-administrativeactfile'), + url(r'show-file/(?P<pk>.+)?/(?P<type>.+)?$', 'show_file', + name='show-file'), + url(r'show-historized-file/(?P<pk>.+)?/(?P<date>.+)?$', + 'show_file', name='show-historized-file'), + url(r'revert-file/(?P<pk>.+)/(?P<date>.+)$', + 'revert_file', name='revert-file'), +) +""" diff --git a/archaeological_files/views.py b/archaeological_files/views.py new file mode 100644 index 000000000..02332b629 --- /dev/null +++ b/archaeological_files/views.py @@ -0,0 +1,66 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# Copyright (C) 2010-2012 É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. + +import json + +from django.db.models import Q +from django.http import HttpResponse +from django.shortcuts import render_to_response + +from ishtar_common.views import get_item, show_item, revert_item +import models + +def autocomplete_file(request): + person_types = request.user.ishtaruser.person.person_type + if (not request.user.has_perm('ishtar_common.view_file', models.File) and \ + not request.user.has_perm('ishtar_common.view_own_file', models.File) + and not person_types.rights.filter(wizard__url_name='file_search' + ).count()): + return HttpResponse(mimetype='text/plain') + if not request.GET.get('term'): + return HttpResponse(mimetype='text/plain') + q = request.GET.get('term') + query = Q() + for q in q.split(' '): + extra = Q(internal_reference__icontains=q) | \ + Q(towns__name__icontains=q) + try: + value = int(q) + extra = extra | Q(year=q) | Q(numeric_reference=q) + except ValueError: + pass + query = query & extra + limit = 20 + files = models.File.objects.filter(query)[:limit] + data = json.dumps([{'id':file.pk, 'value':unicode(file)} + for file in files]) + return HttpResponse(data, mimetype='text/plain') + +get_file = get_item(models.File, 'get_file', 'file') +show_file = show_item(models.File, 'file') +revert_file = revert_item(models.File) + +def dashboard_file(request, dct, obj_id=None, *args, **kwargs): + """ + Main dashboard + """ + dct = {'dashboard': models.FileDashboard()} + return render_to_response('dashboard_file.html', dct, + context_instance=RequestContext(request)) + diff --git a/archaeological_finds/admin.py b/archaeological_finds/admin.py new file mode 100644 index 000000000..096f05bf3 --- /dev/null +++ b/archaeological_finds/admin.py @@ -0,0 +1,75 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# Copyright (C) 2012 É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. + +from django.conf import settings +from django.contrib import admin + +from ishtar_common.admin import HistorizedObjectAdmin + +import models + +class BaseFindAdmin(HistorizedObjectAdmin): + list_display = ('label', 'context_record', 'is_isolated') + search_fields = ('label', 'context_record__parcel__operation__name',) + model = models.BaseFind + +admin.site.register(models.BaseFind, BaseFindAdmin) + +class FindAdmin(HistorizedObjectAdmin): + list_display = ('label', 'material_type', 'dating', 'volume', 'weight', + 'find_number',) + list_filter = ('material_type',) + search_fields = ('label', "dating__period__label") + model = models.Find + +admin.site.register(models.Find, FindAdmin) + +class FindSourceAdmin(admin.ModelAdmin): + list_display = ('find', 'title', 'source_type',) + list_filter = ('source_type',) + search_fields = ('title', ) + model = models.FindSource + +admin.site.register(models.FindSource, FindSourceAdmin) + +class PropertyAdmin(admin.ModelAdmin): + list_display = ['find', 'person', 'start_date', 'end_date'] + search_fields = ('find__label', 'person__name') + model = models.Property + +admin.site.register(models.Property, PropertyAdmin) + +class TreatmentAdmin(HistorizedObjectAdmin): + list_display = ('location', 'treatment_type', 'container', 'person') + list_filter = ('treatment_type',) + model = models.Treatment + +admin.site.register(models.Treatment, TreatmentAdmin) + +class TreatmentSourceAdmin(admin.ModelAdmin): + list_display = ('treatment', 'title', 'source_type',) + list_filter = ('source_type',) + search_fields = ('title',) + model = models.TreatmentSource + +admin.site.register(models.TreatmentSource, TreatmentSourceAdmin) + +basic_models = [models.MaterialType, models.TreatmentType] +for model in basic_models: + admin.site.register(model) diff --git a/ishtar_common/forms_items.py b/archaeological_finds/forms.py index b763d94b2..7d64214f5 100644 --- a/ishtar_common/forms_items.py +++ b/archaeological_finds/forms.py @@ -59,9 +59,9 @@ class ItemWizard(Wizard): pass current_item = self.get_current_object(request, storage) if current_item: - base_items = current_item.base_items.all() - if base_items: - return base_items[0].context_record + base_finds = current_item.base_finds.all() + if base_finds: + return base_finds[0].context_record def get_template_context(self, request, storage, form=None): """ @@ -89,18 +89,18 @@ class ItemWizard(Wizard): form_list) dct['order'] = 1 if 'pk' in dct and type(dct['pk']) == models.ContextRecord: - dct['base_items__context_record'] = dct.pop('pk') + dct['base_finds__context_record'] = dct.pop('pk') return dct class ItemForm(forms.Form): form_label = _("Item") - base_model = 'base_items' + base_model = 'base_finds' associated_models = {'material_type':models.MaterialType,} label = forms.CharField(label=_(u"ID"), validators=[validators.MaxLengthValidator(60)]) description = forms.CharField(label=_("Description"), widget=forms.Textarea) - base_items__is_isolated = forms.NullBooleanField(label=_(u"Is isolated?"), + base_finds__is_isolated = forms.NullBooleanField(label=_(u"Is isolated?"), required=False) material_type = forms.ChoiceField(label=_("Material type"), choices=models.MaterialType.get_types()) @@ -137,16 +137,16 @@ item_creation_wizard = ItemWizard([ url_name='item_creation',) class ItemSelect(forms.Form): - base_items__context_record__parcel__town = get_town_field() - base_items__context_record__operation__year = forms.IntegerField( + base_finds__context_record__parcel__town = get_town_field() + base_finds__context_record__operation__year = forms.IntegerField( label=_(u"Year")) - base_items__context_record__operation__code_patriarche = \ + base_finds__context_record__operation__code_patriarche = \ forms.IntegerField(label=_(u"Code PATRIARCHE")) dating__period = forms.ChoiceField(label=_(u"Period"), choices=[]) # TODO search by warehouse material_type = forms.ChoiceField(label=_(u"Material type"), choices=[]) - base_items__item__description = forms.CharField(label=_(u"Description")) - base_items__is_isolated = forms.NullBooleanField(label=_(u"Is isolated?")) + base_finds__item__description = forms.CharField(label=_(u"Description")) + base_finds__is_isolated = forms.NullBooleanField(label=_(u"Is isolated?")) def __init__(self, *args, **kwargs): super(ItemSelect, self).__init__(*args, **kwargs) @@ -424,7 +424,7 @@ item_source_creation_wizard = ItemSourceWizard([ url_name='item_source_creation',) class ItemSourceSelect(SourceSelect): - item__base_items__context_record__operation__year = forms.IntegerField( + item__base_finds__context_record__operation__year = forms.IntegerField( label=_(u"Year of the operation")) item__dating__period = forms.ChoiceField( label=_(u"Period of the archaelogical item"), diff --git a/archaeological_finds/ishtar_menu.py b/archaeological_finds/ishtar_menu.py new file mode 100644 index 000000000..55a498184 --- /dev/null +++ b/archaeological_finds/ishtar_menu.py @@ -0,0 +1,67 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# Copyright (C) 2012 É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. + +from django.utils.translation import ugettext_lazy as _ + +from ishtar_common.menu_base import SectionItem, MenuItem + +import models + +MENU_SECTIONS = [ + (50, SectionItem('find_management', _(u"Find"), + childs=[ + MenuItem('find_search', _(u"Search"), + model=models.Find, + access_controls=['view_item', + 'view_own_item']), + MenuItem('find_creation', _(u"Creation"), + model=models.Find, + access_controls=['add_item', + 'add_own_item']), + MenuItem('find_modification', _(u"Modification"), + model=models.Find, + access_controls=['change_item', + 'change_own_item']), + MenuItem('warehouse_packaging', _(u"Packaging"), + model=models.Treatment, + access_controls=['add_treatment', 'add_own_treatment']), + #MenuItem('treatment_creation', _(u"Add a treatment"), + # model=models.Treatment, + # access_controls=['add_treatment', + # 'add_own_treatment']), + SectionItem('find_source', _(u"Documentation"), + childs=[ + MenuItem('find_source_creation', + _(u"Creation"), + model=models.FindSource, + access_controls=['change_item', + 'change_own_item']), + MenuItem('find_source_modification', + _(u"Modification"), + model=models.FindSource, + access_controls=['change_item', + 'change_own_item']), + MenuItem('find_source_deletion', + _(u"Deletion"), + model=models.FindSource, + access_controls=['change_item', + 'change_own_item']), + ]) + ])) +] diff --git a/archaeological_finds/migrations/0001_initial.py b/archaeological_finds/migrations/0001_initial.py index 03a45ed8d..eab75b03c 100644 --- a/archaeological_finds/migrations/0001_initial.py +++ b/archaeological_finds/migrations/0001_initial.py @@ -20,8 +20,8 @@ class Migration(SchemaMigration): )) db.send_create_signal('archaeological_finds', ['MaterialType']) - # Adding model 'HistoricalBaseItem' - db.create_table('archaeological_finds_historicalbaseitem', ( + # Adding model 'HistoricalBaseFind' + db.create_table('archaeological_finds_historicalbasefind', ( ('id', self.gf('django.db.models.fields.IntegerField')(db_index=True, blank=True)), ('history_modifier_id', self.gf('django.db.models.fields.IntegerField')(db_index=True, null=True, blank=True)), ('label', self.gf('django.db.models.fields.CharField')(max_length=60)), @@ -35,23 +35,23 @@ class Migration(SchemaMigration): ('history_user', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['auth.User'], null=True)), ('history_type', self.gf('django.db.models.fields.CharField')(max_length=1)), )) - db.send_create_signal('archaeological_finds', ['HistoricalBaseItem']) + db.send_create_signal('archaeological_finds', ['HistoricalBaseFind']) - # Adding model 'BaseItem' - db.create_table('archaeological_finds_baseitem', ( + # Adding model 'BaseFind' + db.create_table('archaeological_finds_basefind', ( ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)), ('history_modifier', self.gf('django.db.models.fields.related.ForeignKey')(related_name='+', to=orm['auth.User'])), ('label', self.gf('django.db.models.fields.CharField')(max_length=60)), ('description', self.gf('django.db.models.fields.TextField')()), - ('context_record', self.gf('django.db.models.fields.related.ForeignKey')(related_name='base_items', to=orm['archaeological_context_records.ContextRecord'])), + ('context_record', self.gf('django.db.models.fields.related.ForeignKey')(related_name='base_finds', to=orm['archaeological_context_records.ContextRecord'])), ('is_isolated', self.gf('django.db.models.fields.NullBooleanField')(null=True, blank=True)), ('index', self.gf('django.db.models.fields.IntegerField')(default=0)), ('material_index', self.gf('django.db.models.fields.IntegerField')(default=0)), )) - db.send_create_signal('archaeological_finds', ['BaseItem']) + db.send_create_signal('archaeological_finds', ['BaseFind']) - # Adding model 'HistoricalItem' - db.create_table('archaeological_finds_historicalitem', ( + # Adding model 'HistoricalFind' + db.create_table('archaeological_finds_historicalfind', ( ('id', self.gf('django.db.models.fields.IntegerField')(db_index=True, blank=True)), ('history_modifier_id', self.gf('django.db.models.fields.IntegerField')(db_index=True, null=True, blank=True)), ('order', self.gf('django.db.models.fields.IntegerField')()), @@ -60,7 +60,7 @@ class Migration(SchemaMigration): ('material_type_id', self.gf('django.db.models.fields.IntegerField')(db_index=True, null=True, blank=True)), ('volume', self.gf('django.db.models.fields.FloatField')(null=True, blank=True)), ('weight', self.gf('django.db.models.fields.FloatField')(null=True, blank=True)), - ('item_number', self.gf('django.db.models.fields.IntegerField')(null=True, blank=True)), + ('find_number', self.gf('django.db.models.fields.IntegerField')(null=True, blank=True)), ('upstream_treatment_id', self.gf('django.db.models.fields.IntegerField')(db_index=True, null=True, blank=True)), ('downstream_treatment_id', self.gf('django.db.models.fields.IntegerField')(db_index=True, null=True, blank=True)), ('dating_id', self.gf('django.db.models.fields.IntegerField')(db_index=True, null=True, blank=True)), @@ -70,10 +70,10 @@ class Migration(SchemaMigration): ('history_user', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['auth.User'], null=True)), ('history_type', self.gf('django.db.models.fields.CharField')(max_length=1)), )) - db.send_create_signal('archaeological_finds', ['HistoricalItem']) + db.send_create_signal('archaeological_finds', ['HistoricalFind']) - # Adding model 'Item' - db.create_table('archaeological_finds_item', ( + # Adding model 'Find' + db.create_table('archaeological_finds_find', ( ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)), ('history_modifier', self.gf('django.db.models.fields.related.ForeignKey')(related_name='+', to=orm['auth.User'])), ('order', self.gf('django.db.models.fields.IntegerField')()), @@ -82,41 +82,41 @@ class Migration(SchemaMigration): ('material_type', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['archaeological_finds.MaterialType'])), ('volume', self.gf('django.db.models.fields.FloatField')(null=True, blank=True)), ('weight', self.gf('django.db.models.fields.FloatField')(null=True, blank=True)), - ('item_number', self.gf('django.db.models.fields.IntegerField')(null=True, blank=True)), + ('find_number', self.gf('django.db.models.fields.IntegerField')(null=True, blank=True)), ('upstream_treatment', self.gf('django.db.models.fields.related.ForeignKey')(blank=True, related_name='downstream_treatment', null=True, to=orm['archaeological_finds.Treatment'])), ('downstream_treatment', self.gf('django.db.models.fields.related.ForeignKey')(blank=True, related_name='upstream_treatment', null=True, to=orm['archaeological_finds.Treatment'])), ('dating', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['archaeological_context_records.Dating'])), - ('container', self.gf('django.db.models.fields.related.ForeignKey')(blank=True, related_name='items', null=True, to=orm['archaeological_warehouse.Container'])), + ('container', self.gf('django.db.models.fields.related.ForeignKey')(blank=True, related_name='finds', null=True, to=orm['archaeological_warehouse.Container'])), )) - db.send_create_signal('archaeological_finds', ['Item']) + db.send_create_signal('archaeological_finds', ['Find']) - # Adding M2M table for field base_items on 'Item' - db.create_table('archaeological_finds_item_base_items', ( + # Adding M2M table for field base_finds on 'Find' + db.create_table('archaeological_finds_find_base_finds', ( ('id', models.AutoField(verbose_name='ID', primary_key=True, auto_created=True)), - ('item', models.ForeignKey(orm['archaeological_finds.item'], null=False)), - ('baseitem', models.ForeignKey(orm['archaeological_finds.baseitem'], null=False)) + ('find', models.ForeignKey(orm['archaeological_finds.find'], null=False)), + ('basefind', models.ForeignKey(orm['archaeological_finds.basefind'], null=False)) )) - db.create_unique('archaeological_finds_item_base_items', ['item_id', 'baseitem_id']) + db.create_unique('archaeological_finds_find_base_finds', ['find_id', 'basefind_id']) - # Adding model 'ItemSource' - db.create_table('archaeological_finds_itemsource', ( + # Adding model 'FindSource' + db.create_table('archaeological_finds_findsource', ( ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)), ('title', self.gf('django.db.models.fields.CharField')(max_length=200)), ('source_type', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['ishtar_common.SourceType'])), ('associated_url', self.gf('django.db.models.fields.URLField')(max_length=200, null=True, blank=True)), ('receipt_date', self.gf('django.db.models.fields.DateField')(null=True, blank=True)), ('creation_date', self.gf('django.db.models.fields.DateField')(null=True, blank=True)), - ('item', self.gf('django.db.models.fields.related.ForeignKey')(related_name='source', to=orm['archaeological_finds.Item'])), + ('find', self.gf('django.db.models.fields.related.ForeignKey')(related_name='source', to=orm['archaeological_finds.Find'])), )) - db.send_create_signal('archaeological_finds', ['ItemSource']) + db.send_create_signal('archaeological_finds', ['FindSource']) - # Adding M2M table for field authors on 'ItemSource' - db.create_table('archaeological_finds_itemsource_authors', ( + # Adding M2M table for field authors on 'FindSource' + db.create_table('archaeological_finds_findsource_authors', ( ('id', models.AutoField(verbose_name='ID', primary_key=True, auto_created=True)), - ('itemsource', models.ForeignKey(orm['archaeological_finds.itemsource'], null=False)), + ('findsource', models.ForeignKey(orm['archaeological_finds.findsource'], null=False)), ('author', models.ForeignKey(orm['ishtar_common.author'], null=False)) )) - db.create_unique('archaeological_finds_itemsource_authors', ['itemsource_id', 'author_id']) + db.create_unique('archaeological_finds_findsource_authors', ['findsource_id', 'author_id']) # Adding model 'TreatmentType' db.create_table('archaeological_finds_treatmenttype', ( @@ -186,7 +186,7 @@ class Migration(SchemaMigration): ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)), ('history_modifier', self.gf('django.db.models.fields.related.ForeignKey')(related_name='+', to=orm['auth.User'])), ('history_date', self.gf('django.db.models.fields.DateTimeField')(default=datetime.datetime.now)), - ('item', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['archaeological_finds.Item'])), + ('find', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['archaeological_finds.Find'])), ('administrative_act', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['archaeological_operations.AdministrativeAct'])), ('person', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['ishtar_common.Person'])), ('start_date', self.gf('django.db.models.fields.DateField')()), @@ -199,26 +199,26 @@ class Migration(SchemaMigration): # Deleting model 'MaterialType' db.delete_table('archaeological_finds_materialtype') - # Deleting model 'HistoricalBaseItem' - db.delete_table('archaeological_finds_historicalbaseitem') + # Deleting model 'HistoricalBaseFind' + db.delete_table('archaeological_finds_historicalbasefind') - # Deleting model 'BaseItem' - db.delete_table('archaeological_finds_baseitem') + # Deleting model 'BaseFind' + db.delete_table('archaeological_finds_basefind') - # Deleting model 'HistoricalItem' - db.delete_table('archaeological_finds_historicalitem') + # Deleting model 'HistoricalFind' + db.delete_table('archaeological_finds_historicalfind') - # Deleting model 'Item' - db.delete_table('archaeological_finds_item') + # Deleting model 'Find' + db.delete_table('archaeological_finds_find') - # Removing M2M table for field base_items on 'Item' - db.delete_table('archaeological_finds_item_base_items') + # Removing M2M table for field base_finds on 'Find' + db.delete_table('archaeological_finds_find_base_finds') - # Deleting model 'ItemSource' - db.delete_table('archaeological_finds_itemsource') + # Deleting model 'FindSource' + db.delete_table('archaeological_finds_findsource') - # Removing M2M table for field authors on 'ItemSource' - db.delete_table('archaeological_finds_itemsource_authors') + # Removing M2M table for field authors on 'FindSource' + db.delete_table('archaeological_finds_findsource_authors') # Deleting model 'TreatmentType' db.delete_table('archaeological_finds_treatmenttype') @@ -265,7 +265,7 @@ class Migration(SchemaMigration): 'length': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}), 'location': ('django.db.models.fields.CharField', [], {'max_length': '200', 'null': 'True', 'blank': 'True'}), 'operation': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'context_record'", 'to': "orm['archaeological_operations.Operation']"}), - 'parcel': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'context_record'", 'to': "orm['archaeological_context_records.Parcel']"}), + 'parcel': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'context_record'", 'to': "orm['archaeological_operations.Parcel']"}), 'taq': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}), 'taq_estimated': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}), 'thickness': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}), @@ -308,18 +308,6 @@ class Migration(SchemaMigration): 'order': ('django.db.models.fields.IntegerField', [], {}), 'txt_idx': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'}) }, - 'archaeological_context_records.parcel': { - 'Meta': {'object_name': 'Parcel'}, - 'associated_file': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'parcels'", 'null': 'True', 'to': "orm['archaeological_files.File']"}), - 'history_date': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), - 'history_modifier': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'+'", 'to': "orm['auth.User']"}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'operation': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'parcels'", 'null': 'True', 'to': "orm['archaeological_operations.Operation']"}), - 'parcel_number': ('django.db.models.fields.CharField', [], {'max_length': '6'}), - 'section': ('django.db.models.fields.CharField', [], {'max_length': '4'}), - 'town': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'parcels'", 'to': "orm['ishtar_common.Town']"}), - 'year': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}) - }, 'archaeological_context_records.unit': { 'Meta': {'object_name': 'Unit'}, 'available': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), @@ -382,9 +370,9 @@ class Migration(SchemaMigration): 'label': ('django.db.models.fields.CharField', [], {'max_length': '100'}), 'txt_idx': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'}) }, - 'archaeological_finds.baseitem': { - 'Meta': {'object_name': 'BaseItem'}, - 'context_record': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'base_items'", 'to': "orm['archaeological_context_records.ContextRecord']"}), + 'archaeological_finds.basefind': { + 'Meta': {'object_name': 'BaseFind'}, + 'context_record': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'base_finds'", 'to': "orm['archaeological_context_records.ContextRecord']"}), 'description': ('django.db.models.fields.TextField', [], {}), 'history_modifier': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'+'", 'to': "orm['auth.User']"}), 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), @@ -393,8 +381,36 @@ class Migration(SchemaMigration): 'label': ('django.db.models.fields.CharField', [], {'max_length': '60'}), 'material_index': ('django.db.models.fields.IntegerField', [], {'default': '0'}) }, - 'archaeological_finds.historicalbaseitem': { - 'Meta': {'ordering': "('-history_date', '-history_id')", 'object_name': 'HistoricalBaseItem'}, + 'archaeological_finds.find': { + 'Meta': {'object_name': 'Find'}, + 'base_finds': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'find'", 'symmetrical': 'False', 'to': "orm['archaeological_finds.BaseFind']"}), + 'container': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'finds'", 'null': 'True', 'to': "orm['archaeological_warehouse.Container']"}), + 'dating': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['archaeological_context_records.Dating']"}), + 'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), + 'downstream_treatment': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'upstream_treatment'", 'null': 'True', 'to': "orm['archaeological_finds.Treatment']"}), + 'find_number': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}), + 'history_modifier': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'+'", 'to': "orm['auth.User']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'label': ('django.db.models.fields.CharField', [], {'max_length': '60'}), + 'material_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['archaeological_finds.MaterialType']"}), + 'order': ('django.db.models.fields.IntegerField', [], {}), + 'upstream_treatment': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'downstream_treatment'", 'null': 'True', 'to': "orm['archaeological_finds.Treatment']"}), + 'volume': ('django.db.models.fields.FloatField', [], {'null': 'True', 'blank': 'True'}), + 'weight': ('django.db.models.fields.FloatField', [], {'null': 'True', 'blank': 'True'}) + }, + 'archaeological_finds.findsource': { + 'Meta': {'object_name': 'FindSource'}, + 'associated_url': ('django.db.models.fields.URLField', [], {'max_length': '200', 'null': 'True', 'blank': 'True'}), + 'authors': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['ishtar_common.Author']", 'symmetrical': 'False'}), + 'creation_date': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}), + 'find': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'source'", 'to': "orm['archaeological_finds.Find']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'receipt_date': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}), + 'source_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['ishtar_common.SourceType']"}), + 'title': ('django.db.models.fields.CharField', [], {'max_length': '200'}) + }, + 'archaeological_finds.historicalbasefind': { + 'Meta': {'ordering': "('-history_date', '-history_id')", 'object_name': 'HistoricalBaseFind'}, 'context_record_id': ('django.db.models.fields.IntegerField', [], {'db_index': 'True', 'null': 'True', 'blank': 'True'}), 'description': ('django.db.models.fields.TextField', [], {}), 'history_date': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), @@ -408,19 +424,19 @@ class Migration(SchemaMigration): 'label': ('django.db.models.fields.CharField', [], {'max_length': '60'}), 'material_index': ('django.db.models.fields.IntegerField', [], {'default': '0'}) }, - 'archaeological_finds.historicalitem': { - 'Meta': {'ordering': "('-history_date', '-history_id')", 'object_name': 'HistoricalItem'}, + 'archaeological_finds.historicalfind': { + 'Meta': {'ordering': "('-history_date', '-history_id')", 'object_name': 'HistoricalFind'}, 'container_id': ('django.db.models.fields.IntegerField', [], {'db_index': 'True', 'null': 'True', 'blank': 'True'}), 'dating_id': ('django.db.models.fields.IntegerField', [], {'db_index': 'True', 'null': 'True', 'blank': 'True'}), 'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), 'downstream_treatment_id': ('django.db.models.fields.IntegerField', [], {'db_index': 'True', 'null': 'True', 'blank': 'True'}), + 'find_number': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}), 'history_date': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), 'history_id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), 'history_modifier_id': ('django.db.models.fields.IntegerField', [], {'db_index': 'True', 'null': 'True', 'blank': 'True'}), 'history_type': ('django.db.models.fields.CharField', [], {'max_length': '1'}), 'history_user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']", 'null': 'True'}), 'id': ('django.db.models.fields.IntegerField', [], {'db_index': 'True', 'blank': 'True'}), - 'item_number': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}), 'label': ('django.db.models.fields.CharField', [], {'max_length': '60'}), 'material_type_id': ('django.db.models.fields.IntegerField', [], {'db_index': 'True', 'null': 'True', 'blank': 'True'}), 'order': ('django.db.models.fields.IntegerField', [], {}), @@ -444,34 +460,6 @@ class Migration(SchemaMigration): 'start_date': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}), 'treatment_type_id': ('django.db.models.fields.IntegerField', [], {'db_index': 'True', 'null': 'True', 'blank': 'True'}) }, - 'archaeological_finds.item': { - 'Meta': {'object_name': 'Item'}, - 'base_items': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'item'", 'symmetrical': 'False', 'to': "orm['archaeological_finds.BaseItem']"}), - 'container': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'items'", 'null': 'True', 'to': "orm['archaeological_warehouse.Container']"}), - 'dating': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['archaeological_context_records.Dating']"}), - 'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), - 'downstream_treatment': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'upstream_treatment'", 'null': 'True', 'to': "orm['archaeological_finds.Treatment']"}), - 'history_modifier': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'+'", 'to': "orm['auth.User']"}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'item_number': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}), - 'label': ('django.db.models.fields.CharField', [], {'max_length': '60'}), - 'material_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['archaeological_finds.MaterialType']"}), - 'order': ('django.db.models.fields.IntegerField', [], {}), - 'upstream_treatment': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'downstream_treatment'", 'null': 'True', 'to': "orm['archaeological_finds.Treatment']"}), - 'volume': ('django.db.models.fields.FloatField', [], {'null': 'True', 'blank': 'True'}), - 'weight': ('django.db.models.fields.FloatField', [], {'null': 'True', 'blank': 'True'}) - }, - 'archaeological_finds.itemsource': { - 'Meta': {'object_name': 'ItemSource'}, - 'associated_url': ('django.db.models.fields.URLField', [], {'max_length': '200', 'null': 'True', 'blank': 'True'}), - 'authors': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['ishtar_common.Author']", 'symmetrical': 'False'}), - 'creation_date': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'item': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'source'", 'to': "orm['archaeological_finds.Item']"}), - 'receipt_date': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}), - 'source_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['ishtar_common.SourceType']"}), - 'title': ('django.db.models.fields.CharField', [], {'max_length': '200'}) - }, 'archaeological_finds.materialtype': { 'Meta': {'object_name': 'MaterialType'}, 'available': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), @@ -486,10 +474,10 @@ class Migration(SchemaMigration): 'Meta': {'object_name': 'Property'}, 'administrative_act': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['archaeological_operations.AdministrativeAct']"}), 'end_date': ('django.db.models.fields.DateField', [], {}), + 'find': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['archaeological_finds.Find']"}), 'history_date': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), 'history_modifier': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'+'", 'to': "orm['auth.User']"}), 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'item': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['archaeological_finds.Item']"}), 'person': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['ishtar_common.Person']"}), 'start_date': ('django.db.models.fields.DateField', [], {}) }, @@ -588,6 +576,18 @@ class Migration(SchemaMigration): 'label': ('django.db.models.fields.CharField', [], {'max_length': '100'}), 'txt_idx': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'}) }, + 'archaeological_operations.parcel': { + 'Meta': {'object_name': 'Parcel'}, + 'associated_file': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'parcels'", 'null': 'True', 'to': "orm['archaeological_files.File']"}), + 'history_date': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'history_modifier': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'+'", 'to': "orm['auth.User']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'operation': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'parcels'", 'null': 'True', 'to': "orm['archaeological_operations.Operation']"}), + 'parcel_number': ('django.db.models.fields.CharField', [], {'max_length': '6'}), + 'section': ('django.db.models.fields.CharField', [], {'max_length': '4'}), + 'town': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'parcels'", 'to': "orm['ishtar_common.Town']"}), + 'year': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}) + }, 'archaeological_operations.period': { 'Meta': {'object_name': 'Period'}, 'available': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), diff --git a/archaeological_finds/models.py b/archaeological_finds/models.py index cea9a35f1..c61e22e68 100644 --- a/archaeological_finds/models.py +++ b/archaeological_finds/models.py @@ -39,11 +39,11 @@ class MaterialType(GeneralType): verbose_name = _(u"Material type") verbose_name_plural = _(u"Material types") -class BaseItem(BaseHistorizedItem, OwnPerms): +class BaseFind(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")) + related_name='base_finds', verbose_name=_(u"Context Record")) is_isolated = models.NullBooleanField(_(u"Is isolated?"), blank=True, null=True) index = models.IntegerField(u"Index", default=0) @@ -51,40 +51,40 @@ class BaseItem(BaseHistorizedItem, OwnPerms): history = HistoricalRecords() class Meta: - verbose_name = _(u"Base item") - verbose_name_plural = _(u"Base items") + verbose_name = _(u"Base find") + verbose_name_plural = _(u"Base finds") 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")), + ("view_own_basefind", ugettext(u"Can view own Base find")), + ("add_own_basefind", ugettext(u"Can add own Base find")), + ("change_own_basefind", ugettext(u"Can change own Base find")), + ("delete_own_basefind", ugettext(u"Can delete own Base find")), ) 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 get_last_find(self): + #TODO: manage virtuals - property(last_find) ? + finds = self.find.filter().order_by("-order").all() + return finds and finds[0] def full_label(self): return self._real_label() or self._temp_label() def material_type_label(self): - item = self.get_last_item() - items = [item and unicode(item.material_type) or ''] + find = self.get_last_find() + finds = [find and unicode(find.material_type) or ''] ope = self.context_record.operation - items += [ope.code_patriarche or \ + finds += [ope.code_patriarche or \ (unicode(ope.year) + "-" + unicode(ope.operation_code))] - items += [self.context_record.label, unicode(self.material_index)] - return JOINT.join(items) + finds += [self.context_record.label, unicode(self.material_index)] + return JOINT.join(finds) def _real_label(self): if not self.context_record.parcel.operation.code_patriarche: return - item = self.get_last_item() - lbl = item.label or self.label + find = self.get_last_find() + lbl = find.label or self.label return JOINT.join([unicode(it) for it in ( self.context_record.parcel.operation.code_patriarche, self.context_record.label, @@ -93,25 +93,25 @@ class BaseItem(BaseHistorizedItem, OwnPerms): def _temp_label(self): if self.context_record.parcel.operation.code_patriarche: return - item = self.get_last_item() - lbl = item.label or self.label + find = self.get_last_find() + lbl = find.label or self.label return JOINT.join([unicode(it) for it in ( self.context_record.parcel.year, self.index, self.context_record.label, lbl) if it]) -class Item(BaseHistorizedItem, OwnPerms): +class Find(BaseHistorizedItem, OwnPerms): TABLE_COLS = ['label', 'material_type', 'dating.period', - 'base_items.context_record.parcel.town', - 'base_items.context_record.parcel.operation.year', - 'base_items.context_record.parcel.operation.operation_code', - 'base_items.is_isolated'] + 'base_finds.context_record.parcel.town', + 'base_finds.context_record.parcel.operation.year', + 'base_finds.context_record.parcel.operation.operation_code', + 'base_finds.is_isolated'] if settings.COUNTRY == 'fr': TABLE_COLS.insert(6, - 'base_items.context_record.parcel.operation.code_patriarche') - base_items = models.ManyToManyField(BaseItem, verbose_name=_(u"Base item"), - related_name='item') + 'base_finds.context_record.parcel.operation.code_patriarche') + base_finds = models.ManyToManyField(BaseFind, verbose_name=_(u"Base find"), + related_name='find') order = models.IntegerField(_(u"Order")) label = models.CharField(_(u"ID"), max_length=60) description = models.TextField(_(u"Description"), blank=True, null=True) @@ -119,7 +119,7 @@ class Item(BaseHistorizedItem, OwnPerms): 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) + find_number = models.IntegerField(_("Find 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, @@ -127,15 +127,15 @@ class Item(BaseHistorizedItem, OwnPerms): dating = models.ForeignKey(Dating, verbose_name=_(u"Dating")) if WAREHOUSE_AVAILABLE: container = models.ForeignKey(Container, verbose_name=_(u"Container"), - blank=True, null=True, related_name='items') + blank=True, null=True, related_name='finds') history = HistoricalRecords() @classmethod def get_years(cls): years = set() - items = cls.objects.filter(downstream_treatment__isnull=True) - for item in items: - bi = item.base_items.all() + finds = cls.objects.filter(downstream_treatment__isnull=True) + for find in finds: + bi = find.base_finds.all() if not bi: continue bi = bi[0] @@ -146,14 +146,14 @@ class Item(BaseHistorizedItem, OwnPerms): @classmethod def get_by_year(cls, year): return cls.objects.filter(downstream_treatment__isnull=True, - base_items__context_record__operation__start_date__year=year) + base_finds__context_record__operation__start_date__year=year) @classmethod def get_operations(cls): operations = set() - items = cls.objects.filter(downstream_treatment__isnull=True) - for item in items: - bi = item.base_items.all() + finds = cls.objects.filter(downstream_treatment__isnull=True) + for find in finds: + bi = find.base_finds.all() if not bi: continue bi = bi[0] @@ -164,7 +164,7 @@ class Item(BaseHistorizedItem, OwnPerms): @classmethod def get_by_operation(cls, operation_id): return cls.objects.filter(downstream_treatment__isnull=True, - base_items__context_record__operation__pk=operation_id) + base_finds__context_record__operation__pk=operation_id) @classmethod def get_total_number(cls): @@ -173,23 +173,23 @@ class Item(BaseHistorizedItem, OwnPerms): def duplicate(self, user): dct = dict([(attr, getattr(self, attr)) for attr in ('order', 'label', 'description', 'material_type', 'volume', 'weight', - 'item_number', 'dating')]) + 'find_number', 'dating')]) dct['order'] += 1 dct['history_modifier'] = user new = self.__class__(**dct) new.save() - for base_item in self.base_items.all(): - new.base_items.add(base_item) + for base_find in self.base_finds.all(): + new.base_finds.add(base_find) return new class Meta: - verbose_name = _(u"Item") - verbose_name_plural = _(u"Items") + verbose_name = _(u"Find") + verbose_name_plural = _(u"Finds") 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")), + ("view_own_find", ugettext(u"Can view own Find")), + ("add_own_find", ugettext(u"Can add own Find")), + ("change_own_find", ugettext(u"Can change own Find")), + ("delete_own_find", ugettext(u"Can delete own Find")), ) def __unicode__(self): @@ -197,27 +197,27 @@ class Item(BaseHistorizedItem, OwnPerms): 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( + super(Find, self).save(*args, **kwargs) + for base_find in self.base_finds.all(): + if not base_find.index: + idx = BaseFind.objects.filter(context_record=\ + base_find.context_record).aggregate(Max('index')) + base_find.index = idx and idx['index__max'] + 1 or 1 + if not base_find.material_index: + idx = BaseFind.objects.filter(context_record=\ + base_find.context_record, + find__material_type=self.material_type).aggregate( Max('material_index')) - base_item.material_index = idx and \ + base_find.material_index = idx and \ idx['material_index__max'] + 1 or 1 - base_item.save() - super(Item, self).save(*args, **kwargs) + base_find.save() + super(Find, self).save(*args, **kwargs) -class ItemSource(Source): +class FindSource(Source): class Meta: - verbose_name = _(u"Item documentation") - verbose_name_plural = _(u"Item documentations") - item = models.ForeignKey(Item, verbose_name=_(u"Item"), + verbose_name = _(u"Find documentation") + verbose_name_plural = _(u"Find documentations") + find = models.ForeignKey(Find, verbose_name=_(u"Find"), related_name="source") class TreatmentType(GeneralType): @@ -265,7 +265,7 @@ class TreatmentSource(Source): related_name="source") class Property(LightHistorizedItem): - item = models.ForeignKey(Item, verbose_name=_(u"Item")) + find = models.ForeignKey(Find, verbose_name=_(u"Find")) administrative_act = models.ForeignKey(AdministrativeAct, verbose_name=_(u"Administrative act")) person = models.ForeignKey(Person, verbose_name=_(u"Person")) @@ -277,5 +277,5 @@ class Property(LightHistorizedItem): verbose_name_plural = _(u"Properties") def __unicode__(self): - return self.person + JOINT + self.item + return self.person + JOINT + self.find diff --git a/archaeological_finds/urls.py b/archaeological_finds/urls.py new file mode 100644 index 000000000..4c733436d --- /dev/null +++ b/archaeological_finds/urls.py @@ -0,0 +1,52 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# Copyright (C) 2010-2012 É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. + +from django.conf.urls.defaults import * + +""" +import forms + +# forms +urlpatterns = patterns('', + url(r'item_search/(?P<step>.+)$', + forms.item_search_wizard, name='item_search'), + url(r'item_creation/(?P<step>.+)$', + forms.item_creation_wizard, name='item_creation'), + url(r'item_modification/(?P<step>.+)$', + forms.item_modification_wizard, name='item_modification'), + url(r'item_source_creation/(?P<step>.+)$', + forms.item_source_creation_wizard, + name='item_source_creation'), + url(r'item_source_modification/(?P<step>.+)$', + forms.item_source_modification_wizard, + name='item_source_modification'), + url(r'item_source_deletion/(?P<step>.+)$', + forms.item_source_deletion_wizard, + name='item_source_deletion'), +) + +urlpatterns += patterns('archaeological_finds.views', + url(r'get-find/(?P<type>.+)?$', 'get_find', + name='get-find'), + url(r'get-find-full/(?P<type>.+)?$', 'get_find', + name='get-find-full', kwargs={'full':True}), + url(r'get-findsource/(?P<type>.+)?$', + 'get_findsource', name='get-findsource'), +) +""" diff --git a/archaeological_finds/views.py b/archaeological_finds/views.py new file mode 100644 index 000000000..af428d59e --- /dev/null +++ b/archaeological_finds/views.py @@ -0,0 +1,43 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# Copyright (C) 2010-2012 É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. + +from ishtar_common.views import get_item, show_item, revert_item +import models + +get_find = get_item(models.Find, + 'get_find', 'find', + bool_fields = ['base_finds__is_isolated'], + base_request={'downstream_treatment__isnull':True}, + extra_request_keys={ +'base_finds__context_record__parcel__town': + 'base_finds__context_record__parcel__town', +'base_finds__context_record__operation__year': + 'base_finds__context_record__operation__year__contains', +'base_finds__context_record__operation__code_patriarche': + 'base_finds__context_record__operation__code_patriarche', +'dating__period':'dating__period__pk', +'base_finds__find__description':'base_finds__find__description__icontains', +'base_finds__is_isolated':'base_finds__is_isolated'}) +get_findsource = get_item(models.FindSource, + 'get_findsource', 'findsource', + extra_request_keys={ +'find__context_record__operation__year':'find__context_record__operation__year', +'find__dating__period':'find__dating__period__pk', +'find__description':'find__description__icontains', + }) diff --git a/archaeological_operations/admin.py b/archaeological_operations/admin.py new file mode 100644 index 000000000..de8b47edc --- /dev/null +++ b/archaeological_operations/admin.py @@ -0,0 +1,72 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# Copyright (C) 2012 É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. + +from django.conf import settings +from django.contrib import admin + +from ishtar_common.admin import HistorizedObjectAdmin + +import models + +class AdministrativeActAdmin(HistorizedObjectAdmin): + list_display = ('operation', 'act_type', 'signature_date') + list_filter = ('act_type',) + search_fields = ('operation__name',) + model = models.AdministrativeAct + +admin.site.register(models.AdministrativeAct, AdministrativeActAdmin) + +class PeriodAdmin(admin.ModelAdmin): + list_display = ('label', 'start_date', 'end_date', 'parent') + model = models.Period + +admin.site.register(models.Period, PeriodAdmin) + +class OperationAdmin(HistorizedObjectAdmin): + list_display = ['year', 'operation_code', 'start_date', + 'excavation_end_date', 'end_date', + 'operation_type'] + list_filter = ("year", "operation_type",) + search_fields = ['towns__name', 'operation_code'] + if settings.COUNTRY == 'fr': + list_display += ['code_patriarche'] + search_fields += ['code_patriarche'] + model = models.Operation + +admin.site.register(models.Operation, OperationAdmin) + +class OperationSourceAdmin(admin.ModelAdmin): + list_display = ('operation', 'title', 'source_type',) + list_filter = ('source_type',) + search_fields = ('title', 'operation__name') + model = models.OperationSource + +admin.site.register(models.OperationSource, OperationSourceAdmin) + +class ParcelAdmin(HistorizedObjectAdmin): + list_display = ('section', 'parcel_number', 'operation', 'associated_file') + search_fields = ('operation__name',) + model = models.Parcel + +admin.site.register(models.Parcel, ParcelAdmin) + +basic_models = [models.OperationType, models.RemainType, models.ActType, + models.ParcelOwner] +for model in basic_models: + admin.site.register(model) diff --git a/ishtar_common/forms_operations.py b/archaeological_operations/forms.py index e163b5869..d4152d4fa 100644 --- a/ishtar_common/forms_operations.py +++ b/archaeological_operations/forms.py @@ -1,6 +1,6 @@ #!/usr/bin/env python # -*- coding: utf-8 -*- -# Copyright (C) 2010-2011 Étienne Loks <etienne.loks_AT_peacefrogsDOTnet> +# Copyright (C) 2010-2012 É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 @@ -23,6 +23,7 @@ Operations forms definitions import datetime from django import forms +from django.conf import settings from django.shortcuts import render_to_response from django.template import RequestContext from django.core import validators @@ -30,15 +31,13 @@ from django.core.exceptions import ObjectDoesNotExist from django.db.models import Max from django.utils.translation import ugettext_lazy as _ -from ishtar import settings - import models import widgets -from forms import Wizard, FinalForm, FormSet, SearchWizard, ClosingWizard, \ - ClosingDateFormSelection, DeletionWizard, formset_factory, get_now, \ - reverse_lazy, get_form_selection -from forms_common import TownForm, TownFormSet, TownFormset, ParcelFormSet, \ - ParcelForm, AuthorFormset, SourceForm, SourceWizard, SourceSelect, \ +from ishtar_common.forms import Wizard, FinalForm, FormSet, SearchWizard, \ + ClosingWizard, ClosingDateFormSelection, DeletionWizard, formset_factory, \ + get_now, reverse_lazy, get_form_selection +from ishtar_common.forms_common import TownForm, TownFormSet, TownFormset, \ + AuthorFormset, SourceForm, SourceWizard, SourceSelect, \ SourceDeletionForm, get_town_field def is_preventive(form_name, model, type_key='operation_type', key=''): @@ -57,6 +56,57 @@ def is_preventive(form_name, model, type_key='operation_type', key=''): return False return func +class ParcelForm(forms.Form): + form_label = _("Parcels") + base_model = 'parcel' + associated_models = {'parcel':models.Parcel, 'town':models.Town} + town = forms.ChoiceField(label=_("Town"), choices=(), required=False, + validators=[models.valid_id(models.Town)]) + section = forms.CharField(label=_(u"Section"), required=False, + validators=[validators.MaxLengthValidator(4)]) + parcel_number = forms.CharField(label=_(u"Parcel number"), required=False, + validators=[validators.MaxLengthValidator(6)]) + year = forms.IntegerField(label=_("Year"), required=False, + validators=[validators.MinValueValidator(1900), + validators.MaxValueValidator(2100)]) + def __init__(self, *args, **kwargs): + towns = None + if 'data' in kwargs and 'TOWNS' in kwargs['data']: + towns = kwargs['data']['TOWNS'] + # clean data if not "real" data + prefix_value = kwargs['prefix'] + '-town' + if not [k for k in kwargs['data'].keys() + if k.startswith(prefix_value) and kwargs['data'][k]]: + kwargs['data'] = None + if 'files' in kwargs: + kwargs.pop('files') + super(ParcelForm, self).__init__(*args, **kwargs) + if towns: + self.fields['town'].choices = [('', '--')] + towns + + def clean(self): + """Check required fields""" + if any(self.errors): + return + if not self.cleaned_data or DELETION_FIELD_NAME in self.cleaned_data \ + and self.cleaned_data[DELETION_FIELD_NAME]: + return + for key in ('town', 'parcel_number', 'section'): + if not key in self.cleaned_data or not self.cleaned_data[key]: + raise forms.ValidationError(_(u"Town section and parcel number " + u"fields are required.")) + return self.cleaned_data + +class ParcelFormSet(FormSet): + def clean(self): + """Checks that no parcels are duplicated.""" + return self.check_duplicate(('town', 'parcel_number', 'year'), + _(u"There are identical parcels.")) + +ParcelFormSet = formset_factory(ParcelForm, can_delete=True, + formset=ParcelFormSet) +ParcelFormSet.form_label = _(u"Parcels") + class OperationWizard(Wizard): model = models.Operation object_parcel_type = 'operation' diff --git a/archaeological_operations/ishtar_menu.py b/archaeological_operations/ishtar_menu.py index dfd45a167..faf749480 100644 --- a/archaeological_operations/ishtar_menu.py +++ b/archaeological_operations/ishtar_menu.py @@ -1,6 +1,6 @@ #!/usr/bin/env python # -*- coding: utf-8 -*- -# Copyright (C) 2010-2012 Étienne Loks <etienne.loks_AT_peacefrogsDOTnet> +# Copyright (C) 2012 É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 @@ -20,53 +20,69 @@ from django.utils.translation import ugettext_lazy as _ from ishtar_common.menu_base import SectionItem, MenuItem -from ishtar_common.models import AdministrativeAct import models -ORDER = 30 - MENU_SECTIONS = [ - SectionItem('operation_management', _(u"Operation"), + (30, SectionItem('operation_management', _(u"Operation"), + childs=[ + MenuItem('operation_search', _(u"Search"), + model=models.Operation, + access_controls=['view_operation', + 'view_own_operation']), + MenuItem('operation_creation', _(u"Creation"), + model=models.Operation, + access_controls=['add_operation', + 'add_own_operation']), + MenuItem('operation_modification', _(u"Modification"), + model=models.Operation, + access_controls=['change_operation', + 'change_own_operation']), + MenuItem('operation_closing', _(u"Closing"), + model=models.Operation, + access_controls=['change_operation', + 'change_own_operation']), + MenuItem('operation_deletion', _(u"Deletion"), + model=models.Operation, + access_controls=['change_operation', + 'change_own_operation']), + SectionItem('admin_act_operations', + _(u"Administrative act"), childs=[ - MenuItem('operation_search', _(u"Search"), - model=models.Operation, - access_controls=['view_operation', - 'view_own_operation']), - MenuItem('operation_creation', _(u"Creation"), - model=models.Operation, - access_controls=['add_operation', - 'add_own_operation']), - MenuItem('operation_modification', _(u"Modification"), - model=models.Operation, - access_controls=['change_operation', - 'change_own_operation']), - MenuItem('operation_closing', _(u"Closing"), - model=models.Operation, - access_controls=['change_operation', - 'change_own_operation']), - MenuItem('operation_deletion', _(u"Deletion"), - model=models.Operation, - access_controls=['change_operation', - 'change_own_operation']), - SectionItem('admin_act_operations', - _(u"Administrative act"), + MenuItem('operation_administrativeactop', + _(u"Add"), + model=models.Operation, + access_controls=['change_operation', + 'change_own_operation']), + MenuItem('operation_administrativeactop_modification', + _(u"Modification"), + model=models.AdministrativeAct, + access_controls=['change_operation', + 'change_own_operation']), + MenuItem('operation_administrativeactop_deletion', + _(u"Deletion"), + model=models.AdministrativeAct, + access_controls=['operation_deletion', + 'delete_own_operation']), + ],), + SectionItem('operation_source', _(u"Documentation"), childs=[ - MenuItem('operation_administrativeactop', + MenuItem('operation_source_creation', _(u"Add"), - model=models.Operation, - access_controls=['change_operation', - 'change_own_operation']), - MenuItem('operation_administrativeactop_modification', + model=models.OperationSource, + access_controls=['change_operation', + 'change_own_operation']), + MenuItem('operation_source_modification', _(u"Modification"), - model=AdministrativeAct, - access_controls=['change_operation', - 'change_own_operation']), - MenuItem('operation_administrativeactop_deletion', + model=models.OperationSource, + access_controls=['change_operation', + 'change_own_operation']), + MenuItem('operation_source_deletion', _(u"Deletion"), - model=AdministrativeAct, - access_controls=['operation_deletion', - 'delete_own_operation']), - ],), - ]), + model=models.OperationSource, + access_controls=['change_operation', + 'change_own_operation']), + ]) + ]), + ) ] diff --git a/archaeological_operations/migrations/0001_initial.py b/archaeological_operations/migrations/0001_initial.py index e472e5e4d..2039268aa 100644 --- a/archaeological_operations/migrations/0001_initial.py +++ b/archaeological_operations/migrations/0001_initial.py @@ -200,6 +200,32 @@ class Migration(SchemaMigration): )) db.send_create_signal('archaeological_operations', ['AdministrativeAct']) + # Adding model 'Parcel' + db.create_table('archaeological_operations_parcel', ( + ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)), + ('history_modifier', self.gf('django.db.models.fields.related.ForeignKey')(related_name='+', to=orm['auth.User'])), + ('history_date', self.gf('django.db.models.fields.DateTimeField')(default=datetime.datetime.now)), + ('associated_file', self.gf('django.db.models.fields.related.ForeignKey')(blank=True, related_name='parcels', null=True, to=orm['archaeological_files.File'])), + ('operation', self.gf('django.db.models.fields.related.ForeignKey')(blank=True, related_name='parcels', null=True, to=orm['archaeological_operations.Operation'])), + ('year', self.gf('django.db.models.fields.IntegerField')(null=True, blank=True)), + ('town', self.gf('django.db.models.fields.related.ForeignKey')(related_name='parcels', to=orm['ishtar_common.Town'])), + ('section', self.gf('django.db.models.fields.CharField')(max_length=4)), + ('parcel_number', self.gf('django.db.models.fields.CharField')(max_length=6)), + )) + db.send_create_signal('archaeological_operations', ['Parcel']) + + # Adding model 'ParcelOwner' + db.create_table('archaeological_operations_parcelowner', ( + ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)), + ('history_modifier', self.gf('django.db.models.fields.related.ForeignKey')(related_name='+', to=orm['auth.User'])), + ('history_date', self.gf('django.db.models.fields.DateTimeField')(default=datetime.datetime.now)), + ('owner', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['ishtar_common.Person'])), + ('parcel', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['archaeological_operations.Parcel'])), + ('start_date', self.gf('django.db.models.fields.DateField')()), + ('end_date', self.gf('django.db.models.fields.DateField')()), + )) + db.send_create_signal('archaeological_operations', ['ParcelOwner']) + def backwards(self, orm): # Deleting model 'OperationType' @@ -241,6 +267,12 @@ class Migration(SchemaMigration): # Deleting model 'AdministrativeAct' db.delete_table('archaeological_operations_administrativeact') + # Deleting model 'Parcel' + db.delete_table('archaeological_operations_parcel') + + # Deleting model 'ParcelOwner' + db.delete_table('archaeological_operations_parcelowner') + models = { 'archaeological_files.file': { @@ -427,6 +459,28 @@ class Migration(SchemaMigration): 'label': ('django.db.models.fields.CharField', [], {'max_length': '100'}), 'txt_idx': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'}) }, + 'archaeological_operations.parcel': { + 'Meta': {'object_name': 'Parcel'}, + 'associated_file': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'parcels'", 'null': 'True', 'to': "orm['archaeological_files.File']"}), + 'history_date': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'history_modifier': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'+'", 'to': "orm['auth.User']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'operation': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'parcels'", 'null': 'True', 'to': "orm['archaeological_operations.Operation']"}), + 'parcel_number': ('django.db.models.fields.CharField', [], {'max_length': '6'}), + 'section': ('django.db.models.fields.CharField', [], {'max_length': '4'}), + 'town': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'parcels'", 'to': "orm['ishtar_common.Town']"}), + 'year': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}) + }, + 'archaeological_operations.parcelowner': { + 'Meta': {'object_name': 'ParcelOwner'}, + 'end_date': ('django.db.models.fields.DateField', [], {}), + 'history_date': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'history_modifier': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'+'", 'to': "orm['auth.User']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'owner': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['ishtar_common.Person']"}), + 'parcel': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['archaeological_operations.Parcel']"}), + 'start_date': ('django.db.models.fields.DateField', [], {}) + }, 'archaeological_operations.period': { 'Meta': {'object_name': 'Period'}, 'available': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), diff --git a/archaeological_operations/models.py b/archaeological_operations/models.py index 48baa57ba..9b3631114 100644 --- a/archaeological_operations/models.py +++ b/archaeological_operations/models.py @@ -23,7 +23,8 @@ from django.db.models.signals import post_save from django.utils.translation import ugettext_lazy as _, ugettext from ishtar_common.models import GeneralType, BaseHistorizedItem, \ - HistoricalRecords, OwnPerms, Department, Source, Person, Organization, Town + HistoricalRecords, LightHistorizedItem, OwnPerms, Department, Source,\ + Person, Organization, Town, Dashboard FILES_AVAILABLE = 'archaeological_files' in settings.INSTALLED_APPS if FILES_AVAILABLE: from archaeological_files.models import File @@ -288,13 +289,502 @@ related_name='+', verbose_name=_(u"Person in charge of the scientific part")) 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")), + ("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 Parcel(LightHistorizedItem): + if FILES_AVAILABLE: + 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"), blank=True, null=True) + 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): + items = [unicode(self.operation or self.associated_file)] + items += [unicode(item) for item in [self.section, self.parcel_number] + if item] + return JOINT.join(items) + +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 OperationDashboard: + def __init__(self): + main_dashboard = Dashboard(Operation) + + self.total_number = main_dashboard.total_number + + self.filters_keys = ['recorded', 'effective', 'active', 'field', + 'documented', 'closed', 'documented_closed'] + filters = { + 'recorded':{}, + 'effective':{'in_charge__isnull':False}, + 'active':{'in_charge__isnull':False, 'end_date__isnull':True}, + 'field':{'excavation_end_date__isnull':True}, + 'documented':{'source__isnull':False}, + 'documented_closed':{'source__isnull':False, + 'end_date__isnull':False}, + 'closed':{'end_date__isnull':False} + } + filters_label = { + 'recorded':_(u"Recorded"), + 'effective':_(u"Effective"), + 'active':_(u"Active"), + 'field':_(u"Field completed"), + 'documented':_(u"Associated report"), + 'closed':_(u"Closed"), + 'documented_closed':_(u"Documented and closed"), + } + self.filters_label = [filters_label[k] for k in self.filters_keys] + self.total = [] + for fltr_key in self.filters_keys: + fltr, lbl = filters[fltr_key], filters_label[fltr_key] + nb = Operation.objects.filter(**fltr).count() + self.total.append((lbl, nb)) + + self.surface_by_type = Operation.objects\ + .values('operation_type__label')\ + .annotate(number=Sum('surface'))\ + .order_by('-number','operation_type__label') + + self.by_type = [] + self.types = OperationType.objects.filter(available=True).all() + for fltr_key in self.filters_keys: + fltr, lbl = filters[fltr_key], filters_label[fltr_key] + type_res = Operation.objects.filter(**fltr).\ + values('operation_type', 'operation_type__label').\ + annotate(number=Count('pk')).\ + order_by('operation_type') + types_dct = {} + for typ in type_res.all(): + types_dct[typ['operation_type']] = typ["number"] + types = [] + for typ in self.types: + if typ.pk in types_dct: + types.append(types_dct[typ.pk]) + else: + types.append(0) + self.by_type.append((lbl, types)) + + self.by_year = [] + self.years = [res['year'] for res in Operation.objects.values('year')\ + .order_by('-year').distinct()] + for fltr_key in self.filters_keys: + fltr, lbl = filters[fltr_key], filters_label[fltr_key] + year_res = Operation.objects.filter(**fltr).\ + values('year').\ + annotate(number=Count('pk')).\ + order_by('year') + years_dct = {} + for yr in year_res.all(): + years_dct[yr['year']] = yr["number"] + years = [] + for yr in self.years: + if yr in years_dct: + years.append(years_dct[yr]) + else: + years.append(0) + self.by_year.append((lbl, years)) + + self.by_realisation_year = [] + self.realisation_years = [res['date'] for res in \ + Operation.objects.extra( + {'date':"date_trunc('year', start_date)"}).values('date')\ + .filter(start_date__isnull=False).order_by('-date').distinct()] + for fltr_key in self.filters_keys: + fltr, lbl = filters[fltr_key], filters_label[fltr_key] + year_res = Operation.objects.filter(**fltr).extra( + {'date':"date_trunc('year', start_date)"}).values('date').\ + values('date').filter(start_date__isnull=False).\ + annotate(number=Count('pk')).\ + order_by('-date') + years_dct = {} + for yr in year_res.all(): + years_dct[yr['date']] = yr["number"] + years = [] + for yr in self.realisation_years: + if yr in years_dct: + years.append(years_dct[yr]) + else: + years.append(0) + self.by_realisation_year.append((lbl, years)) + + self.effective = [] + for typ in self.types: + year_res = Operation.objects.filter(**{'in_charge__isnull':False, + 'operation_type':typ}).\ + values('year').\ + annotate(number=Count('pk')).\ + order_by('-year').distinct() + years_dct = {} + for yr in year_res.all(): + years_dct[yr['year']] = yr["number"] + years = [] + for yr in self.years: + if yr in years_dct: + years.append(years_dct[yr]) + else: + years.append(0) + self.effective.append((typ, years)) + + # TODO: by date + now = datetime.date.today() + limit = datetime.date(now.year, now.month, 1) - datetime.timedelta(365) + by_realisation_month = Operation.objects.filter(start_date__gt=limit, + start_date__isnull=False).extra( + {'date':"date_trunc('month', start_date)"}) + self.last_months = [] + date = datetime.datetime(now.year, now.month, 1) + for mt_idx in xrange(12): + self.last_months.append(date) + if date.month > 1: + date = datetime.datetime(date.year, date.month - 1, 1) + else: + date = datetime.datetime(date.year - 1, 12, 1) + self.by_realisation_month = [] + for fltr_key in self.filters_keys: + fltr, lbl = filters[fltr_key], filters_label[fltr_key] + month_res = by_realisation_month.filter(**fltr).\ + annotate(number=Count('pk')).\ + order_by('-date') + month_dct = {} + for mt in month_res.all(): + month_dct[mt.date] = mt.number + date = datetime.date(now.year, now.month, 1) + months = [] + for date in self.last_months: + if date in month_dct: + months.append(month_dct[date]) + else: + months.append(0) + self.by_realisation_month.append((lbl, months)) + + # survey and excavations + self.survey, self.excavation = {}, {} + for dct_res, ope_types in ((self.survey, ('arch_diagnostic',)), + (self.excavation, ('prev_excavation', + 'prog_excavation'))): + dct_res['total'] = [] + operation_type = {'operation_type__txt_idx__in':ope_types} + for fltr_key in self.filters_keys: + fltr, lbl = filters[fltr_key], filters_label[fltr_key] + fltr.update(operation_type) + nb = Operation.objects.filter(**fltr).count() + dct_res['total'].append((lbl, nb)) + + dct_res['by_year'] = [] + for fltr_key in self.filters_keys: + fltr, lbl = filters[fltr_key], filters_label[fltr_key] + fltr.update(operation_type) + year_res = Operation.objects.filter(**fltr).\ + values('year').\ + annotate(number=Count('pk')).\ + order_by('year') + years_dct = {} + for yr in year_res.all(): + years_dct[yr['year']] = yr["number"] + years = [] + for yr in self.years: + if yr in years_dct: + years.append(years_dct[yr]) + else: + years.append(0) + dct_res['by_year'].append((lbl, years)) + + dct_res['by_realisation_year'] = [] + for fltr_key in self.filters_keys: + fltr, lbl = filters[fltr_key], filters_label[fltr_key] + fltr.update(operation_type) + year_res = Operation.objects.filter(**fltr).extra( + {'date':"date_trunc('year', start_date)"}).values('date').\ + filter(start_date__isnull=False).\ + annotate(number=Count('pk')).\ + order_by('-date') + years_dct = {} + for yr in year_res.all(): + years_dct[yr['date']] = yr["number"] + years = [] + for yr in self.realisation_years: + if yr in years_dct: + years.append(years_dct[yr]) + else: + years.append(0) + dct_res['by_realisation_year'].append((lbl, years)) + + current_year_ope = Operation.objects.filter(**operation_type)\ + .filter(year=datetime.date.today().year) + current_realisation_year_ope = Operation.objects\ + .filter(**operation_type)\ + .filter(start_date__year=datetime.date.today().year) + res_keys = [('area_realised', current_realisation_year_ope)] + if dct_res == self.survey: + res_keys.append(('area', + current_year_ope)) + for res_key, base_ope in res_keys: + dct_res[res_key] = [] + for fltr_key in self.filters_keys: + fltr, lbl = filters[fltr_key], filters_label[fltr_key] + area_res = base_ope.filter(**fltr)\ + .annotate(number=Sum('surface')).all() + val = 0 + if area_res: + val = area_res[0].number + dct_res[res_key].append(val) + # TODO... + res_keys = [('manday_realised', current_realisation_year_ope)] + if dct_res == self.survey: + res_keys.append(('manday', + current_year_ope)) + for res_key, base_ope in res_keys: + dct_res[res_key] = [] + for fltr_key in self.filters_keys: + dct_res[res_key].append('-') + # TODO... + res_keys = [('mandayhect_realised', current_realisation_year_ope)] + if dct_res == self.survey: + res_keys.append(('mandayhect', + current_year_ope)) + for res_key, base_ope in res_keys: + dct_res[res_key] = [] + for fltr_key in self.filters_keys: + dct_res[res_key].append('-') + # TODO... + dct_res['mandayhect_real_effective'] = '-' + if dct_res == self.survey: + dct_res['mandayhect_effective'] = '-' + + + res_keys = [('org_realised', current_realisation_year_ope)] + if dct_res == self.survey: + res_keys.append(('org', current_year_ope)) + for res_key, base_ope in res_keys: + org_res = base_ope.filter(in_charge__attached_to__isnull=False)\ + .values('in_charge__attached_to', + 'in_charge__attached_to__name')\ + .annotate(area=Sum('surface'))\ + .order_by('in_charge__attached_to__name').all() + # TODO: man-days, man-days/hectare + dct_res[res_key] = org_res + + + year_ope = Operation.objects.filter(**operation_type) + res_keys = ['org_by_year'] + if dct_res == self.survey: + res_keys.append('org_by_year_realised') + q = year_ope.values('in_charge__attached_to', + 'in_charge__attached_to__name').\ + filter(in_charge__attached_to__isnull=False).\ + order_by('in_charge__attached_to__name').distinct() + org_list = [(org['in_charge__attached_to'], + org['in_charge__attached_to__name']) for org in q] + org_list_dct = dict(org_list) + for res_key in res_keys: + dct_res[res_key] = [] + years = self.years + if res_key == 'org_by_year_realised': + years = self.realisation_years + for org_id, org_label in org_list: + org_res = year_ope.filter(in_charge__attached_to__pk=org_id) + key_date = '' + if res_key == 'org_by_year': + org_res = org_res.values('year') + key_date = 'year' + else: + org_res = org_res.extra( + {'date':"date_trunc('year', start_date)"}).values('date').\ + filter(start_date__isnull=False) + key_date = 'date' + org_res = org_res.annotate(area=Sum('surface'), + cost=Sum('cost')) + years_dct = {} + for yr in org_res.all(): + area = yr['area'] if yr['area'] else 0 + cost = yr['cost'] if yr['cost'] else 0 + years_dct[yr[key_date]] = (area, cost) + r_years = [] + for yr in years: + if yr in years_dct: + r_years.append(years_dct[yr]) + else: + r_years.append((0, 0)) + dct_res[res_key].append((org_label, r_years)) + area_means, area_sums = [], [] + cost_means, cost_sums = [], [] + for idx, year in enumerate(years): + vals = [r_years[idx] for lbl, r_years in dct_res[res_key]] + sum_area = sum([a for a, c in vals]) + sum_cost = sum([c for a, c in vals]) + area_means.append(sum_area/len(vals)) + area_sums.append(sum_area) + cost_means.append(sum_cost/len(vals)) + cost_sums.append(sum_cost) + dct_res[res_key+'_area_mean'] = area_means + dct_res[res_key+'_area_sum'] = area_sums + dct_res[res_key+'_cost_mean'] = cost_means + dct_res[res_key+'_cost_mean'] = cost_sums + + if dct_res == self.survey: + self.survey['effective'] = [] + for yr in self.years: + year_res = Operation.objects.filter(in_charge__isnull=False, + year=yr).\ + annotate(number=Sum('surface'), + mean=Avg('surface')) + nb = year_res[0].number if year_res.count() else 0 + nb = nb if nb else 0 + mean = year_res[0].mean if year_res.count() else 0 + mean = mean if mean else 0 + self.survey['effective'].append((nb, mean)) + + # TODO:Man-Days/hectare by Year + + # CHECK: month of realisation or month? + dct_res['by_month'] = [] + for fltr_key in self.filters_keys: + fltr, lbl = filters[fltr_key], filters_label[fltr_key] + fltr.update(operation_type) + month_res = by_realisation_month.filter(**fltr).\ + annotate(number=Count('pk')).\ + order_by('-date') + month_dct = {} + for mt in month_res.all(): + month_dct[mt.date] = mt.number + date = datetime.date(now.year, now.month, 1) + months = [] + for date in self.last_months: + if date in month_dct: + months.append(month_dct[date]) + else: + months.append(0) + dct_res['by_month'].append((lbl, months)) + + operation_type = {'operation_type__txt_idx__in':ope_types} + self.departments = [(fd['department__pk'], fd['department__label']) + for fd in OperationByDepartment.objects\ + .filter(department__isnull=False)\ + .values('department__label', 'department__pk')\ + .order_by('department__label').distinct()] + dct_res['by_dpt'] = [] + for dpt_id, dpt_label in self.departments: + vals = OperationByDepartment.objects\ + .filter(department__pk=dpt_id, + operation__operation_type__txt_idx__in=ope_types)\ + .values('department__pk', 'operation__year')\ + .annotate(number=Count('operation'))\ + .order_by('operation__year') + dct_years = {} + for v in vals: + dct_years[v['operation__year']] = v['number'] + years = [] + for y in self.years: + if y in dct_years: + years.append(dct_years[y]) + else: + years.append(0) + years.append(sum(years)) + dct_res['by_dpt'].append((dpt_label, years)) + dct_res['effective_by_dpt'] = [] + for dpt_id, dpt_label in self.departments: + vals = OperationByDepartment.objects\ + .filter(department__pk=dpt_id, + operation__in_charge__isnull=False, + operation__operation_type__txt_idx__in=ope_types)\ + .values('department__pk', 'operation__year')\ + .annotate(number=Count('operation'), + area=Sum('operation__surface'), + fnap=Sum('operation__fnap_cost'), + cost=Sum('operation__cost'))\ + .order_by('operation__year') + dct_years = {} + for v in vals: + values = [] + for value in (v['number'], v['area'], v['cost'], v['fnap']): + values.append(value if value else 0) + dct_years[v['operation__year']] = values + years = [] + for y in self.years: + if y in dct_years: + years.append(dct_years[y]) + else: + years.append((0, 0, 0, 0)) + nbs, areas, costs, fnaps = zip(*years) + years.append((sum(nbs), sum(areas), sum(costs), sum(fnaps))) + dct_res['effective_by_dpt'].append((dpt_label, years)) + + OperationTown = Operation.towns.through + query = OperationTown.objects\ + .filter(operation__in_charge__isnull=False, + operation__operation_type__txt_idx__in=ope_types)\ + .values('town__name', 'town__departement__number')\ + .annotate(nb=Count('operation'))\ + .order_by('-nb', 'town__name')[:10] + dct_res['towns'] = [] + for r in query: + dct_res['towns'].append((u"%s (%s)" % (r['town__name'], + r['town__departement__number']), + r['nb'])) + + if dct_res == self.survey: + query = OperationTown.objects\ + .filter(operation__in_charge__isnull=False, + operation__operation_type__txt_idx__in=ope_types, + operation__surface__isnull=False)\ + .values('town__name', 'town__departement__number')\ + .annotate(nb=Sum('operation__surface'))\ + .order_by('-nb', 'town__name')[:10] + dct_res['towns_surface'] = [] + for r in query: + dct_res['towns_surface'].append((u"%s (%s)" % ( + r['town__name'], r['town__departement__number']), + r['nb'])) + else: + query = OperationTown.objects\ + .filter(operation__in_charge__isnull=False, + operation__operation_type__txt_idx__in=ope_types, + operation__cost__isnull=False)\ + .values('town__name', 'town__departement__number')\ + .annotate(nb=Sum('operation__cost'))\ + .order_by('-nb', 'town__name')[:10] + dct_res['towns_cost'] = [] + for r in query: + dct_res['towns_cost'].append((u"%s (%s)" % (r['town__name'], + r['town__departement__number']), + r['nb'])) diff --git a/archaeological_operations/urls.py b/archaeological_operations/urls.py new file mode 100644 index 000000000..a761b4ccc --- /dev/null +++ b/archaeological_operations/urls.py @@ -0,0 +1,75 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# Copyright (C) 2010-2012 É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. + +from django.conf.urls.defaults import * + +""" +import forms + +# forms +urlpatterns = patterns('', + url(r'operation_search/(?P<step>.+)$', + forms.operation_search_wizard, name='operation_search'), + url(r'operation_creation/(?P<step>.+)$', + forms.operation_creation_wizard, name='operation_creation'), + url(r'operation_modification/(?P<step>.+)$', + forms.operation_modification_wizard, + name='operation_modification'), + url(r'operation_closing/(?P<step>.+)$', + forms.operation_closing_wizard, name='operation_closing'), + url(r'operation_deletion/(?P<step>.+)$', + forms.operation_deletion_wizard, name='operation_deletion'), + url(r'operation_administrativeactop/(?P<step>.+)$', + forms.operation_administrativeactop_wizard, + name='operation_administrativeactop'), + url(r'operation_administrativeactop_modification/(?P<step>.+)$', + forms.operation_administrativeactop_modification_wizard, + name='operation_administrativeactop_modification'), + url(r'operation_administrativeactop_deletion/(?P<step>.+)$', + forms.operation_administrativeactop_deletion_wizard, + name='operation_administrativeactop_deletion'), + url(r'operation_source_creation/(?P<step>.+)$', + forms.operation_source_creation_wizard, + name='operation_source_creation'), + url(r'operation_source_modification/(?P<step>.+)$', + forms.operation_source_modification_wizard, + name='operation_source_modification'), + url(r'operation_source_deletion/(?P<step>.+)$', + forms.operation_source_deletion_wizard, + name='operation_source_deletion'), +) + +urlpatterns += patterns('archaeological_operations.views', + url(r'autocomplete-operation/$', 'autocomplete_operation', + name='autocomplete-operation'), + url(r'get-operation/(?P<type>.+)?$', 'get_operation', + name='get-operation'), + url(r'get-operation-full/(?P<type>.+)?$', 'get_operation', + name='get-operation-full', kwargs={'full':True}), + url(r'get-available-operation-code/(?P<year>.+)?$', + 'get_available_operation_code', name='get_available_operation_code'), + url(r'revert-operation/(?P<pk>.+)/(?P<date>.+)$', + 'revert_operation', name='revert-operation'), + url(r'show-operation/(?P<pk>.+)?/(?P<type>.+)?$', + 'show_operation', name='show-operation'), + url(r'get-administrativeactop/(?P<type>.+)?$', + 'get_administrativeactop', name='get-administrativeactop'), + url(r'get-operationsource/(?P<type>.+)?$', + 'get_operationsource', name='get-operationsource'), +)""" diff --git a/archaeological_operations/views.py b/archaeological_operations/views.py new file mode 100644 index 000000000..27ebd60e9 --- /dev/null +++ b/archaeological_operations/views.py @@ -0,0 +1,97 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# Copyright (C) 2010-2012 É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. + +import json + +from django.db.models import Q +from django.http import HttpResponse +from django.shortcuts import render_to_response + +from ishtar_common.views import get_item, show_item, revert_item +import models + +def autocomplete_operation(request, non_closed=True): + person_types = request.user.ishtaruser.person.person_type + if (not request.user.has_perm('ishtar_common.view_operation', models.Operation)\ + and not request.user.has_perm('ishtar_common.view_own_operation', + models.Operation) + and not person_types.rights.filter(wizard__url_name='operation_search' + ).count()): + return HttpResponse(mimetype='text/plain') + if not request.GET.get('term'): + return HttpResponse(mimetype='text/plain') + q = request.GET.get('term') + query = Q() + for q in q.split(' '): + extra = Q(towns__name__icontains=q) + try: + value = int(q) + extra = extra | Q(year=q) | Q(operation_code=q) + except ValueError: + pass + query = query & extra + if non_closed: + query = query & Q(end_date__isnull=True) + limit = 15 + operations = models.Operation.objects.filter(query)[:limit] + data = json.dumps([{'id':operation.pk, 'value':unicode(operation)} + for operation in operations]) + return HttpResponse(data, mimetype='text/plain') + +def get_available_operation_code(request, year=None): + if not request.user.has_perm('ishtar_common.view_operation', models.Operation)\ + and not request.user.has_perm('ishtar_common.view_own_operation', + models.Operation): + return HttpResponse(mimetype='text/plain') + data = json.dumps({'id':models.Operation.get_available_operation_code(year)}) + return HttpResponse(data, mimetype='text/plain') + +get_operation = get_item(models.Operation, 'get_operation', 'operation', + bool_fields = ['end_date__isnull'], + extra_request_keys={'common_name':'common_name__icontains', + 'end_date':'end_date__isnull', + 'year_index':('year', 'operation_code')}) +show_operation = show_item(models.Operation, 'operation') +revert_operation = revert_item(models.Operation) + +get_operationsource = get_item(models.OperationSource, + 'get_operationsource', 'operationsource', + extra_request_keys={'operation__towns':'operation__towns__pk', + 'operation__operation_type':'operation__operation_type__pk', + 'operation__year':'operation__year'}) + +get_administrativeactfile = get_item(models.AdministrativeAct, + 'get_administrativeactfile', 'administrativeactfile', + extra_request_keys={'associated_file__towns':'associated_file__towns__pk', + 'operation__towns':'operation__towns__pk', + 'act_type__intented_to':'act_type__intented_to'}) +get_administrativeactop = get_item(models.AdministrativeAct, + 'get_administrativeactop', 'administrativeactop', + extra_request_keys={'associated_file__towns':'associated_file__towns__pk', + 'operation__towns':'operation__towns__pk', + 'act_type__intented_to':'act_type__intented_to'}) + + +def dashboard_operation(request, dct, obj_id=None, *args, **kwargs): + """ + Operation dashboard + """ + dct = {'dashboard': models.OperationDashboard()} + return render_to_response('dashboard_operation.html', dct, + context_instance=RequestContext(request)) diff --git a/archaeological_warehouse/admin.py b/archaeological_warehouse/admin.py new file mode 100644 index 000000000..cf026e86c --- /dev/null +++ b/archaeological_warehouse/admin.py @@ -0,0 +1,51 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# Copyright (C) 2012 É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. + +from django.conf import settings +from django.contrib import admin + +from ishtar_common.admin import HistorizedObjectAdmin + +import models + +class WarehouseAdmin(HistorizedObjectAdmin): + list_display = ('name', 'warehouse_type', 'town') + list_filter = ('warehouse_type',) + search_fields = ('name', 'town') + model = models.Warehouse + +admin.site.register(models.Warehouse, WarehouseAdmin) + +class ContainerTypeAdmin(admin.ModelAdmin): + list_display = ('label', 'reference', 'length', 'width', 'height', + 'volume') + model = models.ContainerType + +admin.site.register(models.ContainerType, ContainerTypeAdmin) + +class ContainerAdmin(admin.ModelAdmin): + list_display = ('reference', 'location', 'container_type',) + list_filter = ("container_type",) + model = models.Container + +admin.site.register(models.Container, ContainerAdmin) + +basic_models = [models.WarehouseType] +for model in basic_models: + admin.site.register(model) diff --git a/archaeological_warehouse/ishtar_menu.py b/archaeological_warehouse/ishtar_menu.py new file mode 100644 index 000000000..1a9d57aaa --- /dev/null +++ b/archaeological_warehouse/ishtar_menu.py @@ -0,0 +1,40 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# Copyright (C) 2012 É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. + +from django.utils.translation import ugettext_lazy as _ + +from ishtar_common.menu_base import SectionItem, MenuItem + +from archaeological_finds.models import Treatment +import models + +MENU_SECTIONS = [ + (60, SectionItem('warehouse', _(u"Warehouse"), + childs=[ + MenuItem('warehouse_inventory', _(u"Inventory"), + model=models.Warehouse, + access_controls=['change_warehouse',]), + MenuItem('warehouse_recording', _(u"Recording"), + model=Treatment, + access_controls=['add_treatment', 'add_own_treatment']), + MenuItem('warehouse_lend', _(u"Lending"), + model=Treatment, + access_controls=['add_treatment', 'add_own_treatment']), + ])) +] diff --git a/archaeological_warehouse/models.py b/archaeological_warehouse/models.py index fe381e521..877d16487 100644 --- a/archaeological_warehouse/models.py +++ b/archaeological_warehouse/models.py @@ -51,7 +51,6 @@ class Warehouse(Address, OwnPerms): def __unicode__(self): return u"%s (%s)" % (self.name, unicode(self.warehouse_type)) - class ContainerType(GeneralType): length = models.IntegerField(_(u"Length (mm)"), blank=True, null=True) width = models.IntegerField(_(u"Width (mm)"), blank=True, null=True) diff --git a/archaeological_warehouse/urls.py b/archaeological_warehouse/urls.py new file mode 100644 index 000000000..914ee2533 --- /dev/null +++ b/archaeological_warehouse/urls.py @@ -0,0 +1,45 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# Copyright (C) 2010-2012 É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. + +from django.conf.urls.defaults import * + +""" +import forms + +# forms +urlpatterns = patterns('', + url(r'treatment_creation/(?P<step>.+)$', + forms.treatment_creation_wizard, name='treatment_creation'), + url(r'warehouse_packaging/(?P<step>.+)$', + forms.warehouse_packaging_wizard, name='warehouse_packaging'), +) + +urlpatterns += patterns('archaeological_warehouse.views', + url(r'new-warehouse/(?P<parent_name>.+)?/$', + 'new_warehouse', name='new-warehouse'), + url(r'autocomplete-warehouse/$', 'autocomplete_warehouse', + name='autocomplete-warehouse'), + url(r'new-container/(?P<parent_name>.+)?/$', + 'new_container', name='new-container'), + url(r'get-container/$', 'get_container', + name='get-container'), + url(r'autocomplete-container/?$', + 'autocomplete_container', name='autocomplete-container'), +) +""" diff --git a/archaeological_warehouse/views.py b/archaeological_warehouse/views.py new file mode 100644 index 000000000..16a8e19f5 --- /dev/null +++ b/archaeological_warehouse/views.py @@ -0,0 +1,81 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# Copyright (C) 2010-2012 É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. + +import json + +from django.db.models import Q +from django.http import HttpResponse +from django.shortcuts import render_to_response + +from ishtar_common.views import get_item, show_item, revert_item +import models + +get_container = get_item(models.Container, + 'get_container', 'container', + extra_request_keys={ + 'location':'location__pk', + 'container_type':'container_type__pk', + 'reference':'reference__icontains', + }) + +new_warehouse = new_item(models.Warehouse) +new_container = new_item(models.Container) + +def autocomplete_warehouse(request): + if not request.user.has_perm('ishtar_common.view_warehouse', + models.Warehouse)\ + and not request.user.has_perm('ishtar_common.view_own_warehouse', + models.Warehouse) : + return HttpResponse(mimetype='text/plain') + if not request.GET.get('term'): + return HttpResponse(mimetype='text/plain') + q = request.GET.get('term') + query = Q() + for q in q.split(' '): + extra = Q(name__icontains=q) | \ + Q(warehouse_type__label__icontains=q) + query = query & extra + limit = 15 + warehouses = models.Warehouse.objects.filter(query)[:limit] + data = json.dumps([{'id':warehouse.pk, 'value':unicode(warehouse)} + for warehouse in warehouses]) + return HttpResponse(data, mimetype='text/plain') + +def autocomplete_container(request): + if not request.user.has_perm('ishtar_common.view_warehouse', + models.Warehouse)\ + and not request.user.has_perm('ishtar_common.view_own_warehouse', + models.Warehouse): + return HttpResponse(mimetype='text/plain') + if not request.GET.get('term'): + return HttpResponse(mimetype='text/plain') + q = request.GET.get('term') + query = Q() + for q in q.split(' '): + extra = Q(container_type__label__icontains=q) | \ + Q(container_type__reference__icontains=q) | \ + Q(reference__icontains=q) | \ + Q(location__name=q) | \ + Q(location__town=q) + query = query & extra + limit = 15 + containers = models.Container.objects.filter(query)[:limit] + data = json.dumps([{'id':container.pk, 'value':unicode(container)} + for container in containers]) + return HttpResponse(data, mimetype='text/plain') diff --git a/example_project/urls.py b/example_project/urls.py index ea54a61e1..02936e6df 100644 --- a/example_project/urls.py +++ b/example_project/urls.py @@ -1,3 +1,4 @@ +from django.conf import settings from django.conf.urls.defaults import * from django.contrib.auth.models import User @@ -5,15 +6,23 @@ from django.contrib import admin admin.autodiscover() #admin.site.unregister(User) -from settings import URL_PATH - -BASE_URL = r'^' + URL_PATH urlpatterns = patterns('', - (BASE_URL + 'accounts/', include('registration.urls')), - (BASE_URL + r'admin/', include(admin.site.urls)), + (r'^accounts/', include('registration.urls')), + (r'^admin/', include(admin.site.urls)), ('', include('ishtar_common.urls')), ) + +""" +for app in ['archaeological_files', 'archaeological_operations', + 'archaeological_context_records', 'archaeological_warehouse', + 'archaeological_finds']: + if app in settings.INSTALLED_APPS: + urlpatterns = patterns('', + ('', include(app+'.urls')), + ) +""" + urlpatterns += patterns('ishtar_common.views', - url(BASE_URL + '$', 'index', name='start'), + url(r'$', 'index', name='start'), ) diff --git a/ishtar_common/admin.py b/ishtar_common/admin.py index c4e5fa7f2..8f6a07f88 100644 --- a/ishtar_common/admin.py +++ b/ishtar_common/admin.py @@ -1,6 +1,6 @@ #!/usr/bin/env python # -*- coding: utf-8 -*- -# Copyright (C) 2010-2011 Étienne Loks <etienne.loks_AT_peacefrogsDOTnet> +# Copyright (C) 2010-2012 É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 @@ -57,130 +57,6 @@ class PersonAdmin(HistorizedObjectAdmin): admin.site.register(models.Person, PersonAdmin) -class FileAdmin(HistorizedObjectAdmin): - list_display = ['year', 'numeric_reference', 'internal_reference', - 'end_date', 'file_type', 'general_contractor',] - if settings.COUNTRY == 'fr': - list_display += ['saisine_type', 'reference_number'] - list_filter = ("file_type", "year",) - search_fields = ('towns__name',) - model = models.File - -admin.site.register(models.File, FileAdmin) - -class OperationAdmin(HistorizedObjectAdmin): - list_display = ['year', 'operation_code', 'start_date', - 'excavation_end_date', 'end_date', - 'operation_type'] - list_filter = ("year", "operation_type",) - search_fields = ['towns__name', 'operation_code'] - if settings.COUNTRY == 'fr': - list_display += ['code_patriarche'] - search_fields += ['code_patriarche'] - model = models.Operation - -admin.site.register(models.Operation, OperationAdmin) - -class OperationSourceAdmin(admin.ModelAdmin): - list_display = ('operation', 'title', 'source_type',) - list_filter = ('source_type',) - search_fields = ('title', 'operation__name') - model = models.OperationSource - -admin.site.register(models.OperationSource, OperationSourceAdmin) - -class ParcelAdmin(HistorizedObjectAdmin): - list_display = ('section', 'parcel_number', 'operation', 'associated_file') - search_fields = ('operation__name',) - model = models.Parcel - -admin.site.register(models.Parcel, ParcelAdmin) - -class PeriodAdmin(admin.ModelAdmin): - list_display = ('label', 'start_date', 'end_date', 'parent') - model = models.Period - -admin.site.register(models.Period, PeriodAdmin) - -class DatingAdmin(admin.ModelAdmin): - list_display = ('period', 'start_date', 'end_date', 'dating_type', - 'quality') - list_filter = ("period", 'dating_type', 'quality') - model = models.Dating - -admin.site.register(models.Dating, DatingAdmin) - -class ContextRecordAdmin(HistorizedObjectAdmin): - list_display = ('label', 'length', 'width', - 'thickness', 'depth') - list_filter = ('has_furniture',) - search_fields = ('parcel__operation__name', "datings__period__label") - model = models.ContextRecord - -admin.site.register(models.ContextRecord, ContextRecordAdmin) - -class ContextRecordSourceAdmin(admin.ModelAdmin): - list_display = ('context_record', 'title', 'source_type',) - list_filter = ('source_type',) - search_fields = ('title', ) - model = models.ContextRecordSource - -admin.site.register(models.ContextRecordSource, ContextRecordSourceAdmin) - -class BaseItemAdmin(HistorizedObjectAdmin): - list_display = ('label', 'context_record', 'is_isolated') - search_fields = ('label', 'context_record__parcel__operation__name',) - model = models.BaseItem - -admin.site.register(models.BaseItem, BaseItemAdmin) - -class ItemAdmin(HistorizedObjectAdmin): - list_display = ('label', 'material_type', 'dating', 'volume', 'weight', - 'item_number',) - list_filter = ('material_type',) - search_fields = ('label', "dating__period__label") - model = models.Item - -admin.site.register(models.Item, ItemAdmin) - -class ItemSourceAdmin(admin.ModelAdmin): - list_display = ('item', 'title', 'source_type',) - list_filter = ('source_type',) - search_fields = ('title', ) - model = models.ItemSource - -admin.site.register(models.ItemSource, ItemSourceAdmin) - -class WarehouseAdmin(HistorizedObjectAdmin): - list_display = ('name', 'warehouse_type', 'town') - list_filter = ('warehouse_type',) - search_fields = ('name', 'town') - model = models.Warehouse - -admin.site.register(models.Warehouse, WarehouseAdmin) - -class AdministrativeActAdmin(HistorizedObjectAdmin): - list_display = ('operation', 'act_type', 'signature_date') - list_filter = ('act_type',) - search_fields = ('operation__name',) - model = models.AdministrativeAct - -admin.site.register(models.AdministrativeAct, AdministrativeActAdmin) - -class ContainerTypeAdmin(admin.ModelAdmin): - list_display = ('label', 'reference', 'length', 'width', 'height', - 'volume') - model = models.ContainerType - -admin.site.register(models.ContainerType, ContainerTypeAdmin) - -class ContainerAdmin(admin.ModelAdmin): - list_display = ('reference', 'location', 'container_type',) - list_filter = ("container_type",) - model = models.Container - -admin.site.register(models.Container, ContainerAdmin) - class TownAdmin(admin.ModelAdmin): list_display = ['name',] search_fields = ['name'] @@ -199,42 +75,16 @@ class AuthorAdmin(admin.ModelAdmin): admin.site.register(models.Author, AuthorAdmin) -class PropertyAdmin(admin.ModelAdmin): - list_display = ['item', 'person', 'start_date', 'end_date'] - search_fields = ('item__label', 'person__name') - model = models.Property - -admin.site.register(models.Property, PropertyAdmin) - -class TreatmentAdmin(HistorizedObjectAdmin): - list_display = ('location', 'treatment_type', 'container', 'person') - list_filter = ('treatment_type',) - model = models.Treatment - -admin.site.register(models.Treatment, TreatmentAdmin) - -class TreatmentSourceAdmin(admin.ModelAdmin): - list_display = ('treatment', 'title', 'source_type',) - list_filter = ('source_type',) - search_fields = ('title',) - model = models.TreatmentSource - -admin.site.register(models.TreatmentSource, TreatmentSourceAdmin) - class PersonTypeAdmin(admin.ModelAdmin): model = models.PersonType filter_vertical = ('rights',) admin.site.register(models.PersonType, PersonTypeAdmin) -basic_models = [models.IshtarUser, models.FileType, models.OperationType, - models.DatingType, models.DatingQuality, models.SourceType, - models.MaterialType, models.ParcelOwner, models.WarehouseType, - models.ActType, models.AuthorType, models.OrganizationType, - models.TreatmentType, models.RemainType, models.PermitType, - models.Unit, models.ActivityType, models.IdentificationType] +basic_models = [models.IshtarUser, models.SourceType, models.AuthorType, + models.OrganizationType] if settings.COUNTRY == 'fr': - basic_models += [models.Arrondissement, models.Canton, models.SaisineType] + basic_models += [models.Arrondissement, models.Canton] for model in basic_models: admin.site.register(model) diff --git a/ishtar_common/context_processors.py b/ishtar_common/context_processors.py index 5de5a6afd..884ec19b4 100644 --- a/ishtar_common/context_processors.py +++ b/ishtar_common/context_processors.py @@ -23,6 +23,19 @@ from django.utils.translation import ugettext, ugettext_lazy as _ from menus import Menu import models +from archaeological_operations.models import Operation + +CURRENT_ITEMS = [] +if 'archaeological_files' in settings.INSTALLED_APPS: + from archaeological_files.models import File + CURRENT_ITEMS.append((_(u"Archaeological file"), File)) +CURRENT_ITEMS.append((_(u"Operation"), Operation)) +if 'archaeological_context_records' in settings.INSTALLED_APPS: + from archaeological_context_records.models import ContextRecord + CURRENT_ITEMS.append((_(u"Context record"), ContextRecord)) +if 'archaeological_finds' in settings.INSTALLED_APPS: + from archaeological_finds.models import Find + CURRENT_ITEMS.append((_(u"Find"), Find)) def get_base_context(request): dct = {'URL_PATH':settings.URL_PATH} @@ -39,11 +52,7 @@ def get_base_context(request): dct['JQUERY_URL'] = settings.JQUERY_URL dct['JQUERY_UI_URL'] = settings.JQUERY_UI_URL dct['current_menu'] = [] - for lbl, model in ((_(u"Archaeological file"), models.File), - (_(u"Operation"), models.Operation), - (_(u"Context record"), models.ContextRecord), - (_(u"Archaeological item"), models.Item), - ): + for lbl, model in CURRENT_ITEMS: model_name = model.__name__.lower() current = model_name in request.session and request.session[model_name] items = [] diff --git a/ishtar_common/forms_common.py b/ishtar_common/forms_common.py index e5f23e620..d3ae65d55 100644 --- a/ishtar_common/forms_common.py +++ b/ishtar_common/forms_common.py @@ -265,58 +265,6 @@ class TownFormSet(FormSet): TownFormset = formset_factory(TownForm, can_delete=True, formset=TownFormSet) TownFormset.form_label = _("Towns") -class ParcelForm(forms.Form): - form_label = _("Parcels") - base_model = 'parcel' - associated_models = {'parcel':models.Parcel, 'town':models.Town} - town = forms.ChoiceField(label=_("Town"), choices=(), required=False, - validators=[models.valid_id(models.Town)]) - section = forms.CharField(label=_(u"Section"), required=False, - validators=[validators.MaxLengthValidator(4)]) - parcel_number = forms.CharField(label=_(u"Parcel number"), required=False, - validators=[validators.MaxLengthValidator(6)]) - year = forms.IntegerField(label=_("Year"), required=False, - validators=[validators.MinValueValidator(1900), - validators.MaxValueValidator(2100)]) - def __init__(self, *args, **kwargs): - towns = None - if 'data' in kwargs and 'TOWNS' in kwargs['data']: - towns = kwargs['data']['TOWNS'] - # clean data if not "real" data - prefix_value = kwargs['prefix'] + '-town' - if not [k for k in kwargs['data'].keys() - if k.startswith(prefix_value) and kwargs['data'][k]]: - kwargs['data'] = None - if 'files' in kwargs: - kwargs.pop('files') - super(ParcelForm, self).__init__(*args, **kwargs) - if towns: - self.fields['town'].choices = [('', '--')] + towns - - def clean(self): - """Check required fields""" - if any(self.errors): - return - if not self.cleaned_data or DELETION_FIELD_NAME in self.cleaned_data \ - and self.cleaned_data[DELETION_FIELD_NAME]: - return - for key in ('town', 'parcel_number', 'section'): - if not key in self.cleaned_data or not self.cleaned_data[key]: - raise forms.ValidationError(_(u"Town section and parcel number " - u"fields are required.")) - return self.cleaned_data - - -class ParcelFormSet(FormSet): - def clean(self): - """Checks that no parcels are duplicated.""" - return self.check_duplicate(('town', 'parcel_number', 'year'), - _(u"There are identical parcels.")) - -ParcelFormSet = formset_factory(ParcelForm, can_delete=True, - formset=ParcelFormSet) -ParcelFormSet.form_label = _(u"Parcels") - ###################### # Sources management # ###################### diff --git a/ishtar_common/menus.py b/ishtar_common/menus.py index c55e2a328..3737846b9 100644 --- a/ishtar_common/menus.py +++ b/ishtar_common/menus.py @@ -47,127 +47,31 @@ _extra_menus = [( ] )] +# collect menu from INSTALLED_APPS for app in settings.INSTALLED_APPS: + print app + if app == 'ishtar_common': + continue mod = __import__(app, fromlist=['ishtar_menu']) if hasattr(mod, 'ishtar_menu'): menu = getattr(mod, 'ishtar_menu') - _extra_menus.append((menu.ORDER, menu.MENU_SECTIONS)) + _extra_menus += menu.MENU_SECTIONS -_section_items = [] -for order, menu in sorted(_extra_menus, key=lambda x:x[0]): - _section_items += menu +# sort +__section_items = [menu for order, menu in sorted(_extra_menus, + key=lambda x:x[0])] + +# regroup menus +_section_items, __keys = [], [] +for section_item in _section_items: + if section_item.idx not in __keys: + __keys.append(section_item.idx) + _section_items.append(section_item) + continue + _section_items[_section_items.index(section_item.idx)].childs.append( + section_item.childs) """ - SectionItem('record_management', _(u"Context record"), - childs=[ - MenuItem('record_search', _(u"Search"), - model=models.ContextRecord, - access_controls=['view_contextrecord', - 'view_own_contextrecord']), - MenuItem('record_creation', _(u"Creation"), - model=models.ContextRecord, - access_controls=['add_contextrecord', - 'add_own_contextrecord']), - MenuItem('record_modification', _(u"Modification"), - model=models.ContextRecord, - access_controls=['change_contextrecord', - 'change_own_contextrecord']), - MenuItem('record_deletion', _(u"Deletion"), - model=models.ContextRecord, - access_controls=['delete_contextrecord', - 'delete_own_contextrecord']), - ]), - SectionItem('item_management', _(u"Item"), - childs=[ - MenuItem('item_search', _(u"Search"), - model=models.Item, - access_controls=['view_item', - 'view_own_item']), - MenuItem('item_creation', _(u"Creation"), - model=models.Item, - access_controls=['add_item', - 'add_own_item']), - MenuItem('item_modification', _(u"Modification"), - model=models.Item, - access_controls=['change_item', - 'change_own_item']), - MenuItem('warehouse_packaging', _(u"Packaging"), - model=models.Treatment, - access_controls=['add_treatment', 'add_own_treatment']), - #MenuItem('treatment_creation', _(u"Add a treatment"), - # model=models.Treatment, - # access_controls=['add_treatment', - # 'add_own_treatment']), - ]), - SectionItem('source_management', _(u"Documentation"), - childs=[ - SectionItem('admin_add_sources', _(u"Add"), - childs=[ - MenuItem('operation_source_creation', - _(u"Related to an operation"), - model=models.OperationSource, - access_controls=['change_operation', - 'change_own_operation']), - MenuItem('record_source_creation', - _(u"Related to a context record"), - model=models.ContextRecordSource, - access_controls=['change_contextrecord', - 'change_own_contextrecord']), - MenuItem('item_source_creation', - _(u"Related to an archaelogical item"), - model=models.ItemSource, - access_controls=['change_item', - 'change_own_item']), - ]), - SectionItem('admin_mod_sources', _(u"Modify"), - childs=[ - MenuItem('operation_source_modification', - _(u"Related to an operation"), - model=models.OperationSource, - access_controls=['change_operation', - 'change_own_operation']), - MenuItem('record_source_modification', - _(u"Related to a context record"), - model=models.ContextRecordSource, - access_controls=['change_contextrecord', - 'change_own_contextrecord']), - MenuItem('item_source_modification', - _(u"Related to an archaelogical item"), - model=models.ItemSource, - access_controls=['change_item', - 'change_own_item']), - ]), - SectionItem('admin_del_sources', _(u"Deletion"), - childs=[ - MenuItem('operation_source_deletion', - _(u"Related to an operation"), - model=models.OperationSource, - access_controls=['change_operation', - 'change_own_operation']), - MenuItem('record_source_deletion', - _(u"Related to a context record"), - model=models.ContextRecordSource, - access_controls=['change_contextrecord', - 'change_own_contextrecord']), - MenuItem('item_source_deletion', - _(u"Related to an archaelogical item"), - model=models.ItemSource, - access_controls=['change_item', - 'change_own_item']), - ]), - ]), - #SectionItem('warehouse', _(u"Warehouse"), - # childs=[ - # MenuItem('warehouse_inventory', _(u"Inventory"), - # model=models.Warehouse, - # access_controls=['change_warehouse',]), - # MenuItem('warehouse_recording', _(u"Recording"), - # model=models.Treatment, - # access_controls=['add_treatment', 'add_own_treatment']), - # MenuItem('warehouse_lend', _(u"Lending"), - # model=models.Treatment, - # access_controls=['add_treatment', 'add_own_treatment']), - # ]), SectionItem('dashboard', _(u"Dashboard"), childs=[ MenuItem('dashboard_main', _(u"General informations"), diff --git a/ishtar_common/models.py b/ishtar_common/models.py index 4406a2b86..b4bf2cd57 100644 --- a/ishtar_common/models.py +++ b/ishtar_common/models.py @@ -398,570 +398,6 @@ class UserDashboard: self.types = types.annotate(number=Count('pk'))\ .order_by('person__person_type') -class FileDashboard: - def __init__(self): - main_dashboard = Dashboard(File) - - self.total_number = main_dashboard.total_number - - types = File.objects.values('file_type', 'file_type__label') - self.types = types.annotate(number=Count('pk')).order_by('file_type') - - by_year = File.objects.extra( - {'date':"date_trunc('year', creation_date)"}) - self.by_year = by_year.values('date')\ - .annotate(number=Count('pk')).order_by('-date') - - now = datetime.date.today() - limit = datetime.date(now.year, now.month, 1) - datetime.timedelta(365) - by_month = File.objects.filter(creation_date__gt=limit).extra( - {'date':"date_trunc('month', creation_date)"}) - self.by_month = by_month.values('date')\ - .annotate(number=Count('pk')).order_by('-date') - - # research - self.research = {} - prog_type = FileType.objects.get(txt_idx='prog') - researchs = File.objects.filter(file_type=prog_type) - self.research['total_number'] = researchs.count() - by_year = researchs.extra({'date':"date_trunc('year', creation_date)"}) - self.research['by_year'] = by_year.values('date')\ - .annotate(number=Count('pk'))\ - .order_by('-date') - by_month = researchs.filter(creation_date__gt=limit)\ - .extra({'date':"date_trunc('month', creation_date)"}) - self.research['by_month'] = by_month.values('date')\ - .annotate(number=Count('pk'))\ - .order_by('-date') - - self.research['by_dpt'] = FileByDepartment.objects\ - .filter(file__file_type=prog_type, - department__isnull=False)\ - .values('department__label')\ - .annotate(number=Count('file'))\ - .order_by('department__label') - FileTown = File.towns.through - self.research['towns'] = FileTown.objects\ - .filter(file__file_type=prog_type)\ - .values('town__name')\ - .annotate(number=Count('file'))\ - .order_by('-number','town__name')[:10] - - # rescue - rescue_type = FileType.objects.get(txt_idx='preventive') - rescues = File.objects.filter(file_type=rescue_type) - self.rescue = {} - self.rescue['total_number'] = rescues.count() - self.rescue['saisine'] = rescues.values('saisine_type__label')\ - .annotate(number=Count('pk'))\ - .order_by('saisine_type__label') - self.rescue['administrative_act'] = AdministrativeAct.objects\ - .filter(associated_file__isnull=False)\ - .values('act_type__label')\ - .annotate(number=Count('pk'))\ - .order_by('act_type__pk') - - by_year = rescues.extra({'date':"date_trunc('year', creation_date)"}) - self.rescue['by_year'] = by_year.values('date')\ - .annotate(number=Count('pk'))\ - .order_by('-date') - by_month = rescues.filter(creation_date__gt=limit)\ - .extra({'date':"date_trunc('month', creation_date)"}) - self.rescue['by_month'] = by_month.values('date')\ - .annotate(number=Count('pk'))\ - .order_by('-date') - - self.rescue['by_dpt'] = FileByDepartment.objects\ - .filter(file__file_type=rescue_type, - department__isnull=False)\ - .values('department__label')\ - .annotate(number=Count('file'))\ - .order_by('department__label') - self.rescue['towns'] = FileTown.objects\ - .filter(file__file_type=rescue_type)\ - .values('town__name')\ - .annotate(number=Count('file'))\ - .order_by('-number','town__name')[:10] - - self.rescue['with_associated_operation'] = rescues\ - .filter(operations__isnull=False).count() - - self.rescue['with_associated_operation_percent'] = round( - float(self.rescue['with_associated_operation'])\ - /self.rescue['total_number']*100, 2) - - by_year_operationnal = rescues.filter(operations__isnull=False)\ - .extra({'date':"date_trunc('year', creation_date)"}) - by_year_operationnal = by_year_operationnal.values('date')\ - .annotate(number=Count('pk'))\ - .order_by('-date') - percents, idx = [], 0 - for dct in self.rescue['by_year']: - if idx > len(by_year_operationnal): - break - if by_year_operationnal[idx]['date'] != dct['date'] or\ - not dct['number']: - continue - val = round(float(by_year_operationnal[idx]['number'])/\ - dct['number']*100, 2) - percents.append({'date':dct['date'], 'number':val}) - self.rescue['operational_by_year'] = percents - - self.rescue['surface_by_town'] = FileTown.objects\ - .filter(file__file_type=rescue_type)\ - .values('town__name')\ - .annotate(number=Sum('file__total_surface'))\ - .order_by('-number','town__name')[:10] - self.rescue['surface_by_dpt'] = FileByDepartment.objects\ - .filter(file__file_type=rescue_type, - department__isnull=False)\ - .values('department__label')\ - .annotate(number=Sum('file__total_surface'))\ - .order_by('department__label') - -class OperationDashboard: - def __init__(self): - main_dashboard = Dashboard(Operation) - - self.total_number = main_dashboard.total_number - - self.filters_keys = ['recorded', 'effective', 'active', 'field', - 'documented', 'closed', 'documented_closed'] - filters = { - 'recorded':{}, - 'effective':{'in_charge__isnull':False}, - 'active':{'in_charge__isnull':False, 'end_date__isnull':True}, - 'field':{'excavation_end_date__isnull':True}, - 'documented':{'source__isnull':False}, - 'documented_closed':{'source__isnull':False, - 'end_date__isnull':False}, - 'closed':{'end_date__isnull':False} - } - filters_label = { - 'recorded':_(u"Recorded"), - 'effective':_(u"Effective"), - 'active':_(u"Active"), - 'field':_(u"Field completed"), - 'documented':_(u"Associated report"), - 'closed':_(u"Closed"), - 'documented_closed':_(u"Documented and closed"), - } - self.filters_label = [filters_label[k] for k in self.filters_keys] - self.total = [] - for fltr_key in self.filters_keys: - fltr, lbl = filters[fltr_key], filters_label[fltr_key] - nb = Operation.objects.filter(**fltr).count() - self.total.append((lbl, nb)) - - self.surface_by_type = Operation.objects\ - .values('operation_type__label')\ - .annotate(number=Sum('surface'))\ - .order_by('-number','operation_type__label') - - self.by_type = [] - self.types = OperationType.objects.filter(available=True).all() - for fltr_key in self.filters_keys: - fltr, lbl = filters[fltr_key], filters_label[fltr_key] - type_res = Operation.objects.filter(**fltr).\ - values('operation_type', 'operation_type__label').\ - annotate(number=Count('pk')).\ - order_by('operation_type') - types_dct = {} - for typ in type_res.all(): - types_dct[typ['operation_type']] = typ["number"] - types = [] - for typ in self.types: - if typ.pk in types_dct: - types.append(types_dct[typ.pk]) - else: - types.append(0) - self.by_type.append((lbl, types)) - - self.by_year = [] - self.years = [res['year'] for res in Operation.objects.values('year')\ - .order_by('-year').distinct()] - for fltr_key in self.filters_keys: - fltr, lbl = filters[fltr_key], filters_label[fltr_key] - year_res = Operation.objects.filter(**fltr).\ - values('year').\ - annotate(number=Count('pk')).\ - order_by('year') - years_dct = {} - for yr in year_res.all(): - years_dct[yr['year']] = yr["number"] - years = [] - for yr in self.years: - if yr in years_dct: - years.append(years_dct[yr]) - else: - years.append(0) - self.by_year.append((lbl, years)) - - self.by_realisation_year = [] - self.realisation_years = [res['date'] for res in \ - Operation.objects.extra( - {'date':"date_trunc('year', start_date)"}).values('date')\ - .filter(start_date__isnull=False).order_by('-date').distinct()] - for fltr_key in self.filters_keys: - fltr, lbl = filters[fltr_key], filters_label[fltr_key] - year_res = Operation.objects.filter(**fltr).extra( - {'date':"date_trunc('year', start_date)"}).values('date').\ - values('date').filter(start_date__isnull=False).\ - annotate(number=Count('pk')).\ - order_by('-date') - years_dct = {} - for yr in year_res.all(): - years_dct[yr['date']] = yr["number"] - years = [] - for yr in self.realisation_years: - if yr in years_dct: - years.append(years_dct[yr]) - else: - years.append(0) - self.by_realisation_year.append((lbl, years)) - - self.effective = [] - for typ in self.types: - year_res = Operation.objects.filter(**{'in_charge__isnull':False, - 'operation_type':typ}).\ - values('year').\ - annotate(number=Count('pk')).\ - order_by('-year').distinct() - years_dct = {} - for yr in year_res.all(): - years_dct[yr['year']] = yr["number"] - years = [] - for yr in self.years: - if yr in years_dct: - years.append(years_dct[yr]) - else: - years.append(0) - self.effective.append((typ, years)) - - # TODO: by date - now = datetime.date.today() - limit = datetime.date(now.year, now.month, 1) - datetime.timedelta(365) - by_realisation_month = Operation.objects.filter(start_date__gt=limit, - start_date__isnull=False).extra( - {'date':"date_trunc('month', start_date)"}) - self.last_months = [] - date = datetime.datetime(now.year, now.month, 1) - for mt_idx in xrange(12): - self.last_months.append(date) - if date.month > 1: - date = datetime.datetime(date.year, date.month - 1, 1) - else: - date = datetime.datetime(date.year - 1, 12, 1) - self.by_realisation_month = [] - for fltr_key in self.filters_keys: - fltr, lbl = filters[fltr_key], filters_label[fltr_key] - month_res = by_realisation_month.filter(**fltr).\ - annotate(number=Count('pk')).\ - order_by('-date') - month_dct = {} - for mt in month_res.all(): - month_dct[mt.date] = mt.number - date = datetime.date(now.year, now.month, 1) - months = [] - for date in self.last_months: - if date in month_dct: - months.append(month_dct[date]) - else: - months.append(0) - self.by_realisation_month.append((lbl, months)) - - # survey and excavations - self.survey, self.excavation = {}, {} - for dct_res, ope_types in ((self.survey, ('arch_diagnostic',)), - (self.excavation, ('prev_excavation', - 'prog_excavation'))): - dct_res['total'] = [] - operation_type = {'operation_type__txt_idx__in':ope_types} - for fltr_key in self.filters_keys: - fltr, lbl = filters[fltr_key], filters_label[fltr_key] - fltr.update(operation_type) - nb = Operation.objects.filter(**fltr).count() - dct_res['total'].append((lbl, nb)) - - dct_res['by_year'] = [] - for fltr_key in self.filters_keys: - fltr, lbl = filters[fltr_key], filters_label[fltr_key] - fltr.update(operation_type) - year_res = Operation.objects.filter(**fltr).\ - values('year').\ - annotate(number=Count('pk')).\ - order_by('year') - years_dct = {} - for yr in year_res.all(): - years_dct[yr['year']] = yr["number"] - years = [] - for yr in self.years: - if yr in years_dct: - years.append(years_dct[yr]) - else: - years.append(0) - dct_res['by_year'].append((lbl, years)) - - dct_res['by_realisation_year'] = [] - for fltr_key in self.filters_keys: - fltr, lbl = filters[fltr_key], filters_label[fltr_key] - fltr.update(operation_type) - year_res = Operation.objects.filter(**fltr).extra( - {'date':"date_trunc('year', start_date)"}).values('date').\ - filter(start_date__isnull=False).\ - annotate(number=Count('pk')).\ - order_by('-date') - years_dct = {} - for yr in year_res.all(): - years_dct[yr['date']] = yr["number"] - years = [] - for yr in self.realisation_years: - if yr in years_dct: - years.append(years_dct[yr]) - else: - years.append(0) - dct_res['by_realisation_year'].append((lbl, years)) - - current_year_ope = Operation.objects.filter(**operation_type)\ - .filter(year=datetime.date.today().year) - current_realisation_year_ope = Operation.objects\ - .filter(**operation_type)\ - .filter(start_date__year=datetime.date.today().year) - res_keys = [('area_realised', current_realisation_year_ope)] - if dct_res == self.survey: - res_keys.append(('area', - current_year_ope)) - for res_key, base_ope in res_keys: - dct_res[res_key] = [] - for fltr_key in self.filters_keys: - fltr, lbl = filters[fltr_key], filters_label[fltr_key] - area_res = base_ope.filter(**fltr)\ - .annotate(number=Sum('surface')).all() - val = 0 - if area_res: - val = area_res[0].number - dct_res[res_key].append(val) - # TODO... - res_keys = [('manday_realised', current_realisation_year_ope)] - if dct_res == self.survey: - res_keys.append(('manday', - current_year_ope)) - for res_key, base_ope in res_keys: - dct_res[res_key] = [] - for fltr_key in self.filters_keys: - dct_res[res_key].append('-') - # TODO... - res_keys = [('mandayhect_realised', current_realisation_year_ope)] - if dct_res == self.survey: - res_keys.append(('mandayhect', - current_year_ope)) - for res_key, base_ope in res_keys: - dct_res[res_key] = [] - for fltr_key in self.filters_keys: - dct_res[res_key].append('-') - # TODO... - dct_res['mandayhect_real_effective'] = '-' - if dct_res == self.survey: - dct_res['mandayhect_effective'] = '-' - - - res_keys = [('org_realised', current_realisation_year_ope)] - if dct_res == self.survey: - res_keys.append(('org', current_year_ope)) - for res_key, base_ope in res_keys: - org_res = base_ope.filter(in_charge__attached_to__isnull=False)\ - .values('in_charge__attached_to', - 'in_charge__attached_to__name')\ - .annotate(area=Sum('surface'))\ - .order_by('in_charge__attached_to__name').all() - # TODO: man-days, man-days/hectare - dct_res[res_key] = org_res - - - year_ope = Operation.objects.filter(**operation_type) - res_keys = ['org_by_year'] - if dct_res == self.survey: - res_keys.append('org_by_year_realised') - q = year_ope.values('in_charge__attached_to', - 'in_charge__attached_to__name').\ - filter(in_charge__attached_to__isnull=False).\ - order_by('in_charge__attached_to__name').distinct() - org_list = [(org['in_charge__attached_to'], - org['in_charge__attached_to__name']) for org in q] - org_list_dct = dict(org_list) - for res_key in res_keys: - dct_res[res_key] = [] - years = self.years - if res_key == 'org_by_year_realised': - years = self.realisation_years - for org_id, org_label in org_list: - org_res = year_ope.filter(in_charge__attached_to__pk=org_id) - key_date = '' - if res_key == 'org_by_year': - org_res = org_res.values('year') - key_date = 'year' - else: - org_res = org_res.extra( - {'date':"date_trunc('year', start_date)"}).values('date').\ - filter(start_date__isnull=False) - key_date = 'date' - org_res = org_res.annotate(area=Sum('surface'), - cost=Sum('cost')) - years_dct = {} - for yr in org_res.all(): - area = yr['area'] if yr['area'] else 0 - cost = yr['cost'] if yr['cost'] else 0 - years_dct[yr[key_date]] = (area, cost) - r_years = [] - for yr in years: - if yr in years_dct: - r_years.append(years_dct[yr]) - else: - r_years.append((0, 0)) - dct_res[res_key].append((org_label, r_years)) - area_means, area_sums = [], [] - cost_means, cost_sums = [], [] - for idx, year in enumerate(years): - vals = [r_years[idx] for lbl, r_years in dct_res[res_key]] - sum_area = sum([a for a, c in vals]) - sum_cost = sum([c for a, c in vals]) - area_means.append(sum_area/len(vals)) - area_sums.append(sum_area) - cost_means.append(sum_cost/len(vals)) - cost_sums.append(sum_cost) - dct_res[res_key+'_area_mean'] = area_means - dct_res[res_key+'_area_sum'] = area_sums - dct_res[res_key+'_cost_mean'] = cost_means - dct_res[res_key+'_cost_mean'] = cost_sums - - if dct_res == self.survey: - self.survey['effective'] = [] - for yr in self.years: - year_res = Operation.objects.filter(in_charge__isnull=False, - year=yr).\ - annotate(number=Sum('surface'), - mean=Avg('surface')) - nb = year_res[0].number if year_res.count() else 0 - nb = nb if nb else 0 - mean = year_res[0].mean if year_res.count() else 0 - mean = mean if mean else 0 - self.survey['effective'].append((nb, mean)) - - # TODO:Man-Days/hectare by Year - - # CHECK: month of realisation or month? - dct_res['by_month'] = [] - for fltr_key in self.filters_keys: - fltr, lbl = filters[fltr_key], filters_label[fltr_key] - fltr.update(operation_type) - month_res = by_realisation_month.filter(**fltr).\ - annotate(number=Count('pk')).\ - order_by('-date') - month_dct = {} - for mt in month_res.all(): - month_dct[mt.date] = mt.number - date = datetime.date(now.year, now.month, 1) - months = [] - for date in self.last_months: - if date in month_dct: - months.append(month_dct[date]) - else: - months.append(0) - dct_res['by_month'].append((lbl, months)) - - operation_type = {'operation_type__txt_idx__in':ope_types} - self.departments = [(fd['department__pk'], fd['department__label']) - for fd in OperationByDepartment.objects\ - .filter(department__isnull=False)\ - .values('department__label', 'department__pk')\ - .order_by('department__label').distinct()] - dct_res['by_dpt'] = [] - for dpt_id, dpt_label in self.departments: - vals = OperationByDepartment.objects\ - .filter(department__pk=dpt_id, - operation__operation_type__txt_idx__in=ope_types)\ - .values('department__pk', 'operation__year')\ - .annotate(number=Count('operation'))\ - .order_by('operation__year') - dct_years = {} - for v in vals: - dct_years[v['operation__year']] = v['number'] - years = [] - for y in self.years: - if y in dct_years: - years.append(dct_years[y]) - else: - years.append(0) - years.append(sum(years)) - dct_res['by_dpt'].append((dpt_label, years)) - dct_res['effective_by_dpt'] = [] - for dpt_id, dpt_label in self.departments: - vals = OperationByDepartment.objects\ - .filter(department__pk=dpt_id, - operation__in_charge__isnull=False, - operation__operation_type__txt_idx__in=ope_types)\ - .values('department__pk', 'operation__year')\ - .annotate(number=Count('operation'), - area=Sum('operation__surface'), - fnap=Sum('operation__fnap_cost'), - cost=Sum('operation__cost'))\ - .order_by('operation__year') - dct_years = {} - for v in vals: - values = [] - for value in (v['number'], v['area'], v['cost'], v['fnap']): - values.append(value if value else 0) - dct_years[v['operation__year']] = values - years = [] - for y in self.years: - if y in dct_years: - years.append(dct_years[y]) - else: - years.append((0, 0, 0, 0)) - nbs, areas, costs, fnaps = zip(*years) - years.append((sum(nbs), sum(areas), sum(costs), sum(fnaps))) - dct_res['effective_by_dpt'].append((dpt_label, years)) - - OperationTown = Operation.towns.through - query = OperationTown.objects\ - .filter(operation__in_charge__isnull=False, - operation__operation_type__txt_idx__in=ope_types)\ - .values('town__name', 'town__departement__number')\ - .annotate(nb=Count('operation'))\ - .order_by('-nb', 'town__name')[:10] - dct_res['towns'] = [] - for r in query: - dct_res['towns'].append((u"%s (%s)" % (r['town__name'], - r['town__departement__number']), - r['nb'])) - - if dct_res == self.survey: - query = OperationTown.objects\ - .filter(operation__in_charge__isnull=False, - operation__operation_type__txt_idx__in=ope_types, - operation__surface__isnull=False)\ - .values('town__name', 'town__departement__number')\ - .annotate(nb=Sum('operation__surface'))\ - .order_by('-nb', 'town__name')[:10] - dct_res['towns_surface'] = [] - for r in query: - dct_res['towns_surface'].append((u"%s (%s)" % ( - r['town__name'], r['town__departement__number']), - r['nb'])) - else: - query = OperationTown.objects\ - .filter(operation__in_charge__isnull=False, - operation__operation_type__txt_idx__in=ope_types, - operation__cost__isnull=False)\ - .values('town__name', 'town__departement__number')\ - .annotate(nb=Sum('operation__cost'))\ - .order_by('-nb', 'town__name')[:10] - dct_res['towns_cost'] = [] - for r in query: - dct_res['towns_cost'].append((u"%s (%s)" % (r['town__name'], - r['town__departement__number']), - r['nb'])) - class Dashboard: def __init__(self, model): self.model = model @@ -973,7 +409,7 @@ class Dashboard: last_ids = history_model.objects.values('id')\ .annotate(hd=Max('history_date')) last_ids = last_ids.filter(history_type=modif_type) - if self.model == Item: + if self.model == Find: last_ids = last_ids.filter(downstream_treatment_id__isnull=True) if modif_type == '+': last_ids = last_ids.filter(upstream_treatment_id__isnull=True) @@ -1252,5 +688,3 @@ class Town(models.Model): if settings.COUNTRY == "fr": return u"%s (%s)" % (self.name, self.numero_insee) return self.name - - diff --git a/ishtar_common/templates/sheet_contextrecord.html b/ishtar_common/templates/sheet_contextrecord.html index c2b94bafe..806b06288 100644 --- a/ishtar_common/templates/sheet_contextrecord.html +++ b/ishtar_common/templates/sheet_contextrecord.html @@ -109,7 +109,7 @@ <th>{% trans "Parcel" %}</th> <th class='link'> </th> </tr> - {% for find in item.base_items.all %} + {% for find in item.base_finds.all %} <tr> <td>{{ find.full_label }}</td> {# Displayed as (Patriarche operation code)-(Record unit label)-(Finds label). #} @@ -119,10 +119,10 @@ {# or displayed as (Year)-(index)-(Record unit label)-(material code)-(Finds label indexed by material type) #} <td class='string'>{{find.context_record}}</td> - <td>{{ find.get_last_item.dating}}</td>{# TODO .all|join:", " ? #} - <td>{{ find.get_last_item.description }}</td> - <td>{{ find.get_last_item.weight }}</td> - <td>{{ find.get_last_item.item_number }}</td> + <td>{{ find.get_last_find.dating}}</td>{# TODO .all|join:", " ? #} + <td>{{ find.get_last_find.description }}</td> + <td>{{ find.get_last_find.weight }}</td> + <td>{{ find.get_last_find.item_number }}</td> <td>{{ item.context_record.parcel.short_label }}</td> <td class='link'><a href="#">{% trans "Details" %}</a></td> {#<a href="#" onclick='load_window("{% url show-find find.pk%}");'>{%trans "Details"%}</a></td>#} diff --git a/ishtar_common/templates/sheet_operation.html b/ishtar_common/templates/sheet_operation.html index aa571d20c..edceca989 100644 --- a/ishtar_common/templates/sheet_operation.html +++ b/ishtar_common/templates/sheet_operation.html @@ -156,17 +156,17 @@ <th class='link'> </th> </tr> {% for context_record in item.context_record.all %} - {% for find in context_record.base_items.all %} + {% for find in context_record.base_finds.all %} <tr> <td class="ref">{{ find.full_label }}</td> {# Displayed as (Patriarche operation code)-(Record unit label)-(Finds label). #} {# or displayed as (Year)-(index)-(Record unit label)-(Finds label). #} <td class="ref">{{ find.material_type_label }}</td> <td>{{find.context_record.label}}</td> - <td class='string'>{{ find.get_last_item.dating}}</td>{# TODO .all|join:", " ? #} - <td class='string'>{{ find.get_last_item.description }}</td> - <td>{{ find.get_last_item.weight }}</td> - <td>{{ find.get_last_item.item_number }}</td> + <td class='string'>{{ find.get_last_find.dating}}</td>{# TODO .all|join:", " ? #} + <td class='string'>{{ find.get_last_find.description }}</td> + <td>{{ find.get_last_find.weight }}</td> + <td>{{ find.get_last_find.item_number }}</td> <td class="ref">{{ context_record.parcel.short_label }}</td> <td class='link'><a href="#">{% trans "Details" %}</a></td> {#<a href="#" onclick='load_window("{% url show-find find.pk%}");'>{%trans "Details"%}</a></td>#} diff --git a/ishtar_common/urls.py b/ishtar_common/urls.py index f6636255e..40ef4a1d2 100644 --- a/ishtar_common/urls.py +++ b/ishtar_common/urls.py @@ -1,6 +1,6 @@ #!/usr/bin/env python # -*- coding: utf-8 -*- -# Copyright (C) 2010-2011 Étienne Loks <etienne.loks_AT_peacefrogsDOTnet> +# Copyright (C) 2010-2012 É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 @@ -37,97 +37,6 @@ urlpatterns = patterns('', url(r'account_management/(?P<step>.+)?$', views.account_management_wizard, name='account_management'), ) -""" - # Archaelogical files - url(r'file_search/(?P<step>.+)$', - ishtar_forms.file_search_wizard, name='file_search'), - url(r'file_creation/(?P<step>.+)$', - ishtar_forms.file_creation_wizard, name='file_creation'), - url(r'file_modification/(?P<step>.+)$', - ishtar_forms.file_modification_wizard, name='file_modification'), - url(r'file_closing/(?P<step>.+)$', - ishtar_forms.file_closing_wizard, name='file_closing'), - url(r'file_deletion/(?P<step>.+)$', - ishtar_forms.file_deletion_wizard, name='file_deletion'), - url(r'file_administrativeactfile/(?P<step>.+)$', - ishtar_forms.file_administrativeactfile_wizard, - name='file_administrativeactfile'), - url(r'file_administrativeactfile_modification/(?P<step>.+)$', - ishtar_forms.file_administrativeactfile_modification_wizard, - name='file_administrativeactfile_modification'), - url(r'file_administrativeactfile_deletion/(?P<step>.+)$', - ishtar_forms.file_administrativeactfile_deletion_wizard, - name='file_administrativeactfile_deletion'), - # Operations - url(r'operation_search/(?P<step>.+)$', - ishtar_forms.operation_search_wizard, name='operation_search'), - url(r'operation_creation/(?P<step>.+)$', - ishtar_forms.operation_creation_wizard, name='operation_creation'), - url(r'operation_modification/(?P<step>.+)$', - ishtar_forms.operation_modification_wizard, - name='operation_modification'), - url(r'operation_closing/(?P<step>.+)$', - ishtar_forms.operation_closing_wizard, name='operation_closing'), - url(r'operation_deletion/(?P<step>.+)$', - ishtar_forms.operation_deletion_wizard, name='operation_deletion'), - url(r'operation_administrativeactop/(?P<step>.+)$', - ishtar_forms.operation_administrativeactop_wizard, - name='operation_administrativeactop'), - url(r'operation_administrativeactop_modification/(?P<step>.+)$', - ishtar_forms.operation_administrativeactop_modification_wizard, - name='operation_administrativeactop_modification'), - url(r'operation_administrativeactop_deletion/(?P<step>.+)$', - ishtar_forms.operation_administrativeactop_deletion_wizard, - name='operation_administrativeactop_deletion'), - url(r'operation_source_creation/(?P<step>.+)$', - ishtar_forms.operation_source_creation_wizard, - name='operation_source_creation'), - url(r'operation_source_modification/(?P<step>.+)$', - ishtar_forms.operation_source_modification_wizard, - name='operation_source_modification'), - url(r'operation_source_deletion/(?P<step>.+)$', - ishtar_forms.operation_source_deletion_wizard, - name='operation_source_deletion'), - # Context records - url(r'record_search/(?P<step>.+)$', - ishtar_forms.record_search_wizard, name='record_search'), - url(r'record_creation/(?P<step>.+)$', - ishtar_forms.record_creation_wizard, name='record_creation'), - url(r'record_modification/(?P<step>.+)$', - ishtar_forms.record_modification_wizard, name='record_modification'), - url(r'record_deletion/(?P<step>.+)$', - ishtar_forms.record_deletion_wizard, name='record_deletion'), - url(r'record_source_creation/(?P<step>.+)$', - ishtar_forms.record_source_creation_wizard, - name='record_source_creation'), - url(r'record_source_modification/(?P<step>.+)$', - ishtar_forms.record_source_modification_wizard, - name='record_source_modification'), - url(r'record_source_deletion/(?P<step>.+)$', - ishtar_forms.record_source_deletion_wizard, - name='record_source_deletion'), - # Finds - url(r'item_search/(?P<step>.+)$', - ishtar_forms.item_search_wizard, name='item_search'), - url(r'item_creation/(?P<step>.+)$', - ishtar_forms.item_creation_wizard, name='item_creation'), - url(r'item_modification/(?P<step>.+)$', - ishtar_forms.item_modification_wizard, name='item_modification'), - url(r'item_source_creation/(?P<step>.+)$', - ishtar_forms.item_source_creation_wizard, - name='item_source_creation'), - url(r'item_source_modification/(?P<step>.+)$', - ishtar_forms.item_source_modification_wizard, - name='item_source_modification'), - url(r'item_source_deletion/(?P<step>.+)$', - ishtar_forms.item_source_deletion_wizard, - name='item_source_deletion'), - # Treatments - url(r'treatment_creation/(?P<step>.+)$', - ishtar_forms.treatment_creation_wizard, name='treatment_creation'), - url(r'warehouse_packaging/(?P<step>.+)$', - ishtar_forms.warehouse_packaging_wizard, name='warehouse_packaging'), - )""" for section in menu.childs: for menu_item in section.childs: if hasattr(menu_item, 'childs'): @@ -142,6 +51,8 @@ urlpatterns += patterns('ishtar_common.views', # General url(r'(?P<action_slug>' + actions + r')/$', 'action', name='action'), + url(r'update-current-item/$', 'update_current_item', + name='update-current-item'), url(r'new-person/(?P<parent_name>.+)?/$', 'new_person', name='new-person'), url(r'autocomplete-person/([0-9_]+)?$', 'autocomplete_person', @@ -156,66 +67,4 @@ urlpatterns += patterns('ishtar_common.views', 'new_organization', name='new-organization'), url(r'autocomplete-organization/([0-9_]+)?$', 'autocomplete_organization', name='autocomplete-organization'), - url(r'new-warehouse/(?P<parent_name>.+)?/$', - 'new_warehouse', name='new-warehouse'), - url(r'autocomplete-warehouse/$', 'autocomplete_warehouse', - name='autocomplete-warehouse'), - # Archaelogical files - url(r'autocomplete-file/$', 'autocomplete_file', - name='autocomplete-file'), - url(r'get-file/(?P<type>.+)?$', 'get_file', - name='get-file'), - url(r'get-file-full/(?P<type>.+)?$', 'get_file', - name='get-file-full', kwargs={'full':True}), - url(r'get-administrativeactfile/(?P<type>.+)?$', - 'get_administrativeactfile', name='get-administrativeactfile'), - url(r'show-file/(?P<pk>.+)?/(?P<type>.+)?$', 'show_file', - name='show-file'), - url(r'show-historized-file/(?P<pk>.+)?/(?P<date>.+)?$', - 'show_file', name='show-historized-file'), - url(r'revert-file/(?P<pk>.+)/(?P<date>.+)$', - 'revert_file', name='revert-file'), - # Operations - url(r'autocomplete-operation/$', 'autocomplete_operation', - name='autocomplete-operation'), - url(r'get-operation/(?P<type>.+)?$', 'get_operation', - name='get-operation'), - url(r'get-operation-full/(?P<type>.+)?$', 'get_operation', - name='get-operation-full', kwargs={'full':True}), - url(r'get-available-operation-code/(?P<year>.+)?$', - 'get_available_operation_code', name='get_available_operation_code'), - url(r'revert-operation/(?P<pk>.+)/(?P<date>.+)$', - 'revert_operation', name='revert-operation'), - url(r'show-operation/(?P<pk>.+)?/(?P<type>.+)?$', - 'show_operation', name='show-operation'), - url(r'get-administrativeactop/(?P<type>.+)?$', - 'get_administrativeactop', name='get-administrativeactop'), - url(r'get-operationsource/(?P<type>.+)?$', - 'get_operationsource', name='get-operationsource'), - # Context records - url(r'show-contextrecord/(?P<pk>.+)?/(?P<type>.+)?$', - 'show_contextrecord', name='show-contextrecord'), - url(r'get-contextrecord/(?P<type>.+)?$', 'get_contextrecord', - name='get-contextrecord'), - url(r'get-contextrecord-full/(?P<type>.+)?$', - 'get_contextrecord', name='get-contextrecord-full', - kwargs={'full':True}), - url(r'get-contexrecordsource/(?P<type>.+)?$', - 'get_contextrecordsource', name='get-contextrecordsource'), - # Finds - url(r'update-current-item/$', 'update_current_item', - name='update-current-item'), - url(r'get-item/(?P<type>.+)?$', 'get_archaeologicalitem', - name='get-item'), - url(r'get-item-full/(?P<type>.+)?$', 'get_archaeologicalitem', - name='get-item-full', kwargs={'full':True}), - url(r'get-itemsource/(?P<type>.+)?$', - 'get_itemsource', name='get-itemsource'), - url(r'get-container/$', 'get_container', - name='get-container'), - # Treatments - url(r'autocomplete-container/?$', - 'autocomplete_container', name='autocomplete-container'), - url(r'new-container/(?P<parent_name>.+)?/$', - 'new_container', name='new-container'), ) diff --git a/ishtar_common/views.py b/ishtar_common/views.py index fe464ade7..69fa8b90b 100644 --- a/ishtar_common/views.py +++ b/ishtar_common/views.py @@ -1,6 +1,6 @@ #!/usr/bin/env python # -*- coding: utf-8 -*- -# Copyright (C) 2010-2011 Étienne Loks <etienne.loks_AT_peacefrogsDOTnet> +# Copyright (C) 2010-2012 É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 @@ -886,32 +886,6 @@ def autocomplete_town(request): for town in towns]) return HttpResponse(data, mimetype='text/plain') -def autocomplete_file(request): - person_types = request.user.ishtaruser.person.person_type - if (not request.user.has_perm('ishtar_common.view_file', models.File) and \ - not request.user.has_perm('ishtar_common.view_own_file', models.File) - and not person_types.rights.filter(wizard__url_name='file_search' - ).count()): - return HttpResponse(mimetype='text/plain') - if not request.GET.get('term'): - return HttpResponse(mimetype='text/plain') - q = request.GET.get('term') - query = Q() - for q in q.split(' '): - extra = Q(internal_reference__icontains=q) | \ - Q(towns__name__icontains=q) - try: - value = int(q) - extra = extra | Q(year=q) | Q(numeric_reference=q) - except ValueError: - pass - query = query & extra - limit = 20 - files = models.File.objects.filter(query)[:limit] - data = json.dumps([{'id':file.pk, 'value':unicode(file)} - for file in files]) - return HttpResponse(data, mimetype='text/plain') - from types import NoneType def format_val(val): @@ -1198,72 +1172,6 @@ def revert_item(model): return HttpResponse("True", mimetype='text/plain') return func - -get_file = get_item(models.File, 'get_file', 'file') -show_file = show_item(models.File, 'file') -revert_file = revert_item(models.File) - -def autocomplete_operation(request, non_closed=True): - person_types = request.user.ishtaruser.person.person_type - if (not request.user.has_perm('ishtar_common.view_operation', models.Operation)\ - and not request.user.has_perm('ishtar_common.view_own_operation', - models.Operation) - and not person_types.rights.filter(wizard__url_name='operation_search' - ).count()): - return HttpResponse(mimetype='text/plain') - if not request.GET.get('term'): - return HttpResponse(mimetype='text/plain') - q = request.GET.get('term') - query = Q() - for q in q.split(' '): - extra = Q(towns__name__icontains=q) - try: - value = int(q) - extra = extra | Q(year=q) | Q(operation_code=q) - except ValueError: - pass - query = query & extra - if non_closed: - query = query & Q(end_date__isnull=True) - limit = 15 - operations = models.Operation.objects.filter(query)[:limit] - data = json.dumps([{'id':operation.pk, 'value':unicode(operation)} - for operation in operations]) - return HttpResponse(data, mimetype='text/plain') - -def get_available_operation_code(request, year=None): - if not request.user.has_perm('ishtar_common.view_operation', models.Operation)\ - and not request.user.has_perm('ishtar_common.view_own_operation', - models.Operation): - return HttpResponse(mimetype='text/plain') - data = json.dumps({'id':models.Operation.get_available_operation_code(year)}) - return HttpResponse(data, mimetype='text/plain') - -get_operation = get_item(models.Operation, 'get_operation', 'operation', - bool_fields = ['end_date__isnull'], - extra_request_keys={'common_name':'common_name__icontains', - 'end_date':'end_date__isnull', - 'year_index':('year', 'operation_code')}) -show_operation = show_item(models.Operation, 'operation') -revert_operation = revert_item(models.Operation) - -get_operationsource = get_item(models.OperationSource, - 'get_operationsource', 'operationsource', - extra_request_keys={'operation__towns':'operation__towns__pk', - 'operation__operation_type':'operation__operation_type__pk', - 'operation__year':'operation__year'}) - -get_administrativeactfile = get_item(models.AdministrativeAct, - 'get_administrativeactfile', 'administrativeactfile', - extra_request_keys={'associated_file__towns':'associated_file__towns__pk', - 'operation__towns':'operation__towns__pk', - 'act_type__intented_to':'act_type__intented_to'}) -get_administrativeactop = get_item(models.AdministrativeAct, - 'get_administrativeactop', 'administrativeactop', - extra_request_keys={'associated_file__towns':'associated_file__towns__pk', - 'operation__towns':'operation__towns__pk', - 'act_type__intented_to':'act_type__intented_to'}) - def autocomplete_organization(request, orga_type=None): person_types = request.user.ishtaruser.person.person_type if (not request.user.has_perm('ishtar_common.view_organization', @@ -1293,72 +1201,10 @@ def autocomplete_organization(request, orga_type=None): for org in organizations]) return HttpResponse(data, mimetype='text/plain') -show_contextrecord = show_item(models.ContextRecord, 'contextrecord') -get_contextrecord = get_item(models.ContextRecord, - 'get_contextrecord', 'contextrecord', - extra_request_keys={'parcel__town':'parcel__town__pk', - 'operation__year':'operation__year__contains', - 'datings__period':'datings__period__pk'},) -get_contextrecordsource = get_item(models.ContextRecordSource, - 'get_contextrecordsource', 'contextrecordsource', - extra_request_keys={ - 'context_record__parcel__town':'context_record__parcel__town__pk', - 'context_record__operation__year':'context_record__operation__year', - 'context_record__datings__period':'context_record__datings__period__pk', - 'context_record__unit':'context_record__unit__pk', - }) -get_archaeologicalitem = get_item(models.Item, - 'get_archaeologicalitem', 'item', - bool_fields = ['base_items__is_isolated'], - base_request={'downstream_treatment__isnull':True}, - extra_request_keys={ -'base_items__context_record__parcel__town': - 'base_items__context_record__parcel__town', -'base_items__context_record__operation__year': - 'base_items__context_record__operation__year__contains', -'base_items__context_record__operation__code_patriarche': - 'base_items__context_record__operation__code_patriarche', -'dating__period':'dating__period__pk', -'base_items__item__description':'base_items__item__description__icontains', -'base_items__is_isolated':'base_items__is_isolated'}) -get_itemsource = get_item(models.ItemSource, - 'get_itemsource', 'itemsource', - extra_request_keys={ -'item__context_record__operation__year':'item__context_record__operation__year', -'item__dating__period':'item__dating__period__pk', -'item__description':'item__description__icontains', - }) -get_container = get_item(models.Container, - 'get_container', 'container', - extra_request_keys={ -'location':'location__pk', -'container_type':'container_type__pk', -'reference':'reference__icontains', - }) - -def autocomplete_warehouse(request): - if not request.user.has_perm('ishtar_common.view_warehouse', models.Warehouse)\ - and not request.user.has_perm('ishtar_common.view_own_warehouse', - models.Warehouse) : - return HttpResponse(mimetype='text/plain') - if not request.GET.get('term'): - return HttpResponse(mimetype='text/plain') - q = request.GET.get('term') - query = Q() - for q in q.split(' '): - extra = Q(name__icontains=q) | \ - Q(warehouse_type__label__icontains=q) - query = query & extra - limit = 15 - warehouses = models.Warehouse.objects.filter(query)[:limit] - data = json.dumps([{'id':warehouse.pk, 'value':unicode(warehouse)} - for warehouse in warehouses]) - return HttpResponse(data, mimetype='text/plain') - def autocomplete_author(request): if not request.user.has_perm('ishtar_common.view_author', models.Author)\ and not request.user.has_perm('ishtar_common.view_own_author', - models.Warehouse) : + models.Author) : return HttpResponse(mimetype='text/plain') if not request.GET.get('term'): return HttpResponse(mimetype='text/plain') @@ -1376,29 +1222,6 @@ def autocomplete_author(request): for author in authors]) return HttpResponse(data, mimetype='text/plain') -def autocomplete_container(request): - if not request.user.has_perm('ishtar_common.view_warehouse', - models.Warehouse)\ - and not request.user.has_perm('ishtar_common.view_own_warehouse', - models.Warehouse): - return HttpResponse(mimetype='text/plain') - if not request.GET.get('term'): - return HttpResponse(mimetype='text/plain') - q = request.GET.get('term') - query = Q() - for q in q.split(' '): - extra = Q(container_type__label__icontains=q) | \ - Q(container_type__reference__icontains=q) | \ - Q(reference__icontains=q) | \ - Q(location__name=q) | \ - Q(location__town=q) - query = query & extra - limit = 15 - containers = models.Container.objects.filter(query)[:limit] - data = json.dumps([{'id':container.pk, 'value':unicode(container)} - for container in containers]) - return HttpResponse(data, mimetype='text/plain') - def new_item(model): def func(request, parent_name): model_name = model._meta.object_name @@ -1426,11 +1249,9 @@ def new_item(model): context_instance=RequestContext(request)) return func -new_warehouse = new_item(models.Warehouse) new_person = new_item(models.Person) new_organization = new_item(models.Organization) new_author = new_item(models.Author) -new_container = new_item(models.Container) def action(request, action_slug, obj_id=None, *args, **kwargs): """ @@ -1464,19 +1285,3 @@ def dashboard_main(request, dct, obj_id=None, *args, **kwargs): 'ishtar_users':models.UserDashboard()} return render_to_response('dashboard_main.html', dct, context_instance=RequestContext(request)) - -def dashboard_file(request, dct, obj_id=None, *args, **kwargs): - """ - Main dashboard - """ - dct = {'dashboard': models.FileDashboard()} - return render_to_response('dashboard_file.html', dct, - context_instance=RequestContext(request)) - -def dashboard_operation(request, dct, obj_id=None, *args, **kwargs): - """ - Operation dashboard - """ - dct = {'dashboard': models.OperationDashboard()} - return render_to_response('dashboard_operation.html', dct, - context_instance=RequestContext(request)) |