diff options
| author | Étienne Loks <etienne.loks@proxience.com> | 2014-07-21 19:21:27 +0200 | 
|---|---|---|
| committer | Étienne Loks <etienne.loks@proxience.com> | 2014-07-21 19:21:27 +0200 | 
| commit | a27dca4b361b0d2f46a20f9ac2719ef811c093e0 (patch) | |
| tree | 5f38e92243a02add2b1e0176585f17f8c4aef7d6 | |
| parent | 0cc64347e47a0c3117bb4514e6b966f651f552f4 (diff) | |
| download | Ishtar-a27dca4b361b0d2f46a20f9ac2719ef811c093e0.tar.bz2 Ishtar-a27dca4b361b0d2f46a20f9ac2719ef811c093e0.zip | |
Colors and orders in short menu - cache mechanism (refs #1562)
| -rw-r--r-- | archaeological_context_records/models.py | 4 | ||||
| -rw-r--r-- | archaeological_files/models.py | 80 | ||||
| -rw-r--r-- | archaeological_finds/models.py | 5 | ||||
| -rw-r--r-- | archaeological_operations/models.py | 13 | ||||
| -rw-r--r-- | example_project/settings.py | 4 | ||||
| -rw-r--r-- | ishtar_common/context_processors.py | 14 | ||||
| -rw-r--r-- | ishtar_common/models.py | 7 | ||||
| -rw-r--r-- | ishtar_common/static/js/ishtar.js | 3 | ||||
| -rw-r--r-- | ishtar_common/static/media/style.css | 14 | ||||
| -rw-r--r-- | ishtar_common/templates/base.html | 8 | ||||
| -rw-r--r-- | ishtar_common/utils.py | 17 | 
11 files changed, 149 insertions, 20 deletions
| diff --git a/archaeological_context_records/models.py b/archaeological_context_records/models.py index b33b86e8c..4a288b5dc 100644 --- a/archaeological_context_records/models.py +++ b/archaeological_context_records/models.py @@ -23,7 +23,7 @@ from django.core.urlresolvers import reverse  from django.utils.translation import ugettext_lazy as _, ugettext, pgettext  from ishtar_common.models import GeneralType, BaseHistorizedItem, \ -          HistoricalRecords, OwnPerms, Town, Person, Source +          HistoricalRecords, OwnPerms, ShortMenuItem, Town, Person, Source  from archaeological_operations.models import Operation, Period, Parcel  class DatingType(GeneralType): @@ -92,7 +92,7 @@ class IdentificationType(GeneralType):      def __unicode__(self):          return self.label -class ContextRecord(BaseHistorizedItem, OwnPerms): +class ContextRecord(BaseHistorizedItem, OwnPerms, ShortMenuItem):      TABLE_COLS = ['parcel.town', 'operation.year',                    'operation.operation_code',                    'label', 'unit'] diff --git a/archaeological_files/models.py b/archaeological_files/models.py index db33ed9b2..b3dd99edf 100644 --- a/archaeological_files/models.py +++ b/archaeological_files/models.py @@ -1,6 +1,6 @@  #!/usr/bin/env python  # -*- coding: utf-8 -*- -# Copyright (C) 2012-2013 Étienne Loks  <etienne.loks_AT_peacefrogsDOTnet> +# Copyright (C) 2012-2014 É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 @@ -21,15 +21,16 @@ import datetime  from django.conf import settings  from django.contrib.gis.db import models +from django.core.cache import cache  from django.db.models import Q, Count, Sum  from django.db.models.signals import post_save, m2m_changed  from django.utils.translation import ugettext_lazy as _, ugettext -from ishtar_common.utils import cached_label_changed +from ishtar_common.utils import cached_label_changed, get_cache  from ishtar_common.models import GeneralType, BaseHistorizedItem, \      HistoricalRecords, OwnPerms, Person, Organization, Department, Town, \ -    Dashboard, IshtarUser, ValueGetter +    Dashboard, IshtarUser, ValueGetter, ShortMenuItem  class FileType(GeneralType):      class Meta: @@ -60,7 +61,7 @@ if settings.COUNTRY == 'fr':              verbose_name_plural = u"Types Saisine"              ordering = ('label',) -class File(BaseHistorizedItem, OwnPerms, ValueGetter): +class File(BaseHistorizedItem, OwnPerms, ValueGetter, ShortMenuItem):      TABLE_COLS = ['numeric_reference', 'year', 'internal_reference',                    'file_type', 'saisine_type', 'towns', ]      year = models.IntegerField(_(u"Year"), @@ -162,6 +163,71 @@ class File(BaseHistorizedItem, OwnPerms, ValueGetter):      def short_class_name(self):          return _(u"FILE") +    @property +    def delay_date(self): +        cache_key, val = get_cache(self.__class__, [self.pk, 'delay_date']) +        if val: +            return val +        return self.update_delay_date(cache_key) + +    def update_delay_date(self, cache_key=None): +        if not cache_key: +            cache_key, val = get_cache(self.__class__, +                                       [self.pk, 'delay_date']) +        date = self.reception_date +        if not date: +            date = datetime.date(2500, 1, 1) +        elif settings.COUNTRY == 'fr' and self.saisine_type \ +           and self.saisine_type.delay: +            date += datetime.timedelta(days=self.saisine_type.delay) +        cache.set(cache_key, date, settings.CACHE_TIMEOUT) +        return date + +    @property +    def has_adminact(self): +        cache_key, val = get_cache(self.__class__, [self.pk, +                                                    'has_adminact']) +        if val: +            return val +        return self.update_has_admin_act(cache_key) + +    def update_has_admin_act(self, cache_key=None): +        if not cache_key: +            cache_key, val = get_cache(self.__class__, [self.pk, +                                                    'has_adminact']) +        has_adminact = self.administrative_act.count() +        cache.set(cache_key, has_adminact, settings.CACHE_TIMEOUT) +        return has_adminact + +    def get_short_menu_class(self): +        cache_key, val = get_cache(self.__class__, [self.pk, +                                                    'short_class_name']) +        if val: +            return val +        return self.update_short_menu_class(cache_key) + +    def update_short_menu_class(self, cache_key=None): +        if not cache_key: +            cache_key, val = get_cache(self.__class__, [self.pk, +                                                    'short_class_name']) +        cls = 'normal' +        if not self.has_adminact and self.reception_date: +            delta = datetime.date.today() - self.reception_date +            cls = 'red' +            if self.saisine_type and self.saisine_type.delay: +                if delta.days < (self.saisine_type.delay*1/3): +                    cls = 'green' +                elif delta.days < (self.saisine_type.delay*2/3): +                    cls = 'orange' +        cache.set(cache_key, cls, settings.CACHE_TIMEOUT) +        return cls + +    @classmethod +    def get_owns(cls, user): +        owns = super(File, cls).get_owns(user) +        return sorted(owns.all(), key=lambda x:(x.has_adminact, +                                                x.delay_date)) +      @classmethod      def get_years(cls):          return [res['year'] for res in list(cls.objects.values('year').annotate( @@ -263,6 +329,12 @@ class File(BaseHistorizedItem, OwnPerms, ValueGetter):                  acts.append(act)          return acts +    def save(self, *args, **kwargs): +        returned = super(File, self).save(*args, **kwargs) +        self.update_delay_date() +        self.update_short_menu_class() +        return returned +      def is_preventive(self):          return FileType.is_preventive(self.file_type.pk) diff --git a/archaeological_finds/models.py b/archaeological_finds/models.py index 592788b88..cdc87ea95 100644 --- a/archaeological_finds/models.py +++ b/archaeological_finds/models.py @@ -24,7 +24,8 @@ from django.db.models import Max  from django.utils.translation import ugettext_lazy as _, ugettext  from ishtar_common.models import GeneralType, ImageModel, BaseHistorizedItem, \ -              LightHistorizedItem, HistoricalRecords, OwnPerms, Source, Person +    ShortMenuItem, LightHistorizedItem, HistoricalRecords, OwnPerms, Source, \ +    Person  from archaeological_operations.models import AdministrativeAct  from archaeological_context_records.models import ContextRecord, Dating @@ -141,7 +142,7 @@ class BaseFind(BaseHistorizedItem, OwnPerms):                             self.context_record.label,                             lbl) if it]) -class Find(BaseHistorizedItem, ImageModel, OwnPerms): +class Find(BaseHistorizedItem, ImageModel, OwnPerms, ShortMenuItem):      TABLE_COLS = ['label', 'material_type', 'dating.period',                    'base_finds.context_record.parcel.town',                    'base_finds.context_record.operation.year', diff --git a/archaeological_operations/models.py b/archaeological_operations/models.py index 020e66bfb..a3f61f713 100644 --- a/archaeological_operations/models.py +++ b/archaeological_operations/models.py @@ -33,7 +33,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, ValueGetter, \ -     DocumentTemplate +     DocumentTemplate, ShortMenuItem  FILES_AVAILABLE = 'archaeological_files' in settings.INSTALLED_APPS  class OperationType(GeneralType): @@ -132,7 +132,7 @@ class ArchaeologicalSite(BaseHistorizedItem):              name += u" %s %s" % (settings.JOINT, self.name)          return name -class Operation(BaseHistorizedItem, OwnPerms, ValueGetter): +class Operation(BaseHistorizedItem, OwnPerms, ValueGetter, ShortMenuItem):      TABLE_COLS = ['year_index', 'operation_type', 'remains', 'towns',                    'associated_file_short_label', 'start_date',                    'excavation_end_date'] @@ -225,6 +225,12 @@ class Operation(BaseHistorizedItem, OwnPerms, ValueGetter):              ("close_operation", ugettext(u"Can close Operation")),          ) +    @classmethod +    def get_owns(cls, user): +        owns = super(Operation, cls).get_owns(user) +        #owns = owns.annotate(null_count=Count('operation_code')) +        return owns.order_by("-year", "operation_code") +      def __unicode__(self):          if self.cached_label:              return self.cached_label @@ -614,6 +620,9 @@ class AdministrativeAct(BaseHistorizedItem, OwnPerms, ValueGetter):          self._get_index()          super(AdministrativeAct, self).save(*args, **kwargs) +        if hasattr(self, 'associated_file') and self.associated_file: +            self.associated_file.update_has_admin_act() +            self.associated_file.update_short_menu_class()  class Parcel(LightHistorizedItem):      if FILES_AVAILABLE: diff --git a/example_project/settings.py b/example_project/settings.py index 201750936..d276ef6bf 100644 --- a/example_project/settings.py +++ b/example_project/settings.py @@ -14,6 +14,10 @@ SQL_DEBUG = False  IMAGE_MAX_SIZE = (1024, 768)  THUMB_MAX_SIZE = (300, 300) +CACHE_SMALLTIMEOUT = 120 +CACHE_TIMEOUT = 3600 +CACHE_BACKEND = 'memcached://127.0.0.1:11211/' +  ROOT_PATH = os.path.abspath(os.path.curdir) + os.path.sep  STATIC_URL = '/static/'  STATIC_ROOT = ROOT_PATH + 'static/' diff --git a/ishtar_common/context_processors.py b/ishtar_common/context_processors.py index 293a5ad44..e3b14cdda 100644 --- a/ishtar_common/context_processors.py +++ b/ishtar_common/context_processors.py @@ -44,7 +44,7 @@ def get_base_context(request):      dct = {'URL_PATH':settings.URL_PATH}      dct["APP_NAME"] = Site.objects.get_current().name      dct["COUNTRY"] = settings.COUNTRY -    """  +    """      if 'MENU' not in request.session or \         request.session['MENU'].user != request.user:          menu = Menu(request.user) @@ -66,13 +66,17 @@ def get_base_context(request):      dct['current_menu'] = []      for lbl, model in CURRENT_ITEMS:          model_name = model.__name__.lower() +        cls = ''          current = model_name in request.session and request.session[model_name]          items = [] -        for item in sorted(model.get_owns(request.user), -                           key=lambda x:x.cached_label): +        for item in model.get_owns(request.user): +            selected = unicode(item.pk) == current +            if selected: +                cls = item.get_short_menu_class()              items.append((item.pk, shortify(unicode(item), 60), -                          unicode(item.pk) == current)) +                          selected, +                          item.get_short_menu_class()))          if items: -            dct['current_menu'].append((lbl, model_name, items)) +            dct['current_menu'].append((lbl, model_name, cls, items))      return dct diff --git a/ishtar_common/models.py b/ishtar_common/models.py index 3c33928a9..9eba9ccd0 100644 --- a/ishtar_common/models.py +++ b/ishtar_common/models.py @@ -193,7 +193,7 @@ class OwnPerms:          query = cls.get_query_owns(user)          if not query:              return [] -        return cls.objects.filter(query).order_by(*cls._meta.ordering).all() +        return cls.objects.filter(query).order_by(*cls._meta.ordering)  class GeneralType(models.Model):      """ @@ -518,6 +518,11 @@ class BaseHistorizedItem(models.Model):              items.append('00000000')          return u"-".join([unicode(item) for item in items]) + +class ShortMenuItem(object): +    def get_short_menu_class(self): +        return '' +  class LightHistorizedItem(BaseHistorizedItem):      history_date = models.DateTimeField(default=datetime.datetime.now)      class Meta: diff --git a/ishtar_common/static/js/ishtar.js b/ishtar_common/static/js/ishtar.js index 0a15bfc23..b91558194 100644 --- a/ishtar_common/static/js/ishtar.js +++ b/ishtar_common/static/js/ishtar.js @@ -51,6 +51,9 @@ $(document).ready(function(){      if ($.isFunction($(".prettyPhoto a").prettyPhoto)){          $(".prettyPhoto a").prettyPhoto({'social_tools':''});      } +    $('#current_items select').change(function(){ +        $(this).attr('class', $(this).children("option:selected").attr('class')); +    })  });  $('#to_bottom_arrow').live('click', function(){ diff --git a/ishtar_common/static/media/style.css b/ishtar_common/static/media/style.css index 5a7de3694..8722b5d05 100644 --- a/ishtar_common/static/media/style.css +++ b/ishtar_common/static/media/style.css @@ -17,9 +17,11 @@ div.form {  }  /* color  */ +#context_menu .red,  a, a.remove {      color:#D14;  } +  a.add-button{      color:#61615C;  } @@ -28,6 +30,18 @@ a.add-button{      color:#fff;  } +#context_menu .orange { +    color:#dd6011; +} + +#context_menu .green { +    color:#13ae0c; +} + +#context_menu .normal{ +    color:#000; +} +  /* borders */  a.add-button, a.remove,  #progress-content, diff --git a/ishtar_common/templates/base.html b/ishtar_common/templates/base.html index cea906f26..bfacee069 100644 --- a/ishtar_common/templates/base.html +++ b/ishtar_common/templates/base.html @@ -63,13 +63,13 @@          <fieldset>          <legend>{% trans "Default selected items"%}</legend>          <table id='current_items'> -        {% for lbl, model_name, items in current_menu %} +        {% for lbl, model_name, main_cls, items in current_menu %}              <tr>                  <td><label for="current_{{model_name}}">{{lbl}}</label></td>                  <td> -                  <select id='current_{{model_name}}'> -                    <option value=''>--</option> -                    {% for val, label, selected in items %}<option value='{{val}}'{%if selected%} selected="selected"{%endif%}>{{label}}</option> +                  <select class='{{main_cls}}' id='current_{{model_name}}'> +                    <option class='normal' value=''>--</option> +                    {% for val, label, selected, cls in items %}<option class='{{cls}}' value='{{val}}'{%if selected%} selected="selected"{%endif%}>{{label}}</option>                    {% endfor %}</select>                  </td>{% with 'show-'|add:model_name as model_url%}                  <td><a href='#' onclick='load_current_window("{% url model_url 0 %}", "{{model_name}}");' class='display_details'>{% trans "Details" %}</a></td> diff --git a/ishtar_common/utils.py b/ishtar_common/utils.py index d5fc37276..f50031d5d 100644 --- a/ishtar_common/utils.py +++ b/ishtar_common/utils.py @@ -17,7 +17,24 @@  # See the file COPYING for details. +from django.core.cache import cache  from django.utils.translation import ugettext +from django.template.defaultfilters import slugify + +def get_cache(cls, extra_args=[]): +    cache_key = cls.__name__ +    for arg in extra_args: +        if not arg: +            cache_key += '-0' +        else: +            if type(arg) == dict: +                cache_key += '-' + "_".join([unicode(arg[k]) for k in arg]) +            elif type(arg) in (list, tuple): +                cache_key += '-' + "_".join([unicode(v) for v in arg]) +            else: +                cache_key += '-' + unicode(arg) +    cache_key = slugify(cache_key) +    return cache_key, cache.get(cache_key)  def cached_label_changed(sender, **kwargs):      if not kwargs.get('instance'): | 
