diff --git a/ereuse_devicehub/resources/device/metrics.py b/ereuse_devicehub/resources/device/metrics.py new file mode 100644 index 00000000..c8646775 --- /dev/null +++ b/ereuse_devicehub/resources/device/metrics.py @@ -0,0 +1,174 @@ +import copy + + +class Metrics: + """we want get the data metrics of one device""" + + def __init__(self, device): + self.hid = device.hid + self.devicehub_id = device.devicehub_id + self.actions = copy.copy(device.actions) + self.actions.sort(key=lambda x: x.created) + self.rows = [] + self.lifetime = 0 + self.last_trade = None + self.action_create_by = 'Receiver' + self.status_receiver = 'Use' + self.status_supplier = '' + self.act = None + self.end_users = 0 + self.final_user_code = '' + + def get_template_row(self): + """ + This is a template of a row. + """ + return {'type': '', + 'action_type': 'Status', + 'status_receiver': self.status_receiver, + 'status_supplier': self.status_supplier, + 'trade_supplier': '', + 'trade_receiver': self.act.author, + 'trade_confirmed': '', + 'action_create_by': self.action_create_by, + 'devicehubID': self.devicehub_id, + 'hid': self.hid, + 'finalUserCode': '', + 'numEndUsers': 0, + 'liveCreate': 0, + 'usageTimeHdd': self.lifetime, + 'start': self.act.created, + 'usageTimeAllocate': 0} + + def get_action_status(self): + """ + Mark the status of one device. + If exist one trade before this action, then modify the trade action + else, create one row new. + """ + self.status_receiver = self.act.type + self.status_supplier = '' + if self.act.author != self.act.rol_user: + # It is neccesary exist one trade action before + self.last_trade['status_supplier'] = self.act.type + self.last_trade['status_supplier_created'] = self.act.created + return + + if self.last_trade: + # if exist one trade action before + self.last_trade['status_receiver'] = self.act.type + self.last_trade['status_receiver_created'] = self.act.created + return + + # If not exist any trade action for this device + self.action_create_by = 'Receiver' + row = self.get_template_row() + row['type'] = 'Status' + self.rows.append(row) + + def get_snapshot(self): + """ + If there are one snapshot get the last lifetime for to do a calcul of time of use. + """ + lifestimes = self.act.get_last_lifetimes() + if lifestimes: + self.lifetime = lifestimes[0]['lifetime'] + + def get_allocate(self): + """ + If the action is one Allocate, need modify the row base. + """ + self.end_users = self.act.end_users + self.final_user_code = self.act.final_user_code + row = self.get_template_row() + row['type'] = 'Allocate' + row['trade_supplier'] = '' + row['finalUserCode'] = self.final_user_code + row['numEndUsers'] = self.end_users + row['start'] = self.act.start_time + row['usageTimeAllocate'] = self.lifetime + self.rows.append(row) + + def get_live(self): + """ + If the action is one Live, need modify the row base. + """ + row = self.get_template_row() + row['type'] = 'Live' + row['finalUserCode'] = self.final_user_code + row['numEndUsers'] = self.end_users + row['start'] = self.act.start_time + row['usageTimeAllocate'] = self.lifetime + row['liveCreate'] = self.act.created + if self.act.usage_time_hdd: + row['usageTimeHdd'] = self.act.usage_time_hdd.total_seconds() / 3600 + self.rows.append(row) + + def get_deallocate(self): + """ + If the action is one Dellocate, need modify the row base. + """ + row = self.get_template_row() + row['type'] = 'Deallocate' + row['start'] = self.act.start_time + self.rows.append(row) + + def get_confirms(self): + """ + if the action is one trade action, is possible than have a list of confirmations. + Get the doble confirm for to know if this trade is confirmed or not. + """ + if hasattr(self.act, 'acceptances'): + accept = self.act.acceptances[-1] + if accept.t == 'Confirm' and accept.user == self.act.user_to: + return True + return False + + def get_trade(self): + """ + If this action is a trade action modify the base row. + """ + if self.act.author == self.act.user_from: + self.action_create_by = 'Supplier' + row = self.get_template_row() + self.last_trade = row + row['type'] = 'Trade' + row['action_type'] = 'Trade' + row['trade_supplier'] = self.act.user_from + row['trade_receiver'] = self.act.user_to + row['self.status_receiver'] = self.status_receiver + row['self.status_supplier'] = self.status_supplier + row['trade_confirmed'] = self.get_confirms() + self.rows.append(row) + + def get_metrics(self): + """ + This method get a list of values for calculate a metrics from a spreadsheet + """ + for act in self.actions: + self.act = act + if act.type in ['Use', 'Refurbish', 'Recycling', 'Management']: + self.get_action_status() + continue + + if act.type == 'Snapshot': + self.get_snapshot() + continue + + if act.type == 'Allocate': + self.get_allocate() + continue + + if act.type == 'Live': + self.get_live() + continue + + if act.type == 'Deallocate': + self.get_deallocate() + continue + + if act.type == 'Trade': + self.get_trade() + continue + + return self.rows diff --git a/ereuse_devicehub/resources/device/models.py b/ereuse_devicehub/resources/device/models.py index 7c847cbf..d940534d 100644 --- a/ereuse_devicehub/resources/device/models.py +++ b/ereuse_devicehub/resources/device/models.py @@ -34,6 +34,7 @@ from ereuse_devicehub.resources.enums import BatteryTechnology, CameraFacing, Co DataStorageInterface, DisplayTech, PrinterTechnology, RamFormat, RamInterface, Severity, TransferState from ereuse_devicehub.resources.models import STR_SM_SIZE, Thing, listener_reset_field_updated_in_actual_time from ereuse_devicehub.resources.user.models import User +from ereuse_devicehub.resources.device.metrics import Metrics def create_code(context): @@ -206,10 +207,10 @@ class Device(Thing): if isinstance(c, ColumnProperty) and not getattr(c, 'foreign_keys', None) and c.key not in self._NON_PHYSICAL_PROPS} - + @property def public_properties(self) -> Dict[str, object or None]: - """Fields that describe the properties of a device than next show + """Fields that describe the properties of a device than next show in the public page. :return A dictionary: @@ -341,7 +342,7 @@ class Device(Thing): ac = self.last_action_trading if not ac: - return + return first_owner = self.which_user_put_this_device_in_trace() @@ -349,7 +350,7 @@ class Device(Thing): # can to do revoke_confirmed return confirm_revoke - if ac.type == revoke: + if ac.type == revoke: if ac.user == g.user: # can todo revoke_pending return revoke_pending @@ -505,93 +506,8 @@ class Device(Thing): """ This method get a list of values for calculate a metrics from a spreadsheet """ - actions = copy.copy(self.actions) - actions.sort(key=lambda x: x.created) - allocates = [] - lifetime = 0 - for act in actions: - if act.type == 'Snapshot': - snapshot = act - lifestimes = snapshot.get_last_lifetimes() - lifetime = 0 - if lifestimes: - lifetime = lifestimes[0]['lifetime'] - - if act.type == 'Allocate': - allo = {'type': 'Allocate', - 'action_type': 'Status', - 'status_receiver': 'Use', - 'trade_supplier': '', - 'trade_receiver': '', - 'trade_confirmed': '', - 'action_create_by': 'Receiver', - 'devicehubID': self.devicehub_id, - 'finalUserCode': act.final_user_code, - 'numEndUsers': act.end_users, - 'hid': self.hid, - 'liveCreate': 0, - 'usageTimeHdd': 0, - 'start': act.start_time, - 'usageTimeAllocate': lifetime} - allocates.append(allo) - - if act.type == 'Live': - allocate = copy.copy(allo) - allocate['type'] = 'Live' - allocate['liveCreate'] = act.created - allocate['usageTimeHdd'] = 0 - if act.usage_time_hdd: - allocate['usageTimeHdd'] = act.usage_time_hdd.total_seconds()/3600 - allocates.append(allocate) - - if act.type == 'Deallocate': - deallo = {'type': 'Deallocate', - 'devicehubID': self.devicehub_id, - 'action_type': 'Status', - 'status_receiver': 'Use', - 'trade_supplier': '', - 'trade_receiver': '', - 'trade_confirmed': '', - 'action_create_by': 'Receiver', - 'finalUserCode': '', - 'numEndUsers': '', - 'hid': self.hid, - 'liveCreate': 0, - 'usageTimeHdd': lifetime, - 'start': act.start_time, - 'usageTimeAllocate': 0} - allocates.append(deallo) - - if act.type == 'Trade': - confirm = False - if hasattr(act, 'acceptances'): - accept = act.acceptances[-1] - if accept.t == 'Confirm' and accept.user == act.user_to: - confirm = True - - action_create_by = 'Receiver' - if act.author == act.user_from: - action_create_by = 'Supplier' - trade = {'type': 'Trade', - 'action_type': 'Trade', - 'trade_supplier': act.user_from, - 'trade_receiver': act.user_to, - 'trade_confirmed': confirm, - 'action_create_by': action_create_by, - 'trade_confirmed': confirm, - 'devicehubID': self.devicehub_id, - 'finalUserCode': '', - 'numEndUsers': '', - 'hid': self.hid, - 'liveCreate': 0, - 'usageTimeHdd': lifetime, - 'start': act.start_time, - 'status_receiver': 'Use', - 'usageTimeAllocate': 0 - } - allocates.append(trade) - - return allocates + metrics = Metrics(self) + return metrics.get_metrics() def __lt__(self, other): return self.id < other.id @@ -790,7 +706,7 @@ class Computer(Device): return urls def add_mac_to_hid(self, components_snap=None): - """Returns the Naming.hid with the first mac of network adapter, + """Returns the Naming.hid with the first mac of network adapter, following an alphabetical order. """ self.set_hid() @@ -923,7 +839,7 @@ class Component(Device): """ assert self.hid is None, 'Don\'t use this method with a component that has HID' component = self.__class__.query \ - .filter_by(parent=parent, hid=None, owner_id=self.owner_id, + .filter_by(parent=parent, hid=None, owner_id=self.owner_id, **self.physical_properties) \ .filter(~Component.id.in_(blacklist)) \ .first() diff --git a/tests/test_metrics.py b/tests/test_metrics.py index 54544328..aca7808c 100644 --- a/tests/test_metrics.py +++ b/tests/test_metrics.py @@ -2,6 +2,7 @@ import pytest from ereuse_devicehub.client import UserClient from ereuse_devicehub.resources.action import models as ma +from ereuse_devicehub.resources.documents import documents from tests import conftest from tests.conftest import file, yaml2json, json_encode @@ -120,3 +121,56 @@ def test_metrics_with_live_null(user: UserClient): res, _ = user.get("/metrics/") assert res == metrics + +@pytest.mark.mvp +@pytest.mark.usefixtures(conftest.app_context.__name__) +def test_metrics_action_status(user: UserClient, user2: UserClient): + """ Checks one standard query of metrics """ + # Insert computer + lenovo = yaml2json('desktop-9644w8n-lenovo-0169622.snapshot') + snap, _ = user.post(json_encode(lenovo), res=ma.Snapshot) + action = {'type': ma.Use.t, 'devices': [snap['device']['id']]} + action_use, _ = user.post(action, res=ma.Action) + # res, _ = user.get("/metrics/") + csv_str, _ = user.get(res=documents.DocumentDef.t, + item='actions/', + accept='text/csv', + query=[('filter', {'type': ['Computer']})]) + import pdb; pdb.set_trace() + + +@pytest.mark.mvp +@pytest.mark.usefixtures(conftest.app_context.__name__) +def test_complet_metrics(user: UserClient, user2: UserClient): + """ Checks one standard query of metrics """ + # Insert computer + lenovo = yaml2json('desktop-9644w8n-lenovo-0169622.snapshot') + acer = yaml2json('acer.happy.battery.snapshot') + snap1, _ = user.post(json_encode(lenovo), res=ma.Snapshot) + snap2, _ = user.post(json_encode(acer), res=ma.Snapshot) + lot, _ = user.post({'name': 'MyLot'}, res=Lot) + devices = [('id', snap1['device']['id']), + ('id', snap2['device']['id']) + ] + lot, _ = user.post({}, + res=Lot, + item='{}/devices'.format(lot['id']), + query=devices) + # request_post = { + # 'type': 'Trade', + # 'devices': [span1['device']['id'], snap2['device']['id']], + # 'userFromEmail': user2.email, + # 'userToEmail': user.email, + # 'price': 10, + # 'date': "2020-12-01T02:00:00+00:00", + # 'lot': lot['id'], + # 'confirms': True, + # } + + # user.post(res=models.Action, data=request_post) + + # ============================== + # Check metrics + metrics = {'allocateds': 1, 'live': 1} + res, _ = user.get("/metrics/") + assert res == metrics