class Computer(Device): __doc__ = m.Computer.__doc__ components = NestedOn( 'Component', many=True, dump_only=True, collection_class=OrderedSet, description='The components that are inside this computer.') chassis = EnumField(enums.ComputerChassis, required=True, description=m.Computer.chassis.comment) ram_size = Integer(dump_only=True, data_key='ramSize', description=m.Computer.ram_size.__doc__) data_storage_size = Integer( dump_only=True, data_key='dataStorageSize', description=m.Computer.data_storage_size.__doc__) processor_model = Str(dump_only=True, data_key='processorModel', description=m.Computer.processor_model.__doc__) graphic_card_model = Str(dump_only=True, data_key='graphicCardModel', description=m.Computer.graphic_card_model.__doc__) network_speeds = List(Integer(dump_only=True), dump_only=True, data_key='networkSpeeds', description=m.Computer.network_speeds.__doc__) privacy = NestedOn('Event', many=True, dump_only=True, collection_class=set, description=m.Computer.privacy.__doc__)
class DataStorage(Component): size = Integer(validate=Range(0, 10**8), unit=UnitCodes.mbyte, description='The size of the hard-drive in MB.') erasure = NestedOn('EraseBasic', load_only=True) tests = NestedOn('TestHardDrive', many=True, load_only=True) benchmarks = NestedOn('BenchmarkHardDrive', load_only=True, many=True)
class Lot(Thing): id = f.UUID(dump_only=True) name = SanitizedStr(validate=f.validate.Length(max=STR_SIZE), required=True) description = SanitizedStr(description=m.Lot.description.comment) closed = f.Boolean(missing=False, description=m.Lot.closed.comment) devices = NestedOn(s_device.Device, many=True, dump_only=True) children = NestedOn('Lot', many=True, dump_only=True) parents = NestedOn('Lot', many=True, dump_only=True) url = URL(dump_only=True, description=m.Lot.url.__doc__)
class Tag(Thing): id = SanitizedStr(lower=True, description=m.Tag.id.comment, validator=without_slash, required=True) provider = URL(description=m.Tag.provider.comment, validator=without_slash) device = NestedOn(Device, dump_only=True) org = NestedOn(Organization, collection_class=OrderedSet, only_query='id') secondary = SanitizedStr(lower=True, description=m.Tag.secondary.comment) printable = Boolean(dump_only=True, decsription=m.Tag.printable.__doc__) url = URL(dump_only=True, description=m.Tag.url.__doc__)
class Trade(EventWithMultipleDevices): __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 Device(Thing): # todo id is dump_only except when in Snapshot id = Integer(description='The identifier of the device for this database.') hid = Str( dump_only=True, description='The Hardware ID is the unique ID traceability systems ' 'use to ID a device globally.') tags = NestedOn('Tag', many=True, collection_class=OrderedSet) model = Str(validate=Length(max=STR_BIG_SIZE)) manufacturer = Str(validate=Length(max=STR_SIZE)) serial_number = Str(data_key='serialNumber') product_id = Str(data_key='productId') weight = Float(validate=Range(0.1, 3), unit=UnitCodes.kgm, description='The weight of the device in Kgm.') width = Float(validate=Range(0.1, 3), unit=UnitCodes.m, description='The width of the device in meters.') height = Float(validate=Range(0.1, 3), unit=UnitCodes.m, description='The height of the device in meters.') events = NestedOn('Event', many=True, dump_only=True) events_one = NestedOn('Event', many=True, load_only=True, collection_class=OrderedSet) @pre_load def from_events_to_events_one(self, data: dict): """ Not an elegant way of allowing submitting events to a device (in the context of Snapshots) without creating an ``events`` field at the model (which is not possible). :param data: :return: """ # Note that it is secure to allow uploading events_one # as the only time an user can send a device object is # in snapshots. data['events_one'] = data.pop('events', []) return data @post_load def validate_snapshot_events(self, data): """Validates that only snapshot-related events can be uploaded.""" from ereuse_devicehub.resources.event.models import EraseBasic, Test, Rate, Install, \ Benchmark for event in data['events_one']: if not isinstance(event, (Install, EraseBasic, Rate, Test, Benchmark)): raise ValidationError('You cannot upload {}'.format(event), field_names=['events'])
class Event(Thing): id = UUID(dump_only=True) name = String(default='', validate=Length(STR_BIG_SIZE), description=m.Event.name.comment) date = DateTime('iso', description=m.Event.date.comment) error = Boolean(default=False, description=m.Event.error.comment) incidence = Boolean(default=False, description=m.Event.incidence.comment) snapshot = NestedOn('Snapshot', dump_only=True) components = NestedOn(Component, dump_only=True, many=True) description = String(default='', description=m.Event.description.comment) author = NestedOn(User, dump_only=True, exclude=('token', )) closed = Boolean(missing=True, description=m.Event.closed.comment)
class Allocate(EventWithMultipleDevices): __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 TradeDocument(Thing): __doc__ = m.TradeDocument.__doc__ id = Integer(description=m.TradeDocument.id.comment, dump_only=True) date = DateTime(required=False, description=m.TradeDocument.date.comment) id_document = SanitizedStr(data_key='documentId', default='', description=m.TradeDocument.id_document.comment) description = SanitizedStr(default='', description=m.TradeDocument.description.comment, validate=validate.Length(max=500)) file_name = SanitizedStr(data_key='filename', default='', description=m.TradeDocument.file_name.comment, validate=validate.Length(max=100)) file_hash = SanitizedStr(data_key='hash', default='', description=m.TradeDocument.file_hash.comment, validate=validate.Length(max=64)) url = URL(description=m.TradeDocument.url.comment) lot = NestedOn('Lot', only_query='id', description=m.TradeDocument.lot.__doc__) trading = SanitizedStr(dump_only=True, description='') weight = Float(required=False, description=m.TradeDocument.weight.comment) total_weight = Float(required=False, description=m.TradeDocument.weight.comment)
class Tag(Thing): id = String(description='The ID of the tag.', validator=lambda x: '/' not in x, required=True) provider = URL(description='The provider URL.') device = NestedOn(Device, description='The device linked to this tag.') org = String(description='The organization that issued the tag.')
class Snapshot(EventWithOneDevice): """ The Snapshot updates the state of the device with information about its components and events performed at them. See docs for more info. """ uuid = UUID(required=True) software = EnumField( SnapshotSoftware, required=True, description='The software that generated this Snapshot.') version = Version(required=True, description='The version of the software.') events = NestedOn(Event, many=True, dump_only=True) 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.') device = NestedOn(Device) elapsed = TimeDelta(precision=TimeDelta.SECONDS, required=True) 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.') @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 algorithm_version is ' '{}'.format(app.config['MIN_WORKBENCH']), field_names=['version']) @validates_schema def validate_components_only_workbench(self, data: dict): if data['software'] != SnapshotSoftware.Workbench: if data['components'] is not None: raise ValidationError('Only Workbench can add component info', field_names=['components'])
class EraseBasic(EventWithOneDevice): start_time = DateTime(required=True, data_key='startTime') end_time = DateTime(required=True, data_key='endTime') secure_random_steps = Integer(validate=Range(min=0), required=True, data_key='secureRandomSteps') clean_with_zeros = Boolean(required=True, data_key='cleanWithZeros') steps = NestedOn('Step', many=True, required=True)
class DataStorage(Component): __doc__ = m.DataStorage.__doc__ size = Integer(validate=Range(0, 10**8), unit=UnitCodes.mbyte, description=m.DataStorage.size.comment) interface = EnumField(enums.DataStorageInterface) privacy = NestedOn('Event', dump_only=True)
class Computer(Device): __doc__ = m.Computer.__doc__ # TODO TimeOut 1. Comment components if there are time out. components = NestedOn( 'Component', many=True, dump_only=True, collection_class=OrderedSet, description='The components that are inside this computer.') chassis = EnumField(enums.ComputerChassis, description=m.Computer.chassis.comment) ram_size = Integer(dump_only=True, data_key='ramSize', description=m.Computer.ram_size.__doc__) data_storage_size = Integer( dump_only=True, data_key='dataStorageSize', description=m.Computer.data_storage_size.__doc__) processor_model = Str(dump_only=True, data_key='processorModel', description=m.Computer.processor_model.__doc__) graphic_card_model = Str(dump_only=True, data_key='graphicCardModel', description=m.Computer.graphic_card_model.__doc__) network_speeds = List(Integer(dump_only=True), dump_only=True, data_key='networkSpeeds', description=m.Computer.network_speeds.__doc__) privacy = NestedOn('Action', many=True, dump_only=True, collection_class=set, description=m.Computer.privacy.__doc__) amount = Integer(validate=f.validate.Range(min=0, max=100), description=m.Computer.amount.__doc__) # author_id = NestedOn(s_user.User, only_query='author_id') owner_id = UUID(data_key='ownerID') transfer_state = EnumField(enums.TransferState, description=m.Computer.transfer_state.comment) receiver_id = UUID(data_key='receiverID')
class Event(Thing): __doc__ = m.Event.__doc__ id = UUID(dump_only=True) name = SanitizedStr(default='', validate=Length(max=STR_BIG_SIZE), description=m.Event.name.comment) closed = Boolean(missing=True, description=m.Event.closed.comment) severity = EnumField(Severity, description=m.Event.severity.comment) description = SanitizedStr(default='', description=m.Event.description.comment) 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(s_agent.Agent, description=m.Event.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.Event.parent_id.comment) url = URL(dump_only=True, description=m.Event.url.__doc__)
class User(Thing): id = UUID(dump_only=True) email = Email(required=True) password = SanitizedStr(load_only=True, required=True) individuals = NestedOn(Individual, many=True, dump_only=True) name = SanitizedStr() token = String( dump_only=True, description= 'Use this token in an Authorization header to access the app.' 'The token can change overtime.') inventories = NestedOn(Inventory, many=True, dump_only=True) code = String(dump_only=True, description='Code of inactive accounts') def __init__(self, only=None, exclude=('token', ), prefix='', many=False, context=None, load_only=(), dump_only=(), partial=False): """Instantiates the User. By default we exclude token from both load/dump so they are not taken / set in normal usage by mistake. """ super().__init__(only, exclude, prefix, many, context, load_only, dump_only, partial) @post_dump def base64encode_token(self, data: dict): """Encodes the token to base64 so clients don't have to.""" if 'token' in data: # In many cases we don't dump the token (ex. relationships) # Framework needs ':' at the end data['token'] = auth.Auth.encode(data['token']) return data
class Deliverynote(Thing): id = f.UUID(dump_only=True) document_id = SanitizedStr(validate=f.validate.Length(max=STR_SIZE), required=True, data_key='documentID') creator = NestedOn(s_user.User, dump_only=True) supplier_email = SanitizedStr(validate=f.validate.Length(max=STR_SIZE), load_only=True, required=True, data_key='supplierEmail') supplier = NestedOn(s_user.User, dump_only=True) receiver = NestedOn(s_user.User, dump_only=True) date = f.DateTime('iso', required=True) amount = f.Integer(validate=f.validate.Range(min=0, max=100), description=m.Deliverynote.amount.__doc__) expected_devices = f.List(f.Dict, required=True, data_key='expectedDevices') transferred_devices = f.List(f.Integer(), required=False, data_key='transferredDevices') transfer_state = EnumField(TransferState, description=m.Lot.transfer_state.comment)
class AggregateRate(Rate): __doc__ = m.AggregateRate.__doc__ workbench = NestedOn(WorkbenchRate, dump_only=True, description=m.AggregateRate.workbench_id.comment) manual = NestedOn(ManualRate, dump_only=True, description=m.AggregateRate.manual_id.comment) processor = Float(dump_only=True) ram = Float(dump_only=True) data_storage = Float(dump_only=True) graphic_card = Float(dump_only=True) bios = EnumField(Bios, dump_only=True) bios_range = EnumField(Bios, description=m.WorkbenchRate.bios_range.comment, data_key='biosRange') appearance_range = EnumField( AppearanceRange, required=True, data_key='appearanceRange', description=m.ManualRate.appearance_range.comment) functionality_range = EnumField( FunctionalityRange, required=True, data_key='functionalityRange', description=m.ManualRate.functionality_range.comment) labelling = Boolean(description=m.ManualRate.labelling.comment) 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(EventWithOneDevice): __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) software = EnumField(PriceSoftware, dump_only=True, description=m.Price.software.comment) version = Version(dump_only=True, description=m.Price.version.comment) rating = NestedOn(AggregateRate, dump_only=True, description=m.Price.rating_id.comment)
class Device(Thing): __doc__ = m.Device.__doc__ id = Integer(description=m.Device.id.comment, dump_only=True) hid = SanitizedStr(lower=True, dump_only=True, description=m.Device.hid.comment) tags = NestedOn('Tag', many=True, collection_class=OrderedSet, description='A set of tags that identify the device.') model = SanitizedStr(lower=True, validate=Length(max=STR_BIG_SIZE), description=m.Device.model.comment) manufacturer = SanitizedStr(lower=True, validate=Length(max=STR_SIZE), description=m.Device.manufacturer.comment) serial_number = SanitizedStr(lower=True, validate=Length(max=STR_BIG_SIZE), data_key='serialNumber') brand = SanitizedStr(validate=Length(max=STR_BIG_SIZE), description=m.Device.brand.comment) generation = Integer(validate=Range(1, 100), description=m.Device.generation.comment) weight = Float(validate=Range(0.1, 5), unit=UnitCodes.kgm, description=m.Device.weight.comment) width = Float(validate=Range(0.1, 5), unit=UnitCodes.m, description=m.Device.width.comment) height = Float(validate=Range(0.1, 5), unit=UnitCodes.m, description=m.Device.height.comment) depth = Float(validate=Range(0.1, 5), unit=UnitCodes.m, description=m.Device.depth.comment) events = NestedOn('Event', many=True, dump_only=True, description=m.Device.events.__doc__) events_one = NestedOn('Event', many=True, load_only=True, collection_class=OrderedSet) problems = NestedOn('Event', many=True, dump_only=True, description=m.Device.problems.__doc__) url = URL(dump_only=True, description=m.Device.url.__doc__) lots = NestedOn( 'Lot', many=True, dump_only=True, description='The lots where this device is directly under.') rate = NestedOn('AggregateRate', dump_only=True, description=m.Device.rate.__doc__) price = NestedOn('Price', dump_only=True, description=m.Device.price.__doc__) trading = EnumField(states.Trading, dump_only=True, description=m.Device.trading.__doc__) physical = EnumField(states.Physical, dump_only=True, description=m.Device.physical.__doc__) physical_possessor = NestedOn('Agent', dump_only=True, data_key='physicalPossessor') production_date = DateTime('iso', description=m.Device.updated.comment, data_key='productionDate') working = NestedOn('Event', many=True, dump_only=True, description=m.Device.working.__doc__) @pre_load def from_events_to_events_one(self, data: dict): """ Not an elegant way of allowing submitting events to a device (in the context of Snapshots) without creating an ``events`` field at the model (which is not possible). :param data: :return: """ # Note that it is secure to allow uploading events_one # as the only time an user can send a device object is # in snapshots. data['events_one'] = data.pop('events', []) return data @post_load def validate_snapshot_events(self, data): """Validates that only snapshot-related events can be uploaded.""" from ereuse_devicehub.resources.event.models import EraseBasic, Test, Rate, Install, \ Benchmark for event in data['events_one']: if not isinstance(event, (Install, EraseBasic, Rate, Test, Benchmark)): raise ValidationError('You cannot upload {}'.format(event), field_names=['events'])
class EventWithOneDevice(Event): device = NestedOn(Device)
class Component(Device): parent = NestedOn(Device, dump_only=True)
class EventWithMultipleDevices(Event): devices = NestedOn(Device, many=True)
class Allocate(EventWithMultipleDevices): to = NestedOn(User, description='The user the devices are allocated to.') organization = String( validate=Length(STR_SIZE), description='The organization where the user was when this happened.')
class EraseBasic(EventWithOneDevice): __doc__ = m.EraseBasic.__doc__ steps = NestedOn('Step', many=True) standards = f.List(EnumField(enums.ErasureStandards), dump_only=True) certificate = URL(dump_only=True)
class EventWithMultipleDevices(Event): __doc__ = m.EventWithMultipleDevices.__doc__ devices = NestedOn(s_device.Device, many=True, only_query='id', collection_class=OrderedSet)
class Component(Device): __doc__ = m.Component.__doc__ parent = NestedOn(Device, dump_only=True)
class Computer(Device): components = NestedOn('Component', many=True, dump_only=True, collection_class=OrderedSet) pass
class EventWithOneDevice(Event): __doc__ = m.EventWithOneDevice.__doc__ device = NestedOn(s_device.Device, only_query='id')
class Snapshot(EventWithOneDevice): __doc__ = m.Snapshot.__doc__ """ The Snapshot updates the state of the device with information about its components and events 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.') events = NestedOn(Event, many=True, dump_only=True) 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.') 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'])