Merge remote-tracking branch 'origin/mvp_proof' into devel

This commit is contained in:
nad 2020-03-17 11:54:22 +01:00
commit e437476110
7 changed files with 266 additions and 8 deletions

View File

@ -7,7 +7,8 @@ from teal.config import Config
from teal.enums import Currency from teal.enums import Currency
from teal.utils import import_resource from teal.utils import import_resource
from ereuse_devicehub.resources import action, agent, deliverynote, inventory, lot, tag, user from ereuse_devicehub.resources import action, agent, deliverynote, inventory, \
lot, proof, tag, user
from ereuse_devicehub.resources.device import definitions from ereuse_devicehub.resources.device import definitions
from ereuse_devicehub.resources.documents import documents from ereuse_devicehub.resources.documents import documents
from ereuse_devicehub.resources.enums import PriceSoftware from ereuse_devicehub.resources.enums import PriceSoftware
@ -21,6 +22,7 @@ class DevicehubConfig(Config):
import_resource(agent), import_resource(agent),
import_resource(lot), import_resource(lot),
import_resource(deliverynote), import_resource(deliverynote),
import_resource(proof),
import_resource(documents), import_resource(documents),
import_resource(inventory)), import_resource(inventory)),
) )

View File

@ -4,8 +4,9 @@ from datetime import datetime
from boltons import urlutils from boltons import urlutils
from citext import CIText from citext import CIText
from flask import g from flask import g
from typing import Iterable
from sqlalchemy.types import ARRAY from sqlalchemy.types import ARRAY
from sqlalchemy.dialects.postgresql import UUID from sqlalchemy.dialects.postgresql import UUID, JSONB
from teal.db import CASCADE_OWN, check_range, IntEnum from teal.db import CASCADE_OWN, check_range, IntEnum
from teal.resource import url_for_resource from teal.resource import url_for_resource
@ -41,7 +42,8 @@ class Deliverynote(Thing):
# to SnapshotDelivery entity. # to SnapshotDelivery entity.
# At this stage of implementation they will treated as a # At this stage of implementation they will treated as a
# comma-separated string of the devices expexted/transfered # comma-separated string of the devices expexted/transfered
expected_devices = db.Column(db.ARRAY(db.Integer, dimensions=1), nullable=False) expected_devices = db.Column(JSONB, nullable=False)
# expected_devices = db.Column(db.ARRAY(JSONB, dimensions=1), nullable=False)
transferred_devices = db.Column(db.ARRAY(db.Integer, dimensions=1), nullable=True) transferred_devices = db.Column(db.ARRAY(db.Integer, dimensions=1), nullable=True)
transfer_state = db.Column(IntEnum(TransferState), default=TransferState.Initial, nullable=False) transfer_state = db.Column(IntEnum(TransferState), default=TransferState.Initial, nullable=False)
transfer_state.comment = TransferState.__doc__ transfer_state.comment = TransferState.__doc__
@ -55,8 +57,8 @@ class Deliverynote(Thing):
primaryjoin=Lot.id == lot_id) primaryjoin=Lot.id == lot_id)
def __init__(self, document_id: str, deposit: str, date, def __init__(self, document_id: str, deposit: str, date,
supplier_email: str, supplier_email: str,
expected_devices: str, expected_devices: Iterable,
transfer_state: TransferState) -> None: transfer_state: TransferState) -> None:
"""Initializes a delivery note """Initializes a delivery note
""" """

View File

@ -22,8 +22,7 @@ class Deliverynote(Thing):
date = f.DateTime('iso', required=True) date = f.DateTime('iso', required=True)
deposit = f.Integer(validate=f.validate.Range(min=0, max=100), deposit = f.Integer(validate=f.validate.Range(min=0, max=100),
description=m.Deliverynote.deposit.__doc__) description=m.Deliverynote.deposit.__doc__)
ethereum_address = f.String(description='User identifier address inside the Blockchain', ethereum_address = f.String(description='User identifier address inside the Blockchain')
data_key='ethereumAddress') expected_devices = f.List(f.Dict, required=True, data_key='expectedDevices')
expected_devices = f.List(f.Integer(), required=True, data_key='expectedDevices')
transferred_devices = f.List(f.Integer(), required=False, data_key='transferredDevices') transferred_devices = f.List(f.Integer(), required=False, data_key='transferredDevices')
transfer_state = EnumField(TransferState, description=m.Lot.transfer_state.comment) transfer_state = EnumField(TransferState, description=m.Lot.transfer_state.comment)

