Merge branch 'mvp_deliverynote' into devel

This commit is contained in:
Big Lebowski 2020-04-13 22:45:25 +02:00
commit 315f5b793a
10 changed files with 152 additions and 66 deletions

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

@ -52,22 +52,22 @@ class Device(Thing):
"""
type = Column(Unicode(STR_SM_SIZE), nullable=False)
hid = Column(Unicode(), check_lower('hid'), unique=False)
hid.comment = """The Hardware ID (HID) is the unique ID traceability
hid.comment = """The Hardware ID (HID) is the unique ID traceability
systems use to ID a device globally. This field is auto-generated
from Devicehub using literal identifiers from the device,
so it can re-generated *offline*.
so it can re-generated *offline*.
""" + HID_CONVERSION_DOC
model = Column(Unicode(), check_lower('model'))
model.comment = """The model of the device in lower case.
The model is the unambiguous, as technical as possible, denomination
for the product. This field, among others, is used to identify
for the product. This field, among others, is used to identify
the product.
"""
manufacturer = Column(Unicode(), check_lower('manufacturer'))
manufacturer.comment = """The normalized name of the manufacturer,
in lower case.
Although as of now Devicehub does not enforce normalization,
users can choose a list of normalized manufacturer names
from the own ``/manufacturers`` REST endpoint.
@ -76,7 +76,7 @@ class Device(Thing):
serial_number.comment = """The serial number of the device in lower case."""
brand = db.Column(CIText())
brand.comment = """A naming for consumers. This field can represent
several models, so it can be ambiguous, and it is not used to
several models, so it can be ambiguous, and it is not used to
identify the product.
"""
generation = db.Column(db.SmallInteger, check_range('generation', 0))
@ -94,13 +94,13 @@ class Device(Thing):
color = Column(ColorType)
color.comment = """The predominant color of the device."""
production_date = Column(db.DateTime)
production_date.comment = """The date of production of the device.
production_date.comment = """The date of production of the device.
This is timezone naive, as Workbench cannot report this data with timezone information.
"""
variant = Column(db.CIText())
variant.comment = """A variant or sub-model of the device."""
sku = db.Column(db.CIText())
sku.comment = """The Stock Keeping Unit (SKU), i.e. a
sku.comment = """The Stock Keeping Unit (SKU), i.e. a
merchant-specific identifier for a product or service.
"""
image = db.Column(db.URL)
@ -311,17 +311,17 @@ class DisplayMixin:
size = Column(Float(decimal_return_scale=1), check_range('size', 2, 150), nullable=False)
size.comment = """The size of the monitor in inches."""
technology = Column(DBEnum(DisplayTech))
technology.comment = """The technology the monitor uses to display
technology.comment = """The technology the monitor uses to display
the image.
"""
resolution_width = Column(SmallInteger, check_range('resolution_width', 10, 20000),
nullable=False)
resolution_width.comment = """The maximum horizontal resolution the
resolution_width.comment = """The maximum horizontal resolution the
monitor can natively support in pixels.
"""
resolution_height = Column(SmallInteger, check_range('resolution_height', 10, 20000),
nullable=False)
resolution_height.comment = """The maximum vertical resolution the
resolution_height.comment = """The maximum vertical resolution the
monitor can natively support in pixels.
"""
refresh_rate = Column(SmallInteger, check_range('refresh_rate', 10, 1000))
@ -381,18 +381,19 @@ class Computer(Device):
It is a subset of the Linux definition of DMI / DMI decode.
"""
deposit = Column(Integer, check_range('deposit', min=0, max=100), default=0)
owner_address = db.Column(CIText(),
db.ForeignKey(User.ethereum_address),
ethereum_address = Column(CIText(), unique=True, default=None)
deposit = Column(Integer, check_range('deposit',min=0,max=100), default=0)
owner_id = db.Column(UUID(as_uuid=True),
db.ForeignKey(User.id),
nullable=False,
default=lambda: g.user.ethereum_address)
author = db.relationship(User, primaryjoin=owner_address == User.ethereum_address)
default=lambda: g.user.id)
author = db.relationship(User, primaryjoin=owner_id == User.id)
transfer_state = db.Column(IntEnum(TransferState), default=TransferState.Initial, nullable=False)
transfer_state.comment = TransferState.__doc__
receiver_address = db.Column(CIText(),
db.ForeignKey(User.ethereum_address),
receiver_id = db.Column(UUID(as_uuid=True),
db.ForeignKey(User.id),
nullable=True)
receiver = db.relationship(User, primaryjoin=receiver_address == User.ethereum_address)
receiver = db.relationship(User, primaryjoin=receiver_id == User.id)
deliverynote_address = db.Column(CIText(), nullable=True)
def __init__(self, chassis, **kwargs) -> None:
@ -500,11 +501,11 @@ class Mobile(Device):
id = Column(BigInteger, ForeignKey(Device.id), primary_key=True)
imei = Column(BigInteger)
imei.comment = """The International Mobile Equipment Identity of
imei.comment = """The International Mobile Equipment Identity of
the smartphone as an integer.
"""
meid = Column(Unicode)
meid.comment = """The Mobile Equipment Identifier as a hexadecimal
meid.comment = """The Mobile Equipment Identifier as a hexadecimal
string.
"""
ram_size = db.Column(db.Integer, check_range('ram_size', min=128, max=36000))
@ -640,7 +641,7 @@ class Motherboard(JoinedComponentTableMixin, Component):
class NetworkMixin:
speed = Column(SmallInteger, check_range('speed', min=10, max=10000))
speed.comment = """The maximum speed this network adapter can handle,
speed.comment = """The maximum speed this network adapter can handle,
in mbps.
"""
wireless = Column(Boolean, nullable=False, default=False)
@ -699,7 +700,7 @@ class Battery(JoinedComponentTableMixin, Component):
technology = db.Column(db.Enum(BatteryTechnology))
size = db.Column(db.Integer, nullable=False)
size.comment = """Maximum battery capacity by design, in mAh.
Use BatteryTest's "size" to get the actual size of the battery.
"""

