diff options
28 files changed, 991 insertions, 194 deletions
| diff --git a/archaeological_files/tests.py b/archaeological_files/tests.py index f24a29582..84f8269c7 100644 --- a/archaeological_files/tests.py +++ b/archaeological_files/tests.py @@ -20,7 +20,8 @@  """  Unit tests  """ -import json, datetime +import datetime +import json  from django.conf import settings  from django.contrib.auth.models import User @@ -31,6 +32,7 @@ from archaeological_files import models  from archaeological_operations.models import Parcel, ParcelOwner  from archaeological_operations.tests import OperationInitTest +  class FileInit(object):      def login_as_superuser(self):          self.client.login(username='username', password='tralala') @@ -60,16 +62,17 @@ class FileInit(object):          self.extra_models['file_type'] = file_type          self.model_list.append(file_type) -        dct = {'year':2010, 'numeric_reference':1000, 'file_type':file_type, -               'internal_reference':u'UNIT_testÉ ?', 'in_charge':person, -               'history_modifier':self.o_user, 'total_surface':10000} +        dct = {'year': 2010, 'numeric_reference': 1000, 'file_type': file_type, +               'internal_reference': u'UNIT_testÉ ?', 'in_charge': person, +               'history_modifier': self.o_user, 'total_surface': 10000}          self.item = self.model(**dct)          self.item.save() +  class FileTest(TestCase, FileInit): -    fixtures = [settings.ROOT_PATH + \ +    fixtures = [settings.ROOT_PATH +                  '../fixtures/initial_data-auth-fr.json', -                settings.ROOT_PATH + \ +                settings.ROOT_PATH +                  '../ishtar_common/fixtures/initial_data-fr.json']      model = models.File @@ -86,7 +89,7 @@ class FileTest(TestCase, FileInit):          self.item.internal_reference = u"Unité_Test"          self.item.history_modifier = self.user          self.item.save() -        self.failUnlessEqual(self.item.history.count(), nb_hist+1) +        self.failUnlessEqual(self.item.history.count(), nb_hist + 1)          self.failUnlessEqual(self.item.history.all()[1].internal_reference,                               base_label)          self.item.internal_reference = u"Unité_Testée" @@ -94,7 +97,7 @@ class FileTest(TestCase, FileInit):          self.item.skip_history_when_saving = True          self.item.save()          self.item.skip_history_when_saving = False -        self.failUnlessEqual(self.item.history.count(), nb_hist+1) +        self.failUnlessEqual(self.item.history.count(), nb_hist + 1)      def testCreatorHistorized(self):          """ @@ -116,7 +119,7 @@ class FileTest(TestCase, FileInit):          self.item.internal_reference = u"Unité_Test"          self.item.history_modifier = self.user          self.item.save() -        self.failUnlessEqual(self.item.history.count(), nb_hist+1) +        self.failUnlessEqual(self.item.history.count(), nb_hist + 1)          nb_hist = self.item.history.count()          self.item.save()          self.failUnlessEqual(self.item.history.count(), nb_hist) @@ -133,21 +136,22 @@ class FileTest(TestCase, FileInit):          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], -                        msg=u"for %s: %s != %s" % (k, unicode(new_values[k]), -                                                   unicode(initial_values[k]))) +            self.assertEqual( +                new_values[k], initial_values[k], +                msg=u"for %s: %s != %s" % (k, unicode(new_values[k]), +                                           unicode(initial_values[k])))      def testRESTGetFile(self): -        response = self.client.post('/get-file/', -                             {'numeric_reference':self.item.numeric_reference}) +        response = self.client.post( +            '/get-file/', {'numeric_reference': self.item.numeric_reference})          self.assertEqual(response.status_code, 200)          data = json.loads(response.content)          # not allowed -> no data          self.assertTrue(not data)          self.login_as_superuser() -        response = self.client.post('/get-file/', -                             {'numeric_reference':self.item.numeric_reference}) +        response = self.client.post( +            '/get-file/', {'numeric_reference': self.item.numeric_reference})          self.assertEqual(response.status_code, 200)          data = json.loads(response.content)          self.assertTrue('records' in data) @@ -160,33 +164,36 @@ class FileTest(TestCase, FileInit):          self.item.internal_reference = new_ref          self.item.history_modifier = self.user          self.item.save() -        response = self.client.post('/get-file/', -                     {'numeric_reference':self.item.numeric_reference, 'old':1}) +        response = self.client.post( +            '/get-file/', +            {'numeric_reference': self.item.numeric_reference, 'old': 1})          self.assertEqual(response.status_code, 200)          data = json.loads(response.content)          # not allowed -> no data          self.assertTrue(not data)          self.login_as_superuser() -        response = self.client.post('/get-file/', -                     {'numeric_reference':self.item.numeric_reference, 'old':1}) +        response = self.client.post( +            '/get-file/', +            {'numeric_reference': self.item.numeric_reference, 'old': 1})          self.assertEqual(response.status_code, 200)          data = json.loads(response.content)          self.assertTrue('records' in data)          self.assertTrue(data['records'] == 1)          self.assertEqual(data['rows'][0]['internal_reference'], initial_ref) -#class ImporterTest(TestCase): +# class ImporterTest(TestCase):  #    def testFormaters(self):  #        from archaeological_files import data_importer  #        for formater in [data_importer.SurfaceFormater]:  #            formater().test() +  class FileOperationTest(TestCase, OperationInitTest, FileInit):      model = models.File -    fixtures = [settings.ROOT_PATH + \ +    fixtures = [settings.ROOT_PATH +                  '../fixtures/initial_data-auth-fr.json', -                settings.ROOT_PATH + \ +                settings.ROOT_PATH +                  '../ishtar_common/fixtures/initial_data-fr.json',                  settings.ROOT_PATH +                  '../ishtar_common/fixtures/test_towns.json', @@ -207,34 +214,34 @@ class FileOperationTest(TestCase, OperationInitTest, FileInit):          # parcel association          default_town = Town.objects.all()[0]          for p in range(0, 10): -            parcel = Parcel.objects.create(parcel_number=unicode(p), -                                     section='YY', -                                     town=default_town, -                                     operation=self.operation) +            parcel = Parcel.objects.create( +                parcel_number=unicode(p), +                section='YY', +                town=default_town, +                operation=self.operation)              if p == 1:                  ParcelOwner.objects.create( -                        owner=self.extra_models['person'], -                        parcel=parcel, start_date=datetime.date.today(), -                        end_date=datetime.date.today()) +                    owner=self.extra_models['person'], +                    parcel=parcel, start_date=datetime.date.today(), +                    end_date=datetime.date.today())          initial_nb = self.item.parcels.count()          # no parcel on the file -> new parcels are copied from the          # operation          self.operation.associated_file = self.item          self.operation.save() -        self.assertEqual(self.item.parcels.count(), initial_nb+10) +        self.assertEqual(self.item.parcels.count(), initial_nb + 10)          # parcel owner well attached          q = ParcelOwner.objects.filter(parcel__associated_file=self.item)          self.assertEqual(q.count(), 1)          # when attaching parcel from a file to an operation, copy is done -        parcel = Parcel.objects.create(parcel_number='42', -                                 section='ZZ', -                                 town=default_town, -                                 associated_file=self.item) +        parcel = Parcel.objects.create( +            parcel_number='42', section='ZZ', +            town=default_town, associated_file=self.item)          ParcelOwner.objects.create( -                owner=self.extra_models['person'], -                parcel=parcel, start_date=datetime.date.today(), -                end_date=datetime.date.today()) +            owner=self.extra_models['person'], +            parcel=parcel, start_date=datetime.date.today(), +            end_date=datetime.date.today())          parcel.operation = self.operation          parcel.save()          # double reference to operation and associated_file is deleted @@ -249,5 +256,3 @@ class FileOperationTest(TestCase, OperationInitTest, FileInit):          q = ParcelOwner.objects.filter(parcel__operation=self.operation,                                         parcel__parcel_number='42')          self.assertEqual(q.count(), 1) - - diff --git a/archaeological_files_pdl/tests.py b/archaeological_files_pdl/tests.py deleted file mode 100644 index 501deb776..000000000 --- a/archaeological_files_pdl/tests.py +++ /dev/null @@ -1,16 +0,0 @@ -""" -This file demonstrates writing tests using the unittest module. These will pass -when you run "manage.py test". - -Replace this with more appropriate tests for your application. -""" - -from django.test import TestCase - - -class SimpleTest(TestCase): -    def test_basic_addition(self): -        """ -        Tests that 1 + 1 always equals 2. -        """ -        self.assertEqual(1 + 1, 2) diff --git a/archaeological_finds/forms.py b/archaeological_finds/forms.py index 99907781b..f5387febe 100644 --- a/archaeological_finds/forms.py +++ b/archaeological_finds/forms.py @@ -432,8 +432,12 @@ class SelectFindBasketForm(forms.Form):      basket = forms.ChoiceField(label=_(u"Basket"), required=True, choices=[])      def __init__(self, *args, **kwargs): -        self.user = kwargs.pop('user') +        self.user = None +        if 'user' in kwargs: +            self.user = kwargs.pop('user')          super(SelectFindBasketForm, self).__init__(*args, **kwargs) +        if not self.user: +            return          self.fields['basket'].choices = [('', '--')] + [              (b.pk, unicode(b))              for b in models.FindBasket.objects.filter(user=self.user)] diff --git a/archaeological_finds/models.py b/archaeological_finds/models.py index 417dd3929..b84b7614f 100644 --- a/archaeological_finds/models.py +++ b/archaeological_finds/models.py @@ -32,6 +32,7 @@ from ishtar_common.models import GeneralType, ImageModel, BaseHistorizedItem, \  from archaeological_operations.models import AdministrativeAct  from archaeological_context_records.models import ContextRecord, Dating +from ishtar_common.models import PRIVATE_FIELDS  from archaeological_warehouse.models import Warehouse, Container @@ -303,10 +304,10 @@ class Find(BaseHistorizedItem, ImageModel, OwnPerms, ShortMenuItem):      find_number = models.IntegerField(_("Find number"), blank=True, null=True)      upstream_treatment = models.ForeignKey(          "Treatment", blank=True, null=True, -        related_name='downstream_treatment', +        related_name='downstream',          verbose_name=_("Upstream treatment"))      downstream_treatment = models.ForeignKey( -        "Treatment", blank=True, null=True, related_name='upstream_treatment', +        "Treatment", blank=True, null=True, related_name='upstream',          verbose_name=_("Downstream treatment"))      datings = models.ManyToManyField(Dating, verbose_name=_(u"Dating"),                                       related_name='find') @@ -412,6 +413,42 @@ class Find(BaseHistorizedItem, ImageModel, OwnPerms, ShortMenuItem):              bf.context_record.operation.get_reference(),              self.index) +    def upstream_treatments(self): +        treatments = [] +        base_finds = [bf.pk for bf in self.base_finds.all()] +        if self.upstream_treatment and \ +                self.upstream_treatment.pk not in treatments: +            treatments.append((self.upstream_treatment.upstream.distinct( +                ).order_by('label').all(), self.upstream_treatment)) +            for upstream in self.upstream_treatment.upstream.all(): +                if upstream.pk != self.pk and not [ +                        bf.pk for bf in upstream.base_finds.all() +                        if bf.pk in base_finds]: +                    continue +                for items, treatment in upstream.upstream_treatments(): +                    if treatment.pk not in treatments: +                        treatments.append((treatment.upstream.order_by( +                            'label').all(), treatment)) +        return treatments + +    def downstream_treatments(self): +        treatments = [] +        base_finds = [bf.pk for bf in self.base_finds.all()] +        if self.downstream_treatment and \ +                self.downstream_treatment.pk not in treatments: +            treatments.append((self.downstream_treatment.downstream.distinct( +                 ).order_by('label').all(), self.downstream_treatment)) +            for downstream in self.downstream_treatment.downstream.all(): +                if downstream.pk != self.pk and not [ +                        bf.pk for bf in downstream.base_finds.all() +                        if bf.pk in base_finds]: +                    continue +                for items, treatment in downstream.downstream_treatments(): +                    if treatment.pk not in treatments: +                        treatments.append((treatment.downstream.order_by( +                            'label').all(), treatment)) +        return treatments +      def get_department(self):          bf = self.get_first_base_find()          if not bf: @@ -478,19 +515,24 @@ class Find(BaseHistorizedItem, ImageModel, OwnPerms, ShortMenuItem):          return q.filter(downstream_treatment__isnull=True).count()      def duplicate(self, user): -        # TODO -        raise +        model = self.__class__ +        # base fields +        table_cols = [field.name for field in model._meta.fields +                      if field.name not in PRIVATE_FIELDS or +                      field.name == 'order']          dct = dict([(attr, getattr(self, attr)) for attr in -                    ('order', 'label', 'description', -                     'volume', 'weight', 'find_number', 'dating', -                     'conservatory_state', 'preservation_to_consider', -                     'weight_unit', )]) +                    table_cols])          dct['order'] += 1          dct['history_modifier'] = user          new = self.__class__(**dct)          new.save() -        for base_find in self.base_finds.all(): -            new.base_finds.add(base_find) + +        # m2m fields +        m2m = [field.name for field in model._meta.many_to_many +               if field.name not in PRIVATE_FIELDS] +        for field in m2m: +            for val in getattr(self, field).all(): +                getattr(new, field).add(val)          return new      @classmethod @@ -606,6 +648,35 @@ class Treatment(BaseHistorizedItem, OwnPerms):              lbl += u" %s %s" % (_(u"by"), unicode(self.person))          return lbl +    def save(self, *args, **kwargs): +        items, user, extra_args_for_new = [], None, [] +        if "items" in kwargs: +            items = kwargs.pop('items') +        if "user" in kwargs: +            user = kwargs.pop('user') +        if "extra_args_for_new" in kwargs: +            extra_args_for_new = kwargs.pop('extra_args_for_new') +        is_new = self.pk is None +        super(Treatment, self).save(*args, **kwargs) +        if not is_new or not items: +            return +        basket = None +        if hasattr(items, "items"): +            basket = items +            items = basket.items.all() +        for item in items: +            new = item.duplicate(user) +            item.downstream_treatment = self +            item.save() +            new.upstream_treatment = self +            for k in extra_args_for_new: +                setattr(new, k, extra_args_for_new[k]) +            new.save() +            # update baskets +            for basket in FindBasket.objects.filter(items__pk=item.pk).all(): +                basket.items.remove(item) +                basket.items.add(new) +  class TreatmentSource(Source):      class Meta: diff --git a/archaeological_finds/templates/ishtar/sheet_find.html b/archaeological_finds/templates/ishtar/sheet_find.html index c5d3eb638..d21c85856 100644 --- a/archaeological_finds/templates/ishtar/sheet_find.html +++ b/archaeological_finds/templates/ishtar/sheet_find.html @@ -1,5 +1,5 @@  {% extends "ishtar/sheet.html" %} -{% load i18n window_field from_dict %} +{% load i18n window_field from_dict link_to_window %}  {% block head_sheet %}  {{block.super}} @@ -10,7 +10,7 @@  {% if previous or next %}  <div class='tool'> -{%if previous%} +{% if previous %}  <a href="#" onclick='load_window("{% url show-historized-find item.pk previous|date:"c"%}");$("#{{window_id}}").hide();return false;'>{%trans "Previous version"%} ({{previous}})</a>  {% endif %}  {% if previous and next %} - {% endif %} @@ -25,84 +25,116 @@  <div class='tool modify'><a href='{% url find_modify item.pk %}'>{% trans "Modify" %}</a></div>  {% if item.image %} -<a href='{{item.image.url}}' rel="prettyPhoto" title="{{item.label}}"><img src='{{item.thumbnail.url}}'/></a> +<a href='{{item.image.url}}' rel="prettyPhoto" title="{{item.label}}" class='photo'><img src='{{item.thumbnail.url}}'/></a>  {% endif%} -{% field "Free-ID" item.label %} -{% field "Previous ID" item.previous_id %} -{% field "Description" item.description %} -{% field "Created by" item.history_creator.ishtaruser.full_label %} -{% field "Administrative index" item.administrative_index %} -{% field_multiple "Material types" item.material_types %} -{% field "Dating" item.dating %} -{% field "Length (cm)" item.length %} -{% field "Width (cm)" item.width %} -{% field "Height (cm)" item.height %} -{% field "Diameter (cm)" item.diameter %} -{% field "Volume (l)" item.volume %} +<ul class='form-flex'> +{% field_li "Free-ID" item.label %} +{% field_li "Previous ID" item.previous_id %} +{% field_li "Description" item.description %} +{% field_li "Created by" item.history_creator.ishtaruser.full_label %} +{% field_li "Administrative index" item.administrative_index %} +{% field_li_multiple "Material types" item.material_types %} +{% field_li "Dating" item.dating %} +{% field_li "Length (cm)" item.length %} +{% field_li "Width (cm)" item.width %} +{% field_li "Height (cm)" item.height %} +{% field_li "Diameter (cm)" item.diameter %} +{% field_li "Volume (l)" item.volume %}  {% if item.weight %}{% with item.weight|add:' '|add:item.weight_unit as weight %} -{% field "Weight" weight %} +{% field_li "Weight" weight %}  {% endwith %}{% endif %} -{% field "Weight" item.weight %} +{% field_li "Weight" item.weight %} -{% field "Find number" item.find_number %} -{% field "Conservatory state" item.conservatory_state %} -{% field_multiple "Type of preservation to consider" item.preservation_to_considers %} -{% field_multiple "Object types" item.object_types %} -{% field_multiple "Integrity" item.integrities %} +{% field_li "Find number" item.find_number %} +{% field_li "Conservatory state" item.conservatory_state %} +{% field_li_multiple "Type of preservation to consider" item.preservation_to_considers %} +{% field_li_multiple "Object types" item.object_types %} +{% field_li_multiple "Integrity" item.integrities %}  {% if item.CHECK_DICT %} -{% field "Checked" item.checked|from_dict:item.CHECK_DICT %} +{% field_li "Checked" item.checked|from_dict:item.CHECK_DICT %}  {% endif%}  {% if item.history_object and item.history_object.CHECK_DICT %} -{% field "Checked" item.checked|from_dict:item.history_object.CHECK_DICT %} +{% field_li "Checked" item.checked|from_dict:item.history_object.CHECK_DICT %}  {% endif%} +{% field_li "Container" item.container %} +</ul> -{% if item.upstream_treatment %}<p><label>{%trans "Upstream treatment"%}{% trans ":"%}</label> -<span class='value'>{{item.upstream_treatment}} ({% for up in item.upstream_treatment.upstream_treatment.all %}{% if forloop.counter0 %}, {%endif %}{{up}}{% endfor %})</span></p>{% endif%} -{% if item.downstream_treatment %}<p><label>{%trans "Downstream treatment"%}{% trans ":"%}</label> -<span class='value'>{{item.downstream_treatment}} ({% for dt in item.downstream_treatment.downstream_treatment.all %}{% if forloop.counter0 %}, {%endif %}{{dt}}{% endfor %})</span></p>{% endif%} -{% if item.container %}<p><label>{%trans "Container"%}{% trans ":"%}</label> -<span class='value'>{{item.container}}</span></p>{% endif%} +{% if item.upstream_treatment %} +<table class='simple' id='{{window_id}}-upstream'> +  <caption>{% trans "Upstream treatment" %}</caption> +  <tr> +    <th>{% trans "Type" %}</th> +    <th>{% trans "Related find" %}</th> +    <th>{% trans "Person" %}</th> +    <th>{% trans "Container" %}</th> +    <th>{% trans "Start date" %}</th> +    <th>{% trans "End date" %}</th> +  </tr> +  {% for items, treatment in item.upstream_treatments %} +  <tr> +    <td class='string'>{{ treatment.treatment_type }}</td> +    <td class='item-list'>{% for item in items %}<span>{{item}} {{ item|link_to_window}}</span>{% endfor %}</td> +    <td class='string'>{{ treatment.person|default_if_none:"-" }}</td> +    <td class='string'>{{ treatment.container|default_if_none:"-" }}</td> +    <td class='string'>{{ treatment.start_date|default_if_none:"-" }}</td> +    <td class='string'>{{ treatment.end_date|default_if_none:"-" }}</td> +  </tr> +  {% endfor %} +</table> +{% endif %} + +{% if item.downstream_treatment %} +<table class='simple' id='{{window_id}}-downstream'> +  <caption>{% trans "Downstream treatment" %}</caption> +  <tr> +    <th>{% trans "Type" %}</th> +    <th>{% trans "Related find" %}</th> +    <th>{% trans "Person" %}</th> +    <th>{% trans "Start date" %}</th> +    <th>{% trans "End date" %}</th> +  </tr> +  {% for items, treatment in item.downstream_treatments %} +  <tr> +    <td class='string'>{{ treatment.treatment_type }}</td> +    <td class='item-list'>{% for item in items %}<span>{{item}} {{ item|link_to_window}}</span>{% endfor %}</td> +    <td class='string'>{{ treatment.person|default_if_none:"" }}</td> +    <td class='string'>{{ treatment.start_date|default_if_none:"" }}</td> +    <td class='string'>{{ treatment.end_date|default_if_none:"" }}</td> +  </tr> +  {% endfor %} +</table> +{% endif %}  <h3>{% trans "Associated base finds"%}</h3>  {% for base_find in item.base_finds.all %} -<p><label>{%trans "Complete ID"%}{% trans ":"%}</label> -<span class='value'>{{base_find.complete_id}}</span></p> - -{% field "Short ID" base_find.short_id %} +<ul class='form-flex'> +{% field_li "Complete ID" base_find.complete_id %} +{% field_li "Short ID" base_find.short_id %}  {% if base_find.IS_ISOLATED_DICT %} -{% field "Batch/object" base_find.batch|from_dict:base_find.IS_ISOLATED_DICT %} +{% field_li "Batch/object" base_find.batch|from_dict:base_find.IS_ISOLATED_DICT %}  {% endif %}  {% if base_find.history_object and base_find.history_object.IS_ISOLATED_DICT %} -{% field "Batch/object" base_find.batch|from_dict:base_find.history_object.IS_ISOLATED_DICT %} +{% field_li "Batch/object" base_find.batch|from_dict:base_find.history_object.IS_ISOLATED_DICT %}  {% endif %} -{% if base_find.discovery_date %} -<p><label>{%trans "Discovery date"%}{% trans ":"%}</label> -<span class='value'>{{base_find.discovery_date}}</span></p> -{% endif%} -{% if base_find.description %} -<p><label>{%trans "Description"%}{% trans ":"%}</label> -<span class='value'>{{base_find.description}}</span></p> -{% endif%} -{% field "Comment" base_find.comment "<pre>" "</pre>" %} -{% if base_find.special_interest %} -<p><label>{%trans "Special interest"%}{% trans ":"%}</label> -<span class='value'>{{base_find.special_interest}}</span></p> -{% endif%} +{% field_li "Discovery date" base_find.discovery_date %} +{% field_li "Special interest" base_find.special_interest %} +{% field_li_detail "Context record" base_find.context_record %} +{% if base_find.context_record %}{% field_li "Parcel" base_find.context_record.parcel %} +{% field_li_detail "Operation" base_find.context_record.operation %}{% endif %} +</ul> -<p><label>{%trans "Related context record"%}{% trans ":"%}</label> -<span class='value'><a href="#" onclick='load_window("{% url show-contextrecord base_find.context_record.pk ''%}");'>{{ base_find.context_record }}</a></span></p> -<p><label>{%trans "Related parcel"%}{% trans ":"%}</label> -<span class='value'>{{base_find.context_record.parcel}}</span></p> -<p><label>{%trans "Related operation"%}{% trans ":"%}</label> -<span class='value'><a href="#" onclick='load_window("{% url show-operation base_find.context_record.operation.pk ''%}");'>{{ base_find.context_record.operation }}</a></span></p> +{% field "Description" base_find.description "<pre>" "</pre>" %} +{% field "Comment" base_find.comment "<pre>" "</pre>" %}  {% if forloop.counter0 %}<hr/>{% endif %}  {% endfor %} -<table class='simple'> +{% if item.source.count %} +<h3>{% trans "Documents"%}</h3> +<table id='{{window_id}}-docs'>    <caption>{%trans "Documents"%}</caption>    <tr>      <th>{% trans "Title" %}</th> @@ -115,11 +147,16 @@      <td class='string'>{{ doc.title }}</td>      <td class='string'>{{doc.source_type}}</td>      <td class='string'>{{ doc.authors.all|join:", " }}</td> -    <td class='string'>{% if doc.associated_url  %}<a href='{{doc.associated_url}}'>{% trans "Link"%}</a>{% endif %}</td> +    <td class='string'>{% if doc.associated_url  %}<a href='{{doc.associated_url}}' target="_blank">{% trans "Link"%}</a>{% endif %}</td>    </tr>    {% empty %} -  <tr><td colspan="4" class='no_items'>{% trans "No document associated to this find" %}</td></tr>    {% endfor %}  </table> +<script type='text/javascript'> +tableToGrid('#{{window_id}}-docs', { +    width: null, shrinkToFit: false}); +</script> +{% endif %} +  {% endblock %} diff --git a/archaeological_finds/tests.py b/archaeological_finds/tests.py index ccf821757..f75e9cfa3 100644 --- a/archaeological_finds/tests.py +++ b/archaeological_finds/tests.py @@ -19,11 +19,13 @@  from django.conf import settings  from django.core.files.uploadedfile import SimpleUploadedFile -from ishtar_common.models import ImporterType +from django.test import TestCase +from ishtar_common.models import ImporterType, IshtarUser  from archaeological_finds import models -from archaeological_context_records.tests import ImportContextRecordTest +from archaeological_context_records.tests import ImportContextRecordTest, \ +    ContextRecordInit  from ishtar_common import forms_common @@ -67,3 +69,78 @@ class ImportFindTest(ImportContextRecordTest):          self.assertTrue(current_nb == (old_nb + 4))          self.assertEqual(              models.Find.objects.filter(material_types__pk=ceram).count(), 4) + + +class FindInit(ContextRecordInit): +    test_context_records = False + +    def create_finds(self, user=None, data_base={}, data={}): +        if not getattr(self, 'finds', None): +            self.finds = [] +        if not getattr(self, 'base_finds', None): +            self.base_finds = [] + +        default = {'label': "Base find"} +        if not data_base.get('history_modifier'): +            data_base['history_modifier'] = self.get_default_user() +        if not data_base.get('context_record'): +            data_base['context_record'] = self.get_default_context_record() + +        default.update(data_base) +        base_find = models.BaseFind.objects.create(**default) +        self.base_finds.append(base_find) + +        data["history_modifier"] = data_base["history_modifier"] +        find = models.Find.objects.create(**data) +        find.base_finds.add(base_find) +        self.finds.append(find) +        return self.finds, self.base_finds + +    def get_default_find(self): +        return self.create_finds()[0] + + +class PackagingTest(TestCase, FindInit): +    fixtures = [settings.ROOT_PATH + +                '../fixtures/initial_data.json', +                settings.ROOT_PATH + +                '../ishtar_common/fixtures/initial_data.json', +                settings.ROOT_PATH + +                '../archaeological_files/fixtures/initial_data.json', +                settings.ROOT_PATH + +                '../archaeological_operations/fixtures/initial_data-fr.json', +                settings.ROOT_PATH + +                '../archaeological_finds/fixtures/initial_data-fr.json', +                ] +    model = models.Find + +    def setUp(self): +        self.create_finds({"label": u"Find 1"}) +        self.create_finds({"label": u"Find 2"}) +        self.basket = models.FindBasket.objects.create( +            label="My basket", user=IshtarUser.objects.get( +                pk=self.get_default_user().pk)) +        self.other_basket = models.FindBasket.objects.create( +            label="My other basket", user=IshtarUser.objects.get( +                pk=self.get_default_user().pk)) +        for find in self.finds: +            self.basket.items.add(find) +            self.other_basket.items.add(find) + +    def testPackaging(self): +        treatment_type = models.TreatmentType.objects.get(txt_idx='packaging') +        treatment = models.Treatment(treatment_type=treatment_type) +        items_nb = models.Find.objects.count() +        treatment.save(user=self.get_default_user(), items=self.basket) +        self.assertEqual(items_nb + self.basket.items.count(), +                         models.Find.objects.count(), +                         msg="Packaging doesn't generate enough new finds") +        # new version of the find is in the basket +        for item in self.basket.items.all(): +            self.assertNotIn( +                item, self.finds, +                msg="Original basket have not been upgraded after packaging") +        for item in self.other_basket.items.all(): +            self.assertNotIn( +                item, self.finds, +                msg="Other basket have not been upgraded after packaging") diff --git a/archaeological_operations/models.py b/archaeological_operations/models.py index e3453f0cf..6ae05ca30 100644 --- a/archaeological_operations/models.py +++ b/archaeological_operations/models.py @@ -359,8 +359,6 @@ class Operation(BaseHistorizedItem, OwnPerms, ValueGetter, ShortMenuItem,      @classmethod      def get_available_operation_code(cls, year=None): -        if not year: -            year = datetime.date.today().year          max_val = cls.objects.filter(year=year).aggregate(              Max('operation_code'))["operation_code__max"]          return (max_val + 1) if max_val else 1 diff --git a/archaeological_operations/tests.py b/archaeological_operations/tests.py index 578382ccd..c22cf4b37 100644 --- a/archaeological_operations/tests.py +++ b/archaeological_operations/tests.py @@ -441,9 +441,10 @@ def create_orga(user):      return orga -def create_operation(user, orga=None): +def create_operation(user, orga=None, values={}):      dct = {'year': 2010, 'operation_type_id': 1,             'history_modifier': user} +    dct.update(values)      if orga:          dct['operator'] = orga      operation = models.Operation.objects.create(**dct) @@ -578,6 +579,16 @@ class OperationTest(TestCase, OperationInitTest):                           {'operator': self.orgas[0].pk})          self.assertTrue(json.loads(response.content)['total'] == 1) +    def testIndex(self): +        ope = create_operation(self.user, values={'year': 2042}) +        self.assertEqual(ope.operation_code, 1) +        ope2 = create_operation(self.user, values={'year': 2042}) +        self.assertEqual(ope2.operation_code, 2) +        ope = create_operation(self.user, values={'year': 0}) +        self.assertEqual(ope.operation_code, 1) +        ope2 = create_operation(self.user, values={'year': 0}) +        self.assertEqual(ope2.operation_code, 2) +  def create_administrativact(user, operation):      act_type, created = models.ActType.objects.get_or_create( diff --git a/archaeological_warehouse/forms.py b/archaeological_warehouse/forms.py index f045fac1e..d56df8139 100644 --- a/archaeological_warehouse/forms.py +++ b/archaeological_warehouse/forms.py @@ -1,6 +1,6 @@  #!/usr/bin/env python  # -*- coding: utf-8 -*- -# Copyright (C) 2010-2013  Étienne Loks  <etienne.loks_AT_peacefrogsDOTnet> +# Copyright (C) 2010-2016  É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 @@ -22,12 +22,13 @@ from django.conf import settings  from django.utils.translation import ugettext_lazy as _  from ishtar_common.models import Person, valid_id -from archaeological_finds.models import TreatmentType +from archaeological_finds.models import TreatmentType, FindBasket  import models  from ishtar_common import widgets  from ishtar_common.forms import name_validator, reverse_lazy, \      get_form_selection, TableSelect -from archaeological_finds.forms import FindMultipleFormSelection +from archaeological_finds.forms import FindMultipleFormSelection, \ +    SelectFindBasketForm  def get_warehouse_field(label=_(u"Warehouse"), required=True): @@ -66,6 +67,8 @@ class WarehouseForm(forms.Form):                                     required=False)      def __init__(self, *args, **kwargs): +        if 'limits' in kwargs: +            kwargs.pop('limits')          super(WarehouseForm, self).__init__(*args, **kwargs)          self.fields['warehouse_type'].choices = \              models.WarehouseType.get_types() @@ -99,6 +102,8 @@ class ContainerForm(forms.Form):                                widget=forms.Textarea, required=False)      def __init__(self, *args, **kwargs): +        if 'limits' in kwargs: +            kwargs.pop('limits')          super(ContainerForm, self).__init__(*args, **kwargs)          self.fields['container_type'].choices = \              models.ContainerType.get_types() @@ -135,11 +140,12 @@ ContainerFormSelection = get_form_selection(      new_message=_(u"Add a new container")) -class BasePackagingForm(forms.Form): +class BasePackagingForm(SelectFindBasketForm):      form_label = _(u"Packaging")      associated_models = {'treatment_type': TreatmentType,                           'person': Person, -                         'location': models.Warehouse} +                         'location': models.Warehouse, +                         'basket': FindBasket}      treatment_type = forms.IntegerField(label="", widget=forms.HiddenInput)      person = forms.IntegerField(          label=_(u"Packager"), diff --git a/archaeological_warehouse/views.py b/archaeological_warehouse/views.py index aab18ed40..be5e6150c 100644 --- a/archaeological_warehouse/views.py +++ b/archaeological_warehouse/views.py @@ -89,7 +89,7 @@ def autocomplete_container(request):  warehouse_packaging_wizard = PackagingWizard.as_view([      ('seleccontainer-packaging', ContainerFormSelection),      ('base-packaging', BasePackagingForm), -    ('multiselecitems-packaging', FindPackagingFormSelection), +    # ('multiselecitems-packaging', FindPackagingFormSelection),      ('final-packaging', FinalForm)],      label=_(u"Packaging"),      url_name='warehouse_packaging',) diff --git a/archaeological_warehouse/wizards.py b/archaeological_warehouse/wizards.py index 833c1c697..409f7b28f 100644 --- a/archaeological_warehouse/wizards.py +++ b/archaeological_warehouse/wizards.py @@ -1,6 +1,6 @@  #!/usr/bin/env python  # -*- coding: utf-8 -*- -# Copyright (C) 2012  Étienne Loks  <etienne.loks_AT_peacefrogsDOTnet> +# Copyright (C) 2012-2016  É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 @@ -25,23 +25,24 @@ from archaeological_finds.models import Treatment  class PackagingWizard(TreatmentWizard): + +    def get_form_kwargs(self, step): +        kwargs = super(PackagingWizard, self).get_form_kwargs(step) +        if 'base-packaging' not in step: +            return kwargs +        kwargs['user'] = self.request.user +        return kwargs +      def save_model(self, dct, m2m, whole_associated_models, form_list,                     return_object):          dct = self.get_extra_model(dct, form_list)          obj = self.get_current_saved_object()          dct['location'] = dct['container'].location -        items = dct.pop('finds') +        items = dct.pop('basket')          treatment = Treatment(**dct) -        treatment.save() -        if not hasattr(items, '__iter__'): -            items = [items] -        for item in items: -            new = item.duplicate(self.request.user) -            item.downstream_treatment = treatment -            item.save() -            new.upstream_treatment = treatment -            new.container = dct['container'] -            new.save() +        extra_args_for_new = {"container": dct['container']} +        treatment.save(items=items, user=self.request.user, +                       extra_args_for_new=extra_args_for_new)          res = render_to_response('ishtar/wizard/wizard_done.html', {},                                   context_instance=RequestContext(self.request))          return return_object and (obj, res) or res diff --git a/ishtar_common/migrations/0051_auto__add_field_ishtarsiteprofile_homepage.py b/ishtar_common/migrations/0051_auto__add_field_ishtarsiteprofile_homepage.py new file mode 100644 index 000000000..7f8c65857 --- /dev/null +++ b/ishtar_common/migrations/0051_auto__add_field_ishtarsiteprofile_homepage.py @@ -0,0 +1,404 @@ +# -*- coding: utf-8 -*- +import datetime +from south.db import db +from south.v2 import SchemaMigration +from django.db import models + + +class Migration(SchemaMigration): + +    def forwards(self, orm): +        # Adding field 'IshtarSiteProfile.homepage' +        db.add_column('ishtar_common_ishtarsiteprofile', 'homepage', +                      self.gf('django.db.models.fields.TextField')(null=True, blank=True), +                      keep_default=False) + + +    def backwards(self, orm): +        # Deleting field 'IshtarSiteProfile.homepage' +        db.delete_column('ishtar_common_ishtarsiteprofile', 'homepage') + + +    models = { +        'auth.group': { +            'Meta': {'object_name': 'Group'}, +            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), +            'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}), +            'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}) +        }, +        'auth.permission': { +            'Meta': {'ordering': "('content_type__app_label', 'content_type__model', 'codename')", 'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'}, +            'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}), +            'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}), +            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), +            'name': ('django.db.models.fields.CharField', [], {'max_length': '50'}) +        }, +        'auth.user': { +            'Meta': {'object_name': 'User'}, +            'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), +            'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}), +            'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), +            'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}), +            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), +            'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), +            'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), +            'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), +            'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), +            'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), +            'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}), +            'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}), +            'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'}) +        }, +        'contenttypes.contenttype': { +            'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"}, +            'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}), +            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), +            'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}), +            'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}) +        }, +        'ishtar_common.arrondissement': { +            'Meta': {'object_name': 'Arrondissement'}, +            'department': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['ishtar_common.Department']"}), +            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), +            'name': ('django.db.models.fields.CharField', [], {'max_length': '30'}) +        }, +        'ishtar_common.author': { +            'Meta': {'object_name': 'Author'}, +            'author_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['ishtar_common.AuthorType']"}), +            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), +            'person': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'author'", 'to': "orm['ishtar_common.Person']"}) +        }, +        'ishtar_common.authortype': { +            'Meta': {'object_name': 'AuthorType'}, +            'available': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), +            'comment': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), +            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), +            'label': ('django.db.models.fields.CharField', [], {'max_length': '100'}), +            'txt_idx': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '100'}) +        }, +        'ishtar_common.canton': { +            'Meta': {'object_name': 'Canton'}, +            'arrondissement': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['ishtar_common.Arrondissement']"}), +            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), +            'name': ('django.db.models.fields.CharField', [], {'max_length': '30'}) +        }, +        'ishtar_common.department': { +            'Meta': {'ordering': "['number']", 'object_name': 'Department'}, +            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), +            'label': ('django.db.models.fields.CharField', [], {'max_length': '30'}), +            'number': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '3'}), +            'state': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['ishtar_common.State']", 'null': 'True', 'blank': 'True'}) +        }, +        'ishtar_common.documenttemplate': { +            'Meta': {'ordering': "['associated_object_name', 'name']", 'object_name': 'DocumentTemplate'}, +            'associated_object_name': ('django.db.models.fields.CharField', [], {'max_length': '100'}), +            'available': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), +            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), +            'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}), +            'template': ('django.db.models.fields.files.FileField', [], {'max_length': '100'}) +        }, +        'ishtar_common.format': { +            'Meta': {'object_name': 'Format'}, +            'available': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), +            'comment': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), +            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), +            'label': ('django.db.models.fields.CharField', [], {'max_length': '100'}), +            'txt_idx': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '100'}) +        }, +        'ishtar_common.formatertype': { +            'Meta': {'ordering': "('formater_type', 'options')", 'unique_together': "(('formater_type', 'options', 'many_split'),)", 'object_name': 'FormaterType'}, +            'formater_type': ('django.db.models.fields.CharField', [], {'max_length': '20'}), +            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), +            'many_split': ('django.db.models.fields.CharField', [], {'max_length': '10', 'null': 'True', 'blank': 'True'}), +            'options': ('django.db.models.fields.CharField', [], {'max_length': '500', 'null': 'True', 'blank': 'True'}) +        }, +        'ishtar_common.globalvar': { +            'Meta': {'ordering': "['slug']", 'object_name': 'GlobalVar'}, +            'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), +            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), +            'slug': ('django.db.models.fields.SlugField', [], {'unique': 'True', 'max_length': '50'}), +            'value': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}) +        }, +        'ishtar_common.historicalorganization': { +            'Meta': {'ordering': "('-history_date', '-history_id')", 'object_name': 'HistoricalOrganization'}, +            'address': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), +            'address_complement': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), +            'alt_address': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), +            'alt_address_complement': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), +            'alt_address_is_prefered': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), +            'alt_country': ('django.db.models.fields.CharField', [], {'max_length': '30', 'null': 'True', 'blank': 'True'}), +            'alt_postal_code': ('django.db.models.fields.CharField', [], {'max_length': '10', 'null': 'True', 'blank': 'True'}), +            'alt_town': ('django.db.models.fields.CharField', [], {'max_length': '70', 'null': 'True', 'blank': 'True'}), +            'country': ('django.db.models.fields.CharField', [], {'max_length': '30', 'null': 'True', 'blank': 'True'}), +            'email': ('django.db.models.fields.EmailField', [], {'max_length': '300', 'null': 'True', 'blank': 'True'}), +            'exclude_from_merge': ('django.db.models.fields.NullBooleanField', [], {'default': 'False', 'null': 'True', 'blank': 'True'}), +            'history_creator_id': ('django.db.models.fields.IntegerField', [], {'db_index': 'True', 'null': 'True', 'blank': 'True'}), +            'history_date': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), +            'history_id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), +            'history_modifier_id': ('django.db.models.fields.IntegerField', [], {'db_index': 'True', 'null': 'True', 'blank': 'True'}), +            'history_type': ('django.db.models.fields.CharField', [], {'max_length': '1'}), +            'history_user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']", 'null': 'True'}), +            'id': ('django.db.models.fields.IntegerField', [], {'db_index': 'True', 'blank': 'True'}), +            'merge_key': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), +            'mobile_phone': ('django.db.models.fields.CharField', [], {'max_length': '18', 'null': 'True', 'blank': 'True'}), +            'name': ('django.db.models.fields.CharField', [], {'max_length': '500'}), +            'organization_type_id': ('django.db.models.fields.IntegerField', [], {'db_index': 'True', 'null': 'True', 'blank': 'True'}), +            'phone': ('django.db.models.fields.CharField', [], {'max_length': '18', 'null': 'True', 'blank': 'True'}), +            'phone2': ('django.db.models.fields.CharField', [], {'max_length': '18', 'null': 'True', 'blank': 'True'}), +            'phone3': ('django.db.models.fields.CharField', [], {'max_length': '18', 'null': 'True', 'blank': 'True'}), +            'phone_desc': ('django.db.models.fields.CharField', [], {'max_length': '300', 'null': 'True', 'blank': 'True'}), +            'phone_desc2': ('django.db.models.fields.CharField', [], {'max_length': '300', 'null': 'True', 'blank': 'True'}), +            'phone_desc3': ('django.db.models.fields.CharField', [], {'max_length': '300', 'null': 'True', 'blank': 'True'}), +            'postal_code': ('django.db.models.fields.CharField', [], {'max_length': '10', 'null': 'True', 'blank': 'True'}), +            'raw_phone': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), +            'town': ('django.db.models.fields.CharField', [], {'max_length': '70', 'null': 'True', 'blank': 'True'}) +        }, +        'ishtar_common.import': { +            'Meta': {'object_name': 'Import'}, +            'conservative_import': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), +            'creation_date': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'null': 'True', 'blank': 'True'}), +            'encoding': ('django.db.models.fields.CharField', [], {'default': "'utf-8'", 'max_length': '15'}), +            'end_date': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}), +            'error_file': ('django.db.models.fields.files.FileField', [], {'max_length': '100', 'null': 'True', 'blank': 'True'}), +            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), +            'imported_file': ('django.db.models.fields.files.FileField', [], {'max_length': '100'}), +            'imported_images': ('django.db.models.fields.files.FileField', [], {'max_length': '100', 'null': 'True', 'blank': 'True'}), +            'importer_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['ishtar_common.ImporterType']"}), +            'match_file': ('django.db.models.fields.files.FileField', [], {'max_length': '100', 'null': 'True', 'blank': 'True'}), +            'result_file': ('django.db.models.fields.files.FileField', [], {'max_length': '100', 'null': 'True', 'blank': 'True'}), +            'seconds_remaining': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}), +            'skip_lines': ('django.db.models.fields.IntegerField', [], {'default': '1'}), +            'state': ('django.db.models.fields.CharField', [], {'default': "'C'", 'max_length': '2'}), +            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['ishtar_common.IshtarUser']"}) +        }, +        'ishtar_common.importercolumn': { +            'Meta': {'ordering': "('importer_type', 'col_number')", 'unique_together': "(('importer_type', 'col_number'),)", 'object_name': 'ImporterColumn'}, +            'col_number': ('django.db.models.fields.IntegerField', [], {'default': '1'}), +            'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), +            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), +            'importer_type': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'columns'", 'to': "orm['ishtar_common.ImporterType']"}), +            'regexp_pre_filter': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['ishtar_common.Regexp']", 'null': 'True', 'blank': 'True'}), +            'required': ('django.db.models.fields.BooleanField', [], {'default': 'False'}) +        }, +        'ishtar_common.importerdefault': { +            'Meta': {'object_name': 'ImporterDefault'}, +            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), +            'importer_type': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'defaults'", 'to': "orm['ishtar_common.ImporterType']"}), +            'target': ('django.db.models.fields.CharField', [], {'max_length': '500'}) +        }, +        'ishtar_common.importerdefaultvalues': { +            'Meta': {'object_name': 'ImporterDefaultValues'}, +            'default_target': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'default_values'", 'to': "orm['ishtar_common.ImporterDefault']"}), +            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), +            'target': ('django.db.models.fields.CharField', [], {'max_length': '500'}), +            'value': ('django.db.models.fields.CharField', [], {'max_length': '500'}) +        }, +        'ishtar_common.importerduplicatefield': { +            'Meta': {'object_name': 'ImporterDuplicateField'}, +            'column': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'duplicate_fields'", 'to': "orm['ishtar_common.ImporterColumn']"}), +            'concat': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), +            'concat_str': ('django.db.models.fields.CharField', [], {'max_length': '5', 'null': 'True', 'blank': 'True'}), +            'field_name': ('django.db.models.fields.CharField', [], {'max_length': '200', 'null': 'True', 'blank': 'True'}), +            'force_new': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), +            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}) +        }, +        'ishtar_common.importertype': { +            'Meta': {'object_name': 'ImporterType'}, +            'associated_models': ('django.db.models.fields.CharField', [], {'max_length': '200'}), +            'description': ('django.db.models.fields.CharField', [], {'max_length': '500', 'null': 'True', 'blank': 'True'}), +            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), +            'is_template': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), +            'name': ('django.db.models.fields.CharField', [], {'max_length': '100', 'null': 'True', 'blank': 'True'}), +            'slug': ('django.db.models.fields.SlugField', [], {'max_length': '100', 'unique': 'True', 'null': 'True', 'blank': 'True'}), +            'unicity_keys': ('django.db.models.fields.CharField', [], {'max_length': '500', 'null': 'True', 'blank': 'True'}), +            'users': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'to': "orm['ishtar_common.IshtarUser']", 'null': 'True', 'blank': 'True'}) +        }, +        'ishtar_common.importtarget': { +            'Meta': {'object_name': 'ImportTarget'}, +            'column': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'targets'", 'to': "orm['ishtar_common.ImporterColumn']"}), +            'comment': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), +            'concat': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), +            'concat_str': ('django.db.models.fields.CharField', [], {'max_length': '5', 'null': 'True', 'blank': 'True'}), +            'force_new': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), +            'formater_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['ishtar_common.FormaterType']"}), +            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), +            'regexp_filter': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['ishtar_common.Regexp']", 'null': 'True', 'blank': 'True'}), +            'target': ('django.db.models.fields.CharField', [], {'max_length': '500'}) +        }, +        'ishtar_common.ishtarsiteprofile': { +            'Meta': {'ordering': "['label']", 'object_name': 'IshtarSiteProfile'}, +            'active': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), +            'context_record': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), +            'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), +            'files': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), +            'find': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), +            'homepage': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), +            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), +            'label': ('django.db.models.fields.TextField', [], {}), +            'slug': ('django.db.models.fields.SlugField', [], {'unique': 'True', 'max_length': '50'}), +            'warehouse': ('django.db.models.fields.BooleanField', [], {'default': 'False'}) +        }, +        'ishtar_common.ishtaruser': { +            'Meta': {'object_name': 'IshtarUser', '_ormbases': ['auth.User']}, +            'person': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'ishtaruser'", 'unique': 'True', 'to': "orm['ishtar_common.Person']"}), +            'user_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['auth.User']", 'unique': 'True', 'primary_key': 'True'}) +        }, +        'ishtar_common.itemkey': { +            'Meta': {'object_name': 'ItemKey'}, +            'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}), +            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), +            'importer': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['ishtar_common.Import']", 'null': 'True', 'blank': 'True'}), +            'key': ('django.db.models.fields.CharField', [], {'max_length': '100'}), +            'object_id': ('django.db.models.fields.PositiveIntegerField', [], {}) +        }, +        'ishtar_common.operationtype': { +            'Meta': {'ordering': "['-preventive', 'order', 'label']", 'object_name': 'OperationType'}, +            'available': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), +            'comment': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), +            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), +            'label': ('django.db.models.fields.CharField', [], {'max_length': '100'}), +            'order': ('django.db.models.fields.IntegerField', [], {'default': '1'}), +            'preventive': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), +            'txt_idx': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '100'}) +        }, +        'ishtar_common.organization': { +            'Meta': {'object_name': 'Organization'}, +            'address': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), +            'address_complement': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), +            'alt_address': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), +            'alt_address_complement': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), +            'alt_address_is_prefered': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), +            'alt_country': ('django.db.models.fields.CharField', [], {'max_length': '30', 'null': 'True', 'blank': 'True'}), +            'alt_postal_code': ('django.db.models.fields.CharField', [], {'max_length': '10', 'null': 'True', 'blank': 'True'}), +            'alt_town': ('django.db.models.fields.CharField', [], {'max_length': '70', 'null': 'True', 'blank': 'True'}), +            'country': ('django.db.models.fields.CharField', [], {'max_length': '30', 'null': 'True', 'blank': 'True'}), +            'email': ('django.db.models.fields.EmailField', [], {'max_length': '300', 'null': 'True', 'blank': 'True'}), +            'exclude_from_merge': ('django.db.models.fields.NullBooleanField', [], {'default': 'False', 'null': 'True', 'blank': 'True'}), +            'history_creator': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'+'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['auth.User']"}), +            'history_modifier': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'+'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['auth.User']"}), +            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), +            'imports': ('django.db.models.fields.related.ManyToManyField', [], {'blank': 'True', 'related_name': "'imported_ishtar_common_organization'", 'null': 'True', 'symmetrical': 'False', 'to': "orm['ishtar_common.Import']"}), +            'merge_candidate': ('django.db.models.fields.related.ManyToManyField', [], {'blank': 'True', 'related_name': "'merge_candidate_rel_+'", 'null': 'True', 'to': "orm['ishtar_common.Organization']"}), +            'merge_exclusion': ('django.db.models.fields.related.ManyToManyField', [], {'blank': 'True', 'related_name': "'merge_exclusion_rel_+'", 'null': 'True', 'to': "orm['ishtar_common.Organization']"}), +            'merge_key': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), +            'mobile_phone': ('django.db.models.fields.CharField', [], {'max_length': '18', 'null': 'True', 'blank': 'True'}), +            'name': ('django.db.models.fields.CharField', [], {'max_length': '500'}), +            'organization_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['ishtar_common.OrganizationType']"}), +            'phone': ('django.db.models.fields.CharField', [], {'max_length': '18', 'null': 'True', 'blank': 'True'}), +            'phone2': ('django.db.models.fields.CharField', [], {'max_length': '18', 'null': 'True', 'blank': 'True'}), +            'phone3': ('django.db.models.fields.CharField', [], {'max_length': '18', 'null': 'True', 'blank': 'True'}), +            'phone_desc': ('django.db.models.fields.CharField', [], {'max_length': '300', 'null': 'True', 'blank': 'True'}), +            'phone_desc2': ('django.db.models.fields.CharField', [], {'max_length': '300', 'null': 'True', 'blank': 'True'}), +            'phone_desc3': ('django.db.models.fields.CharField', [], {'max_length': '300', 'null': 'True', 'blank': 'True'}), +            'postal_code': ('django.db.models.fields.CharField', [], {'max_length': '10', 'null': 'True', 'blank': 'True'}), +            'raw_phone': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), +            'town': ('django.db.models.fields.CharField', [], {'max_length': '70', 'null': 'True', 'blank': 'True'}) +        }, +        'ishtar_common.organizationtype': { +            'Meta': {'ordering': "('label',)", 'object_name': 'OrganizationType'}, +            'available': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), +            'comment': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), +            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), +            'label': ('django.db.models.fields.CharField', [], {'max_length': '100'}), +            'txt_idx': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '100'}) +        }, +        'ishtar_common.person': { +            'Meta': {'object_name': 'Person'}, +            'address': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), +            'address_complement': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), +            'alt_address': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), +            'alt_address_complement': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), +            'alt_address_is_prefered': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), +            'alt_country': ('django.db.models.fields.CharField', [], {'max_length': '30', 'null': 'True', 'blank': 'True'}), +            'alt_postal_code': ('django.db.models.fields.CharField', [], {'max_length': '10', 'null': 'True', 'blank': 'True'}), +            'alt_town': ('django.db.models.fields.CharField', [], {'max_length': '70', 'null': 'True', 'blank': 'True'}), +            'attached_to': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'members'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['ishtar_common.Organization']"}), +            'comment': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), +            'contact_type': ('django.db.models.fields.CharField', [], {'max_length': '300', 'null': 'True', 'blank': 'True'}), +            'country': ('django.db.models.fields.CharField', [], {'max_length': '30', 'null': 'True', 'blank': 'True'}), +            'email': ('django.db.models.fields.EmailField', [], {'max_length': '300', 'null': 'True', 'blank': 'True'}), +            'exclude_from_merge': ('django.db.models.fields.NullBooleanField', [], {'default': 'False', 'null': 'True', 'blank': 'True'}), +            'history_creator': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'+'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['auth.User']"}), +            'history_modifier': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'+'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['auth.User']"}), +            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), +            'imports': ('django.db.models.fields.related.ManyToManyField', [], {'blank': 'True', 'related_name': "'imported_ishtar_common_person'", 'null': 'True', 'symmetrical': 'False', 'to': "orm['ishtar_common.Import']"}), +            'merge_candidate': ('django.db.models.fields.related.ManyToManyField', [], {'blank': 'True', 'related_name': "'merge_candidate_rel_+'", 'null': 'True', 'to': "orm['ishtar_common.Person']"}), +            'merge_exclusion': ('django.db.models.fields.related.ManyToManyField', [], {'blank': 'True', 'related_name': "'merge_exclusion_rel_+'", 'null': 'True', 'to': "orm['ishtar_common.Person']"}), +            'merge_key': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), +            'mobile_phone': ('django.db.models.fields.CharField', [], {'max_length': '18', 'null': 'True', 'blank': 'True'}), +            'name': ('django.db.models.fields.CharField', [], {'max_length': '200', 'null': 'True', 'blank': 'True'}), +            'person_types': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['ishtar_common.PersonType']", 'symmetrical': 'False'}), +            'phone': ('django.db.models.fields.CharField', [], {'max_length': '18', 'null': 'True', 'blank': 'True'}), +            'phone2': ('django.db.models.fields.CharField', [], {'max_length': '18', 'null': 'True', 'blank': 'True'}), +            'phone3': ('django.db.models.fields.CharField', [], {'max_length': '18', 'null': 'True', 'blank': 'True'}), +            'phone_desc': ('django.db.models.fields.CharField', [], {'max_length': '300', 'null': 'True', 'blank': 'True'}), +            'phone_desc2': ('django.db.models.fields.CharField', [], {'max_length': '300', 'null': 'True', 'blank': 'True'}), +            'phone_desc3': ('django.db.models.fields.CharField', [], {'max_length': '300', 'null': 'True', 'blank': 'True'}), +            'postal_code': ('django.db.models.fields.CharField', [], {'max_length': '10', 'null': 'True', 'blank': 'True'}), +            'raw_name': ('django.db.models.fields.CharField', [], {'max_length': '300', 'null': 'True', 'blank': 'True'}), +            'raw_phone': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), +            'surname': ('django.db.models.fields.CharField', [], {'max_length': '50', 'null': 'True', 'blank': 'True'}), +            'title': ('django.db.models.fields.CharField', [], {'max_length': '100', 'null': 'True', 'blank': 'True'}), +            'town': ('django.db.models.fields.CharField', [], {'max_length': '70', 'null': 'True', 'blank': 'True'}) +        }, +        'ishtar_common.persontype': { +            'Meta': {'ordering': "('label',)", 'object_name': 'PersonType'}, +            'available': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), +            'comment': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), +            'groups': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'to': "orm['auth.Group']", 'null': 'True', 'blank': 'True'}), +            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), +            'label': ('django.db.models.fields.CharField', [], {'max_length': '100'}), +            'txt_idx': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '100'}) +        }, +        'ishtar_common.regexp': { +            'Meta': {'object_name': 'Regexp'}, +            'description': ('django.db.models.fields.CharField', [], {'max_length': '500', 'null': 'True', 'blank': 'True'}), +            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), +            'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}), +            'regexp': ('django.db.models.fields.CharField', [], {'max_length': '500'}) +        }, +        'ishtar_common.sourcetype': { +            'Meta': {'object_name': 'SourceType'}, +            'available': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), +            'comment': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), +            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), +            'label': ('django.db.models.fields.CharField', [], {'max_length': '100'}), +            'txt_idx': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '100'}) +        }, +        'ishtar_common.state': { +            'Meta': {'ordering': "['number']", 'object_name': 'State'}, +            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), +            'label': ('django.db.models.fields.CharField', [], {'max_length': '30'}), +            'number': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '3'}) +        }, +        'ishtar_common.supporttype': { +            'Meta': {'object_name': 'SupportType'}, +            'available': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), +            'comment': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), +            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), +            'label': ('django.db.models.fields.CharField', [], {'max_length': '100'}), +            'txt_idx': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '100'}) +        }, +        'ishtar_common.targetkey': { +            'Meta': {'unique_together': "(('target', 'key', 'associated_user', 'associated_import'),)", 'object_name': 'TargetKey'}, +            'associated_import': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['ishtar_common.Import']", 'null': 'True', 'blank': 'True'}), +            'associated_user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['ishtar_common.IshtarUser']", 'null': 'True', 'blank': 'True'}), +            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), +            'is_set': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), +            'key': ('django.db.models.fields.TextField', [], {}), +            'target': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'keys'", 'to': "orm['ishtar_common.ImportTarget']"}), +            'value': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}) +        }, +        'ishtar_common.town': { +            'Meta': {'ordering': "['numero_insee']", 'object_name': 'Town'}, +            'canton': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['ishtar_common.Canton']", 'null': 'True', 'blank': 'True'}), +            'center': ('django.contrib.gis.db.models.fields.PointField', [], {'srid': '27572', 'null': 'True', 'blank': 'True'}), +            'departement': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['ishtar_common.Department']", 'null': 'True', 'blank': 'True'}), +            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), +            'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}), +            'numero_insee': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '6'}), +            'surface': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}) +        } +    } + +    complete_apps = ['ishtar_common']
\ No newline at end of file diff --git a/ishtar_common/models.py b/ishtar_common/models.py index f3974d04d..d8948d3e3 100644 --- a/ishtar_common/models.py +++ b/ishtar_common/models.py @@ -552,25 +552,27 @@ class ImageModel(models.Model):              filename = os.path.splitext(os.path.split(self.image.name)[-1])[0]              old_path = self.image.path              filename = "%s.jpg" % filename -            image = Image.open(self.image.file) - -            # convert to RGB -            if image.mode not in ('L', 'RGB'): -                image = image.convert('RGB') - -            # resize if necessary -            self.image.save(filename, -                            self.create_thumb(image, self.IMAGE_MAX_SIZE), -                            save=False) - -            if old_path != self.image.path: -                os.remove(old_path) - -            # save the thumbnail -            self.thumbnail.save( -                '_%s' % filename, -                self.create_thumb(image, self.THUMB_MAX_SIZE), -                save=False) +            try: +                image = Image.open(self.image.file) +                # convert to RGB +                if image.mode not in ('L', 'RGB'): +                    image = image.convert('RGB') + +                # resize if necessary +                self.image.save(filename, +                                self.create_thumb(image, self.IMAGE_MAX_SIZE), +                                save=False) + +                if old_path != self.image.path: +                    os.remove(old_path) + +                # save the thumbnail +                self.thumbnail.save( +                    '_%s' % filename, +                    self.create_thumb(image, self.THUMB_MAX_SIZE), +                    save=False) +            except IOError: +                pass          super(ImageModel, self).save(*args, **kwargs) @@ -581,6 +583,8 @@ class HistoryError(Exception):      def __str__(self):          return repr(self.value) +PRIVATE_FIELDS = ('id', 'history_modifier', 'order') +  class BaseHistorizedItem(Imported):      IS_BASKET = False @@ -816,6 +820,10 @@ class IshtarSiteProfile(models.Model, Cached):      warehouse = models.BooleanField(          _(u"Warehouses module"), default=False,          help_text=_(u"Need finds module")) +    homepage = models.TextField( +        _(u"Home page"), null=True, blank=True, +        help_text=_(u"Homepage of Ishtar - if not defined a default homepage " +                    u"will appear. Use the markdown syntax."))      active = models.BooleanField(_(u"Current active"), default=False)      class Meta: diff --git a/ishtar_common/static/js/grid.tbltogrid.js b/ishtar_common/static/js/grid.tbltogrid.js new file mode 100644 index 000000000..addab1e48 --- /dev/null +++ b/ishtar_common/static/js/grid.tbltogrid.js @@ -0,0 +1,106 @@ +/* + Transform a table to a jqGrid. + Peter Romianowski <peter.romianowski@optivo.de>  + If the first column of the table contains checkboxes or + radiobuttons then the jqGrid is made selectable. +*/ +// Addition - selector can be a class or id +function tableToGrid(selector, options) { +jQuery(selector).each(function() { +	if(this.grid) {return;} //Adedd from Tony Tomov +	// This is a small "hack" to make the width of the jqGrid 100% +	jQuery(this).width("99%"); +	var w = jQuery(this).width(); + +	// Text whether we have single or multi select +	var inputCheckbox = jQuery('tr td:first-child input[type=checkbox]:first', jQuery(this)); +	var inputRadio = jQuery('tr td:first-child input[type=radio]:first', jQuery(this)); +	var selectMultiple = inputCheckbox.length > 0; +	var selectSingle = !selectMultiple && inputRadio.length > 0; +	var selectable = selectMultiple || selectSingle; +	//var inputName = inputCheckbox.attr("name") || inputRadio.attr("name"); + +	// Build up the columnModel and the data +	var colModel = []; +	var colNames = []; +	jQuery('th', jQuery(this)).each(function() { +		if (colModel.length === 0 && selectable) { +			colModel.push({ +				name: '__selection__', +				index: '__selection__', +				width: 0, +				hidden: true +			}); +			colNames.push('__selection__'); +		} else { +			colModel.push({ +				name: jQuery(this).attr("id") || jQuery.trim(jQuery.jgrid.stripHtml(jQuery(this).html())).split(' ').join('_'), +				index: jQuery(this).attr("id") || jQuery.trim(jQuery.jgrid.stripHtml(jQuery(this).html())).split(' ').join('_'), +				width: jQuery(this).width() || 150 +			}); +			colNames.push(jQuery(this).html()); +		} +	}); +	var data = []; +	var rowIds = []; +	var rowChecked = []; +	jQuery('tbody > tr', jQuery(this)).each(function() { +		var row = {}; +		var rowPos = 0; +		jQuery('td', jQuery(this)).each(function() { +			if (rowPos === 0 && selectable) { +				var input = jQuery('input', jQuery(this)); +				var rowId = input.attr("value"); +				rowIds.push(rowId || data.length); +				if (input.is(":checked")) { +					rowChecked.push(rowId); +				} +				row[colModel[rowPos].name] = input.attr("value"); +			} else { +				row[colModel[rowPos].name] = jQuery(this).html(); +			} +			rowPos++; +		}); +		if(rowPos >0) { data.push(row); } +	}); + +	// Clear the original HTML table +	jQuery(this).empty(); + +	// Mark it as jqGrid +	jQuery(this).addClass("scroll"); + +	jQuery(this).jqGrid(jQuery.extend({ +		datatype: "local", +		width: w, +		colNames: colNames, +		colModel: colModel, +		multiselect: selectMultiple +		//inputName: inputName, +		//inputValueCol: imputName != null ? "__selection__" : null +	}, options || {})); + +	// Add data +	var a; +	for (a = 0; a < data.length; a++) { +		var id = null; +		if (rowIds.length > 0) { +			id = rowIds[a]; +			if (id && id.replace) { +				// We have to do this since the value of a checkbox +				// or radio button can be anything  +				id = encodeURIComponent(id).replace(/[.\-%]/g, "_"); +			} +		} +		if (id === null) { +			id = a + 1; +		} +		jQuery(this).jqGrid("addRowData",id, data[a]); +	} + +	// Set the selection +	for (a = 0; a < rowChecked.length; a++) { +		jQuery(this).jqGrid("setSelection",rowChecked[a]); +	} +}); +}; diff --git a/ishtar_common/static/media/style.css b/ishtar_common/static/media/style.css index 1d47bcbd9..e74a6d205 100644 --- a/ishtar_common/static/media/style.css +++ b/ishtar_common/static/media/style.css @@ -45,7 +45,7 @@ a.add-button,      color:#000;  } -#context_menu .basket{ +#context_menu option.basket{      color:#000;      font-weight: bold;      font-style: italic; @@ -434,6 +434,11 @@ div#main_menu > ul > li{  }  */ +div#welcome{ +    text-align:left; +    font-size: 1.2em; +} +  div#content{      clear:both;      margin-top:190px ; @@ -546,21 +551,20 @@ ul.form-flex {      margin: 0;  } -  ul.form-flex li{      width: 395px;  }  @media screen and (min-width: 1051px) {      div.form, -    ul.form-flex { +    ul.form-flex{          width: 800px;      }  }  @media screen and (min-width: 1400px) {      div.form, -    ul.form-flex { +    ul.form-flex{          width: 1200px;      }  } @@ -571,6 +575,10 @@ ul.form-flex label {      padding-left: 10px;  } +#window ul.form-flex label { +    width: 150px; +} +  .form table{      padding:0.2em;      margin-left:auto; @@ -750,6 +758,10 @@ table.confirm tr.spacer td:last-child{      right:135px;  } +a.photo{ +    padding: 0.5em; +} +  #global-vars textarea{      width:220px;  } @@ -898,6 +910,22 @@ table.confirm tr.spacer td:last-child{      font-style:italic;  } +#window table.simple td.item-list{ +    text-align:left; +} + +table td.item-list span{ +    padding: 4px 4px; +    background-color: #eee; +    margin: 0.2em; +    line-height: 26px; +    border-radius: 4px; +} + +#window table.simple td.item-list .display_details{ +    vertical-align: middle; +} +  #window .head{      text-align:center;      background-color:#f1f2f6; @@ -946,6 +974,11 @@ table.confirm tr.spacer td:last-child{      width:465px;  } +#window ul.form-flex span.value { +    display: inline-table; +    width: 230px; +} +  .strong{      font-weight:bold;  } diff --git a/ishtar_common/static/media/style_basic.css b/ishtar_common/static/media/style_basic.css index d624ae09c..1d92928dc 100644 --- a/ishtar_common/static/media/style_basic.css +++ b/ishtar_common/static/media/style_basic.css @@ -85,3 +85,7 @@ p{      width:100%;      border-bottom:1px solid #922;  } + +.display_details, .display_details_inline{ +    display: none; +} diff --git a/ishtar_common/templates/base.html b/ishtar_common/templates/base.html index 1bab3d647..8cd3c7793 100644 --- a/ishtar_common/templates/base.html +++ b/ishtar_common/templates/base.html @@ -17,6 +17,7 @@      <script language="javascript" type="text/javascript" src="{{STATIC_URL}}/js/prettyPhoto/js/jquery.prettyPhoto.js"></script>      <script language="javascript" type="text/javascript" src="{{STATIC_URL}}js/i18n/grid.locale-{{COUNTRY}}.js"></script>      <script language="javascript" type="text/javascript" src="{{STATIC_URL}}js/jquery.jqGrid.min.js"></script> +    <script language="javascript" type="text/javascript" src="{{STATIC_URL}}js/grid.tbltogrid.js"></script>      <script language="javascript" type="text/javascript" src="{{STATIC_URL}}/js/ishtar.js"></script>      <link type="text/css" href="{{JQUERY_UI_URL}}jquery-ui.css" rel="stylesheet" />      <link rel="stylesheet" href="{{STATIC_URL}}/js/prettyPhoto/css/prettyPhoto.css" /> @@ -74,7 +75,7 @@                  <td>                    <select class='{{main_cls}}' id='current_{{model_name}}'>                      <option class='normal' value=''>--</option> -                    {% for val, label, selected, cls in items %}<option class='{{cls}}' value='{{val}}'{% if selected %} selected="selected"{%endif%}>{{label}}</option> +                    {% for val, label, selected, cls in items %}<option{% if cls %} class='{{cls}}'{% endif %} value='{{val}}'{% if selected %} selected="selected"{%endif%}>{{label}}</option>                    {% endfor %}</select>                  </td>{% with 'show-'|add:model_name as model_url%}                  <td><a href='#' onclick='load_current_window("{% url model_url 0 %}", "{{model_name}}");' class='display_details'>{% trans "Details" %}</a></td> diff --git a/ishtar_common/templates/index.html b/ishtar_common/templates/index.html index c4c0b0f2a..2b6d7004e 100644 --- a/ishtar_common/templates/index.html +++ b/ishtar_common/templates/index.html @@ -1,2 +1,3 @@  {% extends "base.html" %}  {% load i18n %} +{% block content %}<div id='welcome'>{% if homepage %}{{homepage|safe}}{% else %}{% include "welcome.html" %}{% endif %}</div>{% endblock %} diff --git a/ishtar_common/templates/ishtar/blocks/window_field.html b/ishtar_common/templates/ishtar/blocks/window_field.html index 14cf1c21e..969f32dbb 100644 --- a/ishtar_common/templates/ishtar/blocks/window_field.html +++ b/ishtar_common/templates/ishtar/blocks/window_field.html @@ -1,3 +1,3 @@ -{% load i18n %} -{% if data %}<p><label>{% trans caption %}{% trans ":"%}</label> -<span class='value'>{{pre_data|safe}}{{data|linebreaksbr}}{{post_data|safe}}</span></p>{% endif%} +{% load i18n %}{% if data %}{% if li %}<li>{% else %}<p>{% endif %}<label>{% trans caption %}{% trans ":"%}</label> +<span class='value'>{{pre_data|safe}}{{data|linebreaksbr}}{{post_data|safe}}</span>{% if li %}</li>{% else %}</p>{% endif %} +{% endif %} diff --git a/ishtar_common/templates/ishtar/blocks/window_field_detail.html b/ishtar_common/templates/ishtar/blocks/window_field_detail.html new file mode 100644 index 000000000..2be9c5ec4 --- /dev/null +++ b/ishtar_common/templates/ishtar/blocks/window_field_detail.html @@ -0,0 +1,3 @@ +{% load i18n %}{% if item %}{% if li %}<li>{% else %}<p>{% endif %}<label>{% trans caption %}{% trans ":"%}</label> +<span class='value'>{{item}}{{link}}</span>{% if li %}</li>{% else %}</p>{% endif %} +{% endif %} diff --git a/ishtar_common/templates/ishtar/blocks/window_field_multiple.html b/ishtar_common/templates/ishtar/blocks/window_field_multiple.html index 30903b0d5..d1ee25c7b 100644 --- a/ishtar_common/templates/ishtar/blocks/window_field_multiple.html +++ b/ishtar_common/templates/ishtar/blocks/window_field_multiple.html @@ -1,8 +1,6 @@ -{% load i18n %} -{% if data.count %}<p><label>{% trans caption %}{% trans ":"%}</label> +{% load i18n %}{% if data.count %}{% if li %}<li>{% else %}<p>{% endif %}<label>{% trans caption %}{% trans ":"%}</label>      <span class='value'>{% for d in data.all %}          {% if forloop.counter0 %}, {% endif %}{{ d }}      {% endfor %}</span> -</p> +{% if li %}</li>{% else %}</p>{% endif %}  {% endif %} - diff --git a/ishtar_common/templates/ishtar/blocks/window_field_url.html b/ishtar_common/templates/ishtar/blocks/window_field_url.html index b91f318b6..637366d1d 100644 --- a/ishtar_common/templates/ishtar/blocks/window_field_url.html +++ b/ishtar_common/templates/ishtar/blocks/window_field_url.html @@ -1,3 +1,3 @@ -{% load i18n %} -{% if link %}<p><label>{% trans caption %}{% trans ":"%}</label> -<span class='value'><a target="_blank" href='{{link|safe}}'>{% if link_name %}{{link_name}}{% else %}{% trans "link" %}{% endif %}</a></span></p>{% endif%} +{% load i18n %}{% if link %}{% if li %}<li>{% else %}<p>{% endif %}<p><label>{% trans caption %}{% trans ":"%}</label> +<span class='value'><a target="_blank" href='{{link|safe}}'>{% if link_name %}{{link_name}}{% else %}{% trans "link" %}{% endif %}</a></span>{% if li %}</li>{% else %}</p>{% endif %} +{% endif%} diff --git a/ishtar_common/templates/ishtar/sheet_toolbar.html b/ishtar_common/templates/ishtar/sheet_toolbar.html index 7e2c1659a..a6736b6cc 100644 --- a/ishtar_common/templates/ishtar/sheet_toolbar.html +++ b/ishtar_common/templates/ishtar/sheet_toolbar.html @@ -1,2 +1,2 @@ -{% load i18n link_to_window %}<div class='tool'>{%trans "Export as:"%} <a href='{{item|link_to_odt}}'>{%trans "OpenOffice.org file"%}</a>, <a href='{{item|link_to_pdf}}'>{%trans "PDF file"%}</a></div> +{% load i18n link_to_window %}<div class='tool'>{% trans "Export as:" %} <a href='{{item|link_to_odt}}'>{%trans "OpenOffice.org file"%}</a>, <a href='{{item|link_to_pdf}}'>{%trans "PDF file"%}</a></div>  {% modify_toolbar item "person_modification" %} diff --git a/ishtar_common/templates/welcome.html b/ishtar_common/templates/welcome.html new file mode 100644 index 000000000..42935d8f9 --- /dev/null +++ b/ishtar_common/templates/welcome.html @@ -0,0 +1,11 @@ +{% load i18n %} +{% load url from future %} + +<h2>{% trans "Welcome in Ishtar, open source software for management and inventory of archaeological data" %}</h2> +<p>{% trans "Some useful links:" %}</p> +<ul> +    <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> +</ul> diff --git a/ishtar_common/templatetags/link_to_window.py b/ishtar_common/templatetags/link_to_window.py index 5ff928bb1..009a089d2 100644 --- a/ishtar_common/templatetags/link_to_window.py +++ b/ishtar_common/templatetags/link_to_window.py @@ -3,6 +3,7 @@  from django.core.urlresolvers import reverse  from django.template import Library +from django.utils.safestring import mark_safe  from django.utils.translation import ugettext as _  register = Library() @@ -10,10 +11,11 @@ register = Library()  @register.filter  def link_to_window(item): -    return u' <a class="display_details" href="#" '\ -        'onclick="load_window(\'{}\')">{}</a>'.format( +    return mark_safe( +        u' <a class="display_details" href="#" ' +        u'onclick="load_window(\'{}\')">{}</a>'.format(              reverse(item.SHOW_URL, args=[item.pk, '']), -            _("Details")) +            _("Details")))  @register.filter diff --git a/ishtar_common/templatetags/window_field.py b/ishtar_common/templatetags/window_field.py index 34071f60f..9ce2cf6b6 100644 --- a/ishtar_common/templatetags/window_field.py +++ b/ishtar_common/templatetags/window_field.py @@ -1,26 +1,53 @@  from django import template  from django.utils.translation import ugettext_lazy as _ +from ishtar_common.templatetags.link_to_window import link_to_window  register = template.Library()  @register.inclusion_tag('ishtar/blocks/window_field.html') -def field(caption, data, pre_data='', post_data=''): +def field(caption, data, pre_data='', post_data='', li=False):      if data in (True, False):          data = _(unicode(data))      return {'caption': caption, 'data': data, "pre_data": pre_data, -            'post_data': post_data} +            'post_data': post_data, 'li': li} + + +@register.inclusion_tag('ishtar/blocks/window_field.html') +def field_li(caption, data, pre_data='', post_data=''): +    return field(caption, data, pre_data, post_data, li=True)  @register.inclusion_tag('ishtar/blocks/window_field_url.html') -def field_url(caption, link, link_name=''): +def field_url(caption, link, link_name='', li=False):      if not link:          return u''      if not link.startswith('http://'):          link = 'http://' + link -    return {'caption': caption, 'link': link, "link_name": link_name} +    return {'caption': caption, 'link': link, "link_name": link_name, 'li': li} + + +@register.inclusion_tag('ishtar/blocks/window_field_url.html') +def field_li_url(caption, link, link_name=''): +    return field_url(caption, link, link_name, li=True)  @register.inclusion_tag('ishtar/blocks/window_field_multiple.html') -def field_multiple(caption, data): -    return {'caption': caption, 'data': data} +def field_multiple(caption, data, li=False): +    return {'caption': caption, 'data': data, 'li': li} + + +@register.inclusion_tag('ishtar/blocks/window_field_multiple.html') +def field_li_multiple(caption, data): +    return field_multiple(caption, data, li=True) + + +@register.inclusion_tag('ishtar/blocks/window_field_detail.html') +def field_detail(caption, item, li=False): +    return {'caption': caption, 'item': item, 'link': link_to_window(item), +            'li': li} + + +@register.inclusion_tag('ishtar/blocks/window_field_detail.html') +def field_li_detail(caption, item): +    return field_detail(caption, item, li=True) diff --git a/ishtar_common/views.py b/ishtar_common/views.py index 59cfe6321..d508444a4 100644 --- a/ishtar_common/views.py +++ b/ishtar_common/views.py @@ -24,6 +24,7 @@ import cStringIO as StringIO  import datetime  import ho.pisa as pisa  import json +from markdown import markdown  import optparse  import re  from tempfile import NamedTemporaryFile @@ -61,7 +62,8 @@ from archaeological_files.forms import DashboardForm as DashboardFormFile  from ishtar_common.forms import FinalForm, FinalDeleteForm  from ishtar_common import forms_common as forms  from ishtar_common import wizards -from ishtar_common.models import HistoryError +from ishtar_common.models import HistoryError, PRIVATE_FIELDS, \ +    get_current_profile  import models @@ -74,6 +76,9 @@ def index(request):      Main page      """      dct = {} +    profile = get_current_profile() +    if hasattr(profile, 'homepage') and profile.homepage: +        dct['homepage'] = markdown(profile.homepage)      try:          return render_to_response('index.html', dct,                                    context_instance=RequestContext(request)) @@ -325,7 +330,6 @@ def format_val(val):  HIERARCHIC_LEVELS = 5  HIERARCHIC_FIELDS = ['periods', 'period', 'unit', 'material_types',                       'material_type', 'conservatory_state'] -PRIVATE_FIELDS = ('id', 'history_modifier', 'order')  def get_item(model, func_name, default_name, extra_request_keys=[], diff --git a/requirements.txt b/requirements.txt index c6ae18abd..c41427681 100644 --- a/requirements.txt +++ b/requirements.txt @@ -16,3 +16,4 @@ django-extra-views==0.2.4  # git+https://github.com/treyhunner/django-simple-history.git@0fd9b8e9c6f36b0141367b502420efe92d4e21ce  # oook_replace  beautifulsoup4==4.3.2 +markdown==2.1.1 | 
