diff options
author | Étienne Loks <etienne.loks@iggdrasil.net> | 2017-01-24 11:00:36 +0100 |
---|---|---|
committer | Étienne Loks <etienne.loks@iggdrasil.net> | 2017-01-24 11:00:36 +0100 |
commit | 2a8d0c0edb7281e955fd7ad097e205f07cca1d5c (patch) | |
tree | db3e5e1d57ccf4684df9289242f62ae6597d76b0 | |
parent | ac595e9aa13d27fb15e70eb1f6e2d11f928a4c4a (diff) | |
parent | 3fbc69487b0fde33bb78f8d92da8eac25cb5da2e (diff) | |
download | Ishtar-2a8d0c0edb7281e955fd7ad097e205f07cca1d5c.tar.bz2 Ishtar-2a8d0c0edb7281e955fd7ad097e205f07cca1d5c.zip |
Merge branch 'master' into v0.9
-rw-r--r-- | CHANGES.md | 18 | ||||
-rw-r--r-- | archaeological_files/tests.py | 31 | ||||
-rw-r--r-- | archaeological_finds/models_finds.py | 6 | ||||
-rw-r--r-- | archaeological_finds/tests.py | 15 | ||||
-rw-r--r-- | archaeological_finds/urls.py | 2 | ||||
-rw-r--r-- | archaeological_finds/views.py | 5 | ||||
-rw-r--r-- | archaeological_operations/models.py | 29 | ||||
-rw-r--r-- | archaeological_operations/tests.py | 36 | ||||
-rw-r--r-- | install/install-ishtar.sh | 2 | ||||
-rwxr-xr-x | install/install.sh | 2 | ||||
-rw-r--r-- | ishtar_common/admin.py | 7 | ||||
-rw-r--r-- | ishtar_common/data_importer.py | 5 | ||||
-rw-r--r-- | ishtar_common/fixtures/initial_importtypes-fr.json | 404 | ||||
-rw-r--r-- | ishtar_common/management/commands/clean_ishtar.py | 35 | ||||
-rw-r--r-- | ishtar_common/models.py | 12 | ||||
-rw-r--r-- | ishtar_common/templates/blocks/inline_formset.html | 2 | ||||
-rw-r--r-- | ishtar_common/templates/ishtar/display_item.html | 7 | ||||
-rw-r--r-- | ishtar_common/templates/ishtar/formset.html | 2 | ||||
-rw-r--r-- | ishtar_common/templates/welcome.html | 2 | ||||
-rw-r--r-- | ishtar_common/tests.py | 18 | ||||
-rw-r--r-- | ishtar_common/views.py | 9 | ||||
-rw-r--r-- | ishtar_common/wizards.py | 13 | ||||
-rw-r--r-- | version.py | 2 |
23 files changed, 481 insertions, 183 deletions
diff --git a/CHANGES.md b/CHANGES.md index d41f7aa44..466d91d1d 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,6 +1,24 @@ Ishtar changelog ================ +0.99.4 (2017-01-24) +------------------- + +### Features ### + +- New specific view to display items (available for finds) +- Add a command to clean Ishtar DB +- Import: new formater types - new fixture +- Admin: add item key + +### Bug fixes ### + +- Operations: fix deletion of sym-linked operations +- Parcels: on associated operation or file detachment remove the parcel +- Fix inline_formset id in template +- Imports: fix attached files imports + + 0.99.3 (2017-01-20) ------------------- diff --git a/archaeological_files/tests.py b/archaeological_files/tests.py index f3419013a..8a4c105c6 100644 --- a/archaeological_files/tests.py +++ b/archaeological_files/tests.py @@ -44,9 +44,9 @@ class FileInit(object): self.user.set_password('tralala') self.user.save() self.o_user, created = User.objects.get_or_create(username='ousername') - person_type = PersonType(label=u'Test person type', - txt_idx='test_person', available=True) - person_type.save() + person_type, created = PersonType.objects.get_or_create( + label=u'Test ' u'person type', txt_idx='test_person', + available=True) self.extra_models['person_type'] = person_type self.model_list.append(person_type) @@ -56,9 +56,8 @@ class FileInit(object): self.extra_models['person'] = person self.model_list.append(person) - file_type = models.FileType(label=u'Test file type', - txt_idx='test_file', available=True) - file_type.save() + file_type, created = models.FileType.objects.get_or_create( + label=u'Test file type', txt_idx='test_file', available=True) self.extra_models['file_type'] = file_type self.model_list.append(file_type) @@ -205,6 +204,26 @@ class FileTest(TestCase, FileInit): self.assertTrue(data['records'] == 1) self.assertEqual(data['rows'][0]['internal_reference'], initial_ref) + def testPostDeleteParcels(self): + fle = self.item + town = Town.objects.create(name='plouf', numero_insee='20000') + parcel = Parcel.objects.create(town=town) + parcel_nb = Parcel.objects.count() + fle.parcels.add(parcel) + fle.delete() + # our parcel has no operation attached and should be deleted + self.assertEqual(parcel_nb - 1, Parcel.objects.count()) + + self.create_file() + fle = self.item + parcel = Parcel.objects.create(town=town) + parcel_nb = Parcel.objects.count() + fle.parcels.add(parcel) + fle.parcels.clear() # no signal raised... should resave + Parcel.objects.filter(pk=parcel.pk).all()[0].save() + # our parcel has no operation attached and should be deleted + self.assertEqual(parcel_nb - 1, Parcel.objects.count()) + # class ImporterTest(TestCase): # def testFormaters(self): # from archaeological_files import data_importer diff --git a/archaeological_finds/models_finds.py b/archaeological_finds/models_finds.py index 45c750f66..e8b6135a8 100644 --- a/archaeological_finds/models_finds.py +++ b/archaeological_finds/models_finds.py @@ -551,9 +551,9 @@ class Find(BaseHistorizedItem, ImageModel, OwnPerms, ShortMenuItem): def _get_treatments(self, model, rel='upstream', limit=None): treatments, findtreats = [], [] q = model.objects.filter( - find_id=self.pk).order_by( - 'treatment_nb', 'treatment__start_date', - 'treatment__end_date') + find_id=self.pk).order_by( + 'treatment_nb', 'treatment__start_date', + 'treatment__end_date') for findtreat in q.distinct().all(): if findtreat.pk in findtreats: continue diff --git a/archaeological_finds/tests.py b/archaeological_finds/tests.py index 18744ea3d..2d1367c58 100644 --- a/archaeological_finds/tests.py +++ b/archaeological_finds/tests.py @@ -20,8 +20,11 @@ import datetime from django.conf import settings +from django.contrib.auth.models import User from django.core.files.uploadedfile import SimpleUploadedFile +from django.core.urlresolvers import reverse from django.test import TestCase +from django.test.client import Client from ishtar_common.models import ImporterType, IshtarUser, ImporterColumn,\ FormaterType, ImportTarget @@ -294,6 +297,11 @@ class FindTest(FindInit, TestCase): def setUp(self): self.create_finds(force=True) + password = 'mypassword' + my_admin = User.objects.create_superuser( + 'myuser', 'myemail@test.com', password) + self.client = Client() + self.client.login(username=my_admin.username, password=password) def testExternalID(self): find = self.finds[0] @@ -309,6 +317,13 @@ class FindTest(FindInit, TestCase): base_find.context_record.external_id, base_find.label)) + def testShowFind(self): + find = self.finds[0] + response = self.client.get(reverse('display-find', args=[find.pk])) + self.assertEqual(response.status_code, 200) + self.assertIn('load_window("/show-find/{}/");'.format(find.pk), + response.content) + class PackagingTest(FindInit, TestCase): fixtures = [settings.ROOT_PATH + diff --git a/archaeological_finds/urls.py b/archaeological_finds/urls.py index 7be07d015..4a8dec030 100644 --- a/archaeological_finds/urls.py +++ b/archaeological_finds/urls.py @@ -269,6 +269,8 @@ urlpatterns += patterns( name='show-findbasket'), url(r'^show-find(?:/(?P<pk>.+))?/(?P<type>.+)?$', 'show_find', name=models.Find.SHOW_URL), + url(r'^display-find/(?P<pk>.+)/$', 'display_find', + name='display-' + models.Find.SLUG), url(r'^show-historized-find/(?P<pk>.+)?/(?P<date>.+)?$', 'show_find', name='show-historized-find'), url(r'^revert-find/(?P<pk>.+)/(?P<date>.+)$', diff --git a/archaeological_finds/views.py b/archaeological_finds/views.py index 01e88c1b7..084f15d13 100644 --- a/archaeological_finds/views.py +++ b/archaeological_finds/views.py @@ -38,8 +38,8 @@ from archaeological_operations.forms import FinalAdministrativeActDeleteForm from archaeological_context_records.forms \ import RecordFormSelection as RecordFormSelectionTable -from ishtar_common.views import get_item, show_item, revert_item, \ - get_autocomplete_generic, IshtarMixin, LoginRequiredMixin +from ishtar_common.views import get_item, show_item, display_item, \ + revert_item, get_autocomplete_generic, IshtarMixin, LoginRequiredMixin from ishtar_common.wizards import SearchWizard from archaeological_operations.wizards import AdministrativeActDeletionWizard @@ -108,6 +108,7 @@ show_findsource = show_item(models.FindSource, 'findsource') get_findsource = get_item(models.FindSource, 'get_findsource', 'findsource') show_find = show_item(models.Find, 'find') +display_find = display_item(models.Find, 'find') revert_find = revert_item(models.Find) show_findbasket = show_item(models.FindBasket, 'findbasket') diff --git a/archaeological_operations/models.py b/archaeological_operations/models.py index c8c291752..4a900c276 100644 --- a/archaeological_operations/models.py +++ b/archaeological_operations/models.py @@ -557,14 +557,6 @@ class Operation(ClosedItem, BaseHistorizedItem, ImageModel, OwnPerms, def is_active(self): return not bool(self.end_date) - def save(self, *args, **kwargs): - # put a default year if start_date is defined - if self.start_date and not self.year: - self.year = self.start_date.year - if self.operation_code is None: - self.operation_code = self.get_available_operation_code(self.year) - return super(Operation, self).save(*args, **kwargs) - @property def nb_parcels(self): _(u"Number of parcels") @@ -767,6 +759,14 @@ class Operation(ClosedItem, BaseHistorizedItem, ImageModel, OwnPerms, res['mode'] = u" ; ".join([str(m) for m in mode(finds)]) return res + def save(self, *args, **kwargs): + # put a default year if start_date is defined + if self.start_date and not self.year: + self.year = self.start_date.year + if self.operation_code is None: + self.operation_code = self.get_available_operation_code(self.year) + return super(Operation, self).save(*args, **kwargs) + m2m_changed.connect(cached_label_changed, sender=Operation.towns.through) @@ -1193,10 +1193,10 @@ class Parcel(LightHistorizedItem): associated_file = models.ForeignKey( 'archaeological_files.File', related_name='parcels', verbose_name=_(u"File"), - blank=True, null=True) + blank=True, null=True, on_delete=models.CASCADE) operation = models.ForeignKey( Operation, related_name='parcels', blank=True, null=True, - verbose_name=_(u"Operation")) + verbose_name=_(u"Operation"), on_delete=models.CASCADE) year = models.IntegerField(_(u"Year"), blank=True, null=True) town = models.ForeignKey(Town, related_name='parcels', verbose_name=_(u"Town")) @@ -1356,6 +1356,13 @@ def parcel_post_save(sender, **kwargs): if not kwargs['instance']: return parcel = kwargs['instance'] + created = kwargs.get('created', None) + + # remove when the parcel is linked to nothing + if not getattr(parcel, '_updated_id', None) and not created and not \ + parcel.operation and not parcel.associated_file: + parcel.delete() + return updated = False if not parcel.external_id or parcel.auto_external_id: @@ -1365,6 +1372,7 @@ def parcel_post_save(sender, **kwargs): parcel.auto_external_id = True parcel.external_id = external_id if updated: + parcel._updated_id = True parcel.save() return @@ -1378,6 +1386,7 @@ def parcel_post_save(sender, **kwargs): if parcel.operation and parcel.associated_file: # parcels are copied between files and operations parcel.copy_to_operation() + post_save.connect(parcel_post_save, sender=Parcel) diff --git a/archaeological_operations/tests.py b/archaeological_operations/tests.py index d8399eedb..33199dd04 100644 --- a/archaeological_operations/tests.py +++ b/archaeological_operations/tests.py @@ -35,7 +35,7 @@ import models from archaeological_operations import views from ishtar_common.models import OrganizationType, Organization, \ - ImporterType, IshtarUser, TargetKey, IshtarSiteProfile + ImporterType, IshtarUser, TargetKey, IshtarSiteProfile, Town from ishtar_common import forms_common from ishtar_common.tests import WizardTest, WizardTestFormData as FormData, \ @@ -578,8 +578,7 @@ class OperationTest(TestCase, OperationInitTest): {'operator': self.orgas[0].pk}) self.assertTrue(json.loads(response.content)['total'] == 2) - def testRelatedSearch(self): - c = Client() + def create_relations(self): rel1 = models.RelationType.objects.create( symmetrical=True, label='Include', txt_idx='include') rel2 = models.RelationType.objects.create( @@ -589,6 +588,11 @@ class OperationTest(TestCase, OperationInitTest): left_record=self.operations[0], right_record=self.operations[1], relation_type=rel1) + return rel1, rel2 + + def testRelatedSearch(self): + c = Client() + rel1, rel2 = self.create_relations() self.operations[1].year = 2011 self.operations[1].save() search = {'year': '2010', 'relation_types_0': rel2.pk} @@ -599,6 +603,28 @@ class OperationTest(TestCase, OperationInitTest): response = c.get(reverse('get-operation'), search) self.assertTrue(json.loads(response.content)['total'] == 2) + def testPostDeleteRelations(self): + self.create_relations() + self.operations[0].delete() + + def testPostDeleteParcels(self): + ope = self.operations[0] + town = Town.objects.create(name='plouf', numero_insee='20000') + parcel = models.Parcel.objects.create(town=town) + parcel_nb = models.Parcel.objects.count() + ope.parcels.add(parcel) + ope.delete() + # our parcel has no operation attached and should be deleted + self.assertEqual(parcel_nb - 1, models.Parcel.objects.count()) + ope = self.operations[1] + parcel = models.Parcel.objects.create(town=town) + parcel_nb = models.Parcel.objects.count() + ope.parcels.add(parcel) + ope.parcels.clear() # no signal raised... should resave + models.Parcel.objects.filter(pk=parcel.pk).all()[0].save() + # our parcel has no operation attached and should be deleted + self.assertEqual(parcel_nb - 1, models.Parcel.objects.count()) + def testOwnSearch(self): c = Client() response = c.get(reverse('get-operation'), {'year': '2010'}) @@ -718,6 +744,8 @@ class OperationWizardDeleteTest(OperationWizardCreationTest): def pre_wizard(self): self.ope = self.get_default_operation(force=True) + self.ope.parcels.add(self.create_parcel()[0]) + self.parcel_nb = models.Parcel.objects.count() self.form_datas[0].form_datas['selec-operation_deletion']['pk'] = \ self.ope.pk self.operation_number = models.Operation.objects.count() @@ -726,6 +754,8 @@ class OperationWizardDeleteTest(OperationWizardCreationTest): def post_wizard(self): self.assertEqual(self.operation_number - 1, models.Operation.objects.count()) + # associated parcel removed + self.assertEqual(self.parcel_nb - 1, models.Parcel.objects.count()) class OperationWizardClosingTest(OperationWizardCreationTest): diff --git a/install/install-ishtar.sh b/install/install-ishtar.sh index 214813406..4c5995ca0 100644 --- a/install/install-ishtar.sh +++ b/install/install-ishtar.sh @@ -327,7 +327,7 @@ EOF mkdir -p $full_install_path cd $full_install_path ( set -x; git clone git://git.proxience.com/git/oook_replace.git 2> /dev/null ) - ( set -x; git clone https://gitlab.com/ishtar/ishtar.git 2> /dev/null ) + ( set -x; git clone https://gitlab.com/iggdrasil/ishtar.git 2> /dev/null ) cd ishtar git fetch 2> /dev/null git checkout $version 2> /dev/null diff --git a/install/install.sh b/install/install.sh index e80730f9d..1556306c6 100755 --- a/install/install.sh +++ b/install/install.sh @@ -133,7 +133,7 @@ mkdir $INSTALL_PATH'/conf' cd $INSTALL_PATH echo ' * ishtar' -git clone https://gitlab.com/ishtar/ishtar.git 2> /dev/null +git clone https://gitlab.com/iggdrasil/ishtar.git 2> /dev/null # echo ' * oook!' # git clone git://git.proxience.com/git/oook_replace.git 2> /dev/null # ln -s $INSTALL_PATH'/oook_replace/oook_replace' $INSTALL_PATH'/ishtar/' diff --git a/ishtar_common/admin.py b/ishtar_common/admin.py index efb452e74..b3e0763b5 100644 --- a/ishtar_common/admin.py +++ b/ishtar_common/admin.py @@ -246,6 +246,13 @@ class IshtarUserAdmin(admin.ModelAdmin): admin.site.register(models.IshtarUser, IshtarUserAdmin) + +class ItemKeyAdmin(admin.ModelAdmin): + list_display = ('content_type', 'key', 'content_object', 'importer') + search_fields = ('key', ) +admin.site.register(models.ItemKey, ItemKeyAdmin) + + basic_models = [models.DocumentTemplate] if settings.COUNTRY == 'fr': basic_models += [models.Arrondissement, models.Canton] diff --git a/ishtar_common/data_importer.py b/ishtar_common/data_importer.py index b669995e3..49705f0df 100644 --- a/ishtar_common/data_importer.py +++ b/ishtar_common/data_importer.py @@ -1311,11 +1311,14 @@ class Importer(object): self.get_field(cls, attribute, data, m2ms, c_c_path, new_created) - # filter uncessary default values create_dict = copy.deepcopy(data) for k in create_dict.keys(): + # filter unnecessary default values if type(create_dict[k]) == dict: create_dict.pop(k) + # File doesn't like deepcopy + if type(create_dict[k]) == File: + create_dict[k] = copy.copy(data[k]) # default values path = tuple(path) diff --git a/ishtar_common/fixtures/initial_importtypes-fr.json b/ishtar_common/fixtures/initial_importtypes-fr.json index 0592ca60f..342367f2b 100644 --- a/ishtar_common/fixtures/initial_importtypes-fr.json +++ b/ishtar_common/fixtures/initial_importtypes-fr.json @@ -7,6 +7,7 @@ "is_template": true, "unicity_keys": "code_patriarche", "users": [ + 2 ], "slug": "operatio", "associated_models": "archaeological_operations.models.Operation", @@ -21,6 +22,7 @@ "is_template": true, "unicity_keys": null, "users": [ + 2 ], "slug": null, "associated_models": "archaeological_operations.models.Operation", @@ -35,6 +37,7 @@ "is_template": true, "unicity_keys": null, "users": [ + 2 ], "slug": null, "associated_models": "archaeological_context_records.models.ContextRecord", @@ -3136,7 +3139,7 @@ "description": "Chronologies associ\u00e9es (plusieurs possibles s\u00e9par\u00e9es par &), exemple : \"Gallo-romain & M\u00e9di\u00e9val\", \"GR&MED\", \"M\u00e9solithique final & M\u00e9so moyen & Epipal\", etc.", "regexp_pre_filter": null, "required": false, - "label": null, + "label": "Periodes", "importer_type": 18 } }, @@ -3421,7 +3424,7 @@ "model": "ishtar_common.importercolumn", "fields": { "col_number": 7, - "description": "R\u00e9f\u00e9rence du point topo, d'ordinaire un entier mais peut \u00eatre autre chose. Champ texte, max. 120 caract\u00e8res. Exemple : \"7220\", \"pt. 72\", etc.\r\n\r\nNON LI\u00c9 POUR L'INSTANT.", + "description": "R\u00e9f\u00e9rence du point topo, d'ordinaire un entier mais peut \u00eatre autre chose. Champ texte, max. 120 caract\u00e8res. Exemple : \"7220\", \"pt. 72\", etc.\r\n", "regexp_pre_filter": null, "required": false, "label": "Ref. point topo", @@ -3505,7 +3508,7 @@ "model": "ishtar_common.importercolumn", "fields": { "col_number": 14, - "description": "Type(s) d'objet(s), s\u00e9par\u00e9s par des \"&\". Exemple : \"tesson & charbon\", \"os & m\u00e9tal\", \"faune\", \"fibule & bague\", \"lame & lamelle\", \"\u00e9clat & nucl\u00e9us\", etc.", + "description": "Type(s) d'objet(s), s\u00e9par\u00e9s par des \"&\". Exemple : \"tesson & charbon\", \"vase\", \"scapula\", \"fibule & bague\", \"lame & lamelle\", \"\u00e9clat & nucl\u00e9us\", etc.", "regexp_pre_filter": null, "required": false, "label": "Type(s) d'objet(s)", @@ -3693,6 +3696,54 @@ } }, { + "pk": 365, + "model": "ishtar_common.importercolumn", + "fields": { + "col_number": 30, + "description": "Coordonn\u00e9e X pour cet objet", + "regexp_pre_filter": null, + "required": false, + "label": "Coordonn\u00e9e X", + "importer_type": 20 + } + }, + { + "pk": 366, + "model": "ishtar_common.importercolumn", + "fields": { + "col_number": 31, + "description": "Coordonn\u00e9e Y pour cet objet", + "regexp_pre_filter": null, + "required": false, + "label": "Coordonn\u00e9e Y", + "importer_type": 20 + } + }, + { + "pk": 367, + "model": "ishtar_common.importercolumn", + "fields": { + "col_number": 32, + "description": "Coordonn\u00e9e Z pour cet objet (altitude NGF ou arbitraire)", + "regexp_pre_filter": null, + "required": false, + "label": "Coordonn\u00e9e Z", + "importer_type": 20 + } + }, + { + "pk": 368, + "model": "ishtar_common.importercolumn", + "fields": { + "col_number": 33, + "description": "Code permettant de qualifi\u00e9 le mode de projection des donnes (SRS /EPSG). Exemple : \"EPSG:2154\" pour le Lambert 93", + "regexp_pre_filter": null, + "required": false, + "label": "Syst\u00e8me de r\u00e9f\u00e9rence spatiale", + "importer_type": 20 + } + }, + { "pk": 46, "model": "ishtar_common.importtarget", "fields": { @@ -4505,6 +4556,20 @@ } }, { + "pk": 38, + "model": "ishtar_common.importtarget", + "fields": { + "comment": "", + "target": "label", + "column": 37, + "formater_type": 3, + "concat_str": null, + "regexp_filter": null, + "concat": false, + "force_new": false + } + }, + { "pk": 305, "model": "ishtar_common.importtarget", "fields": { @@ -4673,20 +4738,6 @@ } }, { - "pk": 335, - "model": "ishtar_common.importtarget", - "fields": { - "comment": "", - "target": "parcel__external_id", - "column": 311, - "formater_type": 28, - "concat_str": "", - "regexp_filter": null, - "concat": false, - "force_new": false - } - }, - { "pk": 338, "model": "ishtar_common.importtarget", "fields": { @@ -4757,20 +4808,6 @@ } }, { - "pk": 350, - "model": "ishtar_common.importtarget", - "fields": { - "comment": "", - "target": "datings__period", - "column": 325, - "formater_type": 6, - "concat_str": "", - "regexp_filter": null, - "concat": false, - "force_new": false - } - }, - { "pk": 353, "model": "ishtar_common.importtarget", "fields": { @@ -4813,20 +4850,6 @@ } }, { - "pk": 362, - "model": "ishtar_common.importtarget", - "fields": { - "comment": "", - "target": "context_record__external_id", - "column": 337, - "formater_type": 28, - "concat_str": "", - "regexp_filter": null, - "concat": false, - "force_new": false - } - }, - { "pk": 365, "model": "ishtar_common.importtarget", "fields": { @@ -4841,20 +4864,6 @@ } }, { - "pk": 368, - "model": "ishtar_common.importtarget", - "fields": { - "comment": "", - "target": "is_isolated", - "column": 344, - "formater_type": 19, - "concat_str": "", - "regexp_filter": null, - "concat": false, - "force_new": false - } - }, - { "pk": 371, "model": "ishtar_common.importtarget", "fields": { @@ -4953,34 +4962,6 @@ } }, { - "pk": 388, - "model": "ishtar_common.importtarget", - "fields": { - "comment": "", - "target": "find__datings__period", - "column": 364, - "formater_type": 6, - "concat_str": "", - "regexp_filter": null, - "concat": false, - "force_new": false - } - }, - { - "pk": 38, - "model": "ishtar_common.importtarget", - "fields": { - "comment": "", - "target": "label", - "column": 37, - "formater_type": 3, - "concat_str": null, - "regexp_filter": null, - "concat": false, - "force_new": false - } - }, - { "pk": 3, "model": "ishtar_common.importtarget", "fields": { @@ -5191,20 +5172,6 @@ } }, { - "pk": 336, - "model": "ishtar_common.importtarget", - "fields": { - "comment": "", - "target": "parcel__external_id", - "column": 312, - "formater_type": 11, - "concat_str": "", - "regexp_filter": null, - "concat": false, - "force_new": false - } - }, - { "pk": 339, "model": "ishtar_common.importtarget", "fields": { @@ -5317,20 +5284,6 @@ } }, { - "pk": 363, - "model": "ishtar_common.importtarget", - "fields": { - "comment": "", - "target": "context_record__external_id", - "column": 338, - "formater_type": 35, - "concat_str": "", - "regexp_filter": null, - "concat": false, - "force_new": false - } - }, - { "pk": 366, "model": "ishtar_common.importtarget", "fields": { @@ -6115,6 +6068,76 @@ } }, { + "pk": 336, + "model": "ishtar_common.importtarget", + "fields": { + "comment": "", + "target": "parcel__external_id", + "column": 312, + "formater_type": 11, + "concat_str": "-", + "regexp_filter": null, + "concat": false, + "force_new": false + } + }, + { + "pk": 335, + "model": "ishtar_common.importtarget", + "fields": { + "comment": "", + "target": "parcel__external_id", + "column": 311, + "formater_type": 28, + "concat_str": "-", + "regexp_filter": null, + "concat": false, + "force_new": false + } + }, + { + "pk": 362, + "model": "ishtar_common.importtarget", + "fields": { + "comment": "", + "target": "context_record__external_id", + "column": 337, + "formater_type": 28, + "concat_str": "-", + "regexp_filter": null, + "concat": false, + "force_new": false + } + }, + { + "pk": 363, + "model": "ishtar_common.importtarget", + "fields": { + "comment": "", + "target": "context_record__external_id", + "column": 338, + "formater_type": 35, + "concat_str": "-", + "regexp_filter": null, + "concat": false, + "force_new": false + } + }, + { + "pk": 388, + "model": "ishtar_common.importtarget", + "fields": { + "comment": "", + "target": "find__datings__period", + "column": 364, + "formater_type": 6, + "concat_str": "", + "regexp_filter": null, + "concat": false, + "force_new": true + } + }, + { "pk": 99, "model": "ishtar_common.importtarget", "fields": { @@ -7445,34 +7468,6 @@ } }, { - "pk": 361, - "model": "ishtar_common.importtarget", - "fields": { - "comment": "", - "target": "context_record__external_id", - "column": 336, - "formater_type": 11, - "concat_str": "", - "regexp_filter": null, - "concat": false, - "force_new": false - } - }, - { - "pk": 364, - "model": "ishtar_common.importtarget", - "fields": { - "comment": "", - "target": "context_record__external_id", - "column": 339, - "formater_type": 3, - "concat_str": "", - "regexp_filter": null, - "concat": false, - "force_new": false - } - }, - { "pk": 367, "model": "ishtar_common.importtarget", "fields": { @@ -7529,6 +7524,20 @@ } }, { + "pk": 364, + "model": "ishtar_common.importtarget", + "fields": { + "comment": "", + "target": "context_record__external_id", + "column": 339, + "formater_type": 3, + "concat_str": "-", + "regexp_filter": null, + "concat": false, + "force_new": false + } + }, + { "pk": 381, "model": "ishtar_common.importtarget", "fields": { @@ -7557,6 +7566,104 @@ } }, { + "pk": 389, + "model": "ishtar_common.importtarget", + "fields": { + "comment": "", + "target": "topographic_localisation", + "column": 343, + "formater_type": 3, + "concat_str": "", + "regexp_filter": null, + "concat": false, + "force_new": false + } + }, + { + "pk": 390, + "model": "ishtar_common.importtarget", + "fields": { + "comment": "", + "target": "x", + "column": 365, + "formater_type": 21, + "concat_str": "", + "regexp_filter": null, + "concat": false, + "force_new": false + } + }, + { + "pk": 391, + "model": "ishtar_common.importtarget", + "fields": { + "comment": "", + "target": "y", + "column": 366, + "formater_type": 21, + "concat_str": "", + "regexp_filter": null, + "concat": false, + "force_new": false + } + }, + { + "pk": 392, + "model": "ishtar_common.importtarget", + "fields": { + "comment": "", + "target": "z", + "column": 367, + "formater_type": 21, + "concat_str": "", + "regexp_filter": null, + "concat": false, + "force_new": false + } + }, + { + "pk": 350, + "model": "ishtar_common.importtarget", + "fields": { + "comment": "", + "target": "datings__period", + "column": 325, + "formater_type": 6, + "concat_str": "", + "regexp_filter": null, + "concat": false, + "force_new": true + } + }, + { + "pk": 393, + "model": "ishtar_common.importtarget", + "fields": { + "comment": "", + "target": "spatial_reference_system", + "column": 368, + "formater_type": 46, + "concat_str": "", + "regexp_filter": null, + "concat": false, + "force_new": false + } + }, + { + "pk": 361, + "model": "ishtar_common.importtarget", + "fields": { + "comment": "", + "target": "context_record__external_id", + "column": 336, + "formater_type": 11, + "concat_str": "-", + "regexp_filter": null, + "concat": false, + "force_new": false + } + }, + { "pk": 25, "model": "ishtar_common.formatertype", "fields": { @@ -7764,6 +7871,15 @@ } }, { + "pk": 46, + "model": "ishtar_common.formatertype", + "fields": { + "formater_type": "TypeFormater", + "many_split": "", + "options": "SpatialReferenceSystem" + } + }, + { "pk": 13, "model": "ishtar_common.formatertype", "fields": { @@ -8449,4 +8565,4 @@ "force_new": false } } -] +]
\ No newline at end of file diff --git a/ishtar_common/management/commands/clean_ishtar.py b/ishtar_common/management/commands/clean_ishtar.py new file mode 100644 index 000000000..d3da40fd8 --- /dev/null +++ b/ishtar_common/management/commands/clean_ishtar.py @@ -0,0 +1,35 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# Copyright (C) 2017 É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 sys + +from django.core.management.base import BaseCommand + +from archaeological_operations.models import Parcel + + +class Command(BaseCommand): + args = '' + help = 'Clean unused items' + + def handle(self, *args, **options): + for parcel in Parcel.objects.all(): + parcel.skip_history_when_saving = True + parcel.save() + self.stdout.write('Parcel cleaned.\n') diff --git a/ishtar_common/models.py b/ishtar_common/models.py index d58d549c8..d1d58f184 100644 --- a/ishtar_common/models.py +++ b/ishtar_common/models.py @@ -959,11 +959,12 @@ def post_delete_record_relation(sender, instance, **kwargs): # no symetric/inverse is defined if not sym_rel_type: return - - dct = {'right_record': instance.left_record, - 'left_record': instance.right_record, + dct = {'right_record_id': instance.left_record_id, + 'left_record_id': instance.right_record_id, 'relation_type': sym_rel_type} - instance.__class__.objects.filter(**dct).delete() + q = instance.__class__.objects.filter(**dct) + if q.count(): + q.delete() class ShortMenuItem(object): @@ -2048,10 +2049,13 @@ TARGET_MODELS = [ _(u"Conservatory state")), ('archaeological_finds.models.PreservationType', _(u"Preservation type")), ('archaeological_finds.models.ObjectType', _(u"Object type")), + ('archaeological_finds.models.IntegrityType', _(u"Integrity type")), + ('archaeological_finds.models.RemarkabilityType', _(u"Remarkability type")), ('archaeological_context_records.models.IdentificationType', _("Identification type")), ('archaeological_context_records.models.RelationType', _(u"Context record relation type")), + ('SpatialReferenceSystem', _(u"Spatial reference system")), ('SupportType', _(u"Support type")), ] diff --git a/ishtar_common/templates/blocks/inline_formset.html b/ishtar_common/templates/blocks/inline_formset.html index 4d4042985..9c1daad28 100644 --- a/ishtar_common/templates/blocks/inline_formset.html +++ b/ishtar_common/templates/blocks/inline_formset.html @@ -1,6 +1,6 @@ {% load i18n %} {% if extra_formset.non_form_errors %}<div class='errors'>{{extra_formset.non_form_errors.as_ul}}</div>{% endif %} - {% if header %}<table class='inline-table' id='{{formset}}'> + {% if header %}<table class='inline-table' id='{{formset.prefix}}'> <caption>{% trans caption %}</caption> <thead> <tr>{% for field in formset.0.visible_fields%} diff --git a/ishtar_common/templates/ishtar/display_item.html b/ishtar_common/templates/ishtar/display_item.html index 87dda6dcf..e00fef05b 100644 --- a/ishtar_common/templates/ishtar/display_item.html +++ b/ishtar_common/templates/ishtar/display_item.html @@ -1,11 +1,10 @@ {% extends "base.html" %} {% load i18n %} -{% load url from future %} {% block content %} -<h2>{{page_name}}</h2> <script type='text/javascript'> -$(function() { - load_window("{% url item_url pk %}"); +$(document).ready( +function(){ + load_window("/show-{{item_type}}/{{pk}}/"); }); </script> {% endblock %} diff --git a/ishtar_common/templates/ishtar/formset.html b/ishtar_common/templates/ishtar/formset.html index c8a92165e..5454ae7e5 100644 --- a/ishtar_common/templates/ishtar/formset.html +++ b/ishtar_common/templates/ishtar/formset.html @@ -5,7 +5,7 @@ <div class='form' id='global-vars'> <form action="." method="post">{% csrf_token %} {% inline_formset ' ' formset %} -<input type="submit" value="{% trans "Validate" %}"/> +<input type="submit" value="{% trans 'Validate' %}"/> </form> </div> {% endblock %} diff --git a/ishtar_common/templates/welcome.html b/ishtar_common/templates/welcome.html index eb9de475e..508f7be02 100644 --- a/ishtar_common/templates/welcome.html +++ b/ishtar_common/templates/welcome.html @@ -8,5 +8,5 @@ <li><a href='https://ishtar-archeo.net' target="_blank">{% trans "Presentation site and blog" %}</a>{% trans ":"%} {% trans "stay tuned with Ishtar news!" %}</li> <li><a href='{% url 'admin:index' %}' target="_blank">{% trans "Admin interface" %}</a>{% trans ":"%} {% trans "for admin only." %}</li> <li><a href="https://forum.ishtar-archeo.net/" target="_blank">{% trans "Forum" %}</a>{% trans ":"%} {% trans "need help? find a new bug? a fantastic feature to propose? Here is the place to go." %}</li> - <li><a href="https://gitlab.com/ishtar/ishtar" target="_blank">{% trans "Source code" %}</a> – <a href="https://tickets.iggdrasil.net/projects/ishtar" target="_blank">{% trans "tickets" %}</a>{% trans ":"%} {% trans "where the magic happens." %}</li> + <li><a href="https://gitlab.com/iggdrasil/ishtar" target="_blank">{% trans "Source code" %}</a> – <a href="https://tickets.iggdrasil.net/projects/ishtar" target="_blank">{% trans "tickets" %}</a>{% trans ":"%} {% trans "where the magic happens." %}</li> </ul> diff --git a/ishtar_common/tests.py b/ishtar_common/tests.py index f22e42e38..10584e4f2 100644 --- a/ishtar_common/tests.py +++ b/ishtar_common/tests.py @@ -17,6 +17,7 @@ # See the file COPYING for details. +from StringIO import StringIO from bs4 import BeautifulSoup as Soup from django.conf import settings @@ -25,6 +26,7 @@ from django.contrib.contenttypes.models import ContentType from django.core.cache import cache from django.core.exceptions import ValidationError from django.core.files.base import File as DjangoFile +from django.core.management import call_command from django.core.urlresolvers import reverse from django.template.defaultfilters import slugify from django.test import TestCase @@ -76,6 +78,22 @@ def create_user(): return username, password, user +class CommandsTestCase(TestCase): + def test_clean_ishtar(self): + """ + Clean ishtar db + """ + from archaeological_operations.models import Parcel + p = Parcel.objects.create( + town=models.Town.objects.create(name='test', numero_insee='25000')) + parcel_nb = Parcel.objects.count() + out = StringIO() + call_command('clean_ishtar', stdout=out) + # no operation or file attached - the parcel should have disappear + self.assertEqual(parcel_nb - 1, Parcel.objects.count()) + self.assertEqual(Parcel.objects.filter(pk=p.pk).count(), 0) + + class WizardTestFormData(object): """ Test set to simulate wizard steps diff --git a/ishtar_common/views.py b/ishtar_common/views.py index 42ebdf277..b0817fc59 100644 --- a/ishtar_common/views.py +++ b/ishtar_common/views.py @@ -1112,6 +1112,15 @@ def get_by_importer(request, slug, data_type='json', full=False, )(request, data_type, full, force_own, **dct) +def display_item(model, name, extra_dct=None): + def func(request, pk, **dct): + dct['item_type'] = name + dct['pk'] = pk + return render_to_response('ishtar/display_item.html', dct, + context_instance=RequestContext(request)) + return func + + def show_item(model, name, extra_dct=None): def func(request, pk, **dct): try: diff --git a/ishtar_common/wizards.py b/ishtar_common/wizards.py index 18336cff5..735ad62fd 100644 --- a/ishtar_common/wizards.py +++ b/ishtar_common/wizards.py @@ -31,6 +31,7 @@ from django.core.files.storage import default_storage from django.core.mail import send_mail from django.db.models.fields.files import FileField from django.db.models.fields.related import ManyToManyField + from django.http import HttpResponseRedirect from django.forms import ValidationError from django.shortcuts import render_to_response, redirect @@ -679,6 +680,18 @@ class Wizard(NamedUrlWizardView): # material_index management for baseitems obj._cached_label_checked = False obj.save() + + # force post_save for old related m2ms (which can have been detached + # from the current object) + for model in old_m2ms: + for item in old_m2ms[model]: + # verify it hasn't been deleted + q = item.__class__.objects.filter(pk=item.pk) + if q.count(): + item = q.all()[0] + item.skip_history_when_saving = True + item.save() + # make the new object a default if self.current_obj_slug: self.request.session[self.current_obj_slug] = unicode(obj.pk) diff --git a/version.py b/version.py index 128b5c14a..2092b9c8f 100644 --- a/version.py +++ b/version.py @@ -1,4 +1,4 @@ -VERSION = (0, 99, 3) +VERSION = (0, 99, 4) def get_version(): |