diff options
Diffstat (limited to 'ishtar_common')
3 files changed, 194 insertions, 0 deletions
| diff --git a/ishtar_common/management/commands/relations_update_cache_tables.py b/ishtar_common/management/commands/relations_update_cache_tables.py new file mode 100644 index 000000000..ab7f134ff --- /dev/null +++ b/ishtar_common/management/commands/relations_update_cache_tables.py @@ -0,0 +1,83 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# Copyright (C) 2013-2018 É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 +# 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 <http://www.gnu.org/licenses/>. + +# See the file COPYING for details. + +import sys + +from django.core.management.base import BaseCommand + +from django.apps import apps + + +APPS = ['ishtar_common', 'archaeological_operations', +        'archaeological_context_records', 'archaeological_finds', +        'archaeological_warehouse'] + + +class Command(BaseCommand): +    args = '' +    help = 'Regenerate geo, cached labels and search vectors' + +    def add_arguments(self, parser): +        parser.add_argument('app_name', nargs='?', default=None, +                            choices=APPS) +        parser.add_argument('model_name', nargs='?', default=None) +        parser.add_argument( +            '--quiet', dest='quiet', action='store_true', +            help='Quiet output') + +    def handle(self, *args, **options): +        quiet = options['quiet'] +        limit = options['app_name'] +        model_name = options['model_name'] +        if model_name: +            model_name = model_name.lower() +        for app in APPS: +            if limit and app != limit: +                continue +            if not quiet: +                print("* app: {}".format(app)) +            for model in apps.get_app_config(app).get_models(): +                if model_name and model.__name__.lower() != model_name: +                    continue +                if model.__name__.startswith('Historical'): +                    continue +                if not bool( +                        [k for k in dir(model) +                         if k.startswith('_generate_') or +                            k == "search_vector"]): +                    continue +                msg = "-> processing {}: ".format(model._meta.verbose_name) +                ln = model.objects.count() +                for idx, obj_id in enumerate(model.objects.values('pk').all()): +                    obj = model.objects.get(pk=obj_id['pk']) +                    obj.skip_history_when_saving = True +                    obj._no_move = True +                    if hasattr(obj, "point_source") and obj.point_source in ( +                            "M", "T"): +                        obj.point = None +                        obj.point_2d = None +                        obj.x = None +                        obj.y = None +                    cmsg = "\r{} {}/{}".format(msg, idx + 1, ln) +                    if not quiet: +                        sys.stdout.write(cmsg) +                        sys.stdout.flush() +                    obj.save() +                if not quiet: +                    sys.stdout.write("\n") diff --git a/ishtar_common/migrations/0215_ishtarsiteprofile_parent_relations_engine.py b/ishtar_common/migrations/0215_ishtarsiteprofile_parent_relations_engine.py new file mode 100644 index 000000000..acf410837 --- /dev/null +++ b/ishtar_common/migrations/0215_ishtarsiteprofile_parent_relations_engine.py @@ -0,0 +1,20 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.11.28 on 2021-06-14 19:06 +from __future__ import unicode_literals + +from django.db import migrations, models + + +class Migration(migrations.Migration): + +    dependencies = [ +        ('ishtar_common', '0214_auto_20210308_1628'), +    ] + +    operations = [ +        migrations.AddField( +            model_name='ishtarsiteprofile', +            name='parent_relations_engine', +            field=models.CharField(choices=[('V', 'SQL views'), ('T', 'Cache tables')], default='V', help_text='If you experience performance problems with complex relations (for instance: complex statigraphic relations), set it to "Cache tables" in order to use static cache tables. Do not forget to update theses table with the "relations_update_cache_tables" manage.py command.', max_length=1, verbose_name='Parent relations engine'), +        ), +    ] diff --git a/ishtar_common/models.py b/ishtar_common/models.py index 94e02aafe..fdeba5f26 100644 --- a/ishtar_common/models.py +++ b/ishtar_common/models.py @@ -62,6 +62,7 @@ from django.core.exceptions import (  from django.core.files.base import ContentFile  from django.core.files.uploadedfile import SimpleUploadedFile  from django.core.urlresolvers import reverse +from django.db import connection  from django.db.models import Q, Max, Count  from django.db.models.signals import post_save, post_delete, m2m_changed  from django.db.utils import DatabaseError @@ -76,6 +77,8 @@ from ishtar_common.utils import (      get_current_profile,      duplicate_item,      get_image_path, +    serialize_args_for_tasks, +    task,  )  from ishtar_common.utils_secretary import IshtarSecretaryRenderer @@ -825,6 +828,80 @@ def post_delete_record_relation(sender, instance, **kwargs):          q.delete() +@task() +def relation_view_update(sender, kwargs): +    if isinstance(sender, (tuple, list)): +        sender = apps.get_model(*sender) +    sender._update(kwargs["item_id"]) + + +class RelationsViews(models.Model): +    CREATE_SQL = ""  # SQL view creation +    DELETE_SQL = ""  # SQL view deletion +    CREATE_TABLE_SQL = ""  # SQL table creation + +    class Meta: +        managed = False +        abstract = True + +    @classmethod +    def _update(cls, item_id): +        raise NotImplemented() + +    @classmethod +    def update(cls, item_id): +        profile = get_current_profile() +        if profile.parent_relations_engine == "V": +            return +        if not settings.USE_BACKGROUND_TASK: +            return relation_view_update(cls, {"item_id": item_id}) +        else: +            sender, kwargs = serialize_args_for_tasks( +                cls, None, {"item_id": item_id} +            ) +            return relation_view_update.delay(sender, kwargs) + +    @classmethod +    def create_table(cls): +        raise NotImplemented() + +    @classmethod +    def check_engine(cls): +        """ +        Check view or table properly created with settings on the profile +        :return: True if table or view updated +        """ +        assert cls.CREATE_SQL +        assert cls.DELETE_SQL +        assert cls.CREATE_TABLE_SQL +        profile = get_current_profile(force=True) +        table_type = '' +        with connection.cursor() as cursor: +            q = "select table_type from information_schema.tables WHERE " \ +                "table_name=%s;" +            cursor.execute(q, [cls._meta.db_table]) +            q = cursor.fetchall() +            if q: +                table_type = q[0][0] + +            if profile.parent_relations_engine == "V": +                if table_type == 'VIEW': +                    return +                elif 'TABLE' in table_type: +                    q = "DROP TABLE %s" +                    cursor.execute(q, [cls._meta.db_table]) +                cursor.execute(cls.CREATE_SQL) +                return True + +            if profile.parent_relations_engine == "T": +                if 'TABLE' in table_type: +                    return +                elif table_type == 'VIEW': +                    cursor.execute(cls.DELETE_SQL) +                cursor.execute(cls.CREATE_TABLE_SQL) +                return True + +  class SearchQuery(models.Model):      label = models.TextField(_("Label"), blank=True, default="")      query = models.TextField(_("Query"), blank=True, default="") @@ -918,6 +995,20 @@ class IshtarSiteProfile(models.Model, Cached):          _("Container - calculate weight only when all find has a weight"),          default=False,      ) +    parent_relations_engine = models.CharField( +        _("Parent relations engine"), +        choices=( +            ("V", _("SQL views")), +            ("T", _("Cache tables")), +        ), +        default="V", +        max_length=1, +        help_text=_("If you experience performance problems with complex relations " +                    "(for instance: complex statigraphic relations), set it to " +                    "\"Cache tables\" in order to use static cache tables. Do not " +                    "forget to update theses table with the " +                    "\"relations_update_cache_tables\" manage.py command.") +    )      config = models.CharField(          _("Alternate configuration"),          max_length=200, | 
