cookiecutter template

my-submodulepoetico
Guido Longoni 2023-02-08 17:20:37 +01:00
commit 80a4b62b40
88 changed files with 36030 additions and 0 deletions

13
.dockerignore 100644
View File

@ -0,0 +1,13 @@
*/.git
*/node_modules
*/__pycache__
*.pyc
*/log.json
*/conf_auto.js
*/conf_auto.js.gz
*/puntamenti.js
*/vue/dist
red/*
dist.*
docker/.cups*
*/.venv

10
.gitignore vendored 100644
View File

@ -0,0 +1,10 @@
.env
.vscode/
*.sqlite3
*.pyc
nginx.pid
nginx_localrun.conf
nginx.conf
django_static
!docker/deployment/staging/.env
!docker/deployment/production/.env

92
Dockerfile 100644
View File

@ -0,0 +1,92 @@
# syntax=docker/dockerfile:1.3
FROM python:3.10-slim-bullseye AS sistema_base
USER root
RUN useradd --create-home --uid 1000 --shell /bin/bash briq
# installo le dipendenze che servono anche a runtime
RUN apt-get update\
&& apt-get install -y libpq-dev nginx curl bash\
gettext mosquitto-clients libjpeg-dev postgresql-client-13 \
&& rm -rf /var/lib/apt/lists/* \
&& rm -rf /var/cache/apt/archives
RUN mkdir -p /django /django_static /var/lib/nginx /vue /logs /home/briq/.cache/pypoetry && \
touch /nginx.conf /maybe_ssl.conf /full_chain.pem /poetry_location && \
chown -R briq:briq /var/lib/nginx /django /vue /django_static /nginx.conf /logs /maybe_ssl.conf /full_chain.pem /poetry_location /home/briq/.cache/pypoetry
SHELL ["/bin/bash", "-c"]
USER briq
RUN curl -sSL https://install.python-poetry.org | python3 -\
&& echo "/home/briq/.local/bin/poetry" > /poetry_location
USER root
RUN ln -s "$(cat /poetry_location)" /usr/bin/ && rm /poetry_location
FROM sistema_base as sistema_build
USER root
RUN apt-get update\
&& apt-get -y install npm --no-install-recommends\
&& npm install -g javascript-obfuscator\
&& rm -rf /var/lib/apt/lists/* \
&& rm -rf /var/cache/apt/archives
FROM sistema_build as sistema_build_dipendenze_python
USER root
RUN apt-get update\
&& apt-get -y install gcc build-essential git libpython3-dev ccache
COPY --chown=briq:briq /django/pyproject.toml /django/poetry.lock /django/
USER briq
RUN --mount=target=/home/briq/.cache/pypoetry/cache,type=cache,uid=1000,gid=1000 cd /django && poetry install --no-dev
USER root
RUN bash -c "set -e; find / -iname '__pycache__' -prune -exec rm -rf '{}' \; \
&& find / -iname '*.pyc' -delete"
FROM sistema_base AS sistema_base_con_python
USER briq:briq
COPY --from=sistema_build_dipendenze_python /home/briq/.cache/pypoetry/ /home/briq/.cache/pypoetry/
FROM sistema_build_dipendenze_python as sistema_build_python
USER briq:briq
WORKDIR /django
RUN --mount=target=/home/briq/.cache/pypoetry/cache,type=cache,uid=1000,gid=1000 poetry install
USER root
COPY --chown=briq:briq django /django/
USER briq:briq
RUN --mount=target=/ccache,type=cache,uid=1000,gid=1000 env CCACHE_DIR=/ccache bash compila_apps.sh
USER root
FROM sistema_build AS sistema_build_js
USER root
COPY --chown=briq:briq vue/package.json vue/package-lock.json /vue/
USER briq:briq
WORKDIR /vue
RUN npm install
COPY /vue/ /vue/
#COPY docker/babel.config.js /vue/babel.config.js
RUN npm run build
FROM sistema_base_con_python AS sistema_deploy
USER briq:briq
COPY --from=sistema_build_python /django_static/ /django_static/
RUN true #vedi https://github.com/moby/moby/issues/37965
COPY --from=sistema_build_python /django /django/
RUN true
COPY --from=sistema_build_python /django/pyproject.toml /django/
RUN true
COPY --from=sistema_build_js /vue/dist /vue/dist
WORKDIR /
ARG GIT_COMMIT_SHA=N/A
ARG GIT_COMMIT_REF=N/A
ARG GIT_CLEAN=true
ARG GIT_COMMIT_DATE=ContinuousIntegration
USER root
RUN echo "{\"shash\": \"${GIT_COMMIT_SHA}\", \"lhash\": \"${GIT_COMMIT_SHA}\", \"commsdate\": \"${GIT_COMMIT_DATE}\", \"refnames\": \"${GIT_COMMIT_REF}\", \"clean\":\"${GIT_CLEAN}\"}" > /git_info
COPY --chown=briq:briq docker/test_and_build/run_in_docker.sh docker/test_and_build/startstop_utils.sh docker/test_and_build/nginx.conf.tpl /
RUN cp -f /git_info /vue/dist/ver.json
RUN chown briq:briq /vue/dist/ver.json
USER briq:briq
CMD /run_in_docker.sh

27
Jenkinsfile vendored 100644
View File

@ -0,0 +1,27 @@
node {
stage('Checkout') {
checkout scm
}
if ("${env.BRANCH_NAME}".startsWith('my-')) {
print "è un branch da non compilare"
currentBuild.result = 'SUCCESS'
return
}
stage('Extract Git Info') {
commitId = sh(returnStdout: true, script: 'git --no-pager log -1 --pretty=format:"%H"')
commitDate = sh(returnStdout: true, script: 'git --no-pager log -1 --pretty=format:"%ai"')
refs = sh(returnStdout: true, script: 'git --no-pager log -1 --pretty=format:"%d"')
}
stage('Build docker') {
withEnv(['DOCKER_BUILDKIT=1']) {
customImage = docker.build("briq/sangue:${env.BUILD_ID}", "--build-arg GIT_COMMIT_SHA=\"${commitId}\" --build-arg GIT_COMMIT_DATE=\"${commitDate}\" --build-arg GIT_COMMIT_REF=\"${refs}\" .")
}
}
stage('Push to registry') {
catchError(buildResult: 'SUCCESS', stageResult: 'FAILURE') {
docker.withRegistry('https://docker.briq.it', 'briq-docker-cred') {
customImage.push("${env.BRANCH_NAME}")
}
}
}
}

1
README.md 100644
View File

@ -0,0 +1 @@
# sangue_app

View File

View File

@ -0,0 +1,12 @@
{% extends "admin/base_site.html" %}
{% load i18n %}
{% block breadcrumbs %}<div class="breadcrumbs"><a href="{% url 'admin:index' %}">{% translate 'Home' %}</a></div>{% endblock %}
{% block nav-sidebar %}{% endblock %}
{% block content %}
<p><a href="{% url 'admin:index' %}">{% translate 'Log in again' %}</a></p>
{% endblock %}

View File

@ -0,0 +1,22 @@
#!/bin/bash --
set -e
IFS=$'\n'
mkdir -p /django_static
find /django \( -iname '*.js' -and ! -path '*/vendor/*' \) -prune -exec bash -c 'set -x; mv "$1" "$1.orig.js" && javascript-obfuscator "$1.orig.js" -o "$1" && rm "$1.orig.js"' '' '{}' \;
env DEBUG_TOOLBAR=true poetry run python manage.py collectstatic --no-input
for DJANGO_APP in $(poetry run python manage.py shell < get_django_apps.py); do
echo -ne "Compilo app $DJANGO_APP..."
rm -rf "${DJANGO_APP}/migrations"
poetry run python -m nuitka --module "${DJANGO_APP}" --include-package="${DJANGO_APP}"
rm -rf "${DJANGO_APP}.build"
pushd "${DJANGO_APP}" || exit 1
find . -iname '*.py*' -delete;
popd
done
find . -iname '__pycache__' -prune -exec rm -rf '{}' \;
find . -iname '*.pyc' -delete
find . -iname 'compila_apps.sh' -delete
find . -iname 'sqldes2django.py' -delete
find . -iname 'modello_*.xml' -delete
#poetry cache:clear --all -n ..
#poetry install --no-dev && rm -rf /root/.cache/pypoetry/cache/

View File

@ -0,0 +1,16 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Created on Wed Nov 27 17:59:25 2019
@author: Guido Longoni - guidolongoni@gmail.com
"""
import os
from django.conf import settings
out = set()
for x in settings.INSTALLED_APPS:
stem = x.split('.')[0]
if stem in next(d for _, d, _ in os.walk('.')):
out.add(stem)
print('\n'.join(out))

View File

@ -0,0 +1,11 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Created on Wed Nov 27 17:59:25 2019
@author: Guido Longoni - guidolongoni@gmail.com
"""
import os
from django.conf import settings
print(settings.ASGI_APPLICATION.split('.')[0])

View File

@ -0,0 +1,23 @@
from django.db import connection
import logging
import sys
from manage import main
logger = logging.getLogger("locking_manage")
logger.setLevel(logging.DEBUG)
formatter = logging.Formatter(
'%(asctime)-24s|%(levelname)-7s|%(name)-4s:%(lineno)-2d: %(message)s')
console_h = logging.StreamHandler(sys.stderr)
console_h.setLevel(logging.DEBUG)
console_h.setFormatter(formatter)
logger.addHandler(console_h)
with connection.cursor() as c:
# acquisisco un lock con una chiave che è unica per questa applicazione
logger.info("Acquisizione advisory lock:")
c.execute("select pg_advisory_lock(132306982255);")
logger.info("advisory lock acquisito.")
logger.info("Eseguo manage.py con argomenti %s", sys.argv)
main()
logger.info("Rilascio advisory lock")
# il lock viene rilasciato automaticamente

22
django/manage.py 100755
View File

@ -0,0 +1,22 @@
#!/usr/bin/env python
"""Django's command-line utility for administrative tasks."""
import os
import sys
def main():
"""Run administrative tasks."""
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'sangue_django.settings')
try:
from django.core.management import execute_from_command_line
except ImportError as exc:
raise ImportError(
"Couldn't import Django. Are you sure it's installed and "
"available on your PYTHONPATH environment variable? Did you "
"forget to activate a virtual environment?"
) from exc
execute_from_command_line(sys.argv)
if __name__ == '__main__':
main()

1651
django/poetry.lock generated 100644

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,36 @@
[tool.poetry]
name = "sangue"
version = "0.1.0"
description = "Gestione Briq"
authors = ["Guido Longoni <guido.longoni@briq.it>"]
readme = "README.md"
[tool.poetry.dependencies]
python = "^3.10"
django = "^4.1.6"
python-dotenv = "^0.21.1"
djangorestframework = "^3.14.0"
django-cors-headers = "^3.13.0"
drf-yasg = "^1.21.4"
django-import-export = "^3.0.2"
psycopg2 = "^2.9.5"
channels = "^4.0.0"
uvicorn = {extras = ["standard"], version = "^0.20.0"}
channels-redis = "^4.0.0"
django-ordered-model = "^3.6"
django-auto-prefetching = "^0.2.12"
[tool.poetry.group.dev.dependencies]
nuitka = "^1.4.4"
ordered-set = "^4.1.0"
autopep8 = "^2.0.1"
pylint = "^2.16.1"
rope = "^1.7.0"
django-extensions = "^3.2.1"
docopt = "^0.6.2"
lark = "^1.1.5"
django-debug-toolbar = "^3.8.1"
[build-system]
requires = ["poetry-core"]
build-backend = "poetry.core.masonry.api"

View File

View File

@ -0,0 +1,39 @@
from import_export.admin import ImportExportModelAdmin
from django.contrib import admin
# from django.core.cache import cache
# from django.core.paginator import Paginator
from . import models
from . import resources
# # Modified version of a GIST I found in a SO thread
# # cfr. http://masnun.rocks/2017/03/20/django-admin-expensive-count-all-queries/
# class CachingPaginator(Paginator):
# def _get_count(self):
# if not hasattr(self, "_count"):
# self._count = None
# if self._count is None:
# try:
# key = "adm:{0}:count".format(hash(self.object_list.query.__str__()))
# self._count = cache.get(key, -1)
# if self._count == -1:
# self._count = super().count
# cache.set(key, self._count, 60)
# except:
# self._count = len(self.object_list)
# return self._count
# count = property(_get_count)
# # Main reusable Admin class for only viewing
# class ViewAdminMixin(admin.ModelAdmin):
# def has_add_permission(self, request):
# return False
#
# def has_change_permission(self, request, obj=None):
# return False
#
# def has_delete_permission(self, request, obj=None):
# return False
# --------------- FINE PREFISSO TEMPLATE ---------------
raise Exception('Fai girare sqldes2django oppure elimina questa eccezione.')

View File

@ -0,0 +1,12 @@
import pathlib
from django.apps import AppConfig
class SangueAppConfig(AppConfig):
name = 'sangue_app'
verbose_name = 'Sangue'
path = pathlib.Path(__file__).parent
def ready(self):
from . import signals

View File

@ -0,0 +1,17 @@
import json
from channels.generic.websocket import WebsocketConsumer
from asgiref.sync import async_to_sync
class WsConsumer(WebsocketConsumer):
def connect(self):
self.accept()
print("accept ws connection")
def disconnect(self, close_code):
print("ws disconnection")
def receive(self, text_data):
text_data_json = json.loads(text_data)
print("ws receive", text_data_json)

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,4 @@
from django.db import models
# --------------- FINE PREFISSO TEMPLATE ---------------
raise Exception('Fai girare sqldes2django oppure elimina questa eccezione.')

View File

@ -0,0 +1,5 @@
from import_export import resources
from . import models
# --------------- FINE PREFISSO TEMPLATE ---------------
raise Exception('Fai girare sqldes2django oppure elimina questa eccezione.')

View File

@ -0,0 +1,5 @@
from rest_framework import serializers
from . import models
# --------------- FINE PREFISSO TEMPLATE ---------------
raise Exception('Fai girare sqldes2django oppure elimina questa eccezione.')

View File

@ -0,0 +1,8 @@
from django.db.models.signals import pre_save, post_save, pre_delete, post_delete
from django.dispatch import receiver
from . import models
# @receiver(pre_save, sender=models.ExampleModel)
# def example_callback(sender, instance, created=False, **kwargs):
# if kwargs['raw'] or created:
# return

View File

@ -0,0 +1,480 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
sqldes2django.
Usage:
sqldes2django.py write [--tgt-dir=<tgt-dir>] [--unmanaged=<unmanaged-name>] [--models-file=<models-file>] [--serializers-file=<serializers-file>] [--views-file=<views-file>] [--urls-file=<urls-file>] [--admin-file=<admin-file>] [--resources-file=<resources-file>] [--no-format] [--xml=<xml-path>]
sqldes2django.py (-h | --help)
sqldes2django.py (--version)
Options:
--tgt-dir=<tgt-dir> Directory di output [default: .].
--xml=<xml-path> Directory di output [default: modello_database.xml].
--models-file=<models-file> Nome del file dei modelli [default: <tgt-dir>/models.py].
--serializers-file=<serializers-file> Nome del file dei serializers [default: <tgt-dir>/serializers.py].
--views-file=<views-file> Nome del file delle views [default: <tgt-dir>/views.py].
--urls-file=<urls-file> Nome del file degli url [default: <tgt-dir>/urls.py].
--admin-file=<admin-file> Nome del file dell'admin [default: <tgt-dir>/admin.py].
--resources-file=<resources-file> Nome del file delle resources [default: <tgt-dir>/resources.py].
--no-format Non formattare l'output con autopep8.
--unmanaged=<unmanaged-name> Crea modelli unmanaged (senza migrazioni): supporta pk e fk multiple, setta database=<unmanaged-name>
--version Show version.
-h --help Show this screen.
"""
import xml.etree.ElementTree as ET
from collections import defaultdict
import lark
import os
import sys
import autopep8
import re
from docopt import docopt
"""
Created on Thu Feb 28 15:28:17 2019
@author: guido
"""
# TODO: Validare nomi per uso con Django
# TODO: Trattare le chiavi primarie manuali e le chiavi primarie composite
# TODO: Aggiungere tipi mancanti alla mappa
if __name__ == '__main__':
arguments = docopt(__doc__, version='Sqldes2django 0.1')
for arg in arguments:
if isinstance(arguments[arg], str):
arguments[arg] = arguments[arg].replace("<tgt-dir>",
arguments['--tgt-dir'])
tgtdir = arguments['--tgt-dir']
unmanaged = False
if arguments['--unmanaged']:
unmanaged = True
nome_db = arguments['--unmanaged']
print(arguments)
tgtfil = {x: arguments["--" + x + "-file"]
for x in ('models', 'admin', 'serializers', 'views', 'urls', 'resources',)}
sql_type_grammar = lark.Lark(r"""
%import common.ESCAPED_STRING
%import common.SIGNED_NUMBER
%import common.WS
%import common.CNAME
%ignore WS
tipo: costruttore | costruttore "(" [arg ("," arg)* ] ")"
?costruttore: CNAME
?arg: val | keyword_argument
val: SIGNED_NUMBER | ESCAPED_STRING
keyword_argument: CNAME "=" val""",
parser="lalr",
start="tipo") # TODO: leggere lo standard vero sql
def tutti_parametri(stringa_orig):
params = sql_type_grammar.parse(stringa_orig)
parametri = params.children[1:]
posizionali = []
keyword_args = {}
trovato_kw = False
for x in parametri:
if x.data == "val":
assert not trovato_kw, f"Argomento posizionale non può seguire keyword argument in '{stringa_orig}'"
posizionali.append(x.children[0].value)
if x.data == "keyword_argument":
trovato_kw = True
keyword_args[x.children[0].value] = x.children[1].children[0].value
return {"costruttore": params.children[0].value, "posizionali": posizionali, "keyword_args": keyword_args, "stringa": stringa_orig}
def get_tipo_mappato(parsato):
tipodato = parsato['costruttore'].upper()
return mappa_tipi[tipodato]
def is_parametrico(parsato):
return len(parsato["posizionali"]) > 0 or len(parsato["keyword_args"]) > 0
def get_parametro(parametri, positional=None, keyword=None):
if keyword is not None and keyword in parametri['keyword_args']:
val = parametri['keyword_args'][keyword]
del parametri['keyword_args'][keyword]
return val
if positional is not None:
try:
return parametri['posizionali'][positional]
except IndexError as e:
raise Exception(
f"Richiesti almeno {positional + 1} argomenti posizionali, trovati {len(parametri['posizionali'])} in {paremtri['stringa']}") from e
raise Exception("Occorre specificare positional o keyword")
fine_prefisso_RE = re.compile(r'# +-+ +FINE PREFISSO TEMPLATE +-+')
def leggi_prefisso(nome_modulo):
tmpl_prefisso = []
with open(tgtfil[nome_modulo]) as fp:
for l in fp.readlines():
tmpl_prefisso.append(l)
if fine_prefisso_RE.search(l):
break
return ''.join(tmpl_prefisso)
multifkimport = "from compositefk.fields import CompositeForeignKey # Installa django-composite-foreignkey"
multipkmodel = """
class MultiPkModel(models.Model):
class Meta:
abstract = True
def save(self, *args, **kwargs):
raise NotImplementedError("È un MultiPkModel")
# TODO
def __hash__(self):
attributi_chiave_multipla = self._meta.unique_together[0]
parti = []
for x in attributi_chiave_multipla:
try:
parte = getattr(self, x + '_id') # se oltre che parte di primary è foreign, non vogliamo fetchare tutto l'oggetto
except AttributeError:
parte = getattr(self, x)
parti.append(parte)
return hash(tuple(parti))
class MultiPkQuerySet(models.QuerySet):
def get(self, *args, **kwargs):
attributi_chiave_multipla = self.model._meta.unique_together[0]
if any(key not in kwargs for key in attributi_chiave_multipla):
raise Exception("È un MultiPkModel. Fornire anche i seguenti keywoard arguments: " + str(set(attributi_chiave_multipla) - set(kwargs.keys())))
filtro = {k : kwargs[k] for k in attributi_chiave_multipla}
risultati = list(self.filter(**filtro).all())
if len(risultati) == 1:
return risultati[0]
elif len(risultati) == 0:
raise self.model.DoesNotExist(self.model._meta.object_name + " with " + str(filtro))
else:
raise Exception("MultiPkQuerySet.get: righe multiple con " + str(filtro))
class MultiPkManager(models.Manager):
def get_queryset(self):
return MultiPkQuerySet(self.model, using=self._db)
"""
tmpl_models_prefisso = leggi_prefisso('models')
tmpl_models = '''
{multifkimport}
{multipkmodel}
{corpo}
'''
tmpl_model_class = '''
class {nometab}({classe_model}):{db_name}
class Meta:
verbose_name = '{nometab_lower}'
verbose_name_plural = '{nometab_lower}'
{is_managed}{unique_together}
def __str__(self):
return f"{nometab} ({chiavi})"
{righetxt}
'''
tmpl_riga = '''{nomeriga} = models.{tiporiga}({opzioniriga})'''
tmpl_chiave_fk = '''{nomeriga} = models.ForeignKey('{altratab}', {opzioniriga})'''
tmpl_admin_prefisso = leggi_prefisso('admin')
tmpl_admin = '''
{corpo}
'''
tmpl_admin_class = '''
@admin.register(models.{nometab})
class {nometab}Admin(ImportExportModelAdmin):
# resource = resources.{nometab}Resource
# list_per_page = 15
# paginator = CachingPaginator
# show_full_result_count = False
pass
'''
tmpl_serializers_prefisso = leggi_prefisso('serializers')
tmpl_serializers = '''
{corpo}
'''
tmpl_serializer_class = '''
class {nometab}Serializer(serializers.ModelSerializer):
class Meta:
model = models.{nometab}
fields = ('{campi}')
'''
tmpl_views_prefisso = leggi_prefisso('views')
tmpl_views = '''
{corpo}
'''
tmpl_view_class = '''
class {nometab}_View(viewsets.ModelViewSet):
# authentication_classes = [BasicAuthentication, SessionAuthentication, TokenAuthentication]
# permission_classes = [DjangoModelPermissions]
queryset = models.{nometab}.objects.all()
serializer_class = serializers.{nometab}Serializer
'''
tmpl_urls_prefisso = leggi_prefisso('urls')
tmpl_urls = '''
{urls}
urlpatterns += router.urls
'''
tmpl_url_route = '''router.register(r'{nometaburl}', views.{nometab}_View)'''
tmpl_resources_prefisso = leggi_prefisso('resources')
tmpl_resources = '''
{corpo}
'''
tmpl_resource_class = '''
class {nometab}Resource(resources.ModelResource):
class Meta:
model = models.{nometab}
'''
mappa_tipi = {
'INTEGER': 'IntegerField',
'INT': 'IntegerField',
'NUMERIC': 'DecimalField',
'DECIMAL': 'DecimalField',
'SMALLINT': 'SmallIntegerField',
'FLOAT': 'FloatField',
'TEXT': 'TextField',
'VARCHAR': 'CharField',
'CHAR': 'CharField',
'MEDIUMTEXT': 'TextField',
'DATE': 'DateField',
'TIME': 'TimeField',
'DATETIME': 'DateTimeField',
'TIMESTAMP': 'DateTimeField',
'DECIMAL': 'DecimalField',
'BIT': 'BooleanField',
'BINARY': 'BooleanField',
'JSONB': 'JSONField'
}
tabelle = list(ET.ElementTree(file=arguments['--xml'])
.findall('.//table'))
tabelle2 = (ET.ElementTree(file=arguments['--xml'])
.findall('.//table'))
models = []
admin = []
serializers = []
views = []
urls = []
resources = []
nomi_tabelle = []
any_multiple_fk = False
any_multiple_pk = False
tabelle_multi_pk = set()
for t in tabelle2:
nome = t.get('name')
chiave = t.findall('.//key/part')
if len(chiave) > 1:
tabelle_multi_pk.add(nome)
any_multiple_pk = True
renames = {}
is_fk_and_pk = set()
for t in tabelle:
righe = t.findall('.//row')
n_tab = t.get('name')
chiave = t.findall('.//key/part')
# prima decido i rename
for r in righe:
fk = r.find('relation')
n = r.get('name')
is_simple_fk = fk is not None and fk.get(
'table') not in tabelle_multi_pk
if is_simple_fk and unmanaged:
# renames[(n_tab, n)] = 'fk_' + n # poi ci pensiamo
renames[(n_tab, n)] = n
else:
renames[(n_tab, n)] = n
if is_simple_fk and n in [x.text for x in chiave]:
is_fk_and_pk.add((n_tab, n))
for t in tabelle:
rec_tabella = dict()
rec_serializers = dict()
rec_tabella['nometab'] = t.get('name')
rec_tabella['nometab_lower'] = rec_tabella['nometab'].lower()
n_tab = t.get('name')
rec_tabella['classe_model'] = 'models.Model'
nomi_tabelle.append(rec_tabella['nometab'])
rec_serializers['nometab'] = t.get('name')
rec_tabella['is_managed'] = ''
rec_tabella['db_name'] = ''
rec_tabella['unique_together'] = ''
if unmanaged:
rec_tabella['is_managed'] = f'managed = False\n db_table = "{rec_tabella["nometab"]}"\n '
rec_tabella['db_name'] = f'\n database = "{nome_db}"'
righe = t.findall('.//row')
chiave = t.findall('.//key/part')
pk_singola = None
righetxt = []
if len(chiave) == 1:
if not unmanaged:
righe = [r for r in righe if r.get('name') != chiave[0].text]
pk_singola = chiave[0].text
if unmanaged:
rec_tabella['chiavi'] = renames[(n_tab, pk_singola)] + ': {self.' + (renames[(n_tab, pk_singola)]+'_id' if (
n_tab, pk_singola) in is_fk_and_pk else renames[(n_tab, pk_singola)]) + '}'
else:
rec_tabella['chiavi'] = "id: {self.id}"
elif len(chiave) > 1:
multi_pk = True
rec_tabella['classe_model'] = "MultiPkModel"
nomi = ", ".join(f"'{renames[(n_tab, x.text)]}'" for x in chiave)
rec_tabella['unique_together'] = f"unique_together=[({nomi})]"
if unmanaged:
rec_tabella['chiavi'] = ", ".join(renames[(n_tab, x.text)] + ': {self.' + (renames[(n_tab, x.text)]+'_id' if (
n_tab, x.text) in is_fk_and_pk else renames[(n_tab, x.text)]) + '}' for x in chiave)
else:
rec_tabella['chiavi'] = "non puoi fare una pk multipla in django"
righetxt.append('objects = MultiPkManager()')
righeserializerstxt = []
# TODO: implementare eventuali tipi multiparametro e con spazi
datoparametricoRE = re.compile(
r'(?P<tipo>[^(]*)\((?P<lunghezza>[^)]*)\)')
fk_composite = defaultdict(list)
is_first = True
for r in righe:
rec_riga = dict()
rec_chiave_fk = dict()
rec_chiave_fk['opzioniriga'] = dict()
rec_riga['opzioniriga'] = dict()
comm = r.find('comment')
if comm is not None and 'django_ignore' in comm.text:
continue
riga_nullable = r.get('null', "1") == "1"
if is_first and unmanaged and rec_tabella['nometab'] in tabelle_multi_pk:
if not riga_nullable:
# il primo field diventa chiave primaria fittizia. Attenzione a non usarlo davvero!
righetxt.append("")
righetxt.append(
"# chiave primaria fittizia. Attenzione a non usarla! Fa fede unique_together.")
rec_riga['opzioniriga']['primary_key'] = True
rec_chiave_fk['opzioniriga']['primary_key'] = True
is_first = False
fk = r.find('relation')
is_simple_fk = fk is not None and fk.get(
'table') not in tabelle_multi_pk
if is_simple_fk:
rec_chiave_fk['nomeriga'] = renames[(n_tab, r.get('name'))]
n_altra_tab = rec_chiave_fk['altratab'] = fk.get('table')
rec_chiave_fk['altracol'] = renames[(
n_altra_tab, fk.get('row'))]
rec_chiave_fk['opzioniriga']['on_delete'] = 'models.CASCADE'
rec_chiave_fk['opzioniriga']['null'] = riga_nullable
rec_chiave_fk['opzioniriga']['blank'] = riga_nullable
if unmanaged:
rec_chiave_fk['opzioniriga']['to_field'] = f"'{rec_chiave_fk['altracol']}'"
rec_chiave_fk['opzioniriga']['db_column'] = f"'{r.get('name')}'"
rec_chiave_fk['opzioniriga']['related_name'] = (
f""""{rec_tabella['nometab']}_da_{rec_chiave_fk['altratab']}_{rec_chiave_fk['nomeriga']}" """.strip())
if not rec_chiave_fk['opzioniriga']['null']:
del rec_chiave_fk['opzioniriga']['null']
del rec_chiave_fk['opzioniriga']['blank']
rec_chiave_fk['opzioniriga'] = ', '.join(
f'{k}={v}' for k, v in rec_chiave_fk['opzioniriga'].items())
righetxt.append(tmpl_chiave_fk.format(**rec_chiave_fk))
else:
rec_riga['opzioniriga']['null'] = riga_nullable
rec_riga['opzioniriga']['blank'] = riga_nullable
if fk is not None: # è composita
tab_target = fk.get('table')
fk_composite[tab_target].append({"campo": r.get('name'),
"campo_tgt": renames[(tab_target, fk.get("row"))],
"nullabile": rec_riga['opzioniriga']['null']})
rec_riga['nomeriga'] = r.get('name')
orig = tipodato = str(r.find('datatype').text)
parsato = tutti_parametri(tipodato)
#parametrico = datoparametricoRE.match(tipodato)
parametrico = is_parametrico(parsato)
# parametrico = tipo
rec_riga['tiporiga'] = get_tipo_mappato(parsato)
if parametrico:
if rec_riga['tiporiga'] in ('CharField',):
lunghezza = get_parametro(
parsato, positional=0, keyword="max_length")
rec_riga['opzioniriga']['max_length'] = int(lunghezza)
elif rec_riga['tiporiga'] == "DecimalField":
max_digits = get_parametro(
parsato, positional=0, keyword="max_digits")
decimal_places = get_parametro(
parsato, positional=1, keyword="decimal_places")
rec_riga['opzioniriga']['max_digits'] = int(max_digits)
rec_riga['opzioniriga']['decimal_places'] = int(
decimal_places)
arg_avanzati = parsato["keyword_args"]
for x in arg_avanzati:
rec_riga['opzioniriga'][x] = arg_avanzati[x]
elif unmanaged:
if tipodato in ('CHAR', 'VARCHAR'):
rec_riga['opzioniriga']['max_length'] = 1000000
if r.get('name') == pk_singola:
# chiave singola
rec_riga['opzioniriga']['primary_key'] = True
if not rec_riga['opzioniriga']['null']:
del rec_riga['opzioniriga']['null']
del rec_riga['opzioniriga']['blank']
if rec_riga['tiporiga'] in ('CharField', 'TextField') and 'blank' in rec_riga['opzioniriga']:
del rec_riga['opzioniriga']['blank']
rec_riga['opzioniriga'] = ','.join(
f'{k}={v}' for k, v in rec_riga['opzioniriga'].items())
righetxt.append(tmpl_riga.format(**rec_riga))
rec_riga_serializers = dict()
rec_riga_serializers['nomecampo'] = r.get('name')
righeserializerstxt.append(rec_riga_serializers['nomecampo'])
for tgt in fk_composite:
nullabile = any(x['nullabile'] for x in fk_composite[tgt])
fk_fields = ",\n ".join(
f"'{x['campo_tgt']}': '{x['campo']}'" for x in fk_composite[tgt])
asd = f"fk_{tgt} = CompositeForeignKey('{tgt}', on_delete=models.CASCADE, null={nullabile}, to_fields=" + "{\n " + fk_fields + "\n })"
righetxt.append(asd)
any_multiple_fk = True
rec_tabella['righetxt'] = '\n '.join(righetxt)
rec_serializers['campi'] = "','".join(righeserializerstxt)
models.append(tmpl_model_class.format(**rec_tabella))
admin.append(tmpl_admin_class.format(**rec_tabella))
serializers.append(tmpl_serializer_class.format(**rec_serializers))
views.append(tmpl_view_class.format(nometab=rec_tabella['nometab']))
urls.append(tmpl_url_route.format(
nometaburl=rec_tabella['nometab'].lower(), nometab=rec_tabella['nometab']))
resources.append(tmpl_resource_class.format(
nometab=rec_tabella['nometab']))
models = tmpl_models_prefisso + tmpl_models.format(corpo='\n'.join(models),
multipkmodel=multipkmodel if any_multiple_pk else "",
multifkimport=multifkimport if any_multiple_fk else "")
admin = tmpl_admin_prefisso + tmpl_admin.format(corpo='\n'.join(admin))
serializers = tmpl_serializers_prefisso + tmpl_serializers.format(corpo='\n'.join(serializers))
views = tmpl_views_prefisso + tmpl_views.format(corpo='\n'.join(views))
urls = tmpl_urls_prefisso + tmpl_urls.format(urls='\n'.join(urls))
resources = tmpl_resources_prefisso + tmpl_resources.format(corpo='\n'.join(resources))
for tgt_name, tgt_path in tgtfil.items():
with open(tgt_path, 'w') as f:
print(
f"Writing {tgt_name} to {tgt_path}, autopep8: {not arguments['--no-format']}...")
if arguments['--no-format']:
f.write(globals()[tgt_name])
else:
f.write(autopep8.fix_code(globals()[tgt_name]))

View File

@ -0,0 +1,3 @@
from django.test import TestCase
# Create your tests here.

View File

@ -0,0 +1,13 @@
from rest_framework import routers
from django.urls import include, path
from . import views
app_name = "sangue_app"
urlpatterns = [
# path('', views.index, name='index'),
]
router = routers.DefaultRouter()
# --------------- FINE PREFISSO TEMPLATE ---------------
raise Exception('Fai girare sqldes2django oppure elimina questa eccezione.')

View File

@ -0,0 +1,19 @@
from copy import deepcopy
from django.shortcuts import render, redirect
from django.http import JsonResponse, HttpResponse
from django.utils.http import url_has_allowed_host_and_scheme
from django.contrib.auth.forms import AuthenticationForm
from django.contrib.auth import authenticate, login, logout
from rest_framework import viewsets
from rest_framework.authentication import SessionAuthentication, BasicAuthentication
from rest_framework.permissions import DjangoModelPermissions, IsAuthenticated
from django_auto_prefetching import AutoPrefetchViewSetMixin
from . import models
from . import serializers
# def index(request):
# return HttpResponse("Hello, %s!" % (request.user.username if request.user.is_authenticated else 'World'))
# --------------- FINE PREFISSO TEMPLATE ---------------

View File

View File

@ -0,0 +1,16 @@
"""
ASGI config for sangue_app project.
It exposes the ASGI callable as a module-level variable named ``application``.
For more information on this file, see
https://docs.djangoproject.com/en/3.1/howto/deployment/asgi/
"""
import os
import django
from channels.routing import get_default_application
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'sangue_django.settings')
django.setup()
application = get_default_application()

View File

@ -0,0 +1,10 @@
from channels.routing import ProtocolTypeRouter, URLRouter
from django.core.asgi import get_asgi_application
from django.urls import path
from sangue_app import consumers
application = ProtocolTypeRouter({
# "websocket": URLRouter([path(r'ws/sangue_app', consumers.WsConsumer), ])
"http": get_asgi_application(),
})

View File

@ -0,0 +1,310 @@
"""
Django settings for sangue_django project.
"""
import os
import sys
from pathlib import Path
from dotenv import find_dotenv, load_dotenv
load_dotenv(find_dotenv())
def tristate_env_flag(name):
env_flag = os.getenv(name)
if env_flag:
globals()[name] = True if env_flag.strip().lower() in (
'true', '1') else False
# ADMINS =
ALLOWED_HOSTS = ['*']
ASGI_APPLICATION = "sangue_django.routing.application"
# AUTHENTICATION_BACKENDS =
# AUTHLIB_OAUTH_CLIENTS =
# Password validation
# https://docs.djangoproject.com/en/3.1/ref/settings/#auth-password-validators
AUTH_PASSWORD_VALIDATORS = [
{
'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
},
{
'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
},
{
'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
},
{
'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
},
]
# AUTH_USER_MODEL =
# Build paths inside the project like this: os.path.join(BASE_DIR, ...)
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
# BOWER_COMPONENTS_ROOT =
# BOWER_INSTALLED_APPS =
# CACHES = {
# "default": {
# "BACKEND": "django_redis.cache.RedisCache",
# "LOCATION": f"redis://{os.getenv('REDIS_HOST', 'Invalid redis host')}:{ os.getenv('REDIS_PORT', 'Invalid redis port')}/0",
# "OPTIONS": {
# "CLIENT_CLASS": "django_redis.client.DefaultClient",
# }
# }
# }
# CONTEXT =
# CORS_ORIGIN_ALLOW_ALL =
# CSP_FRAME_ANCESTORS =
tristate_env_flag('CSRF_COOKIE_SECURE')
MYHOSTNAME = os.getenv('MYHOSTNAME','http://imposta-MYHOSTNAME-nell-env.briq.it')
CSRF_TRUSTED_ORIGINS = list({x for x in (os.getenv('CSRF_TRUSTED_ORIGINS','').split(',')+[MYHOSTNAME]) if x})
# DATABASE_ROUTERS =
# Database
# https://docs.djangoproject.com/en/3.1/ref/settings/#databases
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.postgresql',
'NAME': os.getenv('DB_NAME'),
'USER': os.getenv('DB_USER'),
'PASSWORD': os.getenv('DB_PASSWORD'),
'HOST': os.getenv('DB_HOST'),
'PORT': os.getenv('DB_PORT', "5432"),
'CONN_MAX_AGE': 120,
'TEST': {
'NAME': os.getenv('DB_TEST_NAME')
}
}
}
DATA_UPLOAD_MAX_NUMBER_FIELDS = 10000
# SECURITY WARNING: don't run with debug turned on in production!
DEBUG=True
DEBUG_TOOLBAR=False
tristate_env_flag('DEBUG')
if DEBUG:
CHANNEL_LAYERS = {
"default": {
"BACKEND": "channels.layers.InMemoryChannelLayer"
}
}
tristate_env_flag('DEBUG_TOOLBAR')
else:
CHANNEL_LAYERS = {
"default": {
"BACKEND": "channels_redis.core.RedisChannelLayer",
"CONFIG": {
"hosts": [(os.getenv("REDIS_HOST", "Invalid redis host"), os.getenv("REDIS_PORT", "Invalid redis port"))],
},
},
}
DEBUG_TOOLBAR = True if os.getenv('DEBUG_TOOLBAR') in (
'True', 'true', '1', 'TRUE') else False
DEFAULT_AUTO_FIELD='django.db.models.AutoField'
# DEPLOY_NAME =
# DOCS_DIR =
tristate_env_flag('ENABLE_MQTT')
if any(x in sys.argv for x in ['test', 'dumpdata', 'sqlshell', 'loaddata', 'sqlflush']):
ENABLE_MQTT = False
FIXTURE_DIRS = (
'sangue_app/fixtures/',
)
# IMPORT_EXPORT_USE_TRANSACTIONS
# Application definition
INSTALLED_APPS = [
'sangue_app.apps.SangueAppConfig',
'rest_framework',
# 'rest_framework.authtoken',
'corsheaders',
'drf_yasg',
'import_export',
'ordered_model',
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'channels',
# 'django_q',
]
if DEBUG_TOOLBAR:
INSTALLED_APPS.append('debug_toolbar')
if DEBUG:
INSTALLED_APPS.append('django_extensions')
INTERNAL_IPS = [
'127.0.0.1',
]
LANGUAGE_CODE = 'it-it'
LOGGING = {
'version': 1,
'disable_existing_loggers': False,
'handlers': {
'console': {
'class': 'logging.StreamHandler',
},
},
'root': {
'handlers': ['console'],
'level': 'WARNING',
},
'loggers': {
'django': {
'handlers': ['console'],
'level': os.getenv('DJANGO_LOG_LEVEL', 'INFO'),
'propagate': False,
},
'django.db.backends': {
'handlers': ['console'],
'level': os.getenv('DJANGO_LOG_SQL', 'INFO'),
'propagate': False,
},
},
}
# LOGIN_REDIRECT_URL =
# LOGIN_URL =
# MAIN_SITE_URL =
# MANAGERS =
MEDIA_ROOT = os.getenv('MEDIA_ROOT')
if MEDIA_ROOT:
MEDIA_ROOT=MEDIA_ROOT.replace("$(pwd)", os.path.realpath(os.path.dirname(BASE_DIR)))
MEDIA_URL = os.getenv('MEDIA_URL')
MIDDLEWARE = [
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'corsheaders.middleware.CorsMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
]
if DEBUG_TOOLBAR:
MIDDLEWARE = [
'debug_toolbar.middleware.DebugToolbarMiddleware'] + MIDDLEWARE
if os.getenv('MQTT_HOST') and os.getenv('MQTT_PORT'):
MQTT_ADDR = [os.getenv('MQTT_HOST'), int(os.getenv('MQTT_PORT'))]
else:
# stesso default che usano i programmi mosquitto_pub etc
MQTT_ADDR = ["localhost", 1883]
# PROJECT_ROOT =
Q_CLUSTER = {
'name': 'sangue',
'workers': 1,
'recycle': 10,
'timeout': 7200,
'retry': 8000,
'max_attempts': 2,
'compress': True,
'save_limit': 250,
'queue_limit': 100,
'cpu_affinity': 1,
'label': 'Django Q',
'django_redis': 'default',
'redis': {
'host': os.getenv("REDIS_HOST", "Invalid redis host"),
'port': os.getenv("REDIS_PORT", "Invalid redis port"),
'db': 0, },
}
REST_FRAMEWORK = {
'DEFAULT_AUTHENTICATION_CLASSES': [
'rest_framework.authentication.BasicAuthentication',
'rest_framework.authentication.SessionAuthentication',
'rest_framework.authentication.TokenAuthentication',
],
'DEFAULT_PERMISSION_CLASSES': [
'rest_framework.permissions.DjangoModelPermissions'
]
}
ROOT_URLCONF = 'sangue_django.urls'
# SECURITY WARNING: keep the secret key used in production secret!
SECRET_KEY = (os.getenv('SECRET_KEY')
if os.getenv('SECRET_KEY')
else 'YCaAOqbBzWCYvNXfwbqntFvNhElemwclBzjWenTcpPYdYtcEOzAYfzkqnoqQYXKirtcDzRmwvpGCzuoRCkrMINUtWzEAonGKpcsZ')
# SECURE_HSTS_INCLUDE_SUBDOMAINS =
# SECURE_HSTS_PRELOAD =
# SECURE_HSTS_SECONDS =
# SECURE_PROXY_SSL_HEADER =
# SECURE_REFERRER_POLICY =
tristate_env_flag('SECURE_SSL_REDIRECT')
tristate_env_flag('SESSION_COOKIE_SECURE')
# SILENCED_SYSTEM_CHECKS = ['security.W019'] # Per la modale di Django Admin interface
# SILKY_ANALYZE_QUERIES =
# SILKY_AUTHENTICATION =
# SILKY_AUTHORISATION =
# SILKY_INTERCEPT_PERCENT =
# SILKY_MAX_RECORDED_REQUESTS =
# SILKY_MAX_RESPONSE_BODY_SIZE =
# SILKY_PERMISSIONS =
# SITE_ID =
# Static files (CSS, JavaScript, Images)
# https://docs.djangoproject.com/en/3.1/howto/static-files/
STATICFILES_DIRS = [
os.path.join(BASE_DIR, "static")
]
# STATICFILES_FINDERS =
STATIC_ROOT = '../django_static/'
STATIC_URL = '/static/'
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [os.path.join(BASE_DIR, 'Templates')],
'APP_DIRS': True,
'OPTIONS': {
'context_processors': [
'django.template.context_processors.debug',
'django.template.context_processors.request',
'django.contrib.auth.context_processors.auth',
'django.contrib.messages.context_processors.messages',
],
},
},
]
# TEST_RUNNER =
TIME_ZONE = 'Europe/Rome'
USE_I18N = True
USE_L10N = True
USE_TZ = True

View File

@ -0,0 +1,40 @@
"""sangue_django URL Configuration
The `urlpatterns` list routes URLs to views. For more information please see:
https://docs.djangoproject.com/en/3.1/topics/http/urls/
Examples:
Function views
1. Add an import: from my_app import views
2. Add a URL to urlpatterns: path('', views.home, name='home')
Class-based views
1. Add an import: from other_app.views import Home
2. Add a URL to urlpatterns: path('', Home.as_view(), name='home')
Including another URLconf
1. Import the include() function: from django.urls import include, path
2. Add a URL to urlpatterns: path('blog/', include('blog.urls'))
"""
from django.contrib import admin
from django.urls import path, include
from django.conf import settings
from django.conf.urls.static import static
from . import views
urlpatterns = [
path('', views.index, name='index'),
path('login/', views.login_page, name='login'),
path('logout/', views.logout_page, name='logout'),
path('is_authenticated/', views.is_authenticated, name='is_authenticated'),
path('admin/', admin.site.urls),
path('sangue_app/', include('sangue_app.urls')),
]
if settings.DEBUG_TOOLBAR:
import debug_toolbar
urlpatterns = [path('__debug__/', include(debug_toolbar.urls))] + urlpatterns
if settings.DEBUG:
urlpatterns += static(settings.MEDIA_URL,
document_root=settings.MEDIA_ROOT)
admin.site.index_title = 'Portale Sangue'
admin.site.site_title = 'Portale Sangue'
admin.site.site_header = 'Portale Sangue'

View File

@ -0,0 +1,58 @@
from django.shortcuts import render, redirect
from django.http import JsonResponse, HttpResponse, HttpResponseRedirect
from django.utils.http import url_has_allowed_host_and_scheme
from django.contrib.auth.forms import AuthenticationForm
from django.contrib.auth import authenticate, login, logout
from django.shortcuts import reverse
import os
import json
def login_page(request):
if request.method == 'POST':
redirect_to = request.POST.get('next', "/")
redirect_to = (redirect_to
if url_has_allowed_host_and_scheme(redirect_to, request.get_host())
else "/endpointnonvalido")
form = AuthenticationForm(request, request.POST)
if form.is_valid():
user = form.get_user()
login(request, user)
return redirect(redirect_to)
else:
print(form.errors.as_data())
return render(request, "login.html", {'form': form})
else:
if request.user.is_authenticated:
return redirect("/")
form = AuthenticationForm()
for x in form.fields:
form.fields[x].widget.attrs.update({"class": "form-control"})
redirect_to = request.GET.get('next', '/')
redirect_to = (redirect_to
if url_has_allowed_host_and_scheme(redirect_to, request.get_host())
else '/')
return render(request, "login.html", {'form': form, 'next': redirect_to})
def logout_page(request):
logout(request)
return redirect("/")
def is_authenticated(request):
if request.user.is_authenticated:
out = {'user': request.user.username}
else:
out = {'user': None}
return JsonResponse(out)
def index(request):
return HttpResponseRedirect("/admin/")
def auto_config_frontend(request):
auto_config_frontend = {
#'api_url': reverse('sangue_app:api-root'),
#'mqtt_url': "/mqtt/",
'nome_deployment': os.getenv('DEPLOY_NAME',''),
'context': os.getenv('CONTEXT','')
}
return HttpResponse("let auto_config_frontend = " + json.dumps(auto_config_frontend) + ";", content_type="text/javascript")

View File

@ -0,0 +1,8 @@
.inline-group .tabular tr.has_original td {
padding-top: 1em;
}
.select2-container--admin-autocomplete.select2-container {
width: auto!important;
min-width: 274px;
}

View File

@ -0,0 +1,12 @@
'use strict';
{
window.addEventListener("load", function() {
const $ = django.jQuery;
$(document).ready(function() {
$('.vDateField').each(function() {
this.setAttribute('type', 'date');
});
});
});
}

Binary file not shown.

View File

@ -0,0 +1,17 @@
version: "3.4"
services: {}
# red:
# image: "nodered/node-red"
# volumes:
# - "./red_data:/data:rw"
# network_mode: host
# env_file:
# - env_docker
# hivemq:
# image: "docker.briq.it/public/hivemq-community-edition:master"
# env_file:
# - env_docker
# volumes:
# - ./hivemq.config.xml:/config.xml
# network_mode: host

View File

@ -0,0 +1,118 @@
#! /bin/bash --
set -e
set -o pipefail
cd "$(dirname "$0")"
FROM="production"
TO="none (export only)"
EXIT_STATUS_ON_NO_TARGET=0
SERVICE="sangue"
print_usage() {
echo "copydb.sh [options]
Copy db.
Options:
-f, --from source deployment name [default: $FROM]
-t, --to destination deployment name [default: $TO]
-s, --service service name [default: $SERVICE]
-h, --help show this help
"
}
set +e
TEMP=$(getopt -o f:t:s:h --long from:,to:,service:,help -- "$@")
GETOPT_STATUS=$?
set -e
if [[ ! $GETOPT_STATUS = 0 ]]; then
print_usage
exit 1
fi
eval set -- "$TEMP"
while true; do
case "$1" in
-f|--from)
FROM="$2"
shift 2
;;
-t|--to)
EXIT_STATUS_ON_NO_TARGET=6
TO="$2"
shift 2
;;
-s|--service)
SERVICE="$2"
shift 2
;;
-h|--help)
print_usage
exit 1
;;
--)
shift
break
;;
*)
echo "Internal error!" 1>&2
exit 1
;;
esac
done
OUTNAME="/tmp/$SERVICE-$FROM-$(date '+%s')-XXXXXX.dump.zstd"
OUTFILE="$(mktemp $OUTNAME)"
echo -e "from deployment:\t$FROM\nto deployment:\t$TO\ncompose service:\t$SERVICE\ntemporary file:\t$OUTFILE" | column -t -s "$(printf '\t')"
read -p "Are you sure? (y/n) " -n 1 -r CONFIRM
echo #a capo
if [[ ! $CONFIRM =~ ^[Yy]$ ]]; then
exit 3;
fi
COMANDO_EXPORT='set -e; set -o pipefail; env PGPASSWORD="$DB_PASSWORD" pg_dump -h ${DB_HOST:-localhost} -p "${DB_PORT:-5432}" -U "${DB_USER:-postgres}" -c -Fc -Z0 "$DB_NAME"'
B64_EXPORT=$(echo "$COMANDO_EXPORT" | base64)
COMANDO_DROP='set -e; set -o pipefail; echo "DROP DATABASE IF EXISTS \"$DB_NAME\"; CREATE DATABASE \"$DB_NAME\" WITH OWNER=\"$DB_USER\"; " | env PGPASSWORD="$DB_PASSWORD" psql -v ON_ERROR_STOP=1 -h ${DB_HOST:-localhost} -p "${DB_PORT:-5432}" -U "${DB_USER:-postgres}" postgres'
COMANDO_IMPORT='set -e; set -o pipefail; env PGPASSWORD="$DB_PASSWORD" pg_restore --no-owner -h ${DB_HOST:-localhost} -p "${DB_PORT:-5432}" -U "${DB_USER:postgres}" -d "$DB_NAME" -Fc'
B64_IMPORT=$(echo "$COMANDO_IMPORT" | base64)
echo "Exporting from $FROM..."
echo "$B64_EXPORT" | (cd "$FROM" && docker-compose exec -T "$SERVICE" "/bin/bash" -c 'base64 -d | bash' | zstd )> "$OUTFILE"
echo "Export from $FROM OK"
if [[ ! -f "./$TO/docker-compose.yml" ]]; then
if [[ ! $EXIT_STATUS_ON_NO_TARGET = 0 ]]; then
echo "No target specified or found"
fi
exit $EXIT_STATUS_ON_NO_TARGET
fi
MUST_RESTART=0
IS_RUNNING=$(cd "$TO" && docker-compose ps -q "$SERVICE" | wc -l)
if [[ $IS_RUNNING != "0" ]]; then
MUST_RESTART=1
echo -n "Stop $SERVICE in $TO... "
(cd "$TO" && docker-compose stop -t3 "$SERVICE")
echo "stopped."
else
echo "$TO is already stopped."
fi
echo "Drop old/create new database in $TO..."
DROP_OUTPUT=$(cd "$TO" && docker-compose run --rm -T "$SERVICE" "/bin/bash" -c "$COMANDO_DROP")
echo "Drop/create database in $TO OK"
echo "Importing into $TO..."
zstdcat "$OUTFILE" | (cd "$TO" && docker-compose run --rm -T "$SERVICE" "/bin/bash" -c "$COMANDO_IMPORT")
echo "Import into $TO OK"
if [[ $MUST_RESTART = "1" ]]; then
echo -n "Starting $TO... "
(cd "$TO" && docker-compose up -d)
echo "started."
fi
exit 0

View File

@ -0,0 +1,4 @@
#! /bin/bash --
set -e
cd "$(dirname "$0")/production"
source .env && PGPASSWORD="$DB_PASSWORD" pg_dump -h localhost -p "${DB_PORT:-5432}" -U "${DB_USER:postgres}" -c -Fp -Z0 "$DB_NAME" | zstd

View File

@ -0,0 +1,4 @@
#! /bin/bash --
set -e
cd "$(dirname "$0")/staging"
source .env && PGPASSWORD="$DB_PASSWORD" pg_dump -h localhost -p "${DB_PORT:-5432}" -U "${DB_USER:postgres}" -c -Fp -Z0 "$DB_NAME" | zstd

View File

@ -0,0 +1,46 @@
#! /bin/bash --
set -e
set -o pipefail
cd "$(dirname "$0")" || exit 1
CD_TO="./staging/"
PROJ="sangue"
cd-docker-compose() {
(cd "$1" && docker-compose "${@:2}")
}
log_images() {
if cd-docker-compose "$CD_TO" exec "$PROJ" bash -c 'exit 0'; then
{
date | tr -d '\n';
echo -ne "\t";
cd-docker-compose "$CD_TO" exec "$PROJ" bash -c 'cat /git_info' | jq -r '.lhash' | tr -d '\n';
echo -ne '\t';
cd-docker-compose "$CD_TO" images -q "$PROJ" | tr -d '\n';
echo -e "\t$1";
} >> "$2"
else
{
date | tr -d '\n';
echo -ne "\timpossibile leggere il tag git\t";
{ cd-docker-compose "$CD_TO" images -q "$PROJ" || echo "N/A"; } | tr -d '\n';
echo -e "\t$1";
} >> "$2"
fi
}
log_images "prima" "log_deployment_staging.txt"
cd-docker-compose "$CD_TO" pull
PULL_OK=$?
if [[ $PULL_OK -eq 0 && "$1" == "-d" ]]; then
echo "Copia del database da produzione a staging..."
if ./copydb.sh --from production --to staging; #l'utente può dire no
then
echo "Copia completata"
else
echo "Copia annullata"
fi
fi
[[ $PULL_OK -eq 0 ]] && cd-docker-compose "$CD_TO" up -d
echo "Master caricato su ambiente di TEST (Staging)"
log_images "dopo" "log_deployment_staging.txt"

View File

@ -0,0 +1,32 @@
#! /bin/bash --
set -e
DOVE="$(mktemp)"
cd "$(dirname "$0")"
pushd production
source .env && \
PGPASSWORD="$DB_PASSWORD" \
pg_dump \
-h localhost \
-p "${DB_PORT:-5432}" \
-U "${DB_USER:postgres}" \
-c -Fc -Z0 "$DB_NAME" > "$DOVE"
popd
pushd staging
source .env && \
echo 'DROP DATABASE "'"$DB_NAME"'";' | \
psql \
-h localhost \
-p "${DB_PORT:-5432}" \
-U "${DB_USER:postgres}"
pg_restore \
-h localhost \
-p "${DB_PORT:-5432}" \
-U "${DB_USER:postgres}" \
-d "$DB_NAME" \
--clean -Fc \
-j $(cat /proc/cpuinfo | grep processor | wc -l) "$DOVE"
popd
rm -rf "$DOVE"
docker-compose -f ./staging/docker-compose.yml up -dt1

View File

@ -0,0 +1,44 @@
COMPOSE_PROJECT_NAME=sangue_production
EVERYTHING_EXTERNAL_PORT=9090
SECRET_KEY=QDMneXbKaRPQpkOLkbpzoUxrEXQJfSCtmkFAXnMvMiBCQVAXdpohVphkzakVKBbFQxISUMKDMldMhSQwqcYOOmcmTsGzuNzUjNFA
DEBUG=false
DOLLAR=$
# usato per nginx
EVERYTHING_LISTEN=0.0.0.0
EVERYTHING_PORT=9090
DJANGO_PORT=8000
# path assoluti:
MEDIA_ROOT=/django_upload/
# url che deve coincidere in django e nginx
MEDIA_URL=uploads/
# Redis serve per i django channels
REDIS_HOST=redis
REDIS_PORT=6379
# DJANGO DB
DB_NAME=sangue
DB_USER=sangue
DB_PASSWORD=sangue
DB_HOST=postgres_svc # se fisso, vedere yml di infrastruttura briq
DB_TEST_NAME=sangue_test
# MQTT
ENABLE_MQTT=false
MQTT_HOST=hivemq_svc
MQTT_WEBSOCKET_HOST=hivemq_svc
MQTT_PORT=1833
MQTT_WEBSOCKET_PORT=1834
#MQTT_PORT=
#MQTT_WEBSOCKET_PORT=

View File

@ -0,0 +1,61 @@
version: "3.5"
services:
sangue:
image: sangue:production
restart: unless-stopped
volumes:
- /etc/localtime:/etc/localtime:ro
- ./.django_uploads:/django_upload
env_file:
- .env
networks:
- sangue_prod_net
- bq_infrastructure_net
# - fixed_ip_host_net # Rete per accedere a servizi dell'host tramite ip fisso
# - postgres_sys_relay_net # Rete per postgres esterno ultia spiaggia
ports:
- "127.0.0.1:${EVERYTHING_EXTERNAL_PORT}:${EVERYTHING_PORT}"
logging:
driver: journald
options:
tag: sangue-production
extra_hosts:
- "host.docker.internal:host-gateway"
redis:
image: redis:6
restart: unless-stopped
networks:
- sangue_prod_net
## ATTENZIONE: se si usa il docker-compose.yml standard dell'infrastruttura briq,
## allora c'è già un hivemq che ascolta globalmente
# hivemq:
# restart: unless-stopped
# image: "docker.briq.it/public/hivemq-community-edition:master"
# env_file:
# - .env
# volumes:
# - ./hivemq.config.xml:/config.xml
# logging:
# driver: syslog
# options:
# tag: "hivemq-prod"
# ports:
# - 127.0.0.1:${MQTT_PORT}:1833
# - 127.0.0.1:${MQTT_WEBSOCKET_PORT}:1834
# networks:
# - sangue_prod_net
networks:
sangue_prod_net:
driver: bridge
bq_infrastructure_net:
external: true
name: bq_infrastructure_net
# fixed_ip_host_net:
# external: true
# name: fixed_ip_host_net
# postgres_sys_relay_net:
# external: true
# name: postgres_sys_relay_net

View File

@ -0,0 +1,33 @@
<?xml version="1.0"?>
<hivemq xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="hivemq-config.xsd">
<listeners>
<tcp-listener>
<port>1833</port>
<bind-address>0.0.0.0</bind-address>
</tcp-listener>
<websocket-listener>
<port>1834</port>
<path>/</path>
<bind-address>0.0.0.0</bind-address>
</websocket-listener>
</listeners>
<mqtt>
<max-client-id-length>65535</max-client-id-length>
<retry-interval>0</retry-interval>
<no-connect-packet-idle-timeout-millis>10000</no-connect-packet-idle-timeout-millis>
<max-queued-messages>100000</max-queued-messages>
</mqtt>
<throttling>
<max-connections>-1</max-connections>
<max-message-size>268435456</max-message-size>
<outgoing-limit>0</outgoing-limit>
<incoming-limit>0</incoming-limit>
</throttling>
<general>
<update-check-enabled>false</update-check-enabled>
</general>
</hivemq>

View File

@ -0,0 +1,45 @@
COMPOSE_PROJECT_NAME=sangue_staging
EVERYTHING_EXTERNAL_PORT=9070
SECRET_KEY=EaqzWFIUeWsNfAeBYTJFgnysbqcMlVASyDNTxAJitXzUmesuPvFprXMuFnoEIPbsZysKgLNzqFDdEmmfHirYrOkDFVnkoABJySVV
DEBUG=false
DOLLAR=$
# usato per nginx
EVERYTHING_LISTEN=0.0.0.0
EVERYTHING_PORT=9090
DJANGO_PORT=8000
# path assoluti:
MEDIA_ROOT=/django_upload/
# url che deve coincidere in django e nginx
MEDIA_URL=uploads/
# Redis serve per i django channels
REDIS_HOST=redis
REDIS_PORT=6379
# DJANGO DB
DB_NAME=sangue_staging
DB_USER=sangue_staging
DB_PASSWORD=sangue_staging
DB_HOST=postgres_svc # se fisso, vedere yml di infrastruttura briq
DB_TEST_NAME=sangue_staging_test
# MQTT
ENABLE_MQTT=false
MQTT_HOST=hivemq_svc
MQTT_WEBSOCKET_HOST=hivemq_svc
MQTT_PORT=1833
MQTT_WEBSOCKET_PORT=1834
#MQTT_PORT=
#MQTT_WEBSOCKET_PORT=

View File

@ -0,0 +1,62 @@
version: "3.5"
services:
sangue:
image: docker.briq.it/briq/sangue:master
restart: unless-stopped
volumes:
- /etc/localtime:/etc/localtime:ro
- ./.django_uploads:/django_upload
env_file:
- .env
networks:
- sangue_staging_net
- bq_infrastructure_net
# - fixed_ip_host_net # Rete per accedere a servizi dell'host tramite ip fisso
# - postgres_sys_relay_net # Rete per postgres esterno ultia spiaggia
ports:
- "127.0.0.1:${EVERYTHING_EXTERNAL_PORT}:${EVERYTHING_PORT}"
logging:
driver: journald
options:
tag: sangue-staging
extra_hosts:
- "host.docker.internal:host-gateway"
redis:
image: redis:6
restart: unless-stopped
networks:
- sangue_staging_net
## ATTENZIONE: se si usa il docker-compose.yml standard dell'infrastruttura briq,
## allora c'è già un hivemq che ascolta globalmente
# hivemq:
# restart: unless-stopped
# image: "docker.briq.it/public/hivemq-community-edition:master"
# env_file:
# - .env
# volumes:
# - ./hivemq.config.xml:/config.xml
# logging:
# driver: syslog
# options:
# tag: "hivemq-staging"
# ports:
# - 127.0.0.1:${MQTT_PORT}:1833
# - 127.0.0.1:${MQTT_WEBSOCKET_PORT}:1834
# networks:
# - sangue_staging_net
networks:
sangue_staging_net:
driver: bridge
bq_infrastructure_net:
external: true
name: bq_infrastructure_net
# fixed_ip_host_net:
# external: true
# name: fixed_ip_host_net
# postgres_sys_relay_net:
# external: true
# name: postgres_sys_relay_net

View File

@ -0,0 +1,33 @@
<?xml version="1.0"?>
<hivemq xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="hivemq-config.xsd">
<listeners>
<tcp-listener>
<port>1833</port>
<bind-address>0.0.0.0</bind-address>
</tcp-listener>
<websocket-listener>
<port>1834</port>
<path>/</path>
<bind-address>0.0.0.0</bind-address>
</websocket-listener>
</listeners>
<mqtt>
<max-client-id-length>65535</max-client-id-length>
<retry-interval>0</retry-interval>
<no-connect-packet-idle-timeout-millis>10000</no-connect-packet-idle-timeout-millis>
<max-queued-messages>100000</max-queued-messages>
</mqtt>
<throttling>
<max-connections>-1</max-connections>
<max-message-size>268435456</max-message-size>
<outgoing-limit>0</outgoing-limit>
<incoming-limit>0</incoming-limit>
</throttling>
<general>
<update-check-enabled>false</update-check-enabled>
</general>
</hivemq>

View File

@ -0,0 +1,38 @@
#! /bin/bash --
set -e
set -o pipefail
DOCKER_NAMESPACE="briq"
PROJ="sangue"
cd "$(dirname "$0")" || exit 1
CD_TO="./production/"
cd-docker-compose() {
(cd "$1" && docker-compose "${@:2}")
}
log_images() {
if cd-docker-compose "$CD_TO" exec "$PROJ" bash -c 'exit 0'; then
{
date | tr -d '\n';
echo -ne "\t";
cd-docker-compose "$CD_TO" exec "$PROJ" bash -c 'cat /git_info' | jq -r '.lhash' | tr -d '\n';
echo -ne '\t';
cd-docker-compose "$CD_TO" images -q "$PROJ" | tr -d '\n';
echo -e "\t$1";
} >> "$2"
else
{
date | tr -d '\n';
echo -ne "\timpossibile leggere il tag git\t";
{ cd-docker-compose "$CD_TO" images -q "$PROJ" || echo "N/A"; } | tr -d '\n';
echo -e "\t$1";
} >> "$2"
fi
}
log_images "prima" "log_deployment_production.txt"
docker tag "$PROJ":production "$PROJ":backup || true
docker tag docker.briq.it/"$DOCKER_NAMESPACE/$PROJ":master "$PROJ":production
cd-docker-compose "$CD_TO" up -dt1
log_images "dopo" "log_deployment_production.txt"

View File

@ -0,0 +1,85 @@
version: '3.5'
services:
hivemq_svc:
restart: unless-stopped
image: "docker.briq.it/public/hivemq-community-edition:master"
env_file:
- .env
volumes:
- ./hivemq.config.xml:/config.xml
logging:
driver: journald
options:
tag: hivemq-briq
ports:
- 127.0.0.1:1833:1833
- 127.0.0.1:1834:1834
networks:
- bq_infrastructure_net
- bq_infrastructure_ext_net
postgres_svc:
image: postgres:14
restart: unless-stopped
networks:
- bq_infrastructure_net
- bq_infrastructure_ext_net
ports:
- 127.0.0.1:5432:5432
environment:
POSTGRES_PASSWORD: LvdSkluLfE
POSTGRES_USER: postgres
POSTGRES_DB: postgres
logging:
driver: journald
options:
tag: postgres-briq
#volumes:
# - ./data:/var/lib/postgresql/data
#
## ATTENZIONE: avviare con volume qui sopra commentato, poi eseguire dalla cartella corrente (e poi decommentare il volume):
## docker-compose exec postgres_svc sh -c "tar -cpO -C /var/lib/postgresql data | base64" | base64 -di | tar -xp
# nodered_svc:
# image: nodered/node-red:3.0.0
# restart: unless-stopped
# environment:
# - TZ=Europe/Rome
# ports:
# - "127.0.0.1:1880:1880"
# - "0.0.0.0:1890:1890" # per mandare il file da windows fuori (particolarità di eusebio)
# networks:
# - bq_infrastructure_net
# - bq_infrastructure_ext_net
# # volumes:
# # - ./red_data:/data
# ## ATTENZIONE: avviare con volume qui sopra commentato, poi eseguire dalla cartella corrente (e poi decommentare il volume):
# ## mkdir -p red_data && docker-compose exec -u0 nodered_svc sh -c "tar -cpO -C /data . | base64" | base64 -di | sudo tar -C ./red_data -xp
# # Questo è il relay. Usare come ultima spiaggia e soltanto con postgresql di sistema.
# postgres_sys:
# image: docker.briq.it/public/psql_relay:master
# restart: unless-stopped
# networks:
# - postgres_sys_relay_net
# environment:
# - PSQL_SOCK_NAME=postgresql/.s.PGSQL.5432
# volumes:
# - /var/run/:/psql_sock/
networks:
postgres_sys_relay_net:
name: postgres_sys_relay_net
internal: true
bq_infrastructure_net:
name: bq_infrastructure_net
internal: true
bq_infrastructure_ext_net:
name: bq_infrastructure_ext_net
# # Usare in caso di bisogno di connettere servizi dell'host direttamente ai container
# fixed_ip_host_net:
# name: fixed_ip_host_net
# ipam:
# driver: default
# config:
# - subnet: 172.28.42.0/24

View File

@ -0,0 +1,60 @@
version: "3.4"
services:
# red:
# image: "nodered/node-red"
# volumes:
# - "./red_data:/data:rw"
# environment:
# - PORT=${NODE_RED_PORT}
# - DJANGO_URL=http://localhost:${EVERYTHING_EXTERNAL_PORT}/api
# network_mode: host
# # env_file:
# # - .env
# hivemq:
# image: "docker.briq.it/public/hivemq-community-edition:master"
# # env_file:
# # - .env
# volumes:
# - ./hivemq.config.xml:/config.xml
# ports:
# - "${EVERYTHING_LISTEN}:${MQTT_PORT}:1833"
# - "${EVERYTHING_LISTEN}:${MQTT_WEBSOCKET_PORT}:1834"
sangue:
image: sangue
build:
context: ../..
args:
GIT_COMMIT_SHA: ${GIT_COMMIT_SHA}
GIT_COMMIT_DATE: ${GIT_COMMIT_DATE}
GIT_COMMIT_REF: ${GIT_COMMIT_REF}
GIT_CLEAN: ${GIT_CLEAN}
container_name: "sangue"
restart: unless-stopped
volumes:
- /etc/timezone:/etc/timezone:ro
- ./.django_uploads:/django_upload
env_file:
- .env
ports:
- "127.0.0.1:${EVERYTHING_EXTERNAL_PORT}:${EVERYTHING_PORT}"
redis:
image: redis:6
restart: unless-stopped
psql_relay:
image: docker.briq.it/public/psql_relay:master
environment:
- PSQL_SOCK_NAME=postgresql/.s.PGSQL.5432
volumes:
- /var/run/:/psql_sock/
restart: unless-stopped
# cupsd:
# image: "docker.briq.it/public/cupsd:master"
# volumes:
# - /etc/timezone:/etc/timezone:ro
# - ./.cups_sock:/run/cups/
# - ./.cups_config/:/etc/cups/
# ports:
# - "1631:631"

View File

@ -0,0 +1,80 @@
pid /django/nginx.pid;
daemon off;
error_log /dev/stderr warn;
events {}
http {
include /etc/nginx/mime.types;
client_body_temp_path /tmp/nginx;
proxy_temp_path /tmp/nginx;
gzip on;
gzip_vary on;
gzip_min_length 100;
gzip_types *;
upstream django {
server unix:///django/sock fail_timeout=0;
}
server {
include /maybe_ssl.conf;
access_log /dev/stdout;
root /django_static;
charset utf-8;
# location = /django_auth {
# internal;
# proxy_pass http://django/auth/is_authorized/;
# proxy_pass_request_body off;
# proxy_set_header Content-Length "";
# proxy_set_header Host ${DOLLAR}http_host;
# proxy_set_header X-Original-URI ${DOLLAR}request_uri;
# proxy_set_header X-Forwarded-For ${DOLLAR}proxy_add_x_forwarded_for;
# proxy_set_header X-Forwarded-Proto ${DOLLAR}http_x_forwarded_proto;
# }
location /frontend/ {
# auth_request /django_auth;
alias /vue/dist/;
}
location /static {
alias /django_static;
}
keepalive_timeout 5;
location / {
client_max_body_size 10M;
proxy_pass http://django;
proxy_redirect off;
proxy_set_header X-Forwarded-For ${DOLLAR}proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto ${DOLLAR}http_x_forwarded_proto;
proxy_set_header Host ${DOLLAR}http_host;
}
location /ws/ {
# we don't want nginx trying to do something clever with
# redirects, we set the Host: header above already.
proxy_pass http://django/ws/;
proxy_http_version 1.1;
proxy_set_header Upgrade ${DOLLAR}http_upgrade;
proxy_set_header Connection "Upgrade";
proxy_set_header X-Forwarded-For ${DOLLAR}proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto ${DOLLAR}http_x_forwarded_proto;
proxy_set_header Host ${DOLLAR}http_host;
proxy_read_timeout 3600s;
}
location /${MEDIA_URL} {
# auth_request /django_auth;
# error_page 403 = @error403;
alias ${MEDIA_ROOT};
}
location @error403 {
absolute_redirect off;
return 302 /;
}
}
}

View File

@ -0,0 +1,54 @@
#! /bin/bash --
set -x
export DOLLAR="$"
. "./startstop_utils.sh"
DJANGO_WEB_PID=none
DJANGO_QCLUSTER_PID=none
NGINX_PID=none
trap 'stop $DJANGO_WEB_PID $NGINX_PID $DJANGO_QCLUSTER_PID' SIGTERM SIGINT SIGQUIT SIGHUP ERR
# mkdir -p "$MEDIA_ROOT"
if [ -S '/django/sock' ]; then
# exit code speciale interpretato da briq-docker-watcher (in briq-docker-utils)
# che forza la ricreazione del container
exit 47
fi
echo >&2 "Nginx ..."
envsubst <"/nginx.conf.tpl" >"/nginx.conf"
{ if [ "$NGINX_SSL" = "1" ]; then
{ cat "${NGINX_SSL_CERTIFICATE}"; echo; cat "${NGINX_SSL_CHAIN}"; } > "/full_chain.pem";
echo "listen ${EVERYTHING_LISTEN}:${EVERYTHING_PORT} ssl;
ssl_certificate /full_chain.pem;
ssl_certificate_key ${NGINX_SSL_KEY};
ssl_session_cache shared:le_nginx_SSL:10m;
ssl_session_timeout 1440m;
ssl_session_tickets off;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_prefer_server_ciphers off;
ssl_ciphers \"ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384\";"
if [ -n "$NGINX_DH_PARAM" ]; then
echo "ssl_dhparam ${NGINX_DH_PARAM};"
fi
else
echo "listen ${EVERYTHING_LISTEN}:${EVERYTHING_PORT};"
fi; } > "/maybe_ssl.conf"
nginx -c /nginx.conf -p / &
NGINX_PID=$!
echo "Setting Django up..."
cd /django/ || exit 1
DJANGO_PROJ="$(poetry run python manage.py shell < get_django_proj.py)"
if [[ "$(poetry run python manage.py help | grep -o '\[django_q\]')" == "[django_q]" ]]; then
echo "Django qcluster..."
poetry run python manage.py qcluster &
DJANGO_QCLUSTER_PID=$!
fi
echo "Django web..."
env DJANGO_SETTINGS_MODULE="$DJANGO_PROJ.settings" poetry run uvicorn "$DJANGO_PROJ".asgi:application --uds /django/sock --workers="${DJANGO_WORKERS:-4}" &
DJANGO_WEB_PID=$!
wait

View File

@ -0,0 +1,45 @@
#! /bin/bash --
killa() {
if [[ "$1" != 'none' ]]; then
if [[ "$2" == "" ]]; then
SIGNAL=15
else
SIGNAL="$2"
fi
case "$SIGNAL" in
15)
echo >&2 "stopping $1..."
;;
9)
echo >&2 "killing $1..."
;;
*)
echo >&2 "sending signal $SIGNAL to $1..."
;;
esac
kill -"$SIGNAL" "$1" 2>/dev/null
fi
}
stop() {
PID_DA_WAITARE=()
for pid in "$@"; do
if [[ "$pid" != 'none' ]]; then
PID_DA_WAITARE+=("$pid")
killa "$pid"
fi
done
{
if sleep 5; then
for pid in "$@"; do
killa "$pid" 9
done
wait
fi
}&
SLEEP_PID=$!
wait "${PID_DA_WAITARE[@]}" #aspetto solo i pid nei parametri, non sleep...&
# se questo kill fallisce vuol dire che il ciclo con sleep 5 è terminato -> è servito killare qualche processo
kill "$SLEEP_PID" 2>/dev/null && echo >&2 "Tutti i processi terminati normalmente"
exit 0
}

48
env.example 100644
View File

@ -0,0 +1,48 @@
# ADDRESSES
export EVERYTHING_LISTEN=127.0.0.1 # security by default!
# export EVERYTHING_PORT=9090
export DJANGO_HOST=127.0.0.1
export CONTEXT=Locale
# export MYHOSTNAME=
# PORTS
export DJANGO_PORT=8000
export DBG_PORT=9090
export VUE_PORT=9091
# DJANGO DB
export DB_NAME=sangue
export DB_USER=sangue
export DB_PASSWORD=sangue
export DB_HOST=127.0.0.1
# export DB_PORT=5432
export DB_TEST_NAME=sangue_test
# DJANGO MISC
export SECRET_KEY=FpstwQgWItsIpsaBuPlTwZIYynCdkdrIieYstnifekcgxfxMdIqVsflNuGrDTbFeiCUTMiGonXDfjyEKxHeHgfXFIwlUTiFdmVId
export MEDIA_ROOT=$(pwd)/django_upload/
export MEDIA_URL=uploads/
export DEBUG=true
export DEBUG_TOOLBAR=true
# export DJANGO_LOG_LEVEL=DEBUG
# export DJANGO_LOG_SQL
# MQTT
# export ENABLE_MQTT=
# export MQTT_HOST=
# export MQTT_PORT=
# REDIS
# export REDIS_HOST=
# export REDIS_PORT=
# SECURITY
# export CSRF_COOKIE_SECURE=
# export CSRF_TRUSTED_ORIGINS=
# export SECURE_SSL_REDIRECT=
# export SESSION_COOKIE_SECURE=
# workarounds
export DOLLAR=$

42
localrun.sh 100755
View File

@ -0,0 +1,42 @@
#! /bin/bash --
cd "$(dirname "$0")" || exit 1
. "docker/test_and_build/startstop_utils.sh"
DJANGO_WEB_PID=none
DJANGO_QCLUSTER_PID=none
VUE_PID=none
NGINX_PID=none
trap 'stop $DJANGO_WEB_PID $NGINX_PID $DJANGO_QCLUSTER_PID' SIGTERM SIGINT SIGQUIT SIGHUP ERR
root="$(pwd)"
export root
source ".env"
# # DECOMMENTARE QUI DI SEGUITO SE NEL PROGETTO SERVE REDIS:
# if ! nc -z "$REDIS_HOST" "$REDIS_PORT"; then
# echo >&2 "Non c'è redis su $REDIS_HOST:$REDIS_PORT"
# exit 1
# fi
pushd django/ || exit 1
echo >&2 "Starting django web server..."
poetry run python manage.py runserver "127.0.0.1:$DJANGO_PORT" &
DJANGO_WEB_PID=$!
# echo >&2 "Starting django qcluster..."
# poetry run python manage.py qcluster &
# DJANGO_QCLUSTER_PID=$!
popd || exit 1
pushd vue || exit 1
echo >&2 "Starting vue dev server..."
npm run serve -- --port "$VUE_PORT" &
VUE_PID=$!
popd || exit 1
echo >&2 "Starting nginx..."
envsubst < nginx_localrun.conf.tpl > nginx_localrun.conf
nginx -c "$root/nginx_localrun.conf"&
NGINX_PID=$!
echo >&2 "Starting docker compose localrun services..."
export COMPOSE_PROJECT_NAME="sangue_localrun"
docker-compose --log-level error -f docker-compose-localrun.yml up --timeout 1 1>/dev/null
# ctrl-c su docker compose killa tutto. docker-compose sostituisce il wait.

View File

@ -0,0 +1,117 @@
pid ${root}/nginx.pid;
daemon off;
error_log /dev/stderr warn;
events {}
http {
include /etc/nginx/mime.types;
client_body_temp_path /tmp/nginx;
proxy_temp_path /tmp/nginx;
gzip on;
gzip_vary on;
gzip_min_length 100;
gzip_types *;
upstream django {
server 127.0.0.1:${DJANGO_PORT} fail_timeout=0;
}
# upstream mqtt {
# server 127.0.0.1:${MQTT_WEBSOCKET_PORT} fail_timeout=0;
# }
upstream vue {
server 127.0.0.1:${VUE_PORT} fail_timeout=0;
}
# server {
# access_log /dev/stdout;
# charset utf-8;
# listen 127.0.0.1:${DJANGO_PORT};
# location / {
# client_max_body_size 10M;
# # we don't want nginx trying to do something clever with
# # redirects, we set the Host: header above already.
# proxy_pass http://django;
# proxy_redirect off;
# proxy_set_header X-Forwarded-For ${DOLLAR}proxy_add_x_forwarded_for;
# proxy_set_header X-Forwarded-Proto ${DOLLAR}scheme;
# proxy_set_header Host ${DOLLAR}http_host;
# }
# }
server {
access_log /dev/stdout;
#root /vue/dist;
listen ${EVERYTHING_LISTEN}:${DBG_PORT};
charset utf-8;
# location = /django_auth {
# internal;
# proxy_pass http://django/auth/is_authorized/;
# proxy_pass_request_body off;
# proxy_set_header Content-Length "";
# proxy_set_header Host ${DOLLAR}http_host;
# proxy_set_header X-Original-URI ${DOLLAR}request_uri;
# }
location /frontend/ {
# auth_request /django_auth;
# error_page 403 = @error403;
proxy_pass http://vue/;
proxy_redirect off;
proxy_set_header X-Forwarded-For ${DOLLAR}proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto ${DOLLAR}scheme;
proxy_set_header Host ${DOLLAR}http_host;
}
location /sockjs-node/ {
proxy_pass http://vue;
proxy_http_version 1.1;
proxy_set_header Upgrade ${DOLLAR}http_upgrade;
proxy_set_header Connection "Upgrade";
proxy_set_header X-Forwarded-For ${DOLLAR}proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto ${DOLLAR}scheme;
proxy_set_header Host ${DOLLAR}http_host;
proxy_read_timeout 3600s;
}
# location /backend/static/ {
# proxy_pass http://django/static/;
# proxy_set_header Host ${DOLLAR}http_host;
# proxy_set_header X-Forwarded-For ${DOLLAR}proxy_add_x_forwarded_for;
# }
location / {
client_max_body_size 10M;
proxy_pass http://django/;
# set ${DOLLAR}sc_name ${DOLLAR}http_x_script_name/backend;
# proxy_set_header X-Script-Name ${DOLLAR}sc_name;
proxy_redirect off;
proxy_set_header X-Forwarded-For ${DOLLAR}proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto ${DOLLAR}scheme;
proxy_set_header Host ${DOLLAR}http_host;
}
location /ws/ {
# we don't want nginx trying to do something clever with
# redirects, we set the Host: header above already.
proxy_pass http://django/ws/;
proxy_http_version 1.1;
proxy_set_header Upgrade ${DOLLAR}http_upgrade;
proxy_set_header Connection "Upgrade";
proxy_set_header X-Forwarded-For ${DOLLAR}proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto ${DOLLAR}scheme;
proxy_set_header Host ${DOLLAR}http_host;
proxy_read_timeout 3600s;
}
location /${MEDIA_URL} {
# auth_request /django_auth;
# error_page 403 = @error403;
alias ${MEDIA_ROOT};
}
location @error403 {
absolute_redirect off;
return 302 /;
}
}
}

View File

@ -0,0 +1,19 @@
#! /bin/bash --
# REQUISITI:
# sul server E sul client ci deve essere installato:
# - pgdump versione corretta
# - zstd
# - base64
# - ssh server
set -e
DOVE="$(mktemp)"
cd "$(dirname "$0")/.." && source .env
COMANDO='set -e; cd "'"$2"'" && source env_docker && PGPASSWORD="$DB_PASSWORD" pg_dump -h localhost -p "${DB_PORT:-5432}" -U "${DB_USER:postgres}" -c -Fc -Z0 "$DB_NAME" | zstd'
B64=$(echo "$COMANDO" | base64)
ssh "$1" "echo '$B64' | base64 -d | bash" | zstdcat > "$DOVE"
echo 'DROP DATABASE "'"$DB_NAME"'";' | psql -h localhost -p "${DB_PORT:-5432}" -U "${DB_USER:postgres}"
pg_restore -h localhost -p "${DB_PORT:-5432}" -U "${DB_USER:postgres}" -d "$DB_NAME" --clean -Fc -j $(cat /proc/cpuinfo | grep processor | wc -l) "$DOVE"
rm -rf "$DOVE"

View File

@ -0,0 +1,20 @@
#! /bin/bash --
cd "$(dirname "$0")/.."
set -e
pushd vue
npm install
popd
pushd django
poetry install
popd
# pushd docker/red_data
# pushd lib/vendor
# git clone https://git.briq.it/Briq/node-red-contrib-s7-briq || git pull
# git clone https://git.briq.it/Briq/red-briq-nodes || git pull
# popd
# npm install
# popd

21
vue/.gitignore vendored 100644
View File

@ -0,0 +1,21 @@
.DS_Store
node_modules
/dist
# local env files
.env.local
.env.*.local
# Log files
npm-debug.log*
yarn-debug.log*
yarn-error.log*
# Editor directories and files
.idea
.vscode
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw*

29
vue/README.md 100644
View File

@ -0,0 +1,29 @@
# sangue_app
## Project setup
```
npm install
```
### Compiles and hot-reloads for development
```
npm run serve
```
### Compiles and minifies for production
```
npm run build
```
### Run your tests
```
npm run test
```
### Lints and fixes files
```
npm run lint
```
### Customize configuration
See [Configuration Reference](https://cli.vuejs.org/config/).

View File

@ -0,0 +1,5 @@
module.exports = {
presets: [
'@vue/app'
]
}

29091
vue/package-lock.json generated 100644

File diff suppressed because it is too large Load Diff

60
vue/package.json 100644
View File

@ -0,0 +1,60 @@
{
"name": "sangue",
"version": "0.1.0",
"private": true,
"scripts": {
"serve": "vue-cli-service serve",
"build": "vue-cli-service build",
"lint": "vue-cli-service lint"
},
"dependencies": {
"core-js": "^3.6.5",
"vue": "^3.0.0",
"vuex": "^4.0.2",
"webpack-obfuscator": "^2.6.0"
},
"devDependencies": {
"@vue/cli-plugin-babel": "~4.5.0",
"@vue/cli-plugin-eslint": "~4.5.0",
"@vue/cli-service": "~4.5.0",
"@vue/compiler-sfc": "^3.0.0",
"babel-eslint": "^10.1.0",
"eslint": "^6.7.2",
"eslint-plugin-vue": "^7.0.0"
},
"eslintConfig": {
"root": true,
"env": {
"node": true
},
"extends": [
"plugin:vue/vue3-essential",
"eslint:recommended"
],
"rules": {
"vue/no-v-for-template-key-on-child": 2,
"no-console": 0,
"no-unused-vars": [
2,
{
"vars": "all",
"varsIgnorePattern": "^_",
"argsIgnorePattern": "^_"
}
]
},
"parserOptions": {
"parser": "babel-eslint"
}
},
"postcss": {
"plugins": {
"autoprefixer": {}
}
},
"browserslist": [
"> 1%",
"last 2 versions",
"not dead"
]
}

View File

@ -0,0 +1,26 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width,initial-scale=1.0">
<link rel="icon" href="<%= BASE_URL %>favicon.ico">
<title>{{cookiecutter.project_name_snake}}_django</title>
<!--<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css" integrity="sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T" crossorigin="anonymous"/>
<link rel="stylesheet" id="Oswald-css" href="http://fonts.googleapis.com/css?family=Oswald%3A100%2C300%2C400%2C400italic%2C700&amp;ver=d900f3de0b340aaffdf647c3002595eb" type="text/css" media="all"/>
<link href="https://cdnjs.cloudflare.com/ajax/libs/tabulator/4.2.2/css/bootstrap/tabulator_bootstrap4.min.css" rel="stylesheet"/>-->
<link rel="icon" href="<%= BASE_URL %>favicon.ico">
<link rel="stylesheet" id="Oswald-css" href="http://fonts.googleapis.com/css?family=Oswald%3A100%2C300%2C400%2C400italic%2C700&amp;ver=d900f3de0b340aaffdf647c3002595eb" type="text/css" media="all"/>
<script type="text/javascript" src="/auto_config_frontend.js"></script>
</head>
<body>
<noscript>
<strong>We're sorry but {{cookiecutter.project_name_snake}}_django doesn't work properly without JavaScript enabled. Please enable it to continue.</strong>
</noscript>
<div id="app"></div>
<!-- built files will be auto injected -->
</body>
</html>

27
vue/src/App.vue 100644
View File

@ -0,0 +1,27 @@
<template>
<div id="app">
<Vista />
</div>
</template>
<script>
import Vista from "./views/Vista.vue";
export default {
name: "app",
components: {
Vista,
},
};
</script>
<style>
/*#app {
font-family: 'Avenir', Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-align: center;
color: #2c3e50;
margin-top: 60px;
}*/
</style>

