From 8dd926de80ae454cf1f94bc849cccd3f846a3ae8 Mon Sep 17 00:00:00 2001 From: Cayo Puigdefabregas Date: Thu, 19 Jan 2023 12:26:25 +0100 Subject: [PATCH] add dpp --- ereuse_devicehub/config.py | 2 +- .../8334535d56fa_add_digital_passport_dpp.py | 86 +++++++++++++ ereuse_devicehub/resources/did/did.py | 63 ++++++---- ereuse_devicehub/resources/did/models.py | 115 ++++++++++-------- 4 files changed, 193 insertions(+), 73 deletions(-) create mode 100644 ereuse_devicehub/migrations/versions/8334535d56fa_add_digital_passport_dpp.py diff --git a/ereuse_devicehub/config.py b/ereuse_devicehub/config.py index 86e6dbf0..03908686 100644 --- a/ereuse_devicehub/config.py +++ b/ereuse_devicehub/config.py @@ -17,8 +17,8 @@ from ereuse_devicehub.resources import ( user, ) from ereuse_devicehub.resources.device import definitions -from ereuse_devicehub.resources.documents import documents from ereuse_devicehub.resources.did import did +from ereuse_devicehub.resources.documents import documents from ereuse_devicehub.resources.enums import PriceSoftware from ereuse_devicehub.resources.licences import licences from ereuse_devicehub.resources.metric import definitions as metric_def diff --git a/ereuse_devicehub/migrations/versions/8334535d56fa_add_digital_passport_dpp.py b/ereuse_devicehub/migrations/versions/8334535d56fa_add_digital_passport_dpp.py new file mode 100644 index 00000000..1dd7baf1 --- /dev/null +++ b/ereuse_devicehub/migrations/versions/8334535d56fa_add_digital_passport_dpp.py @@ -0,0 +1,86 @@ +"""add digital passport dpp + +Revision ID: 8334535d56fa +Revises: 4b7f77f121bf +Create Date: 2023-01-19 12:01:54.102326 + +""" +from alembic import op, context +import sqlalchemy as sa +import citext +from sqlalchemy.dialects import postgresql + + +# revision identifiers, used by Alembic. +revision = '8334535d56fa' +down_revision = '4b7f77f121bf' +branch_labels = None +depends_on = None + + +def get_inv(): + INV = context.get_x_argument(as_dictionary=True).get('inventory') + if not INV: + raise ValueError("Inventory value is not specified") + return INV + + +def upgrade(): + op.create_table('proof', + sa.Column('updated', sa.TIMESTAMP(timezone=True), server_default=sa.text('CURRENT_TIMESTAMP'), + nullable=False, + comment='The last time Devicehub recorded a change for \n this thing.\n '), + sa.Column('created', sa.TIMESTAMP(timezone=True), server_default=sa.text('CURRENT_TIMESTAMP'), + nullable=False, comment='When Devicehub created this.'), + sa.Column('id', sa.BigInteger(), nullable=False), + sa.Column('type', sa.Unicode(), nullable=False), + sa.Column('documentId', citext.CIText(), nullable=True), + sa.Column('documentSignature', citext.CIText(), nullable=True), + sa.Column('timestamp', sa.BigInteger(), nullable=False), + sa.Column('device_id', sa.BigInteger(), nullable=False), + sa.Column('snapshot_id', postgresql.UUID(as_uuid=True), nullable=False), + sa.Column('issuer_id', postgresql.UUID(as_uuid=True), nullable=False), + sa.ForeignKeyConstraint(['snapshot_id'], [f'{get_inv()}.snapshot.id'], ), + sa.ForeignKeyConstraint(['device_id'], [f'{get_inv()}.device.id'], ), + sa.ForeignKeyConstraint(['issuer_id'], [f'common.user.id'], ), + sa.PrimaryKeyConstraint('id'), + schema=f'{get_inv()}' + ) + # op.create_index(op.f('ix_proof_created'), 'proof', ['created'], unique=False, schema=f'{get_inv()}') + # op.create_index(op.f('ix_proof_timestamp'), 'proof', ['timestamp'], unique=False, schema=f'{get_inv()}') + op.add_column('device', sa.Column('chid_dpp', citext.CIText(), nullable=True), schema=f'{get_inv()}') + op.add_column('snapshot', sa.Column('phid_dpp', citext.CIText(), nullable=True), schema=f'{get_inv()}') + op.add_column('snapshot', sa.Column('json_wb', citext.CIText(), nullable=True), schema=f'{get_inv()}') + op.add_column('snapshot', sa.Column('json_hw', citext.CIText(), nullable=True), schema=f'{get_inv()}') + + op.create_table('dpp', + sa.Column('updated', sa.TIMESTAMP(timezone=True), server_default=sa.text('CURRENT_TIMESTAMP'), + nullable=False, + comment='The last time Devicehub recorded a change for \n this thing.\n '), + sa.Column('created', sa.TIMESTAMP(timezone=True), server_default=sa.text('CURRENT_TIMESTAMP'), + nullable=False, comment='When Devicehub created this.'), + sa.Column('id', sa.BigInteger(), nullable=False), + sa.Column('documentId', citext.CIText(), nullable=True), + sa.Column('documentSignature', citext.CIText(), nullable=True), + sa.Column('timestamp', sa.BigInteger(), nullable=False), + sa.Column('device_id', sa.BigInteger(), nullable=False), + sa.Column('snapshot_id', postgresql.UUID(as_uuid=True), nullable=False), + sa.Column('issuer_id', postgresql.UUID(as_uuid=True), nullable=False), + sa.ForeignKeyConstraint(['snapshot_id'], [f'{get_inv()}.snapshot.id'], ), + sa.ForeignKeyConstraint(['device_id'], [f'{get_inv()}.device.id'], ), + sa.ForeignKeyConstraint(['issuer_id'], [f'common.user.id'], ), + sa.Column('key', sa.Unicode(), nullable=False), + sa.PrimaryKeyConstraint('id'), + schema=f'{get_inv()}' + ) + + +def downgrade(): + op.drop_table('dpp', schema=f'{get_inv()}') + op.drop_table('proof', schema=f'{get_inv()}') + # op.drop_index(op.f('ix_proof_created'), table_name='proof', schema=f'{get_inv()}') + # op.drop_index(op.f('ix_proof_timestamp'), table_name='proof', schema=f'{get_inv()}') + op.drop_column('device', 'chid_dpp', schema=f'{get_inv()}') + op.drop_column('snapshot', 'phid_dpp', schema=f'{get_inv()}') + op.drop_column('snapshot', 'json_wb', schema=f'{get_inv()}') + op.drop_column('snapshot', 'json_hw', schema=f'{get_inv()}') diff --git a/ereuse_devicehub/resources/did/did.py b/ereuse_devicehub/resources/did/did.py index fd066171..75f0a5d8 100644 --- a/ereuse_devicehub/resources/did/did.py +++ b/ereuse_devicehub/resources/did/did.py @@ -1,9 +1,9 @@ import csv -import json -import enum -import uuid -import time import datetime +import enum +import json +import time +import uuid from collections import OrderedDict from io import StringIO from typing import Callable, Iterable, Tuple @@ -13,8 +13,8 @@ import flask import flask_weasyprint import teal.marshmallow from boltons import urlutils -from flask import make_response, g, request from flask import current_app as app +from flask import g, make_response, request from flask.json import jsonify from teal.cache import cache from teal.resource import Resource, View @@ -30,6 +30,7 @@ class DidView(View): This view render one public ans static page for see the links for to do the check of one csv file """ + def get_url_path(self): url = urlutils.URL(request.url) url.normalize() @@ -47,8 +48,11 @@ class DidView(View): if 'json' not in request.headers['Accept']: result = self.get_result(result, template) - return flask.render_template(template, rq_url=self.get_url_path(), - result={"dpp": dpp, "result": result}) + return flask.render_template( + template, + rq_url=self.get_url_path(), + result={"dpp": dpp, "result": result}, + ) return jsonify(self.get_result(result, template)) @@ -77,7 +81,9 @@ class DidView(View): return {'data': dpps} def get_last_dpp(self, dpp): - dpps = [act.dpp[0] for act in dpp.device.actions if act.t == 'Snapshot' and act.dpp] + dpps = [ + act.dpp[0] for act in dpp.device.actions if act.t == 'Snapshot' and act.dpp + ] last_dpp = '' for d in dpps: if d.key == dpp.key: @@ -93,18 +99,31 @@ class DidDef(Resource): VIEW = None # We do not want to create default / documents endpoint AUTH = False - def __init__(self, app, - import_name=__name__, - static_folder='static', - static_url_path=None, - template_folder='templates', - url_prefix=None, - subdomain=None, - url_defaults=None, - root_path=None, - cli_commands: Iterable[Tuple[Callable, str or None]] = tuple()): - super().__init__(app, import_name, static_folder, static_url_path, template_folder, - url_prefix, subdomain, url_defaults, root_path, cli_commands) + def __init__( + self, + app, + import_name=__name__, + static_folder='static', + static_url_path=None, + template_folder='templates', + url_prefix=None, + subdomain=None, + url_defaults=None, + root_path=None, + cli_commands: Iterable[Tuple[Callable, str or None]] = tuple(), + ): + super().__init__( + app, + import_name, + static_folder, + static_url_path, + template_folder, + url_prefix, + subdomain, + url_defaults, + root_path, + cli_commands, + ) view = DidView.as_view('main', definition=self, auth=app.auth) @@ -112,4 +131,6 @@ class DidDef(Resource): # view = app.auth.requires_auth(view) did_view = DidView.as_view('DidView', definition=self, auth=app.auth) - self.add_url_rule('/', defaults={}, view_func=did_view, methods={'GET'}) + self.add_url_rule( + '/', defaults={}, view_func=did_view, methods={'GET'} + ) diff --git a/ereuse_devicehub/resources/did/models.py b/ereuse_devicehub/resources/did/models.py index 03c84e21..d1b15377 100644 --- a/ereuse_devicehub/resources/did/models.py +++ b/ereuse_devicehub/resources/did/models.py @@ -1,17 +1,15 @@ -from sortedcontainers import SortedSet -from sqlalchemy.util import OrderedSet -from sqlalchemy import Unicode, BigInteger, Column, Sequence, ForeignKey -from sqlalchemy.orm import backref, relationship -from sqlalchemy.dialects.postgresql import UUID from flask import g +from sortedcontainers import SortedSet +from sqlalchemy import BigInteger, Column, ForeignKey, Sequence, Unicode +from sqlalchemy.dialects.postgresql import UUID +from sqlalchemy.orm import backref, relationship +from sqlalchemy.util import OrderedSet from teal.db import CASCADE_OWN -from ereuse_devicehub.resources.user.models import User + +from ereuse_devicehub.resources.action.models import ActionStatus, Snapshot from ereuse_devicehub.resources.device.models import Device -from ereuse_devicehub.resources.action.models import Snapshot, ActionStatus - - -from ereuse_devicehub.resources.models import Thing, STR_SM_SIZE - +from ereuse_devicehub.resources.models import STR_SM_SIZE, Thing +from ereuse_devicehub.resources.user.models import User PROOF_ENUM = { 'Register': 'Register', @@ -19,10 +17,7 @@ PROOF_ENUM = { 'proof_of_recycling': 'proof_of_recycling', } -_sorted_proofs = { - 'order_by': lambda: Proof.created, - 'collection_class': SortedSet -} +_sorted_proofs = {'order_by': lambda: Proof.created, 'collection_class': SortedSet} class Proof(Thing): @@ -36,31 +31,42 @@ class Proof(Thing): timestamp = Column(BigInteger, nullable=False) type = Column(Unicode(STR_SM_SIZE), nullable=False) - issuer_id = Column(UUID(as_uuid=True), - ForeignKey(User.id), - nullable=False, - default=lambda: g.user.id) - issuer = relationship(User, - backref=backref('issuered_proofs', lazy=True, collection_class=set), - primaryjoin=User.id == issuer_id) + issuer_id = Column( + UUID(as_uuid=True), + ForeignKey(User.id), + nullable=False, + default=lambda: g.user.id, + ) + issuer = relationship( + User, + backref=backref('issuered_proofs', lazy=True, collection_class=set), + primaryjoin=User.id == issuer_id, + ) issuer_id.comment = """The user that recorded this proof in the system.""" device_id = Column(BigInteger, ForeignKey(Device.id), nullable=False) - device = relationship(Device, - backref=backref('proofs', - lazy=True, - cascade=CASCADE_OWN), - primaryjoin=Device.id == device_id) + device = relationship( + Device, + backref=backref('proofs', lazy=True, cascade=CASCADE_OWN), + primaryjoin=Device.id == device_id, + ) snapshot_id = Column(UUID(as_uuid=True), ForeignKey(Snapshot.id), nullable=True) - snapshot = relationship(Snapshot, - backref=backref('proofs', lazy=True), - collection_class=OrderedSet, - primaryjoin=Snapshot.id == snapshot_id) + snapshot = relationship( + Snapshot, + backref=backref('proofs', lazy=True), + collection_class=OrderedSet, + primaryjoin=Snapshot.id == snapshot_id, + ) + + action_status_id = Column( + UUID(as_uuid=True), ForeignKey(ActionStatus.id), nullable=True + ) + action_status = relationship( + ActionStatus, + backref=backref('proofs', lazy=True), + primaryjoin=ActionStatus.id == action_status_id, + ) - action_status_id = Column(UUID(as_uuid=True), ForeignKey(ActionStatus.id), nullable=True) - action_status = relationship(ActionStatus, - backref=backref('proofs', lazy=True), - primaryjoin=ActionStatus.id == action_status_id) class Dpp(Thing): """ @@ -69,6 +75,7 @@ class Dpp(Thing): Is the official Digital Passport """ + id = Column(BigInteger, Sequence('device_seq'), primary_key=True) key = Column(Unicode(STR_SM_SIZE), nullable=False) key.comment = "chid:phid, (chid it's in device and phid it's in the snapshot)" @@ -78,23 +85,29 @@ class Dpp(Thing): documentSignature.comment = "is the snapshot.json_wb with the signature of the user" timestamp = Column(BigInteger, nullable=False) - issuer_id = Column(UUID(as_uuid=True), - ForeignKey(User.id), - nullable=False, - default=lambda: g.user.id) - issuer = relationship(User, - backref=backref('issuered_dpp', lazy=True, collection_class=set), - primaryjoin=User.id == issuer_id) + issuer_id = Column( + UUID(as_uuid=True), + ForeignKey(User.id), + nullable=False, + default=lambda: g.user.id, + ) + issuer = relationship( + User, + backref=backref('issuered_dpp', lazy=True, collection_class=set), + primaryjoin=User.id == issuer_id, + ) issuer_id.comment = """The user that recorded this proof in the system.""" device_id = Column(BigInteger, ForeignKey(Device.id), nullable=False) - device = relationship(Device, - backref=backref('dpps', - lazy=True, - cascade=CASCADE_OWN), - primaryjoin=Device.id == device_id) + device = relationship( + Device, + backref=backref('dpps', lazy=True, cascade=CASCADE_OWN), + primaryjoin=Device.id == device_id, + ) snapshot_id = Column(UUID(as_uuid=True), ForeignKey(Snapshot.id), nullable=False) - snapshot = relationship(Snapshot, - backref=backref('dpp', lazy=True), - collection_class=OrderedSet, - primaryjoin=Snapshot.id == snapshot_id) + snapshot = relationship( + Snapshot, + backref=backref('dpp', lazy=True), + collection_class=OrderedSet, + primaryjoin=Snapshot.id == snapshot_id, + )