Merge branch 'mvp_proof' into mvp_deliverynote

This commit is contained in:
yiorgos marinellis 2020-04-06 13:07:11 +02:00
commit fea6f23c0c
9 changed files with 348 additions and 5 deletions

View File

@ -7,7 +7,8 @@ from teal.config import Config
from teal.enums import Currency
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.documents import documents
from ereuse_devicehub.resources.enums import PriceSoftware
@ -21,6 +22,7 @@ class DevicehubConfig(Config):
import_resource(agent),
import_resource(lot),
import_resource(deliverynote),
import_resource(proof),
import_resource(documents),
import_resource(inventory)),
)

View File

@ -16,6 +16,7 @@ from ereuse_devicehub.db import db
from ereuse_devicehub.query import things_response
from ereuse_devicehub.resources.deliverynote.models import Deliverynote
from ereuse_devicehub.resources.lot.models import Lot
from ereuse_devicehub.resources.device.models import Computer
class DeliverynoteView(View):
@ -41,8 +42,18 @@ class DeliverynoteView(View):
'ethereum_address'), partial=True)
d = request.get_json(schema=patch_schema)
dlvnote = Deliverynote.query.filter_by(id=id).one()
# device_fields = ['transfer_state', 'deliverynote_address']
# computers = [x for x in dlvnote.transferred_devices if isinstance(x, Computer)]
for key, value in d.items():
setattr(dlvnote, key, value)
# Transalate ethereum_address attribute
# devKey = key
# if key == 'ethereum_address':
# devKey = 'deliverynote_address'
# if devKey in device_fields:
# for dev in computers:
# setattr(dev, devKey, value)
db.session.commit()
return Response(status=204)

View File

@ -382,6 +382,7 @@ class Computer(Device):
It is a subset of the Linux definition of DMI / DMI decode.
"""
ethereum_address = Column(CIText(), unique=True, default=None)
deposit = Column(Integer, check_range('deposit',min=0,max=100), default=0)
owner_address = db.Column(CIText(),
db.ForeignKey(User.ethereum_address),

View File

@ -122,6 +122,7 @@ class Computer(Device):
dump_only=True,
collection_class=set,
description=m.Computer.privacy.__doc__)
ethereum_address = SanitizedStr(validate=f.validate.Length(max=42))
deposit = Integer(validate=f.validate.Range(min=0, max=100),
description=m.Computer.deposit.__doc__)
# author_id = NestedOn(s_user.User,only_query='author_id')

View File

@ -1,10 +1,10 @@
import datetime
import marshmallow
from flask import current_app as app, render_template, request
from flask import current_app as app, render_template, request, Response
from flask.json import jsonify
from flask_sqlalchemy import Pagination
from marshmallow import fields, fields as f, validate as v
from marshmallow import fields, fields as f, validate as v, ValidationError
from teal import query
from teal.cache import cache
from teal.resource import View
@ -15,7 +15,7 @@ from ereuse_devicehub.query import SearchQueryParser, things_response
from ereuse_devicehub.resources import search
from ereuse_devicehub.resources.action import models as actions
from ereuse_devicehub.resources.device import states
from ereuse_devicehub.resources.device.models import Device, Manufacturer
from ereuse_devicehub.resources.device.models import Device, Manufacturer, Computer
from ereuse_devicehub.resources.device.search import DeviceSearch
from ereuse_devicehub.resources.lot.models import LotDeviceDescendants
from ereuse_devicehub.resources.tag.model import Tag
@ -93,6 +93,23 @@ class DeviceView(View):
"""
return super().get(id)
def patch(self, id):
dev = Device.query.filter_by(id=id).one()
if isinstance(dev, Computer):
resource_def = app.resources['Computer']
# TODO check how to handle the 'actions_one'
patch_schema = resource_def.SCHEMA(only=['ethereum_address', 'transfer_state', 'deliverynote_address', 'actions_one'], partial=True)
json = request.get_json(schema=patch_schema)
# TODO check how to handle the 'actions_one'
json.pop('actions_one')
if not dev:
raise ValueError('Device non existent')
for key, value in json.items():
setattr(dev,key,value)
db.session.commit()
return Response(status=204)
raise ValueError('Cannot patch a non computer')
def one(self, id: int):
"""Gets one device."""
if not request.authorization:

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,158 @@
"""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.device.models import Device
from ereuse_devicehub.resources.models import Thing
from ereuse_devicehub.resources.user import User
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_hash = Column(CIText(), default='', nullable=False)
device_id = db.Column(BigInteger,
db.ForeignKey(Device.id),
nullable=False)
device = db.relationship(Device,
backref=db.backref('proofs_device', uselist=True, lazy=True),
lazy=True,
primaryjoin=Device.id == device_id)
@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):
supplier_id = db.Column(UUID(as_uuid=True),
db.ForeignKey(User.id),
nullable=False,
default=lambda: g.user.id)
supplier = db.relationship(User, primaryjoin=lambda: ProofTransfer.supplier_id == User.id)
receiver_id = db.Column(UUID(as_uuid=True),
db.ForeignKey(User.id),
nullable=False)
receiver = db.relationship(User, primaryjoin=lambda: ProofTransfer.receiver_id == User.id)
deposit = Column(db.Integer, default=0)
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."""
proof_author_id = Column(UUID(as_uuid=True),
db.ForeignKey(User.id),
nullable=False,
default=lambda: g.user.id)
proof_author = relationship(User, primaryjoin=lambda: ProofDataWipe.proof_author_id == User.id)
erasure_id = Column(UUID(as_uuid=True), ForeignKey(EraseBasic.id), nullable=False)
erasure = relationship(EraseBasic,
backref=backref('proof_datawipe',
lazy=True,
uselist=False,
cascade=CASCADE_OWN),
primaryjoin=EraseBasic.id == erasure_id)
class ProofFunction(JoinedTableMixin, Proof):
disk_usage = Column(db.Integer, default=0)
proof_author_id = Column(UUID(as_uuid=True),
db.ForeignKey(User.id),
nullable=False,
default=lambda: g.user.id)
proof_author = db.relationship(User, primaryjoin=lambda: ProofFunction.proof_author_id == User.id)
rate_id = Column(UUID(as_uuid=True), ForeignKey(Rate.id), nullable=False)
rate = relationship(Rate,
backref=backref('proof_function',
lazy=True,
uselist=False,
cascade=CASCADE_OWN),
primaryjoin=Rate.id == rate_id)
class ProofReuse(JoinedTableMixin, Proof):
receiver_segment = Column(CIText(), default='', nullable=False)
id_receipt = Column(CIText(), default='', nullable=False)
supplier_id = db.Column(UUID(as_uuid=True),
db.ForeignKey(User.id),
# nullable=False,
# default=lambda: g.user.id)
nullable=True)
supplier = db.relationship(User, primaryjoin=lambda: ProofReuse.supplier_id == User.id)
receiver_id = db.Column(UUID(as_uuid=True),
db.ForeignKey(User.id),
# nullable=False)
nullable=True)
receiver = db.relationship(User, primaryjoin=lambda: ProofReuse.receiver_id == User.id)
price = Column(db.Integer)
class ProofRecycling(JoinedTableMixin, Proof):
collection_point = Column(CIText(), default='', nullable=False)
date = 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)
recycler_code = Column(CIText(), default='', nullable=False)

