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