Add base proof models

This commit is contained in:
Big Lebowski 2020-03-12 00:40:41 +01:00
parent 2aa527c0c3
commit 994dce4349
5 changed files with 911 additions and 0 deletions

View File

@ -21,6 +21,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

@ -0,0 +1,279 @@
from typing import Callable, Iterable, Tuple
from teal.resource import Converters, Resource
from ereuse_devicehub.resources.action import schemas
from ereuse_devicehub.resources.action.views import ActionView
from ereuse_devicehub.resources.device.sync import Sync
class ProofDef(Resource):
SCHEMA = schemas.Proof
VIEW = ProofView
AUTH = True
ID_CONVERTER = Converters.uuid
class ActionDef(Resource):
SCHEMA = schemas.Action
VIEW = ActionView
AUTH = True
ID_CONVERTER = Converters.uuid
class AddDef(ActionDef):
VIEW = None
SCHEMA = schemas.Add
class RemoveDef(ActionDef):
VIEW = None
SCHEMA = schemas.Remove
class EraseBasicDef(ActionDef):
VIEW = None
SCHEMA = schemas.EraseBasic
class EraseSectorsDef(EraseBasicDef):
VIEW = None
SCHEMA = schemas.EraseSectors
class ErasePhysicalDef(EraseBasicDef):
VIEW = None
SCHEMA = schemas.ErasePhysical
class StepDef(Resource):
VIEW = None
SCHEMA = schemas.Step
class StepZeroDef(StepDef):
VIEW = None
SCHEMA = schemas.StepZero
class StepRandomDef(StepDef):
VIEW = None
SCHEMA = schemas.StepRandom
class BenchmarkDef(ActionDef):
VIEW = None
SCHEMA = schemas.Benchmark
class BenchmarkDataStorageDef(BenchmarkDef):
VIEW = None
SCHEMA = schemas.BenchmarkDataStorage
class BenchmarkWithRateDef(BenchmarkDef):
VIEW = None
SCHEMA = schemas.BenchmarkWithRate
class BenchmarkProcessorDef(BenchmarkWithRateDef):
VIEW = None
SCHEMA = schemas.BenchmarkProcessor
class BenchmarkProcessorSysbenchDef(BenchmarkProcessorDef):
VIEW = None
SCHEMA = schemas.BenchmarkProcessorSysbench
class BenchmarkRamSysbenchDef(BenchmarkWithRateDef):
VIEW = None
SCHEMA = schemas.BenchmarkRamSysbench
class TestDef(ActionDef):
VIEW = None
SCHEMA = schemas.Test
class MeasureBattery(TestDef):
VIEW = None
SCHEMA = schemas.MeasureBattery
class TestDataStorageDef(TestDef):
VIEW = None
SCHEMA = schemas.TestDataStorage
class StressTestDef(TestDef):
VIEW = None
SCHEMA = schemas.StressTest
class TestAudioDef(TestDef):
VIEW = None
SCHEMA = schemas.TestAudio
class TestConnectivityDef(TestDef):
VIEW = None
SCHEMA = schemas.TestConnectivity
class TestCameraDef(TestDef):
VIEW = None
SCHEMA = schemas.TestCamera
class TestKeyboardDef(TestDef):
VIEW = None
SCHEMA = schemas.TestKeyboard
class TestTrackpadDef(TestDef):
VIEW = None
SCHEMA = schemas.TestTrackpad
class TestBiosDef(TestDef):
VIEW = None
SCHEMA = schemas.TestBios
class VisualTestDef(TestDef):
VIEW = None
SCHEMA = schemas.VisualTest
class RateDef(ActionDef):
VIEW = None
SCHEMA = schemas.Rate
class RateComputerDef(RateDef):
VIEW = None
SCHEMA = schemas.RateComputer
class PriceDef(ActionDef):
VIEW = None
SCHEMA = schemas.Price
class EreusePriceDef(ActionDef):
VIEW = None
SCHEMA = schemas.EreusePrice
class InstallDef(ActionDef):
VIEW = None
SCHEMA = schemas.Install
class SnapshotDef(ActionDef):
VIEW = None
SCHEMA = schemas.Snapshot
def __init__(self, app, import_name=__name__.split('.')[0], static_folder=None,
static_url_path=None,
template_folder=None, url_prefix=None, subdomain=None, url_defaults=None,
root_path=None, cli_commands: Iterable[Tuple[Callable, str or None]] = tuple()):
url_prefix = '/{}'.format(ActionDef.resource)
super().__init__(app, import_name, static_folder, static_url_path, template_folder,
url_prefix, subdomain, url_defaults, root_path, cli_commands)
self.sync = Sync()
class ToRepairDef(ActionDef):
VIEW = None
SCHEMA = schemas.ToRepair
class RepairDef(ActionDef):
VIEW = None
SCHEMA = schemas.Repair
class ReadyDef(ActionDef):
VIEW = None
SCHEMA = schemas.Ready
class ToPrepareDef(ActionDef):
VIEW = None
SCHEMA = schemas.ToPrepare
class PrepareDef(ActionDef):
VIEW = None
SCHEMA = schemas.Prepare
class LiveDef(ActionDef):
VIEW = None
SCHEMA = schemas.Live
class ReserveDef(ActionDef):
VIEW = None
SCHEMA = schemas.Reserve
class CancelReservationDef(ActionDef):
VIEW = None
SCHEMA = schemas.CancelReservation
class SellDef(ActionDef):
VIEW = None
SCHEMA = schemas.Sell
class DonateDef(ActionDef):
VIEW = None
SCHEMA = schemas.Donate
class RentDef(ActionDef):
VIEW = None
SCHEMA = schemas.Rent
class MakeAvailable(ActionDef):
VIEW = None
SCHEMA = schemas.MakeAvailable
class CancelTradeDef(ActionDef):
VIEW = None
SCHEMA = schemas.CancelTrade
class ToDisposeProductDef(ActionDef):
VIEW = None
SCHEMA = schemas.ToDisposeProduct
class DisposeProductDef(ActionDef):
VIEW = None
SCHEMA = schemas.DisposeProduct
class ReceiveDef(ActionDef):
VIEW = None
SCHEMA = schemas.Receive
class MigrateToDef(ActionDef):
VIEW = None
SCHEMA = schemas.MigrateTo
class MigrateFromDef(ActionDef):
VIEW = None
SCHEMA = schemas.MigrateFrom
class TransferredDef(ActionDef):
VIEW = None
SCHEMA = schemas.Transferred

