This repository has been archived on 2024-05-31. You can view files and clone it, but cannot push or open issues or pull requests.
devicehub-teal/ereuse_devicehub/resources/action/schemas.py

1002 lines
31 KiB
Python
Raw Normal View History

2021-04-27 13:39:11 +00:00
import copy
2020-11-26 17:44:08 +00:00
from datetime import datetime, timedelta
2022-03-18 11:07:22 +00:00
2020-11-26 17:44:08 +00:00
from dateutil.tz import tzutc
2022-03-18 11:07:22 +00:00
from flask import current_app as app
from flask import g
from marshmallow import Schema as MarshmallowSchema
from marshmallow import ValidationError
from marshmallow import fields as f
from marshmallow import post_load, pre_load, validates_schema
from marshmallow.fields import (
UUID,
Boolean,
DateTime,
Decimal,
Float,
Integer,
Nested,
String,
TimeDelta,
)
2018-11-26 12:11:07 +00:00
from marshmallow.validate import Length, OneOf, Range
from sqlalchemy.util import OrderedSet
2018-06-20 21:18:15 +00:00
2018-04-27 17:16:43 +00:00
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
2021-07-26 09:33:11 +00:00
from ereuse_devicehub.resources.documents import schemas as s_generic_document
2022-03-18 11:07:22 +00:00
from ereuse_devicehub.resources.enums import (
R_POSITIVE,
AppearanceRange,
BiosAccessRange,
FunctionalityRange,
PhysicalErasureMethod,
RatingRange,
Severity,
SnapshotSoftware,
TestDataStorageLength,
)
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
2022-03-18 11:07:22 +00:00
from ereuse_devicehub.resources.tradedocument import schemas as s_document
from ereuse_devicehub.resources.tradedocument.models import TradeDocument
from ereuse_devicehub.resources.user import schemas as s_user
2021-03-18 15:36:19 +00:00
from ereuse_devicehub.resources.user.models import User
2023-05-26 15:11:47 +00:00
from ereuse_devicehub.teal.enums import Currency
from ereuse_devicehub.teal.marshmallow import URL, EnumField, SanitizedStr, Version
2023-03-21 11:08:13 +00:00
from ereuse_devicehub.teal.resource import Schema
2018-04-27 17:16:43 +00:00
class Action(Thing):
__doc__ = m.Action.__doc__
2018-06-15 13:31:03 +00:00
id = UUID(dump_only=True)
2022-03-18 11:07:22 +00:00
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)
2022-03-18 11:07:22 +00:00
parent = NestedOn(
s_device.Computer, dump_only=True, description=m.Action.parent_id.comment
)
url = URL(dump_only=True, description=m.Action.url.__doc__)
2018-04-27 17:16:43 +00:00
2020-11-30 15:37:52 +00:00
@validates_schema
2020-11-30 20:18:10 +00:00
def validate_times(self, data: dict):
unix_time = datetime.fromisoformat("1970-01-02 00:00:00+00:00")
if 'end_time' in data and data['end_time'].replace(tzinfo=tzutc()) < unix_time:
2020-11-30 20:18:10 +00:00
data['end_time'] = unix_time
2022-07-20 12:58:32 +00:00
if (
'start_time' in data
and data['start_time'].replace(tzinfo=tzutc()) < unix_time
):
2020-11-30 20:18:10 +00:00
data['start_time'] = unix_time
2020-11-30 15:37:52 +00:00
2022-02-02 11:49:03 +00:00
if data.get('end_time') and data.get('start_time'):
if data['start_time'] > data['end_time']:
raise ValidationError('The action cannot finish before it starts.')
2018-04-27 17:16:43 +00:00
class ActionWithOneDevice(Action):
2021-06-24 09:41:14 +00:00
__doc__ = m.ActionWithOneDevice.__doc__
device = NestedOn(s_device.Device, only_query='id')
2021-06-24 09:41:14 +00:00
class ActionWithMultipleDocuments(Action):
__doc__ = m.ActionWithMultipleTradeDocuments.__doc__
2022-03-18 11:07:22 +00:00
documents = NestedOn(
s_document.TradeDocument,
many=True,
required=True, # todo test ensuring len(devices) >= 1
only_query='id',
collection_class=OrderedSet,
)
2018-04-27 17:16:43 +00:00
class ActionWithMultipleDevices(Action):
__doc__ = m.ActionWithMultipleDevices.__doc__
2022-03-18 11:07:22 +00:00
devices = NestedOn(
s_device.Device,
many=True,
required=True, # todo test ensuring len(devices) >= 1
only_query='id',
collection_class=OrderedSet,
)
2018-04-27 17:16:43 +00:00
2021-11-22 11:01:12 +00:00
class ActionWithMultipleDevicesCheckingOwner(ActionWithMultipleDevices):
@post_load
def check_owner_of_device(self, data):
for dev in data['devices']:
if dev.owner != g.user:
raise ValidationError("Some Devices not exist")
class Add(ActionWithOneDevice):
__doc__ = m.Add.__doc__
2018-04-27 17:16:43 +00:00
class Remove(ActionWithOneDevice):
__doc__ = m.Remove.__doc__
2018-04-27 17:16:43 +00:00
2021-11-22 11:01:12 +00:00
class Allocate(ActionWithMultipleDevicesCheckingOwner):
__doc__ = m.Allocate.__doc__
2022-03-18 11:07:22 +00:00
start_time = DateTime(
data_key='startTime', required=True, description=m.Action.start_time.comment
)
end_time = DateTime(
data_key='endTime', required=False, description=m.Action.end_time.comment
)
final_user_code = SanitizedStr(
data_key="finalUserCode",
validate=Length(min=1, max=STR_BIG_SIZE),
required=False,
description='This is a internal code for mainteing the secrets of the \
personal datas of the new holder',
)
transaction = SanitizedStr(
validate=Length(min=1, max=STR_BIG_SIZE),
required=False,
description='The code used from the owner for \
relation with external tool.',
)
end_users = Integer(
data_key='endUsers',
validate=[Range(min=1, error="Value must be greater than 0")],
)
2018-04-27 17:16:43 +00:00
@validates_schema
def validate_allocate(self, data: dict):
2020-11-26 17:44:08 +00:00
txt = "You need to allocate for a day before today"
delay = timedelta(days=1)
today = datetime.now().replace(tzinfo=tzutc()) + delay
start_time = data['start_time'].replace(tzinfo=tzutc())
if start_time > today:
raise ValidationError(txt)
2020-11-23 14:53:25 +00:00
txt = "You need deallocate before allocate this device again"
for device in data['devices']:
2020-11-26 17:44:08 +00:00
if device.allocated:
raise ValidationError(txt)
2020-11-23 14:53:25 +00:00
device.allocated = True
2018-04-27 17:16:43 +00:00
2021-11-22 11:01:12 +00:00
class Deallocate(ActionWithMultipleDevicesCheckingOwner):
__doc__ = m.Deallocate.__doc__
2022-03-18 11:07:22 +00:00
start_time = DateTime(
data_key='startTime', required=True, description=m.Action.start_time.comment
)
transaction = SanitizedStr(
validate=Length(min=1, max=STR_BIG_SIZE),
required=False,
description='The code used from the owner for \
relation with external tool.',
)
2020-11-21 14:52:29 +00:00
@validates_schema
def validate_deallocate(self, data: dict):
2020-11-26 17:44:08 +00:00
txt = "You need to deallocate for a day before today"
delay = timedelta(days=1)
today = datetime.now().replace(tzinfo=tzutc()) + delay
start_time = data['start_time'].replace(tzinfo=tzutc())
if start_time > today:
raise ValidationError(txt)
2020-11-26 17:44:08 +00:00
txt = "Sorry some of this devices are actually deallocate"
for device in data['devices']:
2020-11-26 17:44:08 +00:00
if not device.allocated:
raise ValidationError(txt)
2020-11-26 17:44:08 +00:00
device.allocated = False
2018-04-27 17:16:43 +00:00
class EraseBasic(ActionWithOneDevice):
__doc__ = m.EraseBasic.__doc__
steps = NestedOn('Step', many=True)
standards = f.List(EnumField(enums.ErasureStandards), dump_only=True)
2018-12-30 11:43:29 +00:00
certificate = URL(dump_only=True)
2018-04-27 17:16:43 +00:00
class EraseSectors(EraseBasic):
__doc__ = m.EraseSectors.__doc__
2018-04-27 17:16:43 +00:00
class ErasePhysical(EraseBasic):
__doc__ = m.ErasePhysical.__doc__
method = EnumField(PhysicalErasureMethod, description=PhysicalErasureMethod.__doc__)
2018-04-27 17:16:43 +00:00
class Step(Schema):
__doc__ = m.Step.__doc__
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')
severity = EnumField(Severity, description=m.Action.severity.comment)
2018-06-10 16:47:49 +00:00
class StepZero(Step):
__doc__ = m.StepZero.__doc__
2018-06-10 16:47:49 +00:00
class StepRandom(Step):
__doc__ = m.StepRandom.__doc__
2018-06-10 16:47:49 +00:00
class Benchmark(ActionWithOneDevice):
2019-03-10 19:41:10 +00:00
__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):
2019-03-10 19:41:10 +00:00
__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)
2022-03-18 11:07:22 +00:00
cycle_count = Integer(
data_key='cycleCount', description=m.MeasureBattery.cycle_count.comment
)
health = EnumField(enums.BatteryHealth, description=m.MeasureBattery.health.comment)
2019-03-10 19:41:10 +00:00
class TestDataStorage(Test):
__doc__ = m.TestDataStorage.__doc__
elapsed = TimeDelta(precision=TimeDelta.SECONDS, required=True)
2019-03-10 19:41:10 +00:00
length = EnumField(TestDataStorageLength, required=True)
status = SanitizedStr(lower=True, validate=Length(max=STR_SIZE), required=True)
lifetime = TimeDelta(precision=TimeDelta.HOURS)
2021-11-23 14:27:16 +00:00
power_on_hours = Integer(data_key='powerOnHours', dump_only=True)
2019-03-10 19:41:10 +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')
2022-09-28 09:10:34 +00:00
@post_load
def default_remaining_lifetime_percentage(self, data):
if not data.get('remaining_lifetime_percentage'):
return
if data.get('remaining_lifetime_percentage') > 100:
2022-09-28 09:55:31 +00:00
data['remaining_lifetime_percentage'] = None
2022-09-28 09:10:34 +00:00
2019-03-10 19:41:10 +00:00
class StressTest(Test):
__doc__ = m.StressTest.__doc__
elapsed = TimeDelta(precision=TimeDelta.SECONDS, required=True)
2019-03-10 19:41:10 +00:00
class TestAudio(Test):
__doc__ = m.TestAudio.__doc__
2019-05-08 17:12:05 +00:00
speaker = Boolean(description=m.TestAudio._speaker.comment)
microphone = Boolean(description=m.TestAudio._microphone.comment)
2019-03-10 19:41:10 +00:00
class TestConnectivity(Test):
__doc__ = m.TestConnectivity.__doc__
class TestCamera(Test):
__doc__ = m.TestCamera.__doc__
2019-03-13 15:32:59 +00:00
class TestKeyboard(Test):
__doc__ = m.TestKeyboard.__doc__
2019-03-13 15:32:59 +00:00
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')
2019-03-13 15:32:59 +00:00
2019-05-08 17:12:05 +00:00
class VisualTest(Test):
__doc__ = m.VisualTest.__doc__
appearance_range = EnumField(AppearanceRange, data_key='appearanceRange')
2022-03-18 11:07:22 +00:00
functionality_range = EnumField(FunctionalityRange, data_key='functionalityRange')
labelling = Boolean()
2019-03-10 19:41:10 +00:00
class Rate(ActionWithOneDevice):
__doc__ = m.Rate.__doc__
2022-03-18 11:07:22 +00:00
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__,
)
2018-06-10 16:47:49 +00:00
2019-04-16 15:47:28 +00:00
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')
2022-03-18 11:07:22 +00:00
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')
2022-03-18 11:07:22 +00:00
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)
2022-03-18 11:07:22 +00:00
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)
2019-04-16 15:47:28 +00:00
rating = NestedOn(Rate, dump_only=True, description=m.Price.rating_id.comment)
2018-07-14 14:41:22 +00:00
class EreusePrice(Price):
__doc__ = m.EreusePrice.__doc__
2018-07-14 14:41:22 +00:00
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__
2022-03-18 11:07:22 +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)
2018-11-26 12:11:07 +00:00
address = Integer(validate=OneOf({8, 16, 32, 64, 128, 256}))
2018-04-27 17:16:43 +00:00
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.
"""
2018-06-20 21:18:15 +00:00
uuid = UUID()
sid = String(required=False)
2022-03-18 11:07:22 +00:00
software = EnumField(
SnapshotSoftware,
required=True,
description='The software that generated this Snapshot.',
)
2023-07-12 14:18:56 +00:00
version = String(required=True, description='The version of the software.')
actions = NestedOn(Action, many=True, dump_only=True)
2018-06-20 21:18:15 +00:00
elapsed = TimeDelta(precision=TimeDelta.SECONDS)
2022-03-18 11:07:22 +00:00
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.',
)
2022-11-30 11:54:11 +00:00
settings_version = String(required=False)
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 '
2022-03-18 11:07:22 +00:00
'{} but yours is {}.'.format(
app.config['MIN_WORKBENCH'], data['version']
),
field_names=['version'],
2018-04-27 17:16:43 +00:00
)
@validates_schema
def validate_components_only_workbench(self, data: dict):
2023-07-11 12:06:16 +00:00
software = [
SnapshotSoftware.Workbench,
SnapshotSoftware.WorkbenchAndroid,
SnapshotSoftware.UsodyOS,
]
if data['software'] not in software:
2018-06-20 21:18:15 +00:00
if data.get('components', None) is not None:
2022-03-18 11:07:22 +00:00
raise ValidationError(
'Only Workbench can add component info', field_names=['components']
)
2018-04-27 17:16:43 +00:00
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
2023-07-11 12:06:16 +00:00
software = [
SnapshotSoftware.Workbench,
SnapshotSoftware.UsodyOS,
]
if data['software'] in software:
2018-06-20 21:18:15 +00:00
if not data.get('uuid', None):
2022-03-18 11:07:22 +00:00
raise ValidationError(
'Snapshots from Workbench and WorkbenchAndroid must have uuid',
field_names=['uuid'],
)
if data.get('elapsed', None) is None:
2022-03-18 11:07:22 +00:00
raise ValidationError(
'Snapshots from Workbench must have elapsed',
field_names=['elapsed'],
)
elif data['software'] == SnapshotSoftware.WorkbenchAndroid:
if not data.get('uuid', None):
2022-03-18 11:07:22 +00:00
raise ValidationError(
'Snapshots from Workbench and WorkbenchAndroid must have uuid',
field_names=['uuid'],
)
2018-06-20 21:18:15 +00:00
else:
if data.get('uuid', None):
2022-03-18 11:07:22 +00:00
raise ValidationError(
'Only Snapshots from Workbench or WorkbenchAndroid can have uuid',
field_names=['uuid'],
)
2018-06-20 21:18:15 +00:00
if data.get('elapsed', None):
2022-03-18 11:07:22 +00:00
raise ValidationError(
'Only Snapshots from Workbench can have elapsed',
field_names=['elapsed'],
)
2018-06-20 21:18:15 +00:00
2018-04-27 17:16:43 +00:00
2021-11-22 11:01:12 +00:00
class ToRepair(ActionWithMultipleDevicesCheckingOwner):
__doc__ = m.ToRepair.__doc__
2021-11-22 11:01:12 +00:00
class Repair(ActionWithMultipleDevicesCheckingOwner):
__doc__ = m.Repair.__doc__
2021-11-22 11:01:12 +00:00
class Ready(ActionWithMultipleDevicesCheckingOwner):
__doc__ = m.Ready.__doc__
2021-10-01 08:46:08 +00:00
class ActionStatus(Action):
rol_user = NestedOn(s_user.User, dump_only=True, exclude=('token',))
2022-03-18 11:07:22 +00:00
devices = NestedOn(
s_device.Device,
many=True,
required=False, # todo test ensuring len(devices) >= 1
only_query='id',
collection_class=OrderedSet,
)
documents = NestedOn(
s_document.TradeDocument,
many=True,
required=False, # todo test ensuring len(devices) >= 1
only_query='id',
collection_class=OrderedSet,
)
2021-10-01 08:46:08 +00:00
@pre_load
def put_devices(self, data: dict):
2021-11-05 12:10:23 +00:00
if 'devices' not in data.keys():
2021-10-01 08:46:08 +00:00
data['devices'] = []
2021-09-27 12:58:25 +00:00
@post_load
def put_rol_user(self, data: dict):
for dev in data['devices']:
2021-11-05 12:10:23 +00:00
trades = [ac for ac in dev.actions if ac.t == 'Trade']
if not trades:
2021-09-27 12:58:25 +00:00
return data
2021-11-05 12:10:23 +00:00
trade = trades[-1]
2021-11-10 17:59:44 +00:00
if trade.user_from == g.user:
2021-09-27 12:58:25 +00:00
data['rol_user'] = trade.user_to
2021-11-05 12:10:23 +00:00
data['trade'] = trade
2021-09-27 12:58:25 +00:00
class Recycling(ActionStatus):
__doc__ = m.Recycling.__doc__
class Use(ActionStatus):
__doc__ = m.Use.__doc__
class Refurbish(ActionStatus):
__doc__ = m.Refurbish.__doc__
class Management(ActionStatus):
__doc__ = m.Management.__doc__
2021-11-22 11:01:12 +00:00
class ToPrepare(ActionWithMultipleDevicesCheckingOwner):
__doc__ = m.ToPrepare.__doc__
2021-11-22 11:01:12 +00:00
class Prepare(ActionWithMultipleDevicesCheckingOwner):
__doc__ = m.Prepare.__doc__
2021-11-22 11:01:12 +00:00
class DataWipe(ActionWithMultipleDevicesCheckingOwner):
2021-07-29 10:45:43 +00:00
__doc__ = m.DataWipe.__doc__
document = NestedOn(s_generic_document.DataWipeDocument, only_query='id')
2021-07-21 06:35:18 +00:00
2023-05-26 15:11:47 +00:00
class EraseDataWipe(ActionWithMultipleDevicesCheckingOwner):
__doc__ = m.DataWipe.__doc__
document = NestedOn(s_generic_document.DataWipeDocument, only_query='id')
class Live(ActionWithOneDevice):
__doc__ = m.Live.__doc__
2020-12-28 14:31:57 +00:00
"""
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()
2022-03-18 11:07:22 +00:00
software = EnumField(
SnapshotSoftware,
required=True,
description='The software that generated this Snapshot.',
)
2020-12-28 14:31:57 +00:00
version = Version(required=True, description='The version of the software.')
2020-12-01 16:27:43 +00:00
final_user_code = SanitizedStr(data_key="finalUserCode", dump_only=True)
2020-12-28 14:31:57 +00:00
licence_version = Version(required=True, description='The version of the software.')
2022-03-18 11:07:22 +00:00
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.',
)
usage_time_allocate = TimeDelta(
data_key='usageTimeAllocate',
required=False,
precision=TimeDelta.HOURS,
dump_only=True,
)
class Organize(ActionWithMultipleDevices):
__doc__ = m.Organize.__doc__
class Reserve(Organize):
__doc__ = m.Reserve.__doc__
class CancelReservation(Organize):
__doc__ = m.CancelReservation.__doc__
2021-04-22 09:12:14 +00:00
2021-06-24 09:41:14 +00:00
class Confirm(ActionWithMultipleDevices):
2021-04-19 17:33:35 +00:00
__doc__ = m.Confirm.__doc__
2021-04-22 09:12:14 +00:00
action = NestedOn('Action', only_query='id')
2021-04-27 13:39:11 +00:00
@validates_schema
def validate_revoke(self, data: dict):
for dev in data['devices']:
# if device not exist in the Trade, then this query is wrong
2021-11-22 11:01:12 +00:00
if dev not in data['action'].devices:
txt = "Device {} not exist in the trade".format(dev.devicehub_id)
2021-04-27 13:39:11 +00:00
raise ValidationError(txt)
2021-06-24 09:41:14 +00:00
class Revoke(ActionWithMultipleDevices):
2021-04-30 15:54:03 +00:00
__doc__ = m.Revoke.__doc__
action = NestedOn('Action', only_query='id')
@validates_schema
def validate_revoke(self, data: dict):
for dev in data['devices']:
# if device not exist in the Trade, then this query is wrong
2021-11-22 11:01:12 +00:00
if dev not in data['action'].devices:
txt = "Device {} not exist in the trade".format(dev.devicehub_id)
2021-04-30 15:54:03 +00:00
raise ValidationError(txt)
2021-05-21 11:16:30 +00:00
for doc in data.get('documents', []):
# if document not exist in the Trade, then this query is wrong
2021-11-22 11:01:12 +00:00
if doc not in data['action'].documents:
2021-05-21 11:16:30 +00:00
txt = "Document {} not exist in the trade".format(doc.file_name)
raise ValidationError(txt)
2021-04-30 15:54:03 +00:00
2021-05-21 11:16:30 +00:00
@validates_schema
def validate_documents(self, data):
"""Check if there are or no one before confirmation,
2022-03-18 11:07:22 +00:00
This is not checked in the view becouse the list of documents is inmutable
2021-05-21 11:16:30 +00:00
"""
if not data['devices'] == OrderedSet():
return
documents = []
for doc in data['documents']:
actions = copy.copy(doc.actions)
actions.reverse()
for ac in actions:
if ac == data['action']:
# data['action'] is a Trade action, if this is the first action
# to find mean that this document don't have a confirmation
break
if ac.t == 'Revoke' and ac.user == g.user:
# this doc is confirmation jet
break
if ac.t == Confirm.t and ac.user == g.user:
documents.append(doc)
break
if not documents:
txt = 'No there are documents to revoke'
raise ValidationError(txt)
2021-11-10 17:59:44 +00:00
class ConfirmRevoke(Revoke):
pass
class ConfirmDocument(ActionWithMultipleDocuments):
__doc__ = m.Confirm.__doc__
2021-06-24 09:41:14 +00:00
action = NestedOn('Action', only_query='id')
@validates_schema
def validate_documents(self, data):
"""If there are one device than have one confirmation,
2022-03-18 11:07:22 +00:00
then remove the list this device of the list of devices of this action
"""
if data['documents'] == OrderedSet():
return
for doc in data['documents']:
if not doc.lot.trade:
return
data['action'] = doc.lot.trade
if not doc.actions:
continue
2021-11-22 11:01:12 +00:00
if not doc.trading == 'Need Confirmation':
2021-06-29 17:13:00 +00:00
txt = 'No there are documents to confirm'
raise ValidationError(txt)
class RevokeDocument(ActionWithMultipleDocuments):
__doc__ = m.RevokeDocument.__doc__
action = NestedOn('Action', only_query='id')
2021-06-24 09:41:14 +00:00
@validates_schema
def validate_documents(self, data):
"""Check if there are or no one before confirmation,
2022-03-18 11:07:22 +00:00
This is not checked in the view becouse the list of documents is inmutable
2021-06-24 09:41:14 +00:00
"""
if data['documents'] == OrderedSet():
2021-06-24 09:41:14 +00:00
return
for doc in data['documents']:
if not doc.lot.trade:
return
2021-06-24 09:41:14 +00:00
data['action'] = doc.lot.trade
2021-06-24 09:41:14 +00:00
if not doc.actions:
continue
2021-11-22 11:01:12 +00:00
if doc.trading not in ['Document Confirmed', 'Confirm']:
2021-06-29 17:13:00 +00:00
txt = 'No there are documents to revoke'
raise ValidationError(txt)
2021-06-24 09:41:14 +00:00
class ConfirmRevokeDocument(ActionWithMultipleDocuments):
2021-11-02 13:25:49 +00:00
__doc__ = m.ConfirmRevokeDocument.__doc__
2021-04-30 10:44:32 +00:00
action = NestedOn('Action', only_query='id')
2021-04-22 09:12:14 +00:00
@validates_schema
def validate_documents(self, data):
2021-06-24 09:41:14 +00:00
"""Check if there are or no one before confirmation,
2022-03-18 11:07:22 +00:00
This is not checked in the view becouse the list of documents is inmutable
2021-06-24 09:41:14 +00:00
"""
if data['documents'] == OrderedSet():
2021-06-24 09:41:14 +00:00
return
for doc in data['documents']:
if not doc.lot.trade:
return
2021-06-24 09:41:14 +00:00
if not doc.actions:
continue
2021-06-24 09:41:14 +00:00
2021-06-29 17:13:00 +00:00
if not doc.trading == 'Revoke':
txt = 'No there are documents with revoke for confirm'
raise ValidationError(txt)
2021-06-24 09:41:14 +00:00
2021-06-29 17:13:00 +00:00
data['action'] = doc.actions[-1]
2021-06-24 09:41:14 +00:00
2021-04-19 17:33:35 +00:00
class Trade(ActionWithMultipleDevices):
__doc__ = m.Trade.__doc__
2021-03-18 09:59:38 +00:00
date = DateTime(data_key='date', required=False)
price = Float(required=False, data_key='price')
2021-06-01 13:59:54 +00:00
user_to_email = SanitizedStr(
validate=Length(max=STR_SIZE),
data_key='userToEmail',
2021-06-01 13:59:54 +00:00
missing='',
2022-03-18 11:07:22 +00:00
required=False,
2021-06-01 13:59:54 +00:00
)
user_to = NestedOn(s_user.User, dump_only=True, data_key='userTo')
user_from_email = SanitizedStr(
validate=Length(max=STR_SIZE),
data_key='userFromEmail',
2021-06-01 13:59:54 +00:00
missing='',
2022-03-18 11:07:22 +00:00
required=False,
2021-06-01 13:59:54 +00:00
)
user_from = NestedOn(s_user.User, dump_only=True, data_key='userFrom')
2021-04-07 08:49:28 +00:00
code = SanitizedStr(validate=Length(max=STR_SIZE), data_key='code', required=False)
confirm = Boolean(
data_key='confirms',
missing=True,
2022-03-18 11:07:22 +00:00
description="""If you need confirmation of the user you need actevate this field""",
)
2022-03-18 11:07:22 +00:00
lot = NestedOn('Lot', many=False, required=True, only_query='id')
2021-03-18 15:36:19 +00:00
2021-10-01 11:30:39 +00:00
@pre_load
def adding_devices(self, data: dict):
2023-05-26 15:11:47 +00:00
if 'devices' not in data.keys():
2021-10-01 11:30:39 +00:00
data['devices'] = []
2021-04-14 11:02:39 +00:00
@validates_schema
def validate_lot(self, data: dict):
2023-05-26 15:11:47 +00:00
if g.user.email not in [data['user_from_email'], data['user_to_email']]:
txt = "you need to be one of the users of involved in the Trade"
raise ValidationError(txt)
for dev in data['lot'].devices:
if not dev.owner == g.user:
txt = "you need to be the owner of the devices for to do a trade"
raise ValidationError(txt)
if not data['lot'].owner == g.user:
txt = "you need to be the owner of the lot for to do a trade"
raise ValidationError(txt)
2021-05-19 11:59:59 +00:00
for doc in data['lot'].documents:
if not doc.owner == g.user:
txt = "you need to be the owner of the documents for to do a trade"
raise ValidationError(txt)
2021-04-14 11:02:39 +00:00
data['devices'] = data['lot'].devices
2021-05-19 11:59:59 +00:00
data['documents'] = data['lot'].documents
2021-04-14 11:02:39 +00:00
2021-03-18 15:36:19 +00:00
@validates_schema
2021-06-01 13:59:54 +00:00
def validate_user_to_email(self, data: dict):
2021-04-07 08:49:28 +00:00
"""
2021-04-08 17:11:27 +00:00
- if user_to exist
2021-04-07 08:49:28 +00:00
* confirmation
* without confirmation
2021-04-08 17:11:27 +00:00
- if user_to don't exist
2021-04-07 08:49:28 +00:00
* without confirmation
2021-04-08 17:11:27 +00:00
2021-04-07 08:49:28 +00:00
"""
2021-06-01 13:59:54 +00:00
if data['user_to_email']:
user_to = User.query.filter_by(email=data['user_to_email']).one()
2021-04-08 17:11:27 +00:00
data['user_to'] = user_to
2021-04-07 08:49:28 +00:00
else:
data['confirm'] = False
2021-03-18 15:36:19 +00:00
@validates_schema
2021-06-01 13:59:54 +00:00
def validate_user_from_email(self, data: dict):
2021-04-07 08:49:28 +00:00
"""
2021-04-08 17:11:27 +00:00
- if user_from exist
2021-04-07 08:49:28 +00:00
* confirmation
* without confirmation
2021-04-08 17:11:27 +00:00
- if user_from don't exist
2021-04-07 08:49:28 +00:00
* without confirmation
2021-04-08 17:11:27 +00:00
2021-04-07 08:49:28 +00:00
"""
2021-06-01 13:59:54 +00:00
if data['user_from_email']:
user_from = User.query.filter_by(email=data['user_from_email']).one()
2021-04-08 17:11:27 +00:00
data['user_from'] = user_from
2021-06-07 13:45:04 +00:00
@validates_schema
def validate_email_users(self, data: dict):
"""We need at least one user"""
2021-06-16 08:53:18 +00:00
confirm = data['confirm']
user_from = data['user_from_email']
user_to = data['user_to_email']
if not (user_from or user_to):
2021-06-07 13:45:04 +00:00
txt = "you need one user from or user to for to do a trade"
raise ValidationError(txt)
2021-06-16 08:53:18 +00:00
if confirm and not (user_from and user_to):
txt = "you need one user for to do a trade"
raise ValidationError(txt)
2023-05-26 15:11:47 +00:00
if g.user.email not in [user_from, user_to]:
2021-06-07 13:45:04 +00:00
txt = "you need to be one of participate of the action"
raise ValidationError(txt)
2021-04-07 08:49:28 +00:00
@validates_schema
def validate_code(self, data: dict):
"""If the user not exist, you need a code to be able to do the traceability"""
2021-06-01 13:59:54 +00:00
if data['user_from_email'] and data['user_to_email']:
2021-06-07 13:45:04 +00:00
data['confirm'] = True
2021-04-07 08:49:28 +00:00
return
2021-06-07 13:48:55 +00:00
if not data['confirm'] and not data.get('code'):
2021-04-07 08:49:28 +00:00
txt = "you need a code to be able to do the traceability"
raise ValidationError(txt)
2021-03-18 09:59:38 +00:00
2021-06-16 08:53:18 +00:00
if not data['confirm']:
data['code'] = data['code'].replace('@', '_')
2021-06-07 14:04:33 +00:00
2021-03-18 09:59:38 +00:00
2019-12-12 20:17:35 +00:00
class InitTransfer(Trade):
__doc__ = m.InitTransfer.__doc__
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__
2019-12-21 15:41:23 +00:00
class TransferOwnershipBlockchain(Trade):
__doc__ = m.TransferOwnershipBlockchain.__doc__
2021-11-22 11:01:12 +00:00
class Delete(ActionWithMultipleDevicesCheckingOwner):
2021-10-05 09:56:19 +00:00
__doc__ = m.Delete.__doc__
2021-10-05 10:17:07 +00:00
@post_load
def deactivate_device(self, data):
for dev in data['devices']:
if dev.last_action_trading is None:
dev.active = False
2022-07-20 10:39:21 +00:00
if dev.binding:
dev.binding.device.active = False
if dev.placeholder:
dev.placeholder.device.active = False
2021-10-05 10:17:07 +00:00
2021-10-05 09:56:19 +00:00
class Migrate(ActionWithMultipleDevices):
__doc__ = m.Migrate.__doc__
other = URL()
class MigrateTo(Migrate):
__doc__ = m.MigrateTo.__doc__
class MigrateFrom(Migrate):
__doc__ = m.MigrateFrom.__doc__
2021-09-06 10:45:47 +00:00
class MoveOnDocument(Action):
__doc__ = m.MoveOnDocument.__doc__
2021-09-06 10:45:47 +00:00
weight = Integer()
container_from = NestedOn('TradeDocument', only_query='id')
container_to = NestedOn('TradeDocument', only_query='id')
@pre_load
def extract_container(self, data):
id_hash = data['container_to']
docs = TradeDocument.query.filter_by(owner=g.user, file_hash=id_hash).all()
if len(docs) > 1:
txt = 'This document it is associated in more than one lot'
raise ValidationError(txt)
if len(docs) < 1:
txt = 'This document not exist'
raise ValidationError(txt)
data['container_to'] = docs[0].id
@post_load
def adding_documents(self, data):
"""Adding action in the 2 TradeDocuments"""
docs = OrderedSet()
docs.add(data['container_to'])
docs.add(data['container_from'])
data['documents'] = docs