2018-08-03 16:15:08 +00:00
|
|
|
import decimal
|
|
|
|
|
2018-06-20 21:18:15 +00:00
|
|
|
from flask import current_app as app
|
2018-07-14 14:41:22 +00:00
|
|
|
from marshmallow import Schema as MarshmallowSchema, ValidationError, validates_schema
|
2018-08-03 16:15:08 +00:00
|
|
|
from marshmallow.fields import Boolean, DateTime, Decimal, Float, Integer, List, Nested, String, \
|
2018-10-05 15:13:23 +00:00
|
|
|
TimeDelta, UUID
|
2018-06-20 21:18:15 +00:00
|
|
|
from marshmallow.validate import Length, Range
|
2018-08-03 16:15:08 +00:00
|
|
|
from sqlalchemy.util import OrderedSet
|
2018-09-07 10:38:02 +00:00
|
|
|
from teal.enums import Country, Currency, Subdivision
|
2018-10-05 15:13:23 +00:00
|
|
|
from teal.marshmallow import EnumField, IP, SanitizedStr, Version, URL
|
2018-09-07 10:38:02 +00:00
|
|
|
from teal.resource import Schema
|
2018-06-20 21:18:15 +00:00
|
|
|
|
2018-04-27 17:16:43 +00:00
|
|
|
from ereuse_devicehub.marshmallow import NestedOn
|
2018-08-03 16:15:08 +00:00
|
|
|
from ereuse_devicehub.resources.agent.schemas import Agent
|
2018-07-22 20:42:49 +00:00
|
|
|
from ereuse_devicehub.resources.device.schemas import Component, Computer, Device
|
2018-06-10 16:47:49 +00:00
|
|
|
from ereuse_devicehub.resources.enums import AppearanceRange, Bios, FunctionalityRange, \
|
2018-08-03 16:15:08 +00:00
|
|
|
PriceSoftware, RATE_POSITIVE, RatingSoftware, ReceiverRole, SnapshotExpectedEvents, \
|
|
|
|
SnapshotSoftware, TestHardDriveLength
|
2018-06-12 14:50:05 +00:00
|
|
|
from ereuse_devicehub.resources.event import models as m
|
2018-04-27 17:16:43 +00:00
|
|
|
from ereuse_devicehub.resources.models import STR_BIG_SIZE, STR_SIZE
|
|
|
|
from ereuse_devicehub.resources.schemas import Thing
|
|
|
|
from ereuse_devicehub.resources.user.schemas import User
|
|
|
|
|
|
|
|
|
|
|
|
class Event(Thing):
|
2018-06-15 13:31:03 +00:00
|
|
|
id = UUID(dump_only=True)
|
2018-09-30 10:29:33 +00:00
|
|
|
name = SanitizedStr(default='',
|
|
|
|
validate=Length(max=STR_BIG_SIZE),
|
|
|
|
description=m.Event.name.comment)
|
2018-06-26 13:35:13 +00:00
|
|
|
incidence = Boolean(default=False, description=m.Event.incidence.comment)
|
2018-08-03 16:15:08 +00:00
|
|
|
closed = Boolean(missing=True, description=m.Event.closed.comment)
|
|
|
|
error = Boolean(default=False, description=m.Event.error.comment)
|
2018-09-30 10:29:33 +00:00
|
|
|
description = SanitizedStr(default='', description=m.Event.description.comment)
|
2018-08-03 16:15:08 +00:00
|
|
|
start_time = DateTime(data_key='startTime', description=m.Event.start_time.comment)
|
|
|
|
end_time = DateTime(data_key='endTime', description=m.Event.end_time.comment)
|
|
|
|
snapshot = NestedOn('Snapshot', dump_only=True)
|
|
|
|
agent = NestedOn(Agent, description=m.Event.agent_id.comment)
|
2018-06-12 14:50:05 +00:00
|
|
|
author = NestedOn(User, dump_only=True, exclude=('token',))
|
2018-08-03 16:15:08 +00:00
|
|
|
components = NestedOn(Component, dump_only=True, many=True)
|
2018-07-19 19:25:06 +00:00
|
|
|
parent = NestedOn(Computer, dump_only=True, description=m.Event.parent_id.comment)
|
2018-10-05 15:13:23 +00:00
|
|
|
url = URL(dump_only=True, description=m.Event.url.__doc__)
|
2018-04-27 17:16:43 +00:00
|
|
|
|
|
|
|
|
|
|
|
class EventWithOneDevice(Event):
|
2018-08-03 16:15:08 +00:00
|
|
|
device = NestedOn(Device, only_query='id')
|
2018-04-27 17:16:43 +00:00
|
|
|
|
|
|
|
|
|
|
|
class EventWithMultipleDevices(Event):
|
2018-08-03 16:15:08 +00:00
|
|
|
devices = NestedOn(Device, many=True, only_query='id', collection_class=OrderedSet)
|
2018-04-27 17:16:43 +00:00
|
|
|
|
|
|
|
|
|
|
|
class Add(EventWithOneDevice):
|
|
|
|
pass
|
|
|
|
|
|
|
|
|
|
|
|
class Remove(EventWithOneDevice):
|
|
|
|
pass
|
|
|
|
|
|
|
|
|
|
|
|
class Allocate(EventWithMultipleDevices):
|
2018-05-11 16:58:48 +00:00
|
|
|
to = NestedOn(User,
|
|
|
|
description='The user the devices are allocated to.')
|
2018-09-30 10:29:33 +00:00
|
|
|
organization = SanitizedStr(validate=Length(max=STR_SIZE),
|
|
|
|
description='The organization where the '
|
|
|
|
'user was when this happened.')
|
2018-04-27 17:16:43 +00:00
|
|
|
|
|
|
|
|
|
|
|
class Deallocate(EventWithMultipleDevices):
|
2018-05-11 16:58:48 +00:00
|
|
|
from_rel = Nested(User,
|
2018-04-27 17:16:43 +00:00
|
|
|
data_key='from',
|
|
|
|
description='The user where the devices are not allocated to anymore.')
|
2018-09-30 10:29:33 +00:00
|
|
|
organization = SanitizedStr(validate=Length(max=STR_SIZE),
|
|
|
|
description='The organization where the '
|
|
|
|
'user was when this happened.')
|
2018-04-27 17:16:43 +00:00
|
|
|
|
|
|
|
|
|
|
|
class EraseBasic(EventWithOneDevice):
|
2018-07-02 10:52:54 +00:00
|
|
|
zeros = Boolean(required=True, description=m.EraseBasic.zeros.comment)
|
2018-06-10 16:47:49 +00:00
|
|
|
steps = NestedOn('Step', many=True, required=True)
|
2018-04-27 17:16:43 +00:00
|
|
|
|
|
|
|
|
|
|
|
class EraseSectors(EraseBasic):
|
|
|
|
pass
|
|
|
|
|
|
|
|
|
|
|
|
class Step(Schema):
|
2018-06-10 16:47:49 +00:00
|
|
|
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')
|
|
|
|
error = Boolean(default=False, description='Did the event fail?')
|
|
|
|
|
|
|
|
|
|
|
|
class StepZero(Step):
|
|
|
|
pass
|
|
|
|
|
|
|
|
|
|
|
|
class StepRandom(Step):
|
|
|
|
pass
|
|
|
|
|
|
|
|
|
|
|
|
class Rate(EventWithOneDevice):
|
|
|
|
rating = Integer(validate=Range(*RATE_POSITIVE),
|
|
|
|
dump_only=True,
|
|
|
|
data_key='ratingValue',
|
|
|
|
description='The rating for the content.')
|
2018-07-14 14:41:22 +00:00
|
|
|
software = EnumField(RatingSoftware,
|
|
|
|
dump_only=True,
|
|
|
|
description='The algorithm used to produce this rating.')
|
|
|
|
version = Version(dump_only=True,
|
|
|
|
description='The version of the software.')
|
2018-06-10 16:47:49 +00:00
|
|
|
appearance = Integer(validate=Range(-3, 5), dump_only=True)
|
|
|
|
functionality = Integer(validate=Range(-3, 5),
|
|
|
|
dump_only=True,
|
|
|
|
data_key='functionalityScore')
|
|
|
|
|
|
|
|
|
|
|
|
class IndividualRate(Rate):
|
|
|
|
pass
|
|
|
|
|
|
|
|
|
|
|
|
class AggregateRate(Rate):
|
|
|
|
ratings = NestedOn(IndividualRate, many=True)
|
|
|
|
|
|
|
|
|
|
|
|
class PhotoboxRate(IndividualRate):
|
|
|
|
num = Integer(dump_only=True)
|
|
|
|
# todo Image
|
|
|
|
|
|
|
|
|
2018-06-20 21:18:15 +00:00
|
|
|
class PhotoboxUserRate(IndividualRate):
|
2018-06-10 16:47:49 +00:00
|
|
|
assembling = Integer()
|
|
|
|
parts = Integer()
|
|
|
|
buttons = Integer()
|
|
|
|
dents = Integer()
|
|
|
|
decolorization = Integer()
|
|
|
|
scratches = Integer()
|
|
|
|
tag_adhesive = Integer()
|
|
|
|
dirt = Integer()
|
2018-04-27 17:16:43 +00:00
|
|
|
|
|
|
|
|
2018-06-20 21:18:15 +00:00
|
|
|
class PhotoboxSystemRate(IndividualRate):
|
2018-06-10 16:47:49 +00:00
|
|
|
pass
|
|
|
|
|
|
|
|
|
2018-06-20 21:18:15 +00:00
|
|
|
class ManualRate(IndividualRate):
|
2018-06-10 16:47:49 +00:00
|
|
|
appearance_range = EnumField(AppearanceRange,
|
|
|
|
required=True,
|
|
|
|
data_key='appearanceRange',
|
|
|
|
description='Grades the imperfections that aesthetically '
|
|
|
|
'affect the device, but not its usage.')
|
|
|
|
functionality_range = EnumField(FunctionalityRange,
|
|
|
|
required=True,
|
|
|
|
data_key='functionalityRange',
|
2018-07-14 14:41:22 +00:00
|
|
|
description='Grades the defects of a device affecting usage.')
|
2018-06-20 21:18:15 +00:00
|
|
|
labelling = Boolean(description='Sets if there are labels stuck that should be removed.')
|
|
|
|
|
|
|
|
|
|
|
|
class AppRate(ManualRate):
|
|
|
|
pass
|
|
|
|
|
|
|
|
|
|
|
|
class WorkbenchRate(ManualRate):
|
|
|
|
processor = Float()
|
|
|
|
ram = Float()
|
|
|
|
data_storage = Float()
|
|
|
|
graphic_card = Float()
|
|
|
|
bios = EnumField(Bios, description='How difficult it has been to set the bios to '
|
|
|
|
'boot from the network.')
|
2018-06-10 16:47:49 +00:00
|
|
|
|
|
|
|
|
2018-07-14 14:41:22 +00:00
|
|
|
class Price(EventWithOneDevice):
|
|
|
|
currency = EnumField(Currency, required=True)
|
2018-08-03 16:15:08 +00:00
|
|
|
price = Decimal(places=4, rounding=decimal.ROUND_HALF_EVEN, required=True)
|
2018-07-14 14:41:22 +00:00
|
|
|
software = EnumField(PriceSoftware, dump_only=True)
|
|
|
|
version = Version(dump_only=True)
|
|
|
|
rating = NestedOn(AggregateRate, dump_only=True)
|
|
|
|
|
|
|
|
|
|
|
|
class EreusePrice(Price):
|
|
|
|
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)
|
|
|
|
|
|
|
|
|
2018-06-10 16:47:49 +00:00
|
|
|
class Install(EventWithOneDevice):
|
2018-09-30 10:29:33 +00:00
|
|
|
name = SanitizedStr(validate=Length(min=4, max=STR_BIG_SIZE),
|
|
|
|
required=True,
|
|
|
|
description='The name of the OS installed.')
|
2018-04-27 17:16:43 +00:00
|
|
|
elapsed = TimeDelta(precision=TimeDelta.SECONDS, required=True)
|
|
|
|
|
|
|
|
|
|
|
|
class Snapshot(EventWithOneDevice):
|
2018-05-13 13:13:12 +00:00
|
|
|
"""
|
|
|
|
The Snapshot updates the state of the device with information about
|
|
|
|
its components and events performed at them.
|
|
|
|
|
|
|
|
See docs for more info.
|
|
|
|
"""
|
2018-06-20 21:18:15 +00:00
|
|
|
uuid = UUID()
|
2018-06-10 16:47:49 +00:00
|
|
|
software = EnumField(SnapshotSoftware,
|
|
|
|
required=True,
|
|
|
|
description='The software that generated this Snapshot.')
|
|
|
|
version = Version(required=True, description='The version of the software.')
|
2018-06-16 10:41:12 +00:00
|
|
|
events = NestedOn(Event, many=True, dump_only=True)
|
2018-06-19 16:38:42 +00:00
|
|
|
expected_events = List(EnumField(SnapshotExpectedEvents),
|
|
|
|
data_key='expectedEvents',
|
|
|
|
description='Keep open this Snapshot until the following events'
|
|
|
|
'are performed. Setting this value will activate'
|
|
|
|
'the async Snapshot.')
|
|
|
|
|
2018-06-20 21:18:15 +00:00
|
|
|
elapsed = TimeDelta(precision=TimeDelta.SECONDS)
|
2018-05-13 13:13:12 +00:00
|
|
|
components = NestedOn(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.')
|
2018-04-27 17:16:43 +00:00
|
|
|
|
|
|
|
@validates_schema
|
|
|
|
def validate_workbench_version(self, data: dict):
|
2018-06-10 16:47:49 +00:00
|
|
|
if data['software'] == SnapshotSoftware.Workbench:
|
2018-04-27 17:16:43 +00:00
|
|
|
if data['version'] < app.config['MIN_WORKBENCH']:
|
|
|
|
raise ValidationError(
|
2018-07-14 14:41:22 +00:00
|
|
|
'Min. supported Workbench version is '
|
2018-09-08 14:46:39 +00:00
|
|
|
'{} but yours is {}.'.format(app.config['MIN_WORKBENCH'], data['version']),
|
2018-04-27 17:16:43 +00:00
|
|
|
field_names=['version']
|
|
|
|
)
|
|
|
|
|
|
|
|
@validates_schema
|
|
|
|
def validate_components_only_workbench(self, data: dict):
|
2018-06-10 16:47:49 +00:00
|
|
|
if data['software'] != SnapshotSoftware.Workbench:
|
2018-06-20 21:18:15 +00:00
|
|
|
if data.get('components', None) is not None:
|
2018-04-27 17:16:43 +00:00
|
|
|
raise ValidationError('Only Workbench can add component info',
|
|
|
|
field_names=['components'])
|
|
|
|
|
2018-06-20 21:18:15 +00:00
|
|
|
@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'])
|
2018-09-08 14:46:39 +00:00
|
|
|
if data.get('elapsed', None) is None:
|
2018-06-20 21:18:15 +00:00
|
|
|
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'])
|
|
|
|
|
2018-04-27 17:16:43 +00:00
|
|
|
|
|
|
|
class Test(EventWithOneDevice):
|
|
|
|
elapsed = TimeDelta(precision=TimeDelta.SECONDS, required=True)
|
|
|
|
|
|
|
|
|
2018-06-10 16:47:49 +00:00
|
|
|
class TestDataStorage(Test):
|
2018-04-27 17:16:43 +00:00
|
|
|
length = EnumField(TestHardDriveLength, required=True)
|
2018-09-30 10:29:33 +00:00
|
|
|
status = SanitizedStr(lower=True, validate=Length(max=STR_SIZE), required=True)
|
2018-07-02 10:52:54 +00:00
|
|
|
lifetime = TimeDelta(precision=TimeDelta.DAYS)
|
2018-06-19 16:38:42 +00:00
|
|
|
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')
|
2018-04-27 17:16:43 +00:00
|
|
|
|
|
|
|
|
|
|
|
class StressTest(Test):
|
|
|
|
pass
|
2018-06-19 16:38:42 +00:00
|
|
|
|
|
|
|
|
|
|
|
class Benchmark(EventWithOneDevice):
|
|
|
|
elapsed = TimeDelta(precision=TimeDelta.SECONDS)
|
|
|
|
|
|
|
|
|
|
|
|
class BenchmarkDataStorage(Benchmark):
|
|
|
|
read_speed = Float(required=True, data_key='readSpeed')
|
|
|
|
write_speed = Float(required=True, data_key='writeSpeed')
|
|
|
|
|
|
|
|
|
|
|
|
class BenchmarkWithRate(Benchmark):
|
|
|
|
rate = Integer(required=True)
|
|
|
|
|
|
|
|
|
|
|
|
class BenchmarkProcessor(BenchmarkWithRate):
|
|
|
|
pass
|
|
|
|
|
|
|
|
|
|
|
|
class BenchmarkProcessorSysbench(BenchmarkProcessor):
|
|
|
|
pass
|
|
|
|
|
|
|
|
|
|
|
|
class BenchmarkRamSysbench(BenchmarkWithRate):
|
|
|
|
pass
|
2018-07-22 20:42:49 +00:00
|
|
|
|
|
|
|
|
|
|
|
class ToRepair(EventWithMultipleDevices):
|
|
|
|
pass
|
|
|
|
|
|
|
|
|
|
|
|
class Repair(EventWithMultipleDevices):
|
|
|
|
pass
|
|
|
|
|
|
|
|
|
2018-08-03 16:15:08 +00:00
|
|
|
class ReadyToUse(EventWithMultipleDevices):
|
|
|
|
pass
|
|
|
|
|
|
|
|
|
2018-07-22 20:42:49 +00:00
|
|
|
class ToPrepare(EventWithMultipleDevices):
|
|
|
|
pass
|
|
|
|
|
|
|
|
|
|
|
|
class Prepare(EventWithMultipleDevices):
|
|
|
|
pass
|
|
|
|
|
|
|
|
|
2018-08-03 16:15:08 +00:00
|
|
|
class Live(EventWithOneDevice):
|
|
|
|
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)
|
2018-09-30 10:29:33 +00:00
|
|
|
city = SanitizedStr(lower=True, dump_only=True)
|
2018-08-03 16:15:08 +00:00
|
|
|
city_confidence = Integer(dump_only=True, data_key='cityConfidence')
|
2018-09-30 10:29:33 +00:00
|
|
|
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')
|
2018-08-03 16:15:08 +00:00
|
|
|
|
|
|
|
|
|
|
|
class Organize(EventWithMultipleDevices):
|
|
|
|
pass
|
|
|
|
|
|
|
|
|
|
|
|
class Reserve(Organize):
|
|
|
|
pass
|
|
|
|
|
|
|
|
|
|
|
|
class CancelReservation(Organize):
|
|
|
|
pass
|
|
|
|
|
|
|
|
|
|
|
|
class Trade(EventWithMultipleDevices):
|
|
|
|
shipping_date = DateTime(data_key='shippingDate')
|
2018-09-30 10:29:33 +00:00
|
|
|
invoice_number = SanitizedStr(validate=Length(max=STR_SIZE), data_key='invoiceNumber')
|
2018-08-03 16:15:08 +00:00
|
|
|
price = NestedOn(Price)
|
2018-09-20 14:40:41 +00:00
|
|
|
to = NestedOn(Agent, only_query='id', required=True, comment=m.Trade.to_comment)
|
2018-08-03 16:15:08 +00:00
|
|
|
confirms = NestedOn(Organize)
|
|
|
|
|
|
|
|
|
|
|
|
class Sell(Trade):
|
|
|
|
pass
|
|
|
|
|
|
|
|
|
|
|
|
class Donate(Trade):
|
|
|
|
pass
|
|
|
|
|
|
|
|
|
|
|
|
class Rent(Trade):
|
|
|
|
pass
|
|
|
|
|
|
|
|
|
|
|
|
class CancelTrade(Trade):
|
|
|
|
pass
|
|
|
|
|
|
|
|
|
|
|
|
class ToDisposeProduct(Trade):
|
|
|
|
pass
|
|
|
|
|
|
|
|
|
|
|
|
class DisposeProduct(Trade):
|
|
|
|
pass
|
|
|
|
|
|
|
|
|
|
|
|
class Receive(EventWithMultipleDevices):
|
|
|
|
role = EnumField(ReceiverRole)
|
|
|
|
|
|
|
|
|
|
|
|
class Migrate(EventWithMultipleDevices):
|
|
|
|
other = URL()
|
|
|
|
|
|
|
|
|
|
|
|
class MigrateTo(Migrate):
|
2018-07-22 20:42:49 +00:00
|
|
|
pass
|
|
|
|
|
|
|
|
|
2018-08-03 16:15:08 +00:00
|
|
|
class MigrateFrom(Migrate):
|
2018-07-22 20:42:49 +00:00
|
|
|
pass
|