diff options
author | Étienne Loks <etienne.loks@peacefrogs.net> | 2013-12-02 14:51:09 +0100 |
---|---|---|
committer | Étienne Loks <etienne.loks@peacefrogs.net> | 2013-12-02 14:59:06 +0100 |
commit | 6326867adb72dd9ded160769ef09e77b2e482784 (patch) | |
tree | e7c44902b0dc74675ae3dda5658fc29c14ec71ba | |
parent | de9fbce423f780a02b73a9e943995b0b7efb0008 (diff) | |
download | Ishtar-6326867adb72dd9ded160769ef09e77b2e482784.tar.bz2 Ishtar-6326867adb72dd9ded160769ef09e77b2e482784.zip |
Manage document template
* ooo_replace: generate a document by mapping ooo variables with
a given dict
* DocumentTemplate model: store templates associated with a type
of objects
* get_values method: generate a dict of value from a model
* new form/view to generate document from administrativ acts
-rw-r--r-- | archaeological_files/forms.py | 26 | ||||
-rw-r--r-- | archaeological_files/ishtar_menu.py | 6 | ||||
-rw-r--r-- | archaeological_files/models.py | 14 | ||||
-rw-r--r-- | archaeological_files/templates/ishtar/administrativeact_document.html | 23 | ||||
-rw-r--r-- | archaeological_files/urls.py | 7 | ||||
-rw-r--r-- | archaeological_files/views.py | 28 | ||||
-rw-r--r-- | archaeological_operations/models.py | 5 | ||||
-rw-r--r-- | ishtar_common/admin.py | 2 | ||||
-rw-r--r-- | ishtar_common/migrations/0005_auto__add_documenttemplate.py | 209 | ||||
-rw-r--r-- | ishtar_common/models.py | 57 | ||||
-rw-r--r-- | ishtar_common/ooo_replace.py | 102 | ||||
-rw-r--r-- | ishtar_common/templates/ishtar/wizard/wizard_list_search_result.html | 2 | ||||
-rw-r--r-- | ishtar_common/views.py | 3 |
13 files changed, 467 insertions, 17 deletions
diff --git a/archaeological_files/forms.py b/archaeological_files/forms.py index 2adc58ba1..9affc0901 100644 --- a/archaeological_files/forms.py +++ b/archaeological_files/forms.py @@ -33,7 +33,7 @@ from django.utils.translation import ugettext_lazy as _ from django.utils.safestring import mark_safe from ishtar_common.models import Person, PersonType, Town, Organization, \ - OrganizationType, valid_id, is_unique + OrganizationType, valid_id, is_unique, DocumentTemplate from archaeological_operations.models import ActType, AdministrativeAct import models from ishtar_common.forms import FinalForm, FormSet, ClosingDateFormSelection, \ @@ -185,16 +185,36 @@ class FileFormPreventive(forms.Form): self.fields['permit_type'].choices = models.PermitType.get_types() self.fields['permit_type'].help_text = models.PermitType.get_help() - class FinalFileClosingForm(FinalForm): confirm_msg = " " confirm_end_msg = _(u"Would you like to close this archaeological file?") - class FinalFileDeleteForm(FinalForm): confirm_msg = " " confirm_end_msg = _(u"Would you like to delete this archaelogical file ?") +class DocumentGenerationAdminActForm(forms.Form): + _associated_model = AdministrativeAct + document_template = forms.ChoiceField(label=_("Template"), choices=[]) + + def __init__(self, *args, **kwargs): + super(DocumentGenerationAdminActForm, self).__init__(*args, **kwargs) + self.fields['document_template'].choices = DocumentTemplate.get_tuples( + dct={'associated_object_name': + 'archaeological_operations.models.AdministrativeAct'}) + + def save(self, object_pk): + try: + c_object = self._associated_model.objects.get(pk=object_pk) + except self._associated_model.DoesNotExist: + return + try: + template = DocumentTemplate.objects.get( + pk=self.cleaned_data.get('document_template')) + except DocumentTemplate.DoesNotExist: + return + return template.publish(c_object) + class AdministrativeActFileSelect(TableSelect): associated_file__towns = get_town_field() act_type = forms.ChoiceField(label=_("Act type"), choices=[]) diff --git a/archaeological_files/ishtar_menu.py b/archaeological_files/ishtar_menu.py index 6655a381c..87958848a 100644 --- a/archaeological_files/ishtar_menu.py +++ b/archaeological_files/ishtar_menu.py @@ -1,6 +1,6 @@ #!/usr/bin/env python # -*- coding: utf-8 -*- -# Copyright (C) 2010-2012 Étienne Loks <etienne.loks_AT_peacefrogsDOTnet> +# Copyright (C) 2010-2013 É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 @@ -60,6 +60,10 @@ MENU_SECTIONS = [ _(u"Deletion"), model=AdministrativeAct, access_controls=['delete_file', 'delete_own_file']), + MenuItem('file_administrativeact_document', + _(u"Documents"), + model=AdministrativeAct, + access_controls=['change_file', 'change_own_file']), ],), ]), ), diff --git a/archaeological_files/models.py b/archaeological_files/models.py index 0095dd13b..2b739fb1c 100644 --- a/archaeological_files/models.py +++ b/archaeological_files/models.py @@ -29,7 +29,7 @@ from ishtar_common.utils import cached_label_changed from ishtar_common.models import GeneralType, BaseHistorizedItem, \ HistoricalRecords, OwnPerms, Person, Organization, Department, Town, \ - Dashboard, IshtarUser + Dashboard, IshtarUser, ValueGetter class FileType(GeneralType): class Meta: @@ -53,14 +53,14 @@ class PermitType(GeneralType): ordering = ('label',) if settings.COUNTRY == 'fr': - class SaisineType(GeneralType): + class SaisineType(GeneralType, ValueGetter): delay = models.IntegerField(_(u"Delay (in days)")) class Meta: verbose_name = u"Type Saisine" verbose_name_plural = u"Types Saisine" ordering = ('label',) -class File(BaseHistorizedItem, OwnPerms): +class File(BaseHistorizedItem, OwnPerms, ValueGetter): TABLE_COLS = ['numeric_reference', 'year', 'internal_reference', 'file_type', 'saisine_type', 'towns', ] year = models.IntegerField(_(u"Year"), @@ -139,6 +139,14 @@ class File(BaseHistorizedItem, OwnPerms): def get_total_number(cls): return cls.objects.count() + def get_values(self, prefix=''): + values = super(File, self).get_values(prefix=prefix) + values['adminact_associated_file_towns_count'] = unicode( + self.towns.count()) + values['adminact_associated_file_towns'] = u", ".join( + [unicode(town)for town in self.towns.all()]) + return values + def __unicode__(self): if self.cached_label: return self.cached_label diff --git a/archaeological_files/templates/ishtar/administrativeact_document.html b/archaeological_files/templates/ishtar/administrativeact_document.html new file mode 100644 index 000000000..cdb2b45be --- /dev/null +++ b/archaeological_files/templates/ishtar/administrativeact_document.html @@ -0,0 +1,23 @@ +{% extends "base.html" %}Q +{% load i18n %} +{% block extra_head %} +{{search_form.media}} +{{ template_form.media }} +{% endblock %} + +{% block content %} +<h2>{% trans "Document generation" %}</h2> +<form action="." method="post">{% csrf_token %} +<div class='form'> +<table> +{{ search_form.as_table }} +</table> +<h4>{% trans "Choose the type of document" %}</h4> +<table> +{{ template_form }} +</table> +<input type="submit" id="submit_form" name='validate' value="{% trans "Generate" %}"/> +</div> +</form> +{% endblock %} + diff --git a/archaeological_files/urls.py b/archaeological_files/urls.py index 72a6e2df7..9d1f4f56f 100644 --- a/archaeological_files/urls.py +++ b/archaeological_files/urls.py @@ -1,6 +1,6 @@ #!/usr/bin/env python # -*- coding: utf-8 -*- -# Copyright (C) 2010-2012 Étienne Loks <etienne.loks_AT_peacefrogsDOTnet> +# Copyright (C) 2010-2013 É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 @@ -59,5 +59,8 @@ urlpatterns += patterns('archaeological_files.views', 'show_file', name='show-historized-file'), url(r'revert-file/(?P<pk>.+)/(?P<date>.+)$', 'revert_file', name='revert-file'), - url(r'dashboard_file/$', 'dashboard_file', name='dashboard-file') + url(r'dashboard_file/$', 'dashboard_file', name='dashboard-file'), + url(r'file_administrativeact_document/$', + 'file_administrativeactfile_document', + name='administrativeact_document'), ) diff --git a/archaeological_files/views.py b/archaeological_files/views.py index 407bbe821..e55368e62 100644 --- a/archaeological_files/views.py +++ b/archaeological_files/views.py @@ -18,10 +18,12 @@ # See the file COPYING for details. import json +import os from django.db.models import Q from django.http import HttpResponse from django.shortcuts import render_to_response +from django.template.defaultfilters import slugify from django.utils.translation import ugettext_lazy as _ from ishtar_common.views import get_item, show_item, revert_item @@ -156,3 +158,29 @@ file_administrativeactfile_deletion_wizard = \ label=_(u"File: administrative act deletion"), url_name='file_administrativeactfile_deletion',) +def file_administrativeactfile_document(request): + dct = {} + if request.POST: + dct['search_form'] = AdministrativeActFileFormSelection(request.POST) + dct['template_form'] = DocumentGenerationAdminActForm(request.POST) + if dct['search_form'].is_valid() and dct['template_form'].is_valid(): + doc = dct['template_form'].save( + dct['search_form'].cleaned_data.get('pk')) + if doc: + MIMES = {'odt':'application/vnd.oasis.opendocument.text', + 'ods':'application/vnd.oasis.opendocument.spreadsheet'} + ext = doc.split('.')[-1] + doc_name = slugify(doc.split(os.path.sep)[-1][:-len(ext)])+ "."\ + + ext + mimetype = 'text/csv' + if ext in MIMES: + mimetype = MIMES[ext] + response = HttpResponse(open(doc), mimetype=mimetype) + response['Content-Disposition'] = 'attachment; filename=%s' % \ + doc_name + return response + else: + dct['search_form'] = AdministrativeActFileFormSelection() + dct['template_form'] = DocumentGenerationAdminActForm() + return render_to_response('ishtar/administrativeact_document.html', dct, + context_instance=RequestContext(request)) diff --git a/archaeological_operations/models.py b/archaeological_operations/models.py index 3d6eaa15a..e846f15f8 100644 --- a/archaeological_operations/models.py +++ b/archaeological_operations/models.py @@ -31,7 +31,7 @@ from ishtar_common.utils import cached_label_changed from ishtar_common.models import GeneralType, BaseHistorizedItem, \ HistoricalRecords, LightHistorizedItem, OwnPerms, Department, Source,\ - Person, Organization, Town, Dashboard, IshtarUser + Person, Organization, Town, Dashboard, IshtarUser, ValueGetter FILES_AVAILABLE = 'archaeological_files' in settings.INSTALLED_APPS if FILES_AVAILABLE: from archaeological_files.models import File @@ -347,7 +347,7 @@ class ActType(GeneralType): verbose_name_plural = _(u"Act types") ordering = ('label',) -class AdministrativeAct(BaseHistorizedItem, OwnPerms): +class AdministrativeAct(BaseHistorizedItem, OwnPerms, ValueGetter): TABLE_COLS = ['act_type', 'associated_file', 'operation', 'associated_file.towns', 'operation.towns'] TABLE_COLS_FILE = ['act_type', 'associated_file', 'associated_file.towns',] @@ -375,6 +375,7 @@ class AdministrativeAct(BaseHistorizedItem, OwnPerms): ref_sra = models.CharField(u"Référence SRA", max_length=15, blank=True, null=True) history = HistoricalRecords() + _prefix = 'adminact_' class Meta: verbose_name = _(u"Administrative act") diff --git a/ishtar_common/admin.py b/ishtar_common/admin.py index 093d137ae..320136aec 100644 --- a/ishtar_common/admin.py +++ b/ishtar_common/admin.py @@ -88,7 +88,7 @@ general_models = [models.OrganizationType, models.SourceType, models.AuthorType] for model in general_models: admin.site.register(model, GeneralTypeAdmin) -basic_models = [models.IshtarUser] +basic_models = [models.IshtarUser, models.DocumentTemplate] if settings.COUNTRY == 'fr': basic_models += [models.Arrondissement, models.Canton] diff --git a/ishtar_common/migrations/0005_auto__add_documenttemplate.py b/ishtar_common/migrations/0005_auto__add_documenttemplate.py new file mode 100644 index 000000000..383ee5a32 --- /dev/null +++ b/ishtar_common/migrations/0005_auto__add_documenttemplate.py @@ -0,0 +1,209 @@ +# -*- 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 model 'DocumentTemplate' + db.create_table('ishtar_common_documenttemplate', ( + ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)), + ('name', self.gf('django.db.models.fields.CharField')(max_length=100)), + ('template', self.gf('django.db.models.fields.files.FileField')(max_length=100)), + ('associated_object_name', self.gf('django.db.models.fields.CharField')(max_length=100)), + ('available', self.gf('django.db.models.fields.BooleanField')(default=True)), + )) + db.send_create_signal('ishtar_common', ['DocumentTemplate']) + + + def backwards(self, orm): + # Deleting model 'DocumentTemplate' + db.delete_table('ishtar_common_documenttemplate') + + + 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': 'False'}), + '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': '30'}) + }, + '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'}) + }, + 'ishtar_common.documenttemplate': { + 'Meta': {'ordering': "['associated_object_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.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'}), + 'country': ('django.db.models.fields.CharField', [], {'max_length': '30', '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'}), + 'mobile_phone': ('django.db.models.fields.CharField', [], {'max_length': '18', 'null': 'True', 'blank': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + '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'}), + 'postal_code': ('django.db.models.fields.CharField', [], {'max_length': '10', 'null': 'True', 'blank': 'True'}), + 'town': ('django.db.models.fields.CharField', [], {'max_length': '30', 'null': 'True', 'blank': 'True'}) + }, + '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.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'}), + 'country': ('django.db.models.fields.CharField', [], {'max_length': '30', 'null': 'True', 'blank': 'True'}), + 'history_modifier': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'+'", 'to': "orm['auth.User']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'mobile_phone': ('django.db.models.fields.CharField', [], {'max_length': '18', 'null': 'True', 'blank': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + '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'}), + 'postal_code': ('django.db.models.fields.CharField', [], {'max_length': '10', 'null': 'True', 'blank': 'True'}), + 'town': ('django.db.models.fields.CharField', [], {'max_length': '30', 'null': 'True', 'blank': 'True'}) + }, + 'ishtar_common.organizationtype': { + 'Meta': {'ordering': "('label',)", 'object_name': 'OrganizationType'}, + 'available': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + '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': '30'}) + }, + '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'}), + 'attached_to': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['ishtar_common.Organization']", 'null': 'True', 'blank': 'True'}), + 'country': ('django.db.models.fields.CharField', [], {'max_length': '30', 'null': 'True', 'blank': 'True'}), + 'email': ('django.db.models.fields.CharField', [], {'max_length': '40', 'null': 'True', 'blank': 'True'}), + 'history_modifier': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'+'", 'to': "orm['auth.User']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'mobile_phone': ('django.db.models.fields.CharField', [], {'max_length': '18', 'null': 'True', 'blank': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '30'}), + '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'}), + 'postal_code': ('django.db.models.fields.CharField', [], {'max_length': '10', 'null': 'True', 'blank': 'True'}), + 'surname': ('django.db.models.fields.CharField', [], {'max_length': '20', 'null': 'True', 'blank': 'True'}), + 'title': ('django.db.models.fields.CharField', [], {'max_length': '2'}), + 'town': ('django.db.models.fields.CharField', [], {'max_length': '30', 'null': 'True', 'blank': 'True'}) + }, + 'ishtar_common.persontype': { + 'Meta': {'ordering': "('label',)", 'object_name': 'PersonType'}, + 'available': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'comment': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), + 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'symmetrical': 'False'}), + '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': '30'}) + }, + 'ishtar_common.sourcetype': { + 'Meta': {'object_name': 'SourceType'}, + 'available': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + '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': '30'}) + }, + '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'}) + }, + 'ishtar_common.wizard': { + 'Meta': {'ordering': "['url_name']", 'object_name': 'Wizard'}, + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'url_name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '128'}) + }, + 'ishtar_common.wizardstep': { + 'Meta': {'ordering': "['wizard', 'order']", 'object_name': 'WizardStep'}, + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '128'}), + 'order': ('django.db.models.fields.IntegerField', [], {}), + 'url_name': ('django.db.models.fields.CharField', [], {'max_length': '128'}), + 'wizard': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['ishtar_common.Wizard']"}) + } + } + + complete_apps = ['ishtar_common']
\ No newline at end of file diff --git a/ishtar_common/models.py b/ishtar_common/models.py index 89fbd1589..8d153f120 100644 --- a/ishtar_common/models.py +++ b/ishtar_common/models.py @@ -24,6 +24,7 @@ from cStringIO import StringIO import datetime from PIL import Image import os +import tempfile from django.conf import settings from django.core.exceptions import ObjectDoesNotExist, ValidationError @@ -43,6 +44,8 @@ from django.contrib import admin from simple_history.models import HistoricalRecords as BaseHistoricalRecords +from ooo_replace import ooo_replace + def post_save_user(sender, **kwargs): user = kwargs['instance'] ishtaruser = None @@ -57,6 +60,22 @@ def post_save_user(sender, **kwargs): txt_idx='administrator')) post_save.connect(post_save_user, sender=User) +class ValueGetter(object): + _prefix = "" + def get_values(self, prefix=''): + if not prefix: + prefix = self._prefix + values = {} + for field_name in self._meta.get_all_field_names(): + if not hasattr(self, field_name): + continue + value = getattr(self, field_name) + if hasattr(value, 'get_values'): + values.update(value.get_values(prefix + field_name + '_')) + else: + values[prefix + field_name] = value + return values + class HistoricalRecords(BaseHistoricalRecords): def create_historical_record(self, instance, type): history_modifier = getattr(instance, 'history_modifier', None) @@ -592,6 +611,41 @@ class Dashboard: if vals[v] == mx: return v +class DocumentTemplate(models.Model): + CLASSNAMES = (('archaeological_operations.models.AdministrativeAct', + _(u"Administrative Act")),) + name = models.CharField(_(u"Name"), max_length=100) + template = models.FileField(_(u"Template"), upload_to="upload/templates/") + associated_object_name = models.CharField(_(u"Associated object"), + max_length=100, choices=CLASSNAMES) + available = models.BooleanField(_(u"Available"), default=True) + + class Meta: + verbose_name = _(u"Document template") + verbose_name_plural = _(u"Document templates") + ordering = ['associated_object_name'] + + def __unicode__(self): + return self.name + + @classmethod + def get_tuples(cls, dct={}, empty_first=True): + dct['available'] = True + if empty_first: + yield ('', '--') + items = cls.objects.filter(**dct) + for item in items.order_by(*cls._meta.ordering).all(): + yield (item.pk, _(unicode(item))) + + def publish(self, c_object): + tempdir = tempfile.mkdtemp("-ishtardocs") + output_name = tempdir + os.path.sep + \ + self.name.replace(' ', '_').lower() + u'-' +\ + datetime.date.today().strftime('%Y-%m-%d') +\ + u"." + self.template.name.split('.')[-1] + missing = ooo_replace(self.template, output_name, c_object.get_values()) + return output_name + class Department(models.Model): label = models.CharField(_(u"Label"), max_length=30) number = models.CharField(_(u"Number"), unique=True, max_length=3) @@ -654,7 +708,8 @@ class PersonType(GeneralType): verbose_name_plural = _(u"Person types") ordering = ('label',) -class Person(Address, OwnPerms) : +class Person(Address, OwnPerms, ValueGetter) : + _prefix = 'person_' TYPE = (('Mr', _(u'Mr')), ('Ms', _(u'Miss')), ('Md', _(u'Mrs')), diff --git a/ishtar_common/ooo_replace.py b/ishtar_common/ooo_replace.py new file mode 100644 index 000000000..63b983b7b --- /dev/null +++ b/ishtar_common/ooo_replace.py @@ -0,0 +1,102 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- +# Copyright (C) 2013 Étienne Loks <etienne.loks_AT_peacefrogsDOTnet> + +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. + +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. + +# See the file COPYING for details. + + +from zipfile import ZipFile, ZIP_DEFLATED +from cStringIO import StringIO +from xml.etree.cElementTree import ElementTree, fromstring + +OOO_NS = "{urn:oasis:names:tc:opendocument:xmlns:text:1.0}" + +def _set_value_from_formula(value, context): + value = value.strip() + if value.startswith("ooow:") and len(value) >= 5: + value = value[5:] + if value.startswith('"') and value.endswith('"') and len(value) > 1: + value = value[1:-1] + elif value in context: + value = unicode(context[value]) + else: + value = None + return value + +def _parse_condition(condition, context): + # parse only == and != operator + operator = "" + if "!=" in condition: + operator = "!=" + elif "==" in condition: + operator = "==" + else: + return + var1, var2 = condition.split(operator) + var1 = _set_value_from_formula(var1, context) + var2 = _set_value_from_formula(var2, context) + res = var1 == var2 + if operator == '!=': + res = not res + return res + +def _format_value(value, default_value): + value = unicode(value) if value else default_value + #if hasattr(value, 'strftime'): + # value = value.strftime() + return value + +def ooo_replace(infile, outfile, context, default_value=''): + inzip = ZipFile(infile, 'r', ZIP_DEFLATED) + outzip = ZipFile(outfile, 'w', ZIP_DEFLATED) + content = ElementTree(fromstring(inzip.read('content.xml'))) + missing_keys = set() + for xp in ('variable-set', 'variable-get'): + for p in content.findall(".//"+OOO_NS+xp): + name = p.get(OOO_NS+"name") + if name in context: + value = context[name] + p.text = _format_value(value, default_value) + else: + if default_value != None: + p.text = default_value + missing_keys.add(name) + for p in content.findall(".//"+OOO_NS+"conditional-text"): + condition = p.get(OOO_NS+"condition") + res = 'true' if _parse_condition(condition, context) else 'false' + value = p.get(OOO_NS+'string-value-if-' + res) + value = _format_value(value, default_value) + if value.strip() in context: + value = context[value.strip()] + p.text = value + for f in inzip.infolist(): + if f.filename == 'content.xml': + s = StringIO() + content.write(s) + outzip.writestr('content.xml', s.getvalue()) + else: + outzip.writestr(f, inzip.read(f.filename)) + inzip.close() + outzip.close() + return missing_keys + +if __name__ == '__main__': + infile = "../archaeological_files/tests/AR_dossier_DRAC_modele_ishtar_1-MOD.odt" + outfile = "../archaeological_files/tests/AR_dossier_DRAC_modele_ishtar-test.odt" + rep = {"file_incharge_surname":u"Yann", + "file_incharge_name":u"Le Jeune", + "fileact_ref":u"ref"} + ooo_replace(infile, outfile, rep, default_value="") diff --git a/ishtar_common/templates/ishtar/wizard/wizard_list_search_result.html b/ishtar_common/templates/ishtar/wizard/wizard_list_search_result.html index c6be97b64..aca1798d9 100644 --- a/ishtar_common/templates/ishtar/wizard/wizard_list_search_result.html +++ b/ishtar_common/templates/ishtar/wizard/wizard_list_search_result.html @@ -3,7 +3,7 @@ {% block content %} <div class='form'> - <p>{% trans {%trans "PLease note that the file must be processed before :"%} %} {% calculated_deadline %} </p> + <p>{% trans {%trans "Please note that the file must be processed before :"%} %} {% calculated_deadline %} </p> <p>{% trans {%trans "Item successfully saved"%} %}</p> <p>{% trans "You have saved the following informations:" %}</p> diff --git a/ishtar_common/views.py b/ishtar_common/views.py index 46ea938de..31f5f688b 100644 --- a/ishtar_common/views.py +++ b/ishtar_common/views.py @@ -608,9 +608,6 @@ def action(request, action_slug, obj_id=None, *args, **kwargs): globals_dct = globals() if action_slug in globals_dct: return globals_dct[action_slug](request, dct, obj_id, *args, **kwargs) - elif hasattr(ishtar_forms, action_slug + "_wizard"): - return getattr(ishtar_forms, action_slug+"_wizard")(request, *args, - **kwargs) return render_to_response('index.html', dct, context_instance=RequestContext(request)) |