View File

@ -0,0 +1,127 @@
"""This file contains all proofs related to actions
"""
from collections import Iterable
from contextlib import suppress
from datetime import datetime, timedelta, timezone
from decimal import Decimal, ROUND_HALF_EVEN, ROUND_UP
from typing import Optional, Set, Union
from uuid import uuid4
import inflection
import teal.db
from boltons import urlutils
from citext import CIText
from flask import current_app as app, g
from sortedcontainers import SortedSet
from sqlalchemy import BigInteger, Boolean, CheckConstraint, Column, Enum as DBEnum, \
Float, ForeignKey, Integer, Interval, JSON, Numeric, SmallInteger, Unicode, event, orm
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.orm.events import AttributeEvents as Events
from sqlalchemy.util import OrderedSet
from teal.db import CASCADE_OWN, INHERIT_COND, IP, POLYMORPHIC_ID, \
POLYMORPHIC_ON, StrictVersionType, URL, check_lower, check_range
from teal.enums import Country, Currency, Subdivision
from teal.marshmallow import ValidationError
from teal.resource import url_for_resource
from ereuse_devicehub.db import db
from ereuse_devicehub.resources.agent.models import Agent
from ereuse_devicehub.resources.device.models import Component, Computer, DataStorage, Desktop, \
Device, Laptop, Server
from ereuse_devicehub.resources.enums import AppearanceRange, BatteryHealth, BiosAccessRange, \
ErasureStandards, FunctionalityRange, PhysicalErasureMethod, PriceSoftware, \
R_NEGATIVE, R_POSITIVE, RatingRange, ReceiverRole, Severity, SnapshotSoftware, \
TestDataStorageLength
from ereuse_devicehub.resources.models import STR_SM_SIZE, Thing
from ereuse_devicehub.resources.user.models 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_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))
@property
def certificate(self) -> Optional[urlutils.URL]:
return None
# 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(BigInteger, ForeignKey(Action.id), nullable=False)
transfer = relationship(DisposeProduct,
primaryjoin=DisposeProduct.id == transfer_id)
class ProofDataWipe(JoinedTableMixin, Proof):
erasure_type = Column(CIText())
date = db.Column(db.DateTime, nullable=False, default=datetime.utcnow)
result = db.Column(db.Boolean, default=False, nullable=False)
erasure_id = Column(BigInteger, ForeignKey(Device.id), nullable=False)
erasure = relationship(EraseBasic,
backref=backref('proofs_datawipe',
lazy=True,
cascade=CASCADE_OWN),
primaryjoin=EraseBasic.id == erasure_id)
class ProofFunction(JoinedTableMixin, Proof):
disk_usage = db.Column(db.Integer, default=0)
rate_id = Column(BigInteger, ForeignKey(Rate.id), nullable=False)
rate = relationship(Rate,
primaryjoin=Rate.id == rate_id)
class ProofReuse(JoinedTableMixin, Proof):
price = db.Column(db.Integer, required=True)
class ProofRecycling(JoinedTableMixin, Proof):
collection_point = Column(CIText())
date = db.Column(db.DateTime, nullable=False, default=datetime.utcnow)
contact = Column(CIText())
ticket = Column(CIText())
gps_location = Column(CIText())