View File

@ -0,0 +1,37 @@
from teal.resource import Converters, Resource
from ereuse_devicehub.resources.proof import schemas
from ereuse_devicehub.resources.proof.views import ProofView
class ProofDef(Resource):
SCHEMA = schemas.Proof
VIEW = ProofView
# AUTH = True
AUTH = False
ID_CONVERTER = Converters.uuid
class ProofTransferDef(ProofDef):
VIEW = None
SCHEMA = schemas.ProofTransfer
class ProofDataWipeDef(ProofDef):
VIEW = None
SCHEMA = schemas.ProofDataWipe
class ProofFunction(ProofDef):
VIEW = None
SCHEMA = schemas.ProofFunction
class ProofReuse(ProofDef):
VIEW = None
SCHEMA = schemas.ProofReuse
class ProofRecycling(ProofDef):
VIEW = None
SCHEMA = schemas.ProofRecycling

View File

@ -0,0 +1,119 @@
"""This file contains all proofs related to actions
"""
from collections import Iterable
from datetime import datetime
from typing import Optional, Set, Union
from uuid import uuid4
from boltons import urlutils
from citext import CIText
from flask import current_app as app, g
from sortedcontainers import SortedSet
from sqlalchemy import BigInteger, Column, Enum as DBEnum, \
ForeignKey, Integer, Unicode
from sqlalchemy.dialects.postgresql import UUID
from sqlalchemy.ext.declarative import declared_attr
from sqlalchemy.ext.orderinglist import ordering_list
from sqlalchemy.orm import backref, relationship, validates
from sqlalchemy.util import OrderedSet
from teal.db import CASCADE_OWN, INHERIT_COND, POLYMORPHIC_ID, \
POLYMORPHIC_ON, StrictVersionType, URL
from teal.marshmallow import ValidationError
from teal.resource import url_for_resource
from ereuse_devicehub.db import db
from ereuse_devicehub.resources.action.models import Action, DisposeProduct, \
EraseBasic, Rate, Trade
from ereuse_devicehub.resources.models import Thing
class JoinedTableMixin:
# noinspection PyMethodParameters
@declared_attr
def id(cls):
return Column(UUID(as_uuid=True), ForeignKey(Proof.id), primary_key=True)
class Proof(Thing):
"""Proof over an action.
"""
id = Column(UUID(as_uuid=True), primary_key=True, default=uuid4)
type = Column(Unicode, nullable=False)
ethereum_hashes = Column(CIText(), default='', nullable=False)
@property
def url(self) -> urlutils.URL:
"""The URL where to GET this proof."""
return urlutils.URL(url_for_resource(Proof, item_id=self.id))
# noinspection PyMethodParameters
@declared_attr
def __mapper_args__(cls):
"""Defines inheritance.
From `the guide <http://docs.sqlalchemy.org/en/latest/orm/
extensions/declarative/api.html
#sqlalchemy.ext.declarative.declared_attr>`_
"""
args = {POLYMORPHIC_ID: cls.t}
if cls.t == 'Proof':
args[POLYMORPHIC_ON] = cls.type
# noinspection PyUnresolvedReferences
if JoinedTableMixin in cls.mro():
args[INHERIT_COND] = cls.id == Proof.id
return args
def __init__(self, **kwargs) -> None:
# sortedset forces us to do this before calling our parent init
super().__init__(**kwargs)
def __repr__(self):
return '<{0.t} {0.id} >'.format(self)
class ProofTransfer(JoinedTableMixin, Proof):
transfer_id = Column(UUID, ForeignKey(Trade.id), nullable=False)
transfer = relationship(DisposeProduct,
backref=backref("proof_transfer",
lazy=True,
cascade=CASCADE_OWN),
uselist=False,
primaryjoin=DisposeProduct.id == transfer_id)
class ProofDataWipe(JoinedTableMixin, Proof):
erasure_type = Column(CIText(), default='', nullable=False)
date = Column(db.DateTime, nullable=False, default=datetime.utcnow)
result = Column(db.Boolean, default=False, nullable=False)
result.comment = """Identifies proof datawipe as a result."""
erasure_id = Column(UUID(as_uuid=True), ForeignKey(EraseBasic.id), nullable=False)
erasure = relationship(EraseBasic,
backref=backref('proof_datawipe',
lazy=True,
cascade=CASCADE_OWN),
primaryjoin=EraseBasic.id == erasure_id)
class ProofFunction(JoinedTableMixin, Proof):
disk_usage = Column(db.Integer, default=0)
rate_id = Column(UUID, ForeignKey(Rate.id), nullable=False)
rate = relationship(Rate,
backref=backref('proof_function',
lazy=True,
cascade=CASCADE_OWN),
primaryjoin=Rate.id == rate_id)
class ProofReuse(JoinedTableMixin, Proof):
price = Column(db.Integer)
class ProofRecycling(JoinedTableMixin, Proof):
collection_point = Column(CIText(), default='', nullable=False)
date = db.Column(db.DateTime, nullable=False, default=datetime.utcnow)
contact = Column(CIText(), default='', nullable=False)
ticket = Column(CIText(), default='', nullable=False)
gps_location = Column(CIText(), default='', nullable=False)

