#!/usr/bin/env python # -*- coding: utf-8 -*- # Copyright (C) 2013-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 datetime from functools import wraps from itertools import chain import hashlib import random from django import forms from django.conf import settings from django.contrib.gis.geos import GEOSGeometry from django.core.cache import cache from django.core.urlresolvers import reverse from django.utils.safestring import mark_safe from django.utils.translation import ugettext_lazy as _, ugettext from django.template.defaultfilters import slugify class BColors: """ Bash colors. Don't forget to finish your colored string with ENDC. """ HEADER = '\033[95m' OKBLUE = '\033[94m' OKGREEN = '\033[92m' WARNING = '\033[93m' FAIL = '\033[91m' ENDC = '\033[0m' BOLD = '\033[1m' UNDERLINE = '\033[4m' def get_current_year(): return datetime.datetime.now().year def get_cache(cls, extra_args=[]): cache_key = u"{}-{}-{}".format( settings.PROJECT_SLUG, cls._meta.app_label, 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) if not cache_key.endswith('_current_keys') \ and hasattr(cls, '_add_cache_key_to_refresh'): cls._add_cache_key_to_refresh(extra_args) if len(cache_key) >= 250: m = hashlib.md5() m.update(cache_key) cache_key = m.hexdigest() return cache_key, cache.get(cache_key) def force_cached_label_changed(sender, **kwargs): if not kwargs.get('instance'): return kwargs['instance']._cached_label_checked = False cached_label_changed(sender, **kwargs) def cached_label_changed(sender, **kwargs): if not kwargs.get('instance'): return instance = kwargs.get('instance') if hasattr(instance, 'test_obj'): instance.test_obj.reached(sender, **kwargs) if hasattr(instance, '_cached_label_checked') \ and instance._cached_label_checked: return instance._cached_label_checked = True cached_labels = ['cached_label'] if hasattr(sender, 'CACHED_LABELS'): cached_labels = sender.CACHED_LABELS changed = False for cached_label in cached_labels: lbl = getattr(instance, '_generate_' + cached_label)() if lbl != getattr(instance, cached_label): setattr(instance, cached_label, lbl) changed = True if changed: instance._search_updated = False if hasattr(instance, '_cascade_change') and instance._cascade_change: instance.skip_history_when_saving = True instance.save() if hasattr(instance, 'update_search_vector'): instance.update_search_vector() updated = False if hasattr(instance, '_cached_labels_bulk_update'): updated = instance._cached_labels_bulk_update() if not updated and hasattr(instance, '_get_associated_cached_labels'): for item in instance._get_associated_cached_labels(): item._cascade_change = True if hasattr(instance, 'test_obj'): item.test_obj = instance.test_obj cached_label_changed(item.__class__, instance=item) SHORTIFY_STR = ugettext(" (...)") def shortify(lbl, number=20): if not lbl: lbl = '' if len(lbl) <= number: return lbl return lbl[:number - len(SHORTIFY_STR)] + SHORTIFY_STR def mode(array): most = max(list(map(array.count, array))) return list(set(filter(lambda x: array.count(x) == most, array))) def disable_for_loaddata(signal_handler): """ Decorator that turns off signal handlers when loading fixture data. """ @wraps(signal_handler) def wrapper(*args, **kwargs): if kwargs.get('raw'): return signal_handler(*args, **kwargs) return wrapper def _get_image_link(item): # manage missing images if not item.thumbnail or not item.thumbnail.url: return "" return mark_safe(u"""

