Merge two devices (#40)
* Add authenticated view to merge two devices * Assign actions of argument device to device * Merge device components * Sloppy implementation to test * Correctly update base device * Correctly append actions
This commit is contained in:
parent
9ad217fc5b
commit
d2d48280cb
|
@ -4,7 +4,7 @@ from teal.resource import Converters, Resource
|
|||
|
||||
from ereuse_devicehub.resources.device import schemas
|
||||
from ereuse_devicehub.resources.device.models import Manufacturer
|
||||
from ereuse_devicehub.resources.device.views import DeviceView, ManufacturerView
|
||||
from ereuse_devicehub.resources.device.views import DeviceView, DeviceMergeView, ManufacturerView
|
||||
|
||||
|
||||
class DeviceDef(Resource):
|
||||
|
@ -26,6 +26,13 @@ class DeviceDef(Resource):
|
|||
super().__init__(app, import_name, static_folder, static_url_path, template_folder,
|
||||
url_prefix, subdomain, url_defaults, root_path, cli_commands)
|
||||
|
||||
device_merge = DeviceMergeView.as_view('merge-devices', definition=self, auth=app.auth)
|
||||
if self.AUTH:
|
||||
device_merge = app.auth.requires_auth(device_merge)
|
||||
self.add_url_rule('/<{}:{}>/merge/'.format(self.ID_CONVERTER.value, self.ID_NAME),
|
||||
view_func=device_merge,
|
||||
methods={'POST'})
|
||||
|
||||
|
||||
class ComputerDef(DeviceDef):
|
||||
VIEW = None
|
||||
|
|
|
@ -1,10 +1,13 @@
|
|||
import datetime
|
||||
import uuid
|
||||
from itertools import filterfalse
|
||||
|
||||
import marshmallow
|
||||
from flask import current_app as app, render_template, request, Response
|
||||
from flask.json import jsonify
|
||||
from flask_sqlalchemy import Pagination
|
||||
from marshmallow import fields, fields as f, validate as v, ValidationError
|
||||
from marshmallow import fields, fields as f, validate as v, ValidationError, \
|
||||
Schema as MarshmallowSchema
|
||||
from teal import query
|
||||
from teal.cache import cache
|
||||
from teal.resource import View
|
||||
|
@ -19,6 +22,7 @@ from ereuse_devicehub.resources.device.models import Device, Manufacturer, Compu
|
|||
from ereuse_devicehub.resources.device.search import DeviceSearch
|
||||
from ereuse_devicehub.resources.lot.models import LotDeviceDescendants
|
||||
from ereuse_devicehub.resources.tag.model import Tag
|
||||
from ereuse_devicehub.resources.enums import SnapshotSoftware
|
||||
|
||||
|
||||
class OfType(f.Str):
|
||||
|
@ -152,6 +156,67 @@ class DeviceView(View):
|
|||
return query.filter(*args['filter']).order_by(*args['sort'])
|
||||
|
||||
|
||||
class DeviceMergeView(View):
|
||||
|
||||
"""View for merging two devices
|
||||
Ex. ``device/<id>/merge/id=X``.
|
||||
"""
|
||||
class FindArgs(MarshmallowSchema):
|
||||
id = fields.Integer()
|
||||
|
||||
def get_merge_id(self) -> uuid.UUID:
|
||||
args = self.QUERY_PARSER.parse(self.find_args, request, locations=('querystring',))
|
||||
return args['id']
|
||||
|
||||
def post(self, id: uuid.UUID):
|
||||
device = Device.query.filter_by(id=id).one()
|
||||
with_device = Device.query.filter_by(id=self.get_merge_id()).one()
|
||||
self.merge_devices(device, with_device)
|
||||
|
||||
db.session().final_flush()
|
||||
ret = self.schema.jsonify(device)
|
||||
ret.status_code = 201
|
||||
|
||||
db.session.commit()
|
||||
return ret
|
||||
|
||||
def merge_devices(self, base_device, with_device):
|
||||
"""Merge the current device with `with_device` by
|
||||
adding all `with_device` actions under the current device.
|
||||
|
||||
This operation is highly costly as it forces refreshing
|
||||
many models in session.
|
||||
"""
|
||||
snapshots = sorted(filterfalse(lambda x: not isinstance(x, actions.Snapshot), (base_device.actions + with_device.actions)))
|
||||
workbench_snapshots = [ s for s in snapshots if s.software == (SnapshotSoftware.Workbench or SnapshotSoftware.WorkbenchAndroid)]
|
||||
latest_snapshot_device = [ d for d in (base_device, with_device) if d.id == snapshots[-1].device.id][0]
|
||||
latest_snapshotworkbench_device = [ d for d in (base_device, with_device) if d.id == workbench_snapshots[-1].device.id][0]
|
||||
# Adding actions of with_device
|
||||
with_actions_one = [a for a in with_device.actions if isinstance(a, actions.ActionWithOneDevice)]
|
||||
with_actions_multiple = [a for a in with_device.actions if isinstance(a, actions.ActionWithMultipleDevices)]
|
||||
|
||||
for action in with_actions_one:
|
||||
if action.parent:
|
||||
action.parent = base_device
|
||||
else:
|
||||
base_device.actions_one.add(action)
|
||||
for action in with_actions_multiple:
|
||||
if action.parent:
|
||||
action.parent = base_device
|
||||
else:
|
||||
base_device.actions_multiple.add(action)
|
||||
|
||||
# Keeping the components of latest SnapshotWorkbench
|
||||
base_device.components = latest_snapshotworkbench_device.components
|
||||
|
||||
# Properties from latest Snapshot
|
||||
base_device.type = latest_snapshot_device.type
|
||||
base_device.hid = latest_snapshot_device.hid
|
||||
base_device.manufacturer = latest_snapshot_device.manufacturer
|
||||
base_device.model = latest_snapshot_device.model
|
||||
base_device.chassis = latest_snapshot_device.chassis
|
||||
|
||||
|
||||
class ManufacturerView(View):
|
||||
class FindArgs(marshmallow.Schema):
|
||||
search = marshmallow.fields.Str(required=True,
|
||||
|
|
Reference in a new issue