View File

@ -0,0 +1,72 @@
from flask import current_app as app
from marshmallow import Schema as MarshmallowSchema, ValidationError, fields as f, 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
from ereuse_devicehub.resources.device import schemas as s_device
from ereuse_devicehub.resources.user import schemas as s_user
class Proof(Thing):
__doc__ = m.Proof.__doc__
id = UUID(dump_only=True)
ethereum_hash = SanitizedStr(default='', validate=Length(max=STR_BIG_SIZE),
data_key="ethereumHash", required=True)
url = URL(dump_only=True, description=m.Proof.url.__doc__)
device_id = Integer(load_only=True, data_key='deviceID')
device = NestedOn(s_device.Device, dump_only=True)
class ProofTransfer(Proof):
__doc__ = m.ProofTransfer.__doc__
deposit = Integer(validate=f.validate.Range(min=0, max=100))
supplier_id = UUID(load_only=True, required=True, data_key='supplierID')
receiver_id = UUID(load_only=True, required=True, data_key='receiverID')
class ProofDataWipe(Proof):
__doc__ = m.ProofDataWipe.__doc__
# erasure_type = String(default='', data_key='erasureType')
date = DateTime('iso', required=True)
result = Boolean(required=True)
proof_author_id = SanitizedStr(validate=f.validate.Length(max=STR_SIZE),
load_only=True, required=True, data_key='proofAuthorID')
proof_author = NestedOn(s_user.User, dump_only=True)
erasure = NestedOn(s_action.EraseBasic, only_query='id', data_key='erasureID')
class ProofFunction(Proof):
__doc__ = m.ProofFunction.__doc__
disk_usage = Integer(validate=f.validate.Range(min=0, max=100), data_key='diskUsage')
proof_author_id = SanitizedStr(validate=f.validate.Length(max=STR_SIZE),
load_only=True, required=True, data_key='proofAuthorID')
proof_author = NestedOn(s_user.User, dump_only=True)
rate = NestedOn(s_action.Rate, required=True,
only_query='id', data_key='rateID')
class ProofReuse(Proof):
__doc__ = m.ProofReuse.__doc__
receiver_segment = String(default='', data_key='receiverSegment', required=True)
id_receipt = String(default='', data_key='idReceipt', required=True)
supplier_id = UUID(load_only=True, required=False, data_key='supplierID')
receiver_id = UUID(load_only=True, required=False, data_key='receiverID')
price = Integer(required=True)
class ProofRecycling(Proof):
__doc__ = m.ProofRecycling.__doc__
collection_point = SanitizedStr(default='', data_key='collectionPoint', required=True)
date = DateTime('iso', required=True)
contact = SanitizedStr(default='', required=True)
ticket = SanitizedStr(default='', required=True)
gps_location = SanitizedStr(default='', data_key='gpsLocation', required=True)
recycler_code = SanitizedStr(default='', data_key='recyclerCode', required=True)

View File

@ -0,0 +1,44 @@
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(resource_def.schema.dump(proof))
db.session().final_flush()
db.session.commit()
response = jsonify({
'items': proofs,
'url': request.path
})
response.status_code = 201
return response