#!/usr/bin/env python # -*- coding: utf-8 -*- # Copyright (C) 2015-2017 Étienne Loks # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as # published by the Free Software Foundation, either version 3 of the # License, or (at your option) any later version. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU Affero General Public License for more details. # You should have received a copy of the GNU Affero General Public License # along with this program. If not, see . # See the file COPYING for details. import json from django.conf import settings from django.core.exceptions import ValidationError, ImproperlyConfigured from django.core.urlresolvers import reverse from django.test.client import Client from ishtar_common.models import IshtarSiteProfile, ImporterModel from ishtar_common.tests import create_superuser, TestCase from archaeological_operations.tests import OperationInitTest, \ ImportTest, ImportOperationTest from archaeological_operations import models as models_ope from archaeological_context_records import models class ImportContextRecordTest(ImportTest, TestCase): fixtures = ImportOperationTest.fixtures + [ settings.ROOT_PATH + '../archaeological_context_records/fixtures/initial_data-fr.json', ] def test_mcc_import_contextrecords(self): old_nb = models.ContextRecord.objects.count() mcc, form = self.init_context_record_import() self.assertTrue(form.is_valid()) impt = form.save(self.ishtar_user) impt.initialize() self.init_cr_targetkey(impt) impt.importation() # new context records has now been imported current_nb = models.ContextRecord.objects.count() self.assertEqual(current_nb, old_nb + 4) self.assertEqual( models.ContextRecord.objects.filter( unit__txt_idx='not_in_context').count(), 3) self.assertEqual( models.ContextRecord.objects.filter( unit__txt_idx='layer').count(), 1) def test_model_limitation(self): old_nb = models.ContextRecord.objects.count() mcc, form = self.init_context_record_import() mcc.created_models.clear() self.assertTrue(form.is_valid()) impt = form.save(self.ishtar_user) impt.initialize() self.init_cr_targetkey(impt) impt.importation() # no model defined in created_models: normal import current_nb = models.ContextRecord.objects.count() self.assertEqual(current_nb, old_nb + 4) # add an inadequate model to make created_models non empty for cr in models.ContextRecord.objects.all(): cr.delete() mcc, form = self.init_context_record_import() mcc.created_models.clear() mcc.created_models.add(ImporterModel.objects.get( klass='ishtar_common.models.Organization' )) impt = form.save(self.ishtar_user) impt.initialize() self.init_cr_targetkey(impt) # Dating is not in models that can be created but force new is # set for a column that references Dating impt.importation() self.assertEqual(len(impt.errors), 4) self.assertIn("doesn't exist in the database.", impt.errors[0]['error']) # retry with only Dating (no context record) for cr in models.ContextRecord.objects.all(): cr.delete() mcc, form = self.init_context_record_import() mcc.created_models.clear() dat_model, c = ImporterModel.objects.get_or_create( klass='archaeological_context_records.models.Dating', defaults={"name": 'Dating'}) mcc.created_models.add(dat_model) impt = form.save(self.ishtar_user) impt.initialize() self.init_cr_targetkey(impt) impt.importation() current_nb = models.ContextRecord.objects.count() self.assertEqual(current_nb, 0) # add a context record model for cr in models.ContextRecord.objects.all(): cr.delete() mcc, form = self.init_context_record_import() mcc.created_models.clear() mcc.created_models.add(ImporterModel.objects.get( klass='archaeological_context_records.models.ContextRecord' )) mcc.created_models.add(dat_model) impt = form.save(self.ishtar_user) impt.initialize() self.init_cr_targetkey(impt) impt.importation() current_nb = models.ContextRecord.objects.count() self.assertEqual(current_nb, 4) ''' # add a context record model for cr in models.ContextRecord.objects.all(): cr.delete() mcc, form = self.init_context_record_import() mcc.created_models.clear() mcc.created_models.add(ImporterModel.objects.get( klass='archaeological_context_records.models.ContextRecord' )) impt = form.save(self.ishtar_user) impt.initialize() self.init_cr_targetkey(impt) impt.importation() current_nb = models.ContextRecord.objects.count() self.assertEqual(current_nb, 4) ''' class ContextRecordInit(OperationInitTest): def create_context_record(self, user=None, data={}, force=False): if not getattr(self, 'context_records', None): self.context_records = [] default = {'label': "Context record"} if force or not data.get('operation') \ or not models.Operation.objects.filter( pk=data['operation'].pk).count(): data['operation'] = self.get_default_operation(force=force) if not data.get('parcel') or not data['parcel'].pk \ or not models.Parcel.objects.filter( pk=data['parcel'].pk).count(): data['parcel'] = self.get_default_parcel(force=force) if not data.get('history_modifier'): data['history_modifier'] = self.get_default_user() default.update(data) data['operation'] = models.Operation.objects.get( pk=data['operation'].pk) data['parcel'] = models.Parcel.objects.get( pk=data['parcel'].pk) self.context_records.append(models.ContextRecord.objects.create( **default)) return self.context_records def get_default_context_record(self, force=False): if force: return self.create_context_record(force=force)[-1] return self.create_context_record(force=force)[0] def tearDown(self): if hasattr(self, 'context_records'): for cr in self.context_records: try: cr.delete() except: pass self.context_records = [] super(ContextRecordInit, self).tearDown() class ContextRecordTest(ContextRecordInit, TestCase): fixtures = ImportContextRecordTest.fixtures def setUp(self): IshtarSiteProfile.objects.create() self.username, self.password, self.user = create_superuser() self.create_context_record(data={"label": u"CR 1"}) self.create_context_record(data={"label": u"CR 2"}) cr_1 = self.context_records[0] cr_2 = self.context_records[1] sym_rel_type = models.RelationType.objects.filter( symmetrical=True).all()[0] self.cr_rel_type = sym_rel_type models.RecordRelations.objects.create( left_record=cr_1, right_record=cr_2, relation_type=sym_rel_type) def testExternalID(self): cr = self.context_records[0] self.assertEqual( cr.external_id, u"{}-{}".format(cr.parcel.external_id, cr.label)) def test_lost_parcel_dont_delete_context_record(self): cr = self.create_context_record(force=True)[0] parcel = models.Parcel.objects.get(pk=cr.parcel.pk) parcel.operation = None parcel.save() # associated context record is not removed self.assertEqual( models.ContextRecord.objects.filter(pk=cr.pk).count(), 1) # associated operation is restored self.assertEqual( models.Parcel.objects.get(pk=parcel.pk).operation, cr.operation ) def test_cache_update(self): cr = self.create_context_record()[0] cr_pk = cr.pk # OP2010 - 1 | A | 1 | CR 1 ope_id, parcel_sec, parcel_nb, cr_label = cr.cached_label.split(' | ') self.assertEqual(ope_id, 'OP2010-1') self.assertEqual(parcel_sec, cr.parcel.section) self.assertEqual(parcel_nb, cr.parcel.parcel_number) self.assertEqual(cr_label, cr.label) new_lbl = "UE 2" cr.label = new_lbl cr.save() cr = models.ContextRecord.objects.get(pk=cr_pk) ope_id, parcel_sec, parcel_nb, cr_label = cr.cached_label.split(' | ') self.assertEqual(cr_label, new_lbl) new_sec, new_nb = "B", "42" parcel = cr.parcel parcel.section = new_sec parcel.parcel_number = new_nb parcel.save() cr = models.ContextRecord.objects.get(pk=cr_pk) ope_id, parcel_sec, parcel_nb, cr_label = cr.cached_label.split(' | ') self.assertEqual(parcel_sec, new_sec) self.assertEqual(parcel_nb, new_nb) cr.operation.year = 2017 cr.operation.save() cr = models.ContextRecord.objects.get(pk=cr_pk) ope_id, parcel_sec, parcel_nb, cr_label = cr.cached_label.split(' | ') self.assertEqual(ope_id, 'OP2017-1') class ContextRecordSearchTest(ContextRecordInit, TestCase): fixtures = ImportContextRecordTest.fixtures def setUp(self): IshtarSiteProfile.objects.create() self.username, self.password, self.user = create_superuser() self.create_context_record(data={"label": u"CR 1"}) self.create_context_record(data={"label": u"CR 2"}) cr_1 = self.context_records[0] cr_2 = self.context_records[1] sym_rel_type = models.RelationType.objects.filter( symmetrical=True).all()[0] self.cr_rel_type = sym_rel_type models.RecordRelations.objects.create( left_record=cr_1, right_record=cr_2, relation_type=sym_rel_type) def testSearchExport(self): c = Client() response = c.get(reverse('get-contextrecord')) # no result when no authentification self.assertTrue(not json.loads(response.content)) c.login(username=self.username, password=self.password) response = c.get(reverse('get-contextrecord')) self.assertTrue(json.loads(response.content)['total'] == 2) # test search label response = c.get(reverse('get-contextrecord'), {'label': 'cr 1'}) self.assertEqual(json.loads(response.content)['total'], 1) # test search between relations response = c.get(reverse('get-contextrecord'), {'label': 'cr 1', 'relation_types_0': self.cr_rel_type.pk}) self.assertEqual(json.loads(response.content)['total'], 2) # test search between related operations first_ope = self.operations[0] first_ope.year = 2010 first_ope.save() cr_1 = self.context_records[0] cr_1.operation = first_ope cr_1.save() other_ope = self.operations[1] other_ope.year = 2016 other_ope.save() cr_2 = self.context_records[1] cr_2.operation = other_ope cr_2.save() rel_ope = models_ope.RelationType.objects.create( symmetrical=True, label='Linked', txt_idx='link') models_ope.RecordRelations.objects.create( left_record=other_ope, right_record=first_ope, relation_type=rel_ope) response = c.get(reverse('get-contextrecord'), {'operation__year': 2010, 'ope_relation_types_0': rel_ope.pk}) self.assertEqual(json.loads(response.content)['total'], 2) # export response = c.get(reverse('get-contextrecord-full', kwargs={'type': 'csv'}), {'submited': '1'}) # 2 lines + header lines = [line for line in response.content.split('\n') if line] self.assertEqual(len(lines), 3) def testUnitHierarchicSearch(self): cr = self.context_records[0] c = Client() neg = models.Unit.objects.get(txt_idx='negative') dig = models.Unit.objects.get(txt_idx='digging') dest = models.Unit.objects.get(txt_idx='partial_destruction') cr.unit = (dig) cr.save() search = {'unit': dig.pk} # no result when no authentication response = c.get(reverse('get-contextrecord'), search) self.assertEqual(response.status_code, 200) self.assertTrue(not json.loads(response.content)) # one result for exact search c.login(username=self.username, password=self.password) response = c.get(reverse('get-contextrecord'), search) self.assertEqual(response.status_code, 200) res = json.loads(response.content) self.assertTrue(res['total'] == 1) self.assertEqual(res["rows"][0]["unit"], unicode(dig)) # no result for the brother search = {'unit': dest.pk} response = c.get(reverse('get-contextrecord'), search) self.assertEqual(response.status_code, 200) self.assertEqual(json.loads(response.content)['total'], 0) # one result for the father search = {'unit': neg.pk} response = c.get(reverse('get-contextrecord'), search) self.assertEqual(response.status_code, 200) self.assertEqual(json.loads(response.content)['total'], 1) def testPeriodHierarchicSearch(self): cr = self.context_records[0] c = Client() neo = models.Period.objects.get(txt_idx='neolithic') final_neo = models.Period.objects.get(txt_idx='final_neolithic') recent_neo = models.Period.objects.get(txt_idx='recent_neolithic') dating = models.Dating.objects.create( period=final_neo ) cr.datings.add(dating) search = {'datings__period': final_neo.pk} # no result when no authentication response = c.get(reverse('get-contextrecord'), search) self.assertEqual(response.status_code, 200) self.assertTrue(not json.loads(response.content)) # one result for exact search c.login(username=self.username, password=self.password) response = c.get(reverse('get-contextrecord'), search) self.assertEqual(response.status_code, 200) res = json.loads(response.content) self.assertTrue(res['total'] == 1) # no result for the brother search = {'datings__period': recent_neo.pk} response = c.get(reverse('get-contextrecord'), search) self.assertEqual(response.status_code, 200) self.assertEqual(json.loads(response.content)['total'], 0) # one result for the father search = {'datings__period': neo.pk} response = c.get(reverse('get-contextrecord'), search) self.assertEqual(response.status_code, 200) self.assertEqual(json.loads(response.content)['total'], 1) class RecordRelationsTest(ContextRecordInit, TestCase): fixtures = ImportOperationTest.fixtures model = models.ContextRecord def setUp(self): # two different context records self.create_context_record({"label": u"CR 1"}) self.create_context_record({"label": u"CR 2"}) def testRelations(self): sym_rel_type = models.RelationType.objects.create( symmetrical=True, txt_idx='sym') rel_type_1 = models.RelationType.objects.create( symmetrical=False, txt_idx='rel_1') # cannot be symmetrical and have an inverse_relation with self.assertRaises(ValidationError): rel_test = models.RelationType.objects.create( symmetrical=True, inverse_relation=rel_type_1, txt_idx='rel_3') rel_test.full_clean() # auto fill inverse relations rel_type_2 = models.RelationType.objects.create( symmetrical=False, inverse_relation=rel_type_1, txt_idx='rel_2') self.assertEqual(rel_type_1.inverse_relation, rel_type_2) cr_1 = self.context_records[0] cr_2 = self.context_records[1] # inserting a new symmetrical relation automatically creates the same # relation for the second context record rel = models.RecordRelations.objects.create( left_record=cr_1, right_record=cr_2, relation_type=sym_rel_type) self.assertEqual(models.RecordRelations.objects.filter( left_record=cr_2, right_record=cr_1, relation_type=sym_rel_type).count(), 1) # removing one symmetrical relation removes the other rel.delete() self.assertEqual(models.RecordRelations.objects.filter( left_record=cr_2, right_record=cr_1, relation_type=sym_rel_type).count(), 0) # for non-symmetrical relation, adding one relation automatically # adds the inverse rel = models.RecordRelations.objects.create( left_record=cr_1, right_record=cr_2, relation_type=rel_type_1) self.assertEqual(models.RecordRelations.objects.filter( left_record=cr_2, right_record=cr_1, relation_type=rel_type_2).count(), 1)