{} - {}
""".format( item.thumbnail.url, unicode(item.__class__._meta.verbose_name), unicode(item), reverse(item.SHOW_URL, args=[item.pk, '']), unicode(_(u"Load another random image?")))) def get_random_item_image_link(request): from archaeological_operations.models import Operation from archaeological_context_records.models import ContextRecord from archaeological_finds.models import Find ope_image_nb, cr_image_nb, find_image_nb = 0, 0, 0 q_ope = Operation.objects.filter( thumbnail__isnull=False).exclude(thumbnail='') q_cr = ContextRecord.objects.filter( thumbnail__isnull=False).exclude(thumbnail='') q_find = Find.objects.filter( thumbnail__isnull=False).exclude(thumbnail='') if request.user.has_perm('archaeological_operations.view_operation', Operation): ope_image_nb = q_ope.count() if request.user.has_perm( 'archaeological_context_records.view_contextrecord', ContextRecord): cr_image_nb = q_cr.count() if request.user.has_perm('archaeological_finds.view_find', Find): find_image_nb = q_find.count() image_total = ope_image_nb + cr_image_nb + find_image_nb if not image_total: return '' image_nb = random.randint(0, image_total - 1) if image_nb >= 0 and image_nb < ope_image_nb: return _get_image_link(q_ope.all()[image_nb]) if image_nb >= ope_image_nb and image_nb < (cr_image_nb + ope_image_nb): return _get_image_link(q_cr.all()[image_nb - ope_image_nb]) if image_nb >= (cr_image_nb + ope_image_nb): return _get_image_link(q_find.all()[ image_nb - ope_image_nb - cr_image_nb]) # should never happen except in case of deletion during the excution return '' def convert_coordinates_to_point(x, y, z=None, srid=4326): if z: geom = GEOSGeometry('POINT({} {} {})'.format(x, y, z), srid=srid) else: geom = GEOSGeometry('POINT({} {})'.format(x, y), srid=srid) if not geom.valid: raise forms.ValidationError(geom.valid_reason) return geom def post_save_point(sender, **kwargs): """ Convert raw x, y, z point to real geo field """ if not kwargs.get('instance'): return instance = kwargs.get('instance') point = None point_2d = None if instance.x and instance.y and \ instance.spatial_reference_system and \ instance.spatial_reference_system.auth_name == 'EPSG' and \ instance.spatial_reference_system.srid != 0: point_2d = convert_coordinates_to_point( instance.x, instance.y, srid=instance.spatial_reference_system.srid) if instance.z: point = convert_coordinates_to_point( instance.x, instance.y, instance.z, srid=instance.spatial_reference_system.srid) if point_2d != instance.point_2d or point != instance.point: instance.point = point instance.point_2d = point_2d instance.skip_history_when_saving = True instance.save() return def create_slug(model, name, slug_attr='slug', max_length=100): base_slug = slugify(name) slug = base_slug[:max_length] final_slug = None idx = 1 while not final_slug: if slug and not model.objects.filter(**{slug_attr:slug}).exists(): final_slug = slug break slug = base_slug[:(max_length - 1 - len(str(idx)))] + "-" + str(idx) idx += 1 return final_slug def get_all_field_names(model): return list(set(chain.from_iterable( (field.name, field.attname) if hasattr(field, 'attname') else ( field.name,) for field in model._meta.get_fields() if not (field.many_to_one and field.related_model is None) ))) def get_all_related_m2m_objects_with_model(model): return [ (f, f.model if f.model != model else None) for f in model._meta.get_fields(include_hidden=True) if f.many_to_many and f.auto_created ] def get_all_related_many_to_many_objects(model): return [ f for f in model._meta.get_fields(include_hidden=True) if f.many_to_many and f.auto_created ] def get_all_related_objects(model): return [ f for f in model._meta.get_fields() if (f.one_to_many or f.one_to_one) and f.auto_created and not f.concrete ] def merge_tsvectors(vectors): """ Parse tsvector to merge them in one string :param vectors: list of tsvector string :return: merged tsvector """ result_dict = {} for vector in vectors: if not vector: continue current_position = 0 if result_dict: for key in result_dict: max_position = max(result_dict[key]) if max_position > current_position: current_position = max_position for dct_member in vector.split(" "): splitted = dct_member.split(':') key = ":".join(splitted[:-1]) positions = splitted[-1] key = key[1:-1] # remove quotes positions = [int(pos) + current_position for pos in positions.split(',')] if key in result_dict: result_dict[key] += positions else: result_dict[key] = positions # {'lamelie': [1, 42, 5]} => {'lamelie': "1,42,5"} result_dict = {k: ",".join([str(val) for val in result_dict[k]]) for k in result_dict} # {'lamelie': "1,5", "hagarde": "2", "regarde": "4"} => # "'lamelie':1,5 'hagarde':2 'regarde':4" result = " ".join(["'{}':{}".format(k, result_dict[k]) for k in result_dict]) return result