View File

@ -0,0 +1,56 @@
from flask import current_app as app
from marshmallow import Schema as MarshmallowSchema, ValidationError, validates_schema
from marshmallow.fields import Boolean, DateTime, Integer, Nested, String, UUID
from marshmallow.validate import Length
from sqlalchemy.util import OrderedSet
from teal.marshmallow import SanitizedStr, URL
from teal.resource import Schema
from ereuse_devicehub.marshmallow import NestedOn
from ereuse_devicehub.resources.proof import models as m
from ereuse_devicehub.resources.models import STR_BIG_SIZE, STR_SIZE
from ereuse_devicehub.resources.schemas import Thing
from ereuse_devicehub.resources.action import schemas as s_action
class Proof(Thing):
__doc__ = m.Proof.__doc__
id = UUID(dump_only=True)
ethereum_hashes = SanitizedStr(default='', validate=Length(max=STR_BIG_SIZE),
data_key="ethereumHashes")
url = URL(dump_only=True, description=m.Proof.url.__doc__)
class ProofTransfer(Proof):
__doc__ = m.ProofTransfer.__doc__
transfer = NestedOn(s_action.DisposeProduct,
required=True,
only_query='id')
class ProofDataWipe(Proof):
__doc__ = m.ProofDataWipe.__doc__
erasure_type = SanitizedStr(default='')
date = DateTime('iso', required=True)
result = Boolean(missing=False)
erasure = NestedOn(s_action.EraseBasic, only_query='id')
class ProofFunction(Proof):
__doc__ = m.ProofFunction.__doc__
disk_usage = Integer()
rate = NestedOn(s_action.Rate, required=True, only_query='id')
class ProofReuse(Proof):
__doc__ = m.ProofReuse.__doc__
price = Integer()
class ProofRecycling(Proof):
__doc__ = m.ProofRecycling.__doc__
collection_point = SanitizedStr(default='')
date = DateTime()
contact = SanitizedStr(default='')
ticket = SanitizedStr(default='')
gps_location = SanitizedStr(default='')

View File

@ -0,0 +1,43 @@
from distutils.version import StrictVersion
from typing import List
from uuid import UUID
from flask import current_app as app, request, jsonify
from sqlalchemy.util import OrderedSet
from teal.marshmallow import ValidationError
from teal.resource import View
from ereuse_devicehub.db import db
from ereuse_devicehub.query import things_response
from ereuse_devicehub.resources.action.models import Action, RateComputer, Snapshot, VisualTest
from ereuse_devicehub.resources.action.rate.v1_0 import CannotRate
from ereuse_devicehub.resources.device.models import Component, Computer
from ereuse_devicehub.resources.enums import SnapshotSoftware
SUPPORTED_WORKBENCH = StrictVersion('11.0')
class ProofView(View):
def post(self):
"""Posts batches of proofs."""
json = request.get_json(validate=False)
if not json:
raise ValidationError('JSON is not correct.')
# todo there should be a way to better get subclassess resource
# defs
proofs = list()
if json['batch']:
for prf in json['proofs']:
resource_def = app.resources[prf['type']]
p = resource_def.schema.load(prf)
Model = db.Model._decl_class_registry.data[prf['type']]()
proof = Model(**p)
db.session.add(proof)
proofs.append(self.schema.dump(proof))
db.session.commit()
response = jsonify({
'items': proofs,
'url': request.path
})
response.status_code = 201
return response