summaryrefslogtreecommitdiff
path: root/archaeological_operations
diff options
context:
space:
mode:
Diffstat (limited to 'archaeological_operations')
-rw-r--r--archaeological_operations/admin.py9
-rw-r--r--archaeological_operations/data_importer.py280
-rw-r--r--archaeological_operations/forms.py1
-rw-r--r--archaeological_operations/migrations/0009_auto_20171011_1644.py51
-rw-r--r--archaeological_operations/migrations/0010_auto_20171012_1316.py25
-rw-r--r--archaeological_operations/migrations/0011_auto_20171017_1840.py51
-rw-r--r--archaeological_operations/models.py7
-rw-r--r--archaeological_operations/templates/ishtar/sheet_administrativeact_pdf.html4
-rw-r--r--archaeological_operations/templates/ishtar/sheet_operation.html2
-rw-r--r--archaeological_operations/templates/ishtar/sheet_operation_pdf.html4
-rw-r--r--archaeological_operations/templates/ishtar/sheet_operationsource_pdf.html4
-rw-r--r--archaeological_operations/tests.py147
-rw-r--r--archaeological_operations/tests/operations-with-json-fields.csv3
13 files changed, 289 insertions, 299 deletions
diff --git a/archaeological_operations/admin.py b/archaeological_operations/admin.py
index f1deac188..bf1415989 100644
--- a/archaeological_operations/admin.py
+++ b/archaeological_operations/admin.py
@@ -40,7 +40,7 @@ class AdministrativeActAdmin(HistorizedObjectAdmin):
search_fields = ('year', 'index')
readonly_fields = HistorizedObjectAdmin.readonly_fields + [
'in_charge', 'operator', 'scientist', 'signatory', 'associated_file',
- 'imports', 'departments_label', 'towns_label']
+ 'departments_label', 'towns_label']
model = models.AdministrativeAct
form = make_ajax_form(
models.AdministrativeAct, {'operation': 'operation'}
@@ -69,7 +69,6 @@ class ArchaeologicalSiteAdmin(HistorizedObjectAdmin):
list_display = ('name', 'reference')
search_fields = ('name', 'reference')
model = models.ArchaeologicalSite
- readonly_fields = HistorizedObjectAdmin.readonly_fields + ['imports']
inlines = [OperationInline]
admin_site.register(models.ArchaeologicalSite, ArchaeologicalSiteAdmin)
@@ -112,7 +111,7 @@ class OperationAdmin(HistorizedObjectAdmin):
search_fields += ['code_patriarche']
model = models.Operation
readonly_fields = HistorizedObjectAdmin.readonly_fields + [
- 'imports', 'cached_label']
+ 'cached_label']
form = AdminOperationForm
inlines = [ArchaeologicalSiteInline]
@@ -144,7 +143,7 @@ class ParcelAdmin(HistorizedObjectAdmin):
'town': 'town'}
)
readonly_fields = HistorizedObjectAdmin.readonly_fields + [
- 'imports', 'history_date'
+ 'history_date'
]
admin_site.register(models.Parcel, ParcelAdmin)
@@ -196,7 +195,7 @@ class ParcelOwnerAdmin(HistorizedObjectAdmin):
'parcel': 'parcel'}
)
readonly_fields = HistorizedObjectAdmin.readonly_fields + [
- 'imports', 'history_date'
+ 'history_date'
]
admin_site.register(models.ParcelOwner, ParcelOwnerAdmin)
diff --git a/archaeological_operations/data_importer.py b/archaeological_operations/data_importer.py
deleted file mode 100644
index b4cd2f0d0..000000000
--- a/archaeological_operations/data_importer.py
+++ /dev/null
@@ -1,280 +0,0 @@
-#!/usr/bin/env python
-# -*- coding: utf-8 -*-
-# Copyright (C) 2015 É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 re
-
-from django.db import IntegrityError
-from django.template.defaultfilters import slugify
-
-from ishtar_common.data_importer import *
-from ishtar_common.models import Town, OrganizationType, SourceType, \
- SupportType, Format, AuthorType
-
-from archaeological_operations import models
-from archaeological_operations.forms import OPERATOR
-from archaeological_operations.utils import parse_parcels
-
-RE_PERMIT_REFERENCE = re.compile('[A-Za-z]*(.*)')
-
-
-class ImportParcelFormater(ImportFormater):
- NEED = ['town', ]
- PARCEL_OWNER_KEY = 'associated_file'
-
- def post_process(self, obj, context, value, owner=None):
- value = value.strip()
- base_dct = {self.PARCEL_OWNER_KEY: obj, 'history_modifier': owner}
- if 'parcels' in context:
- for key in context['parcels']:
- if context['parcels'][key]:
- base_dct[key] = context['parcels'][key]
- for parcel_dct in parse_parcels(value, owner=owner):
- parcel_dct.update(base_dct)
- try:
- models.Parcel.objects.get_or_create(**parcel_dct)
- except IntegrityError:
- try:
- p = unicode(parcel_dct)
- except UnicodeDecodeError:
- try:
- p = str(parcel_dct).decode('utf-8')
- except UnicodeDecodeError:
- p = u""
- raise ImporterError(u"Erreur d'import parcelle, contexte : %s"
- % p)
-
-
-class ImportYearFormater(ImportFormater):
- def post_process(self, obj, context, value, owner=None):
- value = self.formater.format(value)
- if not value:
- return
- obj.year = value.year
- obj.save()
-
-
-class TownFormater(Formater):
- def __init__(self, town_full_dct={}, town_dct={}):
- self._town_full_dct = town_full_dct
- self._town_dct = town_dct
- self._initialized = False if not self._town_full_dct else True
-
- def town_dct_init(self):
- for town in Town.objects.all():
- key = (slugify(town.name.strip()), town.numero_insee[:2])
- if key in self._town_full_dct:
- # print("Danger! %s is ambiguous with another town on the same"
- # " department." % town.name)
- continue
- self._town_full_dct[key] = town
- key = slugify(town.name.strip())
- if key in self._town_dct:
- # print("Warning %s is ambiguous with no department provided" %
- # town.name)
- continue
- self._town_dct[key] = town
- self._initialized = True
-
- def format(self, value, extra=None):
- if not self._initialized:
- self.town_dct_init()
- m = RE_FILTER_CEDEX.match(value)
- if m:
- value = m.groups()[0]
- if not value:
- return None
- if extra:
- key = (slugify(value), extra)
- if key in self._town_full_dct:
- return self._town_full_dct[key]
- key = slugify(value)
- if key in self._town_dct:
- return self._town_dct[key]
-
-
-class TownINSEEFormater(Formater):
- def __init__(self):
- self._town_dct = {}
-
- def format(self, value, extra=None):
- value = value.strip()
- if not value:
- return None
- if value in self._town_dct:
- return self._town_dct[value]
- q = Town.objects.filter(numero_insee=value)
- if not q.count():
- return
- self._town_dct[value] = q.all()[0]
- return self._town_dct[value]
-
-
-class SurfaceFormater(Formater):
- def test(self):
- assert self.format(u"352 123") == 352123
- assert self.format(u"456 789 m²") == 456789
- assert self.format(u"78ha") == 780000
-
- def format(self, value, extra=None):
- value = value.strip()
- if not value:
- return None
- factor = 1
- if value.endswith(u"m2") or value.endswith(u"m²"):
- value = value[:-2]
- if value.endswith(u"ha"):
- value = value[:-2]
- factor = 10000
- try:
- return int(value.replace(' ', '')) * factor
- except ValueError:
- raise ImporterError("Erreur import surface : %s" % unicode(value))
-
-# RE_ADD_CD_POSTAL_TOWN = re.compile("(.*)[, ](\d{5}) (.*?) *(?: "\
-# "*CEDEX|cedex|Cedex *\d*)*")
-
-RE_NAME_ADD_CD_POSTAL_TOWN = re.compile(
- "(.+)?[, ]*" + NEW_LINE_BREAK + "(.+)?[, ]*(\d{2} *\d{3})[, ]*(.+)")
-
-RE_ADD_CD_POSTAL_TOWN = re.compile("(.+)?[, ]*(\d{2} *\d{3})[, ]*(.+)")
-
-RE_CD_POSTAL_FILTER = re.compile("(\d*) (\d*)")
-
-RE_ORGA = re.compile("([^,\n]*)")
-
-
-class OperationImporterBibracte(Importer):
- OBJECT_CLS = models.Operation
- DESC = u"Exports Bibracte : importeur pour l'onglet opération"
- DEFAULTS = {
- ('operator',): {
- 'organization_type': OPERATOR
- },
- }
- LINE_FORMAT = [
- # CODE OPE
- ImportFormater('operation_code', IntegerFormater(),),
- # REGION
- None,
- # TYPE operation
- ImportFormater('operation_type', TypeFormater(models.OperationType),),
- # NOM
- ImportFormater('common_name', UnicodeFormater(120),),
- # OPERATEUR
- ImportFormater('operator__name', UnicodeFormater(120),),
- # resp. lien IMPORT avec personne
- ImportFormater('in_charge__raw_name', UnicodeFormater(300),),
- # début
- ImportFormater('start_date', DateFormater(['%Y/%m/%d']),),
- # fin
- ImportFormater('excavation_end_date', DateFormater(['%Y/%m/%d']),),
- # Chronos
- ImportFormater('periods', TypeFormater(models.Period, many_split="&"),
- required=False),
- ]
-
-RE_PARCEL_SECT_NUM = re.compile("([A-Za-z]*)([0-9]*)")
-RE_NUM_INSEE = re.compile("([0-9]*)")
-
-
-class ParcelImporterBibracte(Importer):
- OBJECT_CLS = models.Parcel
- DESC = u"Exports Bibracte : importeur pour l'onglet parcelles"
- DEFAULTS = {
- ('operator',): {
- 'organization_type': OrganizationType.objects.get(
- txt_idx="operator")},
- }
- LINE_FORMAT = [
- # code OA
- ImportFormater('operation__operation_code', IntegerFormater(),),
- # identifiant parcelle
- ImportFormater(
- ['section', 'parcel_number'],
- [UnicodeFormater(4), UnicodeFormater(6), ],
- regexp=RE_PARCEL_SECT_NUM,
- regexp_formater_args=[[0], [1]], required=False,
- duplicate_fields=[('external_id', False)],),
- # numero parcelle
- ImportFormater('parcel_number', UnicodeFormater(6),
- required=False,),
- # section cadastre
- ImportFormater('section', UnicodeFormater(4),
- required=False,),
- # annee cadastre
- ImportFormater('year', YearFormater(), required=False,),
- # nom commune
- None,
- # numero INSEE commune
- ImportFormater('town__numero_insee', UnicodeFormater(6),
- regexp=RE_NUM_INSEE, required=False,),
- # nom departement
- None,
- # lieu dit adresse
- ImportFormater('address', UnicodeFormater(500),
- required=False,),
- ]
-
-MAIN_AUTHOR, created = AuthorType.objects.get_or_create(txt_idx='main_author')
-
-
-class DocImporterBibracte(Importer):
- OBJECT_CLS = models.OperationSource
- DEFAULTS = {
- ('authors',): {'author_type': MAIN_AUTHOR},
- }
- DESC = u"Exports Bibracte : importeur pour l'onglet documentation"
- LINE_FORMAT = [
- # code OA
- ImportFormater('operation__operation_code', IntegerFormater(),),
- # identifiant documentation
- ImportFormater('external_id', UnicodeFormater(12),),
- # type
- ImportFormater('source_type', TypeFormater(SourceType),
- required=False),
- # nature support
- ImportFormater('support_type', TypeFormater(SupportType),
- required=False),
- # nombre element
- ImportFormater('item_number', IntegerFormater(), required=False),
- # auteur
- ImportFormater('authors__person__raw_name', UnicodeFormater(300),
- required=False),
- # annee
- ImportFormater('creation_date', DateFormater(['%Y']),),
- # format
- ImportFormater('format_type', TypeFormater(Format), required=False),
- # description legende
- ImportFormater('description', UnicodeFormater(1000), required=False),
- # type contenant
- None,
- # numero contenant
- None,
- # commentaire
- ImportFormater('comment', UnicodeFormater(1000), required=False),
- # echelle
- ImportFormater('scale', UnicodeFormater(30), required=False),
- # type sous contenant
- None,
- # numero sous contenant
- None,
- # informations complementaires
- ImportFormater('additional_information', UnicodeFormater(1000),
- required=False),
- ]
diff --git a/archaeological_operations/forms.py b/archaeological_operations/forms.py
index 651cd740f..841131da6 100644
--- a/archaeological_operations/forms.py
+++ b/archaeological_operations/forms.py
@@ -480,6 +480,7 @@ RecordRelationsFormSet.form_label = _(u"Relations")
class OperationSelect(TableSelect):
+ search_vector = forms.CharField(label=_(u"Full text search"))
year = forms.IntegerField(label=_("Year"))
operation_code = forms.IntegerField(label=_(u"Numeric reference"))
if settings.COUNTRY == 'fr':
diff --git a/archaeological_operations/migrations/0009_auto_20171011_1644.py b/archaeological_operations/migrations/0009_auto_20171011_1644.py
new file mode 100644
index 000000000..18a284a21
--- /dev/null
+++ b/archaeological_operations/migrations/0009_auto_20171011_1644.py
@@ -0,0 +1,51 @@
+# -*- coding: utf-8 -*-
+# Generated by Django 1.11 on 2017-10-11 16:44
+from __future__ import unicode_literals
+
+import django.contrib.postgres.search
+from django.db import migrations
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('archaeological_operations', '0008_auto_20170829_1639'),
+ ]
+
+ operations = [
+ migrations.AddField(
+ model_name='administrativeact',
+ name='search_vector',
+ field=django.contrib.postgres.search.SearchVectorField(blank=True, help_text='Auto filled at save', null=True, verbose_name='Search vector'),
+ ),
+ migrations.AddField(
+ model_name='archaeologicalsite',
+ name='search_vector',
+ field=django.contrib.postgres.search.SearchVectorField(blank=True, help_text='Auto filled at save', null=True, verbose_name='Search vector'),
+ ),
+ migrations.AddField(
+ model_name='historicaladministrativeact',
+ name='search_vector',
+ field=django.contrib.postgres.search.SearchVectorField(blank=True, help_text='Auto filled at save', null=True, verbose_name='Search vector'),
+ ),
+ migrations.AddField(
+ model_name='historicaloperation',
+ name='search_vector',
+ field=django.contrib.postgres.search.SearchVectorField(blank=True, help_text='Auto filled at save', null=True, verbose_name='Search vector'),
+ ),
+ migrations.AddField(
+ model_name='operation',
+ name='search_vector',
+ field=django.contrib.postgres.search.SearchVectorField(blank=True, help_text='Auto filled at save', null=True, verbose_name='Search vector'),
+ ),
+ migrations.AddField(
+ model_name='parcel',
+ name='search_vector',
+ field=django.contrib.postgres.search.SearchVectorField(blank=True, help_text='Auto filled at save', null=True, verbose_name='Search vector'),
+ ),
+ migrations.AddField(
+ model_name='parcelowner',
+ name='search_vector',
+ field=django.contrib.postgres.search.SearchVectorField(blank=True, help_text='Auto filled at save', null=True, verbose_name='Search vector'),
+ ),
+ ]
diff --git a/archaeological_operations/migrations/0010_auto_20171012_1316.py b/archaeological_operations/migrations/0010_auto_20171012_1316.py
new file mode 100644
index 000000000..3a847a803
--- /dev/null
+++ b/archaeological_operations/migrations/0010_auto_20171012_1316.py
@@ -0,0 +1,25 @@
+# -*- coding: utf-8 -*-
+# Generated by Django 1.11 on 2017-10-12 13:16
+from __future__ import unicode_literals
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('archaeological_operations', '0009_auto_20171011_1644'),
+ ]
+
+ operations = [
+ migrations.AlterField(
+ model_name='historicaloperation',
+ name='cached_label',
+ field=models.CharField(blank=True, db_index=True, max_length=500, null=True, verbose_name='Cached name'),
+ ),
+ migrations.AlterField(
+ model_name='operation',
+ name='cached_label',
+ field=models.CharField(blank=True, db_index=True, max_length=500, null=True, verbose_name='Cached name'),
+ ),
+ ]
diff --git a/archaeological_operations/migrations/0011_auto_20171017_1840.py b/archaeological_operations/migrations/0011_auto_20171017_1840.py
new file mode 100644
index 000000000..cd169957a
--- /dev/null
+++ b/archaeological_operations/migrations/0011_auto_20171017_1840.py
@@ -0,0 +1,51 @@
+# -*- coding: utf-8 -*-
+# Generated by Django 1.11 on 2017-10-17 18:40
+from __future__ import unicode_literals
+
+import django.contrib.postgres.fields.jsonb
+from django.db import migrations
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('archaeological_operations', '0010_auto_20171012_1316'),
+ ]
+
+ operations = [
+ migrations.AddField(
+ model_name='administrativeact',
+ name='data',
+ field=django.contrib.postgres.fields.jsonb.JSONField(db_index=True, default={}),
+ ),
+ migrations.AddField(
+ model_name='archaeologicalsite',
+ name='data',
+ field=django.contrib.postgres.fields.jsonb.JSONField(db_index=True, default={}),
+ ),
+ migrations.AddField(
+ model_name='historicaladministrativeact',
+ name='data',
+ field=django.contrib.postgres.fields.jsonb.JSONField(db_index=True, default={}),
+ ),
+ migrations.AddField(
+ model_name='historicaloperation',
+ name='data',
+ field=django.contrib.postgres.fields.jsonb.JSONField(db_index=True, default={}),
+ ),
+ migrations.AddField(
+ model_name='operation',
+ name='data',
+ field=django.contrib.postgres.fields.jsonb.JSONField(db_index=True, default={}),
+ ),
+ migrations.AddField(
+ model_name='parcel',
+ name='data',
+ field=django.contrib.postgres.fields.jsonb.JSONField(db_index=True, default={}),
+ ),
+ migrations.AddField(
+ model_name='parcelowner',
+ name='data',
+ field=django.contrib.postgres.fields.jsonb.JSONField(db_index=True, default={}),
+ ),
+ ]
diff --git a/archaeological_operations/models.py b/archaeological_operations/models.py
index bc03ee387..70c1c02ba 100644
--- a/archaeological_operations/models.py
+++ b/archaeological_operations/models.py
@@ -248,6 +248,10 @@ class Operation(ClosedItem, BaseHistorizedItem, ImageModel, OwnPerms,
'archaeological_sites__reference': _(u"Archaeological sites ("
u"reference)"),
}
+ BASE_SEARCH_VECTORS = ["scientist__raw_name", "cached_label",
+ "common_name", "comment", "address", "old_code"]
+ INT_SEARCH_VECTORS = ["year"]
+ M2M_SEARCH_VECTORS = ["towns__name"]
# fields definition
creation_date = models.DateField(_(u"Creation date"),
@@ -309,6 +313,7 @@ class Operation(ClosedItem, BaseHistorizedItem, ImageModel, OwnPerms,
code_patriarche = models.TextField(u"Code PATRIARCHE", null=True,
blank=True, unique=True)
TABLE_COLS = ['full_code_patriarche'] + TABLE_COLS
+ BASE_SEARCH_VECTORS = ['code_patriarche'] + BASE_SEARCH_VECTORS
# preventive
fnap_financing = models.FloatField(u"Financement FNAP (%)",
blank=True, null=True)
@@ -340,7 +345,7 @@ class Operation(ClosedItem, BaseHistorizedItem, ImageModel, OwnPerms,
scientific_documentation_comment = models.TextField(
_(u"Comment about scientific documentation"), null=True, blank=True)
cached_label = models.CharField(_(u"Cached name"), max_length=500,
- null=True, blank=True)
+ null=True, blank=True, db_index=True)
archaeological_sites = models.ManyToManyField(
ArchaeologicalSite, verbose_name=_(u"Archaeological sites"),
blank=True, related_name='operations')
diff --git a/archaeological_operations/templates/ishtar/sheet_administrativeact_pdf.html b/archaeological_operations/templates/ishtar/sheet_administrativeact_pdf.html
index b6d257cb0..be3e24428 100644
--- a/archaeological_operations/templates/ishtar/sheet_administrativeact_pdf.html
+++ b/archaeological_operations/templates/ishtar/sheet_administrativeact_pdf.html
@@ -1,6 +1,5 @@
{% extends "ishtar/sheet_administrativeact.html" %}
{% block header %}
-<link rel="stylesheet" href="{{STATIC_URL}}/media/style_basic.css?ver={{VERSION}}" />
{% endblock %}
{% block main_head %}
{{ block.super }}
@@ -10,9 +9,6 @@ Ishtar &ndash; {{APP_NAME}} &ndash; {{item}}
{% endblock %}
{%block head_sheet%}{%endblock%}
{%block main_foot%}
-<div id="pdffooter">
-&ndash; <pdf:pagenumber/> &ndash;
-</div>
</body>
</html>
{%endblock%}
diff --git a/archaeological_operations/templates/ishtar/sheet_operation.html b/archaeological_operations/templates/ishtar/sheet_operation.html
index 5a02236a3..e46db74c7 100644
--- a/archaeological_operations/templates/ishtar/sheet_operation.html
+++ b/archaeological_operations/templates/ishtar/sheet_operation.html
@@ -71,6 +71,8 @@
{% field "Abstract" item.abstract "<pre>" "</pre>" %}
{% field "Comment about scientific documentation" item.scientific_documentation_comment "<pre>" "</pre>" %}
+{% include "ishtar/blocks/sheet_json.html" %}
+
{% if not next %}
{% if item.towns.count %}
<h3>{% trans "Localisation"%}</h3>
diff --git a/archaeological_operations/templates/ishtar/sheet_operation_pdf.html b/archaeological_operations/templates/ishtar/sheet_operation_pdf.html
index dc3c8b46f..7d86bd924 100644
--- a/archaeological_operations/templates/ishtar/sheet_operation_pdf.html
+++ b/archaeological_operations/templates/ishtar/sheet_operation_pdf.html
@@ -1,6 +1,5 @@
{% extends "ishtar/sheet_operation.html" %}
{% block header %}
-<link rel="stylesheet" href="{{STATIC_URL}}/media/style_basic.css?ver={{VERSION}}" />
{% endblock %}
{% block main_head %}
{{ block.super }}
@@ -10,9 +9,6 @@ Ishtar &ndash; {{APP_NAME}} &ndash; {{item}}
{% endblock %}
{%block head_sheet%}{%endblock%}
{%block main_foot%}
-<div id="pdffooter">
-&ndash; <pdf:pagenumber/> &ndash;
-</div>
</body>
</html>
{%endblock%}
diff --git a/archaeological_operations/templates/ishtar/sheet_operationsource_pdf.html b/archaeological_operations/templates/ishtar/sheet_operationsource_pdf.html
index 1b2cd9ff3..68eb7aa2d 100644
--- a/archaeological_operations/templates/ishtar/sheet_operationsource_pdf.html
+++ b/archaeological_operations/templates/ishtar/sheet_operationsource_pdf.html
@@ -1,6 +1,5 @@
{% extends "ishtar/sheet_operationsource.html" %}
{% block header %}
-<link rel="stylesheet" href="{{STATIC_URL}}/media/style_basic.css?ver={{VERSION}}" />
{% endblock %}
{% block main_head %}
{{ block.super }}
@@ -10,9 +9,6 @@ Ishtar &ndash; {{APP_NAME}} &ndash; {{item}}
{% endblock %}
{%block head_sheet%}{%endblock%}
{%block main_foot%}
-<div id="pdffooter">
-&ndash; <pdf:pagenumber/> &ndash;
-</div>
</body>
</html>
{%endblock%}
diff --git a/archaeological_operations/tests.py b/archaeological_operations/tests.py
index 0d6908374..b75c02cae 100644
--- a/archaeological_operations/tests.py
+++ b/archaeological_operations/tests.py
@@ -19,10 +19,12 @@
import json
import datetime
+from subprocess import Popen, PIPE
import StringIO
import zipfile
from django.conf import settings
+from django.contrib.contenttypes.models import ContentType
from django.core.files.uploadedfile import SimpleUploadedFile
from django.core.urlresolvers import reverse
from django.db.models import Q
@@ -37,7 +39,8 @@ from archaeological_operations import views
from ishtar_common.models import OrganizationType, Organization, ItemKey, \
ImporterType, IshtarUser, TargetKey, ImporterModel, IshtarSiteProfile, \
Town, ImporterColumn, Person, Author, SourceType, AuthorType, \
- DocumentTemplate, PersonType, TargetKeyGroup
+ DocumentTemplate, PersonType, TargetKeyGroup, JsonDataField, \
+ JsonDataSection, ImportTarget, FormaterType
from archaeological_files.models import File, FileType
from archaeological_context_records.models import Unit
@@ -453,6 +456,24 @@ class ImportOperationTest(ImportTest, TestCase):
impt.delete()
self.assertEqual(parcel_count - 3, models.Parcel.objects.count())
+ def test_json_fields(self):
+ importer, form = self.init_ope_import("operations-with-json-fields.csv")
+ col = ImporterColumn.objects.create(importer_type=importer,
+ col_number=11)
+ formater_type = FormaterType.objects.get(
+ formater_type='IntegerFormater')
+ ImportTarget.objects.create(
+ column=col, target='data__autre_refs__arbitraire',
+ formater_type=formater_type)
+ impt = form.save(self.ishtar_user)
+ impt.initialize()
+ self.init_ope_targetkey(imp=impt)
+ impt.importation()
+ ope1 = models.Operation.objects.get(code_patriarche='4200')
+ self.assertEqual(ope1.data, {u'autre_refs': {u'arbitraire': 789}})
+ ope2 = models.Operation.objects.get(code_patriarche='4201')
+ self.assertEqual(ope2.data, {u'autre_refs': {u'arbitraire': 456}})
+
class ParcelTest(ImportTest, TestCase):
fixtures = OPERATION_TOWNS_FIXTURES
@@ -895,6 +916,21 @@ class OperationTest(TestCase, OperationInitTest):
self.assertEqual(ope_id, 'OP2011-1')
self.assertEqual(town, self.towns[0].name)
+ def test_search_vector_update(self):
+ operation = self.operations[0]
+ town = self.create_towns({'numero_insee': '12346', 'name': 'Daisy'})[-1]
+ operation.towns.add(town)
+ town = self.create_towns(
+ {'numero_insee': '12347', 'name': 'Dirty old'})[-1]
+ operation.towns.add(town)
+ operation = models.Operation.objects.get(pk=operation.pk)
+ operation.comment = u"Zardoz"
+ operation.code_patriarche = u"HUIAAA5"
+ operation.save()
+ for key in ('old', 'op2010', 'dirty', 'daisy', "'2010'", "zardoz",
+ "huiaaa5"):
+ self.assertIn(key, operation.search_vector)
+
def test_cache_bulk_update(self):
if settings.USE_SPATIALITE_FOR_TESTS:
# using views - can only be tested with postgresql
@@ -1008,6 +1044,32 @@ class OperationTest(TestCase, OperationInitTest):
self.assertEqual(response.status_code, 200)
self.assertIn('class="sheet"', response.content)
+ def test_show_pdf(self):
+ operation = self.operations[0]
+ c = Client()
+ response = c.get(reverse('show-operation',
+ kwargs={'pk': operation.pk, 'type': 'pdf'}))
+ self.assertEqual(response.status_code, 200)
+ # empty content when not allowed
+ self.assertEqual(response.content, "")
+ c.login(username=self.username, password=self.password)
+ response = c.get(reverse('show-operation',
+ kwargs={'pk': operation.pk, 'type': 'pdf'}))
+ self.assertEqual(response.status_code, 200)
+ f = StringIO.StringIO(response.content)
+ filetype = Popen("/usr/bin/file -b --mime -", shell=True, stdout=PIPE,
+ stdin=PIPE).communicate(f.read(1024))[0].strip()
+ self.assertTrue(filetype.startswith('application/pdf'))
+
+ def test_show_odt(self):
+ operation = self.operations[0]
+ c = Client()
+ response = c.get(reverse('show-operation',
+ kwargs={'pk': operation.pk, 'type': 'pdf'}))
+ self.assertEqual(response.status_code, 200)
+ # empty content when not allowed
+ self.assertEqual(response.content, "")
+ c.login(username=self.username, password=self.password)
response = c.get(reverse('show-operation', kwargs={'pk': operation.pk,
'type': 'odt'}))
self.assertEqual(response.status_code, 200)
@@ -1015,6 +1077,53 @@ class OperationTest(TestCase, OperationInitTest):
z = zipfile.ZipFile(f)
self.assertIsNone(z.testzip())
+ def test_json(self):
+ operation = self.operations[0]
+ operation.data = {"groundhog": {"number": 53444,
+ "awake_state": u"réveillée",
+ "with_feather": "Oui"},
+ "frog_number": 32303}
+ operation.save()
+
+ content_type = ContentType.objects.get_for_model(operation)
+ groundhog_section = JsonDataSection.objects.create(
+ name="Marmotte", content_type=content_type)
+ JsonDataField.objects.create(name=u"État d'éveil",
+ key='groundhog__awake_state',
+ content_type=content_type,
+ section=groundhog_section)
+ JsonDataField.objects.create(name=u"Avec plume",
+ key='groundhog__with_feather',
+ content_type=content_type,
+ section=groundhog_section)
+ JsonDataField.objects.create(name=u"Zzzzzzzz",
+ key='groundhog__zzz',
+ content_type=content_type,
+ section=groundhog_section)
+ JsonDataField.objects.create(name=u"Grenouille",
+ key='frog_number',
+ content_type=content_type)
+
+ c = Client()
+ c.login(username=self.username, password=self.password)
+ response = c.get(reverse('show-operation', kwargs={'pk': operation.pk}))
+ self.assertEqual(response.status_code, 200)
+ self.assertIn('class="sheet"', response.content)
+ self.assertIn(u"Marmotte".encode('utf-8'), response.content)
+ self.assertIn(u"État d&#39;éveil".encode('utf-8'), response.content)
+ self.assertIn(u"réveillée".encode('utf-8'), response.content)
+ self.assertIn(u"Grenouille".encode('utf-8'), response.content)
+ self.assertIn(u"32303".encode('utf-8'), response.content)
+ self.assertNotIn(u"53444".encode('utf-8'), response.content)
+ self.assertNotIn(u"Zzzzzzzz".encode('utf-8'), response.content)
+
+ operation.data = {}
+ operation.save()
+ response = c.get(reverse('show-operation', kwargs={'pk': operation.pk}))
+ self.assertEqual(response.status_code, 200)
+ self.assertIn('class="sheet"', response.content)
+ self.assertNotIn(u"Marmotte".encode('utf-8'), response.content)
+
class OperationSearchTest(TestCase, OperationInitTest):
fixtures = FILE_FIXTURES
@@ -1104,6 +1213,42 @@ class OperationSearchTest(TestCase, OperationInitTest):
self.assertEqual(response.status_code, 200)
self.assertEqual(json.loads(response.content)['total'], 1)
+ def test_town_search(self):
+ c = Client()
+ c.login(username=self.username, password=self.password)
+
+ data = {'numero_insee': '98989', 'name': 'base_town'}
+ base_town = self.create_towns(datas=data)[-1]
+
+ data = {'numero_insee': '56789', 'name': 'parent_town'}
+ parent_town = self.create_towns(datas=data)[-1]
+ parent_town.children.add(base_town)
+
+ data = {'numero_insee': '01234', 'name': 'child_town'}
+ child_town = self.create_towns(datas=data)[-1]
+ base_town.children.add(child_town)
+
+ ope = self.operations[1]
+ ope.towns.add(base_town)
+
+ # simple search
+ search = {'towns': base_town.pk}
+ response = c.get(reverse('get-operation'), search)
+ self.assertEqual(response.status_code, 200)
+ self.assertEqual(json.loads(response.content)['total'], 1)
+
+ # parent search
+ search = {'towns': parent_town.pk}
+ response = c.get(reverse('get-operation'), search)
+ self.assertEqual(response.status_code, 200)
+ self.assertEqual(json.loads(response.content)['total'], 1)
+
+ # child search
+ search = {'towns': child_town.pk}
+ response = c.get(reverse('get-operation'), search)
+ self.assertEqual(response.status_code, 200)
+ self.assertEqual(json.loads(response.content)['total'], 1)
+
def testOwnSearch(self):
c = Client()
response = c.get(reverse('get-operation'), {'year': '2010'})
diff --git a/archaeological_operations/tests/operations-with-json-fields.csv b/archaeological_operations/tests/operations-with-json-fields.csv
new file mode 100644
index 000000000..015497b4c
--- /dev/null
+++ b/archaeological_operations/tests/operations-with-json-fields.csv
@@ -0,0 +1,3 @@
+code OA,region,type operation,intitule operation,operateur,responsable operation,date debut terrain,date fin terrain,chronologie generale,identifiant document georeferencement,notice scientifique,numéro arbitraire
+4201,Bourgogne,Fouille programmée,Oppìdum de Paris 2,L'opérateur,,2000/01/31,2002/12/31,Age du Fer,,456
+4200,Bourgogne,Fouille programmée,Oppìdum de Paris,L'opérateur,Jean Sui-Resp'on Sablé,2000/01/22,2002/12/31,Age du Fer & Gallo-Romain & Néolithik & Moderne,,789