add system_uuid to sync run function
This commit is contained in:
parent
f86df91862
commit
686d0a9307
|
@ -13,10 +13,14 @@ from teal.marshmallow import ValidationError
|
|||
|
||||
from ereuse_devicehub.db import db
|
||||
from ereuse_devicehub.resources.action.models import Remove
|
||||
from ereuse_devicehub.resources.device.models import Component, Computer, Device, DataStorage
|
||||
from ereuse_devicehub.resources.device.models import (
|
||||
Component,
|
||||
Computer,
|
||||
DataStorage,
|
||||
Device,
|
||||
)
|
||||
from ereuse_devicehub.resources.tag.model import Tag
|
||||
|
||||
|
||||
DEVICES_ALLOW_DUPLICITY = [
|
||||
'RamModule',
|
||||
'Display',
|
||||
|
@ -26,12 +30,13 @@ DEVICES_ALLOW_DUPLICITY = [
|
|||
'GraphicCard',
|
||||
]
|
||||
|
||||
|
||||
class Sync:
|
||||
"""Synchronizes the device and components with the database."""
|
||||
|
||||
def run(self,
|
||||
device: Device,
|
||||
components: Iterable[Component] or None) -> (Device, OrderedSet):
|
||||
def run(
|
||||
self, device: Device, components: Iterable[Component] or None
|
||||
) -> (Device, OrderedSet):
|
||||
"""Synchronizes the device and components with the database.
|
||||
|
||||
Identifies if the device and components exist in the database
|
||||
|
@ -72,9 +77,9 @@ class Sync:
|
|||
blacklist = set() # type: Set[int]
|
||||
not_new_components = set()
|
||||
for component in components:
|
||||
db_component, is_new = self.execute_register_component(component,
|
||||
blacklist,
|
||||
parent=db_device)
|
||||
db_component, is_new = self.execute_register_component(
|
||||
component, blacklist, parent=db_device
|
||||
)
|
||||
db_components.add(db_component)
|
||||
if not is_new:
|
||||
not_new_components.add(db_component)
|
||||
|
@ -83,10 +88,9 @@ class Sync:
|
|||
db_device.components = db_components
|
||||
return db_device, actions
|
||||
|
||||
def execute_register_component(self,
|
||||
component: Component,
|
||||
blacklist: Set[int],
|
||||
parent: Computer):
|
||||
def execute_register_component(
|
||||
self, component: Component, blacklist: Set[int], parent: Computer
|
||||
):
|
||||
"""Synchronizes one component to the DB.
|
||||
|
||||
This method is a specialization of :meth:`.execute_register`
|
||||
|
@ -118,9 +122,12 @@ class Sync:
|
|||
# if not, then continue with the traditional behaviour
|
||||
try:
|
||||
if component.hid:
|
||||
db_component = Device.query.filter_by(hid=component.hid, owner_id=g.user.id).one()
|
||||
assert isinstance(db_component, Device), \
|
||||
'{} must be a component'.format(db_component)
|
||||
db_component = Device.query.filter_by(
|
||||
hid=component.hid, owner_id=g.user.id
|
||||
).one()
|
||||
assert isinstance(
|
||||
db_component, Device
|
||||
), '{} must be a component'.format(db_component)
|
||||
else:
|
||||
# Is there a component similar to ours?
|
||||
db_component = component.similar_one(parent, blacklist)
|
||||
|
@ -166,15 +173,31 @@ class Sync:
|
|||
:return: The synced device from the db with the tags linked.
|
||||
"""
|
||||
assert inspect(device).transient, 'Device cannot be already synced from DB'
|
||||
assert all(inspect(tag).transient for tag in device.tags), 'Tags cannot be synced from DB'
|
||||
assert all(
|
||||
inspect(tag).transient for tag in device.tags
|
||||
), 'Tags cannot be synced from DB'
|
||||
db_device = None
|
||||
if device.hid:
|
||||
if isinstance(db_device, Computer):
|
||||
if db_device.uuid:
|
||||
db_device = Computer.query.filter_by(
|
||||
uuid=device.uuid, owner_id=g.user.id, active=True
|
||||
).one()
|
||||
elif device.hid:
|
||||
with suppress(ResourceNotFound):
|
||||
db_device = Device.query.filter_by(hid=device.hid, owner_id=g.user.id, active=True).one()
|
||||
db_device = Device.query.filter_by(
|
||||
hid=device.hid, owner_id=g.user.id, active=True
|
||||
).one()
|
||||
elif device.hid:
|
||||
with suppress(ResourceNotFound):
|
||||
db_device = Device.query.filter_by(
|
||||
hid=device.hid, owner_id=g.user.id, active=True
|
||||
).one()
|
||||
if db_device and db_device.allocated:
|
||||
raise ResourceNotFound('device is actually allocated {}'.format(device))
|
||||
try:
|
||||
tags = {Tag.from_an_id(tag.id).one() for tag in device.tags} # type: Set[Tag]
|
||||
tags = {
|
||||
Tag.from_an_id(tag.id).one() for tag in device.tags
|
||||
} # type: Set[Tag]
|
||||
except ResourceNotFound:
|
||||
raise ResourceNotFound('tag you are linking to device {}'.format(device))
|
||||
linked_tags = {tag for tag in tags if tag.device_id} # type: Set[Tag]
|
||||
|
@ -182,16 +205,22 @@ class Sync:
|
|||
sample_tag = next(iter(linked_tags))
|
||||
for tag in linked_tags:
|
||||
if tag.device_id != sample_tag.device_id:
|
||||
raise MismatchBetweenTags(tag, sample_tag) # Tags linked to different devices
|
||||
raise MismatchBetweenTags(
|
||||
tag, sample_tag
|
||||
) # Tags linked to different devices
|
||||
if db_device: # Device from hid
|
||||
if sample_tag.device_id != db_device.id: # Device from hid != device from tags
|
||||
if (
|
||||
sample_tag.device_id != db_device.id
|
||||
): # Device from hid != device from tags
|
||||
raise MismatchBetweenTagsAndHid(db_device.id, db_device.hid)
|
||||
else: # There was no device from hid
|
||||
if sample_tag.device.physical_properties != device.physical_properties:
|
||||
# Incoming physical props of device != props from tag's device
|
||||
# which means that the devices are not the same
|
||||
raise MismatchBetweenProperties(sample_tag.device.physical_properties,
|
||||
device.physical_properties)
|
||||
raise MismatchBetweenProperties(
|
||||
sample_tag.device.physical_properties,
|
||||
device.physical_properties,
|
||||
)
|
||||
db_device = sample_tag.device
|
||||
if db_device: # Device from hid or tags
|
||||
self.merge(device, db_device)
|
||||
|
@ -199,17 +228,21 @@ class Sync:
|
|||
device.tags.clear() # We don't want to add the transient dummy tags
|
||||
db.session.add(device)
|
||||
db_device = device
|
||||
db_device.tags |= tags # Union of tags the device had plus the (potentially) new ones
|
||||
db_device.tags |= (
|
||||
tags # Union of tags the device had plus the (potentially) new ones
|
||||
)
|
||||
try:
|
||||
db.session.flush()
|
||||
except IntegrityError as e:
|
||||
# Manage 'one tag per organization' unique constraint
|
||||
if 'One tag per organization' in e.args[0]:
|
||||
# todo test for this
|
||||
id = int(e.args[0][135:e.args[0].index(',', 135)])
|
||||
raise ValidationError('The device is already linked to tag {} '
|
||||
id = int(e.args[0][135 : e.args[0].index(',', 135)])
|
||||
raise ValidationError(
|
||||
'The device is already linked to tag {} '
|
||||
'from the same organization.'.format(id),
|
||||
field_names=['device.tags'])
|
||||
field_names=['device.tags'],
|
||||
)
|
||||
else:
|
||||
raise
|
||||
assert db_device is not None
|
||||
|
@ -229,8 +262,7 @@ class Sync:
|
|||
setattr(db_device, field_name, value)
|
||||
|
||||
@staticmethod
|
||||
def add_remove(device: Computer,
|
||||
components: Set[Component]) -> OrderedSet:
|
||||
def add_remove(device: Computer, components: Set[Component]) -> OrderedSet:
|
||||
"""Generates the Add and Remove actions (but doesn't add them to
|
||||
session).
|
||||
|
||||
|
@ -251,9 +283,13 @@ class Sync:
|
|||
if adding:
|
||||
# For the components we are adding, let's remove them from their old parents
|
||||
def g_parent(component: Component) -> Device:
|
||||
return component.parent or Device(id=0) # Computer with id 0 is our Identity
|
||||
return component.parent or Device(
|
||||
id=0
|
||||
) # Computer with id 0 is our Identity
|
||||
|
||||
for parent, _components in groupby(sorted(adding, key=g_parent), key=g_parent):
|
||||
for parent, _components in groupby(
|
||||
sorted(adding, key=g_parent), key=g_parent
|
||||
):
|
||||
set_components = OrderedSet(_components)
|
||||
check_owners = (x.owner_id == g.user.id for x in set_components)
|
||||
# Is not Computer Identity and all components have the correct owner
|
||||
|
@ -263,21 +299,18 @@ class Sync:
|
|||
|
||||
|
||||
class MismatchBetweenTags(ValidationError):
|
||||
def __init__(self,
|
||||
tag: Tag,
|
||||
other_tag: Tag,
|
||||
field_names={'device.tags'}):
|
||||
message = '{!r} and {!r} are linked to different devices.'.format(tag, other_tag)
|
||||
def __init__(self, tag: Tag, other_tag: Tag, field_names={'device.tags'}):
|
||||
message = '{!r} and {!r} are linked to different devices.'.format(
|
||||
tag, other_tag
|
||||
)
|
||||
super().__init__(message, field_names)
|
||||
|
||||
|
||||
class MismatchBetweenTagsAndHid(ValidationError):
|
||||
def __init__(self,
|
||||
device_id: int,
|
||||
hid: str,
|
||||
field_names={'device.hid'}):
|
||||
message = 'Tags are linked to device {} but hid refers to device {}.'.format(device_id,
|
||||
hid)
|
||||
def __init__(self, device_id: int, hid: str, field_names={'device.hid'}):
|
||||
message = 'Tags are linked to device {} but hid refers to device {}.'.format(
|
||||
device_id, hid
|
||||
)
|
||||
super().__init__(message, field_names)
|
||||
|
||||
|
||||
|
@ -285,6 +318,8 @@ class MismatchBetweenProperties(ValidationError):
|
|||
def __init__(self, props1, props2, field_names={'device'}):
|
||||
message = 'The device from the tag and the passed-in differ the following way:'
|
||||
message += '\n'.join(
|
||||
difflib.ndiff(yaml.dump(props1).splitlines(), yaml.dump(props2).splitlines())
|
||||
difflib.ndiff(
|
||||
yaml.dump(props1).splitlines(), yaml.dump(props2).splitlines()
|
||||
)
|
||||
)
|
||||
super().__init__(message, field_names)
|
||||
|
|
Reference in a new issue