View File

@ -0,0 +1,551 @@
<?xml version="1.0" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd" >
<svg>
<metadata>
Created by FontForge 20120731 at Tue Jan 22 19:36:11 2019
By www
Copyright 2002 YouWorkForThem. All Rights Reserved. http://www.youworkforthem.com. YWFT Administration Contact: info@youworkforthem.com
</metadata>
<defs>
<font id="MaetlExtraBold" horiz-adv-x="914" >
<font-face
font-family="Maetl"
font-weight="800"
font-stretch="normal"
units-per-em="1000"
panose-1="0 0 8 0 0 0 0 0 0 0"
ascent="800"
descent="-200"
x-height="582"
cap-height="728"
bbox="-143 -254 1166 1074"
underline-thickness="20"
underline-position="-123"
unicode-range="U+0020-F002"
/>
<missing-glyph horiz-adv-x="500"
d="M63 0v800h375v-800h-375zM125 63h250v675h-250v-675z" />
<glyph glyph-name=".notdef" horiz-adv-x="500"
d="M63 0v800h375v-800h-375zM125 63h250v675h-250v-675z" />
<glyph glyph-name="glyph1" horiz-adv-x="0"
/>
<glyph glyph-name="glyph2" horiz-adv-x="300"
/>
<glyph glyph-name="space" unicode=" " horiz-adv-x="300"
/>
<glyph glyph-name=".null" horiz-adv-x="0"
/>
<glyph glyph-name="Eth" unicode="&#xd0;" horiz-adv-x="871"
d="M817 120l-120 -120h-643v290h-73v147h73v291h643l120 -120v-488zM670 147v434h-470v-144h109v-147h-109v-143h470z" />
<glyph glyph-name="eth" unicode="&#xf0;" horiz-adv-x="871"
d="M817 120l-120 -120h-523l-120 120v197l120 120h361l-35 35h-192v147h81l-108 109h199l109 -109h228v-147h-82l82 -82v-270zM670 147v143h-470v-143h470z" />
<glyph glyph-name="Lslash" unicode="&#x141;" horiz-adv-x="798"
d="M745 0h-691v290h-73v147h73v327h146v-327h109v-147h-109v-143h545v-147z" />
<glyph glyph-name="lslash" unicode="&#x142;" horiz-adv-x="363"
d="M309 0h-135l-120 120v170h-73v147h73v291h146v-291h109v-147h-109v-143h109v-147z" />
<glyph glyph-name="Scaron" unicode="&#x160;" horiz-adv-x="871"
d="M839 120l-120 -120h-524l-120 120v136h147v-109h470v143h-497l-120 120v198l120 120h524l120 -120v-136h-147v109h-470v-144h497l120 -120v-197zM600 866l-120 -120h-89l-120 120v99h100l64 -64l65 64h100v-99z" />
<glyph glyph-name="scaron" unicode="&#x161;" horiz-adv-x="871"
d="M766 120l-120 -120h-571v147h544v71h-424l-120 120v124l120 120h571v-147h-544v-70h424l120 -120v-125zM600 721l-120 -120h-89l-120 120v99h100l64 -65l65 65h100v-99z" />
<glyph glyph-name="Yacute" unicode="&#xdd;" horiz-adv-x="835"
d="M803 592l-291 -291v-301h-147v301l-290 291v136h147v-84l217 -217l217 217v84h147v-136zM600 975l-193 -193h-136v63l193 193h136v-63z" />
<glyph glyph-name="yacute" unicode="&#xfd;" horiz-adv-x="835"
d="M803 -98l-120 -120h-499v147h472v117l-46 -46h-415l-120 120v462h147v-435h434v435h147v-680zM636 793l-193 -192h-135v63l192 192h136v-63z" />
<glyph glyph-name="HT" horiz-adv-x="0"
/>
<glyph glyph-name="Thorn" unicode="&#xde;" horiz-adv-x="871"
d="M817 229l-120 -120h-451l-46 46v-155h-146v728h146v-146h497l120 -120v-233zM670 256v179h-470v-179h470z" />
<glyph glyph-name="thorn" unicode="&#xfe;" horiz-adv-x="871"
d="M817 120l-120 -120h-451l-46 46v-264h-146v946h146v-191l46 45h451l120 -120v-342zM670 147v288h-470v-288h470z" />
<glyph glyph-name="nonmarkingreturn" horiz-adv-x="0"
/>
<glyph glyph-name="Zcaron" unicode="&#x17d;" horiz-adv-x="762"
d="M730 0h-655v136l445 445h-445v147h655v-136l-445 -445h445v-147zM563 902l-120 -120h-88l-120 120v100h99l65 -65l65 65h99v-100z" />
<glyph glyph-name="zcaron" unicode="&#x17e;" horiz-adv-x="798"
d="M766 0h-691v245l120 120h424v70h-544v147h691v-244l-120 -120h-424v-71h544v-147zM563 721l-120 -120h-88l-120 120v99h99l65 -65l65 65h99v-99z" />
<glyph glyph-name="brokenbar" unicode="&#xa6;" horiz-adv-x="254"
d="M200 399h-146v329h146v-329zM200 0h-146v328h146v-328z" />
<glyph glyph-name="minus" unicode="&#xad;" horiz-adv-x="472"
d="M418 290h-364v147h364v-147z" />
<glyph glyph-name="minus" unicode="&#x2212;" horiz-adv-x="472"
d="M418 290h-364v147h364v-147z" />
<glyph glyph-name="multiply" unicode="&#xd7;" horiz-adv-x="617"
d="M563 109h-99l-156 155l-155 -155h-99v136l137 137l-137 137v100h135l119 -119l120 119h135v-100l-137 -137l137 -137v-136z" />
<glyph glyph-name="exclam" unicode="!" horiz-adv-x="297"
d="M222 218h-147v510h147v-510zM222 0h-147v147h147v-147z" />
<glyph glyph-name="quotedbl" unicode="&#x22;" horiz-adv-x="478"
d="M403 556l-47 -48h-52l-48 48v172h147v-172zM222 556l-47 -48h-53l-47 48v172h147v-172z" />
<glyph glyph-name="numbersign" unicode="#" horiz-adv-x="732"
d="M657 181h-108v-108h-147v108h-71v-108h-147v108h-109v147h109v71h-109v147h109v109h147v-109h71v109h147v-109h108v-147h-108v-71h108v-147zM402 328v71h-71v-71h71z" />
<glyph glyph-name="dollar" unicode="$" horiz-adv-x="878"
d="M803 156l-120 -120h-171v-109h-147v109h-170l-120 120v100h147v-73h434v107h-461l-120 120v161l120 120h170v109h147v-109h171l120 -120v-99h-147v72h-434v-107h461l120 -120v-161z" />
<glyph glyph-name="percent" unicode="%" horiz-adv-x="878"
d="M766 592l-508 -508v-84h-147v136l508 508v84h147v-136zM803 120l-120 -120h-125l-120 120v125l120 120h125l120 -120v-125zM440 483l-120 -120h-125l-120 120v125l120 120h125l120 -120v-125zM329 535v21l-61 61h-21l-61 -61v-21l61 -61h21zM692 172v21l-61 61h-21
l-61 -61v-21l61 -61h21z" />
<glyph glyph-name="ampersand" unicode="&#x26;"
d="M839 290h-109v-170l-120 -120h-415l-120 120v161l83 83l-83 83v161l120 120h426v-147h-399v-144h361v109h147v-109h109v-147zM583 147v143h-361v-143h361z" />
<glyph glyph-name="quotesingle" unicode="'" horiz-adv-x="297"
d="M222 556l-47 -48h-53l-47 48v172h147v-172z" />
<glyph glyph-name="parenleft" unicode="(" horiz-adv-x="406"
d="M331 -109h-136l-120 120v705l120 120h136v-146h-109v-652h109v-147z" />
<glyph glyph-name="parenright" unicode=")" horiz-adv-x="406"
d="M331 11l-120 -120h-136v147h109v652h-109v146h136l120 -120v-705z" />
<glyph glyph-name="asterisk" unicode="*" horiz-adv-x="732"
d="M657 254h-99l-118 118v-107l-48 -47h-52l-47 47v107l-118 -118h-100v100l119 119l-119 119v99h100l118 -118v107l47 48h52l48 -48v-107l118 118h99v-99l-119 -119l119 -119v-100z" />
<glyph glyph-name="plus" unicode="+" horiz-adv-x="587"
d="M512 290h-145v-145h-147v145h-145v147h145v145h147v-145h145v-147z" />
<glyph glyph-name="comma" unicode="," horiz-adv-x="297"
d="M222 -73h-63l-48 48v36l-36 36v100h147v-220z" />
<glyph glyph-name="hyphen" unicode="-" horiz-adv-x="515"
d="M440 290h-365v147h365v-147z" />
<glyph glyph-name="hyphen" unicode="&#x2010;" horiz-adv-x="515"
d="M440 290h-365v147h365v-147z" />
<glyph glyph-name="period" unicode="." horiz-adv-x="297"
d="M222 0h-147v147h147v-147z" />
<glyph glyph-name="slash" unicode="/" horiz-adv-x="805"
d="M730 592l-508 -508v-84h-147v136l508 508v84h147v-136z" />
<glyph glyph-name="zero" unicode="0" horiz-adv-x="841"
d="M766 120l-120 -120h-451l-120 120v488l120 120h451l120 -120v-488zM619 147v434h-397v-434h397z" />
<glyph glyph-name="one" unicode="1" horiz-adv-x="515"
d="M440 0h-365v147h109v434h-109v147h256v-581h109v-147z" />
<glyph glyph-name="two" unicode="2" horiz-adv-x="841"
d="M766 0h-691v317l120 120h424v144h-544v147h571l120 -120v-198l-120 -120h-424v-143h544v-147z" />
<glyph glyph-name="three" unicode="3" horiz-adv-x="805"
d="M730 120l-120 -120h-535v147h508v143h-508v147h508v144h-508v147h535l120 -120v-198l-47 -46l47 -47v-197z" />
<glyph glyph-name="four" unicode="4" horiz-adv-x="878"
d="M803 181h-109v-181h-147v181h-472v245l301 302h200l-354 -354v-46h325v400h147v-400h109v-147z" />
<glyph glyph-name="five" unicode="5" horiz-adv-x="805"
d="M730 120l-120 -120h-535v147h508v180h-508v401h655v-147h-508v-107h388l120 -120v-234z" />
<glyph glyph-name="six" unicode="6" horiz-adv-x="841"
d="M766 120l-120 -120h-451l-120 120v488l120 120h462v-147h-435v-153l46 46h378l120 -120v-234zM619 147v180h-397v-180h397z" />
<glyph glyph-name="seven" unicode="7" horiz-adv-x="769"
d="M694 592l-291 -291v-301h-147v354l228 227h-409v147h619v-136z" />
<glyph glyph-name="eight" unicode="8" horiz-adv-x="841"
d="M766 120l-120 -120h-451l-120 120v197l47 47l-47 46v198l120 120h451l120 -120v-198l-46 -46l46 -47v-197zM619 437v144h-397v-144h397zM619 147v143h-397v-143h397z" />
<glyph glyph-name="nine" unicode="9" horiz-adv-x="841"
d="M766 120l-120 -120h-462v147h435v153l-45 -46h-379l-120 120v234l120 120h451l120 -120v-488zM619 401v180h-397v-180h397z" />
<glyph glyph-name="colon" unicode=":" horiz-adv-x="297"
d="M222 290h-147v147h147v-147zM222 0h-147v147h147v-147z" />
<glyph glyph-name="semicolon" unicode=";" horiz-adv-x="297"
d="M222 290h-147v147h147v-147zM222 -73h-63l-48 48v36l-36 36v100h147v-220z" />
<glyph glyph-name="less" unicode="&#x3c;" horiz-adv-x="478"
d="M403 109h-99l-229 229v52l229 229h99v-100l-155 -155l155 -156v-99z" />
<glyph glyph-name="equal" unicode="=" horiz-adv-x="624"
d="M549 399h-474v147h474v-147zM549 181h-474v147h474v-147z" />
<glyph glyph-name="greater" unicode="&#x3e;" horiz-adv-x="478"
d="M403 338l-228 -229h-100v99l155 156l-155 155v100h100l228 -229v-52z" />
<glyph glyph-name="question" unicode="?" horiz-adv-x="696"
d="M621 410l-120 -120h-170v-72h-147v99l120 120h170v144h-399v147h426l120 -120v-198zM331 0h-147v147h147v-147z" />
<glyph glyph-name="at" unicode="@" horiz-adv-x="769"
d="M694 338l-157 -157h-161l-120 120v125l120 120h125l46 -45v80h-227l-98 -98v-238l98 -98h229v-147h-281l-193 193v342l193 193h306l120 -120v-270zM547 328v71h-144v-71h144z" />
<glyph glyph-name="A" unicode="A"
d="M839 0h-147v181h-470v-181h-147v390l338 338h88l338 -338v-390zM692 328v10l-235 235l-235 -235v-10h470z" />
<glyph glyph-name="B" unicode="B"
d="M839 120l-120 -120h-644v728h644l120 -120v-161l-83 -83l83 -83v-161zM692 499v82h-470v-144h409zM692 147v82l-61 61h-409v-143h470z" />
<glyph glyph-name="C" unicode="C"
d="M839 120l-120 -120h-524l-120 120v488l120 120h524l120 -120v-136h-147v109h-470v-434h470v109h147v-136z" />
<glyph glyph-name="D" unicode="D"
d="M839 120l-120 -120h-644v728h644l120 -120v-488zM692 147v434h-470v-434h470z" />
<glyph glyph-name="E" unicode="E" horiz-adv-x="841"
d="M766 0h-691v728h691v-147h-544v-144h544v-147h-544v-143h544v-147z" />
<glyph glyph-name="F" unicode="F" horiz-adv-x="769"
d="M694 290h-472v-290h-147v728h619v-147h-472v-144h472v-147z" />
<glyph glyph-name="G" unicode="G"
d="M839 120l-120 -120h-524l-120 120v488l120 120h524l120 -120v-136h-147v109h-470v-434h470v107h-218v147h365v-281z" />
<glyph glyph-name="H" unicode="H"
d="M839 0h-147v290h-470v-290h-147v728h147v-291h470v291h147v-728z" />
<glyph glyph-name="I" unicode="I" horiz-adv-x="587"
d="M512 0h-437v147h145v434h-145v147h437v-147h-145v-434h145v-147z" />
<glyph glyph-name="J" unicode="J" horiz-adv-x="878"
d="M803 120l-120 -120h-488l-120 120v136h147v-109h434v434h-182v147h329v-608z" />
<glyph glyph-name="K" unicode="K" horiz-adv-x="878"
d="M803 0h-136l-291 290h-154v-290h-147v728h147v-291h154l291 291h136v-64l-301 -300l301 -301v-63z" />
<glyph glyph-name="L" unicode="L" horiz-adv-x="769"
d="M694 0h-619v728h147v-581h472v-147z" />
<glyph glyph-name="M" unicode="M" horiz-adv-x="950"
d="M875 0h-147v517l-253 -253l-253 253v-517h-147v728h136l264 -265l264 265h136v-728z" />
<glyph glyph-name="N" unicode="N" horiz-adv-x="950"
d="M875 0h-136l-517 517v-517h-147v728h136l517 -518v518h147v-728z" />
<glyph glyph-name="O" unicode="O"
d="M839 120l-120 -120h-524l-120 120v488l120 120h524l120 -120v-488zM692 147v434h-470v-434h470z" />
<glyph glyph-name="P" unicode="P" horiz-adv-x="841"
d="M766 301l-120 -120h-424v-181h-147v728h571l120 -120v-307zM619 328v253h-397v-253h397z" />
<glyph glyph-name="Q" unicode="Q" horiz-adv-x="986"
d="M911 -109h-135l-109 109h-472l-120 120v488l120 120h524l120 -120v-415l-83 -83l155 -156v-63zM692 147v434h-470v-434h470z" />
<glyph glyph-name="R" unicode="R"
d="M839 0h-136l-290 290h-191v-290h-147v728h644l120 -120v-198l-120 -120h-107l227 -227v-63zM692 437v144h-470v-144h470z" />
<glyph glyph-name="S" unicode="S"
d="M839 120l-120 -120h-524l-120 120v136h147v-109h470v143h-497l-120 120v198l120 120h524l120 -120v-136h-147v109h-470v-144h497l120 -120v-197z" />
<glyph glyph-name="T" unicode="T" horiz-adv-x="878"
d="M803 581h-291v-581h-147v581h-290v147h728v-147z" />
<glyph glyph-name="U" unicode="U"
d="M839 120l-120 -120h-524l-120 120v608h147v-581h470v581h147v-608z" />
<glyph glyph-name="V" unicode="V" horiz-adv-x="878"
d="M803 338l-338 -338h-52l-338 338v390h147v-338l217 -217l217 217v338h147v-390z" />
<glyph glyph-name="W" unicode="W" horiz-adv-x="950"
d="M875 84l-84 -84h-52l-264 264l-264 -264h-52l-84 84v644h147v-518l227 227h52l227 -227v518h147v-644z" />
<glyph glyph-name="X" unicode="X" horiz-adv-x="841"
d="M766 0h-147v84l-198 198l-199 -198v-84h-147v136l228 228l-228 228v136h147v-84l199 -199l198 199v84h147v-136l-228 -228l228 -228v-136z" />
<glyph glyph-name="Y" unicode="Y" horiz-adv-x="878"
d="M803 592l-291 -291v-301h-147v301l-290 291v136h147v-84l217 -217l217 217v84h147v-136z" />
<glyph glyph-name="Z" unicode="Z" horiz-adv-x="805"
d="M730 0h-655v136l445 445h-445v147h655v-136l-445 -445h445v-147z" />
<glyph glyph-name="bracketleft" unicode="[" horiz-adv-x="406"
d="M331 -109h-256v945h256v-146h-109v-652h109v-147z" />
<glyph glyph-name="backslash" unicode="\" horiz-adv-x="805"
d="M730 0h-147v84l-508 508v136h147v-84l508 -508v-136z" />
<glyph glyph-name="bracketright" unicode="]" horiz-adv-x="406"
d="M331 -109h-256v147h109v652h-109v146h256v-945z" />
<glyph glyph-name="asciicircum" unicode="^" horiz-adv-x="515"
d="M440 435h-64l-119 120l-119 -120h-63v136l156 157h52l157 -157v-136z" />
<glyph glyph-name="underscore" unicode="_"
d="M839 -109h-764v147h764v-147z" />
<glyph glyph-name="grave" unicode="`" horiz-adv-x="762"
d="M382 601h-136l-192 192v63h135q63 -64 193 -192v-63z" />
<glyph glyph-name="a" unicode="a"
d="M839 0h-63l-65 65l-65 -65h-451l-120 120v342l120 120h451l65 -64l65 64h63v-582zM692 147v288h-470v-288h470z" />
<glyph glyph-name="b" unicode="b"
d="M839 120l-120 -120h-524l-120 120v680h147v-263l46 45h451l120 -120v-342zM692 147v288h-470v-288h470z" />
<glyph glyph-name="c" unicode="c" horiz-adv-x="841"
d="M766 0h-571l-120 120v342l120 120h571v-147h-544v-288h544v-147z" />
<glyph glyph-name="d" unicode="d"
d="M839 0h-63l-65 65l-65 -65h-451l-120 120v342l120 120h451l46 -45v263h147v-800zM692 147v288h-470v-288h470z" />
<glyph glyph-name="e" unicode="e" horiz-adv-x="878"
d="M803 0h-608l-120 120v342l120 120h488l120 -120v-244h-581v-71h581v-147zM656 365v70h-434v-70h434z" />
<glyph glyph-name="f" unicode="f" horiz-adv-x="696"
d="M621 435h-290v-435h-147v435h-109v147h109v98l120 120h317v-147h-290v-71h290v-147z" />
<glyph glyph-name="g" unicode="g"
d="M839 -98l-120 -120h-535v147h508v117l-46 -46h-451l-120 120v342l120 120h451l65 -64l65 64h63v-680zM692 147v288h-470v-288h470z" />
<glyph glyph-name="h" unicode="h" horiz-adv-x="878"
d="M803 0h-147v435h-434v-435h-147v800h147v-263l46 45h415l120 -120v-462z" />
<glyph glyph-name="i" unicode="i" horiz-adv-x="406"
d="M222 653h-147v147h147v-147zM331 0h-136l-120 120v462h147v-435h109v-147z" />
<glyph glyph-name="j" unicode="j" horiz-adv-x="297"
d="M222 653h-147v147h147v-147zM222 -98l-120 -120h-245v147h218v653h147v-680z" />
<glyph glyph-name="k" unicode="k" horiz-adv-x="841"
d="M766 0h-135l-218 218h-191v-218h-147v800h147v-435h191l218 217h135v-63l-228 -228l228 -228v-63z" />
<glyph glyph-name="l" unicode="l" horiz-adv-x="406"
d="M331 0h-136l-120 120v680h147v-653h109v-147z" />
<glyph glyph-name="m" unicode="m" horiz-adv-x="1168"
d="M1093 0h-147v435h-289v-435h-147v435h-288v-435h-147v582h63l65 -64l65 64h269l65 -64l65 64h306l120 -120v-462z" />
<glyph glyph-name="n" unicode="n" horiz-adv-x="878"
d="M803 0h-147v435h-434v-435h-147v582h63l65 -64l65 64h415l120 -120v-462z" />
<glyph glyph-name="o" unicode="o"
d="M839 120l-120 -120h-524l-120 120v342l120 120h524l120 -120v-342zM692 147v288h-470v-288h470z" />
<glyph glyph-name="p" unicode="p"
d="M839 120l-120 -120h-451l-46 46v-264h-147v800h63l65 -64l65 64h451l120 -120v-342zM692 147v288h-470v-288h470z" />
<glyph glyph-name="q" unicode="q"
d="M839 -218h-147v264l-46 -46h-451l-120 120v342l120 120h524l120 -120v-680zM692 147v288h-470v-288h470z" />
<glyph glyph-name="r" unicode="r" horiz-adv-x="660"
d="M585 435h-363v-435h-147v582h63l65 -64l65 64h317v-147z" />
<glyph glyph-name="s" unicode="s" horiz-adv-x="841"
d="M766 120l-120 -120h-571v147h544v71h-424l-120 120v124l120 120h571v-147h-544v-70h424l120 -120v-125z" />
<glyph glyph-name="t" unicode="t" horiz-adv-x="769"
d="M694 0h-390l-120 120v315h-109v147h109v146h147v-146h363v-147h-363v-288h363v-147z" />
<glyph glyph-name="u" unicode="u" horiz-adv-x="878"
d="M803 0h-64l-64 65l-65 -65h-415l-120 120v462h147v-435h434v435h147v-582z" />
<glyph glyph-name="v" unicode="v" horiz-adv-x="878"
d="M803 338l-338 -338h-52l-338 338v244h147v-192l217 -217l217 217v192h147v-244z" />
<glyph glyph-name="w" unicode="w" horiz-adv-x="1168"
d="M1093 120l-120 -120h-306l-65 65l-65 -65h-342l-120 120v462h147v-435h288v435h147v-435h289v435h147v-462z" />
<glyph glyph-name="x" unicode="x" horiz-adv-x="841"
d="M766 0h-135l-210 210l-210 -210h-136v63l228 228l-228 228v63h136l210 -209l210 209h135v-63l-228 -228l228 -228v-63z" />
<glyph glyph-name="y" unicode="y" horiz-adv-x="878"
d="M803 -98l-120 -120h-499v147h472v117l-46 -46h-415l-120 120v462h147v-435h434v435h147v-680z" />
<glyph glyph-name="z" unicode="z" horiz-adv-x="841"
d="M766 0h-691v245l120 120h424v70h-544v147h691v-244l-120 -120h-424v-71h544v-147z" />
<glyph glyph-name="braceleft" unicode="{" horiz-adv-x="478"
d="M403 -109h-135l-120 120v254l-73 73v52l73 72v254l120 120h135v-146h-108v-243l-73 -73v-20l73 -73v-243h108v-147z" />
<glyph glyph-name="bar" unicode="|" horiz-adv-x="297"
d="M222 0h-147v764h147v-764z" />
<glyph glyph-name="braceright" unicode="}" horiz-adv-x="478"
d="M403 338l-72 -73v-254l-120 -120h-136v147h109v243l72 73v20l-72 73v243h-109v146h136l120 -120v-254l72 -72v-52z" />
<glyph glyph-name="asciitilde" unicode="~" horiz-adv-x="769"
d="M694 374l-120 -120h-161l-138 137l-137 -137h-63v136l120 120h161l137 -137l138 137h63v-136z" />
<glyph glyph-name="DEL" horiz-adv-x="290"
/>
<glyph glyph-name="Adieresis" unicode="&#xc4;" horiz-adv-x="935"
d="M860 0h-147v181h-470v-181h-147v390l338 338h89l337 -338v-390zM713 328v10l-235 235l-235 -235v-10h470zM657 746h-147v147h147v-147zM403 746h-147v147h147v-147z" />
<glyph glyph-name="Aring" unicode="&#xc5;" horiz-adv-x="935"
d="M860 0h-147v181h-470v-181h-147v390l338 338h89l337 -338v-390zM713 328v10l-235 235l-235 -235v-10h470zM657 830l-120 -121h-161l-120 121v124l120 120h161l120 -120v-124zM521 892l-64 64l-64 -64l64 -64z" />
<glyph glyph-name="Ccedilla" unicode="&#xc7;"
d="M839 120l-120 -120h-143l45 -46v-124l-84 -84h-197l-47 47v100h110v-36h82l18 17l-65 65v61h-243l-120 120v488l120 120h524l120 -120v-136h-147v109h-470v-434h470v109h147v-136z" />
<glyph glyph-name="Eacute" unicode="&#xc9;" horiz-adv-x="863"
d="M788 0h-692v728h692v-147h-545v-144h545v-147h-545v-143h545v-147zM657 938l-192 -192h-136v63l193 193h135v-64z" />
<glyph glyph-name="Ntilde" unicode="&#xd1;" horiz-adv-x="972"
d="M897 0h-136l-518 517v-517h-147v728h136l518 -518v518h147v-728zM730 866l-120 -120h-125l-119 119l-119 -119h-63v136l120 120h125l119 -119l119 119h63v-136z" />
<glyph glyph-name="Odieresis" unicode="&#xd6;" horiz-adv-x="935"
d="M860 120l-120 -120h-524l-120 120v488l120 120h524l120 -120v-488zM713 147v434h-470v-434h470zM657 746h-147v147h147v-147zM403 746h-147v147h147v-147z" />
<glyph glyph-name="Udieresis" unicode="&#xdc;" horiz-adv-x="935"
d="M860 120l-120 -120h-524l-120 120v608h147v-581h470v581h147v-608zM657 746h-147v147h147v-147zM403 746h-147v147h147v-147z" />
<glyph glyph-name="aacute" unicode="&#xe1;" horiz-adv-x="935"
d="M860 0h-63l-65 65l-64 -65h-452l-120 120v342l120 120h452l64 -64l65 64h63v-582zM713 147v288h-470v-288h470zM657 793l-192 -192h-136v63l193 192h135v-63z" />
<glyph glyph-name="agrave" unicode="&#xe0;" horiz-adv-x="935"
d="M860 0h-63l-65 65l-64 -65h-452l-120 120v342l120 120h452l64 -64l65 64h63v-582zM713 147v288h-470v-288h470zM549 601h-136l-193 192v63h136q63 -64 193 -192v-63z" />
<glyph glyph-name="acircumflex" unicode="&#xe2;" horiz-adv-x="935"
d="M860 0h-63l-65 65l-64 -65h-452l-120 120v342l120 120h452l64 -64l65 64h63v-582zM713 147v288h-470v-288h470zM585 601h-100l-64 64l-65 -64h-100v99l120 120h89l120 -120v-99z" />
<glyph glyph-name="adieresis" unicode="&#xe4;" horiz-adv-x="935"
d="M860 0h-63l-65 65l-64 -65h-452l-120 120v342l120 120h452l64 -64l65 64h63v-582zM713 147v288h-470v-288h470zM621 601h-147v147h147v-147zM367 601h-147v147h147v-147z" />
<glyph glyph-name="atilde" unicode="&#xe3;" horiz-adv-x="935"
d="M860 0h-63l-65 65l-64 -65h-452l-120 120v342l120 120h452l64 -64l65 64h63v-582zM713 147v288h-470v-288h470zM730 721l-120 -120h-125l-119 119l-119 -119h-63v135l120 120h125l119 -119l119 119h63v-135z" />
<glyph glyph-name="aring" unicode="&#xe5;" horiz-adv-x="935"
d="M860 0h-63l-65 65l-64 -65h-452l-120 120v342l120 120h452l64 -64l65 64h63v-582zM713 147v288h-470v-288h470zM657 721l-120 -120h-161l-120 120v124l120 120h161l120 -120v-124zM521 783l-64 64l-64 -64l64 -64z" />
<glyph glyph-name="ccedilla" unicode="&#xe7;" horiz-adv-x="841"
d="M766 0h-190l45 -46v-124l-84 -84h-197l-47 47v100h110v-36h82l18 17l-65 65v61h-243l-120 120v342l120 120h571v-147h-544v-288h544v-147z" />
<glyph glyph-name="eacute" unicode="&#xe9;" horiz-adv-x="899"
d="M824 0h-608l-120 120v342l120 120h488l120 -120v-244h-581v-71h581v-147zM677 365v70h-434v-70h434zM657 793l-192 -192h-136v63l193 192h135v-63z" />
<glyph glyph-name="egrave" unicode="&#xe8;" horiz-adv-x="899"
d="M824 0h-608l-120 120v342l120 120h488l120 -120v-244h-581v-71h581v-147zM677 365v70h-434v-70h434zM549 601h-136l-193 192v63h136q63 -64 193 -192v-63z" />
<glyph glyph-name="ecircumflex" unicode="&#xea;" horiz-adv-x="899"
d="M824 0h-608l-120 120v342l120 120h488l120 -120v-244h-581v-71h581v-147zM677 365v70h-434v-70h434zM621 601h-99l-65 64l-65 -64h-99v99l120 120h88l120 -120v-99z" />
<glyph glyph-name="edieresis" unicode="&#xeb;" horiz-adv-x="899"
d="M824 0h-608l-120 120v342l120 120h488l120 -120v-244h-581v-71h581v-147zM677 365v70h-434v-70h434zM657 601h-147v147h147v-147zM403 601h-147v147h147v-147z" />
<glyph glyph-name="iacute" unicode="&#xed;" horiz-adv-x="478"
d="M352 0h-136l-120 120v462h147v-435h109v-147zM403 793l-192 -192h-136v63l193 192h135v-63z" />
<glyph glyph-name="igrave" unicode="&#xec;" horiz-adv-x="500"
d="M425 0h-136l-120 120v462h147v-435h109v-147zM403 601h-135l-193 192v63h136q62 -64 192 -192v-63z" />
<glyph glyph-name="icircumflex" unicode="&#xee;" horiz-adv-x="500"
d="M425 0h-136l-120 120v462h147v-435h109v-147zM403 601h-99l-65 64l-64 -64h-100v99l120 120h88l120 -120v-99z" />
<glyph glyph-name="idieresis" unicode="&#xef;" horiz-adv-x="551"
d="M461 0h-136l-120 120v462h147v-435h109v-147zM476 601h-147v147h147v-147zM222 601h-147v147h147v-147z" />
<glyph glyph-name="ntilde" unicode="&#xf1;" horiz-adv-x="899"
d="M824 0h-147v435h-434v-435h-147v582h64l64 -64l65 64h415l120 -120v-462zM803 721l-120 -120h-125l-119 119l-119 -119h-64v135l120 120h125l119 -119l119 119h64v-135z" />
<glyph glyph-name="oacute" unicode="&#xf3;" horiz-adv-x="935"
d="M860 120l-120 -120h-524l-120 120v342l120 120h524l120 -120v-342zM713 147v288h-470v-288h470zM657 793l-192 -192h-136v63l193 192h135v-63z" />
<glyph glyph-name="ograve" unicode="&#xf2;" horiz-adv-x="935"
d="M860 120l-120 -120h-524l-120 120v342l120 120h524l120 -120v-342zM713 147v288h-470v-288h470zM549 601h-136l-193 192v63h136q63 -64 193 -192v-63z" />
<glyph glyph-name="ocircumflex" unicode="&#xf4;" horiz-adv-x="935"
d="M860 120l-120 -120h-524l-120 120v342l120 120h524l120 -120v-342zM713 147v288h-470v-288h470zM621 601h-99l-65 64l-65 -64h-99v99l120 120h88l120 -120v-99z" />
<glyph glyph-name="odieresis" unicode="&#xf6;" horiz-adv-x="935"
d="M860 120l-120 -120h-524l-120 120v342l120 120h524l120 -120v-342zM713 147v288h-470v-288h470zM657 601h-147v147h147v-147zM403 601h-147v147h147v-147z" />
<glyph glyph-name="otilde" unicode="&#xf5;" horiz-adv-x="935"
d="M860 120l-120 -120h-524l-120 120v342l120 120h524l120 -120v-342zM713 147v288h-470v-288h470zM730 721l-120 -120h-125l-119 119l-119 -119h-63v135l120 120h125l119 -119l119 119h63v-135z" />
<glyph glyph-name="uacute" unicode="&#xfa;" horiz-adv-x="899"
d="M824 0h-63l-65 65l-65 -65h-415l-120 120v462h147v-435h434v435h147v-582zM657 793l-192 -192h-136v63l193 192h135v-63z" />
<glyph glyph-name="ugrave" unicode="&#xf9;" horiz-adv-x="899"
d="M824 0h-63l-65 65l-65 -65h-415l-120 120v462h147v-435h434v435h147v-582zM549 601h-136l-193 192v63h136q63 -64 193 -192v-63z" />
<glyph glyph-name="ucircumflex" unicode="&#xfb;" horiz-adv-x="899"
d="M824 0h-63l-65 65l-65 -65h-415l-120 120v462h147v-435h434v435h147v-582zM621 601h-99l-65 64l-65 -64h-99v99l120 120h88l120 -120v-99z" />
<glyph glyph-name="udieresis" unicode="&#xfc;" horiz-adv-x="899"
d="M824 0h-63l-65 65l-65 -65h-415l-120 120v462h147v-435h434v435h147v-582zM657 601h-147v147h147v-147zM403 601h-147v147h147v-147z" />
<glyph glyph-name="dagger" unicode="&#x2020;" horiz-adv-x="660"
d="M585 363h-182v-363h-147v363h-181v147h181v181h147v-181h182v-147z" />
<glyph glyph-name="degree" unicode="&#xb0;" horiz-adv-x="515"
d="M440 483l-120 -120h-125l-120 120v125l120 120h125l120 -120v-125zM293 510v71h-71v-71h71z" />
<glyph glyph-name="cent" unicode="&#xa2;" horiz-adv-x="878"
d="M803 193l-120 -120h-171v-109h-147v109h-170l-120 120v342l120 120h170v109h147v-109h171l120 -120v-100h-147v73h-434v-288h434v72h147v-99z" />
<glyph glyph-name="sterling" unicode="&#xa3;" horiz-adv-x="841"
d="M766 0h-691v147h109v143h-109v147h109v171l120 120h353v-147h-326v-144h218v-147h-218v-143h435v-147z" />
<glyph glyph-name="section" unicode="&#xa7;"
d="M839 84l-120 -120h-535v147h508v70h-497l-120 120v125l47 47l-47 46v125l120 120h535v-147h-508v-71h497l120 -120v-125l-47 -46l47 -47v-124zM692 328v71h-470v-71h470z" />
<glyph glyph-name="bullet" unicode="&#x2022;" horiz-adv-x="515"
d="M440 301l-120 -120h-125l-120 120v125l120 120h125l120 -120v-125z" />
<glyph glyph-name="paragraph" unicode="&#xb6;" horiz-adv-x="732"
d="M657 0h-147v399h-70v-399h-147v399h-98l-120 120v89l120 120h462v-728zM510 546v35h-70v-35h70zM293 546v35h-71v-35h71z" />
<glyph glyph-name="germandbls" unicode="&#xdf;"
d="M839 120l-120 -120h-426v147h399v82l-61 61h-338v147h338l61 62v82h-470v-690h-147v717l120 120h524l120 -120v-161l-83 -83l83 -83v-161z" />
<glyph glyph-name="registered" unicode="&#xae;"
d="M839 120l-120 -120h-524l-120 120v488l120 120h524l120 -120v-488zM692 147v434h-470v-434h470zM657 181h-135l-109 109h-10v-109h-147v365h318l83 -84v-88l-64 -65l64 -64v-64zM510 401v34h-107v-34h107z" />
<glyph glyph-name="copyright" unicode="&#xa9;"
d="M839 120l-120 -120h-524l-120 120v488l120 120h524l120 -120v-488zM692 147v434h-470v-434h470zM657 181h-281l-120 120v125l120 120h281v-147h-254v-71h254v-147z" />
<glyph glyph-name="trademark" unicode="&#x2122;" horiz-adv-x="1241"
d="M1166 218h-147v299l-119 -118h-52l-118 118v-299h-147v510h136l155 -156l156 156h136v-510zM585 581h-182v-363h-147v363h-181v147h510v-147z" />
<glyph glyph-name="acute" unicode="&#xb4;" horiz-adv-x="762"
d="M382 793l-193 -192h-135v63l192 192h136v-63z" />
<glyph glyph-name="dieresis" unicode="&#xa8;" horiz-adv-x="726"
d="M491 746h-147v147h147v-147zM237 746h-147v147h147v-147z" />
<glyph glyph-name="notequal" unicode="&#x2260;" horiz-adv-x="823"
d="M748 619l-100 -100v-120h-84l-70 -71h154v-147h-301l-73 -72h-199l100 99v120h120l70 71h-190v147h301l73 73h199z" />
<glyph glyph-name="AE" unicode="&#xc6;" horiz-adv-x="1146"
d="M1071 0h-561v181h-288v-181h-147v354l374 374h622v-147h-414v-144h414v-147h-414v-143h414v-147zM510 328v253h-9l-252 -253h261z" />
<glyph glyph-name="Oslash" unicode="&#xd8;"
d="M839 120l-120 -120h-524l-120 120v488l120 120h524l120 -120v-488zM692 147v334l-334 -334h334zM556 581h-334v-335z" />
<glyph glyph-name="infinity" unicode="&#x221e;" horiz-adv-x="878"
d="M803 229l-120 -120h-198l-46 46l-47 -46h-197l-120 120v270l120 120h197l47 -47l46 47h198l120 -120v-270zM656 256v216h-144v-216h144zM365 256v216h-143v-216h143z" />
<glyph glyph-name="plusminus" unicode="&#xb1;" horiz-adv-x="587"
d="M512 290h-145v-145h-147v145h-145v147h145v145h147v-145h145v-147zM512 0h-437v147h437v-147z" />
<glyph glyph-name="lessequal" unicode="&#x2264;" horiz-adv-x="587"
d="M440 181h-136l-156 157v52l156 156h136v-99l-83 -83l83 -83v-100zM512 0h-437v147h437v-147z" />
<glyph glyph-name="greaterequal" unicode="&#x2265;" horiz-adv-x="587"
d="M440 338l-157 -157h-135v100l82 83l-82 83v99h135l157 -156v-52zM512 0h-437v147h437v-147z" />
<glyph glyph-name="yen" unicode="&#xa5;" horiz-adv-x="878"
d="M803 628l-291 -290v-10h254v-147h-254v-181h-147v181h-254v147h254v10l-290 290v100h100l264 -265l264 265h100v-100z" />
<glyph glyph-name="mu" unicode="&#xb5;"
d="M839 0h-63l-65 65l-65 -65h-378l-46 46v-264h-147v800h147v-435h470v435h147v-582z" />
<glyph glyph-name="mu" unicode="&#x3bc;"
d="M839 0h-63l-65 65l-65 -65h-378l-46 46v-264h-147v800h147v-435h470v435h147v-582z" />
<glyph glyph-name="partialdiff" unicode="&#x2202;"
d="M839 120l-120 -120h-524l-120 120v197l120 120h361l-227 227v64h172l338 -338v-270zM692 147v143h-470v-143h470z" />
<glyph glyph-name="summation" unicode="&#x2211;" horiz-adv-x="805"
d="M730 0h-535l-120 120v125l119 119l-119 119v125l120 120h535v-147h-508v-46l98 -98h265v-147h-265l-98 -97v-46h508v-147z" />
<glyph glyph-name="product" unicode="&#x220f;" horiz-adv-x="732"
d="M657 581h-108v-581h-147v581h-71v-581h-147v581h-109v147h582v-147z" />
<glyph glyph-name="pi" unicode="&#x3c0;" horiz-adv-x="732"
d="M657 435h-108v-435h-147v435h-71v-435h-147v435h-109v147h582v-147z" />
<glyph glyph-name="integral" unicode="&#x222b;" horiz-adv-x="732"
d="M657 581h-217v-570l-120 -120h-245v147h218v570l120 120h244v-147z" />
<glyph glyph-name="ordfeminine" unicode="&#xaa;"
d="M839 145h-63l-65 65l-65 -65h-451l-120 120v343l120 120h451l65 -65l65 65h63v-583zM692 292v289h-470v-289h470z" />
<glyph glyph-name="ordmasculine" unicode="&#xba;"
d="M839 265l-120 -120h-524l-120 120v343l120 120h524l120 -120v-343zM692 292v289h-470v-289h470z" />
<glyph glyph-name="Omega" unicode="&#x3a9;"
d="M839 0h-136l-120 120v125l109 109v227h-470v-227l109 -109v-125l-120 -120h-136v147h109v46l-109 108v307l120 120h524l120 -120v-307l-109 -108v-46h109v-147z" />
<glyph glyph-name="Omega" unicode="&#x2126;"
d="M839 0h-136l-120 120v125l109 109v227h-470v-227l109 -109v-125l-120 -120h-136v147h109v46l-109 108v307l120 120h524l120 -120v-307l-109 -108v-46h109v-147z" />
<glyph glyph-name="ae" unicode="&#xe6;" horiz-adv-x="1095"
d="M1020 0h-825l-120 120v342l120 120h306l47 -46l46 46h306l120 -120v-244h-399v-71h399v-147zM873 365v70h-252v-70h252zM474 147v288h-252v-288h252z" />
<glyph glyph-name="oslash" unicode="&#xf8;" horiz-adv-x="878"
d="M803 120l-120 -120h-488l-120 120v342l120 120h488l120 -120v-342zM656 147v262l-262 -262h262zM484 435h-262v-261z" />
<glyph glyph-name="questiondown" unicode="&#xbf;" horiz-adv-x="696"
d="M512 472h-147v147h147v-147zM621 -109h-426l-120 120v197l120 120h170v73h147v-100l-120 -120h-170v-143h399v-147z" />
<glyph glyph-name="exclamdown" unicode="&#xa1;" horiz-adv-x="297"
d="M222 472h-147v147h147v-147zM222 -109h-147v510h147v-510z" />
<glyph glyph-name="logicalnot" unicode="&#xac;" horiz-adv-x="624"
d="M549 181h-147v109h-327v147h474v-256z" />
<glyph glyph-name="radical" unicode="&#x221a;" horiz-adv-x="805"
d="M730 0h-535l-120 120v125l119 119l-119 119v125l120 120h535v-147h-508v-46l98 -98h410v-147h-410l-98 -97v-46h508v-147z" />
<glyph glyph-name="florin" unicode="&#x192;" horiz-adv-x="732"
d="M657 327h-217v-316l-120 -120h-245v147h218v289h-109v147h109v134l120 120h244v-147h-217v-107h217v-147z" />
<glyph glyph-name="approxequal" unicode="&#x2248;" horiz-adv-x="769"
d="M694 592l-157 -157h-124l-119 120l-119 -120h-100v100l156 156h125l119 -119l119 119h100v-99zM694 301l-157 -156h-124l-119 119l-119 -119h-100v100l156 156h125l119 -119l119 119h100v-100z" />
<glyph glyph-name="approxequal" unicode="&#x22f2;" horiz-adv-x="769"
d="M694 592l-157 -157h-124l-119 120l-119 -120h-100v100l156 156h125l119 -119l119 119h100v-99zM694 301l-157 -156h-124l-119 119l-119 -119h-100v100l156 156h125l119 -119l119 119h100v-100z" />
<glyph glyph-name="Delta" unicode="&#x394;"
d="M839 0h-764v208l338 338h88l338 -338v-208zM701 147l-244 244l-244 -244h488z" />
<glyph glyph-name="Delta" unicode="&#x2206;"
d="M839 0h-764v208l338 338h88l338 -338v-208zM701 147l-244 244l-244 -244h488z" />
<glyph glyph-name="guillemotleft" unicode="&#xab;" horiz-adv-x="696"
d="M621 181h-99l-157 157v52l157 156h99v-99l-83 -83l83 -83v-100zM440 109h-136l-229 229v52l229 229h136v-100l-156 -155l156 -156v-99z" />
<glyph glyph-name="guillemotright" unicode="&#xbb;" horiz-adv-x="696"
d="M621 338l-229 -229h-136v99l156 156l-156 155v100h136l229 -229v-52zM331 338l-156 -157h-100v100l83 83l-83 83v99h100l156 -156v-52z" />
<glyph glyph-name="ellipsis" unicode="&#x2026;" horiz-adv-x="732"
d="M657 0h-147v147h147v-147zM440 0h-147v147h147v-147zM222 0h-147v147h147v-147z" />
<glyph glyph-name="nonbreakingspace" unicode="&#xa0;" horiz-adv-x="290"
/>
<glyph glyph-name="Agrave" unicode="&#xc0;" horiz-adv-x="935"
d="M860 0h-147v181h-470v-181h-147v390l338 338h89l337 -338v-390zM713 328v10l-235 235l-235 -235v-10h470zM549 746h-136l-193 192v64h136q63 -64 193 -193v-63z" />
<glyph glyph-name="Atilde" unicode="&#xc3;" horiz-adv-x="935"
d="M860 0h-147v181h-470v-181h-147v390l338 338h89l337 -338v-390zM713 328v10l-235 235l-235 -235v-10h470zM694 866l-120 -120h-125l-119 119l-119 -119h-63v136l120 120h124l119 -119l120 119h63v-136z" />
<glyph glyph-name="Otilde" unicode="&#xd5;" horiz-adv-x="935"
d="M860 120l-120 -120h-524l-120 120v488l120 120h524l120 -120v-488zM713 147v434h-470v-434h470zM730 866l-120 -120h-125l-119 119l-119 -119h-63v136l120 120h125l119 -119l119 119h63v-136z" />
<glyph glyph-name="OE" unicode="&#x152;" horiz-adv-x="1095"
d="M1020 0h-825l-120 120v488l120 120h825v-147h-399v-144h399v-147h-399v-143h399v-147zM474 147v434h-252v-434h252z" />
<glyph glyph-name="oe" unicode="&#x153;" horiz-adv-x="1095"
d="M1020 0h-426l-46 47l-47 -47h-306l-120 120v342l120 120h306l47 -46l46 46h306l120 -120v-244h-399v-71h399v-147zM873 365v70h-252v-70h252zM474 147v288h-252v-288h252z" />
<glyph glyph-name="endash" unicode="&#x2013;" horiz-adv-x="805"
d="M730 290h-655v147h655v-147z" />
<glyph glyph-name="emdash" unicode="&#x2014;" horiz-adv-x="950"
d="M875 290h-800v147h800v-147z" />
<glyph glyph-name="quotedblleft" unicode="&#x201c;" horiz-adv-x="478"
d="M403 628l-46 -46l10 -11v-63h-63l-48 48v172h147v-100zM222 628l-47 -46l11 -11v-63h-64l-47 48v172h147v-100z" />
<glyph glyph-name="quotedblright" unicode="&#x201d;" horiz-adv-x="478"
d="M403 556l-47 -48h-63v63l10 11l-47 46v100h147v-172zM222 556l-47 -48h-64v63l11 11l-47 46v100h147v-172z" />
<glyph glyph-name="quoteleft" unicode="&#x2018;" horiz-adv-x="297"
d="M222 628l-47 -47h-45l14 32l42 -42v-63h-64l-47 48v172h147v-100z" />
<glyph glyph-name="quoteright" unicode="&#x2019;" horiz-adv-x="297"
d="M222 556l-47 -48h-64v63l11 11l-47 46v100h147v-172z" />
<glyph glyph-name="divide" unicode="&#xf7;" horiz-adv-x="515"
d="M331 508h-147v147h147v-147zM440 290h-365v147h365v-147zM331 73h-147v147h147v-147z" />
<glyph glyph-name="lozenge" unicode="&#x25ca;" horiz-adv-x="732"
d="M657 338l-228 -229h-125l-229 229v52l229 229h125l228 -229v-52zM484 364l-118 118l-118 -118l118 -118z" />
<glyph glyph-name="ydieresis" unicode="&#xff;" horiz-adv-x="899"
d="M824 -98l-120 -120h-499v147h472v117l-46 -46h-415l-120 120v462h147v-435h434v435h147v-680zM657 601h-147v147h147v-147zM403 601h-147v147h147v-147z" />
<glyph glyph-name="Ydieresis" unicode="&#x178;" horiz-adv-x="935"
d="M860 592l-290 -291v-301h-147v301l-290 291v136h147v-84l217 -217l216 217v84h147v-136zM694 782h-147v147h147v-147zM440 782h-147v147h147v-147z" />
<glyph glyph-name="fraction" unicode="&#x2044;" horiz-adv-x="769"
d="M694 592l-472 -472v-120h-147v172l472 472v120h147v-172z" />
<glyph glyph-name="currency" unicode="&#xa4;" horiz-adv-x="986"
d="M911 120l-120 -120h-523l-120 120v98h-73v292h73v98l120 120h523l120 -120v-136h-146v109h-470v-71h217v-292h-217v-71h470v109h146v-136z" />
<glyph glyph-name="guilsinglleft" unicode="&#x2039;" horiz-adv-x="406"
d="M331 181h-100l-156 157v52l156 156h100v-99l-83 -83l83 -83v-100z" />
<glyph glyph-name="guilsinglright" unicode="&#x203a;" horiz-adv-x="406"
d="M331 338l-156 -157h-100v100l83 83l-83 83v99h100l156 -156v-52z" />
<glyph glyph-name="fi" unicode="&#xf001;" horiz-adv-x="986"
d="M911 0h-135l-120 120v462h147v-435h108v-147zM803 581h-472v-107h326v-147h-326v-327h-147v327h-109v147h109v134l120 120h499v-147z" />
<glyph glyph-name="fl" unicode="&#xf002;" horiz-adv-x="986"
d="M911 0h-135l-120 120v461h-325v-107h326v-147h-326v-327h-147v327h-109v147h109v134l120 120h499v-581h108v-147z" />
<glyph glyph-name="daggerdbl" unicode="&#x2021;" horiz-adv-x="660"
d="M585 145h-182v-145h-147v145h-181v147h181v71h-181v147h181v109h147v-109h182v-147h-182v-71h182v-147z" />
<glyph glyph-name="periodcentered" unicode="&#xb7;" horiz-adv-x="297"
d="M222 290h-147v147h147v-147z" />
<glyph glyph-name="periodcentered" unicode="&#x2219;" horiz-adv-x="297"
d="M222 290h-147v147h147v-147z" />
<glyph glyph-name="quotesinglbase" unicode="&#x201a;" horiz-adv-x="297"
d="M222 -25l-47 -48h-64v64l11 10l-47 46v100h147v-172z" />
<glyph glyph-name="quotedblbase" unicode="&#x201e;" horiz-adv-x="478"
d="M403 -25l-47 -48h-63v64l10 10l-47 46v100h147v-172zM222 -25l-47 -48h-64v64l11 10l-47 46v100h147v-172z" />
<glyph glyph-name="perthousand" unicode="&#x2030;" horiz-adv-x="1241"
d="M1166 120l-121 -120h-124l-120 120v125l120 120h124l121 -120v-125zM766 592l-508 -508v-84h-147v136l508 508v84h147v-136zM803 120l-120 -120h-125l-120 120v125l120 120h125l120 -120v-125zM440 483l-120 -120h-125l-120 120v125l120 120h125l120 -120v-125zM1055 172
v21l-62 61h-20l-62 -61v-21l62 -61h20zM329 535v21l-61 61h-21l-61 -61v-21l61 -61h21zM692 172v21l-61 61h-21l-61 -61v-21l61 -61h21z" />
<glyph glyph-name="Acircumflex" unicode="&#xc2;" horiz-adv-x="935"
d="M860 0h-147v181h-470v-181h-147v390l338 338h89l337 -338v-390zM713 328v10l-235 235l-235 -235v-10h470zM621 746h-99l-65 64l-65 -64h-99v99l120 120h88l120 -120v-99z" />
<glyph glyph-name="Ecircumflex" unicode="&#xca;" horiz-adv-x="863"
d="M788 0h-692v728h692v-147h-545v-144h545v-147h-545v-143h545v-147zM585 746h-100l-64 64l-65 -64h-100v99l120 120h89l120 -120v-99z" />
<glyph glyph-name="Aacute" unicode="&#xc1;" horiz-adv-x="935"
d="M860 0h-147v181h-470v-181h-147v390l338 338h89l337 -338v-390zM713 328v10l-235 235l-235 -235v-10h470zM657 938l-192 -192h-136v63l193 193h135v-64z" />
<glyph glyph-name="Edieresis" unicode="&#xcb;" horiz-adv-x="863"
d="M788 0h-692v728h692v-147h-545v-144h545v-147h-545v-143h545v-147zM621 746h-147v147h147v-147zM367 746h-147v147h147v-147z" />
<glyph glyph-name="Egrave" unicode="&#xc8;" horiz-adv-x="863"
d="M788 0h-692v728h692v-147h-545v-144h545v-147h-545v-143h545v-147zM585 746h-136l-193 192v64h136q63 -64 193 -193v-63z" />
<glyph glyph-name="Iacute" unicode="&#xcd;" horiz-adv-x="645"
d="M570 0h-437v147h145v434h-145v147h437v-147h-145v-434h145v-147zM512 938l-192 -192h-136v63l192 193h136v-64z" />
<glyph glyph-name="Icircumflex" unicode="&#xce;" horiz-adv-x="645"
d="M570 0h-437v147h145v434h-145v147h437v-147h-145v-434h145v-147zM512 746h-99l-65 64l-65 -64h-99v99l120 120h88l120 -120v-99z" />
<glyph glyph-name="Idieresis" unicode="&#xcf;" horiz-adv-x="645"
d="M570 0h-437v147h145v434h-145v147h437v-147h-145v-434h145v-147zM549 746h-147v147h147v-147zM295 746h-147v147h147v-147z" />
<glyph glyph-name="Igrave" unicode="&#xcc;" horiz-adv-x="645"
d="M570 0h-437v147h145v434h-145v147h437v-147h-145v-434h145v-147zM440 746h-136l-193 192v64h136q63 -64 193 -193v-63z" />
<glyph glyph-name="Oacute" unicode="&#xd3;" horiz-adv-x="935"
d="M860 120l-120 -120h-524l-120 120v488l120 120h524l120 -120v-488zM713 147v434h-470v-434h470zM657 938l-192 -192h-136v63l193 193h135v-64z" />
<glyph glyph-name="Ocircumflex" unicode="&#xd4;" horiz-adv-x="935"
d="M860 120l-120 -120h-524l-120 120v488l120 120h524l120 -120v-488zM713 147v434h-470v-434h470zM621 746h-99l-65 64l-65 -64h-99v99l120 120h88l120 -120v-99z" />
<glyph glyph-name="apple" unicode="&#xf000;" horiz-adv-x="716"
d="M641 655l-14 -41l12 8l-10 -33l1 1v-64l-57 -74l-27 26l82 63v-58l-17 -23l-17 -23l-25 28q28 16 30 17v0l31 18l-8 -46l17 9l-28 -61l21 8l-30 -61l-5 -28l20 2l-40 -40l40 -6l-65 -28l15 -7l-47 -21l13 -15h-71l-9 3l-14 -14l-8 32l61 -21l-31 -19l11 -8h3l4 2l7 17
l27 14l21 -22l15 -29l-1 -12l3 -19l-11 -12l-3 -13l-31 -43l-10 29l-12 12l-9 -7h-3l-1 -9l10 -21l-57 14l-16 15l-7 24l14 23l-4 3l-21 11l-17 17l32 19l13 -37l8 -27l-15 -17l5 -22l-3 -83l-29 27l-26 -64l-26 64l-29 -27l-3 83l5 22l-15 17l2 7l-6 -5l-5 -8l-12 29l31 -6
l15 -26l-7 -24l-15 -15l-57 -14l10 21l-2 9h-3l-16 11l-4 18l15 -10l5 4l-5 -12l-15 -16l-11 -29l-31 43l-3 13l-11 12l4 19l-2 12l15 29l22 22l26 -14l7 -17l4 -2h4l10 8l-31 19l32 11l-5 4l29 10l7 21l25 -24l-58 -22h-71l13 15l-47 21l15 7l-65 28l40 6l-40 40l20 -2
l-5 28l-30 61l21 -8l-27 61l16 -9l-7 46l30 -18v0l30 -17l-25 -28l-17 23l-17 23v41l-2 2v64l1 -1l-10 33l12 -8l-14 41l73 -57l38 -41l1 16l113 -145l26 -68l-21 -15l-1 -5q-6 0 -11 2l-9 -9l15 34l2 13l-5 15l13 19h3l1 5l20 -22l-15 -1l-14 11l-8 19l-6 9l-17 17l4 4
q-4 7 -32 103l65 -73l15 -10l16 -14l8 -25l-38 -7l-1 16l-5 5l-26 -2l10 37l16 22l24 3l9 -2l4 2l20 -9l20 -18l14 -6l36 7l-8 -53l-7 -6l5 -10l6 -48h-4l1 -5l5 -14l14 -9l-12 -24l-9 -7l-6 24l-2 -6l-4 18l-21 15l26 68l113 145l1 -16l39 41z" />
<glyph glyph-name="Ograve" unicode="&#xd2;" horiz-adv-x="935"
d="M860 120l-120 -120h-524l-120 120v488l120 120h524l120 -120v-488zM713 147v434h-470v-434h470zM549 746h-136l-193 192v64h136q63 -64 193 -193v-63z" />
<glyph glyph-name="Uacute" unicode="&#xda;" horiz-adv-x="935"
d="M860 120l-120 -120h-524l-120 120v608h147v-581h470v581h147v-608zM657 938l-192 -192h-136v63l193 193h135v-64z" />
<glyph glyph-name="Ucircumflex" unicode="&#xdb;" horiz-adv-x="935"
d="M860 120l-120 -120h-524l-120 120v608h147v-581h470v581h147v-608zM621 746h-99l-65 64l-65 -64h-99v99l120 120h88l120 -120v-99z" />
<glyph glyph-name="Ugrave" unicode="&#xd9;" horiz-adv-x="935"
d="M860 120l-120 -120h-524l-120 120v608h147v-581h470v581h147v-608zM549 746h-136l-193 192v64h136q63 -64 193 -193v-63z" />
<glyph glyph-name="dotlessi" unicode="&#x131;" horiz-adv-x="406"
d="M331 0h-136l-120 120v462h147v-435h109v-147z" />
<glyph glyph-name="circumflex" unicode="&#x2c6;" horiz-adv-x="435"
d="M382 601h-100l-64 64l-65 -64h-99v99l120 120h88l120 -120v-99z" />
<glyph glyph-name="tilde" unicode="&#x2dc;" horiz-adv-x="653"
d="M600 721l-120 -120h-125l-119 119l-119 -119h-63v135l120 120h124l119 -119l119 119h64v-135z" />
<glyph glyph-name="macron" unicode="&#xaf;" horiz-adv-x="726"
d="M418 710h-364v146h364v-146z" />
<glyph glyph-name="macron" unicode="&#x2c9;" horiz-adv-x="726"
d="M418 710h-364v146h364v-146z" />
<glyph glyph-name="breve" unicode="&#x2d8;" horiz-adv-x="581"
d="M527 757l-120 -120h-233l-120 120v99h110v-47l25 -25h202l25 25v47h111v-99z" />
<glyph glyph-name="dotaccent" unicode="&#x2d9;" horiz-adv-x="254"
d="M200 746h-146v147h146v-147z" />
<glyph glyph-name="ring" unicode="&#x2da;" horiz-adv-x="871"
d="M455 721l-121 -120h-160l-120 120v124l120 120h160l121 -120v-124zM318 783l-64 64l-64 -64l64 -64z" />
<glyph glyph-name="cedilla" unicode="&#xb8;" horiz-adv-x="871"
d="M418 -150l-84 -84h-197l-47 47v100h110v-36h82l18 17l-65 65v99h99l84 -84v-124z" />
<glyph glyph-name="hungarumlaut" unicode="&#x2dd;" horiz-adv-x="762"
d="M600 938l-193 -192h-136v63l193 193h136v-64zM382 938l-193 -192h-135v63l192 193h136v-64z" />
<glyph glyph-name="ogonek" unicode="&#x2db;" horiz-adv-x="871"
d="M527 -187l-47 -47h-198l-83 84v124l47 48h136v-63l-47 -47l36 -35h45v36h111v-100z" />
<glyph glyph-name="caron" unicode="&#x2c7;" horiz-adv-x="399"
d="M382 866l-120 -120h-88l-120 120v99h99l65 -64l64 64h100v-99z" />
<hkern u1="A" u2="Y" k="100" />
<hkern u1="A" u2="T" k="100" />
<hkern u1="L" u2="Y" k="100" />
<hkern u1="L" u2="T" k="200" />
<hkern u1="T" u2="J" k="200" />
<hkern u1="T" u2="A" k="100" />
<hkern u1="Y" u2="A" k="100" />
<hkern u1="g" u2="j" k="-100" />
<hkern u1="j" u2="j" k="-100" />
<hkern u1="y" u2="j" k="-100" />
</font>
</defs></svg>

