diff options
| author | Étienne Loks <etienne.loks@iggdrasil.net> | 2020-04-17 13:20:42 +0200 | 
|---|---|---|
| committer | Étienne Loks <etienne.loks@iggdrasil.net> | 2021-02-28 12:15:20 +0100 | 
| commit | 52257bb404da82db28d497c3ece6de4b959e1fb9 (patch) | |
| tree | ebd3f208b1d812a068939cc6ee0b6d4a376316b2 | |
| parent | bc3b4054f96072443871553cfeed49a81d469cd8 (diff) | |
| download | Ishtar-52257bb404da82db28d497c3ece6de4b959e1fb9.tar.bz2 Ishtar-52257bb404da82db28d497c3ece6de4b959e1fb9.zip  | |
Fix statistics for warehouse
6 files changed, 203 insertions, 128 deletions
diff --git a/archaeological_finds/migrations/0105_auto_20200407_1414.py b/archaeological_finds/migrations/0105_auto_20200407_1414.py index b4c05ee8a..1f9bc88d3 100644 --- a/archaeological_finds/migrations/0105_auto_20200407_1414.py +++ b/archaeological_finds/migrations/0105_auto_20200407_1414.py @@ -23,4 +23,10 @@ class Migration(migrations.Migration):              name='collection',              field=models.ForeignKey(blank=True, db_constraint=False, null=True, on_delete=django.db.models.deletion.DO_NOTHING, related_name='+', to='archaeological_warehouse.Warehouse', verbose_name='Collection'),          ), +        migrations.AlterField( +            model_name='materialtype', +            name='code', +            field=models.CharField(blank=True, max_length=100, null=True, +                                   verbose_name='Code'), +        ),      ] diff --git a/archaeological_finds/models_finds.py b/archaeological_finds/models_finds.py index 646ae8088..c18b2f4f1 100644 --- a/archaeological_finds/models_finds.py +++ b/archaeological_finds/models_finds.py @@ -55,7 +55,7 @@ from archaeological_warehouse.models import Warehouse  class MaterialType(HierarchicalType): -    code = models.CharField(_("Code"), max_length=10, blank=True, null=True) +    code = models.CharField(_("Code"), max_length=100, blank=True, null=True)      recommendation = models.TextField(_("Recommendation"), blank=True,                                        null=True) diff --git a/archaeological_warehouse/migrations/0103_auto_container_views.py b/archaeological_warehouse/migrations/0103_auto_container_views.py index f49b7be1f..eb2b32e5e 100644 --- a/archaeological_warehouse/migrations/0103_auto_container_views.py +++ b/archaeological_warehouse/migrations/0103_auto_container_views.py @@ -2,7 +2,7 @@  # Generated by Django 1.11.27 on 2020-04-08 18:23  from __future__ import unicode_literals -from django.db import migrations +from django.db import migrations, models  import archaeological_warehouse.models  import archaeological_finds.models @@ -35,4 +35,15 @@ class Migration(migrations.Migration):          migrations.RunSQL(              archaeological_finds.models.FindInsideContainer.CREATE_SQL),          migrations.RunPython(migrate_to_collections), +        migrations.CreateModel( +            name='ContainerTree', +            fields=[ +                ('id', models.AutoField(auto_created=True, primary_key=True, +                                        serialize=False, verbose_name='ID')), +            ], +            options={ +                'db_table': 'containers_tree', +                'managed': False, +            }, +        ),      ] diff --git a/archaeological_warehouse/models.py b/archaeological_warehouse/models.py index a99db2ef7..41ec901a4 100644 --- a/archaeological_warehouse/models.py +++ b/archaeological_warehouse/models.py @@ -208,7 +208,7 @@ class Warehouse(Address, DocumentItem, GeoItem, QRCodeItem, DashboardFormItem,      @property      def number_of_finds(self):          from archaeological_finds.models import Find -        return Find.objects.filter(container__responsible=self).count() +        return Find.objects.filter(container_ref__location=self).count()      @property      def number_of_finds_hosted(self): @@ -223,14 +223,16 @@ class Warehouse(Address, DocumentItem, GeoItem, QRCodeItem, DashboardFormItem,          if not remaining_division:              return [current_path]          current_division = remaining_division.pop(0) -        q = ContainerLocalisation.objects.filter( -            division=current_division, + +        base_q = Container.objects.filter( +            container_type=current_division, +            location=self          ) +        q = base_q          for div, ref in current_path: -            q = q.filter( -                container__division__division=div, -                container__division__reference=ref -            ) +            q = base_q.filter( +                parent__container_type=div, +                parent__reference=ref)          res = []          old_ref = None          if not q.count(): @@ -252,26 +254,28 @@ class Warehouse(Address, DocumentItem, GeoItem, QRCodeItem, DashboardFormItem,          :return: ordered list of available paths. Each path is a list of          tuple with the WarehouseDivisionLink and the reference.          """ -        divisions = list( -            WarehouseDivisionLink.objects.filter(warehouse=self -                                                 ).order_by('order').all()) +        divisions = [ +            wd.container_type +            for wd in WarehouseDivisionLink.objects.filter( +                warehouse=self).order_by('order').all() +        ]          return self._get_divisions([], divisions) -    def _number_of_items_by_place(self, model, division_key='division'): +    def _number_of_items_by_place(self, model, division_key):          res = {}          paths = self.available_division_tuples[:]          for path in paths: -            q = model.objects              cpath = []              for division, ref in path: +                q = model.objects                  cpath.append(ref)                  attrs = { -                    division_key + "__division": division, -                    division_key + "__reference": ref +                    division_key + "container_type": division, +                    division_key + "reference": ref                  }                  q = q.filter(**attrs)                  if tuple(cpath) not in res: -                    res[tuple(cpath)] = q.count() +                    res[tuple(cpath)] = q.distinct().count()          res = [(k, res[k]) for k in res]          final_res, current_res, depth = [], [], 1          len_divisions = WarehouseDivisionLink.objects.filter( @@ -283,7 +287,7 @@ class Warehouse(Address, DocumentItem, GeoItem, QRCodeItem, DashboardFormItem,                  depth = len(path)              if path[-1] == '-':                  continue -            path = list(path) + ['' for idx in range(len_divisions - len(path))] +            path = list(path) + ['' for __ in range(len_divisions - len(path))]              current_res.append((path, nb))          final_res.append(current_res[:])          return final_res @@ -291,14 +295,15 @@ class Warehouse(Address, DocumentItem, GeoItem, QRCodeItem, DashboardFormItem,      def _number_of_finds_by_place(self):          from archaeological_finds.models import Find          return self._number_of_items_by_place( -            Find, division_key='container__division') +            Find, division_key='inside_container__container__')      @property      def number_of_finds_by_place(self, update=False):          return self._get_or_set_stats('_number_of_finds_by_place', update)      def _number_of_containers_by_place(self): -        return self._number_of_items_by_place(Container) +        return self._number_of_items_by_place( +            ContainerTree, 'container_parent__')      @property      def number_of_containers_by_place(self, update=False): @@ -421,7 +426,7 @@ class WarehouseDivisionLink(models.Model):          return self.warehouse.uuid, self.container_type.txt_idx -class ContainerTree: +class ContainerTree(models.Model):      CREATE_SQL = """      CREATE VIEW containers_tree AS          WITH RECURSIVE rel_tree AS ( @@ -458,6 +463,17 @@ class ContainerTree:      DROP VIEW IF EXISTS container_tree;      DROP VIEW IF EXISTS containers_tree;      """ +    container = models.OneToOneField( +        "archaeological_warehouse.Container", verbose_name=_("Container"), +        related_name="container_tree_child", primary_key=True) +    container_parent = models.ForeignKey( +        "archaeological_warehouse.Container", +        verbose_name=_("Container parent"), +        related_name="container_tree_parent") + +    class Meta: +        managed = False +        db_table = 'containers_tree'  class Container(DocumentItem, Merge, LightHistorizedItem, QRCodeItem, GeoItem, @@ -795,8 +811,7 @@ class Container(DocumentItem, Merge, LightHistorizedItem, QRCodeItem, GeoItem,      @classmethod      def get_query_owns(cls, ishtaruser):          return Q(history_creator=ishtaruser.user_ptr) | \ -            Q(location__person_in_charge__ishtaruser=ishtaruser) | \ -            Q(responsible__person_in_charge__ishtaruser=ishtaruser) +            Q(location__person_in_charge__ishtaruser=ishtaruser)      def get_precise_points(self):          precise_points = super(Container, self).get_precise_points() diff --git a/archaeological_warehouse/templates/ishtar/sheet_warehouse.html b/archaeological_warehouse/templates/ishtar/sheet_warehouse.html index feb8b786a..1307425ff 100644 --- a/archaeological_warehouse/templates/ishtar/sheet_warehouse.html +++ b/archaeological_warehouse/templates/ishtar/sheet_warehouse.html @@ -8,124 +8,167 @@  {% block content %} -<div class="row"> -    <div class="offset-lg-4 col-lg-4 offset-md-3 col-md-6 offset-sm-1 col-sm-10 col-12"> -        <div class="card"> -            {% include "ishtar/blocks/window_image.html" %} -            <div class="card-body"> -                <p class="card-text"> -                    <p class="window-refs"> -                       <strong>{{ item.name|default:"" }}</strong> -                    </p> -                    <p class="window-refs"> -                        {{ item.warehouse_type|default:"" }} -                    </p> -                    {% include "ishtar/blocks/sheet_external_id.html" %} -                </p> + +{% if output != "ODT" and output != "PDF"%} +<ul class="nav nav-tabs" id="{{window_id}}-tabs" role="tablist"> +    <li class="nav-item"> +        <a class="nav-link active" id="{{window_id}}-general-tab" +           data-toggle="tab" href="#{{window_id}}-general" role="tab" +           aria-controls="{{window_id}}-general" aria-selected="true"> +            {% trans "General" %} +        </a> +    </li> +    <li class="nav-item"> +        <a class="nav-link" id="{{window_id}}-content-tab" +           data-toggle="tab" href="#{{window_id}}-content" role="tab" +           aria-controls="{{window_id}}-content" aria-selected="true"> +            {% trans "Content" %} +        </a> +    </li> +    <li class="nav-item"> +        <a class="nav-link" id="{{window_id}}-stats-tab" +           data-toggle="tab" href="#{{window_id}}-stats" role="tab" +           aria-controls="{{window_id}}-stats" aria-selected="false"> +            {% trans "Statistics" %} +        </a> +    </li> +</ul> +{% endif %} + +<div class="tab-content" id="{{window_id}}-tab-content"> + +    <div class="tab-pane fade show active" id="{{window_id}}-general" +         role="tabpanel" aria-labelledby="{{window_id}}-general-tab"> +        <div class="row"> +            <div class="offset-lg-4 col-lg-4 offset-md-3 col-md-6 offset-sm-1 col-sm-10 col-12"> +                <div class="card"> +                    {% include "ishtar/blocks/window_image.html" %} +                    <div class="card-body"> +                        <div class="card-text"> +                        <p class="window-refs"> +                            <strong>{{ item.name|default:"" }}</strong> +                        </p> +                        <p class="window-refs"> +                            {{ item.warehouse_type|default:"" }} +                        </p> +                        {% include "ishtar/blocks/sheet_external_id.html" %} +                        </div> +                    </div> +                </div>              </div>          </div> -    </div> -</div> -<div class='row'> -    {% include "ishtar/blocks/sheet_creation_section.html" %} -    {% field_flex_detail "Person in charge" item.person_in_charge %} -    {% field_flex_detail "Organization" item.organization %} -    {% trans "Default divisions" as def_div_label %} -    {% field_flex def_div_label item.location_types|join:", " %} -    {% field_flex_full "Comment" item.comment "<pre>" "</pre>" %} -    {% include "ishtar/blocks/sheet_json.html" %} -</div> +        <div class='row'> +            {% include "ishtar/blocks/sheet_creation_section.html" %} +            {% field_flex_detail "Person in charge" item.person_in_charge %} +            {% field_flex_detail "Organization" item.organization %} +            {% trans "Default divisions" as def_div_label %} +            {% field_flex def_div_label item.location_types|join:", " %} +            {% field_flex_full "Comment" item.comment "<pre>" "</pre>" %} +            {% include "ishtar/blocks/sheet_json.html" %} +        </div> -{% if item.point_2d or item.multi_polygon or item.get_address or item.get_address_complement or item.get_postal_code or item.get_town %} -<h3>{% trans "Localisation"%}</h3> -<div class='row'> -    {% with geo_item=item %} -    {% if PROFILE.locate_warehouses %}{% include "ishtar/blocks/sheet_simple_map.html"%}{% endif %} -    <div class="col-12 col-lg-6 flex-wrap"> -        {% if PROFILE.locate_warehouses %}{% include "ishtar/blocks/sheet_coordinates.html"%}{% endif %} -        {% with full=True %}{% include "ishtar/blocks/sheet_address_section.html" %}{% endwith %} +        {% if item.point_2d or item.multi_polygon or item.get_address or item.get_address_complement or item.get_postal_code or item.get_town %} +        <h3>{% trans "Localisation"%}</h3> +        <div class='row'> +            {% with geo_item=item %} +            {% if PROFILE.locate_warehouses %}{% include "ishtar/blocks/sheet_simple_map.html"%}{% endif %} +            <div class="col-12 col-lg-6 flex-wrap"> +                {% if PROFILE.locate_warehouses %}{% include "ishtar/blocks/sheet_coordinates.html"%}{% endif %} +                {% with full=True %}{% include "ishtar/blocks/sheet_address_section.html" %}{% endwith %} +            </div> +            {% endwith %} +        </div> +        {% endif %}      </div> -    {% endwith %} -</div> -{% endif %} - -{% if item.containers.count %} -<h4>{% trans "Containers" %}</h4> -{% dynamic_table_document '' 'containers' 'location_id' item.pk 'TABLE_COLS' output %} -{% endif %} +    <div class="tab-pane fade" id="{{window_id}}-content" +         role="tabpanel" aria-labelledby="{{window_id}}-content-tab"> +        {% if item.containers.count %} +        <h4>{% trans "Containers" %}</h4> +        {% dynamic_table_document '' 'containers' 'location_id' item.pk 'TABLE_COLS' output %} +        {% else %} +        <div class="alert alert-info"> +            <i class="fa fa-exclamation-triangle"></i> +            {% trans "No container inside this warehouse" %} +        </div> +        {% endif %} +    </div> -<h3>{% trans "Statistics" %}</h3> -<small class="centered"><em>{% trans "These numbers are updated hourly" %}</em></small> - -<h4>{% trans "Finds" %}</h4> -<div class='row'> -    {% trans "Number of attached finds" as number_of_attached_finds_label %} -    {% field_flex_2 number_of_attached_finds_label item.number_of_finds %} -    {% trans "Number of hosted finds" as number_of_hosted_finds_label %} -    {% field_flex_2 number_of_hosted_finds_label item.number_of_finds_hosted %} -</div> +    <div class="tab-pane fade" id="{{window_id}}-stats" +         role="tabpanel" aria-labelledby="{{window_id}}-stats-tab"> +        <h3>{% trans "Statistics" %}</h3> +        <small class="centered"><em>{% trans "These numbers are updated hourly" %}</em></small> + +        <h4>{% trans "Finds" %}</h4> +        <div class='row'> +            {% trans "Number of attached finds" as number_of_attached_finds_label %} +            {% field_flex_2 number_of_attached_finds_label item.number_of_finds %} +            {% trans "Number of hosted finds" as number_of_hosted_finds_label %} +            {% field_flex_2 number_of_hosted_finds_label item.number_of_finds_hosted %} +        </div> -{% if item.number_of_finds_by_place %} -<h4>{% trans "Finds by location in the warehouse" %}</h4> -{% for items in item.number_of_finds_by_place %} -{% if items %} -<table class='table table-striped datatables' -       id="{{window_id}}-find-by-loca-{{forloop.counter}}"> -    <thead> -    <tr>{% for location_type in item.location_types %} -        <th class="text-center">{{location_type|title}}</th>{% endfor %} -        <th class="text-center">{% trans "Total" %}</th> -    </tr> -    </thead> -    <tbody> -    {% for item in items %} -    <tr> -        {% for local in item.0 %}<td>{{local}}</td>{% endfor %} -        <td class="text-right">{{item.1}}</td> -    </tr> -    {% endfor %} -    </tbody> -</table> -{% endif %} -{% endfor %} -{% endif %} +        {% if item.number_of_finds_by_place %} +        <h4>{% trans "Finds by location in the warehouse" %}</h4> +        {% for items in item.number_of_finds_by_place %} +        {% if items %} +        <table class='table table-striped datatables' +               id="{{window_id}}-find-by-loca-{{forloop.counter}}"> +            <thead> +            <tr>{% for location_type in item.location_types %} +                <th class="text-center">{{location_type|title}}</th>{% endfor %} +                <th class="text-center">{% trans "Total" %}</th> +            </tr> +            </thead> +            <tbody> +            {% for item in items %} +            <tr> +                {% for local in item.0 %}<td>{{local}}</td>{% endfor %} +                <td class="text-right">{{item.1}}</td> +            </tr> +            {% endfor %} +            </tbody> +        </table> +        {% endif %} +        {% endfor %} +        {% endif %} + +        <h4>{% trans "Containers" %}</h4> +        <div class='row'> +            {% trans "Number of containers" as number_of_containers_label %} +            {% field_flex_2 number_of_containers_label item.number_of_containers %} +        </div> -<h4>{% trans "Containers" %}</h4> -<div class='row'> -    {% trans "Number of containers" as number_of_containers_label %} -    {% field_flex_2 number_of_containers_label item.number_of_containers %} +        {% if item.number_of_containers_by_place %} +        <h4>{% trans "Containers by location in the warehouse" %}</h4> +        {% for items in item.number_of_containers_by_place %} +        {% if items %} +        <table class='table table-striped datatables' +               id="{{window_id}}-container-by-loca-{{forloop.counter}}"> +            <thead> +            <tr>{% for location_type in item.location_types %} +                <th class="text-center">{{location_type|title}}</th>{% endfor %} +                <th class="text-center">{% trans "Total" %}</th> +            </tr> +            </thead> +            <tbody> +            {% for item in items %} +            <tr> +                {% for local in item.0 %}<td class="text-center">{{local}}</td>{% endfor %} +                <td class="text-center">{{item.1}}</td> +            </tr> +            {% endfor %} +            </tbody> +        </table> +        {% endif %} +        {% endfor %} +        {% endif %} +    </div>  </div> -{% if item.number_of_containers_by_place %} -<h4>{% trans "Containers by location in the warehouse" %}</h4> -{% for items in item.number_of_containers_by_place %} -{% if items %} -<table class='table table-striped datatables' -       id="{{window_id}}-container-by-loca-{{forloop.counter}}"> -    <thead> -    <tr>{% for location_type in item.location_types %} -        <th class="text-center">{{location_type|title}}</th>{% endfor %} -        <th class="text-center">{% trans "Total" %}</th> -    </tr> -    </thead> -    <tbody> -    {% for item in items %} -    <tr> -        {% for local in item.0 %}<td class="text-center">{{local}}</td>{% endfor %} -        <td class="text-center">{{item.1}}</td> -    </tr> -    {% endfor %} -    </tbody> -</table> -{% endif %} -{% endfor %} -{% endif %} -  <script type="text/javascript">  $(document).ready( function () {      datatable_options = { diff --git a/archaeological_warehouse/templates/ishtar/wizard/wizard_warehouse_divisions.html b/archaeological_warehouse/templates/ishtar/wizard/wizard_warehouse_divisions.html index dff23bc6c..2b11e9235 100644 --- a/archaeological_warehouse/templates/ishtar/wizard/wizard_warehouse_divisions.html +++ b/archaeological_warehouse/templates/ishtar/wizard/wizard_warehouse_divisions.html @@ -3,6 +3,6 @@  {% block form_head %}  <div class="alert alert-info">    <i class="fa fa-exclamation-triangle"></i> -  {% trans "Default division for this warehouse. Theses divisions are only used for imports." %}<br/> +  {% trans "Default division for this warehouse. Theses divisions are only used for imports and statistics." %}<br/>  </div>  {% endblock %}  | 
