#!/usr/bin/env python # -*- coding: utf-8 -*- # Copyright (C) 2010-2016 Étienne Loks # 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 . # See the file COPYING for details. 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 from ishtar_common.utils import ugettext_lazy as _ from archaeological_warehouse import models from archaeological_warehouse import forms from ishtar_common.forms import FinalForm from ishtar_common.views import QABaseLockView, wizard_is_available, \ merge_action, ManualMergeMixin, ManualMergeItemsMixin, IshtarMixin, \ LoginRequiredMixin, QAItemEditForm from ishtar_common.views_item import get_item, show_item, new_qa_item, \ revert_item from archaeological_finds.views import treatment_add from archaeological_warehouse.wizards import PackagingWizard, WarehouseSearch, \ WarehouseWizard, WarehouseModificationWizard, WarehouseDeletionWizard, \ ContainerSearch, ContainerWizard, ContainerModificationWizard, \ ContainerDeletionWizard get_container = get_item(models.Container, 'get_container', 'container', search_form=forms.ContainerSelect) get_divisions_container = get_item( models.Container, 'get_container', 'container', search_form=forms.ContainerSelect, base_request={"container_type__stationary": 'True'}) get_non_divisions_container = get_item( models.Container, 'get_container', 'container', search_form=forms.ContainerSelect, base_request={"container_type__stationary": 'False'}) show_container = show_item(models.Container, 'container') get_warehouse = get_item(models.Warehouse, 'get_warehouse', 'warehouse', search_form=forms.WarehouseSelect) show_warehouse = show_item(models.Warehouse, 'warehouse') revert_warehouse = revert_item(models.Warehouse) new_warehouse = new_qa_item(models.Warehouse, forms.WarehouseForm) new_container = new_qa_item(models.Container, forms.ContainerForm) def autocomplete_warehouse(request): if not request.user.has_perm('ishtar_common.view_warehouse', models.Warehouse)\ and not request.user.has_perm( 'ishtar_common.view_own_warehouse', models.Warehouse): return HttpResponse(content_type='text/plain') if not request.GET.get('term'): return HttpResponse(content_type='text/plain') q = request.GET.get('term') query = Q() for q in q.split(' '): extra = Q(name__icontains=q) | \ Q(warehouse_type__label__icontains=q) query = query & extra limit = 15 warehouses = models.Warehouse.objects.filter(query)[:limit] data = json.dumps([{'id': warehouse.pk, 'value': str(warehouse)} for warehouse in warehouses]) return HttpResponse(data, content_type='text/plain') def autocomplete_container(request, warehouse_id=None): if not request.user.has_perm('ishtar_common.view_warehouse', models.Warehouse)\ and not request.user.has_perm( 'ishtar_common.view_own_warehouse', models.Warehouse): return HttpResponse(content_type='text/plain') if not request.GET.get('term'): return HttpResponse(content_type='text/plain') term = request.GET.get('term').strip() limit = 15 base_query = Q() if warehouse_id: base_query = Q(location_id=warehouse_id) query = base_query # exact index try: query = query & Q(index=int(term)) containers = list(models.Container.objects.filter( query).values('id', 'cached_label')[:limit]) except ValueError: containers = [] # exact ref query = query & Q(reference__unaccent__iexact=term) containers += list(models.Container.objects.filter( query).values('id', 'cached_label')[:limit]) limit = 15 - len(containers) splitted = [s.lower() for s in term.split(' ') if s] if limit > 0 and len(splitted) > 1: type_positions = [] # container_type ID, pos inf, pos sup container_types = [ (c[0], c[1]) for c in models.ContainerType.objects.values_list("id", "label") ] for container_type_id, value in container_types: if value.lower() in term.lower(): # container_type is in search q values = [v.lower() for v in value.split(" ") if v] # verify that all term match in splitted try: index = splitted.index(values[0].lower()) except ValueError: index = None index_is_ok = False while not index_is_ok and index is not None: for idx, v in enumerate(values): try: assert splitted[index + idx] == v except (ValueError, AssertionError): break index_is_ok = True if not index_is_ok: try: index = splitted.index(values[0].lower(), index + 1) except ValueError: index = None if index_is_ok: type_positions.append( (container_type_id, index, index + len(values))) query = base_query if not warehouse_id and type_positions and type_positions[0][1] > 0: for idx in range(type_positions[0][1]): query &= Q(location__name__icontains=splitted[idx]) # group by container type, ref tuple groups = [] for idx, (container_type_id, pos_inf, pos_sup) \ in enumerate(type_positions): if len(type_positions) == idx + 1: # last value = " ".join(splitted[pos_sup:]) else: value = " ".join(splitted[pos_sup:type_positions[idx + 1][1]]) groups.append((container_type_id, value)) if groups: query = base_query for idx, g in enumerate(reversed(groups)): base_key = "parent__" * idx key1 = base_key + "container_type_id" key2 = base_key + "reference__unaccent__iexact" query &= Q(**{key1: g[0], key2: g[1]}) ids = {c["id"] for c in containers} containers += list(models.Container.objects.filter( query).exclude(pk__in=ids).values('id', 'cached_label')[ :limit]) if len(splitted) > 1 and len(containers) < 15: # group to do a "type" "reference" search for idx in range(1, len(splitted)): group_1 = splitted[:idx] group_2 = splitted[idx:] extra = Q( container_type__label__unaccent__iexact=" ".join(group_1), reference__unaccent__iexact=" ".join(group_2)) query = base_query & extra ids = {c["id"] for c in containers} containers += list(models.Container.objects.filter( query).exclude(pk__in=ids).values('id', 'cached_label')[ :limit]) if len(containers) >= 15: break if len(containers) < 15: query = base_query for q in splitted: extra = Q(reference__unaccent__iexact=q) query = query & extra ids = {c["id"] for c in containers} containers += list(models.Container.objects.filter( query).exclude(pk__in=ids).values('id', 'cached_label')[:limit]) limit = 15 - len(containers) if limit > 0: query = base_query for q in splitted: extra = Q(container_type__label__unaccent__icontains=q) | \ Q(container_type__reference__unaccent__icontains=q) | \ Q(reference__unaccent__icontains=q) | \ Q(cached_label__unaccent__icontains=q) if not warehouse_id: extra |= Q(location__name__unaccent=q) | Q( location__town__unaccent=q) query = query & extra ids = {c["id"] for c in containers} containers += list( models.Container.objects.filter(query).exclude( pk__in=ids ).values('id', 'cached_label')[:limit]) data = json.dumps( [{'id': container['id'], 'value': container['cached_label']} for container in containers]) return HttpResponse(data, content_type='text/plain') warehouse_packaging_wizard = PackagingWizard.as_view([ # AFAC ('seleccontainer-packaging', forms.ContainerFormSelection), ('base-packaging', forms.BasePackagingForm), ('final-packaging', FinalForm)], label=_("Packaging"), url_name='warehouse_packaging',) warehouse_search_wizard = WarehouseSearch.as_view([ ('selec-warehouse_search', forms.WarehouseFormSelection)], label=_("Warehouse search"), url_name='warehouse_search', ) warehouse_creation_steps = [ ("warehouse-warehouse_creation", forms.WarehouseForm), ('divisions-warehouse_creation', forms.SelectedDivisionFormset), ('final-warehouse_creation', FinalForm)] warehouse_creation_wizard = WarehouseWizard.as_view( warehouse_creation_steps, label=_("Warehouse creation"), url_name='warehouse_creation', ) warehouse_modification_wizard = WarehouseModificationWizard.as_view([ ('selec-warehouse_modification', forms.WarehouseFormSelection), ("warehouse-warehouse_modification", forms.WarehouseModifyForm), ('divisions-warehouse_modification', forms.SelectedDivisionFormset), ('final-warehouse_modification', FinalForm)], label=_("Warehouse modification"), url_name='warehouse_modification', ) def warehouse_modify(request, pk): if not wizard_is_available(warehouse_modification_wizard, request, models.Warehouse, pk): return HttpResponseRedirect("/") wizard_url = 'warehouse_modification' WarehouseModificationWizard.session_set_value( request, 'selec-' + wizard_url, 'pk', pk, reset=True) return redirect( reverse(wizard_url, kwargs={'step': 'warehouse-' + wizard_url})) warehouse_deletion_wizard = WarehouseDeletionWizard.as_view([ ('selec-warehouse_deletion', forms.WarehouseFormMultiSelection), ('final-warehouse_deletion', forms.WarehouseDeletionForm)], label=_("Warehouse deletion"), url_name='warehouse_deletion',) def warehouse_delete(request, pk): if not wizard_is_available(warehouse_deletion_wizard, request, models.Warehouse, pk): return HttpResponseRedirect("/") wizard_url = 'warehouse_deletion' WarehouseDeletionWizard.session_set_value( request, 'selec-' + wizard_url, 'pks', pk, reset=True) return redirect( reverse(wizard_url, kwargs={'step': 'final-' + wizard_url})) class QAWarehouseLockView(QABaseLockView): model = models.Warehouse base_url = "warehouse-qa-lock" container_search_wizard = ContainerSearch.as_view([ ('selec-container_search', forms.MainContainerFormSelection)], label=_("Container search"), url_name='container_search', ) container_creation_steps = [ ('container-container_creation', forms.ContainerForm), ('final-container_creation', FinalForm)] container_creation_wizard = ContainerWizard.as_view( container_creation_steps, label=_("Container creation"), url_name='container_creation', ) container_modification_wizard = ContainerModificationWizard.as_view([ ('selec-container_modification', forms.MainContainerFormSelection), ('container-container_modification', forms.ContainerModifyForm), ('final-container_modification', FinalForm)], label=_("Container modification"), url_name='container_modification', ) def container_modify(request, pk): if not wizard_is_available(container_modification_wizard, request, models.Container, pk): return HttpResponseRedirect("/") wizard_url = 'container_modification' ContainerModificationWizard.session_set_value( request, 'selec-' + wizard_url, 'pk', pk, reset=True) return redirect( reverse(wizard_url, kwargs={'step': 'container-' + wizard_url})) container_deletion_wizard = ContainerDeletionWizard.as_view([ ('selec-container_deletion', forms.MainContainerFormMultiSelection), ('final-container_deletion', forms.ContainerDeletionForm)], label=_("Container deletion"), url_name='container_deletion',) def container_delete(request, pk): if not wizard_is_available(container_deletion_wizard, request, models.Container, pk): return HttpResponseRedirect("/") wizard_url = 'container_deletion' ContainerDeletionWizard.session_set_value( request, 'selec-' + wizard_url, 'pks', pk, reset=True) return redirect( reverse(wizard_url, kwargs={'step': 'final-' + wizard_url})) def container_treatment_add(request, pk, current_right=None): try: container = models.Container.objects.get(pk=pk) except models.Container.DoesNotExist: raise Http404() return treatment_add( request, ",".join(str(f.pk) for f in container.finds.all()) ) """ warehouse_packaging_wizard = ItemSourceWizard.as_view([ ('selec-warehouse_packaging', ItemsSelection), ('final-warehouse_packaging', FinalForm)], url_name='warehouse_packaging',) """ container_merge = merge_action(models.Container, forms.MergeContainerForm, 'container', name_key="reference") class ContainerManualMerge(ManualMergeMixin, IshtarMixin, LoginRequiredMixin, FormView): form_class = forms.ContainerMergeFormSelection template_name = 'ishtar/form.html' page_name = _("Merge containers") current_url = 'container-manual-merge' redir_url = 'container_manual_merge_items' class ContainerManualMergeItems(ManualMergeItemsMixin, IshtarMixin, LoginRequiredMixin, FormView): form_class = forms.ContainerMergeIntoForm template_name = 'ishtar/form.html' page_name = _("Select the main container") current_url = 'container-manual-merge-items' item_type = 'container' class QAContainerLockView(QABaseLockView): model = models.Container base_url = "container-qa-lock" def reset_wizards(request): for wizard_class, url_name in ( (PackagingWizard, 'warehouse_packaging'), (WarehouseWizard, 'warehouse_creation'), (WarehouseModificationWizard, 'warehouse_modification'), (WarehouseDeletionWizard, 'warehouse_deletion'), (ContainerWizard, 'container_creation'), (ContainerModificationWizard, 'container_modification'), (ContainerDeletionWizard, 'container_deletion'), ): wizard_class.session_reset(request, url_name) class QAContainerForm(QAItemEditForm): model = models.Container form_class = forms.QAContainerFormMulti def get_form_kwargs(self): kwargs = super(QAContainerForm, self).get_form_kwargs() # 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