After

Width:  |  Height:  |  Size: 44 KiB

11
vue/src/main.js 100644
View File

@ -0,0 +1,11 @@
import {createApp } from "vue";
import App from "./App.vue";
import store from "./store";
// Vue.config.productionTip = false;
let app = createApp(App);
app.use(store);
app.mount("#app");
store.dispatch("check_user");

23
vue/src/store.js 100644
View File

@ -0,0 +1,23 @@
import { createStore } from 'vuex'
export const store = createStore({
state: {
user: null,
},
mutations: {
set_user(state, user) {
state.user = user;
},
},
actions: {
async check_user({ commit }) {
let u = await fetch("/is_authenticated/", { Accept: "application/json" });
let j = await u.json();
if (j.user === null) {
window.location = "/login/?next=/frontend/";
} else {
commit("set_user", j.user);
}
},
},
});

View File

@ -0,0 +1,14 @@
<template>
<div class="container">
<template v-if="$store.state.user">
Benvenuto {{ $store.state.user }}!
</template>
<template v-else>Non sei loggato!</template>
</div>
</template>
<script>
export default {};
</script>

28
vue/vue.config.js 100644
View File

@ -0,0 +1,28 @@
const WebpackObfuscator = require("webpack-obfuscator");
//const BundleAnalyzerPlugin = require("webpack-bundle-analyzer")
// .BundleAnalyzerPlugin;
let plugins = [
//new webpack.IgnorePlugin(/^\.\/locale$/, /moment$/),
//new BundleAnalyzerPlugin(),
];
if (process.env.NODE_ENV === "production") {
plugins.push(
new WebpackObfuscator(
{
rotateStringArray: true,
},
["**/chunk-vendors.*.js"]
)
);
}
module.exports = {
configureWebpack: {
devtool: process.env.NODE_ENV === "production" ? "none" : "eval-source-map",
plugins: plugins,
},
runtimeCompiler: false,
lintOnSave: true,
publicPath: "",
};