View File

@ -0,0 +1,461 @@
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, Decimal, Float, Integer, Nested, String, \
TimeDelta, UUID
from marshmallow.validate import Length, OneOf, Range
from sqlalchemy.util import OrderedSet
from teal.enums import Country, Currency, Subdivision
from teal.marshmallow import EnumField, IP, SanitizedStr, URL, Version
from teal.resource import Schema
from ereuse_devicehub.marshmallow import NestedOn
from ereuse_devicehub.resources import enums
from ereuse_devicehub.resources.action import models as m
from ereuse_devicehub.resources.agent import schemas as s_agent
from ereuse_devicehub.resources.device import schemas as s_device
from ereuse_devicehub.resources.enums import AppearanceRange, BiosAccessRange, FunctionalityRange, \
PhysicalErasureMethod, R_POSITIVE, RatingRange, ReceiverRole, \
Severity, SnapshotSoftware, TestDataStorageLength
from ereuse_devicehub.resources.models import STR_BIG_SIZE, STR_SIZE
from ereuse_devicehub.resources.schemas import Thing
from ereuse_devicehub.resources.user import schemas as s_user
class Proof(Thing):
__doc__ = m.Action.__doc__
id = UUID(dump_only=True)
ethereumHashes = SanitizedStr(default='',
validate=Length(max=STR_BIG_SIZE))
url = URL(dump_only=True, description=m.Action.url.__doc__)
class Action(Thing):
__doc__ = m.Action.__doc__
id = UUID(dump_only=True)
name = SanitizedStr(default='',
validate=Length(max=STR_BIG_SIZE),
description=m.Action.name.comment)
closed = Boolean(missing=True, description=m.Action.closed.comment)
severity = EnumField(Severity, description=m.Action.severity.comment)
description = SanitizedStr(default='', description=m.Action.description.comment)
start_time = DateTime(data_key='startTime', description=m.Action.start_time.comment)
end_time = DateTime(data_key='endTime', description=m.Action.end_time.comment)
snapshot = NestedOn('Snapshot', dump_only=True)
agent = NestedOn(s_agent.Agent, description=m.Action.agent_id.comment)
author = NestedOn(s_user.User, dump_only=True, exclude=('token',))
components = NestedOn(s_device.Component, dump_only=True, many=True)
parent = NestedOn(s_device.Computer, dump_only=True, description=m.Action.parent_id.comment)
url = URL(dump_only=True, description=m.Action.url.__doc__)
class ActionWithOneDevice(Action):
__doc__ = m.ActionWithOneDevice.__doc__
device = NestedOn(s_device.Device, only_query='id')
class ActionWithMultipleDevices(Action):
__doc__ = m.ActionWithMultipleDevices.__doc__
devices = NestedOn(s_device.Device,
many=True,
required=True, # todo test ensuring len(devices) >= 1
only_query='id',
collection_class=OrderedSet)
class Add(ActionWithOneDevice):
__doc__ = m.Add.__doc__
class Remove(ActionWithOneDevice):
__doc__ = m.Remove.__doc__
class Allocate(ActionWithMultipleDevices):
__doc__ = m.Allocate.__doc__
to = NestedOn(s_user.User,
description='The user the devices are allocated to.')
organization = SanitizedStr(validate=Length(max=STR_SIZE),
description='The organization where the '
'user was when this happened.')
class Deallocate(ActionWithMultipleDevices):
__doc__ = m.Deallocate.__doc__
from_rel = Nested(s_user.User,
data_key='from',
description='The user where the devices are not allocated to anymore.')
organization = SanitizedStr(validate=Length(max=STR_SIZE),
description='The organization where the '
'user was when this happened.')
class EraseBasic(ActionWithOneDevice):
__doc__ = m.EraseBasic.__doc__
steps = NestedOn('Step', many=True)
standards = f.List(EnumField(enums.ErasureStandards), dump_only=True)
certificate = URL(dump_only=True)
class EraseSectors(EraseBasic):
__doc__ = m.EraseSectors.__doc__
class ErasePhysical(EraseBasic):
__doc__ = m.ErasePhysical.__doc__
method = EnumField(PhysicalErasureMethod, description=PhysicalErasureMethod.__doc__)
class Step(Schema):
__doc__ = m.Step.__doc__
type = String(description='Only required when it is nested.')
start_time = DateTime(required=True, data_key='startTime')
end_time = DateTime(required=True, data_key='endTime')
severity = EnumField(Severity, description=m.Action.severity.comment)
class StepZero(Step):
__doc__ = m.StepZero.__doc__
class StepRandom(Step):
__doc__ = m.StepRandom.__doc__
class Benchmark(ActionWithOneDevice):
__doc__ = m.Benchmark.__doc__
elapsed = TimeDelta(precision=TimeDelta.SECONDS, required=True)
class BenchmarkDataStorage(Benchmark):
__doc__ = m.BenchmarkDataStorage.__doc__
read_speed = Float(required=True, data_key='readSpeed')
write_speed = Float(required=True, data_key='writeSpeed')
class BenchmarkWithRate(Benchmark):
__doc__ = m.BenchmarkWithRate.__doc__
rate = Float(required=True)
class BenchmarkProcessor(BenchmarkWithRate):
__doc__ = m.BenchmarkProcessor.__doc__
class BenchmarkProcessorSysbench(BenchmarkProcessor):
__doc__ = m.BenchmarkProcessorSysbench.__doc__
class BenchmarkRamSysbench(BenchmarkWithRate):
__doc__ = m.BenchmarkRamSysbench.__doc__
class BenchmarkGraphicCard(BenchmarkWithRate):
__doc__ = m.BenchmarkGraphicCard.__doc__
class Test(ActionWithOneDevice):
__doc__ = m.Test.__doc__
class MeasureBattery(Test):
__doc__ = m.MeasureBattery.__doc__
size = Integer(required=True, description=m.MeasureBattery.size.comment)
voltage = Integer(required=True, description=m.MeasureBattery.voltage.comment)
cycle_count = Integer(data_key='cycleCount', description=m.MeasureBattery.cycle_count.comment)
health = EnumField(enums.BatteryHealth, description=m.MeasureBattery.health.comment)
class TestDataStorage(Test):
__doc__ = m.TestDataStorage.__doc__
elapsed = TimeDelta(precision=TimeDelta.SECONDS, required=True)
length = EnumField(TestDataStorageLength, required=True)
status = SanitizedStr(lower=True, validate=Length(max=STR_SIZE), required=True)
lifetime = TimeDelta(precision=TimeDelta.HOURS)
assessment = Boolean()
reallocated_sector_count = Integer(data_key='reallocatedSectorCount')
power_cycle_count = Integer(data_key='powerCycleCount')
reported_uncorrectable_errors = Integer(data_key='reportedUncorrectableErrors')
command_timeout = Integer(data_key='commandTimeout')
current_pending_sector_count = Integer(data_key='currentPendingSectorCount')
offline_uncorrectable = Integer(data_key='offlineUncorrectable')
remaining_lifetime_percentage = Integer(data_key='remainingLifetimePercentage')
class StressTest(Test):
__doc__ = m.StressTest.__doc__
elapsed = TimeDelta(precision=TimeDelta.SECONDS, required=True)
class TestAudio(Test):
__doc__ = m.TestAudio.__doc__
speaker = Boolean(description=m.TestAudio._speaker.comment)
microphone = Boolean(description=m.TestAudio._microphone.comment)
class TestConnectivity(Test):
__doc__ = m.TestConnectivity.__doc__
class TestCamera(Test):
__doc__ = m.TestCamera.__doc__
class TestKeyboard(Test):
__doc__ = m.TestKeyboard.__doc__
class TestTrackpad(Test):
__doc__ = m.TestTrackpad.__doc__
class TestBios(Test):
__doc__ = m.TestBios.__doc__
bios_power_on = Boolean()
access_range = EnumField(BiosAccessRange, data_key='accessRange')
class VisualTest(Test):
__doc__ = m.VisualTest.__doc__
appearance_range = EnumField(AppearanceRange, required=True, data_key='appearanceRange')
functionality_range = EnumField(FunctionalityRange,
required=True,
data_key='functionalityRange')
labelling = Boolean()
class Rate(ActionWithOneDevice):
__doc__ = m.Rate.__doc__
rating = Integer(validate=Range(*R_POSITIVE),
dump_only=True,
description=m.Rate._rating.comment)
version = Version(dump_only=True,
description=m.Rate.version.comment)
appearance = Integer(validate=Range(enums.R_NEGATIVE),
dump_only=True,
description=m.Rate._appearance.comment)
functionality = Integer(validate=Range(enums.R_NEGATIVE),
dump_only=True,
description=m.Rate._functionality.comment)
rating_range = EnumField(RatingRange,
dump_only=True,
data_key='ratingRange',
description=m.Rate.rating_range.__doc__)
class RateComputer(Rate):
__doc__ = m.RateComputer.__doc__
processor = Float(dump_only=True)
ram = Float(dump_only=True)
data_storage = Float(dump_only=True, data_key='dataStorage')
graphic_card = Float(dump_only=True, data_key='graphicCard')
data_storage_range = EnumField(RatingRange, dump_only=True, data_key='dataStorageRange')
ram_range = EnumField(RatingRange, dump_only=True, data_key='ramRange')
processor_range = EnumField(RatingRange, dump_only=True, data_key='processorRange')
graphic_card_range = EnumField(RatingRange, dump_only=True, data_key='graphicCardRange')
class Price(ActionWithOneDevice):
__doc__ = m.Price.__doc__
currency = EnumField(Currency, required=True, description=m.Price.currency.comment)
price = Decimal(places=m.Price.SCALE,
rounding=m.Price.ROUND,
required=True,
description=m.Price.price.comment)
version = Version(dump_only=True, description=m.Price.version.comment)
rating = NestedOn(Rate, dump_only=True, description=m.Price.rating_id.comment)
class EreusePrice(Price):
__doc__ = m.EreusePrice.__doc__
class Service(MarshmallowSchema):
class Type(MarshmallowSchema):
amount = Float()
percentage = Float()
standard = Nested(Type)
warranty2 = Nested(Type)
warranty2 = Float()
refurbisher = Nested(Service)
retailer = Nested(Service)
platform = Nested(Service)
class Install(ActionWithOneDevice):
__doc__ = m.Install.__doc__
name = SanitizedStr(validate=Length(min=4, max=STR_BIG_SIZE),
required=True,
description='The name of the OS installed.')
elapsed = TimeDelta(precision=TimeDelta.SECONDS, required=True)
address = Integer(validate=OneOf({8, 16, 32, 64, 128, 256}))
class Snapshot(ActionWithOneDevice):
__doc__ = m.Snapshot.__doc__
"""
The Snapshot updates the state of the device with information about
its components and actions performed at them.
See docs for more info.
"""
uuid = UUID()
software = EnumField(SnapshotSoftware,
required=True,
description='The software that generated this Snapshot.')
version = Version(required=True, description='The version of the software.')
actions = NestedOn(Action, many=True, dump_only=True)
elapsed = TimeDelta(precision=TimeDelta.SECONDS)
components = NestedOn(s_device.Component,
many=True,
description='A list of components that are inside of the device'
'at the moment of this Snapshot.'
'Order is preserved, so the component num 0 when'
'submitting is the component num 0 when returning it back.')
@validates_schema
def validate_workbench_version(self, data: dict):
if data['software'] == SnapshotSoftware.Workbench:
if data['version'] < app.config['MIN_WORKBENCH']:
raise ValidationError(
'Min. supported Workbench version is '
'{} but yours is {}.'.format(app.config['MIN_WORKBENCH'], data['version']),
field_names=['version']
)
@validates_schema
def validate_components_only_workbench(self, data: dict):
if data['software'] != SnapshotSoftware.Workbench:
if data.get('components', None) is not None:
raise ValidationError('Only Workbench can add component info',
field_names=['components'])
@validates_schema
def validate_only_workbench_fields(self, data: dict):
"""Ensures workbench has ``elapsed`` and ``uuid`` and no others."""
# todo test
if data['software'] == SnapshotSoftware.Workbench:
if not data.get('uuid', None):
raise ValidationError('Snapshots from Workbench must have uuid',
field_names=['uuid'])
if data.get('elapsed', None) is None:
raise ValidationError('Snapshots from Workbench must have elapsed',
field_names=['elapsed'])
else:
if data.get('uuid', None):
raise ValidationError('Only Snapshots from Workbench can have uuid',
field_names=['uuid'])
if data.get('elapsed', None):
raise ValidationError('Only Snapshots from Workbench can have elapsed',
field_names=['elapsed'])
class ToRepair(ActionWithMultipleDevices):
__doc__ = m.ToRepair.__doc__
class Repair(ActionWithMultipleDevices):
__doc__ = m.Repair.__doc__
class Ready(ActionWithMultipleDevices):
__doc__ = m.Ready.__doc__
class ToPrepare(ActionWithMultipleDevices):
__doc__ = m.ToPrepare.__doc__
class Prepare(ActionWithMultipleDevices):
__doc__ = m.Prepare.__doc__
class Live(ActionWithOneDevice):
__doc__ = m.Live.__doc__
ip = IP(dump_only=True)
subdivision_confidence = Integer(dump_only=True, data_key='subdivisionConfidence')
subdivision = EnumField(Subdivision, dump_only=True)
country = EnumField(Country, dump_only=True)
city = SanitizedStr(lower=True, dump_only=True)
city_confidence = Integer(dump_only=True, data_key='cityConfidence')
isp = SanitizedStr(lower=True, dump_only=True)
organization = SanitizedStr(lower=True, dump_only=True)
organization_type = SanitizedStr(lower=True, dump_only=True, data_key='organizationType')
class Organize(ActionWithMultipleDevices):
__doc__ = m.Organize.__doc__
class Reserve(Organize):
__doc__ = m.Reserve.__doc__
class CancelReservation(Organize):
__doc__ = m.CancelReservation.__doc__
class Trade(ActionWithMultipleDevices):
__doc__ = m.Trade.__doc__
shipping_date = DateTime(data_key='shippingDate')
invoice_number = SanitizedStr(validate=Length(max=STR_SIZE), data_key='invoiceNumber')
price = NestedOn(Price)
to = NestedOn(s_agent.Agent, only_query='id', required=True, comment=m.Trade.to_comment)
confirms = NestedOn(Organize)
class Sell(Trade):
__doc__ = m.Sell.__doc__
class Donate(Trade):
__doc__ = m.Donate.__doc__
class Rent(Trade):
__doc__ = m.Rent.__doc__
class MakeAvailable(ActionWithMultipleDevices):
__doc__ = m.MakeAvailable.__doc__
class CancelTrade(Trade):
__doc__ = m.CancelTrade.__doc__
class ToDisposeProduct(Trade):
__doc__ = m.ToDisposeProduct.__doc__
class DisposeProduct(Trade):
__doc__ = m.DisposeProduct.__doc__
class TransferOwnershipBlockchain(Trade):
__doc__ = m.TransferOwnershipBlockchain.__doc__
class Receive(ActionWithMultipleDevices):
__doc__ = m.Receive.__doc__
role = EnumField(ReceiverRole)
class Migrate(ActionWithMultipleDevices):
__doc__ = m.Migrate.__doc__
other = URL()
class MigrateTo(Migrate):
__doc__ = m.MigrateTo.__doc__
class MigrateFrom(Migrate):
__doc__ = m.MigrateFrom.__doc__
class Transferred(ActionWithMultipleDevices):
__doc__ = m.Transferred.__doc__

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
from sqlalchemy.util import OrderedSet
from teal.marshmallow import ValidationError
from teal.resource import View
from ereuse_devicehub.db import db
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
if json['batch']:
for proof in json['proofs']:
resource_def = app.resources[proof['type']]
a = resource_def.schema.load(json)
if json['type'] == Snapshot.t:
return self.snapshot(a, resource_def)
if json['type'] == VisualTest.t:
pass
# TODO JN add compute rate with new visual test and old components device
Model = db.Model._decl_class_registry.data[json['type']]()
action = Model(**a)
db.session.add(action)
db.session().final_flush()
ret = self.schema.jsonify(action)
ret.status_code = 201
db.session.commit()
return ret