summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorÉtienne Loks <etienne.loks@peacefrogs.net>2011-04-09 00:35:53 +0200
committerÉtienne Loks <etienne.loks@peacefrogs.net>2011-04-09 00:35:53 +0200
commit5492bfeaec636bac5a734dea1d988460abcc0ce3 (patch)
treef9001acc59e9cc546f3dc44879d29d9ab3f04f62
parentcfbcd3cd0851dcb9846e4e6701cbcb8e878dd343 (diff)
downloadIshtar-5492bfeaec636bac5a734dea1d988460abcc0ce3.tar.bz2
Ishtar-5492bfeaec636bac5a734dea1d988460abcc0ce3.zip
Manage the rollback on historized items (refs #349)
-rw-r--r--ishtar/furnitures/fixtures/organization_type-fr.json82
-rw-r--r--ishtar/furnitures/fixtures/person_type-fr.json72
-rw-r--r--ishtar/furnitures/fixtures/treatment_type-fr.json123
-rw-r--r--ishtar/furnitures/models.py50
-rw-r--r--ishtar/furnitures/tests.py27
-rw-r--r--ishtar/furnitures/views.py1
6 files changed, 344 insertions, 11 deletions
diff --git a/ishtar/furnitures/fixtures/organization_type-fr.json b/ishtar/furnitures/fixtures/organization_type-fr.json
new file mode 100644
index 000000000..c25bd8179
--- /dev/null
+++ b/ishtar/furnitures/fixtures/organization_type-fr.json
@@ -0,0 +1,82 @@
+[
+ {
+ "pk": 1,
+ "model": "furnitures.organizationtype",
+ "fields": {
+ "comment": "",
+ "available": true,
+ "txt_idx": "sra",
+ "label": "Service R\u00e9gional d'Arch\u00e9ologie"
+ }
+ },
+ {
+ "pk": 2,
+ "model": "furnitures.organizationtype",
+ "fields": {
+ "comment": "D\u00e9cret 2004\r\n\r\n\"Op\u00e9rateurs\" les personnes qui r\u00e9alisent les op\u00e9rations arch\u00e9ologiques.",
+ "available": true,
+ "txt_idx": "operator",
+ "label": "Op\u00e9rateur d'arch\u00e9ologie pr\u00e9ventive"
+ }
+ },
+ {
+ "pk": 4,
+ "model": "furnitures.organizationtype",
+ "fields": {
+ "comment": "Laboratoire ayant sous sa responsabilit\u00e9 du mobilier arch\u00e9ologique de mani\u00e8re temporaire. C'est un type de d\u00e9p\u00f4t. C'est un lieu de traitement.",
+ "available": true,
+ "txt_idx": "restoration_laboratory",
+ "label": "Laboratoire de restauration"
+ }
+ },
+ {
+ "pk": 5,
+ "model": "furnitures.organizationtype",
+ "fields": {
+ "comment": "Pour des entreprises, collectivit\u00e9s territoriales ou autres organisations",
+ "available": true,
+ "txt_idx": "general_contractor",
+ "label": "Am\u00e9nageur"
+ }
+ },
+ {
+ "pk": 6,
+ "model": "furnitures.organizationtype",
+ "fields": {
+ "comment": "Laboratoire de recherche du CNRS. Peut-\u00eatre une UMR et donc int\u00e9gr\u00e9 des chercheurs de l'universit\u00e9.",
+ "available": true,
+ "txt_idx": "cnrs_laboratory",
+ "label": "Laboratoire CNRS"
+ }
+ },
+ {
+ "pk": 7,
+ "model": "furnitures.organizationtype",
+ "fields": {
+ "comment": "Cette organisation et ses membres travaillent b\u00e9n\u00e9volement",
+ "available": true,
+ "txt_idx": "volunteer",
+ "label": "B\u00e9n\u00e9vole"
+ }
+ },
+ {
+ "pk": 8,
+ "model": "furnitures.organizationtype",
+ "fields": {
+ "comment": "les services qui d\u00e9livrent les autorisations requises pour les diff\u00e9rents projets (DDE, services\r\nurbanisme des collectivit\u00e9s, pr\u00e9fectures, Drire, etc.)",
+ "available": true,
+ "txt_idx": "planning_service",
+ "label": "Service instructeur"
+ }
+ },
+ {
+ "pk": 9,
+ "model": "furnitures.organizationtype",
+ "fields": {
+ "comment": "",
+ "available": true,
+ "txt_idx": "museum",
+ "label": "Mus\u00e9e"
+ }
+ }
+]
diff --git a/ishtar/furnitures/fixtures/person_type-fr.json b/ishtar/furnitures/fixtures/person_type-fr.json
new file mode 100644
index 000000000..0613b3129
--- /dev/null
+++ b/ishtar/furnitures/fixtures/person_type-fr.json
@@ -0,0 +1,72 @@
+[
+ {
+ "pk": 1,
+ "model": "furnitures.persontype",
+ "fields": {
+ "comment": "",
+ "available": true,
+ "txt_idx": "administrator",
+ "label": "Administrateur"
+ }
+ },
+ {
+ "pk": 2,
+ "model": "furnitures.persontype",
+ "fields": {
+ "comment": "Article 13 D\u00e9cret 2004\r\n\r\nLe pr\u00e9fet de r\u00e9gion \u00e9dicte les prescriptions arch\u00e9ologiques, d\u00e9livre l'autorisation de fouille et d\u00e9signe le responsable scientifique de toute op\u00e9ration d'arch\u00e9ologie pr\u00e9ventive.\r\n\r\nLe responsable scientifique est l'interlocuteur du pr\u00e9fet de r\u00e9gion et le garant de la qualit\u00e9 scientifique de l'op\u00e9ration arch\u00e9ologique. A ce titre, il prend, dans le cadre de la mise en oeuvre du projet d'intervention de l'op\u00e9rateur, les d\u00e9cisions relatives \u00e0 la conduite scientifique de l'op\u00e9ration et \u00e0 l'\u00e9laboration du rapport dont il dirige la r\u00e9daction. Il peut \u00eatre diff\u00e9rent pour la r\u00e9alisation du diagnostic et pour la r\u00e9alisation de la fouille.",
+ "available": true,
+ "txt_idx": "head_scientist",
+ "label": "Responsable scientifique"
+ }
+ },
+ {
+ "pk": 3,
+ "model": "furnitures.persontype",
+ "fields": {
+ "comment": "Responsables de dossiers d'arch\u00e9ologie",
+ "available": true,
+ "txt_idx": "sra_agent",
+ "label": "Agent scientifique SRA"
+ }
+ },
+ {
+ "pk": 4,
+ "model": "furnitures.persontype",
+ "fields": {
+ "comment": "Acc\u00e8s pour les secr\u00e9taires d'un SRA",
+ "available": true,
+ "txt_idx": "secretarial_dept",
+ "label": "Secr\u00e9tariat SRA"
+ }
+ },
+ {
+ "pk": 5,
+ "model": "furnitures.persontype",
+ "fields": {
+ "comment": "Cette personne peut g\u00e9rer du mobilier qu'il n'a pas cr\u00e9\u00e9\r\n\r\n",
+ "available": true,
+ "txt_idx": "warehouse_manager",
+ "label": "Gestionnaire de d\u00e9p\u00f4t"
+ }
+ },
+ {
+ "pk": 6,
+ "model": "furnitures.persontype",
+ "fields": {
+ "comment": "Responsable de l'am\u00e9nagement",
+ "available": true,
+ "txt_idx": "general_contractor",
+ "label": "Am\u00e9nageur"
+ }
+ },
+ {
+ "pk": 7,
+ "model": "furnitures.persontype",
+ "fields": {
+ "comment": "Un acc\u00e8s limit\u00e9 \u00e0 la base, uniquement en lecture. Apr\u00e8s enregistrement.",
+ "available": true,
+ "txt_idx": "public_access",
+ "label": "Acc\u00e8s publique"
+ }
+ }
+]
diff --git a/ishtar/furnitures/fixtures/treatment_type-fr.json b/ishtar/furnitures/fixtures/treatment_type-fr.json
new file mode 100644
index 000000000..f39f72059
--- /dev/null
+++ b/ishtar/furnitures/fixtures/treatment_type-fr.json
@@ -0,0 +1,123 @@
+[
+ {
+ "pk": 1,
+ "model": "furnitures.treatmenttype",
+ "fields": {
+ "comment": "Le fait de mettre du mobilier dans un contenant. Que cela soit le conditionnement initial ou un re-conditionnement. ",
+ "available": true,
+ "txt_idx": "packaging",
+ "virtual": false,
+ "label": "Conditionnement"
+ }
+ },
+ {
+ "pk": 2,
+ "model": "furnitures.treatmenttype",
+ "fields": {
+ "comment": "Un lot mobilier ou un objet isol\u00e9 subit une radiographie (rayon X) qui produit un ou des films radio.",
+ "available": true,
+ "txt_idx": "regular_x_ray",
+ "virtual": false,
+ "label": "Radiographie argentique"
+ }
+ },
+ {
+ "pk": 3,
+ "model": "furnitures.treatmenttype",
+ "fields": {
+ "comment": "Un lot mobilier ou un objet isol\u00e9 subit une radiographie (rayon X) qui produit un ou des fichiers num\u00e9riques.",
+ "available": true,
+ "txt_idx": "digital_x_ray",
+ "virtual": false,
+ "label": "Radiographie num\u00e9rique"
+ }
+ },
+ {
+ "pk": 4,
+ "model": "furnitures.treatmenttype",
+ "fields": {
+ "comment": "D\u00e9placement de mobilier, entre deux d\u00e9p\u00f4ts : le mobilier ne peut pas \u00eatre stocker ailleurs que dans un lieu consid\u00e9r\u00e9 comme un d\u00e9p\u00f4t.",
+ "available": true,
+ "txt_idx": "moving",
+ "virtual": false,
+ "label": "D\u00e9placement"
+ }
+ },
+ {
+ "pk": 5,
+ "model": "furnitures.treatmenttype",
+ "fields": {
+ "comment": "Regroupement d'un ensemble de mobilier. Exemple : ensemble des outils provenant d'une fouille, Mobilier datant d'un site, tessonier virtuel, etc.",
+ "available": true,
+ "txt_idx": "virtual_group",
+ "virtual": true,
+ "label": "Groupement virtuel"
+ }
+ },
+ {
+ "pk": 7,
+ "model": "furnitures.treatmenttype",
+ "fields": {
+ "comment": "Division d'un lot de mobilier en plusieurs lots",
+ "available": true,
+ "txt_idx": "split",
+ "virtual": false,
+ "label": "Division"
+ }
+ },
+ {
+ "pk": 6,
+ "model": "furnitures.treatmenttype",
+ "fields": {
+ "comment": "R\u00e9union de plusieurs objets ou lots mobiliers en un seul. Ce type de traitement peut impliquer ou non un reconditionnement.\r\n\r\nExemple : Remontage d'une c\u00e9ramique \u00e0 partir de tessons d\u00e9j\u00e0 pr\u00e9sents dans un contenant (pas de reconditionnement), regroupement d'une partie de la faune (os) d'une op\u00e9ration et cr\u00e9ation d'une nouvelle caisse dans ce but (reconditionnement \u00e0 faire)",
+ "available": true,
+ "txt_idx": "physical_grouping",
+ "virtual": false,
+ "label": "Groupement"
+ }
+ },
+ {
+ "pk": 8,
+ "model": "furnitures.treatmenttype",
+ "fields": {
+ "comment": "Prise de vue \u00e0 l'aide d'un appareil photo num\u00e9rique",
+ "available": true,
+ "txt_idx": "digital_photography",
+ "virtual": false,
+ "label": "Photographie num\u00e9rique"
+ }
+ },
+ {
+ "pk": 9,
+ "model": "furnitures.treatmenttype",
+ "fields": {
+ "comment": "Photographie produisant un film (positif ou n\u00e9gatif)",
+ "available": true,
+ "txt_idx": "regular_photography",
+ "virtual": false,
+ "label": "Photographie argentique"
+ }
+ },
+ {
+ "pk": 10,
+ "model": "furnitures.treatmenttype",
+ "fields": {
+ "comment": "Processus qui permet \u00e9liminer le sel qui impr\u00e8gne un objet arch\u00e9ologique ou lot d'objets.",
+ "available": true,
+ "txt_idx": "desalinisation",
+ "virtual": false,
+ "label": "D\u00e9salinisation"
+ }
+ },
+ {
+ "pk": 11,
+ "model": "furnitures.treatmenttype",
+ "fields": {
+ "comment": "R\u00e9duction des oxydes d\u00e9velopp\u00e9s sur/dans un objet arch\u00e9ologique par l'usage de courant \u00e9lectrique.",
+ "available": true,
+ "txt_idx": "electrolysis",
+ "virtual": false,
+ "label": "Electrolyse"
+ }
+ }
+]
diff --git a/ishtar/furnitures/models.py b/ishtar/furnitures/models.py
index 144ada31a..3571256cf 100644
--- a/ishtar/furnitures/models.py
+++ b/ishtar/furnitures/models.py
@@ -157,19 +157,53 @@ class GeneralType(models.Model):
for child in cls._get_childs(item, dct):
yield child
+class HistoryError(Exception):
+ def __init__(self, value):
+ self.value = value
+ def __str__(self):
+ return repr(self.value)
+
class BaseHistorizedItem(models.Model):
history_modifier = models.ForeignKey(User, related_name='+',
verbose_name=_(u"Last modifier"))
+ class Meta:
+ abstract = True
+
def save(self, *args, **kwargs):
- try:
- self.history_modifier
- except ObjectDoesNotExist:
- return
+ assert hasattr(self, 'history_modifier') == True
super(BaseHistorizedItem, self).save(*args, **kwargs)
return True
- class Meta:
- abstract = True
+ def rollback(self, date):
+ to_del, new_item = [], None
+ for item in self.history.all():
+ to_del.append(item)
+ if item.history_date == date:
+ new_item = item
+ break
+ if not new_item:
+ raise HistoryError(u"The date to rollback to doesn't exist.")
+ try:
+ for f in self._meta.fields:
+ k = f.name
+ if k != 'id' and hasattr(self, k):
+ if not hasattr(new_item, k):
+ k = k + "_id"
+ setattr(self, k, getattr(new_item, k))
+ self.save()
+ except:
+ raise HistoryError(u"The rollback has failed.")
+ # clean the non necessary steps
+ for historized_item in to_del:
+ historized_item.delete()
+
+ def values(self):
+ values = {}
+ for f in self._meta.fields:
+ k = f.name
+ if k != 'id':
+ values[k] = getattr(self, k)
+ return values
class LightHistorizedItem(BaseHistorizedItem):
history_date = models.DateTimeField(default=datetime.datetime.now)
@@ -266,7 +300,7 @@ class Person(Address, OwnPerms) :
lbl = u"%s %s - " % (self.name, self.surname)
if self.attached_to:
lbl += unicode(self.attached_to)
- else:
+ elif self.email:
lbl += self.email
return lbl
@@ -331,7 +365,7 @@ class File(BaseHistorizedItem, OwnPerms):
is_active = models.BooleanField(_(u"Is active?"), default=True)
towns = models.ManyToManyField("Town", verbose_name=_(u"Towns"))
creation_date = models.DateField(_(u"Creation date"),
- default=datetime.datetime.now)
+ default=datetime.date.today)
reception_date = models.DateField(_(u'Reception date'), blank=True,
null=True)
related_file = models.ForeignKey("File", verbose_name=_(u"Related file"),
diff --git a/ishtar/furnitures/tests.py b/ishtar/furnitures/tests.py
index 67756cd00..43bbb200a 100644
--- a/ishtar/furnitures/tests.py
+++ b/ishtar/furnitures/tests.py
@@ -20,13 +20,15 @@
"""
Unit tests
"""
+import json
from django.test import TestCase
import models
class FileTest(TestCase):
- fixtures = ['user.json']
+ fixtures = ['user.json', 'person_type-fr.json', 'organization_type-fr.json',
+ 'treatment_type-fr.json']
model = models.File
def setUp(self):
@@ -63,7 +65,7 @@ class FileTest(TestCase):
def testAddAndGetHistorized(self):
"""
- Tests that 1 + 1 always equals 2.
+ Test correct new version and correct access to history
"""
nb_hist = self.item.history.count()
self.assertTrue(self.item.history.count() >= 1)
@@ -74,5 +76,24 @@ class FileTest(TestCase):
self.failUnlessEqual(self.item.history.all()[1].internal_reference,
base_label)
-
+ def testRollbackFile(self):
+ nb_hist = self.item.history.count()
+ initial_values = self.item.values()
+ self.item.internal_reference = u"Unité_Test"
+ backup_date = self.item.history.all()[0].history_date
+ self.item.save()
+ self.item.rollback(backup_date)
+ self.failUnlessEqual(self.item.history.count(), nb_hist)
+ new_values = self.item.values()
+ for k in initial_values.keys():
+ self.assertTrue(k in new_values)
+ self.assertEqual(new_values[k], initial_values[k])
+
+ def testGetFile(self):
+ response = self.client.post('/get-file/',
+ {'numeric_reference':1000})
+ self.assertEqual(response.status_code, 200)
+ data = json.loads(response.content)
+ self.assertTrue('records' in data)
+ self.assertTrue(data['records'] >= 1)
diff --git a/ishtar/furnitures/views.py b/ishtar/furnitures/views.py
index 128d8979c..4ef1f81ea 100644
--- a/ishtar/furnitures/views.py
+++ b/ishtar/furnitures/views.py
@@ -197,6 +197,7 @@ def get_item(model, func_name, default_name, extra_request_keys=[],
query = Q(**dct)
items = model.objects.filter(query)
q = request.GET.get('sidx')
+ # manage tables
if q and q in request_keys:
k = request_keys[q]
if k.endswith("__pk"):