diff options
Diffstat (limited to 'ishtar_common')
| -rw-r--r-- | ishtar_common/locale/fr/LC_MESSAGES/django.po | 261 | ||||
| -rw-r--r-- | ishtar_common/models.py | 90 | ||||
| -rw-r--r-- | ishtar_common/static/media/style.css | 16 | ||||
| -rw-r--r-- | ishtar_common/templates/ishtar/dashboards/dashboard_main.html | 3 | ||||
| -rw-r--r-- | ishtar_common/templates/ishtar/dashboards/dashboard_main_detail.html | 96 | ||||
| -rw-r--r-- | ishtar_common/templatetags/date_formating.py | 19 | ||||
| -rw-r--r-- | ishtar_common/views.py | 28 | 
7 files changed, 343 insertions, 170 deletions
| diff --git a/ishtar_common/locale/fr/LC_MESSAGES/django.po b/ishtar_common/locale/fr/LC_MESSAGES/django.po index 5291ab4ff..923126974 100644 --- a/ishtar_common/locale/fr/LC_MESSAGES/django.po +++ b/ishtar_common/locale/fr/LC_MESSAGES/django.po @@ -8,7 +8,7 @@ msgid ""  msgstr ""  "Project-Id-Version: alpha\n"  "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2014-10-15 01:16+0200\n" +"POT-Creation-Date: 2014-10-21 00:43+0200\n"  "PO-Revision-Date: 2010-12-09\n"  "Last-Translator: Étienne Loks <etienne.loks at peacefrogs net>\n"  "Language-Team: \n" @@ -54,8 +54,7 @@ msgstr "Confirmation"  #: forms.py:80  msgid "Are you sure you want to delete?" -msgstr "" -"Êtes vous sûr de vouloir supprimer ?" +msgstr "Êtes vous sûr de vouloir supprimer ?"  #: forms.py:88  msgid "There are identical items." @@ -73,12 +72,12 @@ msgstr "Vous devez sélectionner un élément."  msgid "Add a new item"  msgstr "Ajouter un nouvel élément" -#: forms.py:164 models.py:672 +#: forms.py:164 models.py:739  msgid "Template"  msgstr "Patron"  #: forms_common.py:42 forms_common.py:104 forms_common.py:185 -#: forms_common.py:190 models.py:721 models.py:976 +#: forms_common.py:190 models.py:788 models.py:1043  #: templates/ishtar/sheet_organization.html:17  #: templates/ishtar/sheet_person.html:20 templates/ishtar/sheet_person.html:30  msgid "Town" @@ -102,68 +101,68 @@ msgstr ""  "<p class='example'>Par exemple tapez « saint denis 93 » pour obtenir la "  "commune Saint-Denis dans le département français de Seine-Saint-Denis.</p>" -#: forms_common.py:56 forms_common.py:354 ishtar_menu.py:30 models.py:794 -#: models.py:861 models.py:901 templates/ishtar/sheet_person.html:6 +#: forms_common.py:56 forms_common.py:354 ishtar_menu.py:30 models.py:861 +#: models.py:928 models.py:968 templates/ishtar/sheet_person.html:6  msgid "Person"  msgstr "Individu" -#: forms_common.py:92 forms_common.py:152 ishtar_menu.py:42 models.py:746 +#: forms_common.py:92 forms_common.py:152 ishtar_menu.py:42 models.py:813  #: templates/ishtar/sheet_organization.html:6  msgid "Organization"  msgstr "Organisation"  #: forms_common.py:94 forms_common.py:130 forms_common.py:148 -#: forms_common.py:177 models.py:671 models.py:741 models.py:787 models.py:962 -#: templates/ishtar/sheet_organization.html:12 +#: forms_common.py:177 models.py:738 models.py:808 models.py:854 +#: models.py:1029 templates/ishtar/sheet_organization.html:12  #: templates/ishtar/sheet_organization.html:25  #: templates/ishtar/sheet_person.html:12 templates/ishtar/sheet_person.html:26  msgid "Name"  msgstr "Nom" -#: forms_common.py:96 models.py:735 +#: forms_common.py:96 models.py:802  msgid "Organization type"  msgstr "Type d'organisation" -#: forms_common.py:98 forms_common.py:179 models.py:716 +#: forms_common.py:98 forms_common.py:179 models.py:783  #: templates/ishtar/sheet_organization.html:14  #: templates/ishtar/sheet_person.html:17 templates/ishtar/sheet_person.html:27  msgid "Address"  msgstr "Adresse" -#: forms_common.py:100 forms_common.py:181 models.py:717 +#: forms_common.py:100 forms_common.py:181 models.py:784  #: templates/ishtar/sheet_organization.html:15  #: templates/ishtar/sheet_person.html:18 templates/ishtar/sheet_person.html:28  msgid "Address complement"  msgstr "Complément d'adresse" -#: forms_common.py:102 forms_common.py:183 models.py:719 +#: forms_common.py:102 forms_common.py:183 models.py:786  #: templates/ishtar/sheet_organization.html:16  #: templates/ishtar/sheet_person.html:19 templates/ishtar/sheet_person.html:29  msgid "Postal code"  msgstr "Code postal" -#: forms_common.py:105 forms_common.py:186 models.py:722 +#: forms_common.py:105 forms_common.py:186 models.py:789  msgid "Country"  msgstr "Pays"  #: forms_common.py:107 forms_common.py:150 forms_common.py:188 -#: forms_common.py:240 models.py:727 templates/ishtar/sheet_person.html:15 +#: forms_common.py:240 models.py:794 templates/ishtar/sheet_person.html:15  msgid "Email"  msgstr "Courriel" -#: forms_common.py:108 forms_common.py:189 models.py:724 +#: forms_common.py:108 forms_common.py:189 models.py:791  #: templates/ishtar/sheet_organization.html:18  #: templates/ishtar/sheet_person.html:21 templates/ishtar/sheet_person.html:31  msgid "Phone"  msgstr "Téléphone" -#: forms_common.py:109 models.py:725 +#: forms_common.py:109 models.py:792  #: templates/ishtar/sheet_organization.html:19  #: templates/ishtar/sheet_person.html:22 templates/ishtar/sheet_person.html:32  msgid "Mobile phone"  msgstr "Téléphone portable" -#: forms_common.py:131 forms_common.py:151 models.py:743 models.py:925 +#: forms_common.py:131 forms_common.py:151 models.py:810 models.py:992  #: templates/sheet_ope.html:85 templates/sheet_ope.html.py:105  #: templates/sheet_ope.html:126 templates/ishtar/sheet_organization.html:27  #: templates/ishtar/sheet_person.html:42 templates/ishtar/sheet_person.html:95 @@ -175,7 +174,7 @@ msgstr "Type"  msgid "Organization search"  msgstr "Recherche d'organisations" -#: forms_common.py:149 forms_common.py:175 models.py:785 +#: forms_common.py:149 forms_common.py:175 models.py:852  #: templates/ishtar/sheet_organization.html:26  #: templates/ishtar/sheet_person.html:13  msgid "Surname" @@ -189,7 +188,7 @@ msgstr "Recherche d'individus"  msgid "Identity"  msgstr "Identité" -#: forms_common.py:174 forms_common.py:311 models.py:784 models.py:924 +#: forms_common.py:174 forms_common.py:311 models.py:851 models.py:991  #: templates/sheet_ope.html:104 templates/ishtar/sheet_person.html:94  #: templates/ishtar/blocks/window_tables/documents.html:5  msgid "Title" @@ -199,7 +198,7 @@ msgstr "Titre"  msgid "Current organization"  msgstr "Organisation actuelle" -#: forms_common.py:199 forms_common.py:222 forms_common.py:225 models.py:771 +#: forms_common.py:199 forms_common.py:222 forms_common.py:225 models.py:838  msgid "Person type"  msgstr "Type d'individu" @@ -231,7 +230,7 @@ msgstr "Ce nom d'utilisateur existe déjà."  msgid "Send the new password by email?"  msgstr "Envoyer le nouveau mot de passe par courriel ?" -#: forms_common.py:291 forms_common.py:303 models.py:977 +#: forms_common.py:291 forms_common.py:303 models.py:1044  #: templates/ishtar/sheet_person.html:72  msgid "Towns"  msgstr "Communes" @@ -244,23 +243,23 @@ msgstr "Il y a des communes identiques."  msgid "Documentation informations"  msgstr "Information sur le document" -#: forms_common.py:313 forms_common.py:332 models.py:920 +#: forms_common.py:313 forms_common.py:332 models.py:987  msgid "Source type"  msgstr "Type de source" -#: forms_common.py:315 models.py:929 +#: forms_common.py:315 models.py:996  msgid "Numerical ressource (web address)"  msgstr "Ressource numérique (adresse web)" -#: forms_common.py:316 models.py:931 +#: forms_common.py:316 models.py:998  msgid "Receipt date"  msgstr "Date de réception" -#: forms_common.py:318 models.py:933 +#: forms_common.py:318 models.py:1000  msgid "Creation date"  msgstr "Date de création" -#: forms_common.py:329 forms_common.py:348 forms_common.py:379 models.py:906 +#: forms_common.py:329 forms_common.py:348 forms_common.py:379 models.py:973  #: templates/ishtar/wizard/wizard_person_deletion.html:124  msgid "Author"  msgstr "Auteur" @@ -269,7 +268,7 @@ msgstr "Auteur"  msgid "Would you like to delete this documentation?"  msgstr "Voulez vous supprimer ce document ?" -#: forms_common.py:355 models.py:897 models.py:903 +#: forms_common.py:355 models.py:964 models.py:970  msgid "Author type"  msgstr "Type d'auteur" @@ -281,7 +280,7 @@ msgstr "Sélection d'auteur"  msgid "There are identical authors."  msgstr "Il y a des auteurs identiques." -#: forms_common.py:389 models.py:907 models.py:926 +#: forms_common.py:389 models.py:974 models.py:993  #: templates/sheet_ope.html:106  #: templates/ishtar/blocks/window_tables/documents.html:7  msgid "Authors" @@ -303,7 +302,7 @@ msgstr "Modification"  msgid "Delete"  msgstr "Supprimer" -#: ishtar_menu.py:57 views.py:119 +#: ishtar_menu.py:57 views.py:121  msgid "Account management"  msgstr "Gestion des comptes" @@ -319,7 +318,7 @@ msgstr "Un élément sélectionné n'est pas valide."  msgid "This item already exist."  msgstr "Cet élément existe déjà." -#: models.py:202 models.py:548 models.py:704 +#: models.py:202 models.py:548 models.py:771  msgid "Label"  msgstr "Libellé" @@ -331,7 +330,7 @@ msgstr "Identifiant textuel"  msgid "Comment"  msgstr "Commentaire" -#: models.py:206 models.py:675 +#: models.py:206 models.py:742  msgid "Available"  msgstr "Disponible" @@ -359,161 +358,154 @@ msgstr "Ordre"  msgid "Wizard step"  msgstr "Étape de l'assistant" -#: models.py:596 templates/sheet_ope.html:61 templates/sheet_ope.html.py:83 -#: templates/ishtar/sheet_person.html:39 templates/ishtar/sheet_person.html:68 -#: templates/ishtar/sheet_person.html:93 -#: templates/ishtar/dashboards/dashboard_main_detail.html:94 -msgid "Year" -msgstr "Année" +#: models.py:643 models.py:674 +msgid "Total" +msgstr "Total" -#: models.py:599 models.py:608 models.py:705 -#: templates/ishtar/dashboards/dashboard_main_detail.html:105 +#: models.py:650 models.py:772 +#: templates/ishtar/dashboards/dashboard_main_detail.html:135  #: templates/ishtar/dashboards/dashboard_main_detail_users.html:26  msgid "Number"  msgstr "Nombre" -#: models.py:607 templates/ishtar/dashboards/dashboard_main_detail.html:100 -msgid "Month" -msgstr "Mois" - -#: models.py:670 +#: models.py:737  msgid "Administrative Act"  msgstr "Acte administratif" -#: models.py:673 +#: models.py:740  msgid "Associated object"  msgstr "Objet associé" -#: models.py:678 +#: models.py:745  msgid "Document template"  msgstr "Patron de document" -#: models.py:679 +#: models.py:746  msgid "Document templates"  msgstr "Patrons de documents" -#: models.py:708 +#: models.py:775  msgid "Department"  msgstr "Département" -#: models.py:709 +#: models.py:776  msgid "Departments"  msgstr "Départements" -#: models.py:736 +#: models.py:803  msgid "Organization types"  msgstr "Types d'organisation" -#: models.py:747 +#: models.py:814  msgid "Organizations"  msgstr "Organisations" -#: models.py:749 +#: models.py:816  msgid "Can view all Organization"  msgstr "Peut voir toutes les Organisations" -#: models.py:750 +#: models.py:817  msgid "Can view own Organization"  msgstr "Peut voir sa propre Organisation" -#: models.py:751 +#: models.py:818  msgid "Can add own Organization"  msgstr "Peut ajouter sa propre Organisation" -#: models.py:752 +#: models.py:819  msgid "Can change own Organization"  msgstr "Peut changer sa propre Organisation" -#: models.py:753 +#: models.py:820  msgid "Can delete own Organization"  msgstr "Peut supprimer sa propre Organisation" -#: models.py:768 +#: models.py:835  msgid "Groups"  msgstr "Groupes" -#: models.py:772 +#: models.py:839  msgid "Person types"  msgstr "Types d'individu" -#: models.py:777 +#: models.py:844  msgid "Mr"  msgstr "M." -#: models.py:778 +#: models.py:845  msgid "Miss"  msgstr "Mlle" -#: models.py:779 +#: models.py:846  msgid "Mrs"  msgstr "Mme" -#: models.py:780 +#: models.py:847  msgid "Doctor"  msgstr "Dr." -#: models.py:788 models.py:823 +#: models.py:855 models.py:890  msgid "Types"  msgstr "Types" -#: models.py:791 +#: models.py:858  msgid "Is attached to"  msgstr "Est rattaché à" -#: models.py:795 +#: models.py:862  msgid "Persons"  msgstr "Individus" -#: models.py:797 +#: models.py:864  msgid "Can view all Person"  msgstr "Peut voir toutes les Personnes" -#: models.py:798 +#: models.py:865  msgid "Can view own Person"  msgstr "Peut voir sa propre Personne" -#: models.py:799 +#: models.py:866  msgid "Can add own Person"  msgstr "Peut ajouter sa propre Personne" -#: models.py:800 +#: models.py:867  msgid "Can change own Person"  msgstr "Peut changer sa propre Personne" -#: models.py:801 +#: models.py:868  msgid "Can delete own Person"  msgstr "Peut supprimer sa propre Personne" -#: models.py:865 +#: models.py:932  msgid "Ishtar user"  msgstr "Utilisateur d'Ishtar" -#: models.py:866 +#: models.py:933  msgid "Ishtar users"  msgstr "Utilisateurs d'Ishtar" -#: models.py:898 +#: models.py:965  msgid "Author types"  msgstr "Types d'auteur" -#: models.py:921 +#: models.py:988  msgid "Source types"  msgstr "Types de source" -#: models.py:934 templates/ishtar/sheet_person.html:40 +#: models.py:1001 templates/ishtar/sheet_person.html:40  #: templates/ishtar/sheet_person.html:67  msgid "Ref."  msgstr "Réf." -#: models.py:936 +#: models.py:1003  msgid "Internal reference"  msgstr "Référence interne" -#: models.py:963 +#: models.py:1030  msgid "Surface (m²)"  msgstr "Area (m²)" -#: models.py:964 templates/sheet_ope.html:46 templates/sheet_ope.html.py:107 +#: models.py:1031 templates/sheet_ope.html:46 templates/sheet_ope.html.py:107  msgid "Localisation"  msgstr "Localisation" @@ -521,78 +513,78 @@ msgstr "Localisation"  msgid " (...)"  msgstr " (...)" -#: views.py:79 +#: views.py:81  msgid "New person"  msgstr "Nouvelle personne" -#: views.py:87 +#: views.py:89  msgid "Person modification"  msgstr "Modification d'une personne" -#: views.py:93 +#: views.py:95  msgid "Person deletion"  msgstr "Suppresion de personne" -#: views.py:99 +#: views.py:101  msgid "New organization"  msgstr "Nouvelle organisation" -#: views.py:106 +#: views.py:108  msgid "Organization modification"  msgstr "Modification d'une organisation" -#: views.py:112 +#: views.py:114  msgid "Organization deletion"  msgstr "Suppresion d'une organisation" -#: views.py:209 +#: views.py:211  msgid "True"  msgstr "Oui" -#: views.py:211 +#: views.py:213  msgid "False"  msgstr "Non" -#: views.py:434 templates/base.html:75 +#: views.py:436 templates/base.html:75  #: templates/ishtar/sheet_organization.html:35  #: templates/ishtar/sheet_person.html:57 templates/ishtar/sheet_person.html:83  msgid "Details"  msgstr "Détails" -#: views.py:644 views.py:691 +#: views.py:646 views.py:693  msgid "Operation not permitted."  msgstr "Opération non permise" -#: views.py:646 +#: views.py:648  #, python-format  msgid "New %s"  msgstr "Nouveau %s" -#: views.py:708 views.py:746 +#: views.py:710 views.py:766  msgid "Archaeological files"  msgstr "Dossiers archéologiques" -#: views.py:709 views.py:750 +#: views.py:711 views.py:770  msgid "Operations"  msgstr "Opérations" -#: views.py:711 views.py:755 +#: views.py:713 views.py:775  msgid "Context records"  msgstr "Unité d'Enregistrement" -#: views.py:713 views.py:760 +#: views.py:715 views.py:780  msgid "Finds"  msgstr "Mobilier" -#: widgets.py:322 +#: widgets.py:326  msgid "No results"  msgstr "Pas de résultats" -#: widgets.py:323 +#: widgets.py:327  msgid "Loading..."  msgstr "Chargement..." -#: widgets.py:324 +#: widgets.py:328  msgid "Remove"  msgstr "Enlever" @@ -848,6 +840,13 @@ msgstr "Parcelles associées"  msgid "Commune"  msgstr "Commune" +#: templates/sheet_ope.html:61 templates/sheet_ope.html.py:83 +#: templates/ishtar/sheet_person.html:39 templates/ishtar/sheet_person.html:68 +#: templates/ishtar/sheet_person.html:93 +#: templates/ishtar/dashboards/dashboard_main_detail.html:120 +msgid "Year" +msgstr "Année" +  #: templates/sheet_ope.html:62  msgid "Section"  msgstr "Section" @@ -1038,7 +1037,7 @@ msgstr "Pas de document associé à cette personne"  msgid "Related to"  msgstr "Associé à" -#: templates/ishtar/dashboards/dashboard_main.html:23 +#: templates/ishtar/dashboards/dashboard_main.html:26  msgid "Users"  msgstr "Utilisateurs" @@ -1046,72 +1045,88 @@ msgstr "Utilisateurs"  msgid "Numbers"  msgstr "Nombre" -#: templates/ishtar/dashboards/dashboard_main_detail.html:6 +#: templates/ishtar/dashboards/dashboard_main_detail.html:12 +msgid "Change" +msgstr "Modifier" + +#: templates/ishtar/dashboards/dashboard_main_detail.html:16  msgid "Total:"  msgstr "Total :" -#: templates/ishtar/dashboards/dashboard_main_detail.html:15 -msgid "Change" -msgstr "Modifier" +#: templates/ishtar/dashboards/dashboard_main_detail.html:19 +msgid "Draw rectangle on the graph to zoom. Double-click to reinitialize." +msgstr "Dessiner un rectangle sur le graphique pour zoomer. Double-cliquer pour réinitialiser." -#: templates/ishtar/dashboards/dashboard_main_detail.html:31 +#: templates/ishtar/dashboards/dashboard_main_detail.html:21 +msgid "Display as an image" +msgstr "Afficher comme une image" + +#: templates/ishtar/dashboards/dashboard_main_detail.html:24 +msgid "Right-click on this image to save it." +msgstr "Click droit sur l'image pour l'enregistrer." + +#: templates/ishtar/dashboards/dashboard_main_detail.html:39  msgid "By years"  msgstr "Par années" -#: templates/ishtar/dashboards/dashboard_main_detail.html:33 -#: templates/ishtar/dashboards/dashboard_main_detail.html:43 +#: templates/ishtar/dashboards/dashboard_main_detail.html:41 +#: templates/ishtar/dashboards/dashboard_main_detail.html:51  msgid "Average:"  msgstr "Moyenne :" -#: templates/ishtar/dashboards/dashboard_main_detail.html:34 -#: templates/ishtar/dashboards/dashboard_main_detail.html:44 +#: templates/ishtar/dashboards/dashboard_main_detail.html:42 +#: templates/ishtar/dashboards/dashboard_main_detail.html:52  msgid "Variance:"  msgstr "Variance :" -#: templates/ishtar/dashboards/dashboard_main_detail.html:35 -#: templates/ishtar/dashboards/dashboard_main_detail.html:45 +#: templates/ishtar/dashboards/dashboard_main_detail.html:43 +#: templates/ishtar/dashboards/dashboard_main_detail.html:53  msgid "Standard deviation:"  msgstr "Écart-type :" -#: templates/ishtar/dashboards/dashboard_main_detail.html:36 -#: templates/ishtar/dashboards/dashboard_main_detail.html:46 +#: templates/ishtar/dashboards/dashboard_main_detail.html:44 +#: templates/ishtar/dashboards/dashboard_main_detail.html:54  msgid "Median:"  msgstr "Médiane :" -#: templates/ishtar/dashboards/dashboard_main_detail.html:37 -#: templates/ishtar/dashboards/dashboard_main_detail.html:47 +#: templates/ishtar/dashboards/dashboard_main_detail.html:45 +#: templates/ishtar/dashboards/dashboard_main_detail.html:55  msgid "Mode:"  msgstr "Mode :" -#: templates/ishtar/dashboards/dashboard_main_detail.html:41 +#: templates/ishtar/dashboards/dashboard_main_detail.html:49  msgid "By operations"  msgstr "Par opérations" -#: templates/ishtar/dashboards/dashboard_main_detail.html:50 +#: templates/ishtar/dashboards/dashboard_main_detail.html:58  msgid "Created last"  msgstr "Derniers créés" -#: templates/ishtar/dashboards/dashboard_main_detail.html:53 +#: templates/ishtar/dashboards/dashboard_main_detail.html:61  msgid "Created"  msgstr "Créé" -#: templates/ishtar/dashboards/dashboard_main_detail.html:57 -#: templates/ishtar/dashboards/dashboard_main_detail.html:68 +#: templates/ishtar/dashboards/dashboard_main_detail.html:65 +#: templates/ishtar/dashboards/dashboard_main_detail.html:76  msgid "Show"  msgstr "Voir" -#: templates/ishtar/dashboards/dashboard_main_detail.html:61 +#: templates/ishtar/dashboards/dashboard_main_detail.html:69  msgid "Recent changes"  msgstr "Derniers changés" -#: templates/ishtar/dashboards/dashboard_main_detail.html:64 +#: templates/ishtar/dashboards/dashboard_main_detail.html:72  msgid "Modified"  msgstr "Modifier" -#: templates/ishtar/dashboards/dashboard_main_detail.html:116 +#: templates/ishtar/dashboards/dashboard_main_detail.html:108  msgid "No data for theses criteria."  msgstr "Pas de données pour ces critères." +#: templates/ishtar/dashboards/dashboard_main_detail.html:126 +msgid "Month" +msgstr "Mois" +  #: templates/ishtar/dashboards/dashboard_main_detail_users.html:26  msgid "User type"  msgstr "Type d'utilisateur" @@ -1294,7 +1309,7 @@ msgstr "La création du compte a échouée"  #: templates/registration/activation_complete.html:7  msgid "You may now login with your username and password." -msgstr +msgstr ""  "Vous pouvez maintenant vous identifier avec votre identifiant et votre mot "  "de passe" diff --git a/ishtar_common/models.py b/ishtar_common/models.py index 98f4addb7..3df557806 100644 --- a/ishtar_common/models.py +++ b/ishtar_common/models.py @@ -25,6 +25,7 @@ import datetime  from PIL import Image  import os  import tempfile +import copy  from django.conf import settings  from django.core.exceptions import ObjectDoesNotExist, ValidationError @@ -560,11 +561,53 @@ class UserDashboard:          self.types = types.annotate(number=Count('pk'))\                            .order_by('person__person_types') +class DashboardFormItem(object): +    @classmethod +    def get_periods(cls, slice='month', fltr={}, date_source='creation'): +        date_var = date_source + '_date' +        q = cls.objects.filter(**{date_var+'__isnull':False}) +        if fltr: +            q = q.filter(**fltr) +        if slice == 'year': +            return [res[date_var].year for res in list(q.values(date_var +                                ).annotate(Count("id")).order_by())] +        elif slice == 'month': +            return [(res[date_var].year, res[date_var].month) +                               for res in list(q.values(date_var +                                ).annotate(Count("id")).order_by())] +        return [] + +    @classmethod +    def get_by_year(cls, year, fltr={}, date_source='creation'): +        date_var = date_source + '_date' +        q = cls.objects.filter(**{date_var+'__isnull':False}) +        if fltr: +            q = q.filter(**fltr) +        return q.filter(**{date_var+'__year':year}).distinct('pk') + +    @classmethod +    def get_by_month(cls, year, month, fltr={}, date_source='creation'): +        date_var = date_source + '_date' +        q = cls.objects.filter(**{date_var+'__isnull':False}) +        if fltr: +            q = q.filter(**fltr) +        q = q.filter(**{date_var+'__year':year, date_var+'__month':month}) +        return q.distinct('pk') + +    @classmethod +    def get_total_number(cls, fltr={}): +        q = cls.objects +        if fltr: +            q = q.filter(**fltr) +        return q.distinct('pk').count() +  class Dashboard: -    def __init__(self, model, slice='year', date_source=None, fltr={}): +    def __init__(self, model, slice='year', date_source=None, show_detail=None, +                 fltr={}):          # don't provide date_source if it is not relevant          self.model = model          self.total_number = model.get_total_number(fltr) +        self.show_detail = show_detail          history_model = self.model.history.model          # last edited - created          self.recents, self.lasts = [], [] @@ -587,31 +630,50 @@ class Dashboard:                  obj.history_date = idx['hd']                  last_lst.append(obj)          # years -        base_kwargs = {'fltr':fltr} +        base_kwargs = {'fltr':fltr.copy()}          if date_source:              base_kwargs['date_source'] = date_source -        periods_kwargs = base_kwargs.copy() +        periods_kwargs = copy.deepcopy(base_kwargs)          periods_kwargs['slice'] = slice          self.periods = model.get_periods(**periods_kwargs)          self.periods = list(set(self.periods))          self.periods.sort()          if not self.total_number or not self.periods:              return +        kwargs_num = copy.deepcopy(base_kwargs) +        self.serie_labels = [_(u"Total")]          # numbers          if slice == 'year': -            self.values = [('year', _(u"Year"), reversed(self.periods))] -            self.numbers = [model.get_by_year(year, **base_kwargs).count() +            self.values = [('year', "", +                           list(reversed(self.periods)))] +            self.numbers = [model.get_by_year(year, **kwargs_num).count()                              for year in self.periods] -            self.values += [('number', _(u"Number"), reversed(self.numbers))] +            self.values += [('number', _(u"Number"), +                            list(reversed(self.numbers)))]          if slice == 'month': -            self.numbers = [model.get_by_month(*period, **base_kwargs).count() -                            for period in self.periods] -            periods = reversed(self.periods) -            self.periods = ["%d-%s-01" % (period[0], ('0'+str(period[1])) -                    if len(str(period[1])) == 1 else period[1]) -                            for period in periods] -            self.values = [('month', _(u"Month"), self.periods)] -            self.values += [('number', _(u"Number"), reversed(self.numbers))] +            periods = list(reversed(self.periods)) +            self.periods = ["%d-%s-01" % (p[0], ('0'+str(p[1])) +                    if len(str(p[1])) == 1 else p[1]) +                            for p in periods] +            self.values = [('month', "", self.periods)] +            if show_detail: +                for dpt in settings.ISHTAR_DPTS: +                    self.serie_labels.append(unicode(dpt)) +                    idx = 'number_' + unicode(dpt) +                    kwargs_num['fltr']["towns__numero_insee__startswith"] = \ +                                                                 unicode(dpt) +                    numbers = [model.get_by_month(*p.split('-')[:2], +                                                  **kwargs_num).count() +                                for p in self.periods] +                    self.values += [(idx, dpt, list(numbers))] +                # put "Total" at the end +                self.serie_labels.append(self.serie_labels.pop(0)) +            kwargs_num = base_kwargs.copy() +            self.numbers = [model.get_by_month(*p.split('-')[:2], +                                               **kwargs_num).count() +                            for p in self.periods] +            self.values += [('number', _(u"Total"), +                            list(self.numbers))]          # calculate          self.average = self.get_average()          self.variance = self.get_variance() diff --git a/ishtar_common/static/media/style.css b/ishtar_common/static/media/style.css index 341308926..3c2c429b1 100644 --- a/ishtar_common/static/media/style.css +++ b/ishtar_common/static/media/style.css @@ -560,6 +560,7 @@ table.confirm tr.spacer td:last-child{  .ui-tabs .ui-tabs-nav li.ui-tabs-active a{      background-color:#fff; +    color:#666;  }  .sheet{ @@ -669,6 +670,13 @@ table.confirm tr.spacer td:last-child{      padding:0.5em 1em;  } +.chart-img{ +    display:none; +} + +.chart-img-form{ +    margin-top:1em; +}  #window table th, .dashboard table.resume th{      background-color:#922; @@ -783,8 +791,7 @@ table.confirm tr.spacer td:last-child{      margin:0.3em;  } -p.alert{ -    color:#D14; +p.info-box, p.alert{      display:block;      font-style:italic;      width:670px; @@ -794,6 +801,11 @@ p.alert{      -moz-border-radius:8px;      -webkit-border-radius:8px;      border-radius:8px; +    font-size: 0.9em; +} + +p.alert{ +    color:#D14;      background-image:url(images/red_flag.png);      background-repeat:no-repeat;      background-position:left center; diff --git a/ishtar_common/templates/ishtar/dashboards/dashboard_main.html b/ishtar_common/templates/ishtar/dashboards/dashboard_main.html index 8822875b5..868f8a5c3 100644 --- a/ishtar_common/templates/ishtar/dashboards/dashboard_main.html +++ b/ishtar_common/templates/ishtar/dashboards/dashboard_main.html @@ -6,9 +6,12 @@  <script language="javascript" type="text/javascript" src="{{STATIC_URL}}js/jqplot/jquery.jqplot.min.js"></script>  <script language="javascript" type="text/javascript" src="{{STATIC_URL}}js/jqplot/plugins/jqplot.canvasTextRenderer.min.js"></script>  <script language="javascript" type="text/javascript" src="{{STATIC_URL}}js/jqplot/plugins/jqplot.canvasAxisLabelRenderer.min.js"></script> +<script language="javascript" type="text/javascript" src="{{STATIC_URL}}js/jqplot/plugins/jqplot.canvasAxisTickRenderer.min.js"></script>  <script language="javascript" type="text/javascript" src="{{STATIC_URL}}js/jqplot/plugins/jqplot.highlighter.min.js"></script>  <script language="javascript" type="text/javascript" src="{{STATIC_URL}}js/jqplot/plugins/jqplot.pieRenderer.min.js"></script>  <script language="javascript" type="text/javascript" src="{{STATIC_URL}}js/jqplot/plugins/jqplot.dateAxisRenderer.min.js"></script> +<script language="javascript" type="text/javascript" src="{{STATIC_URL}}js/jqplot/plugins/jqplot.cursor.min.js"></script> +<script language="javascript" type="text/javascript" src="{{STATIC_URL}}js/jqplot/plugins/jqplot.cursor.min.js"></script>  <link rel="stylesheet" href="{{STATIC_URL}}js/jqplot/jquery.jqplot.min.css" />  {% endblock %}  {% block content %} diff --git a/ishtar_common/templates/ishtar/dashboards/dashboard_main_detail.html b/ishtar_common/templates/ishtar/dashboards/dashboard_main_detail.html index 610457ae3..87ce5c528 100644 --- a/ishtar_common/templates/ishtar/dashboards/dashboard_main_detail.html +++ b/ishtar_common/templates/ishtar/dashboards/dashboard_main_detail.html @@ -1,11 +1,8 @@ -{% load i18n %} +{% load i18n date_formating %}  {% load url from future %}  <div class='dashboard' id="{{unique_id}}-tab">    <div>      <h4>{% trans "Numbers" %}</h4> -    <p><strong>{% trans "Total:" %}</strong> {{dashboard.total_number}}</p> -    <div class='table'> -    <div id="chart_{{unique_id}}" style="height:400px; width:700px;"></div>      {% if form %}      <div class='form'>      <form method='post' action="{% url 'dashboard-main-detail' item_name %}" id='{{unique_id}}_form'> @@ -16,15 +13,27 @@      </form>      </div>      {% endif %} -    {% comment %} -    <table> +    <p><strong>{% trans "Total:" %}</strong> {{dashboard.total_number}}</p> +    <div class='table'> +    <div id="chart_{{unique_id}}" style="height:400px; width:700px;"></div> +    <p class='info-box'>{% trans 'Draw rectangle on the graph to zoom. Double-click to reinitialize.' %}</p> +    <div class='form chart-img-form'> +      <button id="chart_img_display_{{unique_id}}" class='submit'>{% trans "Display as an image" %}</button> +      <div id="chart_img_{{unique_id}}" class='chart-img'> +        <div id="img_{{unique_id}}"></div> +        <p class='info-box'>{% trans 'Right-click on this image to save it.' %}</p> +      </div> +    </div> +    </div> +    <div class='table'> +    <table class='resume'>      {% for idx, lbl, values in dashboard.values %}        <tr class='idx {% if forloop.counter0|divisibleby:"2" %}even{%else%}odd{%endif%}'>        <th>{{lbl}}</th> -      {% for value in values %}<td>{{value}}</td>{% endfor%} +      {% for value in values reversed %}{% if forloop.parentloop.counter0 %}<td>{% else %}<th>{%endif%}{{value|date_formating }}{% if forloop.parentloop.counter0 %}</td>{% else %}</th>{%endif%}{% endfor%}        </tr>      {% endfor%} -    </table>{% endcomment %} +    </table>      </div>      {% if dashboard.periods %}      <h4>{% trans "By years" %}</h4> @@ -71,23 +80,41 @@    </div>  <script language="javascript" type="text/javascript">  $(document).ready(function(){ -var values_{{unique_id}} = []; +{% for idx, lbl, values in dashboard.values %} {% if forloop.counter0 > 0 %} var values_{{forloop.counter0}}_{{unique_id}} = []; {% for idx, lbl, values in dashboard.values %} {% for value in values %} {% if forloop.parentloop.counter0 == 0 %} values_{{forloop.parentloop.parentloop.counter0}}_{{unique_id}}.push([{{VALUE_QUOTE|safe}}{{value}}{{VALUE_QUOTE|safe}}, 0]); {% elif forloop.parentloop.parentloop.counter0 == forloop.parentloop.counter0 %} values_{{forloop.parentloop.parentloop.counter0}}_{{unique_id}}[{{forloop.counter0}}][1] = {{value}}; {% endif %} {% endfor%} {% endfor%} {% endif %} {% endfor %} -{% for idx, lbl, values in dashboard.values %} {% for value in values %}{% ifequal forloop.parentloop.counter0 0 %} -values_{{unique_id}}.push([{{VALUE_QUOTE|safe}}{{value}}{{VALUE_QUOTE|safe}}, 0]); -{% else %}values_{{unique_id}}[{{forloop.counter0}}][1] = {{value}};{% endifequal %}{% endfor%}{% endfor%} +{% comment %} +# less compact version +{% for idx, lbl, values in dashboard.values %} +  {% if forloop.counter0 > 0 %} +    {% for idx, lbl, values in dashboard.values %} +      {% for value in values %} +        {% if forloop.parentloop.counter0 == 0 %} +          values_{{forloop.parentloop.counter0}}_{{unique_id}}.push([{{VALUE_QUOTE|safe}}{{value}}{{VALUE_QUOTE|safe}}, 0]); +        {% elif forloop.parentloop.counter0 == forloop.counter0 %} +           values_{{forloop.counter0}}_{{unique_id}}[{{forloop.counter0}}][1] = {{value}}; +        {% endif %} +      {% endfor%} +    {% endfor%} +  {% endif %} +{% endfor %} +{% endcomment %} + + +if (typeof values_1_{{unique_id}} === 'undefined' +    || values_1_{{unique_id}}.length == 0){ -if (values_{{unique_id}}.length > 0){ +$('#chart_img_{{unique_id}}').hide(); +$('#chart_{{unique_id}}').html("<p class='alert'>{% trans 'No data for theses criteria.' %}</p>"); +} else {  var showmarker = false; -if (values_{{unique_id}}.length < 25){ +if (values_1_{{unique_id}}.length < 25){      var showmarker = true;  }  var plot_{{unique_id}} = $.jqplot('chart_{{unique_id}}', -    [values_{{unique_id}}], { -      series:[{showMarker:showmarker}], +    [{% for idx, lbl, values in dashboard.values %}{% if forloop.counter0 > 0 %} {% if forloop.counter0 > 1 %}, {% endif%} values_{{forloop.counter0}}_{{unique_id}} {% endif %} {% endfor%}], {        axes:{ {%ifequal slicing 'year'%}          xaxis:{            label:'{% trans "Year" %}', @@ -98,7 +125,11 @@ var plot_{{unique_id}} = $.jqplot('chart_{{unique_id}}',          xaxis:{            label:'{% trans "Month" %}',            renderer:$.jqplot.DateAxisRenderer, -          tickOptions:{formatString:'%b %Y'}, +          tickRenderer:$.jqplot.CanvasAxisTickRenderer, +          tickOptions:{ +            formatString:'%b %Y', +            angle:-25 +          }          },{%endifequal%}          yaxis:{            label:'{% trans "Number"%}', @@ -108,11 +139,28 @@ var plot_{{unique_id}} = $.jqplot('chart_{{unique_id}}',        highlighter: {          show: true,          sizeAdjust: 7.5 -      } +      }, +      series:[{% for label in dashboard.serie_labels %} +        {%if forloop.counter0%}, {% endif %}{label:"{{label}}", showmarker:showmarker}{% endfor %} +      ], +      cursor:{ +        show: true, +        zoom:true, +        showTooltip:false +      }, +      legend: { show:true, location: 'nw' } +  }); + +  $('#chart_img_display_{{unique_id}}').click(function(){ +    $('#chart_img_{{unique_id}}').hide(); +    $('#img_{{unique_id}}').html( +      $('<img/>').attr( +             'src', $('#chart_{{unique_id}}').jqplotToImageStr({}) +      ) +    ); +    $('#chart_img_{{unique_id}}').show('slow');    }); -} else { -$('#chart_{{unique_id}}').html("<p class='alert'>{% trans 'No data for theses criteria.' %}</p");  }  $('#search_{{unique_id}}').click(function (){ @@ -124,9 +172,9 @@ $('#search_{{unique_id}}').click(function (){      return false;  }); -{% ifequal item_name 'files' %} -load_jquerydate_after(); -load_jquerydate_before(); -{% endifequal %} +{% if item_name == 'files' or item_name == "operations"%} +load_jquerydate_{{item_name}}_after(); +load_jquerydate_{{item_name}}_before(); +{% endif %}  });  </script> diff --git a/ishtar_common/templatetags/date_formating.py b/ishtar_common/templatetags/date_formating.py new file mode 100644 index 000000000..eb81cf52c --- /dev/null +++ b/ishtar_common/templatetags/date_formating.py @@ -0,0 +1,19 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +from datetime import datetime + +from django.template import Library +from django.utils.translation import ugettext as _ + +register = Library() + +@register.filter +def date_formating(value): +    try: +        d = datetime.strptime(unicode(value), '%Y-%m-%d') +        return _(d.strftime("%B")).capitalize() + u" %d" % d.year +    except ValueError: +        # could be passed to non date value: on error return value +        return value + diff --git a/ishtar_common/views.py b/ishtar_common/views.py index d20b8b2af..889bd3893 100644 --- a/ishtar_common/views.py +++ b/ishtar_common/views.py @@ -50,6 +50,8 @@ if settings.XHTML2ODT_PATH:  from menus import menu +from archaeological_operations.forms import DashboardForm as DashboardFormOpe +  from ishtar_common.forms import FinalForm, FinalDeleteForm  from ishtar_common import forms_common as forms  from ishtar_common import wizards @@ -720,6 +722,8 @@ if 'archaeological_files' in settings.INSTALLED_APPS:      from archaeological_files.forms import DashboardForm as DashboardFormFile      DASHBOARD_FORMS['files'] = DashboardFormFile +DASHBOARD_FORMS['operations'] = DashboardFormOpe +  def dashboard_main_detail(request, item_name):      """      Specific tab of the main dashboard @@ -730,7 +734,11 @@ def dashboard_main_detail(request, item_name):                      'ishtar/dashboards/dashboard_main_detail_users.html',                              dct, context_instance=RequestContext(request))      form = None -    slicing, date_source, fltr  = 'year', None, {} +    slicing, date_source, fltr, show_detail  = 'year', None, {}, False +    if (item_name == 'files' and \ +      'archaeological_files' in settings.INSTALLED_APPS) \ +      or item_name == 'operations': +        slicing = 'month'      if item_name in DASHBOARD_FORMS:          if request.method == 'POST':              form = DASHBOARD_FORMS[item_name](request.POST) @@ -739,22 +747,28 @@ def dashboard_main_detail(request, item_name):                  fltr = form.get_filter()                  if hasattr(form, 'get_date_source'):                      date_source = form.get_date_source() +                if hasattr(form, 'get_show_detail'): +                    show_detail = form.get_show_detail()          else:              form = DASHBOARD_FORMS[item_name]()      lbl, dashboard = None, None -    if item_name == 'files' and \ -      'archaeological_files' in settings.INSTALLED_APPS: -        from archaeological_files.models import File -        dashboard_kwargs = {'slice':slicing, 'fltr':fltr,} +    if (item_name == 'files' and \ +      'archaeological_files' in settings.INSTALLED_APPS) \ +      or item_name == 'operations': +        dashboard_kwargs = {'slice':slicing, 'fltr':fltr, +                            'show_detail':show_detail}          # date_source is only relevant when the form has set one          if date_source:              dashboard_kwargs['date_source'] = date_source +    if item_name == 'files' and \ +      'archaeological_files' in settings.INSTALLED_APPS: +        from archaeological_files.models import File          lbl, dashboard = (_(u"Archaeological files"),                            models.Dashboard(File, **dashboard_kwargs))      if item_name == 'operations':          from archaeological_operations.models import Operation -        lbl, dashboard = (_(u"Operations"), models.Dashboard(Operation, -                                                    slice=slicing, fltr=fltr)) +        lbl, dashboard = (_(u"Operations"), +                          models.Dashboard(Operation, **dashboard_kwargs))      if item_name == 'contextrecords' and \        'archaeological_context_records' in settings.INSTALLED_APPS:          from archaeological_context_records.models import ContextRecord | 