View File

@ -122,12 +122,13 @@ 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')
owner_address = SanitizedStr(validate=f.validate.Length(max=42))
owner_id = UUID(data_key='ownerID')
transfer_state = EnumField(enums.TransferState, description=m.Computer.transfer_state.comment)
receiver_address = SanitizedStr(validate=f.validate.Length(max=42))
receiver_id = UUID(data_key='receiverID')
deliverynote_address = SanitizedStr(validate=f.validate.Length(max=42))

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:
@ -110,7 +127,7 @@ class DeviceView(View):
return self.schema.jsonify(device)
@auth.Auth.requires_auth
@cache(datetime.timedelta(minutes=1))
# @cache(datetime.timedelta(minutes=1))
def find(self, args: dict):
"""Gets many devices."""
# Compute query

View File

@ -33,8 +33,8 @@ class Lot(Thing):
lazy=True,
collection_class=set)
"""The **children** devices that the lot has.
Note that the lot can have more devices, if they are inside
Note that the lot can have more devices, if they are inside
descendant lots.
"""
parents = db.relationship(lambda: Lot,
@ -64,11 +64,11 @@ class Lot(Thing):
descendants.
"""
deposit = db.Column(db.Integer, check_range('deposit', min=0, max=100), default=0)
owner_address = db.Column(CIText(),
db.ForeignKey(User.ethereum_address),
owner_id = db.Column(UUID(as_uuid=True),
db.ForeignKey(User.id),
nullable=False,
default=lambda: g.user.ethereum_address)
owner = db.relationship(User, primaryjoin=owner_address == User.ethereum_address)
default=lambda: g.user.id)
owner = db.relationship(User, primaryjoin=owner_id == User.id)
transfer_state = db.Column(IntEnum(TransferState), default=TransferState.Initial, nullable=False)
transfer_state.comment = TransferState.__doc__
receiver_address = db.Column(CIText(),

View File

@ -22,7 +22,7 @@ class Lot(Thing):
deposit = f.Integer(validate=f.validate.Range(min=0, max=100),
description=m.Lot.deposit.__doc__)
# author_id = NestedOn(s_user.User,only_query='author_id')
owner_address = SanitizedStr(validate=f.validate.Length(max=42))
owner_id = f.UUID(data_key='ownerID')
transfer_state = EnumField(TransferState, description=m.Lot.transfer_state.comment)
receiver_address = SanitizedStr(validate=f.validate.Length(max=42))
deliverynote = NestedOn(s_deliverynote.Deliverynote, dump_only=True)

View File

@ -59,7 +59,7 @@ class LotView(View):
lot = Lot.query.filter_by(id=id).one() # type: Lot
return self.schema.jsonify(lot, nested=2)
@teal.cache.cache(datetime.timedelta(minutes=5))
# @teal.cache.cache(datetime.timedelta(minutes=5))
def find(self, args: dict):
"""Gets lots.

View File

@ -26,7 +26,9 @@ 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:
@ -42,7 +44,14 @@ class Proof(Thing):
"""
id = Column(UUID(as_uuid=True), primary_key=True, default=uuid4)
type = Column(Unicode, nullable=False)
ethereum_hashes = Column(CIText(), default='', 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:
@ -74,46 +83,76 @@ class Proof(Thing):
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)
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)
# 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)
rate_id = Column(UUID, ForeignKey(Rate.id), nullable=False)
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 = db.Column(db.DateTime, nullable=False, default=datetime.utcnow)
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

@ -1,5 +1,5 @@
from flask import current_app as app
from marshmallow import Schema as MarshmallowSchema, ValidationError, validates_schema
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
@ -11,46 +11,62 @@ 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_hashes = SanitizedStr(default='', validate=Length(max=STR_BIG_SIZE),
data_key="ethereumHashes")
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__
transfer = NestedOn(s_action.DisposeProduct,
required=True,
only_query='id')
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 = SanitizedStr(default='')
# erasure_type = String(default='', data_key='erasureType')
date = DateTime('iso', required=True)
result = Boolean(missing=False)
erasure = NestedOn(s_action.EraseBasic, only_query='id')
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()
rate = NestedOn(s_action.Rate, required=True, only_query='id')
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__
price = Integer()
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='')
date = DateTime()
contact = SanitizedStr(default='')
ticket = SanitizedStr(default='')
gps_location = SanitizedStr(default='')
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

@ -33,7 +33,8 @@ class ProofView(View):
Model = db.Model._decl_class_registry.data[prf['type']]()
proof = Model(**p)
db.session.add(proof)
proofs.append(self.schema.dump(proof))
proofs.append(resource_def.schema.dump(proof))
db.session().final_flush()
db.session.commit()
response = jsonify({
'items': proofs,