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(lot),
|
||||
import_resource(deliverynote),
|
||||
import_resource(proof),
|
||||
import_resource(documents),
|
||||
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