diff options
-rw-r--r-- | archaeological_operations/templates/ishtar/sheet_operation.html | 19 | ||||
-rw-r--r-- | archaeological_operations/urls.py | 4 | ||||
-rw-r--r-- | archaeological_operations/views.py | 37 | ||||
-rw-r--r-- | archaeological_warehouse/models.py | 6 | ||||
-rw-r--r-- | archaeological_warehouse/templates/ishtar/sheet_container.html | 22 | ||||
-rw-r--r-- | archaeological_warehouse/templates/ishtar/sheet_warehouse.html | 19 | ||||
-rw-r--r-- | archaeological_warehouse/urls.py | 8 | ||||
-rw-r--r-- | archaeological_warehouse/views.py | 25 | ||||
-rw-r--r-- | ishtar_common/models.py | 31 |
9 files changed, 147 insertions, 24 deletions
diff --git a/archaeological_operations/templates/ishtar/sheet_operation.html b/archaeological_operations/templates/ishtar/sheet_operation.html index ef5de7f59..868040666 100644 --- a/archaeological_operations/templates/ishtar/sheet_operation.html +++ b/archaeological_operations/templates/ishtar/sheet_operation.html @@ -408,7 +408,24 @@ <div class="tab-pane fade" id="{{window_id}}-statistics" role="tabpanel" aria-labelledby="{{window_id}}-statistics-tab"> <h3>{% trans "Statistics" %}</h3> - <small class="centered"><em>{% trans "These numbers are updated hourly" %}</em></small> + + <div class="row mt-2 mb-2"> + <div class="col"> + <div class="btn-group btn-group-sm" role="group" + aria-label="{% trans 'Export' %}"> + <a class="btn btn-success" + onclick="long_wait();return true;" + href="{% url 'generate-stats-operation' item.pk %}"> + + {% trans "Regenerate statistics" %} + </a> + </div> + {% with item.last_stats_update as last_stats_update%} + {% if last_stats_update %}<small class="ml-2"> + <em>{% trans "Last update:" %} {{last_stats_update}}</em> + </small>{% endif %}{% endwith %} + </div> + </div> <h4>{% trans "Administrative acts" %}</h4> <div class='row'> diff --git a/archaeological_operations/urls.py b/archaeological_operations/urls.py index 77791058a..72e06641d 100644 --- a/archaeological_operations/urls.py +++ b/archaeological_operations/urls.py @@ -212,4 +212,8 @@ urlpatterns = [ 'change_own_archaeologicalsite'])( views.QAArchaeologicalSiteForm.as_view()), name='site-qa-bulk-update-confirm', kwargs={"confirm": True}), + + url(r'generate-stats-operation/(?P<pk>.+)/', + views.GenerateStatsOperation.as_view(), + name='generate-stats-operation'), ] diff --git a/archaeological_operations/views.py b/archaeological_operations/views.py index ecacdb560..897302828 100644 --- a/archaeological_operations/views.py +++ b/archaeological_operations/views.py @@ -25,6 +25,7 @@ from django.core.urlresolvers import reverse from django.db.models import Q from django.http import HttpResponse, HttpResponseRedirect, Http404 from django.shortcuts import render, redirect +from django.views.generic import RedirectView from ishtar_common.utils import ugettext_lazy as _, pgettext_lazy from archaeological_operations import models @@ -36,7 +37,8 @@ from ishtar_common.models import get_current_profile, IshtarSiteProfile, \ DocumentTemplate from ishtar_common.utils import put_session_message, check_rights_condition from ishtar_common.views import gen_generate_doc, QAItemEditForm, \ - QABaseLockView, wizard_is_available, QAItemForm + QABaseLockView, wizard_is_available, QAItemForm, IshtarMixin, \ + LoginRequiredMixin from ishtar_common.views_item import get_item, show_item, revert_item, \ new_qa_item from ishtar_common.wizards import SearchWizard @@ -644,3 +646,36 @@ class QAArchaeologicalSiteDuplicateFormView(QAItemForm): class QAArchaeologicalSiteForm(QAItemEditForm): model = models.ArchaeologicalSite form_class = forms.QAArchaeologicalSiteFormMulti + + +class GenerateStatsOperation(IshtarMixin, LoginRequiredMixin, RedirectView): + model = models.Operation + + def get_redirect_url(self, *args, **kwargs): + return reverse('display-item', + args=[self.model.SLUG, self.item.pk]) + "#statistics" + + def get(self, request, *args, **kwargs): + self.item = self.model.objects.get(pk=kwargs['pk']) + self.item._get_or_set_stats('_nb_acts', update=True) + self.item._get_or_set_stats('_nb_indexed_acts', update=True) + self.item._get_or_set_stats('_nb_context_records', update=True) + self.item._get_or_set_stats('_nb_context_records_by_type', update=True, + expected_type=list) + self.item._get_or_set_stats('_nb_context_records_by_periods', + update=True, expected_type=list) + self.item._get_or_set_stats('_nb_finds', update=True) + self.item._get_or_set_stats('_nb_finds_by_material_type', update=True, + expected_type=list) + self.item._get_or_set_stats('_nb_finds_by_types', update=True, + expected_type=list) + self.item._get_or_set_stats('_nb_finds_by_periods', update=True, + expected_type=list) + self.item._get_or_set_stats('_nb_documents', update=True) + self.item._get_or_set_stats('_nb_documents_by_types', update=True, + expected_type=list) + self.item._get_or_set_stats('_nb_stats_finds_by_ue', update=True) + + return super(GenerateStatsOperation, self).get(request, *args, **kwargs) + + diff --git a/archaeological_warehouse/models.py b/archaeological_warehouse/models.py index ffc491ddc..f89092dba 100644 --- a/archaeological_warehouse/models.py +++ b/archaeological_warehouse/models.py @@ -118,7 +118,8 @@ class DivisionContainer(DashboardFormItem): @property def number_of_finds_by_place(self, update=False): - return self._get_or_set_stats('_number_of_finds_by_place', update) + return self._get_or_set_stats('_number_of_finds_by_place', update, + expected_type=list) def _number_of_containers_by_place(self): return self._number_of_items_by_place( @@ -126,7 +127,8 @@ class DivisionContainer(DashboardFormItem): @property def number_of_containers_by_place(self, update=False): - return self._get_or_set_stats('_number_of_containers_by_place', update) + return self._get_or_set_stats('_number_of_containers_by_place', update, + expected_type=list) def _get_divisions(self, current_path, remaining_division, depth=0): if not remaining_division: diff --git a/archaeological_warehouse/templates/ishtar/sheet_container.html b/archaeological_warehouse/templates/ishtar/sheet_container.html index 336684bc8..7ca39645d 100644 --- a/archaeological_warehouse/templates/ishtar/sheet_container.html +++ b/archaeological_warehouse/templates/ishtar/sheet_container.html @@ -154,7 +154,23 @@ role="tabpanel" aria-labelledby="{{window_id}}-stats-tab"> <h3>{% trans "Statistics" %}</h3> - <small class="centered"><em>{% trans "These numbers are updated hourly" %}</em></small> + <div class="row mt-2 mb-2"> + <div class="col"> + <div class="btn-group btn-group-sm" role="group" + aria-label="{% trans 'Export' %}"> + <a class="btn btn-success" + onclick="long_wait();return true;" + href="{% url 'generate-stats-container' item.pk %}"> + + {% trans "Regenerate statistics" %} + </a> + </div> + {% with item.last_stats_update as last_stats_update%} + {% if last_stats_update %}<small class="ml-2"> + <em>{% trans "Last update:" %} {{last_stats_update}}</em> + </small>{% endif %}{% endwith %} + </div> + </div> {% if not item.number_containers and not item.number_divisions %} <div class="alert alert-info"> @@ -188,10 +204,8 @@ {% endif %} {% endfor %} - <h4>{% trans "Containers" %}</h4> - {% if item.number_of_containers_by_place %} - <h4>{% trans "Containers by location in the warehouse" %}</h4> + <h4>{% trans "Containers by location" %}</h4> {% for items in item.number_of_containers_by_place %} {% if items %} <table class='table table-striped datatables' diff --git a/archaeological_warehouse/templates/ishtar/sheet_warehouse.html b/archaeological_warehouse/templates/ishtar/sheet_warehouse.html index e6717f230..b4786de5b 100644 --- a/archaeological_warehouse/templates/ishtar/sheet_warehouse.html +++ b/archaeological_warehouse/templates/ishtar/sheet_warehouse.html @@ -136,7 +136,24 @@ <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> + + <div class="row mt-2 mb-2"> + <div class="col"> + <div class="btn-group btn-group-sm" role="group" + aria-label="{% trans 'Export' %}"> + <a class="btn btn-success" + onclick="long_wait();return true;" + href="{% url 'generate-stats-warehouse' item.pk %}"> + + {% trans "Regenerate statistics" %} + </a> + </div> + {% with item.last_stats_update as last_stats_update%} + {% if last_stats_update %}<small class="ml-2"> + <em>{% trans "Last update:" %} {{last_stats_update}}</em> + </small>{% endif %}{% endwith %} + </div> + </div> {% if not item.number_containers and not item.number_divisions %} <div class="alert alert-info"> diff --git a/archaeological_warehouse/urls.py b/archaeological_warehouse/urls.py index 2f43d74a6..46d812def 100644 --- a/archaeological_warehouse/urls.py +++ b/archaeological_warehouse/urls.py @@ -128,4 +128,12 @@ urlpatterns = [ url(r'container-manual-merge-items/(?P<pks>[0-9_]+?)/$', views.ContainerManualMergeItems.as_view(), name='container_manual_merge_items'), + + url(r'generate-stats-container/(?P<pk>.+)/', + views.GenerateStatsContainer.as_view(), + name='generate-stats-container'), + + url(r'generate-stats-warehouse/(?P<pk>.+)/', + views.GenerateStatsWarehouse.as_view(), + name='generate-stats-warehouse'), ] diff --git a/archaeological_warehouse/views.py b/archaeological_warehouse/views.py index 34def58cf..de0c64d19 100644 --- a/archaeological_warehouse/views.py +++ b/archaeological_warehouse/views.py @@ -21,6 +21,7 @@ import json from django.core.urlresolvers import reverse from django.db.models import Q +from django.views.generic import RedirectView from django.views.generic.edit import FormView from django.http import HttpResponse, Http404, HttpResponseRedirect from django.shortcuts import redirect @@ -313,3 +314,27 @@ class QAContainerForm(QAItemEditForm): # item list is necessary to verify uniqueness rules kwargs['items'] = self.items return kwargs + + +class GenerateStats(IshtarMixin, LoginRequiredMixin, RedirectView): + model = None + + def get_redirect_url(self, *args, **kwargs): + return reverse('display-item', + args=[self.model.SLUG, self.item.pk]) + "#stats" + + def get(self, request, *args, **kwargs): + self.item = self.model.objects.get(pk=kwargs['pk']) + self.item._get_or_set_stats('_number_of_finds_by_place', update=True, + expected_type=list) + self.item._get_or_set_stats('_number_of_containers_by_place', + update=True, expected_type=list) + return super(GenerateStats, self).get(request, *args, **kwargs) + + +class GenerateStatsContainer(GenerateStats): + model = models.Container + + +class GenerateStatsWarehouse(GenerateStats): + model = models.Warehouse diff --git a/ishtar_common/models.py b/ishtar_common/models.py index 2d3320e2e..6eea042ce 100644 --- a/ishtar_common/models.py +++ b/ishtar_common/models.py @@ -3403,33 +3403,34 @@ class DashboardFormItem(object): Provide methods to manage statistics """ - def _get_or_set_stats(self, funcname, update, - timeout=settings.CACHE_TIMEOUT, + def last_stats_update(self): + model_name = self._meta.app_label + "." + self._meta.model_name + q = StatsCache.objects.filter( + model=model_name, model_pk=self.pk).order_by("-updated") + if not q.count(): + return + return q.all()[0].updated + + def _get_or_set_stats(self, funcname, update=False, expected_type=None): - values = {} - from_cache = False model_name = self._meta.app_label + "." + self._meta.model_name sc, __ = StatsCache.objects.get_or_create( model=model_name, model_pk=self.pk ) - now = datetime.datetime.now() - if not settings.DEBUG and ( - not update and sc.values and funcname in sc.values and ( - sc.updated + datetime.timedelta(seconds=timeout)) > now): + if not update: values = sc.values - from_cache = True - if funcname not in values: + if funcname not in values: + if expected_type is not None: + return expected_type() + return 0 + else: values = update_stats(sc, self, funcname) if funcname in values: values = values[funcname] else: values = 0 if expected_type is not None and not isinstance(values, expected_type): - if from_cache: - return self._get_or_set_stats(funcname, True, - expected_type=expected_type) - else: - return expected_type() + return expected_type() return values @classmethod |