summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--archaeological_context_records/migrations/0044_auto_20190225_1637.py26
-rw-r--r--archaeological_context_records/models.py4
-rw-r--r--archaeological_finds/migrations/0065_auto_20190225_1637.py26
-rw-r--r--archaeological_finds/models_finds.py6
-rw-r--r--archaeological_operations/migrations/0054_auto_20190225_1637.py36
-rw-r--r--archaeological_operations/models.py10
-rw-r--r--archaeological_warehouse/migrations/0035_auto_20190225_1637.py26
-rw-r--r--archaeological_warehouse/models.py7
-rw-r--r--example_project/settings.py2
-rwxr-xr-xinstall/ishtar-install8
-rw-r--r--ishtar_common/management/commands/regenerate_qrcodes.py73
-rw-r--r--ishtar_common/models.py60
-rw-r--r--ishtar_common/templates/base.html1
-rw-r--r--requirements.txt3
14 files changed, 265 insertions, 23 deletions
diff --git a/archaeological_context_records/migrations/0044_auto_20190225_1637.py b/archaeological_context_records/migrations/0044_auto_20190225_1637.py
new file mode 100644
index 000000000..5092fa2e7
--- /dev/null
+++ b/archaeological_context_records/migrations/0044_auto_20190225_1637.py
@@ -0,0 +1,26 @@
+# -*- coding: utf-8 -*-
+# Generated by Django 1.11.10 on 2019-02-25 16:37
+from __future__ import unicode_literals
+
+from django.db import migrations, models
+import ishtar_common.models
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('archaeological_context_records', '0043_auto_20190218_1808'),
+ ]
+
+ operations = [
+ migrations.AddField(
+ model_name='contextrecord',
+ name='qrcode',
+ field=models.ImageField(blank=True, max_length=255, null=True, upload_to=ishtar_common.models.get_image_path),
+ ),
+ migrations.AddField(
+ model_name='historicalcontextrecord',
+ name='qrcode',
+ field=models.TextField(blank=True, max_length=255, null=True),
+ ),
+ ]
diff --git a/archaeological_context_records/models.py b/archaeological_context_records/models.py
index 004e292d9..cd2b5f382 100644
--- a/archaeological_context_records/models.py
+++ b/archaeological_context_records/models.py
@@ -35,7 +35,7 @@ from ishtar_common.models import Document, GeneralType, \
GeneralRelationType, GeneralRecordRelations, post_delete_record_relation,\
post_save_cache, ValueGetter, BulkUpdatedItem, ExternalIdManager, \
RelationItem, Town, get_current_profile, document_attached_changed, \
- HistoryModel, SearchAltName, GeoItem
+ HistoryModel, SearchAltName, GeoItem, QRCodeItem
from archaeological_operations.models import Operation, Period, Parcel, \
ArchaeologicalSite
@@ -269,7 +269,7 @@ class CRBulkView(object):
"""
-class ContextRecord(BulkUpdatedItem, BaseHistorizedItem, GeoItem,
+class ContextRecord(BulkUpdatedItem, BaseHistorizedItem, QRCodeItem, GeoItem,
OwnPerms, ValueGetter, ShortMenuItem, RelationItem):
SHOW_URL = 'show-contextrecord'
SLUG = 'contextrecord'
diff --git a/archaeological_finds/migrations/0065_auto_20190225_1637.py b/archaeological_finds/migrations/0065_auto_20190225_1637.py
new file mode 100644
index 000000000..4d9275936
--- /dev/null
+++ b/archaeological_finds/migrations/0065_auto_20190225_1637.py
@@ -0,0 +1,26 @@
+# -*- coding: utf-8 -*-
+# Generated by Django 1.11.10 on 2019-02-25 16:37
+from __future__ import unicode_literals
+
+from django.db import migrations, models
+import ishtar_common.models
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('archaeological_finds', '0064_auto_20190218_1808'),
+ ]
+
+ operations = [
+ migrations.AddField(
+ model_name='find',
+ name='qrcode',
+ field=models.ImageField(blank=True, max_length=255, null=True, upload_to=ishtar_common.models.get_image_path),
+ ),
+ migrations.AddField(
+ model_name='historicalfind',
+ name='qrcode',
+ field=models.TextField(blank=True, max_length=255, null=True),
+ ),
+ ]
diff --git a/archaeological_finds/models_finds.py b/archaeological_finds/models_finds.py
index 01f4b719f..084e2b8d7 100644
--- a/archaeological_finds/models_finds.py
+++ b/archaeological_finds/models_finds.py
@@ -41,7 +41,7 @@ from ishtar_common.models import Document, GeneralType, \
ValueGetter, get_current_profile, IshtarSiteProfile, PRIVATE_FIELDS, \
GeoItem, BulkUpdatedItem, ExternalIdManager, QuickAction, \
MainItem, document_attached_changed, HistoryModel, DynamicRequest, \
- SearchAltName
+ SearchAltName, QRCodeItem
from archaeological_operations.models import AdministrativeAct, Operation
@@ -700,8 +700,8 @@ def query_loan(is_true=True):
container_ref=F('container')), None, None
-class Find(BulkUpdatedItem, ValueGetter, BaseHistorizedItem, OwnPerms,
- MainItem):
+class Find(BulkUpdatedItem, ValueGetter, BaseHistorizedItem, QRCodeItem,
+ OwnPerms, MainItem):
EXTERNAL_ID_KEY = 'find_external_id'
SHOW_URL = 'show-find'
SLUG = 'find'
diff --git a/archaeological_operations/migrations/0054_auto_20190225_1637.py b/archaeological_operations/migrations/0054_auto_20190225_1637.py
new file mode 100644
index 000000000..724631757
--- /dev/null
+++ b/archaeological_operations/migrations/0054_auto_20190225_1637.py
@@ -0,0 +1,36 @@
+# -*- coding: utf-8 -*-
+# Generated by Django 1.11.10 on 2019-02-25 16:37
+from __future__ import unicode_literals
+
+from django.db import migrations, models
+import ishtar_common.models
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('archaeological_operations', '0053_auto_20190218_1808'),
+ ]
+
+ operations = [
+ migrations.AddField(
+ model_name='archaeologicalsite',
+ name='qrcode',
+ field=models.ImageField(blank=True, max_length=255, null=True, upload_to=ishtar_common.models.get_image_path),
+ ),
+ migrations.AddField(
+ model_name='historicalarchaeologicalsite',
+ name='qrcode',
+ field=models.TextField(blank=True, max_length=255, null=True),
+ ),
+ migrations.AddField(
+ model_name='historicaloperation',
+ name='qrcode',
+ field=models.TextField(blank=True, max_length=255, null=True),
+ ),
+ migrations.AddField(
+ model_name='operation',
+ name='qrcode',
+ field=models.ImageField(blank=True, max_length=255, null=True, upload_to=ishtar_common.models.get_image_path),
+ ),
+ ]
diff --git a/archaeological_operations/models.py b/archaeological_operations/models.py
index d79bce7f3..a09798f1f 100644
--- a/archaeological_operations/models.py
+++ b/archaeological_operations/models.py
@@ -39,7 +39,7 @@ from ishtar_common.models import BaseHistorizedItem, Dashboard, \
post_delete_record_relation, post_save_cache, RelationItem, \
ShortMenuItem, SourceType, Town, ValueGetter, get_current_profile, \
document_attached_changed, HistoryModel, SearchAltName, \
- GeoItem
+ GeoItem, QRCodeItem
from ishtar_common.utils import cached_label_changed, \
force_cached_label_changed, mode, m2m_historization_changed, post_save_geo
@@ -107,8 +107,8 @@ post_save.connect(post_save_cache, sender=RecordQualityType)
post_delete.connect(post_save_cache, sender=RecordQualityType)
-class ArchaeologicalSite(BaseHistorizedItem, GeoItem, OwnPerms, ValueGetter,
- ShortMenuItem):
+class ArchaeologicalSite(BaseHistorizedItem, QRCodeItem, GeoItem, OwnPerms,
+ ValueGetter, ShortMenuItem):
SHOW_URL = 'show-site'
TABLE_COLS = ['reference', 'name', 'towns_label', 'periods', 'remains']
SLUG = 'site'
@@ -509,8 +509,8 @@ class OperationManager(models.GeoManager):
return self.get(code_patriarche=txt_idx)
-class Operation(ClosedItem, BaseHistorizedItem, GeoItem, OwnPerms, ValueGetter,
- ShortMenuItem, DashboardFormItem, RelationItem):
+class Operation(ClosedItem, BaseHistorizedItem, QRCodeItem, GeoItem, OwnPerms,\
+ ValueGetter, ShortMenuItem, DashboardFormItem, RelationItem):
SHOW_URL = 'show-operation'
TABLE_COLS = ['year', 'towns_label', 'common_name', 'operation_type',
'start_date', 'excavation_end_date', 'remains']
diff --git a/archaeological_warehouse/migrations/0035_auto_20190225_1637.py b/archaeological_warehouse/migrations/0035_auto_20190225_1637.py
new file mode 100644
index 000000000..4f892a3a7
--- /dev/null
+++ b/archaeological_warehouse/migrations/0035_auto_20190225_1637.py
@@ -0,0 +1,26 @@
+# -*- coding: utf-8 -*-
+# Generated by Django 1.11.10 on 2019-02-25 16:37
+from __future__ import unicode_literals
+
+from django.db import migrations, models
+import ishtar_common.models
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('archaeological_warehouse', '0034_auto_20190218_1808'),
+ ]
+
+ operations = [
+ migrations.AddField(
+ model_name='container',
+ name='qrcode',
+ field=models.ImageField(blank=True, max_length=255, null=True, upload_to=ishtar_common.models.get_image_path),
+ ),
+ migrations.AddField(
+ model_name='warehouse',
+ name='qrcode',
+ field=models.ImageField(blank=True, max_length=255, null=True, upload_to=ishtar_common.models.get_image_path),
+ ),
+ ]
diff --git a/archaeological_warehouse/models.py b/archaeological_warehouse/models.py
index 795c879e4..115f0d7ea 100644
--- a/archaeological_warehouse/models.py
+++ b/archaeological_warehouse/models.py
@@ -31,7 +31,8 @@ from ishtar_common.data_importer import post_importer_action
from ishtar_common.models import Document, GeneralType, get_external_id, \
LightHistorizedItem, OwnPerms, Address, Person, post_save_cache, \
DashboardFormItem, ExternalIdManager, ShortMenuItem, \
- document_attached_changed, SearchAltName, DynamicRequest, GeoItem
+ document_attached_changed, SearchAltName, DynamicRequest, GeoItem, \
+ QRCodeItem
from ishtar_common.utils import cached_label_changed, post_save_geo
@@ -46,7 +47,7 @@ post_save.connect(post_save_cache, sender=WarehouseType)
post_delete.connect(post_save_cache, sender=WarehouseType)
-class Warehouse(Address, GeoItem, DashboardFormItem, OwnPerms,
+class Warehouse(Address, GeoItem, QRCodeItem, DashboardFormItem, OwnPerms,
ShortMenuItem):
SLUG = 'warehouse'
SHOW_URL = 'show-warehouse'
@@ -322,7 +323,7 @@ post_save.connect(post_save_cache, sender=ContainerType)
post_delete.connect(post_save_cache, sender=ContainerType)
-class Container(LightHistorizedItem, GeoItem, OwnPerms):
+class Container(LightHistorizedItem, QRCodeItem, GeoItem, OwnPerms):
SLUG = 'container'
SHOW_URL = 'show-container'
TABLE_COLS = ['reference', 'container_type__label', 'cached_location',
diff --git a/example_project/settings.py b/example_project/settings.py
index bedcfaec7..9c742eedf 100644
--- a/example_project/settings.py
+++ b/example_project/settings.py
@@ -231,6 +231,8 @@ USE_BACKGROUND_TASK = False
# Ishtar custom
ISHTAR_MAP_MAX_ITEMS = 50000
+ISHTAR_QRCODE_VERSION = 10 # density of the QR code
+ISHTAR_QRCODE_SCALE = 3 # scale of the QR code
SRID = 27572
SURFACE_SRID = 2154
diff --git a/install/ishtar-install b/install/ishtar-install
index 9b9f29d2d..f0db9a40f 100755
--- a/install/ishtar-install
+++ b/install/ishtar-install
@@ -422,6 +422,14 @@ EOF
( set -x; $sh_c 'sleep 3; apt-get --no-install-recommends install -y -q \
libreoffice libreoffice-script-provider-python python3-uno' )
fi
+
+ # buster: python-pyqrcode python-png
+ echo "-------------------------------------------------------------------------------";
+ cecho y "Installing pyqrcode"
+ echo "";
+ ( set -x; $sh_c 'pip install pyqrcode==1.2.1' )
+ ( set -x; $sh_c 'pip install pypng==0.0.19' )
+
echo "-------------------------------------------------------------------------------";
cecho y "Installing django-simple-history"
echo "";
diff --git a/ishtar_common/management/commands/regenerate_qrcodes.py b/ishtar_common/management/commands/regenerate_qrcodes.py
new file mode 100644
index 000000000..e56573b06
--- /dev/null
+++ b/ishtar_common/management/commands/regenerate_qrcodes.py
@@ -0,0 +1,73 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+# Copyright (C) 2019 É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
+import tempfile
+import shutil
+
+from django.core.management.base import BaseCommand
+from django.core.exceptions import FieldDoesNotExist
+
+from django.apps import apps
+
+
+APPS = ['ishtar_common', 'archaeological_operations',
+ 'archaeological_context_records', 'archaeological_finds',
+ 'archaeological_warehouse']
+
+
+class Command(BaseCommand):
+ args = ''
+ help = 'Regenerate QR codes'
+
+ def add_arguments(self, parser):
+ parser.add_argument('app_name', nargs='?', default=None,
+ choices=APPS)
+ parser.add_argument('model_name', nargs='?', default=None)
+
+ def handle(self, *args, **options):
+ 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
+ print(u"* 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
+ try:
+ model._meta.get_field('qrcode')
+ except FieldDoesNotExist:
+ continue
+ msg = u"-> processing {}: ".format(model._meta.verbose_name)
+ ln = model.objects.count()
+ tmpdir = tempfile.mkdtemp("-qrcode")
+ for idx, object in enumerate(model.objects.all()):
+ object.skip_history_when_saving = True
+ object._no_move = True
+ cmsg = u"\r{} {}/{}".format(msg, idx + 1, ln)
+ sys.stdout.write(cmsg)
+ sys.stdout.flush()
+ object.generate_qrcode(secure=False, tmpdir=tmpdir)
+ shutil.rmtree(tmpdir)
+ sys.stdout.write("\n")
diff --git a/ishtar_common/models.py b/ishtar_common/models.py
index 9dd90de65..5e81e80a0 100644
--- a/ishtar_common/models.py
+++ b/ishtar_common/models.py
@@ -28,6 +28,7 @@ from jinja2 import TemplateSyntaxError
import json
import logging
import os
+import pyqrcode
import re
import shutil
import tempfile
@@ -45,9 +46,11 @@ from django.contrib.contenttypes.models import ContentType
from django.contrib.gis.db import models
from django.contrib.postgres.fields import JSONField
from django.contrib.postgres.search import SearchVectorField, SearchVector
+from django.contrib.sites.models import Site
from django.core.cache import cache
from django.core.exceptions import ObjectDoesNotExist, ValidationError
from django.core.files.uploadedfile import SimpleUploadedFile
+from django.core.files import File
from django.core.serializers import serialize
from django.core.urlresolvers import reverse, NoReverseMatch
from django.core.validators import validate_slug
@@ -1008,7 +1011,15 @@ def get_image_path(instance, filename):
return instance._get_image_path(filename)
-class ImageModel(models.Model):
+class ImageContainerModel(object):
+ def _get_image_path(self, filename):
+ return u"{}/{}".format(self._get_base_image_path(), filename)
+
+ def _get_base_image_path(self):
+ return u"upload"
+
+
+class ImageModel(models.Model, ImageContainerModel):
image = models.ImageField(upload_to=get_image_path, blank=True, null=True,
max_length=255, help_text=max_size_help())
thumbnail = models.ImageField(
@@ -1021,12 +1032,6 @@ class ImageModel(models.Model):
class Meta:
abstract = True
- def _get_image_path(self, filename):
- return u"{}/{}".format(self._get_base_image_path(), filename)
-
- def _get_base_image_path(self):
- return u"upload"
-
def has_changed(self, field):
if not self.pk:
return True
@@ -1566,6 +1571,40 @@ class FixAssociated(object):
setattr(item, subkey, new_value)
+class QRCodeItem(models.Model, ImageContainerModel):
+ qrcode = models.ImageField(upload_to=get_image_path, blank=True, null=True,
+ max_length=255)
+
+ class Meta:
+ abstract = True
+
+ def generate_qrcode(self, request=None, secure=True, tmpdir=None):
+ url = self.get_absolute_url()
+ site = Site.objects.get_current()
+ if request:
+ scheme = self.request.scheme
+ else:
+ if secure:
+ scheme = "https"
+ else:
+ scheme = "http"
+ url = scheme + "://" + site.domain + url
+ qr = pyqrcode.create(url, version=settings.ISHTAR_QRCODE_VERSION)
+ tmpdir_created = False
+ if not tmpdir:
+ tmpdir = tempfile.mkdtemp("-qrcode")
+ tmpdir_created = True
+ filename = tmpdir + os.sep + 'qrcode.png'
+ qr.png(filename, scale=settings.ISHTAR_QRCODE_SCALE)
+ self.qrcode.save(
+ "qrcode.png", File(open(filename, 'rb')))
+ self.skip_history_when_saving = True
+ self._no_move = True
+ self.save()
+ if tmpdir_created:
+ shutil.rmtree(tmpdir)
+
+
class DocumentItem(object):
@property
def images(self):
@@ -1738,6 +1777,7 @@ class BaseHistorizedItem(DocumentItem, FullSearch, Imported, JsonData,
All historized items are searchable and have a data json field.
"""
IS_BASKET = False
+ SHOW_URL = None
EXTERNAL_ID_KEY = ''
EXTERNAL_ID_DEPENDENCIES = []
HISTORICAL_M2M = []
@@ -1906,9 +1946,11 @@ class BaseHistorizedItem(DocumentItem, FullSearch, Imported, JsonData,
return values
def get_show_url(self):
+ show_url = self.SHOW_URL
+ if not show_url:
+ show_url = 'show-' + self.__class__.__name__.lower()
try:
- return reverse('show-' + self.__class__.__name__.lower(),
- args=[self.pk, ''])
+ return reverse(show_url, args=[self.pk, ''])
except NoReverseMatch:
return
diff --git a/ishtar_common/templates/base.html b/ishtar_common/templates/base.html
index 8c2a83713..bcf965ab2 100644
--- a/ishtar_common/templates/base.html
+++ b/ishtar_common/templates/base.html
@@ -52,7 +52,6 @@
var complete_list_label = "{% trans 'complete list...' %}";
var added_message = "{% trans " items added." %}";
var select_only_one_msg = "{% trans "Select only one item." %}";
- var session
var YES = "{% trans 'yes' %}";
var NO = "{% trans 'no' %}";
var show_msg = "{% trans "Show" %}";
diff --git a/requirements.txt b/requirements.txt
index e79fdd1a7..2e281d125 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -6,6 +6,9 @@ Pillow==3.4.2
WeasyPrint==0.41
html5lib==0.999999999
+pyqrcode==1.2.1
+pypng==0.0.19
+
requests==2.12
dbf==0.96.003