Add base proof models
This commit is contained in:
parent
2aa527c0c3
commit
994dce4349
|
@ -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)),
|
||||||
)
|
)
|
||||||
|
|
|
@ -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
|
|
@ -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())
|
|
@ -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__
|
||||||
|
|
||||||
|
|
|
@ -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
|
Reference in New Issue