From 275b7ad0c55c78e2885d9cc382e807f60c234017 Mon Sep 17 00:00:00 2001 From: RubenPX Date: Mon, 9 May 2022 11:06:48 +0200 Subject: [PATCH 01/51] fix declaration variable --- ereuse_devicehub/static/js/main_inventory.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ereuse_devicehub/static/js/main_inventory.js b/ereuse_devicehub/static/js/main_inventory.js index 848c72e4..7517603d 100644 --- a/ereuse_devicehub/static/js/main_inventory.js +++ b/ereuse_devicehub/static/js/main_inventory.js @@ -268,7 +268,7 @@ function get_device_list() { "Laptop": "", }; - list_devices = devices.map((x) => { + const list_devices = devices.map((x) => { let typ = $(x).data("device-type"); const manuf = $(x).data("device-manufacturer"); const dhid = $(x).data("device-dhid"); From 2e42b08519be877da246fa1f1ef72f01cd883ebb Mon Sep 17 00:00:00 2001 From: RubenPX Date: Mon, 9 May 2022 11:07:57 +0200 Subject: [PATCH 02/51] use babel to support old browsers --- .babelrc.json | 15 + .../static/js/main_inventory.build.js | 571 ++++++++++++++++++ .../templates/inventory/device_list.html | 2 +- package.json | 7 +- 4 files changed, 593 insertions(+), 2 deletions(-) create mode 100644 .babelrc.json create mode 100644 ereuse_devicehub/static/js/main_inventory.build.js diff --git a/.babelrc.json b/.babelrc.json new file mode 100644 index 00000000..15e24a3a --- /dev/null +++ b/.babelrc.json @@ -0,0 +1,15 @@ +{ + "presets": [ + [ + "@babel/preset-env", + { + "targets": { + "edge": "17", + "firefox": "60", + "chrome": "67", + "safari": "11.1" + } + } + ] + ] +} \ No newline at end of file diff --git a/ereuse_devicehub/static/js/main_inventory.build.js b/ereuse_devicehub/static/js/main_inventory.build.js new file mode 100644 index 00000000..521fee9b --- /dev/null +++ b/ereuse_devicehub/static/js/main_inventory.build.js @@ -0,0 +1,571 @@ +"use strict"; + +function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; } + +$(document).ready(() => { + const show_allocate_form = $("#allocateModal").data("show-action-form"); + const show_datawipe_form = $("#datawipeModal").data("show-action-form"); + const show_trade_form = $("#tradeLotModal").data("show-action-form"); + + if (show_allocate_form != "None") { + $("#allocateModal .btn-primary").show(); + newAllocate(show_allocate_form); + } else if (show_datawipe_form != "None") { + $("#datawipeModal .btn-primary").show(); + newDataWipe(show_datawipe_form); + } else if (show_trade_form != "None") { + $("#tradeLotModal .btn-primary").show(); + newTrade(show_trade_form); + } else { + $(".deviceSelect").on("change", deviceSelect); + } // $('#selectLot').selectpicker(); + +}); + +class TableController { + /** + * @returns Selected inputs from device list + */ + static getSelectedDevices() { + if (this.tableRows() == undefined) return []; + return this.tableRows().filter(element => element.querySelector("input").checked).map(element => element.querySelector("input")); + } + /** + * @returns Selected inputs in current page from device list + */ + + + static getAllSelectedDevicesInCurrentPage() { + if (this.tableRowsPage() == undefined) return []; + return this.tableRowsPage().filter(element => element.querySelector("input").checked).map(element => element.querySelector("input")); + } + /** + * @returns All inputs from device list + */ + + + static getAllDevices() { + if (this.tableRows() == undefined) return []; + return this.tableRows().map(element => element.querySelector("input")); + } + /** + * @returns All inputs from current page in device list + */ + + + static getAllDevicesInCurrentPage() { + if (this.tableRowsPage() == undefined) return []; + return this.tableRowsPage().map(element => element.querySelector("input")); + } + /** + * + * @param {HTMLElement} DOMElements + * @returns Procesed input atributes to an Object class + */ + + + static ProcessTR(DOMElements) { + return DOMElements.map(element => { + const info = {}; + info.checked = element.checked; + Object.values(element.attributes).forEach(attrib => { + info[attrib.nodeName.replace(/-/g, "_")] = attrib.nodeValue; + }); + return info; + }); + } + +} +/** + * Select all functionality + */ + + +_defineProperty(TableController, "tableRows", () => table.activeRows.length > 0 ? table.activeRows : []); + +_defineProperty(TableController, "tableRowsPage", () => table.pages[table.rows().dt.currentPage - 1]); + +window.addEventListener("DOMContentLoaded", () => { + const btnSelectAll = document.getElementById("SelectAllBTN"); + const alertInfoDevices = document.getElementById("select-devices-info"); + + function itemListCheckChanged() { + const listDevices = TableController.getAllDevicesInCurrentPage(); + const isAllChecked = listDevices.map(itm => itm.checked); + + if (isAllChecked.every(bool => bool == true)) { + btnSelectAll.checked = true; + btnSelectAll.indeterminate = false; + alertInfoDevices.innerHTML = "Selected devices: ".concat(TableController.getSelectedDevices().length, "\n ").concat(TableController.getAllDevices().length != TableController.getSelectedDevices().length ? "Select all devices (".concat(TableController.getAllDevices().length, ")") : "Cancel selection"); + alertInfoDevices.classList.remove("d-none"); + } else if (isAllChecked.every(bool => bool == false)) { + btnSelectAll.checked = false; + btnSelectAll.indeterminate = false; + alertInfoDevices.classList.add("d-none"); + } else { + btnSelectAll.indeterminate = true; + alertInfoDevices.classList.add("d-none"); + } + } + + TableController.getAllDevices().forEach(item => { + item.addEventListener("click", itemListCheckChanged); + }); + btnSelectAll.addEventListener("click", event => { + const checkedState = event.target.checked; + TableController.getAllDevicesInCurrentPage().forEach(ckeckbox => { + ckeckbox.checked = checkedState; + }); + itemListCheckChanged(); + }); + alertInfoDevices.addEventListener("click", () => { + const checkState = TableController.getAllDevices().length == TableController.getSelectedDevices().length; + TableController.getAllDevices().forEach(ckeckbox => { + ckeckbox.checked = !checkState; + }); + itemListCheckChanged(); + }); // https://github.com/fiduswriter/Simple-DataTables/wiki/Events + + table.on("datatable.page", () => itemListCheckChanged()); + table.on("datatable.perpage", () => itemListCheckChanged()); + table.on("datatable.update", () => itemListCheckChanged()); +}); + +function deviceSelect() { + const devices_count = TableController.getSelectedDevices().length; + get_device_list(); + + if (devices_count == 0) { + $("#addingLotModal .pol").show(); + $("#addingLotModal .btn-primary").hide(); + $("#removeLotModal .pol").show(); + $("#removeLotModal .btn-primary").hide(); + $("#addingTagModal .pol").show(); + $("#addingTagModal .btn-primary").hide(); + $("#actionModal .pol").show(); + $("#actionModal .btn-primary").hide(); + $("#allocateModal .pol").show(); + $("#allocateModal .btn-primary").hide(); + $("#datawipeModal .pol").show(); + $("#datawipeModal .btn-primary").hide(); + } else { + $("#addingLotModal .pol").hide(); + $("#addingLotModal .btn-primary").show(); + $("#removeLotModal .pol").hide(); + $("#removeLotModal .btn-primary").show(); + $("#actionModal .pol").hide(); + $("#actionModal .btn-primary").show(); + $("#allocateModal .pol").hide(); + $("#allocateModal .btn-primary").show(); + $("#datawipeModal .pol").hide(); + $("#datawipeModal .btn-primary").show(); + $("#addingTagModal .pol").hide(); + $("#addingTagModal .btn-primary").show(); + } +} + +function removeLot() { + const devices = TableController.getAllDevices(); + + if (devices.length > 0) { + $("#btnRemoveLots .text-danger").show(); + } else { + $("#btnRemoveLots .text-danger").hide(); + } + + $("#activeRemoveLotModal").click(); +} + +function removeTag() { + const devices = TableController.getSelectedDevices(); + const devices_id = devices.map(dev => dev.data); + + if (devices_id.length == 1) { + const url = "/inventory/tag/devices/".concat(devices_id[0], "/del/"); + window.location.href = url; + } else { + $("#unlinkTagAlertModal").click(); + } +} + +function addTag() { + const devices = TableController.getSelectedDevices(); + const devices_id = devices.map(dev => dev.data); + + if (devices_id.length == 1) { + $("#addingTagModal .pol").hide(); + $("#addingTagModal .btn-primary").show(); + } else { + $("#addingTagModal .pol").show(); + $("#addingTagModal .btn-primary").hide(); + } + + $("#addTagAlertModal").click(); +} + +function newTrade(action) { + let title = "Trade "; + const user_to = $("#user_to").data("email"); + const user_from = $("#user_from").data("email"); + + if (action == "user_from") { + title = "Trade Incoming"; + $("#user_to").attr("readonly", "readonly"); + $("#user_from").prop("readonly", false); + $("#user_from").val(""); + $("#user_to").val(user_to); + } else if (action == "user_to") { + title = "Trade Outgoing"; + $("#user_from").attr("readonly", "readonly"); + $("#user_to").prop("readonly", false); + $("#user_to").val(""); + $("#user_from").val(user_from); + } + + $("#tradeLotModal #title-action").html(title); + $("#activeTradeModal").click(); +} + +function newAction(action) { + $("#actionModal #type").val(action); + $("#actionModal #title-action").html(action); + deviceSelect(); + $("#activeActionModal").click(); +} + +function newAllocate(action) { + $("#allocateModal #type").val(action); + $("#allocateModal #title-action").html(action); + deviceSelect(); + $("#activeAllocateModal").click(); +} + +function newDataWipe(action) { + $("#datawipeModal #type").val(action); + $("#datawipeModal #title-action").html(action); + deviceSelect(); + $("#activeDatawipeModal").click(); +} + +function get_device_list() { + const devices = TableController.getSelectedDevices(); + /* Insert the correct count of devices in actions form */ + + const devices_count = devices.length; + $("#datawipeModal .devices-count").html(devices_count); + $("#allocateModal .devices-count").html(devices_count); + $("#actionModal .devices-count").html(devices_count); + /* Insert the correct value in the input devicesList */ + + const devices_id = $.map(devices, x => $(x).attr("data")).join(","); + $.map($(".devicesList"), x => { + $(x).val(devices_id); + }); + /* Create a list of devices for human representation */ + + const computer = { + "Desktop": "", + "Laptop": "" + }; + const list_devices = devices.map(x => { + let typ = $(x).data("device-type"); + const manuf = $(x).data("device-manufacturer"); + const dhid = $(x).data("device-dhid"); + + if (computer[typ]) { + typ = computer[typ]; + } + + ; + return "".concat(typ, " ").concat(manuf, " ").concat(dhid); + }); + description = $.map(list_devices, x => x).join(", "); + $(".enumeration-devices").html(description); +} + +function export_file(type_file) { + const devices = TableController.getSelectedDevices(); + const devices_id = $.map(devices, x => $(x).attr("data-device-dhid")).join(","); + + if (devices_id) { + const url = "/inventory/export/".concat(type_file, "/?ids=").concat(devices_id); + window.location.href = url; + } else { + $("#exportAlertModal").click(); + } +} +/** + * Reactive lots button + */ + + +async function processSelectedDevices() { + class Actions { + constructor() { + this.list = []; // list of petitions of requests @item --> {type: ["Remove" | "Add"], "LotID": string, "devices": number[]} + } + /** + * Manage the actions that will be performed when applying the changes + * @param {EventSource} ev event (Should be a checkbox type) + * @param {Lot} lot lot id + * @param {Device[]} selectedDevices device id + */ + + + manage(event, lot, selectedDevices) { + event.preventDefault(); + const lotID = lot.id; + const srcElement = event.srcElement.parentElement.children[0]; + const checked = !srcElement.checked; + const found = this.list.filter(list => list.lot.id == lotID)[0]; + + if (checked) { + if (found && found.type == "Remove") { + found.type = "Add"; + } else { + this.list.push({ + type: "Add", + lot, + devices: selectedDevices + }); + } + } else if (found && found.type == "Add") { + found.type = "Remove"; + } else { + this.list.push({ + type: "Remove", + lot, + devices: selectedDevices + }); + } + + if (this.list.length > 0) { + document.getElementById("ApplyDeviceLots").classList.remove("disabled"); + } else { + document.getElementById("ApplyDeviceLots").classList.add("disabled"); + } + } + /** + * Creates notification to give feedback to user + * @param {string} title notification title + * @param {string | null} toastText notification text + * @param {boolean} isError defines if a toast is a error + */ + + + notifyUser(title, toastText, isError) { + const toast = document.createElement("div"); + toast.classList = "alert alert-dismissible fade show ".concat(isError ? "alert-danger" : "alert-success"); + toast.attributes["data-autohide"] = !isError; + toast.attributes.role = "alert"; + toast.style = "margin-left: auto; width: fit-content;"; + toast.innerHTML = "".concat(title, ""); + + if (toastText && toastText.length > 0) { + toast.innerHTML += "
".concat(toastText); + } + + document.getElementById("NotificationsContainer").appendChild(toast); + + if (!isError) { + setTimeout(() => toast.classList.remove("show"), 3000); + } + + setTimeout(() => document.getElementById("NotificationsContainer").innerHTML == "", 3500); + } + /** + * Get actions and execute call request to add or remove devices from lots + */ + + + doActions() { + let requestCount = 0; // This is for count all requested api count, to perform reRender of table device list + + this.list.forEach(async action => { + if (action.type == "Add") { + try { + const devicesIDs = action.devices.filter(dev => !action.lot.devices.includes(dev.id)).map(dev => dev.id); + await Api.devices_add(action.lot.id, devicesIDs); + this.notifyUser("Devices sucefully added to selected lot/s", "", false); + } catch (error) { + this.notifyUser("Failed to add devices to selected lot/s", error.responseJSON.message, true); + } + } else if (action.type == "Remove") { + try { + const devicesIDs = action.devices.filter(dev => action.lot.devices.includes(dev.id)).map(dev => dev.id); + await Api.devices_remove(action.lot.id, devicesIDs); + this.notifyUser("Devices sucefully removed from selected lot/s", "", false); + } catch (error) { + this.notifyUser("Failed to remove devices from selected lot/s", error.responseJSON.message, true); + } + } + + requestCount += 1; + + if (requestCount == this.list.length) { + this.reRenderTable(); + this.list = []; + } + }); + $("#confirmLotsModal").modal("hide"); // Hide dialog when click "Save changes" + + document.getElementById("dropDownLotsSelector").classList.remove("show"); + } + /** + * Re-render list in table + */ + + + async reRenderTable() { + const newRequest = await Api.doRequest(window.location); + const tmpDiv = document.createElement("div"); + tmpDiv.innerHTML = newRequest; + const newTable = Array.from(tmpDiv.querySelectorAll("table.table > tbody > tr .deviceSelect")).map(x => x.attributes["data-device-dhid"].value); // https://github.com/fiduswriter/Simple-DataTables/wiki/rows()#removeselect-arraynumber + + const rowsToRemove = []; + + for (let i = 0; i < table.activeRows.length; i++) { + const row = table.activeRows[i]; + + if (!newTable.includes(row.querySelector("input").attributes["data-device-dhid"].value)) { + rowsToRemove.push(i); + } + } + + table.rows().remove(rowsToRemove); // Restore state of checkbox + + const selectAllBTN = document.getElementById("SelectAllBTN"); + selectAllBTN.checked = false; + selectAllBTN.indeterminate = false; + } + + } + + let eventClickActions; + /** + * Generates a list item with a correspondient checkbox state + * @param {Object} lot Lot model server + * @param {Device[]} selectedDevices list selected devices + * @param {HTMLElement} elementTarget + * @param {Action[]} actions + */ + + function templateLot(lot, selectedDevices, elementTarget, actions) { + elementTarget.innerHTML = ""; + const { + id, + name, + state + } = lot; + const htmlTemplate = "\n "); + const doc = document.createElement("li"); + doc.innerHTML = htmlTemplate; + + switch (state) { + case "true": + doc.children[0].checked = true; + break; + + case "false": + doc.children[0].checked = false; + break; + + case "indetermined": + doc.children[0].indeterminate = true; + break; + + default: + console.warn("This shouldn't be happend: Lot without state: ", lot); + break; + } + + doc.children[0].addEventListener("mouseup", ev => actions.manage(ev, lot, selectedDevices)); + doc.children[1].addEventListener("mouseup", ev => actions.manage(ev, lot, selectedDevices)); + elementTarget.append(doc); + } + + const listHTML = $("#LotsSelector"); // Get selected devices + + const selectedDevicesID = TableController.ProcessTR(TableController.getSelectedDevices()).map(item => item.data); + + if (selectedDevicesID.length <= 0) { + listHTML.html("
  • No devices selected
  • "); + return; + } // Initialize Actions list, and set checkbox triggers + + + const actions = new Actions(); + + if (eventClickActions) { + document.getElementById("ApplyDeviceLots").removeEventListener(eventClickActions); + } + + eventClickActions = document.getElementById("ApplyDeviceLots").addEventListener("click", () => { + const modal = $("#confirmLotsModal"); + modal.modal({ + keyboard: false + }); + let list_changes_html = ""; // {type: ["Remove" | "Add"], "LotID": string, "devices": number[]} + + actions.list.forEach(action => { + let type; + let devices; + + if (action.type == "Add") { + type = "success"; + devices = action.devices.filter(dev => !action.lot.devices.includes(dev.id)); // Only show affected devices + } else { + type = "danger"; + devices = action.devices.filter(dev => action.lot.devices.includes(dev.id)); // Only show affected devices + } + + list_changes_html += "\n
    \n
    ").concat(action.lot.name, "
    \n
    \n

    \n ").concat(devices.map(item => { + const name = "".concat(item.type, " ").concat(item.manufacturer, " ").concat(item.model); + return "").concat(item.devicehubID, ""); + }).join(" "), "\n

    \n
    \n
    "); + }); + modal.find(".modal-body").html(list_changes_html); + const el = document.getElementById("SaveAllActions"); + const elClone = el.cloneNode(true); + el.parentNode.replaceChild(elClone, el); + elClone.addEventListener("click", () => actions.doActions()); + modal.modal("show"); // actions.doActions(); + }); + document.getElementById("ApplyDeviceLots").classList.add("disabled"); + + try { + listHTML.html("
  • "); + const selectedDevices = await Api.get_devices(selectedDevicesID); + let lots = await Api.get_lots(); + lots = lots.map(lot => { + lot.devices = selectedDevices.filter(device => device.lots.filter(devicelot => devicelot.id == lot.id).length > 0).map(device => parseInt(device.id)); + + switch (lot.devices.length) { + case 0: + lot.state = "false"; + break; + + case selectedDevicesID.length: + lot.state = "true"; + break; + + default: + lot.state = "indetermined"; + break; + } + + return lot; + }); + let lotsList = []; + lotsList.push(lots.filter(lot => lot.state == "true").sort((a, b) => a.name.localeCompare(b.name))); + lotsList.push(lots.filter(lot => lot.state == "indetermined").sort((a, b) => a.name.localeCompare(b.name))); + lotsList.push(lots.filter(lot => lot.state == "false").sort((a, b) => a.name.localeCompare(b.name))); + lotsList = lotsList.flat(); // flat array + + listHTML.html(""); + lotsList.forEach(lot => templateLot(lot, selectedDevices, listHTML, actions)); + } catch (error) { + console.log(error); + listHTML.html("
  • Error feching devices and lots
    (see console for more details)
  • "); + } +} diff --git a/ereuse_devicehub/templates/inventory/device_list.html b/ereuse_devicehub/templates/inventory/device_list.html index 61f94f4d..ba287061 100644 --- a/ereuse_devicehub/templates/inventory/device_list.html +++ b/ereuse_devicehub/templates/inventory/device_list.html @@ -434,5 +434,5 @@ perPage: 20 }) - + {% endblock main %} diff --git a/package.json b/package.json index 219a75f4..4cb22b4e 100644 --- a/package.json +++ b/package.json @@ -3,12 +3,15 @@ "version": "1.0.0", "description": "Devicehub is a distributed IT Asset Management System focused in reusing devices, created under the project [eReuse.org](https://www.ereuse.org)", "main": "index.js", + "browserslist": "> 0.25%, not dead", "directories": { "doc": "docs", "example": "examples", "test": "tests" }, "devDependencies": { + "@babel/cli": "^7.17.10", + "@babel/preset-env": "^7.17.10", "eslint": "^8.13.0", "eslint-config-airbnb": "^19.0.4", "eslint-config-prettier": "^8.5.0", @@ -17,12 +20,14 @@ }, "scripts": { "lint:report": "eslint ereuse_devicehub --ext .js --output-file eslint_report.json --format json", - "lint:fix": "eslint ereuse_devicehub --ext .js --fix" + "lint:fix": "eslint ereuse_devicehub --ext .js --fix", + "babel": "babel ereuse_devicehub/static/js/main_inventory.js --out-file ereuse_devicehub/static/js/main_inventory.build.js" }, "keywords": [], "author": "", "license": "ISC", "dependencies": { + "@babel/core": "^7.17.10", "eslint-plugin-jsx-a11y": "^6.5.1", "eslint-plugin-react": "^7.29.4", "eslint-plugin-react-hooks": "^4.4.0" From 4f72654f50506feb7678fc2621991e7a80304966 Mon Sep 17 00:00:00 2001 From: Lint Action Date: Mon, 9 May 2022 09:10:08 +0000 Subject: [PATCH 03/51] Fix code style issues with ESLint --- ereuse_devicehub/static/js/main_inventory.build.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ereuse_devicehub/static/js/main_inventory.build.js b/ereuse_devicehub/static/js/main_inventory.build.js index 521fee9b..e7b5edc6 100644 --- a/ereuse_devicehub/static/js/main_inventory.build.js +++ b/ereuse_devicehub/static/js/main_inventory.build.js @@ -1,6 +1,6 @@ "use strict"; -function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; } +function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; } $(document).ready(() => { const show_allocate_form = $("#allocateModal").data("show-action-form"); From 8f80e7dff44ef7ab6dceaf6b4f711409142f4925 Mon Sep 17 00:00:00 2001 From: RubenPX Date: Mon, 9 May 2022 11:29:48 +0200 Subject: [PATCH 04/51] Fix declaration variable --- .../static/js/main_inventory.build.js | 38 ++++++++++++------- ereuse_devicehub/static/js/main_inventory.js | 2 +- 2 files changed, 25 insertions(+), 15 deletions(-) diff --git a/ereuse_devicehub/static/js/main_inventory.build.js b/ereuse_devicehub/static/js/main_inventory.build.js index e7b5edc6..5da7c442 100644 --- a/ereuse_devicehub/static/js/main_inventory.build.js +++ b/ereuse_devicehub/static/js/main_inventory.build.js @@ -1,6 +1,12 @@ "use strict"; -function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; } +function _classStaticPrivateFieldSpecGet(receiver, classConstructor, descriptor) { _classCheckPrivateStaticAccess(receiver, classConstructor); _classCheckPrivateStaticFieldDescriptor(descriptor, "get"); return _classApplyDescriptorGet(receiver, descriptor); } + +function _classCheckPrivateStaticFieldDescriptor(descriptor, action) { if (descriptor === undefined) { throw new TypeError("attempted to " + action + " private static field before its declaration"); } } + +function _classCheckPrivateStaticAccess(receiver, classConstructor) { if (receiver !== classConstructor) { throw new TypeError("Private static access of wrong provenance"); } } + +function _classApplyDescriptorGet(receiver, descriptor) { if (descriptor.get) { return descriptor.get.call(receiver); } return descriptor.value; } $(document).ready(() => { const show_allocate_form = $("#allocateModal").data("show-action-form"); @@ -27,8 +33,8 @@ class TableController { * @returns Selected inputs from device list */ static getSelectedDevices() { - if (this.tableRows() == undefined) return []; - return this.tableRows().filter(element => element.querySelector("input").checked).map(element => element.querySelector("input")); + if (_classStaticPrivateFieldSpecGet(this, TableController, _tableRows).call(this) == undefined) return []; + return _classStaticPrivateFieldSpecGet(this, TableController, _tableRows).call(this).filter(element => element.querySelector("input").checked).map(element => element.querySelector("input")); } /** * @returns Selected inputs in current page from device list @@ -36,8 +42,8 @@ class TableController { static getAllSelectedDevicesInCurrentPage() { - if (this.tableRowsPage() == undefined) return []; - return this.tableRowsPage().filter(element => element.querySelector("input").checked).map(element => element.querySelector("input")); + if (_classStaticPrivateFieldSpecGet(this, TableController, _tableRowsPage).call(this) == undefined) return []; + return _classStaticPrivateFieldSpecGet(this, TableController, _tableRowsPage).call(this).filter(element => element.querySelector("input").checked).map(element => element.querySelector("input")); } /** * @returns All inputs from device list @@ -45,8 +51,8 @@ class TableController { static getAllDevices() { - if (this.tableRows() == undefined) return []; - return this.tableRows().map(element => element.querySelector("input")); + if (_classStaticPrivateFieldSpecGet(this, TableController, _tableRows).call(this) == undefined) return []; + return _classStaticPrivateFieldSpecGet(this, TableController, _tableRows).call(this).map(element => element.querySelector("input")); } /** * @returns All inputs from current page in device list @@ -54,8 +60,8 @@ class TableController { static getAllDevicesInCurrentPage() { - if (this.tableRowsPage() == undefined) return []; - return this.tableRowsPage().map(element => element.querySelector("input")); + if (_classStaticPrivateFieldSpecGet(this, TableController, _tableRowsPage).call(this) == undefined) return []; + return _classStaticPrivateFieldSpecGet(this, TableController, _tableRowsPage).call(this).map(element => element.querySelector("input")); } /** * @@ -81,10 +87,14 @@ class TableController { */ -_defineProperty(TableController, "tableRows", () => table.activeRows.length > 0 ? table.activeRows : []); - -_defineProperty(TableController, "tableRowsPage", () => table.pages[table.rows().dt.currentPage - 1]); - +var _tableRows = { + writable: true, + value: () => table.activeRows.length > 0 ? table.activeRows : [] +}; +var _tableRowsPage = { + writable: true, + value: () => table.pages[table.rows().dt.currentPage - 1] +}; window.addEventListener("DOMContentLoaded", () => { const btnSelectAll = document.getElementById("SelectAllBTN"); const alertInfoDevices = document.getElementById("select-devices-info"); @@ -279,7 +289,7 @@ function get_device_list() { ; return "".concat(typ, " ").concat(manuf, " ").concat(dhid); }); - description = $.map(list_devices, x => x).join(", "); + const description = $.map(list_devices, x => x).join(", "); $(".enumeration-devices").html(description); } diff --git a/ereuse_devicehub/static/js/main_inventory.js b/ereuse_devicehub/static/js/main_inventory.js index 7517603d..ed20f7a3 100644 --- a/ereuse_devicehub/static/js/main_inventory.js +++ b/ereuse_devicehub/static/js/main_inventory.js @@ -278,7 +278,7 @@ function get_device_list() { return `${typ} ${manuf} ${dhid}`; }); - description = $.map(list_devices, (x) => x).join(", "); + const description = $.map(list_devices, (x) => x).join(", "); $(".enumeration-devices").html(description); } From 776e38aa67ea8ca509fb678b137d6af9820ac72e Mon Sep 17 00:00:00 2001 From: Lint Action Date: Mon, 9 May 2022 09:30:23 +0000 Subject: [PATCH 05/51] Fix code style issues with ESLint --- ereuse_devicehub/static/js/main_inventory.build.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ereuse_devicehub/static/js/main_inventory.build.js b/ereuse_devicehub/static/js/main_inventory.build.js index 5da7c442..09006905 100644 --- a/ereuse_devicehub/static/js/main_inventory.build.js +++ b/ereuse_devicehub/static/js/main_inventory.build.js @@ -2,7 +2,7 @@ function _classStaticPrivateFieldSpecGet(receiver, classConstructor, descriptor) { _classCheckPrivateStaticAccess(receiver, classConstructor); _classCheckPrivateStaticFieldDescriptor(descriptor, "get"); return _classApplyDescriptorGet(receiver, descriptor); } -function _classCheckPrivateStaticFieldDescriptor(descriptor, action) { if (descriptor === undefined) { throw new TypeError("attempted to " + action + " private static field before its declaration"); } } +function _classCheckPrivateStaticFieldDescriptor(descriptor, action) { if (descriptor === undefined) { throw new TypeError(`attempted to ${ action } private static field before its declaration`); } } function _classCheckPrivateStaticAccess(receiver, classConstructor) { if (receiver !== classConstructor) { throw new TypeError("Private static access of wrong provenance"); } } From ff00b88fd798976131b67680c26fa11ebf603733 Mon Sep 17 00:00:00 2001 From: RubenPX Date: Mon, 9 May 2022 14:18:41 +0200 Subject: [PATCH 06/51] fix indeterminated checkbox --- ereuse_devicehub/static/js/main_inventory.js | 28 +++++++++++++------- 1 file changed, 19 insertions(+), 9 deletions(-) diff --git a/ereuse_devicehub/static/js/main_inventory.js b/ereuse_devicehub/static/js/main_inventory.js index 848c72e4..c8159c83 100644 --- a/ereuse_devicehub/static/js/main_inventory.js +++ b/ereuse_devicehub/static/js/main_inventory.js @@ -90,11 +90,10 @@ window.addEventListener("DOMContentLoaded", () => { btnSelectAll.checked = true; btnSelectAll.indeterminate = false; alertInfoDevices.innerHTML = `Selected devices: ${TableController.getSelectedDevices().length} - ${ - TableController.getAllDevices().length != TableController.getSelectedDevices().length - ? `Select all devices (${TableController.getAllDevices().length})` - : "Cancel selection" - }`; + ${TableController.getAllDevices().length != TableController.getSelectedDevices().length + ? `Select all devices (${TableController.getAllDevices().length})` + : "Cancel selection" + }`; alertInfoDevices.classList.remove("d-none"); } else if (isAllChecked.every(bool => bool == false)) { btnSelectAll.checked = false; @@ -315,19 +314,30 @@ async function processSelectedDevices() { const lotID = lot.id; const srcElement = event.srcElement.parentElement.children[0] const checked = !srcElement.checked; + const { indeterminate } = srcElement const found = this.list.filter(list => list.lot.id == lotID)[0]; if (checked) { if (found && found.type == "Remove") { - found.type = "Add"; + const affectedDevices = found.devices.filter(dev => found.lot.devices.includes(dev.id)) + if (affectedDevices.length > 0 && found.indeterminate == false) { // Remove action from list + actions.list = actions.list.filter(x => x.lot.id != found.lot.id) + } else { + found.type = "Add"; + } } else { - this.list.push({ type: "Add", lot, devices: selectedDevices }); + this.list.push({ type: "Add", lot, devices: selectedDevices, indeterminate }); } } else if (found && found.type == "Add") { - found.type = "Remove"; + const affectedDevices = found.devices.filter(dev => !found.lot.devices.includes(dev.id)) + if (affectedDevices.length > 0 && found.indeterminate == false) { // Remove action from list + actions.list = actions.list.filter(x => x.lot.id != found.lot.id) + } else { + found.type = "Remove"; + } } else { - this.list.push({ type: "Remove", lot, devices: selectedDevices }); + this.list.push({ type: "Remove", lot, devices: selectedDevices, indeterminate }); } if (this.list.length > 0) { From 7ee2f2962bb2884b1a4f333728b96f1ac1bf4bc0 Mon Sep 17 00:00:00 2001 From: Cayo Puigdefabregas Date: Wed, 11 May 2022 11:48:53 +0200 Subject: [PATCH 07/51] add view for download Workbench settings --- .../templates/ereuse_devicehub/base_site.html | 10 ++ .../templates/workbench/settings.html | 59 ++++++++++++ ereuse_devicehub/workbench/__init__.py | 0 ereuse_devicehub/workbench/views.py | 94 +++++++++++++++++++ examples/app.py | 2 + tests/conftest.py | 2 + 6 files changed, 167 insertions(+) create mode 100644 ereuse_devicehub/templates/workbench/settings.html create mode 100644 ereuse_devicehub/workbench/__init__.py create mode 100644 ereuse_devicehub/workbench/views.py diff --git a/ereuse_devicehub/templates/ereuse_devicehub/base_site.html b/ereuse_devicehub/templates/ereuse_devicehub/base_site.html index fe014107..1774e6df 100644 --- a/ereuse_devicehub/templates/ereuse_devicehub/base_site.html +++ b/ereuse_devicehub/templates/ereuse_devicehub/base_site.html @@ -64,6 +64,16 @@ +
  • + + + Workbench Settings + +
  • +
  • + +
  • +
  • diff --git a/ereuse_devicehub/templates/workbench/settings.html b/ereuse_devicehub/templates/workbench/settings.html new file mode 100644 index 00000000..80278615 --- /dev/null +++ b/ereuse_devicehub/templates/workbench/settings.html @@ -0,0 +1,59 @@ +{% extends "ereuse_devicehub/base_site.html" %} +{% block main %} + +
    +

    {{ title }}

    + +
    + +
    +
    +
    + +
    +
    + +
    +
    Download your settings for Workbench
    +

    Please select one of this options

    +
    +
    + +
    +

    Download the settings for register devices and erase disks. This is less safe and more fast.

    +
    +
    +
    + +
    +

    Download the settings for register devices and erase disks. This is more safe and more slow.

    +
    +
    +
    + +
    + +
    + +
    + +
    +
    +
    +
    +{% endblock main %} diff --git a/ereuse_devicehub/workbench/__init__.py b/ereuse_devicehub/workbench/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/ereuse_devicehub/workbench/views.py b/ereuse_devicehub/workbench/views.py new file mode 100644 index 00000000..ccbdbdc5 --- /dev/null +++ b/ereuse_devicehub/workbench/views.py @@ -0,0 +1,94 @@ +import time + +import flask +from flask import Blueprint +from flask import current_app as app +from flask import g, make_response, request +from flask_login import login_required + +from ereuse_devicehub import auth +from ereuse_devicehub.db import db +from ereuse_devicehub.resources.enums import SessionType +from ereuse_devicehub.resources.user.models import Session +from ereuse_devicehub.views import GenericMixView + +workbench = Blueprint('workbench', __name__, url_prefix='/workbench') + + +class SettingsView(GenericMixView): + decorators = [login_required] + template_name = 'workbench/settings.html' + page_title = "Workbench Settings" + + def dispatch_request(self): + self.get_context() + self.context.update( + { + 'page_title': self.page_title, + } + ) + + self.opt = request.values.get('opt') + if self.opt in ['register', 'soft', 'hard']: + return self.download() + + return flask.render_template(self.template_name, **self.context) + + def download(self): + self.wbContext = { + 'token': self.get_token(), + 'host': app.config['HOST'], + 'inventory': app.config['SCHEMA'], + 'benchmark': False, + 'stress_test': 1, + 'erase': '', + 'steps': 0, + 'leading_zeros': False, + } + options = {"register": self.register, "soft": self.soft, "hard": self.hard} + return options[self.opt]() + + def register(self): + data = flask.render_template('workbench/wbSettings.ini', **self.wbContext) + return self.response_download(data) + + def soft(self): + self.wbContext['erase'] = 'EraseBasic' + self.wbContext['steps'] = 1 + data = flask.render_template('workbench/wbSettings.ini', **self.wbContext) + return self.response_download(data) + + def hard(self): + self.wbContext['erase'] = 'EraseSectors' + self.wbContext['steps'] = 1 + self.wbContext['leading_zeros'] = True + data = flask.render_template('workbench/wbSettings.ini', **self.wbContext) + return self.response_download(data) + + def response_download(self, data): + bfile = str.encode(data) + output = make_response(bfile) + output.headers['Content-Disposition'] = 'attachment; filename=settings.ini' + output.headers['Content-type'] = 'text/plain' + return output + + def get_token(self): + if not g.user.sessions: + ses = Session(user=g.user) + db.session.add(ses) + db.session.commit() + + tk = '' + now = time.time() + for s in g.user.sessions: + if s.type == SessionType.Internal and (s.expired == 0 or s.expired > now): + tk = s.token + break + + assert tk != '' + + token = auth.Auth.encode(tk) + return token + + +workbench.add_url_rule('/settings/', view_func=SettingsView.as_view('settings')) diff --git a/examples/app.py b/examples/app.py index 415d86ce..b0c9b253 100644 --- a/examples/app.py +++ b/examples/app.py @@ -10,11 +10,13 @@ from ereuse_devicehub.devicehub import Devicehub from ereuse_devicehub.inventory.views import devices from ereuse_devicehub.labels.views import labels from ereuse_devicehub.views import core +from ereuse_devicehub.workbench.views import workbench app = Devicehub(inventory=DevicehubConfig.DB_SCHEMA) app.register_blueprint(core) app.register_blueprint(devices) app.register_blueprint(labels) +app.register_blueprint(workbench) # configure & enable CSRF of Flask-WTF # NOTE: enable by blueprint to exclude API views diff --git a/tests/conftest.py b/tests/conftest.py index a8d04506..53fdbaa9 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -24,6 +24,7 @@ from ereuse_devicehub.resources.enums import SessionType from ereuse_devicehub.resources.tag import Tag from ereuse_devicehub.resources.user.models import Session, User from ereuse_devicehub.views import core +from ereuse_devicehub.workbench.views import workbench STARTT = datetime(year=2000, month=1, day=1, hour=1) """A dummy starting time to use in tests.""" @@ -58,6 +59,7 @@ def _app(config: TestConfig) -> Devicehub: app.register_blueprint(core) app.register_blueprint(devices) app.register_blueprint(labels) + app.register_blueprint(workbench) app.config["SQLALCHEMY_RECORD_QUERIES"] = True app.config['PROFILE'] = True # app.wsgi_app = ProfilerMiddleware(app.wsgi_app, restrictions=[30]) From 0e75865757446ba53e2091339a3fe1b5232c5886 Mon Sep 17 00:00:00 2001 From: Cayo Puigdefabregas Date: Wed, 11 May 2022 11:58:54 +0200 Subject: [PATCH 08/51] fix settings.html --- ereuse_devicehub/templates/workbench/settings.html | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/ereuse_devicehub/templates/workbench/settings.html b/ereuse_devicehub/templates/workbench/settings.html index 80278615..6fbeb592 100644 --- a/ereuse_devicehub/templates/workbench/settings.html +++ b/ereuse_devicehub/templates/workbench/settings.html @@ -28,9 +28,9 @@

    Download the settings only for register devices.

    -
    +

    Download the settings for register devices and erase disks. This is less safe and more fast.

    @@ -38,7 +38,7 @@

    Download the settings for register devices and erase disks. This is more safe and more slow.

    From ce001b64aa66b4779f2e5c3891c66c82f69c6727 Mon Sep 17 00:00:00 2001 From: Cayo Puigdefabregas Date: Wed, 11 May 2022 12:39:23 +0200 Subject: [PATCH 09/51] add test for wbsettings --- tests/test_render_2_0.py | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/tests/test_render_2_0.py b/tests/test_render_2_0.py index 9a52b89f..38dc12cd 100644 --- a/tests/test_render_2_0.py +++ b/tests/test_render_2_0.py @@ -842,3 +842,27 @@ def test_action_datawipe(user3: UserClientFlask): assert dev.actions[-1].type == 'DataWipe' assert 'Action "DataWipe" created successfully!' in body assert dev.devicehub_id in body + + +@pytest.mark.mvp +@pytest.mark.usefixtures(conftest.app_context.__name__) +def test_wb_settings(user3: UserClientFlask): + uri = '/workbench/settings/' + body, status = user3.get(uri) + + assert status == '200 OK' + assert "Download your settings for Workbench" in body + assert "Workbench Settings" in body + + +@pytest.mark.mvp +@pytest.mark.usefixtures(conftest.app_context.__name__) +def test_wb_settings_register(user3: UserClientFlask): + uri = '/workbench/settings/?opt=register' + body, status = user3.get(uri) + + assert status == '200 OK' + assert "WB_BENCHMARK = False" in body + assert "WB_ERASE = \n" in body + assert "WB_ERASE_STEPS = 0" in body + assert "WB_ERASE_LEADING_ZEROS = False" in body From b8e980cabeab2490aa228824a6ab3ade698b8a7d Mon Sep 17 00:00:00 2001 From: Cayo Puigdefabregas Date: Wed, 11 May 2022 13:14:11 +0200 Subject: [PATCH 10/51] fixed tests --- .../templates/workbench/wbSettings.ini | 18 ++++++++++++++++++ tests/test_basic.py | 3 ++- 2 files changed, 20 insertions(+), 1 deletion(-) create mode 100644 ereuse_devicehub/templates/workbench/wbSettings.ini diff --git a/ereuse_devicehub/templates/workbench/wbSettings.ini b/ereuse_devicehub/templates/workbench/wbSettings.ini new file mode 100644 index 00000000..ea45e547 --- /dev/null +++ b/ereuse_devicehub/templates/workbench/wbSettings.ini @@ -0,0 +1,18 @@ +[settings] + +DH_TOKEN = {{token}} + +DH_HOST = {{host}} +DH_DATABASE = {{inventory}} +DEVICEHUB_URL = https://${DB_HOST}/${DB_DATABASE}/ + + +WB_BENCHMARK = {{ benchmark }} +WB_STRESS_TEST = {{ stress_test }} +WB_SMART_TEST = short + +WB_ERASE = {{ erase }} +WB_ERASE_STEPS = {{ steps }} +WB_ERASE_LEADING_ZEROS = {{ leading_zeros }} + +WB_DEBUG = True diff --git a/tests/test_basic.py b/tests/test_basic.py index 4329d996..f9960219 100644 --- a/tests/test_basic.py +++ b/tests/test_basic.py @@ -19,7 +19,7 @@ def test_dependencies(): # Simplejson has a different signature than stdlib json # should be fixed though # noinspection PyUnresolvedReferences - import simplejson + import simplejson # noqa: F401 # noinspection PyArgumentList @@ -87,6 +87,7 @@ def test_api_docs(client: Client): '/users/login/', '/users/logout/', '/versions/', + '/workbench/settings/', } assert docs['info'] == {'title': 'Devicehub', 'version': '0.2'} assert docs['components']['securitySchemes']['bearerAuth'] == { From e7a455018c7a43d5d1c6e03ed40c6af9718fc691 Mon Sep 17 00:00:00 2001 From: Santiago Lamora Date: Wed, 11 May 2022 18:37:37 +0200 Subject: [PATCH 11/51] Add babel instructions on CONTRIBUTING.md --- CONTRIBUTING.md | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index c34d93c5..142fd954 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -2,6 +2,15 @@ ## Writing code +### Javascript and compatibility with "old" browsers +**Warning:** This project is using babel compiler... You need run an additional build step to make build js file +```bash +npm install +npm run babel +``` +NOTE: If you prefer you can use yarn instead, it's compatible +NOTE2: This only affect to file `ereuse_devicehub/static/js/main_inventory.js`. + ### Coding style #### Python style From 42760e5b9eed4150eb00f03154a307b73b6a33bc Mon Sep 17 00:00:00 2001 From: RubenPX Date: Wed, 11 May 2022 18:42:29 +0200 Subject: [PATCH 12/51] Fix print labels devices --- ereuse_devicehub/static/js/main_inventory.js | 2 ++ ereuse_devicehub/templates/inventory/device_list.html | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/ereuse_devicehub/static/js/main_inventory.js b/ereuse_devicehub/static/js/main_inventory.js index 5db11bdd..9479fc48 100644 --- a/ereuse_devicehub/static/js/main_inventory.js +++ b/ereuse_devicehub/static/js/main_inventory.js @@ -111,6 +111,8 @@ window.addEventListener("DOMContentLoaded", () => { } else { btnSelectAll.disabled = false; } + + get_device_list(); } TableController.getAllDevices().forEach(item => { diff --git a/ereuse_devicehub/templates/inventory/device_list.html b/ereuse_devicehub/templates/inventory/device_list.html index 2b1d5cec..b457b080 100644 --- a/ereuse_devicehub/templates/inventory/device_list.html +++ b/ereuse_devicehub/templates/inventory/device_list.html @@ -392,7 +392,7 @@ {% for doc in lot.trade.documents %} - {% if doc.url.to_text() %} + {% if doc.url %} {{ doc.file_name}} {% else %} {{ doc.file_name}} From 90690bdd66cf73c7ca89e1e4a037ba01a0895577 Mon Sep 17 00:00:00 2001 From: Santiago Lamora Date: Wed, 11 May 2022 18:50:39 +0200 Subject: [PATCH 13/51] Release version 2.1.1 --- CHANGELOG.md | 5 +++++ ereuse_devicehub/__init__.py | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 806a01b4..c6c51b46 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,11 @@ ml). ## testing +## [2.1.1] - 2022-05-11 +Hot fix release. +- [fixed] #256 JS support to old browsers using babel. +- [fixed] #266 Fix error when trade.document.url is None on device_list.html + ## [2.1.0] - 2022-05-11 - [added] #219 Add functionality to searchbar (Lots and devices). - [added] #222 Allow user to update its password. diff --git a/ereuse_devicehub/__init__.py b/ereuse_devicehub/__init__.py index 28c957cd..58039f50 100644 --- a/ereuse_devicehub/__init__.py +++ b/ereuse_devicehub/__init__.py @@ -1 +1 @@ -__version__ = "2.2.0.alpha0" +__version__ = "2.1.1" From 2457f776e17c1eb7ba34723ea0497e2580eb0026 Mon Sep 17 00:00:00 2001 From: Santiago Lamora Date: Wed, 11 May 2022 18:53:22 +0200 Subject: [PATCH 14/51] Bump to version 2.1.2.alpha0 --- ereuse_devicehub/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ereuse_devicehub/__init__.py b/ereuse_devicehub/__init__.py index 58039f50..d5fe4817 100644 --- a/ereuse_devicehub/__init__.py +++ b/ereuse_devicehub/__init__.py @@ -1 +1 @@ -__version__ = "2.1.1" +__version__ = "2.1.2.alpha0" From d10bb70da867a90b88586e002c8a01325e8b7a07 Mon Sep 17 00:00:00 2001 From: RubenPX Date: Wed, 11 May 2022 19:07:52 +0200 Subject: [PATCH 15/51] ESLint ignore builded JS files --- .eslintignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.eslintignore b/.eslintignore index 416c47b1..8d09e126 100644 --- a/.eslintignore +++ b/.eslintignore @@ -1,4 +1,5 @@ ereuse_devicehub/static/vendor ereuse_devicehub/static/js/print.pdf.js ereuse_devicehub/static/js/qrcode.js +*.build.js *.min.js \ No newline at end of file From eac90d32cfb88a0ecac920cbb48c866ad09875c6 Mon Sep 17 00:00:00 2001 From: Cayo Puigdefabregas Date: Thu, 12 May 2022 10:05:37 +0200 Subject: [PATCH 16/51] fixed bug #3386 --- ereuse_devicehub/inventory/forms.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ereuse_devicehub/inventory/forms.py b/ereuse_devicehub/inventory/forms.py index 062d185c..bce5ba43 100644 --- a/ereuse_devicehub/inventory/forms.py +++ b/ereuse_devicehub/inventory/forms.py @@ -692,7 +692,7 @@ class AllocateForm(ActionFormMix): transaction = StringField( 'Transaction', [validators.Optional(), validators.length(max=50)] ) - end_users = IntegerField('End users', [validators.Optional()]) + end_users = IntegerField('Number of end users', [validators.Optional()]) def validate(self, extra_validators=None): if not super().validate(extra_validators): From b6966328fbb0ca70db17118e0c741e85e025e9d0 Mon Sep 17 00:00:00 2001 From: Cayo Puigdefabregas Date: Thu, 12 May 2022 10:09:03 +0200 Subject: [PATCH 17/51] fixed bug #3383 --- ereuse_devicehub/inventory/forms.py | 1 + 1 file changed, 1 insertion(+) diff --git a/ereuse_devicehub/inventory/forms.py b/ereuse_devicehub/inventory/forms.py index bce5ba43..0357e19f 100644 --- a/ereuse_devicehub/inventory/forms.py +++ b/ereuse_devicehub/inventory/forms.py @@ -684,6 +684,7 @@ class NewActionForm(ActionFormMix): class AllocateForm(ActionFormMix): + date = HiddenField('') start_time = DateField('Start time') end_time = DateField('End time', [validators.Optional()]) final_user_code = StringField( From 515f31c5626f7308ddb8d76094598d9ef73cf457 Mon Sep 17 00:00:00 2001 From: Cayo Puigdefabregas Date: Thu, 12 May 2022 10:15:46 +0200 Subject: [PATCH 18/51] fixed bug #3387 --- ereuse_devicehub/templates/inventory/allocate.html | 3 +++ 1 file changed, 3 insertions(+) diff --git a/ereuse_devicehub/templates/inventory/allocate.html b/ereuse_devicehub/templates/inventory/allocate.html index f611a763..2f036795 100644 --- a/ereuse_devicehub/templates/inventory/allocate.html +++ b/ereuse_devicehub/templates/inventory/allocate.html @@ -29,6 +29,9 @@ {% else %}
    {{ field.label(class_="form-label") }} + {% if field == form_new_allocate.start_time %} + * + {% endif %} {{ field(class_="form-control") }} {% if field.errors %}

    From 9b6d77573f6f59ecd93bcf34b57d12078238883a Mon Sep 17 00:00:00 2001 From: Cayo Puigdefabregas Date: Thu, 12 May 2022 10:33:04 +0200 Subject: [PATCH 19/51] fixed Enhancement #3384 --- ereuse_devicehub/inventory/views.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/ereuse_devicehub/inventory/views.py b/ereuse_devicehub/inventory/views.py index a09f4f5b..70c058a4 100644 --- a/ereuse_devicehub/inventory/views.py +++ b/ereuse_devicehub/inventory/views.py @@ -326,7 +326,8 @@ class NewAllocateView(NewActionView, DeviceListMix): messages.error('Action {} error!'.format(self.form.type.data)) for k, v in self.form.errors.items(): value = ';'.join(v) - messages.error('Action Error {key}: {value}!'.format(key=k, value=value)) + key = self.form[k].label.text + messages.error('Action Error {key}: {value}!'.format(key=key, value=value)) next_url = self.get_next_url() return flask.redirect(next_url) From fee1d6787ce0b7d82750f81fc41db6fb4f08545e Mon Sep 17 00:00:00 2001 From: Cayo Puigdefabregas Date: Thu, 12 May 2022 11:03:48 +0200 Subject: [PATCH 20/51] add column created and put order descendent from created field --- ereuse_devicehub/labels/views.py | 4 +++- ereuse_devicehub/templates/labels/label_list.html | 2 ++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/ereuse_devicehub/labels/views.py b/ereuse_devicehub/labels/views.py index e7fc3b0d..421effcb 100644 --- a/ereuse_devicehub/labels/views.py +++ b/ereuse_devicehub/labels/views.py @@ -23,7 +23,9 @@ class TagListView(View): def dispatch_request(self): lots = Lot.query.filter(Lot.owner_id == current_user.id) - tags = Tag.query.filter(Tag.owner_id == current_user.id).order_by(Tag.id) + tags = Tag.query.filter(Tag.owner_id == current_user.id).order_by( + Tag.created.desc() + ) context = { 'lots': lots, 'tags': tags, diff --git a/ereuse_devicehub/templates/labels/label_list.html b/ereuse_devicehub/templates/labels/label_list.html index 7d184477..4f843ea0 100644 --- a/ereuse_devicehub/templates/labels/label_list.html +++ b/ereuse_devicehub/templates/labels/label_list.html @@ -47,6 +47,7 @@ Type Provider Device + Created @@ -62,6 +63,7 @@ {% endif %} + {{ tag.created.strftime('%H:%M %d-%m-%Y') }} {% endfor %} From 0e974c0e928d5356a6bcf164d2c0deb4c38ea6f9 Mon Sep 17 00:00:00 2001 From: Cayo Puigdefabregas Date: Thu, 12 May 2022 11:33:28 +0200 Subject: [PATCH 21/51] add tags in device list --- ereuse_devicehub/templates/inventory/device_list.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ereuse_devicehub/templates/inventory/device_list.html b/ereuse_devicehub/templates/inventory/device_list.html index a3c13576..0f022fd6 100644 --- a/ereuse_devicehub/templates/inventory/device_list.html +++ b/ereuse_devicehub/templates/inventory/device_list.html @@ -213,7 +213,7 @@

    -
    - -
    -

    Download the settings for register devices and erase disks. This is less safe and more fast.

    -
    -
    -
    - -
    -

    Download the settings for register devices and erase disks. This is more safe and more slow.

    -
    -
    diff --git a/ereuse_devicehub/templates/workbench/wbSettings.ini b/ereuse_devicehub/templates/workbench/wbSettings.ini index ea45e547..8ce2cf40 100644 --- a/ereuse_devicehub/templates/workbench/wbSettings.ini +++ b/ereuse_devicehub/templates/workbench/wbSettings.ini @@ -1,18 +1,4 @@ [settings] -DH_TOKEN = {{token}} - -DH_HOST = {{host}} -DH_DATABASE = {{inventory}} -DEVICEHUB_URL = https://${DB_HOST}/${DB_DATABASE}/ - - -WB_BENCHMARK = {{ benchmark }} -WB_STRESS_TEST = {{ stress_test }} -WB_SMART_TEST = short - -WB_ERASE = {{ erase }} -WB_ERASE_STEPS = {{ steps }} -WB_ERASE_LEADING_ZEROS = {{ leading_zeros }} - -WB_DEBUG = True +TOKEN = {{ token }} +URL = {{ url }} diff --git a/ereuse_devicehub/workbench/views.py b/ereuse_devicehub/workbench/views.py index ccbdbdc5..ef0c5f8b 100644 --- a/ereuse_devicehub/workbench/views.py +++ b/ereuse_devicehub/workbench/views.py @@ -35,36 +35,18 @@ class SettingsView(GenericMixView): return flask.render_template(self.template_name, **self.context) def download(self): + url = "https://{}/api/".format(app.config['HOST']) self.wbContext = { 'token': self.get_token(), - 'host': app.config['HOST'], - 'inventory': app.config['SCHEMA'], - 'benchmark': False, - 'stress_test': 1, - 'erase': '', - 'steps': 0, - 'leading_zeros': False, + 'url': url, } - options = {"register": self.register, "soft": self.soft, "hard": self.hard} + options = {"register": self.register} return options[self.opt]() def register(self): data = flask.render_template('workbench/wbSettings.ini', **self.wbContext) return self.response_download(data) - def soft(self): - self.wbContext['erase'] = 'EraseBasic' - self.wbContext['steps'] = 1 - data = flask.render_template('workbench/wbSettings.ini', **self.wbContext) - return self.response_download(data) - - def hard(self): - self.wbContext['erase'] = 'EraseSectors' - self.wbContext['steps'] = 1 - self.wbContext['leading_zeros'] = True - data = flask.render_template('workbench/wbSettings.ini', **self.wbContext) - return self.response_download(data) - def response_download(self, data): bfile = str.encode(data) output = make_response(bfile) From 3a2e0bd61a818642463627c3c2e61f4e80b027b0 Mon Sep 17 00:00:00 2001 From: Cayo Puigdefabregas Date: Thu, 12 May 2022 17:25:02 +0200 Subject: [PATCH 28/51] fixing bugs --- ereuse_devicehub/workbench/views.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ereuse_devicehub/workbench/views.py b/ereuse_devicehub/workbench/views.py index ef0c5f8b..95b8c009 100644 --- a/ereuse_devicehub/workbench/views.py +++ b/ereuse_devicehub/workbench/views.py @@ -29,13 +29,13 @@ class SettingsView(GenericMixView): ) self.opt = request.values.get('opt') - if self.opt in ['register', 'soft', 'hard']: + if self.opt in ['register']: return self.download() return flask.render_template(self.template_name, **self.context) def download(self): - url = "https://{}/api/".format(app.config['HOST']) + url = "https://{}/api/inventory/".format(app.config['HOST']) self.wbContext = { 'token': self.get_token(), 'url': url, From 3c02a5480f557a203de4e7ba5844ee483da11fbe Mon Sep 17 00:00:00 2001 From: Cayo Puigdefabregas Date: Thu, 12 May 2022 17:42:26 +0200 Subject: [PATCH 29/51] fixed test render wbsettings --- tests/test_render_2_0.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/tests/test_render_2_0.py b/tests/test_render_2_0.py index 38dc12cd..083d82ee 100644 --- a/tests/test_render_2_0.py +++ b/tests/test_render_2_0.py @@ -862,7 +862,6 @@ def test_wb_settings_register(user3: UserClientFlask): body, status = user3.get(uri) assert status == '200 OK' - assert "WB_BENCHMARK = False" in body - assert "WB_ERASE = \n" in body - assert "WB_ERASE_STEPS = 0" in body - assert "WB_ERASE_LEADING_ZEROS = False" in body + assert "TOKEN = " in body + assert "URL = https://" in body + assert "/api/inventory/" in body From cea1c2b7175321c09053e803b664dc47dc983d0b Mon Sep 17 00:00:00 2001 From: RubenPX Date: Thu, 12 May 2022 18:44:02 +0200 Subject: [PATCH 30/51] full table replacament --- .../static/js/main_inventory.build.js | 94 +++++++++++++------ ereuse_devicehub/static/js/main_inventory.js | 20 ++-- 2 files changed, 73 insertions(+), 41 deletions(-) diff --git a/ereuse_devicehub/static/js/main_inventory.build.js b/ereuse_devicehub/static/js/main_inventory.build.js index 09006905..a58a8260 100644 --- a/ereuse_devicehub/static/js/main_inventory.build.js +++ b/ereuse_devicehub/static/js/main_inventory.build.js @@ -2,7 +2,7 @@ function _classStaticPrivateFieldSpecGet(receiver, classConstructor, descriptor) { _classCheckPrivateStaticAccess(receiver, classConstructor); _classCheckPrivateStaticFieldDescriptor(descriptor, "get"); return _classApplyDescriptorGet(receiver, descriptor); } -function _classCheckPrivateStaticFieldDescriptor(descriptor, action) { if (descriptor === undefined) { throw new TypeError(`attempted to ${ action } private static field before its declaration`); } } +function _classCheckPrivateStaticFieldDescriptor(descriptor, action) { if (descriptor === undefined) { throw new TypeError("attempted to " + action + " private static field before its declaration"); } } function _classCheckPrivateStaticAccess(receiver, classConstructor) { if (receiver !== classConstructor) { throw new TypeError("Private static access of wrong provenance"); } } @@ -100,22 +100,32 @@ window.addEventListener("DOMContentLoaded", () => { const alertInfoDevices = document.getElementById("select-devices-info"); function itemListCheckChanged() { - const listDevices = TableController.getAllDevicesInCurrentPage(); - const isAllChecked = listDevices.map(itm => itm.checked); + alertInfoDevices.innerHTML = "Selected devices: ".concat(TableController.getSelectedDevices().length, "\n ").concat(TableController.getAllDevices().length != TableController.getSelectedDevices().length ? "Select all devices (".concat(TableController.getAllDevices().length, ")") : "Cancel selection"); - if (isAllChecked.every(bool => bool == true)) { - btnSelectAll.checked = true; - btnSelectAll.indeterminate = false; - alertInfoDevices.innerHTML = "Selected devices: ".concat(TableController.getSelectedDevices().length, "\n ").concat(TableController.getAllDevices().length != TableController.getSelectedDevices().length ? "Select all devices (".concat(TableController.getAllDevices().length, ")") : "Cancel selection"); - alertInfoDevices.classList.remove("d-none"); - } else if (isAllChecked.every(bool => bool == false)) { - btnSelectAll.checked = false; - btnSelectAll.indeterminate = false; + if (TableController.getSelectedDevices().length <= 0) { alertInfoDevices.classList.add("d-none"); } else { - btnSelectAll.indeterminate = true; - alertInfoDevices.classList.add("d-none"); + alertInfoDevices.classList.remove("d-none"); } + + if (TableController.getAllDevices().length == TableController.getSelectedDevices().length) { + btnSelectAll.checked = true; + btnSelectAll.indeterminate = false; + } else if (TableController.getAllSelectedDevicesInCurrentPage().length > 0) { + btnSelectAll.indeterminate = true; + } else { + btnSelectAll.checked = false; + btnSelectAll.indeterminate = false; + } + + if (TableController.getAllDevices().length == 0) { + btnSelectAll.checked = false; + btnSelectAll.disabled = true; + } else { + btnSelectAll.disabled = false; + } + + get_device_list(); } TableController.getAllDevices().forEach(item => { @@ -139,6 +149,7 @@ window.addEventListener("DOMContentLoaded", () => { table.on("datatable.page", () => itemListCheckChanged()); table.on("datatable.perpage", () => itemListCheckChanged()); table.on("datatable.update", () => itemListCheckChanged()); + itemListCheckChanged(); }); function deviceSelect() { @@ -327,25 +338,44 @@ async function processSelectedDevices() { const lotID = lot.id; const srcElement = event.srcElement.parentElement.children[0]; const checked = !srcElement.checked; + const { + indeterminate + } = srcElement; const found = this.list.filter(list => list.lot.id == lotID)[0]; if (checked) { if (found && found.type == "Remove") { - found.type = "Add"; + const affectedDevices = found.devices.filter(dev => found.lot.devices.includes(dev.id)); + + if (affectedDevices.length > 0 && found.indeterminate == false) { + // Remove action from list + actions.list = actions.list.filter(x => x.lot.id != found.lot.id); + } else { + found.type = "Add"; + } } else { this.list.push({ type: "Add", lot, - devices: selectedDevices + devices: selectedDevices, + indeterminate }); } } else if (found && found.type == "Add") { - found.type = "Remove"; + const affectedDevices = found.devices.filter(dev => !found.lot.devices.includes(dev.id)); + + if (affectedDevices.length > 0 && found.indeterminate == false) { + // Remove action from list + actions.list = actions.list.filter(x => x.lot.id != found.lot.id); + } else { + found.type = "Remove"; + } } else { this.list.push({ type: "Remove", lot, - devices: selectedDevices + devices: selectedDevices, + indeterminate }); } @@ -430,19 +460,23 @@ async function processSelectedDevices() { const newRequest = await Api.doRequest(window.location); const tmpDiv = document.createElement("div"); tmpDiv.innerHTML = newRequest; - const newTable = Array.from(tmpDiv.querySelectorAll("table.table > tbody > tr .deviceSelect")).map(x => x.attributes["data-device-dhid"].value); // https://github.com/fiduswriter/Simple-DataTables/wiki/rows()#removeselect-arraynumber - - const rowsToRemove = []; - - for (let i = 0; i < table.activeRows.length; i++) { - const row = table.activeRows[i]; - - if (!newTable.includes(row.querySelector("input").attributes["data-device-dhid"].value)) { - rowsToRemove.push(i); - } - } - - table.rows().remove(rowsToRemove); // Restore state of checkbox + const newTable = document.createElement("table"); + newTable.innerHTML = tmpDiv.querySelector("table").innerHTML; + newTable.classList = "table"; + const oldTable = document.querySelector(".dataTable-wrapper"); + oldTable.parentElement.replaceChild(newTable, oldTable); + table = new simpleDatatables.DataTable(newTable, { + perPage: 20 + }); // https://github.com/fiduswriter/Simple-DataTables/wiki/rows()#removeselect-arraynumber + // const rowsToRemove = [] + // for (let i = 0; i < table.activeRows.length; i++) { + // const row = table.activeRows[i]; + // if (!newTable.includes(row.querySelector("input").attributes["data-device-dhid"].value)) { + // rowsToRemove.push(i) + // } + // } + // table.rows().remove(rowsToRemove); + // // Restore state of checkbox const selectAllBTN = document.getElementById("SelectAllBTN"); selectAllBTN.checked = false; diff --git a/ereuse_devicehub/static/js/main_inventory.js b/ereuse_devicehub/static/js/main_inventory.js index bec04232..00b90cd4 100644 --- a/ereuse_devicehub/static/js/main_inventory.js +++ b/ereuse_devicehub/static/js/main_inventory.js @@ -425,19 +425,17 @@ async function processSelectedDevices() { const tmpDiv = document.createElement("div") tmpDiv.innerHTML = newRequest - const newTable = Array.from(tmpDiv.querySelectorAll("table.table > tbody > tr .deviceSelect")).map(x => x.attributes["data-device-dhid"].value) + + const newTable = document.createElement("table") + newTable.innerHTML = tmpDiv.querySelector("table").innerHTML + newTable.classList = "table" - // https://github.com/fiduswriter/Simple-DataTables/wiki/rows()#removeselect-arraynumber - const rowsToRemove = [] - for (let i = 0; i < table.activeRows.length; i++) { - const row = table.activeRows[i]; - if (!newTable.includes(row.querySelector("input").attributes["data-device-dhid"].value)) { - rowsToRemove.push(i) - } - } - table.rows().remove(rowsToRemove); + const oldTable = document.querySelector(".dataTable-wrapper") + oldTable.parentElement.replaceChild(newTable, oldTable) - // Restore state of checkbox + table = new simpleDatatables.DataTable(newTable, {perPage: 20}) + + // // Restore state of checkbox const selectAllBTN = document.getElementById("SelectAllBTN"); selectAllBTN.checked = false; selectAllBTN.indeterminate = false; From 6a408a08c782ea41c4663110866f1753a764dc3d Mon Sep 17 00:00:00 2001 From: RubenPX Date: Thu, 12 May 2022 18:44:26 +0200 Subject: [PATCH 31/51] set const table to var This allow modify variable --- ereuse_devicehub/templates/inventory/device_list.html | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/ereuse_devicehub/templates/inventory/device_list.html b/ereuse_devicehub/templates/inventory/device_list.html index 0f022fd6..84294c48 100644 --- a/ereuse_devicehub/templates/inventory/device_list.html +++ b/ereuse_devicehub/templates/inventory/device_list.html @@ -357,6 +357,14 @@ {{ dev.verbose_name }} + {% if dev.lots | length > 0 %} +
    +
    + {% for lot in dev.lots %} + {{ lot.name }} + {% endfor %} +
    + {% endif %} @@ -430,7 +438,7 @@ From c7e050182885543033ab61129ef73b8f59669519 Mon Sep 17 00:00:00 2001 From: RubenPX Date: Thu, 12 May 2022 18:58:06 +0200 Subject: [PATCH 32/51] add soft init to reassign events --- .../static/js/main_inventory.build.js | 48 +++++++++++-------- ereuse_devicehub/static/js/main_inventory.js | 38 ++++++++++----- 2 files changed, 54 insertions(+), 32 deletions(-) diff --git a/ereuse_devicehub/static/js/main_inventory.build.js b/ereuse_devicehub/static/js/main_inventory.build.js index a58a8260..48ab00a7 100644 --- a/ereuse_devicehub/static/js/main_inventory.build.js +++ b/ereuse_devicehub/static/js/main_inventory.build.js @@ -95,10 +95,27 @@ var _tableRowsPage = { writable: true, value: () => table.pages[table.rows().dt.currentPage - 1] }; -window.addEventListener("DOMContentLoaded", () => { + +const selectorController = action => { const btnSelectAll = document.getElementById("SelectAllBTN"); const alertInfoDevices = document.getElementById("select-devices-info"); + function softInit() { + TableController.getAllDevices().forEach(item => { + item.addEventListener("click", itemListCheckChanged); + }); // https://github.com/fiduswriter/Simple-DataTables/wiki/Events + + table.on("datatable.page", () => itemListCheckChanged()); + table.on("datatable.perpage", () => itemListCheckChanged()); + table.on("datatable.update", () => itemListCheckChanged()); + } + + if (action == "softInit") { + softInit(); + itemListCheckChanged(); + return; + } + function itemListCheckChanged() { alertInfoDevices.innerHTML = "Selected devices: ".concat(TableController.getSelectedDevices().length, "\n ").concat(TableController.getAllDevices().length != TableController.getSelectedDevices().length ? "Select all devices (".concat(TableController.getAllDevices().length, ")") : "Cancel selection"); @@ -128,9 +145,6 @@ window.addEventListener("DOMContentLoaded", () => { get_device_list(); } - TableController.getAllDevices().forEach(item => { - item.addEventListener("click", itemListCheckChanged); - }); btnSelectAll.addEventListener("click", event => { const checkedState = event.target.checked; TableController.getAllDevicesInCurrentPage().forEach(ckeckbox => { @@ -144,13 +158,12 @@ window.addEventListener("DOMContentLoaded", () => { ckeckbox.checked = !checkState; }); itemListCheckChanged(); - }); // https://github.com/fiduswriter/Simple-DataTables/wiki/Events - - table.on("datatable.page", () => itemListCheckChanged()); - table.on("datatable.perpage", () => itemListCheckChanged()); - table.on("datatable.update", () => itemListCheckChanged()); + }); + softInit(); itemListCheckChanged(); -}); +}; + +window.addEventListener("DOMContentLoaded", () => selectorController()); function deviceSelect() { const devices_count = TableController.getSelectedDevices().length; @@ -467,20 +480,13 @@ async function processSelectedDevices() { oldTable.parentElement.replaceChild(newTable, oldTable); table = new simpleDatatables.DataTable(newTable, { perPage: 20 - }); // https://github.com/fiduswriter/Simple-DataTables/wiki/rows()#removeselect-arraynumber - // const rowsToRemove = [] - // for (let i = 0; i < table.activeRows.length; i++) { - // const row = table.activeRows[i]; - // if (!newTable.includes(row.querySelector("input").attributes["data-device-dhid"].value)) { - // rowsToRemove.push(i) - // } - // } - // table.rows().remove(rowsToRemove); - // // Restore state of checkbox + }); // // Restore state of checkbox const selectAllBTN = document.getElementById("SelectAllBTN"); selectAllBTN.checked = false; - selectAllBTN.indeterminate = false; + selectAllBTN.indeterminate = false; // Re-init SelectorController + + selectorController("softInit"); } } diff --git a/ereuse_devicehub/static/js/main_inventory.js b/ereuse_devicehub/static/js/main_inventory.js index 00b90cd4..7a2ecc93 100644 --- a/ereuse_devicehub/static/js/main_inventory.js +++ b/ereuse_devicehub/static/js/main_inventory.js @@ -78,10 +78,28 @@ class TableController { /** * Select all functionality */ -window.addEventListener("DOMContentLoaded", () => { + +const selectorController = (action) => { const btnSelectAll = document.getElementById("SelectAllBTN"); const alertInfoDevices = document.getElementById("select-devices-info"); + function softInit() { + TableController.getAllDevices().forEach(item => { + item.addEventListener("click", itemListCheckChanged); + }) + + // https://github.com/fiduswriter/Simple-DataTables/wiki/Events + table.on("datatable.page", () => itemListCheckChanged()); + table.on("datatable.perpage", () => itemListCheckChanged()); + table.on("datatable.update", () => itemListCheckChanged()); + } + + if (action == "softInit") { + softInit(); + itemListCheckChanged(); + return; + } + function itemListCheckChanged() { alertInfoDevices.innerHTML = `Selected devices: ${TableController.getSelectedDevices().length} ${TableController.getAllDevices().length != TableController.getSelectedDevices().length @@ -114,11 +132,7 @@ window.addEventListener("DOMContentLoaded", () => { get_device_list(); } - - TableController.getAllDevices().forEach(item => { - item.addEventListener("click", itemListCheckChanged); - }) - + btnSelectAll.addEventListener("click", event => { const checkedState = event.target.checked; TableController.getAllDevicesInCurrentPage().forEach(ckeckbox => { ckeckbox.checked = checkedState }); @@ -131,13 +145,12 @@ window.addEventListener("DOMContentLoaded", () => { itemListCheckChanged() }) - // https://github.com/fiduswriter/Simple-DataTables/wiki/Events - table.on("datatable.page", () => itemListCheckChanged()); - table.on("datatable.perpage", () => itemListCheckChanged()); - table.on("datatable.update", () => itemListCheckChanged()); + softInit(); itemListCheckChanged(); -}) +} + +window.addEventListener("DOMContentLoaded", () => selectorController()); function deviceSelect() { const devices_count = TableController.getSelectedDevices().length; @@ -439,6 +452,9 @@ async function processSelectedDevices() { const selectAllBTN = document.getElementById("SelectAllBTN"); selectAllBTN.checked = false; selectAllBTN.indeterminate = false; + + // Re-init SelectorController + selectorController("softInit"); } } From d54f2166dc1ab28915ed83eb5842e235a6ba3140 Mon Sep 17 00:00:00 2001 From: Santiago Lamora Date: Fri, 13 May 2022 10:22:32 +0200 Subject: [PATCH 33/51] Keep unassigned as default option on device list Unassigned devices are the ones that require user attention and also all devices view will generate larger responses and more server load. --- ereuse_devicehub/inventory/forms.py | 4 ++-- ereuse_devicehub/inventory/views.py | 13 ++++++++----- .../templates/ereuse_devicehub/base_site.html | 4 ++-- 3 files changed, 12 insertions(+), 9 deletions(-) diff --git a/ereuse_devicehub/inventory/forms.py b/ereuse_devicehub/inventory/forms.py index fffefa39..cec788b4 100644 --- a/ereuse_devicehub/inventory/forms.py +++ b/ereuse_devicehub/inventory/forms.py @@ -133,7 +133,7 @@ class FilterForm(FlaskForm): super().__init__(*args, **kwargs) self.lots = lots self.lot_id = lot_id - self.unassigned = kwargs.pop('unassigned', False) + self.only_unassigned = kwargs.pop('only_unassigned', True) self._get_types() def _get_types(self): @@ -150,7 +150,7 @@ class FilterForm(FlaskForm): self.devices = Device.query.filter(Device.id.in_(device_ids)) else: self.devices = Device.query.filter(Device.owner_id == g.user.id) - if self.unassigned: + if self.only_unassigned: self.devices = self.devices.filter_by(lots=None) def search(self): diff --git a/ereuse_devicehub/inventory/views.py b/ereuse_devicehub/inventory/views.py index e11e9d2c..09e61535 100644 --- a/ereuse_devicehub/inventory/views.py +++ b/ereuse_devicehub/inventory/views.py @@ -1,5 +1,6 @@ import csv import logging +from distutils.util import strtobool from io import StringIO import flask @@ -40,10 +41,10 @@ logger = logging.getLogger(__name__) class DeviceListMix(GenericMixView): template_name = 'inventory/device_list.html' - def get_context(self, lot_id, unassigned=False): + def get_context(self, lot_id, only_unassigned=True): super().get_context() lots = self.context['lots'] - form_filter = FilterForm(lots, lot_id, unassigned=unassigned) + form_filter = FilterForm(lots, lot_id, only_unassigned=only_unassigned) devices = form_filter.search() lot = None @@ -71,7 +72,7 @@ class DeviceListMix(GenericMixView): 'lot': lot, 'tags': self.get_user_tags(), 'list_devices': self.get_selected_devices(form_new_action), - 'unassigned_devices': unassigned, + 'unassigned_devices': only_unassigned, } ) @@ -94,8 +95,10 @@ class DeviceListMix(GenericMixView): class DeviceListView(DeviceListMix): def dispatch_request(self, lot_id=None): - unassigned = request.args.get('unassigned', False) - self.get_context(lot_id, unassigned) + only_unassigned = request.args.get( + 'only_unassigned', default=True, type=strtobool + ) + self.get_context(lot_id, only_unassigned) return flask.render_template(self.template_name, **self.context) diff --git a/ereuse_devicehub/templates/ereuse_devicehub/base_site.html b/ereuse_devicehub/templates/ereuse_devicehub/base_site.html index 2f254204..74147824 100644 --- a/ereuse_devicehub/templates/ereuse_devicehub/base_site.html +++ b/ereuse_devicehub/templates/ereuse_devicehub/base_site.html @@ -104,14 +104,14 @@
  • - {{ action.type }} + {{ device.is_status(action) }} {{ action.type }} — - {{ action }} + {% if device.is_status(action) %} + {{ action }} {{ action.type }} + {% else %} + {{ action }} + {% endif %}
    From ae221dadd19b028011aba9c61146978f166115d8 Mon Sep 17 00:00:00 2001 From: Cayo Puigdefabregas Date: Mon, 16 May 2022 09:53:22 +0200 Subject: [PATCH 42/51] fixed tests --- tests/files/basic-stock.csv | 4 +- tests/files/basic.csv | 4 +- tests/files/export_devices.csv | 4 +- tests/files/proposal_extended_csv_report.csv | 6 +- tests/test_action.py | 1225 ++++++++++-------- tests/test_documents.py | 638 +++++---- tests/test_render_2_0.py | 7 +- 7 files changed, 1049 insertions(+), 839 deletions(-) diff --git a/tests/files/basic-stock.csv b/tests/files/basic-stock.csv index d037b191..818af1d0 100644 --- a/tests/files/basic-stock.csv +++ b/tests/files/basic-stock.csv @@ -1,2 +1,2 @@ -Type,Chassis,Serial Number,Model,Manufacturer,Registered in,Physical state,Trading state,Price,Processor,RAM (MB),Data Storage Size (MB),Rate,Range,Processor Rate,Processor Range,RAM Rate,RAM Range,Data Storage Rate,Data Storage Range -Desktop,Microtower,d1s,d1ml,d1mr,Tue Jul 2 10:35:10 2019,,,,p1ml,0,0,1.0,Very low,1.0,Very low,1.0,Very low,1.0,Very low +Type;Chassis;Serial Number;Model;Manufacturer;Registered in;Physical state;Allocate state;Lifecycle state;Price;Processor;RAM (MB);Data Storage Size (MB);Rate;Range;Processor Rate;Processor Range;RAM Rate;RAM Range;Data Storage Rate;Data Storage Range +Desktop;Microtower;d1s;d1ml;d1mr;Mon May 16 09:34:22 2022;;;;;p1ml;0;0;1.0;Very low;1.0;Very low;1.0;Very low;1.0;Very low diff --git a/tests/files/basic.csv b/tests/files/basic.csv index 63b4a58a..c625d055 100644 --- a/tests/files/basic.csv +++ b/tests/files/basic.csv @@ -1,2 +1,2 @@ -DHID;DocumentID;Public Link;Lots;Tag 1 Type;Tag 1 ID;Tag 1 Organization;Tag 2 Type;Tag 2 ID;Tag 2 Organization;Tag 3 Type;Tag 3 ID;Tag 3 Organization;Device Hardware ID;Device Type;Device Chassis;Device Serial Number;Device Model;Device Manufacturer;Registered in;Registered (process);Updated in (software);Updated in (web);Physical state;Trading state;Processor;RAM (MB);Data Storage Size (MB);Processor 1;Processor 1 Manufacturer;Processor 1 Model;Processor 1 Serial Number;Processor 1 Number of cores;Processor 1 Speed (GHz);Benchmark Processor 1 (points);Benchmark ProcessorSysbench Processor 1 (points);Processor 2;Processor 2 Manufacturer;Processor 2 Model;Processor 2 Serial Number;Processor 2 Number of cores;Processor 2 Speed (GHz);Benchmark Processor 2 (points);Benchmark ProcessorSysbench Processor 2 (points);RamModule 1;RamModule 1 Manufacturer;RamModule 1 Model;RamModule 1 Serial Number;RamModule 1 Size (MB);RamModule 1 Speed (MHz);RamModule 2;RamModule 2 Manufacturer;RamModule 2 Model;RamModule 2 Serial Number;RamModule 2 Size (MB);RamModule 2 Speed (MHz);RamModule 3;RamModule 3 Manufacturer;RamModule 3 Model;RamModule 3 Serial Number;RamModule 3 Size (MB);RamModule 3 Speed (MHz);RamModule 4;RamModule 4 Manufacturer;RamModule 4 Model;RamModule 4 Serial Number;RamModule 4 Size (MB);RamModule 4 Speed (MHz);DataStorage 1;DataStorage 1 Manufacturer;DataStorage 1 Model;DataStorage 1 Serial Number;DataStorage 1 Size (MB);Erasure DataStorage 1;Erasure DataStorage 1 Serial Number;Erasure DataStorage 1 Size (MB);Erasure DataStorage 1 Software;Erasure DataStorage 1 Result;Erasure DataStorage 1 Certificate URL;Erasure DataStorage 1 Type;Erasure DataStorage 1 Method;Erasure DataStorage 1 Elapsed (hours);Erasure DataStorage 1 Date;Erasure DataStorage 1 Steps;Erasure DataStorage 1 Steps Start Time;Erasure DataStorage 1 Steps End Time;Benchmark DataStorage 1 Read Speed (MB/s);Benchmark DataStorage 1 Writing speed (MB/s);Test DataStorage 1 Software;Test DataStorage 1 Type;Test DataStorage 1 Result;Test DataStorage 1 Power cycle count;Test DataStorage 1 Lifetime (days);Test DataStorage 1 Power on hours;DataStorage 2;DataStorage 2 Manufacturer;DataStorage 2 Model;DataStorage 2 Serial Number;DataStorage 2 Size (MB);Erasure DataStorage 2;Erasure DataStorage 2 Serial Number;Erasure DataStorage 2 Size (MB);Erasure DataStorage 2 Software;Erasure DataStorage 2 Result;Erasure DataStorage 2 Certificate URL;Erasure DataStorage 2 Type;Erasure DataStorage 2 Method;Erasure DataStorage 2 Elapsed (hours);Erasure DataStorage 2 Date;Erasure DataStorage 2 Steps;Erasure DataStorage 2 Steps Start Time;Erasure DataStorage 2 Steps End Time;Benchmark DataStorage 2 Read Speed (MB/s);Benchmark DataStorage 2 Writing speed (MB/s);Test DataStorage 2 Software;Test DataStorage 2 Type;Test DataStorage 2 Result;Test DataStorage 2 Power cycle count;Test DataStorage 2 Lifetime (days);Test DataStorage 2 Power on hours;DataStorage 3;DataStorage 3 Manufacturer;DataStorage 3 Model;DataStorage 3 Serial Number;DataStorage 3 Size (MB);Erasure DataStorage 3;Erasure DataStorage 3 Serial Number;Erasure DataStorage 3 Size (MB);Erasure DataStorage 3 Software;Erasure DataStorage 3 Result;Erasure DataStorage 3 Certificate URL;Erasure DataStorage 3 Type;Erasure DataStorage 3 Method;Erasure DataStorage 3 Elapsed (hours);Erasure DataStorage 3 Date;Erasure DataStorage 3 Steps;Erasure DataStorage 3 Steps Start Time;Erasure DataStorage 3 Steps End Time;Benchmark DataStorage 3 Read Speed (MB/s);Benchmark DataStorage 3 Writing speed (MB/s);Test DataStorage 3 Software;Test DataStorage 3 Type;Test DataStorage 3 Result;Test DataStorage 3 Power cycle count;Test DataStorage 3 Lifetime (days);Test DataStorage 3 Power on hours;DataStorage 4;DataStorage 4 Manufacturer;DataStorage 4 Model;DataStorage 4 Serial Number;DataStorage 4 Size (MB);Erasure DataStorage 4;Erasure DataStorage 4 Serial Number;Erasure DataStorage 4 Size (MB);Erasure DataStorage 4 Software;Erasure DataStorage 4 Result;Erasure DataStorage 4 Certificate URL;Erasure DataStorage 4 Type;Erasure DataStorage 4 Method;Erasure DataStorage 4 Elapsed (hours);Erasure DataStorage 4 Date;Erasure DataStorage 4 Steps;Erasure DataStorage 4 Steps Start Time;Erasure DataStorage 4 Steps End Time;Benchmark DataStorage 4 Read Speed (MB/s);Benchmark DataStorage 4 Writing speed (MB/s);Test DataStorage 4 Software;Test DataStorage 4 Type;Test DataStorage 4 Result;Test DataStorage 4 Power cycle count;Test DataStorage 4 Lifetime (days);Test DataStorage 4 Power on hours;Motherboard 1;Motherboard 1 Manufacturer;Motherboard 1 Model;Motherboard 1 Serial Number;Display 1;Display 1 Manufacturer;Display 1 Model;Display 1 Serial Number;GraphicCard 1;GraphicCard 1 Manufacturer;GraphicCard 1 Model;GraphicCard 1 Serial Number;GraphicCard 1 Memory (MB);GraphicCard 2;GraphicCard 2 Manufacturer;GraphicCard 2 Model;GraphicCard 2 Serial Number;GraphicCard 2 Memory (MB);NetworkAdapter 1;NetworkAdapter 1 Manufacturer;NetworkAdapter 1 Model;NetworkAdapter 1 Serial Number;NetworkAdapter 2;NetworkAdapter 2 Manufacturer;NetworkAdapter 2 Model;NetworkAdapter 2 Serial Number;SoundCard 1;SoundCard 1 Manufacturer;SoundCard 1 Model;SoundCard 1 Serial Number;SoundCard 2;SoundCard 2 Manufacturer;SoundCard 2 Model;SoundCard 2 Serial Number;Device Rate;Device Range;Processor Rate;Processor Range;RAM Rate;RAM Range;Data Storage Rate;Data Storage Range;Price;Benchmark RamSysbench (points) -O48N2;;http://localhost/devices/O48N2;;named;O48N2;FooOrg;;;;;;;desktop-d1mr-d1ml-d1s;Desktop;Microtower;d1s;d1ml;d1mr;Tue Nov 23 15:12:59 2021;Workbench 11.0;2021-11-23 15:12:59.333542+01:00;;;;p1ml;0;0;Processor 6: model p1ml, S/N p1s;p1mr;p1ml;p1s;;1.6;2410.0;;;;;;;;;;RamModule 5: model rm1ml, S/N rm1s;rm1mr;rm1ml;rm1s;;1333;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GraphicCard 4: model gc1ml, S/N gc1s;gc1mr;gc1ml;gc1s;;;;;;;;;;;;;;;;;;;;;;;1.0;VERY_LOW;1.0;VERY_LOW;1.0;VERY_LOW;1.0;VERY_LOW;; +DHID;DocumentID;Public Link;Lots;Tag 1 Type;Tag 1 ID;Tag 1 Organization;Tag 2 Type;Tag 2 ID;Tag 2 Organization;Tag 3 Type;Tag 3 ID;Tag 3 Organization;Device Hardware ID;Device Type;Device Chassis;Device Serial Number;Device Model;Device Manufacturer;Registered in;Registered (process);Updated in (software);Updated in (web);Physical state;Allocate state;Lifecycle state;Processor;RAM (MB);Data Storage Size (MB);Processor 1;Processor 1 Manufacturer;Processor 1 Model;Processor 1 Serial Number;Processor 1 Number of cores;Processor 1 Speed (GHz);Benchmark Processor 1 (points);Benchmark ProcessorSysbench Processor 1 (points);Processor 2;Processor 2 Manufacturer;Processor 2 Model;Processor 2 Serial Number;Processor 2 Number of cores;Processor 2 Speed (GHz);Benchmark Processor 2 (points);Benchmark ProcessorSysbench Processor 2 (points);RamModule 1;RamModule 1 Manufacturer;RamModule 1 Model;RamModule 1 Serial Number;RamModule 1 Size (MB);RamModule 1 Speed (MHz);RamModule 2;RamModule 2 Manufacturer;RamModule 2 Model;RamModule 2 Serial Number;RamModule 2 Size (MB);RamModule 2 Speed (MHz);RamModule 3;RamModule 3 Manufacturer;RamModule 3 Model;RamModule 3 Serial Number;RamModule 3 Size (MB);RamModule 3 Speed (MHz);RamModule 4;RamModule 4 Manufacturer;RamModule 4 Model;RamModule 4 Serial Number;RamModule 4 Size (MB);RamModule 4 Speed (MHz);DataStorage 1;DataStorage 1 Manufacturer;DataStorage 1 Model;DataStorage 1 Serial Number;DataStorage 1 Size (MB);Erasure DataStorage 1;Erasure DataStorage 1 Serial Number;Erasure DataStorage 1 Size (MB);Erasure DataStorage 1 Software;Erasure DataStorage 1 Result;Erasure DataStorage 1 Certificate URL;Erasure DataStorage 1 Type;Erasure DataStorage 1 Method;Erasure DataStorage 1 Elapsed (hours);Erasure DataStorage 1 Date;Erasure DataStorage 1 Steps;Erasure DataStorage 1 Steps Start Time;Erasure DataStorage 1 Steps End Time;Benchmark DataStorage 1 Read Speed (MB/s);Benchmark DataStorage 1 Writing speed (MB/s);Test DataStorage 1 Software;Test DataStorage 1 Type;Test DataStorage 1 Result;Test DataStorage 1 Power cycle count;Test DataStorage 1 Lifetime (days);Test DataStorage 1 Power on hours;DataStorage 2;DataStorage 2 Manufacturer;DataStorage 2 Model;DataStorage 2 Serial Number;DataStorage 2 Size (MB);Erasure DataStorage 2;Erasure DataStorage 2 Serial Number;Erasure DataStorage 2 Size (MB);Erasure DataStorage 2 Software;Erasure DataStorage 2 Result;Erasure DataStorage 2 Certificate URL;Erasure DataStorage 2 Type;Erasure DataStorage 2 Method;Erasure DataStorage 2 Elapsed (hours);Erasure DataStorage 2 Date;Erasure DataStorage 2 Steps;Erasure DataStorage 2 Steps Start Time;Erasure DataStorage 2 Steps End Time;Benchmark DataStorage 2 Read Speed (MB/s);Benchmark DataStorage 2 Writing speed (MB/s);Test DataStorage 2 Software;Test DataStorage 2 Type;Test DataStorage 2 Result;Test DataStorage 2 Power cycle count;Test DataStorage 2 Lifetime (days);Test DataStorage 2 Power on hours;DataStorage 3;DataStorage 3 Manufacturer;DataStorage 3 Model;DataStorage 3 Serial Number;DataStorage 3 Size (MB);Erasure DataStorage 3;Erasure DataStorage 3 Serial Number;Erasure DataStorage 3 Size (MB);Erasure DataStorage 3 Software;Erasure DataStorage 3 Result;Erasure DataStorage 3 Certificate URL;Erasure DataStorage 3 Type;Erasure DataStorage 3 Method;Erasure DataStorage 3 Elapsed (hours);Erasure DataStorage 3 Date;Erasure DataStorage 3 Steps;Erasure DataStorage 3 Steps Start Time;Erasure DataStorage 3 Steps End Time;Benchmark DataStorage 3 Read Speed (MB/s);Benchmark DataStorage 3 Writing speed (MB/s);Test DataStorage 3 Software;Test DataStorage 3 Type;Test DataStorage 3 Result;Test DataStorage 3 Power cycle count;Test DataStorage 3 Lifetime (days);Test DataStorage 3 Power on hours;DataStorage 4;DataStorage 4 Manufacturer;DataStorage 4 Model;DataStorage 4 Serial Number;DataStorage 4 Size (MB);Erasure DataStorage 4;Erasure DataStorage 4 Serial Number;Erasure DataStorage 4 Size (MB);Erasure DataStorage 4 Software;Erasure DataStorage 4 Result;Erasure DataStorage 4 Certificate URL;Erasure DataStorage 4 Type;Erasure DataStorage 4 Method;Erasure DataStorage 4 Elapsed (hours);Erasure DataStorage 4 Date;Erasure DataStorage 4 Steps;Erasure DataStorage 4 Steps Start Time;Erasure DataStorage 4 Steps End Time;Benchmark DataStorage 4 Read Speed (MB/s);Benchmark DataStorage 4 Writing speed (MB/s);Test DataStorage 4 Software;Test DataStorage 4 Type;Test DataStorage 4 Result;Test DataStorage 4 Power cycle count;Test DataStorage 4 Lifetime (days);Test DataStorage 4 Power on hours;Motherboard 1;Motherboard 1 Manufacturer;Motherboard 1 Model;Motherboard 1 Serial Number;Display 1;Display 1 Manufacturer;Display 1 Model;Display 1 Serial Number;GraphicCard 1;GraphicCard 1 Manufacturer;GraphicCard 1 Model;GraphicCard 1 Serial Number;GraphicCard 1 Memory (MB);GraphicCard 2;GraphicCard 2 Manufacturer;GraphicCard 2 Model;GraphicCard 2 Serial Number;GraphicCard 2 Memory (MB);NetworkAdapter 1;NetworkAdapter 1 Manufacturer;NetworkAdapter 1 Model;NetworkAdapter 1 Serial Number;NetworkAdapter 2;NetworkAdapter 2 Manufacturer;NetworkAdapter 2 Model;NetworkAdapter 2 Serial Number;SoundCard 1;SoundCard 1 Manufacturer;SoundCard 1 Model;SoundCard 1 Serial Number;SoundCard 2;SoundCard 2 Manufacturer;SoundCard 2 Model;SoundCard 2 Serial Number;Device Rate;Device Range;Processor Rate;Processor Range;RAM Rate;RAM Range;Data Storage Rate;Data Storage Range;Price;Benchmark RamSysbench (points) +O48N2;;http://localhost/devices/O48N2;;named;O48N2;FooOrg;;;;;;;desktop-d1mr-d1ml-d1s;Desktop;Microtower;d1s;d1ml;d1mr;Mon May 16 09:21:01 2022;Workbench 11.0;2022-05-16 09:21:01.621979+02:00;;;;;p1ml;0;0;Processor 6: model p1ml, S/N p1s;p1mr;p1ml;p1s;;1.6;2410.0;;;;;;;;;;RamModule 5: model rm1ml, S/N rm1s;rm1mr;rm1ml;rm1s;;1333;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GraphicCard 4: model gc1ml, S/N gc1s;gc1mr;gc1ml;gc1s;;;;;;;;;;;;;;;;;;;;;;;1.0;VERY_LOW;1.0;VERY_LOW;1.0;VERY_LOW;1.0;VERY_LOW;; diff --git a/tests/files/export_devices.csv b/tests/files/export_devices.csv index 586a25e7..712e7249 100644 --- a/tests/files/export_devices.csv +++ b/tests/files/export_devices.csv @@ -1,2 +1,2 @@ -DHID;DocumentID;Public Link;Lots;Tag 1 Type;Tag 1 ID;Tag 1 Organization;Tag 2 Type;Tag 2 ID;Tag 2 Organization;Tag 3 Type;Tag 3 ID;Tag 3 Organization;Device Hardware ID;Device Type;Device Chassis;Device Serial Number;Device Model;Device Manufacturer;Registered in;Registered (process);Updated in (software);Updated in (web);Physical state;Trading state;Processor;RAM (MB);Data Storage Size (MB);Processor 1;Processor 1 Manufacturer;Processor 1 Model;Processor 1 Serial Number;Processor 1 Number of cores;Processor 1 Speed (GHz);Benchmark Processor 1 (points);Benchmark ProcessorSysbench Processor 1 (points);Processor 2;Processor 2 Manufacturer;Processor 2 Model;Processor 2 Serial Number;Processor 2 Number of cores;Processor 2 Speed (GHz);Benchmark Processor 2 (points);Benchmark ProcessorSysbench Processor 2 (points);RamModule 1;RamModule 1 Manufacturer;RamModule 1 Model;RamModule 1 Serial Number;RamModule 1 Size (MB);RamModule 1 Speed (MHz);RamModule 2;RamModule 2 Manufacturer;RamModule 2 Model;RamModule 2 Serial Number;RamModule 2 Size (MB);RamModule 2 Speed (MHz);RamModule 3;RamModule 3 Manufacturer;RamModule 3 Model;RamModule 3 Serial Number;RamModule 3 Size (MB);RamModule 3 Speed (MHz);RamModule 4;RamModule 4 Manufacturer;RamModule 4 Model;RamModule 4 Serial Number;RamModule 4 Size (MB);RamModule 4 Speed (MHz);DataStorage 1;DataStorage 1 Manufacturer;DataStorage 1 Model;DataStorage 1 Serial Number;DataStorage 1 Size (MB);Erasure DataStorage 1;Erasure DataStorage 1 Serial Number;Erasure DataStorage 1 Size (MB);Erasure DataStorage 1 Software;Erasure DataStorage 1 Result;Erasure DataStorage 1 Certificate URL;Erasure DataStorage 1 Type;Erasure DataStorage 1 Method;Erasure DataStorage 1 Elapsed (hours);Erasure DataStorage 1 Date;Erasure DataStorage 1 Steps;Erasure DataStorage 1 Steps Start Time;Erasure DataStorage 1 Steps End Time;Benchmark DataStorage 1 Read Speed (MB/s);Benchmark DataStorage 1 Writing speed (MB/s);Test DataStorage 1 Software;Test DataStorage 1 Type;Test DataStorage 1 Result;Test DataStorage 1 Power cycle count;Test DataStorage 1 Lifetime (days);Test DataStorage 1 Power on hours;DataStorage 2;DataStorage 2 Manufacturer;DataStorage 2 Model;DataStorage 2 Serial Number;DataStorage 2 Size (MB);Erasure DataStorage 2;Erasure DataStorage 2 Serial Number;Erasure DataStorage 2 Size (MB);Erasure DataStorage 2 Software;Erasure DataStorage 2 Result;Erasure DataStorage 2 Certificate URL;Erasure DataStorage 2 Type;Erasure DataStorage 2 Method;Erasure DataStorage 2 Elapsed (hours);Erasure DataStorage 2 Date;Erasure DataStorage 2 Steps;Erasure DataStorage 2 Steps Start Time;Erasure DataStorage 2 Steps End Time;Benchmark DataStorage 2 Read Speed (MB/s);Benchmark DataStorage 2 Writing speed (MB/s);Test DataStorage 2 Software;Test DataStorage 2 Type;Test DataStorage 2 Result;Test DataStorage 2 Power cycle count;Test DataStorage 2 Lifetime (days);Test DataStorage 2 Power on hours;DataStorage 3;DataStorage 3 Manufacturer;DataStorage 3 Model;DataStorage 3 Serial Number;DataStorage 3 Size (MB);Erasure DataStorage 3;Erasure DataStorage 3 Serial Number;Erasure DataStorage 3 Size (MB);Erasure DataStorage 3 Software;Erasure DataStorage 3 Result;Erasure DataStorage 3 Certificate URL;Erasure DataStorage 3 Type;Erasure DataStorage 3 Method;Erasure DataStorage 3 Elapsed (hours);Erasure DataStorage 3 Date;Erasure DataStorage 3 Steps;Erasure DataStorage 3 Steps Start Time;Erasure DataStorage 3 Steps End Time;Benchmark DataStorage 3 Read Speed (MB/s);Benchmark DataStorage 3 Writing speed (MB/s);Test DataStorage 3 Software;Test DataStorage 3 Type;Test DataStorage 3 Result;Test DataStorage 3 Power cycle count;Test DataStorage 3 Lifetime (days);Test DataStorage 3 Power on hours;DataStorage 4;DataStorage 4 Manufacturer;DataStorage 4 Model;DataStorage 4 Serial Number;DataStorage 4 Size (MB);Erasure DataStorage 4;Erasure DataStorage 4 Serial Number;Erasure DataStorage 4 Size (MB);Erasure DataStorage 4 Software;Erasure DataStorage 4 Result;Erasure DataStorage 4 Certificate URL;Erasure DataStorage 4 Type;Erasure DataStorage 4 Method;Erasure DataStorage 4 Elapsed (hours);Erasure DataStorage 4 Date;Erasure DataStorage 4 Steps;Erasure DataStorage 4 Steps Start Time;Erasure DataStorage 4 Steps End Time;Benchmark DataStorage 4 Read Speed (MB/s);Benchmark DataStorage 4 Writing speed (MB/s);Test DataStorage 4 Software;Test DataStorage 4 Type;Test DataStorage 4 Result;Test DataStorage 4 Power cycle count;Test DataStorage 4 Lifetime (days);Test DataStorage 4 Power on hours;Motherboard 1;Motherboard 1 Manufacturer;Motherboard 1 Model;Motherboard 1 Serial Number;Display 1;Display 1 Manufacturer;Display 1 Model;Display 1 Serial Number;GraphicCard 1;GraphicCard 1 Manufacturer;GraphicCard 1 Model;GraphicCard 1 Serial Number;GraphicCard 1 Memory (MB);GraphicCard 2;GraphicCard 2 Manufacturer;GraphicCard 2 Model;GraphicCard 2 Serial Number;GraphicCard 2 Memory (MB);NetworkAdapter 1;NetworkAdapter 1 Manufacturer;NetworkAdapter 1 Model;NetworkAdapter 1 Serial Number;NetworkAdapter 2;NetworkAdapter 2 Manufacturer;NetworkAdapter 2 Model;NetworkAdapter 2 Serial Number;SoundCard 1;SoundCard 1 Manufacturer;SoundCard 1 Model;SoundCard 1 Serial Number;SoundCard 2;SoundCard 2 Manufacturer;SoundCard 2 Model;SoundCard 2 Serial Number;Device Rate;Device Range;Processor Rate;Processor Range;RAM Rate;RAM Range;Data Storage Rate;Data Storage Range;Price;Benchmark RamSysbench (points) -O48N2;;http://localhost/devices/O48N2;;named;O48N2;FooOrg;;;;;;;laptop-asustek_computer_inc-1001pxd-b8oaas048285-14:da:e9:42:f6:7b;Laptop;Netbook;b8oaas048285;1001pxd;asustek computer inc.;Tue Apr 19 18:13:44 2022;Workbench 11.0a2;2022-04-19 18:13:45.018710+02:00;;;;intel atom cpu n455 @ 2.66ghz;1024;238475;Processor 6: model intel atom cpu n455 @ 2.66ghz, S/N None;intel corp.;intel atom cpu n455 @ 2.66ghz;;1;2.667;6666.24;164.0803;;;;;;;;;RamModule 10: model None, S/N None;;;;1024;667;;;;;;;;;;;;;;;;;;;HardDrive 11: model hts54322, S/N e2024242cv86mm;hitachi;hts54322;e2024242cv86mm;238475;harddrive-hitachi-hts54322-e2024242cv86mm;e2024242cv86mm;238475;Workbench 11.0a2;Success;;EraseBasic;Shred;1:16:49;2022-04-19 18:13:44.975393+02:00;✓ – StepRandom 1:16:49;2018-07-03 11:15:22.257059+02:00;2018-07-03 12:32:11.843190+02:00;66.2;21.8;Workbench 11.0a2;Short;Failure;;;0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;Motherboard 12: model 1001pxd, S/N eee0123456720;asustek computer inc.;1001pxd;eee0123456720;;;;;GraphicCard 7: model atom processor d4xx/d5xx/n4xx/n5xx integrated graphics controller, S/N None;intel corporation;atom processor d4xx/d5xx/n4xx/n5xx integrated graphics controller;;256;;;;;;NetworkAdapter 4: model ar9285 wireless network adapter, S/N 74:2f:68:8b:fd:c9;qualcomm atheros;ar9285 wireless network adapter;74:2f:68:8b:fd:c9;NetworkAdapter 5: model ar8152 v2.0 fast ethernet, S/N 14:da:e9:42:f6:7b;qualcomm atheros;ar8152 v2.0 fast ethernet;14:da:e9:42:f6:7b;SoundCard 8: model nm10/ich7 family high definition audio controller, S/N None;intel corporation;nm10/ich7 family high definition audio controller;;SoundCard 9: model usb 2.0 uvc vga webcam, S/N 0x0001;azurewave;usb 2.0 uvc vga webcam;0x0001;1.75;LOW;1.55;LOW;1.53;LOW;3.76;HIGH;52.50 €;15.7188 +DHID;DocumentID;Public Link;Lots;Tag 1 Type;Tag 1 ID;Tag 1 Organization;Tag 2 Type;Tag 2 ID;Tag 2 Organization;Tag 3 Type;Tag 3 ID;Tag 3 Organization;Device Hardware ID;Device Type;Device Chassis;Device Serial Number;Device Model;Device Manufacturer;Registered in;Registered (process);Updated in (software);Updated in (web);Physical state;Allocate state;Lifecycle state;Processor;RAM (MB);Data Storage Size (MB);Processor 1;Processor 1 Manufacturer;Processor 1 Model;Processor 1 Serial Number;Processor 1 Number of cores;Processor 1 Speed (GHz);Benchmark Processor 1 (points);Benchmark ProcessorSysbench Processor 1 (points);Processor 2;Processor 2 Manufacturer;Processor 2 Model;Processor 2 Serial Number;Processor 2 Number of cores;Processor 2 Speed (GHz);Benchmark Processor 2 (points);Benchmark ProcessorSysbench Processor 2 (points);RamModule 1;RamModule 1 Manufacturer;RamModule 1 Model;RamModule 1 Serial Number;RamModule 1 Size (MB);RamModule 1 Speed (MHz);RamModule 2;RamModule 2 Manufacturer;RamModule 2 Model;RamModule 2 Serial Number;RamModule 2 Size (MB);RamModule 2 Speed (MHz);RamModule 3;RamModule 3 Manufacturer;RamModule 3 Model;RamModule 3 Serial Number;RamModule 3 Size (MB);RamModule 3 Speed (MHz);RamModule 4;RamModule 4 Manufacturer;RamModule 4 Model;RamModule 4 Serial Number;RamModule 4 Size (MB);RamModule 4 Speed (MHz);DataStorage 1;DataStorage 1 Manufacturer;DataStorage 1 Model;DataStorage 1 Serial Number;DataStorage 1 Size (MB);Erasure DataStorage 1;Erasure DataStorage 1 Serial Number;Erasure DataStorage 1 Size (MB);Erasure DataStorage 1 Software;Erasure DataStorage 1 Result;Erasure DataStorage 1 Certificate URL;Erasure DataStorage 1 Type;Erasure DataStorage 1 Method;Erasure DataStorage 1 Elapsed (hours);Erasure DataStorage 1 Date;Erasure DataStorage 1 Steps;Erasure DataStorage 1 Steps Start Time;Erasure DataStorage 1 Steps End Time;Benchmark DataStorage 1 Read Speed (MB/s);Benchmark DataStorage 1 Writing speed (MB/s);Test DataStorage 1 Software;Test DataStorage 1 Type;Test DataStorage 1 Result;Test DataStorage 1 Power cycle count;Test DataStorage 1 Lifetime (days);Test DataStorage 1 Power on hours;DataStorage 2;DataStorage 2 Manufacturer;DataStorage 2 Model;DataStorage 2 Serial Number;DataStorage 2 Size (MB);Erasure DataStorage 2;Erasure DataStorage 2 Serial Number;Erasure DataStorage 2 Size (MB);Erasure DataStorage 2 Software;Erasure DataStorage 2 Result;Erasure DataStorage 2 Certificate URL;Erasure DataStorage 2 Type;Erasure DataStorage 2 Method;Erasure DataStorage 2 Elapsed (hours);Erasure DataStorage 2 Date;Erasure DataStorage 2 Steps;Erasure DataStorage 2 Steps Start Time;Erasure DataStorage 2 Steps End Time;Benchmark DataStorage 2 Read Speed (MB/s);Benchmark DataStorage 2 Writing speed (MB/s);Test DataStorage 2 Software;Test DataStorage 2 Type;Test DataStorage 2 Result;Test DataStorage 2 Power cycle count;Test DataStorage 2 Lifetime (days);Test DataStorage 2 Power on hours;DataStorage 3;DataStorage 3 Manufacturer;DataStorage 3 Model;DataStorage 3 Serial Number;DataStorage 3 Size (MB);Erasure DataStorage 3;Erasure DataStorage 3 Serial Number;Erasure DataStorage 3 Size (MB);Erasure DataStorage 3 Software;Erasure DataStorage 3 Result;Erasure DataStorage 3 Certificate URL;Erasure DataStorage 3 Type;Erasure DataStorage 3 Method;Erasure DataStorage 3 Elapsed (hours);Erasure DataStorage 3 Date;Erasure DataStorage 3 Steps;Erasure DataStorage 3 Steps Start Time;Erasure DataStorage 3 Steps End Time;Benchmark DataStorage 3 Read Speed (MB/s);Benchmark DataStorage 3 Writing speed (MB/s);Test DataStorage 3 Software;Test DataStorage 3 Type;Test DataStorage 3 Result;Test DataStorage 3 Power cycle count;Test DataStorage 3 Lifetime (days);Test DataStorage 3 Power on hours;DataStorage 4;DataStorage 4 Manufacturer;DataStorage 4 Model;DataStorage 4 Serial Number;DataStorage 4 Size (MB);Erasure DataStorage 4;Erasure DataStorage 4 Serial Number;Erasure DataStorage 4 Size (MB);Erasure DataStorage 4 Software;Erasure DataStorage 4 Result;Erasure DataStorage 4 Certificate URL;Erasure DataStorage 4 Type;Erasure DataStorage 4 Method;Erasure DataStorage 4 Elapsed (hours);Erasure DataStorage 4 Date;Erasure DataStorage 4 Steps;Erasure DataStorage 4 Steps Start Time;Erasure DataStorage 4 Steps End Time;Benchmark DataStorage 4 Read Speed (MB/s);Benchmark DataStorage 4 Writing speed (MB/s);Test DataStorage 4 Software;Test DataStorage 4 Type;Test DataStorage 4 Result;Test DataStorage 4 Power cycle count;Test DataStorage 4 Lifetime (days);Test DataStorage 4 Power on hours;Motherboard 1;Motherboard 1 Manufacturer;Motherboard 1 Model;Motherboard 1 Serial Number;Display 1;Display 1 Manufacturer;Display 1 Model;Display 1 Serial Number;GraphicCard 1;GraphicCard 1 Manufacturer;GraphicCard 1 Model;GraphicCard 1 Serial Number;GraphicCard 1 Memory (MB);GraphicCard 2;GraphicCard 2 Manufacturer;GraphicCard 2 Model;GraphicCard 2 Serial Number;GraphicCard 2 Memory (MB);NetworkAdapter 1;NetworkAdapter 1 Manufacturer;NetworkAdapter 1 Model;NetworkAdapter 1 Serial Number;NetworkAdapter 2;NetworkAdapter 2 Manufacturer;NetworkAdapter 2 Model;NetworkAdapter 2 Serial Number;SoundCard 1;SoundCard 1 Manufacturer;SoundCard 1 Model;SoundCard 1 Serial Number;SoundCard 2;SoundCard 2 Manufacturer;SoundCard 2 Model;SoundCard 2 Serial Number;Device Rate;Device Range;Processor Rate;Processor Range;RAM Rate;RAM Range;Data Storage Rate;Data Storage Range;Price;Benchmark RamSysbench (points) +O48N2;;http://localhost/devices/O48N2;;named;O48N2;FooOrg;;;;;;;laptop-asustek_computer_inc-1001pxd-b8oaas048285-14:da:e9:42:f6:7b;Laptop;Netbook;b8oaas048285;1001pxd;asustek computer inc.;Fri May 13 17:31:46 2022;Workbench 11.0a2;2022-05-13 17:31:46.356350+02:00;;;;;intel atom cpu n455 @ 2.66ghz;1024;238475;Processor 6: model intel atom cpu n455 @ 2.66ghz, S/N None;intel corp.;intel atom cpu n455 @ 2.66ghz;;1;2.667;6666.24;164.0803;;;;;;;;;RamModule 10: model None, S/N None;;;;1024;667;;;;;;;;;;;;;;;;;;;HardDrive 11: model hts54322, S/N e2024242cv86mm;hitachi;hts54322;e2024242cv86mm;238475;harddrive-hitachi-hts54322-e2024242cv86mm;e2024242cv86mm;238475;Workbench 11.0a2;Success;;EraseBasic;Shred;1:16:49;2022-05-13 17:31:46.312640+02:00;✓ – StepRandom 1:16:49;2018-07-03 11:15:22.257059+02:00;2018-07-03 12:32:11.843190+02:00;66.2;21.8;Workbench 11.0a2;Short;Failure;;;0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;Motherboard 12: model 1001pxd, S/N eee0123456720;asustek computer inc.;1001pxd;eee0123456720;;;;;GraphicCard 7: model atom processor d4xx/d5xx/n4xx/n5xx integrated graphics controller, S/N None;intel corporation;atom processor d4xx/d5xx/n4xx/n5xx integrated graphics controller;;256;;;;;;NetworkAdapter 4: model ar9285 wireless network adapter, S/N 74:2f:68:8b:fd:c9;qualcomm atheros;ar9285 wireless network adapter;74:2f:68:8b:fd:c9;NetworkAdapter 5: model ar8152 v2.0 fast ethernet, S/N 14:da:e9:42:f6:7b;qualcomm atheros;ar8152 v2.0 fast ethernet;14:da:e9:42:f6:7b;SoundCard 8: model nm10/ich7 family high definition audio controller, S/N None;intel corporation;nm10/ich7 family high definition audio controller;;SoundCard 9: model usb 2.0 uvc vga webcam, S/N 0x0001;azurewave;usb 2.0 uvc vga webcam;0x0001;1.75;LOW;1.55;LOW;1.53;LOW;3.76;HIGH;52.50 €;15.7188 diff --git a/tests/files/proposal_extended_csv_report.csv b/tests/files/proposal_extended_csv_report.csv index 39b02f88..54582e05 100644 --- a/tests/files/proposal_extended_csv_report.csv +++ b/tests/files/proposal_extended_csv_report.csv @@ -1,3 +1,3 @@ -DHID;DocumentID;Public Link;Lots;Tag 1 Type;Tag 1 ID;Tag 1 Organization;Tag 2 Type;Tag 2 ID;Tag 2 Organization;Tag 3 Type;Tag 3 ID;Tag 3 Organization;Device Hardware ID;Device Type;Device Chassis;Device Serial Number;Device Model;Device Manufacturer;Registered in;Registered (process);Updated in (software);Updated in (web);Physical state;Trading state;Processor;RAM (MB);Data Storage Size (MB);Processor 1;Processor 1 Manufacturer;Processor 1 Model;Processor 1 Serial Number;Processor 1 Number of cores;Processor 1 Speed (GHz);Benchmark Processor 1 (points);Benchmark ProcessorSysbench Processor 1 (points);Processor 2;Processor 2 Manufacturer;Processor 2 Model;Processor 2 Serial Number;Processor 2 Number of cores;Processor 2 Speed (GHz);Benchmark Processor 2 (points);Benchmark ProcessorSysbench Processor 2 (points);RamModule 1;RamModule 1 Manufacturer;RamModule 1 Model;RamModule 1 Serial Number;RamModule 1 Size (MB);RamModule 1 Speed (MHz);RamModule 2;RamModule 2 Manufacturer;RamModule 2 Model;RamModule 2 Serial Number;RamModule 2 Size (MB);RamModule 2 Speed (MHz);RamModule 3;RamModule 3 Manufacturer;RamModule 3 Model;RamModule 3 Serial Number;RamModule 3 Size (MB);RamModule 3 Speed (MHz);RamModule 4;RamModule 4 Manufacturer;RamModule 4 Model;RamModule 4 Serial Number;RamModule 4 Size (MB);RamModule 4 Speed (MHz);DataStorage 1;DataStorage 1 Manufacturer;DataStorage 1 Model;DataStorage 1 Serial Number;DataStorage 1 Size (MB);Erasure DataStorage 1;Erasure DataStorage 1 Serial Number;Erasure DataStorage 1 Size (MB);Erasure DataStorage 1 Software;Erasure DataStorage 1 Result;Erasure DataStorage 1 Certificate URL;Erasure DataStorage 1 Type;Erasure DataStorage 1 Method;Erasure DataStorage 1 Elapsed (hours);Erasure DataStorage 1 Date;Erasure DataStorage 1 Steps;Erasure DataStorage 1 Steps Start Time;Erasure DataStorage 1 Steps End Time;Benchmark DataStorage 1 Read Speed (MB/s);Benchmark DataStorage 1 Writing speed (MB/s);Test DataStorage 1 Software;Test DataStorage 1 Type;Test DataStorage 1 Result;Test DataStorage 1 Power cycle count;Test DataStorage 1 Lifetime (days);Test DataStorage 1 Power on hours;DataStorage 2;DataStorage 2 Manufacturer;DataStorage 2 Model;DataStorage 2 Serial Number;DataStorage 2 Size (MB);Erasure DataStorage 2;Erasure DataStorage 2 Serial Number;Erasure DataStorage 2 Size (MB);Erasure DataStorage 2 Software;Erasure DataStorage 2 Result;Erasure DataStorage 2 Certificate URL;Erasure DataStorage 2 Type;Erasure DataStorage 2 Method;Erasure DataStorage 2 Elapsed (hours);Erasure DataStorage 2 Date;Erasure DataStorage 2 Steps;Erasure DataStorage 2 Steps Start Time;Erasure DataStorage 2 Steps End Time;Benchmark DataStorage 2 Read Speed (MB/s);Benchmark DataStorage 2 Writing speed (MB/s);Test DataStorage 2 Software;Test DataStorage 2 Type;Test DataStorage 2 Result;Test DataStorage 2 Power cycle count;Test DataStorage 2 Lifetime (days);Test DataStorage 2 Power on hours;DataStorage 3;DataStorage 3 Manufacturer;DataStorage 3 Model;DataStorage 3 Serial Number;DataStorage 3 Size (MB);Erasure DataStorage 3;Erasure DataStorage 3 Serial Number;Erasure DataStorage 3 Size (MB);Erasure DataStorage 3 Software;Erasure DataStorage 3 Result;Erasure DataStorage 3 Certificate URL;Erasure DataStorage 3 Type;Erasure DataStorage 3 Method;Erasure DataStorage 3 Elapsed (hours);Erasure DataStorage 3 Date;Erasure DataStorage 3 Steps;Erasure DataStorage 3 Steps Start Time;Erasure DataStorage 3 Steps End Time;Benchmark DataStorage 3 Read Speed (MB/s);Benchmark DataStorage 3 Writing speed (MB/s);Test DataStorage 3 Software;Test DataStorage 3 Type;Test DataStorage 3 Result;Test DataStorage 3 Power cycle count;Test DataStorage 3 Lifetime (days);Test DataStorage 3 Power on hours;DataStorage 4;DataStorage 4 Manufacturer;DataStorage 4 Model;DataStorage 4 Serial Number;DataStorage 4 Size (MB);Erasure DataStorage 4;Erasure DataStorage 4 Serial Number;Erasure DataStorage 4 Size (MB);Erasure DataStorage 4 Software;Erasure DataStorage 4 Result;Erasure DataStorage 4 Certificate URL;Erasure DataStorage 4 Type;Erasure DataStorage 4 Method;Erasure DataStorage 4 Elapsed (hours);Erasure DataStorage 4 Date;Erasure DataStorage 4 Steps;Erasure DataStorage 4 Steps Start Time;Erasure DataStorage 4 Steps End Time;Benchmark DataStorage 4 Read Speed (MB/s);Benchmark DataStorage 4 Writing speed (MB/s);Test DataStorage 4 Software;Test DataStorage 4 Type;Test DataStorage 4 Result;Test DataStorage 4 Power cycle count;Test DataStorage 4 Lifetime (days);Test DataStorage 4 Power on hours;Motherboard 1;Motherboard 1 Manufacturer;Motherboard 1 Model;Motherboard 1 Serial Number;Display 1;Display 1 Manufacturer;Display 1 Model;Display 1 Serial Number;GraphicCard 1;GraphicCard 1 Manufacturer;GraphicCard 1 Model;GraphicCard 1 Serial Number;GraphicCard 1 Memory (MB);GraphicCard 2;GraphicCard 2 Manufacturer;GraphicCard 2 Model;GraphicCard 2 Serial Number;GraphicCard 2 Memory (MB);NetworkAdapter 1;NetworkAdapter 1 Manufacturer;NetworkAdapter 1 Model;NetworkAdapter 1 Serial Number;NetworkAdapter 2;NetworkAdapter 2 Manufacturer;NetworkAdapter 2 Model;NetworkAdapter 2 Serial Number;SoundCard 1;SoundCard 1 Manufacturer;SoundCard 1 Model;SoundCard 1 Serial Number;SoundCard 2;SoundCard 2 Manufacturer;SoundCard 2 Model;SoundCard 2 Serial Number;Device Rate;Device Range;Processor Rate;Processor Range;RAM Rate;RAM Range;Data Storage Rate;Data Storage Range;Price;Benchmark RamSysbench (points) -O48N2;;http://localhost/devices/O48N2;;named;O48N2;FooOrg;;;;;;;laptop-asustek_computer_inc-1001pxd-b8oaas048285-14:da:e9:42:f6:7b;Laptop;Netbook;b8oaas048285;1001pxd;asustek computer inc.;Fri Nov 26 14:29:39 2021;Workbench 11.0a2;2021-11-26 14:29:39.966467+01:00;;;;intel atom cpu n455 @ 2.66ghz;1024;238475;Processor 6: model intel atom cpu n455 @ 2.66ghz, S/N None;intel corp.;intel atom cpu n455 @ 2.66ghz;;1;2.667;6666.24;164.0803;;;;;;;;;RamModule 10: model None, S/N None;;;;1024;667;;;;;;;;;;;;;;;;;;;HardDrive 11: model hts54322, S/N e2024242cv86mm;hitachi;hts54322;e2024242cv86mm;238475;harddrive-hitachi-hts54322-e2024242cv86mm;e2024242cv86mm;238475;Workbench 11.0a2;Success;;EraseBasic;Shred;1:16:49;2021-11-26 14:29:39.927141+01:00;✓ – StepRandom 1:16:49;2018-07-03 11:15:22.257059+02:00;2018-07-03 12:32:11.843190+02:00;66.2;21.8;Workbench 11.0a2;Short;Failure;;;0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;Motherboard 12: model 1001pxd, S/N eee0123456720;asustek computer inc.;1001pxd;eee0123456720;;;;;GraphicCard 7: model atom processor d4xx/d5xx/n4xx/n5xx integrated graphics controller, S/N None;intel corporation;atom processor d4xx/d5xx/n4xx/n5xx integrated graphics controller;;256;;;;;;NetworkAdapter 4: model ar9285 wireless network adapter, S/N 74:2f:68:8b:fd:c9;qualcomm atheros;ar9285 wireless network adapter;74:2f:68:8b:fd:c9;NetworkAdapter 5: model ar8152 v2.0 fast ethernet, S/N 14:da:e9:42:f6:7b;qualcomm atheros;ar8152 v2.0 fast ethernet;14:da:e9:42:f6:7b;SoundCard 8: model nm10/ich7 family high definition audio controller, S/N None;intel corporation;nm10/ich7 family high definition audio controller;;SoundCard 9: model usb 2.0 uvc vga webcam, S/N 0x0001;azurewave;usb 2.0 uvc vga webcam;0x0001;1.75;LOW;1.55;LOW;1.53;LOW;3.76;HIGH;52.50 €;15.7188 -J2MA2;;http://localhost/devices/J2MA2;;named;J2MA2;FooOrg;;;;;;;laptop-asustek_computer_inc-1001pxd-b8oaas048287-14:da:e9:42:f6:7c;Laptop;Netbook;b8oaas048287;1001pxd;asustek computer inc.;Fri Nov 26 14:29:40 2021;Workbench 11.0b11;2021-11-26 14:29:40.289858+01:00;;;;intel atom cpu n455 @ 1.66ghz;2048;558558;Processor 17: model intel atom cpu n455 @ 1.66ghz, S/N None;intel corp.;intel atom cpu n455 @ 1.66ghz;;1;1.667;6666.24;164.0803;;;;;;;;;RamModule 21: model None, S/N None;;;;1024;667;RamModule 22: model 48594d503131325336344350362d53362020, S/N 4f43487b;hynix semiconductor;48594d503131325336344350362d53362020;4f43487b;1024;667;;;;;;;;;;;;;HardDrive 23: model hts54322, S/N e2024242cv86hj;hitachi;hts54322;e2024242cv86hj;238475;harddrive-hitachi-hts54322-e2024242cv86hj;e2024242cv86hj;238475;Workbench 11.0b11;Success;;EraseBasic;Shred;1:16:49;2021-11-26 14:29:40.244699+01:00;✓ – StepRandom 1:16:49;2018-07-03 11:15:22.257059+02:00;2018-07-03 12:32:11.843190+02:00;66.2;21.8;Workbench 11.0b11;Extended;Failure;;;0;DataStorage 24: model wdc wd1600bevt-2, S/N wd-wx11a80w7430;western digital;wdc wd1600bevt-2;wd-wx11a80w7430;160041;datastorage-western_digital-wdc_wd1600bevt-2-wd-wx11a80w7430;wd-wx11a80w7430;160041;Workbench 11.0b11;Failure;;EraseBasic;Shred;0:45:36;2021-11-26 14:29:40.246908+01:00;✓ – StepRandom 0:45:36;2019-10-23 09:49:54.410830+02:00;2019-10-23 10:35:31.400587+02:00;41.6;17.3;Workbench 11.0b11;Short;Success;5293;195 days, 12:00:00;4692;SolidStateDrive 25: model wdc wd1600bevt-2, S/N wd-wx11a80w7430;western digital;wdc wd1600bevt-2;wd-wx11a80w7430;160042;solidstatedrive-western_digital-wdc_wd1600bevt-2-wd-wx11a80w7430;wd-wx11a80w7430;160042;Workbench 11.0b11;Success;;EraseSectors;Badblocks;1:46:03;2021-11-26 14:29:40.250841+01:00;✓ – StepRandom 0:46:03,✓ – StepZero 1:00:00;2019-08-19 18:48:19.690458+02:00,2019-08-19 19:34:22.690458+02:00;2019-08-19 19:34:22.930562+02:00,2019-08-19 20:34:22.930562+02:00;41.1;17.1;Workbench 11.0b11;Short;Success;5231;194 days, 17:00:00;4673;;;;;;;;;;;;;;;;;;;;;;;;;;;Motherboard 26: model 1001pxd, S/N eee0123456789;asustek computer inc.;1001pxd;eee0123456789;;"auo ""auo""";auo lcd monitor;;GraphicCard 18: model atom processor d4xx/d5xx/n4xx/n5xx integrated graphics controller, S/N None;intel corporation;atom processor d4xx/d5xx/n4xx/n5xx integrated graphics controller;;256;;;;;;NetworkAdapter 15: model ar9285 wireless network adapter, S/N 74:2f:68:8b:fd:c8;qualcomm atheros;ar9285 wireless network adapter;74:2f:68:8b:fd:c8;NetworkAdapter 16: model ar8152 v2.0 fast ethernet, S/N 14:da:e9:42:f6:7c;qualcomm atheros;ar8152 v2.0 fast ethernet;14:da:e9:42:f6:7c;SoundCard 19: model nm10/ich7 family high definition audio controller, S/N None;intel corporation;nm10/ich7 family high definition audio controller;;SoundCard 20: model usb 2.0 uvc vga webcam, S/N 0x0001;azurewave;usb 2.0 uvc vga webcam;0x0001;1.72;LOW;1.31;LOW;1.99;LOW;3.97;HIGH;51.60 €;15.7188 +DHID;DocumentID;Public Link;Lots;Tag 1 Type;Tag 1 ID;Tag 1 Organization;Tag 2 Type;Tag 2 ID;Tag 2 Organization;Tag 3 Type;Tag 3 ID;Tag 3 Organization;Device Hardware ID;Device Type;Device Chassis;Device Serial Number;Device Model;Device Manufacturer;Registered in;Registered (process);Updated in (software);Updated in (web);Physical state;Allocate state;Lifecycle state;Processor;RAM (MB);Data Storage Size (MB);Processor 1;Processor 1 Manufacturer;Processor 1 Model;Processor 1 Serial Number;Processor 1 Number of cores;Processor 1 Speed (GHz);Benchmark Processor 1 (points);Benchmark ProcessorSysbench Processor 1 (points);Processor 2;Processor 2 Manufacturer;Processor 2 Model;Processor 2 Serial Number;Processor 2 Number of cores;Processor 2 Speed (GHz);Benchmark Processor 2 (points);Benchmark ProcessorSysbench Processor 2 (points);RamModule 1;RamModule 1 Manufacturer;RamModule 1 Model;RamModule 1 Serial Number;RamModule 1 Size (MB);RamModule 1 Speed (MHz);RamModule 2;RamModule 2 Manufacturer;RamModule 2 Model;RamModule 2 Serial Number;RamModule 2 Size (MB);RamModule 2 Speed (MHz);RamModule 3;RamModule 3 Manufacturer;RamModule 3 Model;RamModule 3 Serial Number;RamModule 3 Size (MB);RamModule 3 Speed (MHz);RamModule 4;RamModule 4 Manufacturer;RamModule 4 Model;RamModule 4 Serial Number;RamModule 4 Size (MB);RamModule 4 Speed (MHz);DataStorage 1;DataStorage 1 Manufacturer;DataStorage 1 Model;DataStorage 1 Serial Number;DataStorage 1 Size (MB);Erasure DataStorage 1;Erasure DataStorage 1 Serial Number;Erasure DataStorage 1 Size (MB);Erasure DataStorage 1 Software;Erasure DataStorage 1 Result;Erasure DataStorage 1 Certificate URL;Erasure DataStorage 1 Type;Erasure DataStorage 1 Method;Erasure DataStorage 1 Elapsed (hours);Erasure DataStorage 1 Date;Erasure DataStorage 1 Steps;Erasure DataStorage 1 Steps Start Time;Erasure DataStorage 1 Steps End Time;Benchmark DataStorage 1 Read Speed (MB/s);Benchmark DataStorage 1 Writing speed (MB/s);Test DataStorage 1 Software;Test DataStorage 1 Type;Test DataStorage 1 Result;Test DataStorage 1 Power cycle count;Test DataStorage 1 Lifetime (days);Test DataStorage 1 Power on hours;DataStorage 2;DataStorage 2 Manufacturer;DataStorage 2 Model;DataStorage 2 Serial Number;DataStorage 2 Size (MB);Erasure DataStorage 2;Erasure DataStorage 2 Serial Number;Erasure DataStorage 2 Size (MB);Erasure DataStorage 2 Software;Erasure DataStorage 2 Result;Erasure DataStorage 2 Certificate URL;Erasure DataStorage 2 Type;Erasure DataStorage 2 Method;Erasure DataStorage 2 Elapsed (hours);Erasure DataStorage 2 Date;Erasure DataStorage 2 Steps;Erasure DataStorage 2 Steps Start Time;Erasure DataStorage 2 Steps End Time;Benchmark DataStorage 2 Read Speed (MB/s);Benchmark DataStorage 2 Writing speed (MB/s);Test DataStorage 2 Software;Test DataStorage 2 Type;Test DataStorage 2 Result;Test DataStorage 2 Power cycle count;Test DataStorage 2 Lifetime (days);Test DataStorage 2 Power on hours;DataStorage 3;DataStorage 3 Manufacturer;DataStorage 3 Model;DataStorage 3 Serial Number;DataStorage 3 Size (MB);Erasure DataStorage 3;Erasure DataStorage 3 Serial Number;Erasure DataStorage 3 Size (MB);Erasure DataStorage 3 Software;Erasure DataStorage 3 Result;Erasure DataStorage 3 Certificate URL;Erasure DataStorage 3 Type;Erasure DataStorage 3 Method;Erasure DataStorage 3 Elapsed (hours);Erasure DataStorage 3 Date;Erasure DataStorage 3 Steps;Erasure DataStorage 3 Steps Start Time;Erasure DataStorage 3 Steps End Time;Benchmark DataStorage 3 Read Speed (MB/s);Benchmark DataStorage 3 Writing speed (MB/s);Test DataStorage 3 Software;Test DataStorage 3 Type;Test DataStorage 3 Result;Test DataStorage 3 Power cycle count;Test DataStorage 3 Lifetime (days);Test DataStorage 3 Power on hours;DataStorage 4;DataStorage 4 Manufacturer;DataStorage 4 Model;DataStorage 4 Serial Number;DataStorage 4 Size (MB);Erasure DataStorage 4;Erasure DataStorage 4 Serial Number;Erasure DataStorage 4 Size (MB);Erasure DataStorage 4 Software;Erasure DataStorage 4 Result;Erasure DataStorage 4 Certificate URL;Erasure DataStorage 4 Type;Erasure DataStorage 4 Method;Erasure DataStorage 4 Elapsed (hours);Erasure DataStorage 4 Date;Erasure DataStorage 4 Steps;Erasure DataStorage 4 Steps Start Time;Erasure DataStorage 4 Steps End Time;Benchmark DataStorage 4 Read Speed (MB/s);Benchmark DataStorage 4 Writing speed (MB/s);Test DataStorage 4 Software;Test DataStorage 4 Type;Test DataStorage 4 Result;Test DataStorage 4 Power cycle count;Test DataStorage 4 Lifetime (days);Test DataStorage 4 Power on hours;Motherboard 1;Motherboard 1 Manufacturer;Motherboard 1 Model;Motherboard 1 Serial Number;Display 1;Display 1 Manufacturer;Display 1 Model;Display 1 Serial Number;GraphicCard 1;GraphicCard 1 Manufacturer;GraphicCard 1 Model;GraphicCard 1 Serial Number;GraphicCard 1 Memory (MB);GraphicCard 2;GraphicCard 2 Manufacturer;GraphicCard 2 Model;GraphicCard 2 Serial Number;GraphicCard 2 Memory (MB);NetworkAdapter 1;NetworkAdapter 1 Manufacturer;NetworkAdapter 1 Model;NetworkAdapter 1 Serial Number;NetworkAdapter 2;NetworkAdapter 2 Manufacturer;NetworkAdapter 2 Model;NetworkAdapter 2 Serial Number;SoundCard 1;SoundCard 1 Manufacturer;SoundCard 1 Model;SoundCard 1 Serial Number;SoundCard 2;SoundCard 2 Manufacturer;SoundCard 2 Model;SoundCard 2 Serial Number;Device Rate;Device Range;Processor Rate;Processor Range;RAM Rate;RAM Range;Data Storage Rate;Data Storage Range;Price;Benchmark RamSysbench (points) +O48N2;;http://localhost/devices/O48N2;;named;O48N2;FooOrg;;;;;;;laptop-asustek_computer_inc-1001pxd-b8oaas048285-14:da:e9:42:f6:7b;Laptop;Netbook;b8oaas048285;1001pxd;asustek computer inc.;Mon May 16 09:26:36 2022;Workbench 11.0a2;2022-05-16 09:26:36.684401+02:00;;;;;intel atom cpu n455 @ 2.66ghz;1024;238475;Processor 6: model intel atom cpu n455 @ 2.66ghz, S/N None;intel corp.;intel atom cpu n455 @ 2.66ghz;;1;2.667;6666.24;164.0803;;;;;;;;;RamModule 10: model None, S/N None;;;;1024;667;;;;;;;;;;;;;;;;;;;HardDrive 11: model hts54322, S/N e2024242cv86mm;hitachi;hts54322;e2024242cv86mm;238475;harddrive-hitachi-hts54322-e2024242cv86mm;e2024242cv86mm;238475;Workbench 11.0a2;Success;;EraseBasic;Shred;1:16:49;2022-05-16 09:26:36.641175+02:00;✓ – StepRandom 1:16:49;2018-07-03 11:15:22.257059+02:00;2018-07-03 12:32:11.843190+02:00;66.2;21.8;Workbench 11.0a2;Short;Failure;;;0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;Motherboard 12: model 1001pxd, S/N eee0123456720;asustek computer inc.;1001pxd;eee0123456720;;;;;GraphicCard 7: model atom processor d4xx/d5xx/n4xx/n5xx integrated graphics controller, S/N None;intel corporation;atom processor d4xx/d5xx/n4xx/n5xx integrated graphics controller;;256;;;;;;NetworkAdapter 4: model ar9285 wireless network adapter, S/N 74:2f:68:8b:fd:c9;qualcomm atheros;ar9285 wireless network adapter;74:2f:68:8b:fd:c9;NetworkAdapter 5: model ar8152 v2.0 fast ethernet, S/N 14:da:e9:42:f6:7b;qualcomm atheros;ar8152 v2.0 fast ethernet;14:da:e9:42:f6:7b;SoundCard 8: model nm10/ich7 family high definition audio controller, S/N None;intel corporation;nm10/ich7 family high definition audio controller;;SoundCard 9: model usb 2.0 uvc vga webcam, S/N 0x0001;azurewave;usb 2.0 uvc vga webcam;0x0001;1.75;LOW;1.55;LOW;1.53;LOW;3.76;HIGH;52.50 €;15.7188 +J2MA2;;http://localhost/devices/J2MA2;;named;J2MA2;FooOrg;;;;;;;laptop-asustek_computer_inc-1001pxd-b8oaas048287-14:da:e9:42:f6:7c;Laptop;Netbook;b8oaas048287;1001pxd;asustek computer inc.;Mon May 16 09:26:36 2022;Workbench 11.0b11;2022-05-16 09:26:36.984894+02:00;;;;;intel atom cpu n455 @ 1.66ghz;2048;558558;Processor 17: model intel atom cpu n455 @ 1.66ghz, S/N None;intel corp.;intel atom cpu n455 @ 1.66ghz;;1;1.667;6666.24;164.0803;;;;;;;;;RamModule 21: model None, S/N None;;;;1024;667;RamModule 22: model 48594d503131325336344350362d53362020, S/N 4f43487b;hynix semiconductor;48594d503131325336344350362d53362020;4f43487b;1024;667;;;;;;;;;;;;;HardDrive 23: model hts54322, S/N e2024242cv86hj;hitachi;hts54322;e2024242cv86hj;238475;harddrive-hitachi-hts54322-e2024242cv86hj;e2024242cv86hj;238475;Workbench 11.0b11;Success;;EraseBasic;Shred;1:16:49;2022-05-16 09:26:36.929441+02:00;✓ – StepRandom 1:16:49;2018-07-03 11:15:22.257059+02:00;2018-07-03 12:32:11.843190+02:00;66.2;21.8;Workbench 11.0b11;Extended;Failure;;;0;DataStorage 24: model wdc wd1600bevt-2, S/N wd-wx11a80w7430;western digital;wdc wd1600bevt-2;wd-wx11a80w7430;160041;datastorage-western_digital-wdc_wd1600bevt-2-wd-wx11a80w7430;wd-wx11a80w7430;160041;Workbench 11.0b11;Failure;;EraseBasic;Shred;0:45:36;2022-05-16 09:26:36.931932+02:00;✓ – StepRandom 0:45:36;2019-10-23 09:49:54.410830+02:00;2019-10-23 10:35:31.400587+02:00;41.6;17.3;Workbench 11.0b11;Short;Success;5293;195 days, 12:00:00;4692;SolidStateDrive 25: model wdc wd1600bevt-2, S/N wd-wx11a80w7430;western digital;wdc wd1600bevt-2;wd-wx11a80w7430;160042;solidstatedrive-western_digital-wdc_wd1600bevt-2-wd-wx11a80w7430;wd-wx11a80w7430;160042;Workbench 11.0b11;Success;;EraseSectors;Badblocks;1:46:03;2022-05-16 09:26:36.935748+02:00;✓ – StepRandom 0:46:03,✓ – StepZero 1:00:00;2019-08-19 18:48:19.690458+02:00,2019-08-19 19:34:22.690458+02:00;2019-08-19 19:34:22.930562+02:00,2019-08-19 20:34:22.930562+02:00;41.1;17.1;Workbench 11.0b11;Short;Success;5231;194 days, 17:00:00;4673;;;;;;;;;;;;;;;;;;;;;;;;;;;Motherboard 26: model 1001pxd, S/N eee0123456789;asustek computer inc.;1001pxd;eee0123456789;;"auo ""auo""";auo lcd monitor;;GraphicCard 18: model atom processor d4xx/d5xx/n4xx/n5xx integrated graphics controller, S/N None;intel corporation;atom processor d4xx/d5xx/n4xx/n5xx integrated graphics controller;;256;;;;;;NetworkAdapter 15: model ar9285 wireless network adapter, S/N 74:2f:68:8b:fd:c8;qualcomm atheros;ar9285 wireless network adapter;74:2f:68:8b:fd:c8;NetworkAdapter 16: model ar8152 v2.0 fast ethernet, S/N 14:da:e9:42:f6:7c;qualcomm atheros;ar8152 v2.0 fast ethernet;14:da:e9:42:f6:7c;SoundCard 19: model nm10/ich7 family high definition audio controller, S/N None;intel corporation;nm10/ich7 family high definition audio controller;;SoundCard 20: model usb 2.0 uvc vga webcam, S/N 0x0001;azurewave;usb 2.0 uvc vga webcam;0x0001;1.72;LOW;1.31;LOW;1.99;LOW;3.97;HIGH;51.60 €;15.7188 diff --git a/tests/test_action.py b/tests/test_action.py index 8de74e81..c2793c91 100644 --- a/tests/test_action.py +++ b/tests/test_action.py @@ -1,37 +1,47 @@ -import os +import copy import ipaddress import json +import os import shutil -import copy -import pytest - from datetime import datetime, timedelta -from io import BytesIO -from dateutil.tz import tzutc from decimal import Decimal -from typing import Tuple, Type -from pytest import raises +from io import BytesIO from json.decoder import JSONDecodeError +from typing import Tuple, Type -from flask import current_app as app, g +import pytest +from dateutil.tz import tzutc +from flask import current_app as app +from flask import g +from pytest import raises from sqlalchemy.util import OrderedSet from teal.enums import Currency, Subdivision +from ereuse_devicehub.client import Client, UserClient from ereuse_devicehub.db import db -from ereuse_devicehub.client import UserClient, Client from ereuse_devicehub.devicehub import Devicehub from ereuse_devicehub.resources import enums -from ereuse_devicehub.resources.user.models import User -from ereuse_devicehub.resources.agent.models import Person -from ereuse_devicehub.resources.lot.models import Lot from ereuse_devicehub.resources.action import models +from ereuse_devicehub.resources.agent.models import Person from ereuse_devicehub.resources.device import states -from ereuse_devicehub.resources.device.models import Desktop, Device, GraphicCard, HardDrive, \ - RamModule, SolidStateDrive +from ereuse_devicehub.resources.device.models import ( + Desktop, + Device, + GraphicCard, + HardDrive, + RamModule, + SolidStateDrive, +) +from ereuse_devicehub.resources.enums import ( + ComputerChassis, + Severity, + TestDataStorageLength, +) +from ereuse_devicehub.resources.lot.models import Lot from ereuse_devicehub.resources.tradedocument.models import TradeDocument -from ereuse_devicehub.resources.enums import ComputerChassis, Severity, TestDataStorageLength +from ereuse_devicehub.resources.user.models import User from tests import conftest -from tests.conftest import create_user, file, yaml2json, json_encode +from tests.conftest import create_user, file, json_encode, yaml2json @pytest.mark.mvp @@ -56,11 +66,8 @@ def test_author(): def test_erase_basic(): erasure = models.EraseBasic( device=HardDrive(serial_number='foo', manufacturer='bar', model='foo-bar'), - steps=[ - models.StepZero(**conftest.T), - models.StepRandom(**conftest.T) - ], - **conftest.T + steps=[models.StepZero(**conftest.T), models.StepRandom(**conftest.T)], + **conftest.T, ) db.session.add(erasure) db.session.commit() @@ -75,13 +82,17 @@ def test_erase_basic(): def test_validate_device_data_storage(): """Checks the validation for data-storage-only actions works.""" # We can't set a GraphicCard - with pytest.raises(TypeError, - message='EraseBasic.device must be a DataStorage ' - 'but you passed '): + with pytest.raises( + TypeError, + message='EraseBasic.device must be a DataStorage ' + 'but you passed ', + ): models.EraseBasic( - device=GraphicCard(serial_number='foo', manufacturer='bar', model='foo-bar'), + device=GraphicCard( + serial_number='foo', manufacturer='bar', model='foo-bar' + ), clean_with_zeros=True, - **conftest.T + **conftest.T, ) @@ -89,13 +100,15 @@ def test_validate_device_data_storage(): @pytest.mark.usefixtures(conftest.auth_app_context.__name__) def test_erase_sectors_steps_erasure_standards_hmg_is5(): erasure = models.EraseSectors( - device=SolidStateDrive(serial_number='foo', manufacturer='bar', model='foo-bar'), + device=SolidStateDrive( + serial_number='foo', manufacturer='bar', model='foo-bar' + ), steps=[ models.StepZero(**conftest.T), models.StepRandom(**conftest.T), - models.StepRandom(**conftest.T) + models.StepRandom(**conftest.T), ], - **conftest.T + **conftest.T, ) db.session.add(erasure) db.session.commit() @@ -118,7 +131,7 @@ def test_test_data_storage_working(): elapsed=timedelta(minutes=25), length=TestDataStorageLength.Short, status=':-(', - lifetime=timedelta(days=120) + lifetime=timedelta(days=120), ) db.session.add(test) db.session.flush() @@ -132,7 +145,7 @@ def test_test_data_storage_working(): elapsed=timedelta(minutes=25), length=TestDataStorageLength.Short, status=':-(', - lifetime=timedelta(days=120) + lifetime=timedelta(days=120), ) db.session.add(test2) db.session.flush() @@ -144,9 +157,9 @@ def test_test_data_storage_working(): @pytest.mark.usefixtures(conftest.auth_app_context.__name__) def test_install(): hdd = HardDrive(serial_number='sn') - install = models.Install(name='LinuxMint 18.04 es', - elapsed=timedelta(seconds=25), - device=hdd) + install = models.Install( + name='LinuxMint 18.04 es', elapsed=timedelta(seconds=25), device=hdd + ) db.session.add(install) db.session.commit() @@ -154,10 +167,12 @@ def test_install(): @pytest.mark.mvp @pytest.mark.usefixtures(conftest.auth_app_context.__name__) def test_update_components_action_one(): - computer = Desktop(serial_number='sn1', - model='ml1', - manufacturer='mr1', - chassis=ComputerChassis.Tower) + computer = Desktop( + serial_number='sn1', + model='ml1', + manufacturer='mr1', + chassis=ComputerChassis.Tower, + ) hdd = HardDrive(serial_number='foo', manufacturer='bar', model='foo-bar') computer.components.add(hdd) @@ -183,10 +198,12 @@ def test_update_components_action_one(): @pytest.mark.mvp @pytest.mark.usefixtures(conftest.auth_app_context.__name__) def test_update_components_action_multiple(): - computer = Desktop(serial_number='sn1', - model='ml1', - manufacturer='mr1', - chassis=ComputerChassis.Tower) + computer = Desktop( + serial_number='sn1', + model='ml1', + manufacturer='mr1', + chassis=ComputerChassis.Tower, + ) hdd = HardDrive(serial_number='foo', manufacturer='bar', model='foo-bar') computer.components.add(hdd) @@ -213,10 +230,12 @@ def test_update_components_action_multiple(): @pytest.mark.mvp @pytest.mark.usefixtures(conftest.auth_app_context.__name__) def test_update_parent(): - computer = Desktop(serial_number='sn1', - model='ml1', - manufacturer='mr1', - chassis=ComputerChassis.Tower) + computer = Desktop( + serial_number='sn1', + model='ml1', + manufacturer='mr1', + chassis=ComputerChassis.Tower, + ) hdd = HardDrive(serial_number='foo', manufacturer='bar', model='foo-bar') computer.components.add(hdd) @@ -232,17 +251,21 @@ def test_update_parent(): @pytest.mark.mvp -@pytest.mark.parametrize('action_model_state', - (pytest.param(ams, id=ams[0].__class__.__name__) - for ams in [ - (models.ToRepair, states.Physical.ToBeRepaired), - (models.Repair, states.Physical.Repaired), - (models.ToPrepare, states.Physical.Preparing), - (models.Ready, states.Physical.Ready), - (models.Prepare, states.Physical.Prepared) - ])) -def test_generic_action(action_model_state: Tuple[models.Action, states.Trading], - user: UserClient): +@pytest.mark.parametrize( + 'action_model_state', + ( + pytest.param(ams, id=ams[0].__class__.__name__) + for ams in [ + (models.ToPrepare, states.Physical.ToPrepare), + (models.Prepare, states.Physical.Prepare), + (models.ToRepair, states.Physical.ToRepair), + (models.Ready, states.Physical.Ready), + ] + ), +) +def test_generic_action( + action_model_state: Tuple[models.Action, states.Trading], user: UserClient +): """Tests POSTing all generic actions.""" action_model, state = action_model_state snapshot, _ = user.post(file('basic.snapshot'), res=models.Snapshot) @@ -257,15 +280,16 @@ def test_generic_action(action_model_state: Tuple[models.Action, states.Trading] @pytest.mark.mvp -@pytest.mark.parametrize('action_model', - (pytest.param(ams, id=ams.__class__.__name__) - for ams in [ - models.Recycling, - models.Use, - models.Refurbish, - models.Management - ])) -def test_simple_status_actions(action_model: models.Action, user: UserClient, user2: UserClient): +@pytest.mark.parametrize( + 'action_model', + ( + pytest.param(ams, id=ams.__class__.__name__) + for ams in [models.Recycling, models.Use, models.Refurbish, models.Management] + ), +) +def test_simple_status_actions( + action_model: models.Action, user: UserClient, user2: UserClient +): """Simple test of status action.""" snap, _ = user.post(file('basic.snapshot'), res=models.Snapshot) @@ -278,23 +302,23 @@ def test_simple_status_actions(action_model: models.Action, user: UserClient, us @pytest.mark.mvp -@pytest.mark.parametrize('action_model', - (pytest.param(ams, id=ams.__class__.__name__) - for ams in [ - models.Recycling, - models.Use, - models.Refurbish, - models.Management - ])) -def test_outgoinlot_status_actions(action_model: models.Action, user: UserClient, user2: UserClient): +@pytest.mark.parametrize( + 'action_model', + ( + pytest.param(ams, id=ams.__class__.__name__) + for ams in [models.Recycling, models.Use, models.Refurbish, models.Management] + ), +) +def test_outgoinlot_status_actions( + action_model: models.Action, user: UserClient, user2: UserClient +): """Test of status actions in outgoinlot.""" snap, _ = user.post(file('basic.snapshot'), res=models.Snapshot) device, _ = user.get(res=Device, item=snap['device']['devicehubID']) lot, _ = user.post({'name': 'MyLot'}, res=Lot) - user.post({}, - res=Lot, - item='{}/devices'.format(lot['id']), - query=[('id', device['id'])]) + user.post( + {}, res=Lot, item='{}/devices'.format(lot['id']), query=[('id', device['id'])] + ) request_post = { 'type': 'Trade', @@ -317,10 +341,13 @@ def test_outgoinlot_status_actions(action_model: models.Action, user: UserClient assert action['rol_user']['id'] == user2.user['id'] # Remove device from lot - lot, _ = user.delete({}, - res=Lot, - item='{}/devices'.format(lot['id']), - query=[('id', device['id'])], status=200) + lot, _ = user.delete( + {}, + res=Lot, + item='{}/devices'.format(lot['id']), + query=[('id', device['id'])], + status=200, + ) action = {'type': action_model.t, 'devices': [device['id']]} action, _ = user.post(action, res=models.Action) @@ -332,23 +359,23 @@ def test_outgoinlot_status_actions(action_model: models.Action, user: UserClient @pytest.mark.mvp -@pytest.mark.parametrize('action_model', - (pytest.param(ams, id=ams.__class__.__name__) - for ams in [ - models.Recycling, - models.Use, - models.Refurbish, - models.Management - ])) -def test_incominglot_status_actions(action_model: models.Action, user: UserClient, user2: UserClient): +@pytest.mark.parametrize( + 'action_model', + ( + pytest.param(ams, id=ams.__class__.__name__) + for ams in [models.Recycling, models.Use, models.Refurbish, models.Management] + ), +) +def test_incominglot_status_actions( + action_model: models.Action, user: UserClient, user2: UserClient +): """Test of status actions in outgoinlot.""" snap, _ = user.post(file('basic.snapshot'), res=models.Snapshot) device, _ = user.get(res=Device, item=snap['device']['devicehubID']) lot, _ = user.post({'name': 'MyLot'}, res=Lot) - user.post({}, - res=Lot, - item='{}/devices'.format(lot['id']), - query=[('id', device['id'])]) + user.post( + {}, res=Lot, item='{}/devices'.format(lot['id']), query=[('id', device['id'])] + ) request_post = { 'type': 'Trade', @@ -396,10 +423,9 @@ def test_history_status_actions(user: UserClient, user2: UserClient): # Case 3 lot, _ = user.post({'name': 'MyLot'}, res=Lot) - user.post({}, - res=Lot, - item='{}/devices'.format(lot['id']), - query=[('id', device.id)]) + user.post( + {}, res=Lot, item='{}/devices'.format(lot['id']), query=[('id', device.id)] + ) request_post = { 'type': 'Trade', @@ -417,14 +443,16 @@ def test_history_status_actions(user: UserClient, user2: UserClient): action3, _ = user.post(action3, res=models.Action) assert action3['id'] == str(device.status.id) assert device.status.t == models.Use.t - assert [action2['id'], action3['id']] == [str(ac.id) for ac in device.history_status] + assert [action2['id'], action3['id']] == [ + str(ac.id) for ac in device.history_status + ] @pytest.mark.mvp @pytest.mark.usefixtures(conftest.app_context.__name__) def test_use_changing_owner(user: UserClient, user2: UserClient): """Check if is it possible to do a use action for one device - when you are not the owner. + when you are not the owner. """ snap, _ = user.post(file('basic.snapshot'), res=models.Snapshot) device = Device.query.filter_by(id=snap['device']['id']).one() @@ -433,10 +461,9 @@ def test_use_changing_owner(user: UserClient, user2: UserClient): # Trade lot, _ = user.post({'name': 'MyLot'}, res=Lot) - user.post({}, - res=Lot, - item='{}/devices'.format(lot['id']), - query=[('id', device.id)]) + user.post( + {}, res=Lot, item='{}/devices'.format(lot['id']), query=[('id', device.id)] + ) request_post = { 'type': 'Trade', @@ -453,11 +480,7 @@ def test_use_changing_owner(user: UserClient, user2: UserClient): trade = models.Trade.query.one() # Doble confirmation and change of owner - request_confirm = { - 'type': 'Confirm', - 'action': trade.id, - 'devices': [device.id] - } + request_confirm = {'type': 'Confirm', 'action': trade.id, 'devices': [device.id]} user2.post(res=models.Action, data=request_confirm) assert device.owner.email == user2.email @@ -475,30 +498,33 @@ def test_use_changing_owner(user: UserClient, user2: UserClient): def test_recycling_container(user: UserClient): """Test of status action recycling for a container.""" lot, _ = user.post({'name': 'MyLotOut'}, res=Lot) - url = 'http://www.ereuse.org/', + url = ('http://www.ereuse.org/',) request_post = { 'filename': 'test.pdf', 'hash': 'bbbbbbbb', 'url': url, 'weight': 150, - 'lot': lot['id'] + 'lot': lot['id'], } tradedocument, _ = user.post(res=TradeDocument, data=request_post) - action = {'type': models.Recycling.t, 'devices': [], 'documents': [tradedocument['id']]} + action = { + 'type': models.Recycling.t, + 'devices': [], + 'documents': [tradedocument['id']], + } action, _ = user.post(action, res=models.Action) trade = TradeDocument.query.one() assert str(trade.actions[0].id) == action['id'] @pytest.mark.mvp -@pytest.mark.parametrize('action_model', - (pytest.param(ams, id=ams.__class__.__name__) - for ams in [ - models.Recycling, - models.Use, - models.Refurbish, - models.Management - ])) +@pytest.mark.parametrize( + 'action_model', + ( + pytest.param(ams, id=ams.__class__.__name__) + for ams in [models.Recycling, models.Use, models.Refurbish, models.Management] + ), +) def test_status_without_lot(action_model: models.Action, user: UserClient): """Test of status actions for devices without lot.""" snap, _ = user.post(file('basic.snapshot'), res=models.Snapshot) @@ -509,23 +535,21 @@ def test_status_without_lot(action_model: models.Action, user: UserClient): @pytest.mark.mvp -@pytest.mark.parametrize('action_model', - (pytest.param(ams, id=ams.__class__.__name__) - for ams in [ - models.Recycling, - models.Use, - models.Refurbish, - models.Management - ])) +@pytest.mark.parametrize( + 'action_model', + ( + pytest.param(ams, id=ams.__class__.__name__) + for ams in [models.Recycling, models.Use, models.Refurbish, models.Management] + ), +) def test_status_in_temporary_lot(action_model: models.Action, user: UserClient): """Test of status actions for devices in a temporary lot.""" snap, _ = user.post(file('basic.snapshot'), res=models.Snapshot) device_id = snap['device']['id'] lot, _ = user.post({'name': 'MyLotOut'}, res=Lot) - lot, _ = user.post({}, - res=Lot, - item='{}/devices'.format(lot['id']), - query=[('id', device_id)]) + lot, _ = user.post( + {}, res=Lot, item='{}/devices'.format(lot['id']), query=[('id', device_id)] + ) action = {'type': action_model.t, 'devices': [device_id]} action, _ = user.post(action, res=models.Action) device, _ = user.get(res=Device, item=snap['device']['devicehubID']) @@ -540,11 +564,15 @@ def test_live(user: UserClient, client: Client, app: Devicehub): snapshot, _ = user.post(acer, res=models.Snapshot) acer = yaml2json('acer.happy.battery.snapshot') device_id = snapshot['device']['id'] - post_request = {"transaction": "ccc", "name": "John", "endUsers": 1, - "devices": [device_id], "description": "aaa", - "finalUserCode": "abcdefjhi", - "startTime": "2020-11-01T02:00:00+00:00", - "endTime": "2020-12-01T02:00:00+00:00" + post_request = { + "transaction": "ccc", + "name": "John", + "endUsers": 1, + "devices": [device_id], + "description": "aaa", + "finalUserCode": "abcdefjhi", + "startTime": "2020-11-01T02:00:00+00:00", + "endTime": "2020-12-01T02:00:00+00:00", } user.post(res=models.Allocate, data=post_request) @@ -574,11 +602,15 @@ def test_live_example(user: UserClient, client: Client, app: Devicehub): acer = file('snapshotLive') snapshot, _ = user.post(acer, res=models.Snapshot) device_id = snapshot['device']['id'] - post_request = {"transaction": "ccc", "name": "John", "endUsers": 1, - "devices": [device_id], "description": "aaa", - "finalUserCode": "abcdefjhi", - "startTime": "2020-11-01T02:00:00+00:00", - "endTime": "2020-12-01T02:00:00+00:00" + post_request = { + "transaction": "ccc", + "name": "John", + "endUsers": 1, + "devices": [device_id], + "description": "aaa", + "finalUserCode": "abcdefjhi", + "startTime": "2020-11-01T02:00:00+00:00", + "endTime": "2020-12-01T02:00:00+00:00", } user.post(res=models.Allocate, data=post_request) @@ -595,7 +627,9 @@ def test_live_example(user: UserClient, client: Client, app: Devicehub): @pytest.mark.mvp @pytest.mark.usefixtures(conftest.app_context.__name__) -def test_live_two_users(user: UserClient, user2: UserClient, client: Client, app: Devicehub): +def test_live_two_users( + user: UserClient, user2: UserClient, client: Client, app: Devicehub +): """Tests inserting a Live into the database and GETting it.""" acer = file('snapshotLive') snapshot, _ = user.post(acer, res=models.Snapshot) @@ -603,11 +637,15 @@ def test_live_two_users(user: UserClient, user2: UserClient, client: Client, app acer2['uuid'] = '3b6a9288-0ba6-4bdd-862a-2b1f660e7115' snapshot2, _ = user2.post(json_encode(acer2), res=models.Snapshot) device_id = snapshot['device']['id'] - post_request = {"transaction": "ccc", "name": "John", "endUsers": 1, - "devices": [device_id], "description": "aaa", - "finalUserCode": "abcdefjhi", - "startTime": "2020-11-01T02:00:00+00:00", - "endTime": "2020-12-01T02:00:00+00:00" + post_request = { + "transaction": "ccc", + "name": "John", + "endUsers": 1, + "devices": [device_id], + "description": "aaa", + "finalUserCode": "abcdefjhi", + "startTime": "2020-11-01T02:00:00+00:00", + "endTime": "2020-12-01T02:00:00+00:00", } user.post(res=models.Allocate, data=post_request) @@ -624,7 +662,9 @@ def test_live_two_users(user: UserClient, user2: UserClient, client: Client, app @pytest.mark.mvp @pytest.mark.usefixtures(conftest.app_context.__name__) -def test_live_two_allocated(user: UserClient, user2: UserClient, client: Client, app: Devicehub): +def test_live_two_allocated( + user: UserClient, user2: UserClient, client: Client, app: Devicehub +): """Tests inserting a Live into the database and GETting it.""" acer = file('snapshotLive') snapshot, _ = user.post(acer, res=models.Snapshot) @@ -633,17 +673,25 @@ def test_live_two_allocated(user: UserClient, user2: UserClient, client: Client, snapshot2, _ = user2.post(json_encode(acer2), res=models.Snapshot) device_id = snapshot['device']['id'] device_id2 = snapshot2['device']['id'] - post_request = {"transaction": "ccc", "name": "John", "endUsers": 1, - "devices": [device_id], "description": "aaa", - "finalUserCode": "abcdefjhi", - "startTime": "2020-11-01T02:00:00+00:00", - "endTime": "2020-12-01T02:00:00+00:00" + post_request = { + "transaction": "ccc", + "name": "John", + "endUsers": 1, + "devices": [device_id], + "description": "aaa", + "finalUserCode": "abcdefjhi", + "startTime": "2020-11-01T02:00:00+00:00", + "endTime": "2020-12-01T02:00:00+00:00", } - post_request2 = {"transaction": "ccc", "name": "John", "endUsers": 1, - "devices": [device_id2], "description": "aaa", - "finalUserCode": "abcdefjhi", - "startTime": "2020-11-01T02:00:00+00:00", - "endTime": "2020-12-01T02:00:00+00:00" + post_request2 = { + "transaction": "ccc", + "name": "John", + "endUsers": 1, + "devices": [device_id2], + "description": "aaa", + "finalUserCode": "abcdefjhi", + "startTime": "2020-11-01T02:00:00+00:00", + "endTime": "2020-12-01T02:00:00+00:00", } user.post(res=models.Allocate, data=post_request) @@ -661,23 +709,29 @@ def test_live_two_allocated(user: UserClient, user2: UserClient, client: Client, @pytest.mark.usefixtures(conftest.app_context.__name__) def test_live_without_TestDataStorage(user: UserClient, client: Client, app: Devicehub): """Tests inserting a Live into the database and GETting it. - If the live don't have a TestDataStorage, then save live and response None + If the live don't have a TestDataStorage, then save live and response None """ acer = file('acer.happy.battery.snapshot') snapshot, _ = user.post(acer, res=models.Snapshot) device_id = snapshot['device']['id'] db_device = Device.query.filter_by(id=device_id).one() - post_request = {"transaction": "ccc", "name": "John", "endUsers": 1, - "devices": [device_id], "description": "aaa", - "finalUserCode": "abcdefjhi", - "startTime": "2020-11-01T02:00:00+00:00", - "endTime": "2020-12-01T02:00:00+00:00" + post_request = { + "transaction": "ccc", + "name": "John", + "endUsers": 1, + "devices": [device_id], + "description": "aaa", + "finalUserCode": "abcdefjhi", + "startTime": "2020-11-01T02:00:00+00:00", + "endTime": "2020-12-01T02:00:00+00:00", } user.post(res=models.Allocate, data=post_request) acer = yaml2json('acer.happy.battery.snapshot') acer['uuid'] = "490fb8c0-81a1-42e9-95e0-5e7db7038ec3" - actions = [a for a in acer['components'][7]['actions'] if a['type'] != 'TestDataStorage'] + actions = [ + a for a in acer['components'][7]['actions'] if a['type'] != 'TestDataStorage' + ] acer['components'][7]['actions'] = actions acer.pop('elapsed') acer['licence_version'] = '1.0.0' @@ -695,17 +749,21 @@ def test_live_without_TestDataStorage(user: UserClient, client: Client, app: Dev @pytest.mark.usefixtures(conftest.app_context.__name__) def test_live_without_hdd_1(user: UserClient, client: Client, app: Devicehub): """Tests inserting a Live into the database and GETting it. - The snapshot have hdd but the live no, and response 404 + The snapshot have hdd but the live no, and response 404 """ acer = file('acer.happy.battery.snapshot') snapshot, _ = user.post(acer, res=models.Snapshot) device_id = snapshot['device']['id'] db_device = Device.query.filter_by(id=device_id).one() - post_request = {"transaction": "ccc", "name": "John", "endUsers": 1, - "devices": [device_id], "description": "aaa", - "finalUserCode": "abcdefjhi", - "startTime": "2020-11-01T02:00:00+00:00", - "endTime": "2020-12-01T02:00:00+00:00" + post_request = { + "transaction": "ccc", + "name": "John", + "endUsers": 1, + "devices": [device_id], + "description": "aaa", + "finalUserCode": "abcdefjhi", + "startTime": "2020-11-01T02:00:00+00:00", + "endTime": "2020-12-01T02:00:00+00:00", } user.post(res=models.Allocate, data=post_request) @@ -725,7 +783,7 @@ def test_live_without_hdd_1(user: UserClient, client: Client, app: Devicehub): @pytest.mark.usefixtures(conftest.app_context.__name__) def test_live_without_hdd_2(user: UserClient, client: Client, app: Devicehub): """Tests inserting a Live into the database and GETting it. - The snapshot haven't hdd and the live neither, and response 404 + The snapshot haven't hdd and the live neither, and response 404 """ acer = yaml2json('acer.happy.battery.snapshot') components = [a for a in acer['components'] if a['type'] != 'HardDrive'] @@ -733,11 +791,15 @@ def test_live_without_hdd_2(user: UserClient, client: Client, app: Devicehub): snapshot, _ = user.post(json_encode(acer), res=models.Snapshot) device_id = snapshot['device']['id'] db_device = Device.query.filter_by(id=device_id).one() - post_request = {"transaction": "ccc", "name": "John", "endUsers": 1, - "devices": [device_id], "description": "aaa", - "finalUserCode": "abcdefjhi", - "startTime": "2020-11-01T02:00:00+00:00", - "endTime": "2020-12-01T02:00:00+00:00" + post_request = { + "transaction": "ccc", + "name": "John", + "endUsers": 1, + "devices": [device_id], + "description": "aaa", + "finalUserCode": "abcdefjhi", + "startTime": "2020-11-01T02:00:00+00:00", + "endTime": "2020-12-01T02:00:00+00:00", } user.post(res=models.Allocate, data=post_request) @@ -754,8 +816,8 @@ def test_live_without_hdd_2(user: UserClient, client: Client, app: Devicehub): @pytest.mark.usefixtures(conftest.app_context.__name__) def test_live_without_hdd_3(user: UserClient, client: Client, app: Devicehub): """Tests inserting a Live into the database and GETting it. - The snapshot haven't hdd and the live have, and save the live - with usage_time_allocate == 0 + The snapshot haven't hdd and the live have, and save the live + with usage_time_allocate == 0 """ acer = yaml2json('acer.happy.battery.snapshot') acer['uuid'] = "490fb8c0-81a1-42e9-95e0-5e7db7038ec3" @@ -764,11 +826,15 @@ def test_live_without_hdd_3(user: UserClient, client: Client, app: Devicehub): snapshot, _ = user.post(json_encode(acer), res=models.Snapshot) device_id = snapshot['device']['id'] db_device = Device.query.filter_by(id=device_id).one() - post_request = {"transaction": "ccc", "name": "John", "endUsers": 1, - "devices": [device_id], "description": "aaa", - "finalUserCode": "abcdefjhi", - "startTime": "2020-11-01T02:00:00+00:00", - "endTime": "2020-12-01T02:00:00+00:00" + post_request = { + "transaction": "ccc", + "name": "John", + "endUsers": 1, + "devices": [device_id], + "description": "aaa", + "finalUserCode": "abcdefjhi", + "startTime": "2020-11-01T02:00:00+00:00", + "endTime": "2020-12-01T02:00:00+00:00", } user.post(res=models.Allocate, data=post_request) @@ -790,24 +856,30 @@ def test_live_without_hdd_3(user: UserClient, client: Client, app: Devicehub): @pytest.mark.usefixtures(conftest.app_context.__name__) def test_live_with_hdd_with_old_time(user: UserClient, client: Client, app: Devicehub): """Tests inserting a Live into the database and GETting it. - The snapshot hdd have a lifetime higher than lifetime of the live action - save the live with usage_time_allocate == 0 + The snapshot hdd have a lifetime higher than lifetime of the live action + save the live with usage_time_allocate == 0 """ acer = file('acer.happy.battery.snapshot') snapshot, _ = user.post(acer, res=models.Snapshot) device_id = snapshot['device']['id'] db_device = Device.query.filter_by(id=device_id).one() - post_request = {"transaction": "ccc", "name": "John", "endUsers": 1, - "devices": [device_id], "description": "aaa", - "finalUserCode": "abcdefjhi", - "startTime": "2020-11-01T02:00:00+00:00", - "endTime": "2020-12-01T02:00:00+00:00" + post_request = { + "transaction": "ccc", + "name": "John", + "endUsers": 1, + "devices": [device_id], + "description": "aaa", + "finalUserCode": "abcdefjhi", + "startTime": "2020-11-01T02:00:00+00:00", + "endTime": "2020-12-01T02:00:00+00:00", } user.post(res=models.Allocate, data=post_request) acer = yaml2json('acer.happy.battery.snapshot') acer['uuid'] = "490fb8c0-81a1-42e9-95e0-5e7db7038ec3" - action = [a for a in acer['components'][7]['actions'] if a['type'] == 'TestDataStorage'] + action = [ + a for a in acer['components'][7]['actions'] if a['type'] == 'TestDataStorage' + ] action[0]['lifetime'] -= 100 acer.pop('elapsed') acer['licence_version'] = '1.0.0' @@ -825,16 +897,19 @@ def test_live_with_hdd_with_old_time(user: UserClient, client: Client, app: Devi @pytest.mark.mvp @pytest.mark.usefixtures(conftest.app_context.__name__) def test_live_search_last_allocate(user: UserClient, client: Client, app: Devicehub): - """Tests inserting a Live into the database and GETting it. - """ + """Tests inserting a Live into the database and GETting it.""" acer = file('acer.happy.battery.snapshot') snapshot, _ = user.post(acer, res=models.Snapshot) device_id = snapshot['device']['id'] - post_request = {"transaction": "ccc", "name": "John", "endUsers": 1, - "devices": [device_id], "description": "aaa", - "finalUserCode": "abcdefjhi", - "startTime": "2020-11-01T02:00:00+00:00", - "endTime": "2020-12-01T02:00:00+00:00" + post_request = { + "transaction": "ccc", + "name": "John", + "endUsers": 1, + "devices": [device_id], + "description": "aaa", + "finalUserCode": "abcdefjhi", + "startTime": "2020-11-01T02:00:00+00:00", + "endTime": "2020-12-01T02:00:00+00:00", } user.post(res=models.Allocate, data=post_request) @@ -847,7 +922,9 @@ def test_live_search_last_allocate(user: UserClient, client: Client, app: Device acer['licence_version'] = '1.0.0' live, _ = client.post(acer, res=models.Live) acer['uuid'] = "490fb8c0-81a1-42e9-95e0-5e7db7038ec4" - actions = [a for a in acer['components'][7]['actions'] if a['type'] != 'TestDataStorage'] + actions = [ + a for a in acer['components'][7]['actions'] if a['type'] != 'TestDataStorage' + ] acer['components'][7]['actions'] = actions live, _ = client.post(acer, res=models.Live) assert live['usageTimeAllocate'] == 1000 @@ -857,17 +934,21 @@ def test_live_search_last_allocate(user: UserClient, client: Client, app: Device @pytest.mark.mvp def test_save_live_json(app: Devicehub, user: UserClient, client: Client): - """ This test check if works the function save_snapshot_in_file """ + """This test check if works the function save_snapshot_in_file""" acer = yaml2json('acer.happy.battery.snapshot') snapshot, _ = user.post(json_encode(acer), res=models.Snapshot) debug = 'AAA' acer['debug'] = debug device_id = snapshot['device']['id'] - post_request = {"transaction": "ccc", "name": "John", "endUsers": 1, - "devices": [device_id], "description": "aaa", - "finalUserCode": "abcdefjhi", - "startTime": "2020-11-01T02:00:00+00:00", - "endTime": "2020-12-01T02:00:00+00:00" + post_request = { + "transaction": "ccc", + "name": "John", + "endUsers": 1, + "devices": [device_id], + "description": "aaa", + "finalUserCode": "abcdefjhi", + "startTime": "2020-11-01T02:00:00+00:00", + "endTime": "2020-12-01T02:00:00+00:00", } user.post(res=models.Allocate, data=post_request) @@ -899,8 +980,7 @@ def test_save_live_json(app: Devicehub, user: UserClient, client: Client): @pytest.mark.mvp @pytest.mark.usefixtures(conftest.app_context.__name__) def test_licences(client: Client): - """Tests inserting a Live into the database and GETting it. - """ + """Tests inserting a Live into the database and GETting it.""" licences, _ = client.get('/licences/') licences = json.loads(licences) assert licences[0]['USOdyPrivacyPolicyVersion'] == '1.0.0' @@ -909,19 +989,20 @@ def test_licences(client: Client): @pytest.mark.mvp @pytest.mark.usefixtures(conftest.app_context.__name__) def test_allocate(user: UserClient): - """ Tests allocate """ + """Tests allocate""" snapshot, _ = user.post(file('basic.snapshot'), res=models.Snapshot) device_id = snapshot['device']['id'] devicehub_id = snapshot['device']['devicehubID'] - post_request = {"transaction": "ccc", - "finalUserCode": "aabbcc", - "name": "John", - "severity": "Info", - "endUsers": 1, - "devices": [device_id], - "description": "aaa", - "startTime": "2020-11-01T02:00:00+00:00", - "endTime": "2020-12-01T02:00:00+00:00", + post_request = { + "transaction": "ccc", + "finalUserCode": "aabbcc", + "name": "John", + "severity": "Info", + "endUsers": 1, + "devices": [device_id], + "description": "aaa", + "startTime": "2020-11-01T02:00:00+00:00", + "endTime": "2020-12-01T02:00:00+00:00", } allocate, _ = user.post(res=models.Allocate, data=post_request) @@ -953,19 +1034,20 @@ def test_allocate(user: UserClient): @pytest.mark.mvp @pytest.mark.usefixtures(conftest.app_context.__name__) def test_allocate_bad_dates(user: UserClient): - """ Tests allocate """ + """Tests allocate""" snapshot, _ = user.post(file('basic.snapshot'), res=models.Snapshot) device_id = snapshot['device']['id'] delay = timedelta(days=30) future = datetime.now().replace(tzinfo=tzutc()) + delay - post_request = {"transaction": "ccc", - "finalUserCode": "aabbcc", - "name": "John", - "severity": "Info", - "end_users": 1, - "devices": [device_id], - "description": "aaa", - "start_time": future, + post_request = { + "transaction": "ccc", + "finalUserCode": "aabbcc", + "name": "John", + "severity": "Info", + "end_users": 1, + "devices": [device_id], + "description": "aaa", + "start_time": future, } res, _ = user.post(res=models.Allocate, data=post_request, status=422) @@ -976,21 +1058,26 @@ def test_allocate_bad_dates(user: UserClient): @pytest.mark.mvp @pytest.mark.usefixtures(conftest.app_context.__name__) def test_deallocate(user: UserClient): - """ Tests deallocate """ + """Tests deallocate""" snapshot, _ = user.post(file('basic.snapshot'), res=models.Snapshot) device_id = snapshot['device']['id'] devicehub_id = snapshot['device']['devicehubID'] - post_deallocate = {"startTime": "2020-11-01T02:00:00+00:00", - "transaction": "ccc", - "devices": [device_id] + post_deallocate = { + "startTime": "2020-11-01T02:00:00+00:00", + "transaction": "ccc", + "devices": [device_id], } res, _ = user.post(res=models.Deallocate, data=post_deallocate, status=422) assert res['code'] == 422 assert res['type'] == 'ValidationError' - post_allocate = {"transaction": "ccc", "name": "John", "endUsers": 1, - "devices": [device_id], "description": "aaa", - "startTime": "2020-11-01T02:00:00+00:00", - "endTime": "2020-12-01T02:00:00+00:00" + post_allocate = { + "transaction": "ccc", + "name": "John", + "endUsers": 1, + "devices": [device_id], + "description": "aaa", + "startTime": "2020-11-01T02:00:00+00:00", + "endTime": "2020-12-01T02:00:00+00:00", } user.post(res=models.Allocate, data=post_allocate) @@ -1008,16 +1095,16 @@ def test_deallocate(user: UserClient): @pytest.mark.mvp @pytest.mark.usefixtures(conftest.app_context.__name__) def test_deallocate_bad_dates(user: UserClient): - """ Tests deallocate with bad date of start_time """ + """Tests deallocate with bad date of start_time""" snapshot, _ = user.post(file('basic.snapshot'), res=models.Snapshot) device_id = snapshot['device']['id'] - delay = timedelta(days=30) + delay = timedelta(days=30) future = datetime.now().replace(tzinfo=tzutc()) + delay - post_deallocate = {"startTime": future, - "devices": [device_id] - } - post_allocate = {"devices": [device_id], "description": "aaa", - "startTime": "2020-11-01T02:00:00+00:00" + post_deallocate = {"startTime": future, "devices": [device_id]} + post_allocate = { + "devices": [device_id], + "description": "aaa", + "startTime": "2020-11-01T02:00:00+00:00", } user.post(res=models.Allocate, data=post_allocate) @@ -1037,7 +1124,7 @@ def test_trade_endpoint(user: UserClient, user2: UserClient): 'userTo': user2.user['email'], 'price': 1.0, 'date': "2020-12-01T02:00:00+00:00", - 'devices': [snapshot['device']['id']] + 'devices': [snapshot['device']['id']], } action, _ = user.post(res=models.Trade, data=request_post) @@ -1075,10 +1162,9 @@ def test_offer_without_to(user: UserClient): snapshot, _ = user.post(file('basic.snapshot'), res=models.Snapshot) device = Device.query.filter_by(id=snapshot['device']['id']).one() lot, _ = user.post({'name': 'MyLot'}, res=Lot) - user.post({}, - res=Lot, - item='{}/devices'.format(lot['id']), - query=[('id', device.id)]) + user.post( + {}, res=Lot, item='{}/devices'.format(lot['id']), query=[('id', device.id)] + ) # check the owner of the device assert device.owner.email == user.email @@ -1093,7 +1179,7 @@ def test_offer_without_to(user: UserClient): 'date': "2020-12-01T02:00:00+00:00", 'lot': lot['id'], 'confirms': False, - 'code': 'MAX' + 'code': 'MAX', } user.post(res=models.Action, data=request_post) @@ -1120,7 +1206,7 @@ def test_offer_without_to(user: UserClient): 'date': "2020-12-01T02:00:00+00:00", 'lot': lot['id'], 'confirms': False, - 'code': 'MAX' + 'code': 'MAX', } user.post(res=models.Action, data=request_post, status=422) trade = models.Trade.query.one() @@ -1142,7 +1228,7 @@ def test_offer_without_to(user: UserClient): 'date': "2020-12-01T02:00:00+00:00", 'lot': lot2.id, 'confirms': False, - 'code': 'MAX' + 'code': 'MAX', } user.post(res=models.Action, data=request_post2) assert User.query.filter_by(email=device.owner.email).count() == 1 @@ -1172,7 +1258,7 @@ def test_offer_without_from(user: UserClient, user2: UserClient): 'date': "2020-12-01T02:00:00+00:00", 'lot': lot.id, 'confirms': False, - 'code': 'MAX' + 'code': 'MAX', } action, _ = user2.post(res=models.Action, data=request_post, status=422) @@ -1216,7 +1302,7 @@ def test_offer_without_users(user: UserClient): 'date': "2020-12-01T02:00:00+00:00", 'lot': lot.id, 'confirms': False, - 'code': 'MAX' + 'code': 'MAX', } action, response = user.post(res=models.Action, data=request_post, status=422) txt = 'you need one user from or user to for to do a trade' @@ -1284,8 +1370,12 @@ def test_offer_without_devices(user: UserClient): @pytest.mark.mvp @pytest.mark.usefixtures(conftest.auth_app_context.__name__) def test_price_custom(): - computer = Desktop(serial_number='sn1', model='ml1', manufacturer='mr1', - chassis=ComputerChassis.Docking) + computer = Desktop( + serial_number='sn1', + model='ml1', + manufacturer='mr1', + chassis=ComputerChassis.Docking, + ) price = models.Price(price=Decimal(25.25), currency=Currency.EUR) price.device = computer assert computer.price == price @@ -1308,12 +1398,15 @@ def test_price_custom_client(user: UserClient): """As test_price_custom but creating the price through the API.""" s = file('basic.snapshot') snapshot, _ = user.post(s, res=models.Snapshot) - price, _ = user.post({ - 'type': 'Price', - 'price': 25, - 'currency': Currency.EUR.name, - 'device': snapshot['device']['id'] - }, res=models.Action) + price, _ = user.post( + { + 'type': 'Price', + 'price': 25, + 'currency': Currency.EUR.name, + 'device': snapshot['device']['id'], + }, + res=models.Action, + ) assert 25 == price['price'] assert Currency.EUR.name == price['currency'] @@ -1326,11 +1419,12 @@ def test_price_custom_client(user: UserClient): def test_erase_physical(): erasure = models.ErasePhysical( device=HardDrive(serial_number='foo', manufacturer='bar', model='foo-bar'), - method=enums.PhysicalErasureMethod.Disintegration + method=enums.PhysicalErasureMethod.Disintegration, ) db.session.add(erasure) db.session.commit() + @pytest.mark.mvp @pytest.mark.usefixtures(conftest.app_context.__name__) def test_endpoint_confirm(user: UserClient, user2: UserClient): @@ -1338,10 +1432,9 @@ def test_endpoint_confirm(user: UserClient, user2: UserClient): snapshot, _ = user.post(file('basic.snapshot'), res=models.Snapshot) device_id = snapshot['device']['id'] lot, _ = user.post({'name': 'MyLot'}, res=Lot) - user.post({}, - res=Lot, - item='{}/devices'.format(lot['id']), - query=[('id', device_id)]) + user.post( + {}, res=Lot, item='{}/devices'.format(lot['id']), query=[('id', device_id)] + ) request_post = { 'type': 'Trade', @@ -1359,11 +1452,7 @@ def test_endpoint_confirm(user: UserClient, user2: UserClient): assert trade.devices[0].owner.email == user.email - request_confirm = { - 'type': 'Confirm', - 'action': trade.id, - 'devices': [device_id] - } + request_confirm = {'type': 'Confirm', 'action': trade.id, 'devices': [device_id]} user2.post(res=models.Action, data=request_confirm) user2.post(res=models.Action, data=request_confirm, status=422) @@ -1378,10 +1467,9 @@ def test_confirm_revoke(user: UserClient, user2: UserClient): snapshot, _ = user.post(file('basic.snapshot'), res=models.Snapshot) device_id = snapshot['device']['id'] lot, _ = user.post({'name': 'MyLot'}, res=Lot) - user.post({}, - res=Lot, - item='{}/devices'.format(lot['id']), - query=[('id', device_id)]) + user.post( + {}, res=Lot, item='{}/devices'.format(lot['id']), query=[('id', device_id)] + ) request_post = { 'type': 'Trade', @@ -1398,11 +1486,7 @@ def test_confirm_revoke(user: UserClient, user2: UserClient): trade = models.Trade.query.one() device = trade.devices[0] - request_confirm = { - 'type': 'Confirm', - 'action': trade.id, - 'devices': [device_id] - } + request_confirm = {'type': 'Confirm', 'action': trade.id, 'devices': [device_id]} request_revoke = { 'type': 'Revoke', @@ -1410,7 +1494,6 @@ def test_confirm_revoke(user: UserClient, user2: UserClient): 'devices': [device_id], } - # Normal confirmation user2.post(res=models.Action, data=request_confirm) @@ -1433,29 +1516,34 @@ def test_usecase_confirmation(user: UserClient, user2: UserClient): snap1, _ = user.post(file('basic.snapshot'), res=models.Snapshot) snap2, _ = user.post(file('acer.happy.battery.snapshot'), res=models.Snapshot) snap3, _ = user.post(file('asus-1001pxd.snapshot'), res=models.Snapshot) - snap4, _ = user.post(file('desktop-9644w8n-lenovo-0169622.snapshot'), res=models.Snapshot) - snap5, _ = user.post(file('laptop-hp_255_g3_notebook-hewlett-packard-cnd52270fw.snapshot'), res=models.Snapshot) + snap4, _ = user.post( + file('desktop-9644w8n-lenovo-0169622.snapshot'), res=models.Snapshot + ) + snap5, _ = user.post( + file('laptop-hp_255_g3_notebook-hewlett-packard-cnd52270fw.snapshot'), + res=models.Snapshot, + ) snap6, _ = user.post(file('1-device-with-components.snapshot'), res=models.Snapshot) snap7, _ = user.post(file('asus-eee-1000h.snapshot.11'), res=models.Snapshot) snap8, _ = user.post(file('complete.export.snapshot'), res=models.Snapshot) snap9, _ = user.post(file('real-hp-quad-core.snapshot.11'), res=models.Snapshot) snap10, _ = user.post(file('david.lshw.snapshot'), res=models.Snapshot) - devices = [('id', snap1['device']['id']), - ('id', snap2['device']['id']), - ('id', snap3['device']['id']), - ('id', snap4['device']['id']), - ('id', snap5['device']['id']), - ('id', snap6['device']['id']), - ('id', snap7['device']['id']), - ('id', snap8['device']['id']), - ('id', snap9['device']['id']), - ('id', snap10['device']['id']), - ] - lot, _ = user.post({}, - res=Lot, - item='{}/devices'.format(lot['id']), - query=devices[:7]) + devices = [ + ('id', snap1['device']['id']), + ('id', snap2['device']['id']), + ('id', snap3['device']['id']), + ('id', snap4['device']['id']), + ('id', snap5['device']['id']), + ('id', snap6['device']['id']), + ('id', snap7['device']['id']), + ('id', snap8['device']['id']), + ('id', snap9['device']['id']), + ('id', snap10['device']['id']), + ] + lot, _ = user.post( + {}, res=Lot, item='{}/devices'.format(lot['id']), query=devices[:7] + ) # the manager shares the temporary lot with the SCRAP as an incoming lot # for the SCRAP to confirm it @@ -1478,7 +1566,11 @@ def test_usecase_confirmation(user: UserClient, user2: UserClient): request_confirm = { 'type': 'Confirm', 'action': trade.id, - 'devices': [snap1['device']['id'], snap2['device']['id'], snap3['device']['id']] + 'devices': [ + snap1['device']['id'], + snap2['device']['id'], + snap3['device']['id'], + ], } assert trade.devices[0].actions[-2].t == 'Trade' assert trade.devices[0].actions[-1].t == 'Confirm' @@ -1493,26 +1585,21 @@ def test_usecase_confirmation(user: UserClient, user2: UserClient): request_confirm = { 'type': 'Confirm', 'action': trade.id, - 'devices': [ - snap10['device']['id'] - ] + 'devices': [snap10['device']['id']], } user2.post(res=models.Action, data=request_confirm, status=422) - # The manager add 3 device more into the lot - lot, _ = user.post({}, - res=Lot, - item='{}/devices'.format(lot['id']), - query=devices[7:]) + lot, _ = user.post( + {}, res=Lot, item='{}/devices'.format(lot['id']), query=devices[7:] + ) assert trade.devices[-1].actions[-2].t == 'Trade' assert trade.devices[-1].actions[-1].t == 'Confirm' assert trade.devices[-1].actions[-1].user == trade.user_to assert len(trade.devices[0].actions) == n_actions - # the SCRAP confirms the rest of devices request_confirm = { 'type': 'Confirm', @@ -1524,8 +1611,8 @@ def test_usecase_confirmation(user: UserClient, user2: UserClient): snap7['device']['id'], snap8['device']['id'], snap9['device']['id'], - snap10['device']['id'] - ] + snap10['device']['id'], + ], } user2.post(res=models.Action, data=request_confirm) @@ -1537,17 +1624,15 @@ def test_usecase_confirmation(user: UserClient, user2: UserClient): # The manager remove one device of the lot and automaticaly # is create one revoke action device_10 = trade.devices[-1] - lot, _ = user.delete({}, - res=Lot, - item='{}/devices'.format(lot['id']), - query=devices[-1:], status=200) + lot, _ = user.delete( + {}, res=Lot, item='{}/devices'.format(lot['id']), query=devices[-1:], status=200 + ) assert len(trade.lot.devices) == len(trade.devices) == 10 assert device_10.actions[-1].t == 'Revoke' - lot, _ = user.delete({}, - res=Lot, - item='{}/devices'.format(lot['id']), - query=devices[-1:], status=200) + lot, _ = user.delete( + {}, res=Lot, item='{}/devices'.format(lot['id']), query=devices[-1:], status=200 + ) assert device_10.actions[-1].t == 'Revoke' @@ -1555,9 +1640,7 @@ def test_usecase_confirmation(user: UserClient, user2: UserClient): request_confirm_revoke = { 'type': 'Revoke', 'action': trade.id, - 'devices': [ - snap10['device']['id'] - ] + 'devices': [snap10['device']['id']], } user2.post(res=models.Action, data=request_confirm_revoke) @@ -1570,30 +1653,24 @@ def test_usecase_confirmation(user: UserClient, user2: UserClient): request_confirm_revoke = { 'type': 'Revoke', 'action': trade.id, - 'devices': [ - snap9['device']['id'] - ] + 'devices': [snap9['device']['id']], } # The manager add again device_10 # assert len(trade.devices) == 9 - lot, _ = user.post({}, - res=Lot, - item='{}/devices'.format(lot['id']), - query=devices[-1:]) + lot, _ = user.post( + {}, res=Lot, item='{}/devices'.format(lot['id']), query=devices[-1:] + ) assert device_10.actions[-1].t == 'Confirm' assert device_10 in trade.devices assert len(trade.devices) == 10 - # the SCRAP confirms the action trade for device_10 request_reconfirm = { 'type': 'Confirm', 'action': trade.id, - 'devices': [ - snap10['device']['id'] - ] + 'devices': [snap10['device']['id']], } user2.post(res=models.Action, data=request_reconfirm) assert device_10.actions[-1].t == 'Confirm' @@ -1614,31 +1691,34 @@ def test_confirmRevoke(user: UserClient, user2: UserClient): snap1, _ = user.post(file('basic.snapshot'), res=models.Snapshot) snap2, _ = user.post(file('acer.happy.battery.snapshot'), res=models.Snapshot) snap3, _ = user.post(file('asus-1001pxd.snapshot'), res=models.Snapshot) - snap4, _ = user.post(file('desktop-9644w8n-lenovo-0169622.snapshot'), res=models.Snapshot) - snap5, _ = user.post(file('laptop-hp_255_g3_notebook-hewlett-packard-cnd52270fw.snapshot'), res=models.Snapshot) + snap4, _ = user.post( + file('desktop-9644w8n-lenovo-0169622.snapshot'), res=models.Snapshot + ) + snap5, _ = user.post( + file('laptop-hp_255_g3_notebook-hewlett-packard-cnd52270fw.snapshot'), + res=models.Snapshot, + ) snap6, _ = user.post(file('1-device-with-components.snapshot'), res=models.Snapshot) snap7, _ = user.post(file('asus-eee-1000h.snapshot.11'), res=models.Snapshot) snap8, _ = user.post(file('complete.export.snapshot'), res=models.Snapshot) snap9, _ = user.post(file('real-hp-quad-core.snapshot.11'), res=models.Snapshot) snap10, _ = user.post(file('david.lshw.snapshot'), res=models.Snapshot) - devices = [('id', snap1['device']['id']), - ('id', snap2['device']['id']), - ('id', snap3['device']['id']), - ('id', snap4['device']['id']), - ('id', snap5['device']['id']), - ('id', snap6['device']['id']), - ('id', snap7['device']['id']), - ('id', snap8['device']['id']), - ('id', snap9['device']['id']), - ('id', snap10['device']['id']), - ] - lot, _ = user.post({}, - res=Lot, - item='{}/devices'.format(lot['id']), - query=devices) + devices = [ + ('id', snap1['device']['id']), + ('id', snap2['device']['id']), + ('id', snap3['device']['id']), + ('id', snap4['device']['id']), + ('id', snap5['device']['id']), + ('id', snap6['device']['id']), + ('id', snap7['device']['id']), + ('id', snap8['device']['id']), + ('id', snap9['device']['id']), + ('id', snap10['device']['id']), + ] + lot, _ = user.post({}, res=Lot, item='{}/devices'.format(lot['id']), query=devices) - # the manager shares the temporary lot with the SCRAP as an incoming lot + # the manager shares the temporary lot with the SCRAP as an incoming lot # for the CRAP to confirm it request_post = { 'type': 'Trade', @@ -1659,8 +1739,8 @@ def test_confirmRevoke(user: UserClient, user2: UserClient): 'type': 'Confirm', 'action': trade.id, 'devices': [ - snap1['device']['id'], - snap2['device']['id'], + snap1['device']['id'], + snap2['device']['id'], snap3['device']['id'], snap4['device']['id'], snap5['device']['id'], @@ -1668,8 +1748,8 @@ def test_confirmRevoke(user: UserClient, user2: UserClient): snap7['device']['id'], snap8['device']['id'], snap9['device']['id'], - snap10['device']['id'] - ] + snap10['device']['id'], + ], } user2.post(res=models.Action, data=request_confirm) @@ -1677,31 +1757,28 @@ def test_confirmRevoke(user: UserClient, user2: UserClient): assert trade.devices[-1].actions[-1].t == 'Confirm' assert trade.devices[-1].actions[-1].user == trade.user_from - # The manager remove one device of the lot and automaticaly + # The manager remove one device of the lot and automaticaly # is create one revoke action device_10 = trade.devices[-1] - lot, _ = user.delete({}, - res=Lot, - item='{}/devices'.format(lot['id']), - query=devices[-1:], status=200) + lot, _ = user.delete( + {}, res=Lot, item='{}/devices'.format(lot['id']), query=devices[-1:], status=200 + ) # assert len(trade.lot.devices) == len(trade.devices) == 9 # assert not device_10 in trade.devices assert device_10.actions[-1].t == 'Revoke' - lot, _ = user.delete({}, - res=Lot, - item='{}/devices'.format(lot['id']), - query=devices[-1:], status=200) + lot, _ = user.delete( + {}, res=Lot, item='{}/devices'.format(lot['id']), query=devices[-1:], status=200 + ) assert device_10.actions[-1].t == 'Revoke' # assert device_10.actions[-2].t == 'Confirm' # The manager add again device_10 # assert len(trade.devices) == 9 - lot, _ = user.post({}, - res=Lot, - item='{}/devices'.format(lot['id']), - query=devices[-1:]) + lot, _ = user.post( + {}, res=Lot, item='{}/devices'.format(lot['id']), query=devices[-1:] + ) # assert device_10.actions[-1].t == 'Confirm' assert device_10 in trade.devices @@ -1711,9 +1788,7 @@ def test_confirmRevoke(user: UserClient, user2: UserClient): request_confirm_revoke = { 'type': 'ConfirmRevoke', 'action': device_10.actions[-2].id, - 'devices': [ - snap10['device']['id'] - ] + 'devices': [snap10['device']['id']], } # check validation error @@ -1721,11 +1796,11 @@ def test_confirmRevoke(user: UserClient, user2: UserClient): # the SCRAP confirms the action trade for device_10 # request_reconfirm = { - # 'type': 'Confirm', - # 'action': trade.id, - # 'devices': [ - # snap10['device']['id'] - # ] + # 'type': 'Confirm', + # 'action': trade.id, + # 'devices': [ + # snap10['device']['id'] + # ] # } # user2.post(res=models.Action, data=request_reconfirm) # assert device_10.actions[-1].t == 'Confirm' @@ -1744,13 +1819,13 @@ def test_trade_case1(user: UserClient, user2: UserClient): snap1, _ = user.post(file('basic.snapshot'), res=models.Snapshot) snap2, _ = user.post(file('acer.happy.battery.snapshot'), res=models.Snapshot) - devices = [('id', snap1['device']['id']), - ('id', snap2['device']['id']), - ] - lot, _ = user.post({}, - res=Lot, - item='{}/devices'.format(lot['id']), - query=devices[:-1]) + devices = [ + ('id', snap1['device']['id']), + ('id', snap2['device']['id']), + ] + lot, _ = user.post( + {}, res=Lot, item='{}/devices'.format(lot['id']), query=devices[:-1] + ) # the manager shares the temporary lot with the SCRAP as an incoming lot # for the CRAP to confirm it @@ -1775,10 +1850,9 @@ def test_trade_case1(user: UserClient, user2: UserClient): assert device.actions[-1].t == 'Confirm' assert device.actions[-1].user == trade.user_to - user.delete({}, - res=Lot, - item='{}/devices'.format(lot.id), - query=devices[:-1], status=200) + user.delete( + {}, res=Lot, item='{}/devices'.format(lot.id), query=devices[:-1], status=200 + ) assert device not in trade.lot.devices assert device.trading(trade.lot) == 'RevokeConfirmed' @@ -1796,15 +1870,15 @@ def test_trade_case2(user: UserClient, user2: UserClient): snap1, _ = user.post(file('basic.snapshot'), res=models.Snapshot) snap2, _ = user.post(file('acer.happy.battery.snapshot'), res=models.Snapshot) - devices = [('id', snap1['device']['id']), - ('id', snap2['device']['id']), - ] - lot, _ = user.post({}, - res=Lot, - item='{}/devices'.format(lot['id']), - query=devices[:-1]) + devices = [ + ('id', snap1['device']['id']), + ('id', snap2['device']['id']), + ] + lot, _ = user.post( + {}, res=Lot, item='{}/devices'.format(lot['id']), query=devices[:-1] + ) - # the manager shares the temporary lot with the SCRAP as an incoming lot + # the manager shares the temporary lot with the SCRAP as an incoming lot # for the CRAP to confirm it request_post = { 'type': 'Trade', @@ -1820,10 +1894,9 @@ def test_trade_case2(user: UserClient, user2: UserClient): user.post(res=models.Action, data=request_post) trade = models.Trade.query.one() - lot, _ = user.post({}, - res=Lot, - item='{}/devices'.format(lot['id']), - query=devices[-1:]) + lot, _ = user.post( + {}, res=Lot, item='{}/devices'.format(lot['id']), query=devices[-1:] + ) device1, device2 = trade.devices @@ -1860,13 +1933,13 @@ def test_trade_case3(user: UserClient, user2: UserClient): snap1, _ = user.post(file('basic.snapshot'), res=models.Snapshot) snap2, _ = user2.post(file('acer.happy.battery.snapshot'), res=models.Snapshot) - devices = [('id', snap1['device']['id']), - ('id', snap2['device']['id']), - ] - lot, _ = user.post({}, - res=Lot, - item='{}/devices'.format(lot['id']), - query=devices[:-1]) + devices = [ + ('id', snap1['device']['id']), + ('id', snap2['device']['id']), + ] + lot, _ = user.post( + {}, res=Lot, item='{}/devices'.format(lot['id']), query=devices[:-1] + ) # the manager shares the temporary lot with the SCRAP as an incoming lot # for the CRAP to confirm it @@ -1884,10 +1957,9 @@ def test_trade_case3(user: UserClient, user2: UserClient): user.post(res=models.Action, data=request_post) trade = models.Trade.query.one() - lot, _ = user2.post({}, - res=Lot, - item='{}/devices'.format(lot['id']), - query=devices[-1:]) + lot, _ = user2.post( + {}, res=Lot, item='{}/devices'.format(lot['id']), query=devices[-1:] + ) device1, device2 = trade.devices @@ -1898,10 +1970,9 @@ def test_trade_case3(user: UserClient, user2: UserClient): assert device2.actions[-1].t == 'Confirm' assert device2.actions[-1].user == trade.user_from - lot, _ = user2.delete({}, - res=Lot, - item='{}/devices'.format(lot['id']), - query=devices[-1:], status=200) + lot, _ = user2.delete( + {}, res=Lot, item='{}/devices'.format(lot['id']), query=devices[-1:], status=200 + ) assert device2.actions[-2].t == 'Confirm' assert device2.actions[-1].t == 'Revoke' @@ -1918,15 +1989,15 @@ def test_trade_case4(user: UserClient, user2: UserClient): snap1, _ = user.post(file('basic.snapshot'), res=models.Snapshot) snap2, _ = user2.post(file('acer.happy.battery.snapshot'), res=models.Snapshot) - devices = [('id', snap1['device']['id']), - ('id', snap2['device']['id']), - ] - lot1, _ = user.post({}, - res=Lot, - item='{}/devices'.format(lot['id']), - query=devices[:-1]) + devices = [ + ('id', snap1['device']['id']), + ('id', snap2['device']['id']), + ] + lot1, _ = user.post( + {}, res=Lot, item='{}/devices'.format(lot['id']), query=devices[:-1] + ) - # the manager shares the temporary lot with the SCRAP as an incoming lot + # the manager shares the temporary lot with the SCRAP as an incoming lot # for the CRAP to confirm it request_post = { 'type': 'Trade', @@ -1942,10 +2013,9 @@ def test_trade_case4(user: UserClient, user2: UserClient): user.post(res=models.Action, data=request_post) trade = models.Trade.query.one() - lot2, _ = user2.post({}, - res=Lot, - item='{}/devices'.format(lot['id']), - query=devices[-1:]) + lot2, _ = user2.post( + {}, res=Lot, item='{}/devices'.format(lot['id']), query=devices[-1:] + ) device1, device2 = trade.devices @@ -1983,15 +2053,13 @@ def test_trade_case5(user: UserClient, user2: UserClient): snap1, _ = user.post(file('basic.snapshot'), res=models.Snapshot) snap2, _ = user.post(file('acer.happy.battery.snapshot'), res=models.Snapshot) - devices = [('id', snap1['device']['id']), - ('id', snap2['device']['id']), - ] - lot, _ = user.post({}, - res=Lot, - item='{}/devices'.format(lot['id']), - query=devices) + devices = [ + ('id', snap1['device']['id']), + ('id', snap2['device']['id']), + ] + lot, _ = user.post({}, res=Lot, item='{}/devices'.format(lot['id']), query=devices) - # the manager shares the temporary lot with the SCRAP as an incoming lot + # the manager shares the temporary lot with the SCRAP as an incoming lot # for the CRAP to confirm it request_post = { 'type': 'Trade', @@ -2016,10 +2084,9 @@ def test_trade_case5(user: UserClient, user2: UserClient): assert device2.actions[-1].t == 'Confirm' assert device2.actions[-1].user == trade.user_to - lot, _ = user2.delete({}, - res=Lot, - item='{}/devices'.format(lot['id']), - query=devices[-1:], status=200) + lot, _ = user2.delete( + {}, res=Lot, item='{}/devices'.format(lot['id']), query=devices[-1:], status=200 + ) assert device2.actions[-2].t == 'Confirm' assert device2.actions[-1].t == 'Revoke' @@ -2049,15 +2116,15 @@ def test_trade_case6(user: UserClient, user2: UserClient): snap1, _ = user.post(file('basic.snapshot'), res=models.Snapshot) snap2, _ = user2.post(file('acer.happy.battery.snapshot'), res=models.Snapshot) - devices = [('id', snap1['device']['id']), - ('id', snap2['device']['id']), - ] - lot, _ = user.post({}, - res=Lot, - item='{}/devices'.format(lot['id']), - query=devices[:-1]) + devices = [ + ('id', snap1['device']['id']), + ('id', snap2['device']['id']), + ] + lot, _ = user.post( + {}, res=Lot, item='{}/devices'.format(lot['id']), query=devices[:-1] + ) - # the manager shares the temporary lot with the SCRAP as an incoming lot + # the manager shares the temporary lot with the SCRAP as an incoming lot # for the CRAP to confirm it request_post = { 'type': 'Trade', @@ -2073,10 +2140,9 @@ def test_trade_case6(user: UserClient, user2: UserClient): user.post(res=models.Action, data=request_post) trade = models.Trade.query.one() - lot, _ = user2.post({}, - res=Lot, - item='{}/devices'.format(lot['id']), - query=devices[-1:]) + lot, _ = user2.post( + {}, res=Lot, item='{}/devices'.format(lot['id']), query=devices[-1:] + ) device1, device2 = trade.devices @@ -2087,10 +2153,9 @@ def test_trade_case6(user: UserClient, user2: UserClient): assert device2.actions[-1].t == 'Confirm' assert device2.actions[-1].user == trade.user_from - lot, _ = user.delete({}, - res=Lot, - item='{}/devices'.format(lot['id']), - query=devices[-1:], status=200) + lot, _ = user.delete( + {}, res=Lot, item='{}/devices'.format(lot['id']), query=devices[-1:], status=200 + ) assert device2.actions[-2].t == 'Confirm' assert device2.actions[-1].t == 'Revoke' @@ -2120,12 +2185,9 @@ def test_trade_case7(user: UserClient, user2: UserClient): snap1, _ = user.post(file('basic.snapshot'), res=models.Snapshot) devices = [('id', snap1['device']['id'])] - lot, _ = user.post({}, - res=Lot, - item='{}/devices'.format(lot['id']), - query=devices) + lot, _ = user.post({}, res=Lot, item='{}/devices'.format(lot['id']), query=devices) - # the manager shares the temporary lot with the SCRAP as an incoming lot + # the manager shares the temporary lot with the SCRAP as an incoming lot # for the CRAP to confirm it request_post = { 'type': 'Trade', @@ -2152,10 +2214,9 @@ def test_trade_case7(user: UserClient, user2: UserClient): user2.post(res=models.Action, data=request_confirm) assert device.trading(trade.lot) == 'TradeConfirmed' - lot, _ = user.delete({}, - res=Lot, - item='{}/devices'.format(lot['id']), - query=devices, status=200) + lot, _ = user.delete( + {}, res=Lot, item='{}/devices'.format(lot['id']), query=devices, status=200 + ) request_confirm_revoke = { 'type': 'Revoke', @@ -2187,12 +2248,9 @@ def test_trade_case8(user: UserClient, user2: UserClient): snap1, _ = user.post(file('basic.snapshot'), res=models.Snapshot) devices = [('id', snap1['device']['id'])] - lot, _ = user.post({}, - res=Lot, - item='{}/devices'.format(lot['id']), - query=devices) + lot, _ = user.post({}, res=Lot, item='{}/devices'.format(lot['id']), query=devices) - # the manager shares the temporary lot with the SCRAP as an incoming lot + # the manager shares the temporary lot with the SCRAP as an incoming lot # for the CRAP to confirm it request_post = { 'type': 'Trade', @@ -2258,15 +2316,12 @@ def test_trade_case9(user: UserClient, user2: UserClient): snap1, _ = user.post(file('basic.snapshot'), res=models.Snapshot) snap2, _ = user2.post(file('acer.happy.battery.snapshot'), res=models.Snapshot) - devices = [('id', snap1['device']['id']), - ('id', snap2['device']['id']) - ] - lot, _ = user.post({}, - res=Lot, - item='{}/devices'.format(lot['id']), - query=devices[:-1]) + devices = [('id', snap1['device']['id']), ('id', snap2['device']['id'])] + lot, _ = user.post( + {}, res=Lot, item='{}/devices'.format(lot['id']), query=devices[:-1] + ) - # the manager shares the temporary lot with the SCRAP as an incoming lot + # the manager shares the temporary lot with the SCRAP as an incoming lot # for the CRAP to confirm it request_post = { 'type': 'Trade', @@ -2282,10 +2337,9 @@ def test_trade_case9(user: UserClient, user2: UserClient): user.post(res=models.Action, data=request_post) trade = models.Trade.query.one() - lot, _ = user2.post({}, - res=Lot, - item='{}/devices'.format(lot['id']), - query=devices[-1:]) + lot, _ = user2.post( + {}, res=Lot, item='{}/devices'.format(lot['id']), query=devices[-1:] + ) device1, device = trade.devices @@ -2303,10 +2357,9 @@ def test_trade_case9(user: UserClient, user2: UserClient): assert device.owner == trade.user_to - lot, _ = user2.delete({}, - res=Lot, - item='{}/devices'.format(lot['id']), - query=devices[-1:], status=200) + lot, _ = user2.delete( + {}, res=Lot, item='{}/devices'.format(lot['id']), query=devices[-1:], status=200 + ) request_confirm_revoke = { 'type': 'Revoke', @@ -2340,15 +2393,12 @@ def test_trade_case10(user: UserClient, user2: UserClient): snap1, _ = user.post(file('basic.snapshot'), res=models.Snapshot) snap2, _ = user2.post(file('acer.happy.battery.snapshot'), res=models.Snapshot) - devices = [('id', snap1['device']['id']), - ('id', snap2['device']['id']) - ] - lot, _ = user.post({}, - res=Lot, - item='{}/devices'.format(lot['id']), - query=devices[:-1]) + devices = [('id', snap1['device']['id']), ('id', snap2['device']['id'])] + lot, _ = user.post( + {}, res=Lot, item='{}/devices'.format(lot['id']), query=devices[:-1] + ) - # the manager shares the temporary lot with the SCRAP as an incoming lot + # the manager shares the temporary lot with the SCRAP as an incoming lot # for the CRAP to confirm it request_post = { 'type': 'Trade', @@ -2364,10 +2414,9 @@ def test_trade_case10(user: UserClient, user2: UserClient): user.post(res=models.Action, data=request_post) trade = models.Trade.query.one() - lot, _ = user2.post({}, - res=Lot, - item='{}/devices'.format(lot['id']), - query=devices[-1:]) + lot, _ = user2.post( + {}, res=Lot, item='{}/devices'.format(lot['id']), query=devices[-1:] + ) device1, device = trade.devices @@ -2427,15 +2476,10 @@ def test_trade_case11(user: UserClient, user2: UserClient): snap1, _ = user.post(file('basic.snapshot'), res=models.Snapshot) snap2, _ = user.post(file('acer.happy.battery.snapshot'), res=models.Snapshot) - devices = [('id', snap1['device']['id']), - ('id', snap2['device']['id']) - ] - lot, _ = user.post({}, - res=Lot, - item='{}/devices'.format(lot['id']), - query=devices) + devices = [('id', snap1['device']['id']), ('id', snap2['device']['id'])] + lot, _ = user.post({}, res=Lot, item='{}/devices'.format(lot['id']), query=devices) - # the manager shares the temporary lot with the SCRAP as an incoming lot + # the manager shares the temporary lot with the SCRAP as an incoming lot # for the CRAP to confirm it request_post = { 'type': 'Trade', @@ -2463,10 +2507,9 @@ def test_trade_case11(user: UserClient, user2: UserClient): user2.post(res=models.Action, data=request_confirm) assert device.trading(trade.lot) == 'TradeConfirmed' - lot, _ = user2.delete({}, - res=Lot, - item='{}/devices'.format(lot['id']), - query=devices[-1:], status=200) + lot, _ = user2.delete( + {}, res=Lot, item='{}/devices'.format(lot['id']), query=devices[-1:], status=200 + ) assert device.trading(trade.lot) == 'Revoke' request_confirm_revoke = { @@ -2499,15 +2542,10 @@ def test_trade_case12(user: UserClient, user2: UserClient): snap1, _ = user.post(file('basic.snapshot'), res=models.Snapshot) snap2, _ = user.post(file('acer.happy.battery.snapshot'), res=models.Snapshot) - devices = [('id', snap1['device']['id']), - ('id', snap2['device']['id']) - ] - lot, _ = user.post({}, - res=Lot, - item='{}/devices'.format(lot['id']), - query=devices) + devices = [('id', snap1['device']['id']), ('id', snap2['device']['id'])] + lot, _ = user.post({}, res=Lot, item='{}/devices'.format(lot['id']), query=devices) - # the manager shares the temporary lot with the SCRAP as an incoming lot + # the manager shares the temporary lot with the SCRAP as an incoming lot # for the CRAP to confirm it request_post = { 'type': 'Trade', @@ -2576,15 +2614,12 @@ def test_trade_case13(user: UserClient, user2: UserClient): snap1, _ = user.post(file('basic.snapshot'), res=models.Snapshot) snap2, _ = user2.post(file('acer.happy.battery.snapshot'), res=models.Snapshot) - devices = [('id', snap1['device']['id']), - ('id', snap2['device']['id']) - ] - lot, _ = user.post({}, - res=Lot, - item='{}/devices'.format(lot['id']), - query=devices[:-1]) + devices = [('id', snap1['device']['id']), ('id', snap2['device']['id'])] + lot, _ = user.post( + {}, res=Lot, item='{}/devices'.format(lot['id']), query=devices[:-1] + ) - # the manager shares the temporary lot with the SCRAP as an incoming lot + # the manager shares the temporary lot with the SCRAP as an incoming lot # for the CRAP to confirm it request_post = { 'type': 'Trade', @@ -2600,10 +2635,9 @@ def test_trade_case13(user: UserClient, user2: UserClient): user.post(res=models.Action, data=request_post) trade = models.Trade.query.one() - lot, _ = user2.post({}, - res=Lot, - item='{}/devices'.format(lot['id']), - query=devices[-1:]) + lot, _ = user2.post( + {}, res=Lot, item='{}/devices'.format(lot['id']), query=devices[-1:] + ) device1, device = trade.devices assert device1.trading(trade.lot) == 'NeedConfirmation' @@ -2619,10 +2653,9 @@ def test_trade_case13(user: UserClient, user2: UserClient): assert device1.trading(trade.lot) == 'Confirm' assert device.trading(trade.lot) == 'TradeConfirmed' - lot, _ = user.delete({}, - res=Lot, - item='{}/devices'.format(lot['id']), - query=devices[-1:], status=200) + lot, _ = user.delete( + {}, res=Lot, item='{}/devices'.format(lot['id']), query=devices[-1:], status=200 + ) assert device1.trading(trade.lot) == 'Confirm' assert device.trading(trade.lot) == 'Revoke' @@ -2656,15 +2689,12 @@ def test_trade_case14(user: UserClient, user2: UserClient): snap1, _ = user.post(file('basic.snapshot'), res=models.Snapshot) snap2, _ = user2.post(file('acer.happy.battery.snapshot'), res=models.Snapshot) - devices = [('id', snap1['device']['id']), - ('id', snap2['device']['id']) - ] - lot, _ = user.post({}, - res=Lot, - item='{}/devices'.format(lot['id']), - query=devices[:-1]) + devices = [('id', snap1['device']['id']), ('id', snap2['device']['id'])] + lot, _ = user.post( + {}, res=Lot, item='{}/devices'.format(lot['id']), query=devices[:-1] + ) - # the manager shares the temporary lot with the SCRAP as an incoming lot + # the manager shares the temporary lot with the SCRAP as an incoming lot # for the CRAP to confirm it request_post = { 'type': 'Trade', @@ -2680,10 +2710,9 @@ def test_trade_case14(user: UserClient, user2: UserClient): user.post(res=models.Action, data=request_post) trade = models.Trade.query.one() - lot, _ = user2.post({}, - res=Lot, - item='{}/devices'.format(lot['id']), - query=devices[-1:]) + lot, _ = user2.post( + {}, res=Lot, item='{}/devices'.format(lot['id']), query=devices[-1:] + ) device1, device = trade.devices assert device1.trading(trade.lot) == 'NeedConfirmation' @@ -2734,11 +2763,26 @@ def test_trade_case14(user: UserClient, user2: UserClient): @pytest.mark.usefixtures(conftest.app_context.__name__) def test_action_web_erase(user: UserClient, client: Client): import hashlib + from ereuse_devicehub.resources.documents import documents + bfile = BytesIO(b'abc') hash3 = hashlib.sha3_256(bfile.read()).hexdigest() snap, _ = user.post(file('acer.happy.battery.snapshot'), res=models.Snapshot) - request = {'type': 'DataWipe', 'devices': [snap['device']['id']], 'name': 'borrado universal', 'severity': 'Info', 'description': 'nada que describir', 'url': 'http://www.google.com/', 'documentId': '33', 'endTime': '2021-07-07T22:00:00.000Z', 'filename': 'Certificado de borrado1.pdf', 'hash': hash3, 'success': 1, 'software': "Blanco"} + request = { + 'type': 'DataWipe', + 'devices': [snap['device']['id']], + 'name': 'borrado universal', + 'severity': 'Info', + 'description': 'nada que describir', + 'url': 'http://www.google.com/', + 'documentId': '33', + 'endTime': '2021-07-07T22:00:00.000Z', + 'filename': 'Certificado de borrado1.pdf', + 'hash': hash3, + 'success': 1, + 'software': "Blanco", + } user.post(res=models.Action, data=request) action = models.DataWipe.query.one() @@ -2748,12 +2792,14 @@ def test_action_web_erase(user: UserClient, client: Client): assert action.document.file_hash == request['hash'] bfile = BytesIO(b'abc') - response, _ = client.post(res=documents.DocumentDef.t, - item='stamps/', - content_type='multipart/form-data', - accept='text/html', - data={'docUpload': [(bfile, 'example.csv')]}, - status=200) + response, _ = client.post( + res=documents.DocumentDef.t, + item='stamps/', + content_type='multipart/form-data', + accept='text/html', + data={'docUpload': [(bfile, 'example.csv')]}, + status=200, + ) assert "alert alert-info" in response assert "100% coincidence." in response assert not "alert alert-danger" in response @@ -2764,13 +2810,15 @@ def test_action_web_erase(user: UserClient, client: Client): def test_moveOnDocument(user: UserClient, user2: UserClient): lotIn, _ = user.post({'name': 'MyLotIn'}, res=Lot) lotOut, _ = user.post({'name': 'MyLotOut'}, res=Lot) - url = 'http://www.ereuse.org/apapaapaapaapaapaapaapaapaapaapapaapaapaapaapaapaapaapaapaapapaapaapaapaapaapaapaapaapaapaaaa', + url = ( + 'http://www.ereuse.org/apapaapaapaapaapaapaapaapaapaapapaapaapaapaapaapaapaapaapaapapaapaapaapaapaapaapaapaapaapaaaa', + ) request_post1 = { 'filename': 'test.pdf', 'hash': 'bbbbbbbb', 'url': url, 'weight': 150, - 'lot': lotIn['id'] + 'lot': lotIn['id'], } tradedocument_from, _ = user.post(res=TradeDocument, data=request_post1) id_hash = 'aaaaaaaaa' @@ -2779,7 +2827,7 @@ def test_moveOnDocument(user: UserClient, user2: UserClient): 'hash': id_hash, 'url': url, 'weight': 0, - 'lot': lotOut['id'] + 'lot': lotOut['id'], } tradedocument_to, _ = user.post(res=TradeDocument, data=request_post2) @@ -2802,7 +2850,7 @@ def test_moveOnDocument(user: UserClient, user2: UserClient): 'weight': 15, 'container_from': tradedocument_from['id'], 'container_to': id_hash, - 'description': description + 'description': description, } doc, _ = user.post(res=models.Action, data=request_moveOn) @@ -2810,7 +2858,7 @@ def test_moveOnDocument(user: UserClient, user2: UserClient): assert doc['container_from']['id'] == tradedocument_from['id'] assert doc['container_to']['id'] == tradedocument_to['id'] - mvs= models.MoveOnDocument.query.filter().first() + mvs = models.MoveOnDocument.query.filter().first() trade_from = TradeDocument.query.filter_by(id=tradedocument_from['id']).one() trade_to = TradeDocument.query.filter_by(id=tradedocument_to['id']).one() assert trade_from in mvs.documents @@ -2826,7 +2874,14 @@ def test_delete_devices(user: UserClient): """This action deactive one device and simulate than one devices is delete.""" snap, _ = user.post(file('acer.happy.battery.snapshot'), res=models.Snapshot) - request = {'type': 'Delete', 'devices': [snap['device']['id']], 'name': 'borrado universal', 'severity': 'Info', 'description': 'duplicity of devices', 'endTime': '2021-07-07T22:00:00.000Z'} + request = { + 'type': 'Delete', + 'devices': [snap['device']['id']], + 'name': 'borrado universal', + 'severity': 'Info', + 'description': 'duplicity of devices', + 'endTime': '2021-07-07T22:00:00.000Z', + } action, _ = user.post(res=models.Action, data=request) @@ -2840,8 +2895,6 @@ def test_delete_devices(user: UserClient): assert str(action_delete.id) == action['id'] assert db_device.active == False - - # Check use of filter from frontend url = '/devices/?filter={"type":["Computer"]}' @@ -2857,14 +2910,28 @@ def test_delete_devices_check_sync(user: UserClient): file_snap1 = file('1-device-with-components.snapshot') file_snap2 = file('2-device-with-components.snapshot') snap, _ = user.post(file_snap1, res=models.Snapshot) - request = {'type': 'Delete', 'devices': [snap['device']['id']], 'name': 'borrado universal', 'severity': 'Info', 'description': 'duplicity of devices', 'endTime': '2021-07-07T22:00:00.000Z'} + request = { + 'type': 'Delete', + 'devices': [snap['device']['id']], + 'name': 'borrado universal', + 'severity': 'Info', + 'description': 'duplicity of devices', + 'endTime': '2021-07-07T22:00:00.000Z', + } action, _ = user.post(res=models.Action, data=request) device1 = Device.query.filter_by(id=snap['device']['id']).one() snap2, _ = user.post(file_snap2, res=models.Snapshot) - request2 = {'type': 'Delete', 'devices': [snap2['device']['id']], 'name': 'borrado universal', 'severity': 'Info', 'description': 'duplicity of devices', 'endTime': '2021-07-07T22:00:00.000Z'} + request2 = { + 'type': 'Delete', + 'devices': [snap2['device']['id']], + 'name': 'borrado universal', + 'severity': 'Info', + 'description': 'duplicity of devices', + 'endTime': '2021-07-07T22:00:00.000Z', + } action2, _ = user.post(res=models.Action, data=request2) @@ -2872,8 +2939,17 @@ def test_delete_devices_check_sync(user: UserClient): # check than device2 is an other device than device1 assert device2.id != device1.id # check than device2 have the components of device1 - assert len([x for x in device2.components - if device1.id in [y.device.id for y in x.actions if hasattr(y, 'device')]]) == 1 + assert ( + len( + [ + x + for x in device2.components + if device1.id + in [y.device.id for y in x.actions if hasattr(y, 'device')] + ] + ) + == 1 + ) @pytest.mark.mvp @@ -2885,7 +2961,14 @@ def test_delete_devices_permitions(user: UserClient, user2: UserClient): snap, _ = user.post(file_snap, res=models.Snapshot) device = Device.query.filter_by(id=snap['device']['id']).one() - request = {'type': 'Delete', 'devices': [snap['device']['id']], 'name': 'borrado universal', 'severity': 'Info', 'description': 'duplicity of devices', 'endTime': '2021-07-07T22:00:00.000Z'} + request = { + 'type': 'Delete', + 'devices': [snap['device']['id']], + 'name': 'borrado universal', + 'severity': 'Info', + 'description': 'duplicity of devices', + 'endTime': '2021-07-07T22:00:00.000Z', + } action, _ = user2.post(res=models.Action, data=request, status=422) @@ -2895,13 +2978,15 @@ def test_moveOnDocument_bug168(user: UserClient, user2: UserClient): """If you use one moveOnDocument in a trade Document. Next you can not drop this document.""" lotIn, _ = user.post({'name': 'MyLotIn'}, res=Lot) lotOut, _ = user.post({'name': 'MyLotOut'}, res=Lot) - url = 'http://www.ereuse.org/apapaapaapaapaapaapaapaapaapaapapaapaapaapaapaapaapaapaapaapapaapaapaapaapaapaapaapaapaapaaaa', + url = ( + 'http://www.ereuse.org/apapaapaapaapaapaapaapaapaapaapapaapaapaapaapaapaapaapaapaapapaapaapaapaapaapaapaapaapaapaaaa', + ) request_post1 = { 'filename': 'test.pdf', 'hash': 'bbbbbbbb', 'url': url, 'weight': 150, - 'lot': lotIn['id'] + 'lot': lotIn['id'], } tradedocument_from, _ = user.post(res=TradeDocument, data=request_post1) id_hash = 'aaaaaaaaa' @@ -2910,7 +2995,7 @@ def test_moveOnDocument_bug168(user: UserClient, user2: UserClient): 'hash': id_hash, 'url': url, 'weight': 0, - 'lot': lotOut['id'] + 'lot': lotOut['id'], } tradedocument_to, _ = user.post(res=TradeDocument, data=request_post2) @@ -2933,7 +3018,7 @@ def test_moveOnDocument_bug168(user: UserClient, user2: UserClient): 'weight': 4, 'container_from': tradedocument_from['id'], 'container_to': id_hash, - 'description': description + 'description': description, } doc, _ = user.post(res=models.Action, data=request_moveOn) trade = models.Trade.query.one() diff --git a/tests/test_documents.py b/tests/test_documents.py index 0db7d716..538e2620 100644 --- a/tests/test_documents.py +++ b/tests/test_documents.py @@ -1,30 +1,30 @@ import csv import hashlib from datetime import datetime -from io import StringIO, BytesIO +from io import BytesIO, StringIO from pathlib import Path -from flask import url_for import pytest -from werkzeug.exceptions import Unauthorized import teal.marshmallow from ereuse_utils.test import ANY +from flask import url_for +from werkzeug.exceptions import Unauthorized from ereuse_devicehub import auth from ereuse_devicehub.client import Client, UserClient +from ereuse_devicehub.db import db from ereuse_devicehub.devicehub import Devicehub -from ereuse_devicehub.resources.user.models import Session -from ereuse_devicehub.resources.action.models import Snapshot, Allocate, Live -from ereuse_devicehub.resources.documents import documents -from ereuse_devicehub.resources.tradedocument.models import TradeDocument +from ereuse_devicehub.resources.action.models import Allocate, Live, Snapshot from ereuse_devicehub.resources.device import models as d +from ereuse_devicehub.resources.documents import documents +from ereuse_devicehub.resources.enums import SessionType +from ereuse_devicehub.resources.hash_reports import ReportHash from ereuse_devicehub.resources.lot.models import Lot from ereuse_devicehub.resources.tag.model import Tag -from ereuse_devicehub.resources.hash_reports import ReportHash -from ereuse_devicehub.resources.enums import SessionType -from ereuse_devicehub.db import db +from ereuse_devicehub.resources.tradedocument.models import TradeDocument +from ereuse_devicehub.resources.user.models import Session from tests import conftest -from tests.conftest import file, yaml2json, json_encode +from tests.conftest import file, json_encode, yaml2json @pytest.mark.mvp @@ -33,27 +33,30 @@ def test_erasure_certificate_public_one(user: UserClient, client: Client): s = file('erase-sectors.snapshot') snapshot, _ = user.post(s, res=Snapshot) - doc, response = user.get(res=documents.DocumentDef.t, - item='erasures/{}'.format( - snapshot['device']['id']), - accept=ANY) + doc, response = user.get( + res=documents.DocumentDef.t, + item='erasures/{}'.format(snapshot['device']['id']), + accept=ANY, + ) assert 'html' in response.content_type assert ' 0 @@ -124,36 +136,46 @@ def test_export_csv_actions(user: UserClient, user2: UserClient, client: Client) acer = yaml2json('acer.happy.battery.snapshot') snapshot, _ = user.post(json_encode(acer), res=Snapshot) device_id = snapshot['device']['id'] - post_request = {"transaction": "ccc", "name": "John", "endUsers": 1, - "devices": [device_id], "description": "aaa", - "finalUserCode": "abcdefjhi", - "startTime": "2020-11-01T02:00:00+00:00", - "endTime": "2020-12-01T02:00:00+00:00" - } + post_request = { + "transaction": "ccc", + "name": "John", + "endUsers": 1, + "devices": [device_id], + "description": "aaa", + "finalUserCode": "abcdefjhi", + "startTime": "2020-11-01T02:00:00+00:00", + "endTime": "2020-12-01T02:00:00+00:00", + } user.post(res=Allocate, data=post_request) hdd = [c for c in acer['components'] if c['type'] == 'HardDrive'][0] - hdd_action = [a for a in hdd['actions'] - if a['type'] == 'TestDataStorage'][0] + hdd_action = [a for a in hdd['actions'] if a['type'] == 'TestDataStorage'][0] hdd_action['lifetime'] += 1000 acer.pop('elapsed') acer['licence_version'] = '1.0.0' snapshot, _ = client.post(acer, res=Live) - csv_user, _ = user.get(res=documents.DocumentDef.t, - item='actions/', - accept='text/csv', - query=[('filter', {'type': ['Computer'], 'ids': [device_id]})]) + csv_user, _ = user.get( + res=documents.DocumentDef.t, + item='actions/', + accept='text/csv', + query=[('filter', {'type': ['Computer'], 'ids': [device_id]})], + ) - csv_user2, _ = user2.get(res=documents.DocumentDef.t, - item='actions/', - accept='text/csv', - query=[('filter', {'type': ['Computer']})]) + csv_user2, _ = user2.get( + res=documents.DocumentDef.t, + item='actions/', + accept='text/csv', + query=[('filter', {'type': ['Computer']})], + ) - _, res = client.get(res=documents.DocumentDef.t, - item='actions/', - accept='text/csv', - query=[('filter', {'type': ['Computer']})], status=401) + _, res = client.get( + res=documents.DocumentDef.t, + item='actions/', + accept='text/csv', + query=[('filter', {'type': ['Computer']})], + status=401, + ) assert res.status_code == 401 assert len(csv_user) > 0 @@ -167,21 +189,27 @@ def test_live_export_csv2(user: UserClient, client: Client, app: Devicehub): acer = yaml2json('acer-happy.snapshot-test1') snapshot, _ = user.post(json_encode(acer), res=Snapshot) device_id = snapshot['device']['id'] - post_request = {"transaction": "ccc", "name": "John", "endUsers": 1, - "devices": [device_id], "description": "aaa", - "finalUserCode": "abcdefjhi", - "startTime": "2020-11-01T02:00:00+00:00", - "endTime": "2020-12-01T02:00:00+00:00" - } + post_request = { + "transaction": "ccc", + "name": "John", + "endUsers": 1, + "devices": [device_id], + "description": "aaa", + "finalUserCode": "abcdefjhi", + "startTime": "2020-11-01T02:00:00+00:00", + "endTime": "2020-12-01T02:00:00+00:00", + } user.post(res=Allocate, data=post_request) acer = yaml2json('acer-happy.live-test1') live, _ = client.post(acer, res=Live) - csv_user, _ = user.get(res=documents.DocumentDef.t, - item='actions/', - accept='text/csv', - query=[('filter', {'type': ['Computer'], 'ids': [device_id]})]) + csv_user, _ = user.get( + res=documents.DocumentDef.t, + item='actions/', + accept='text/csv', + query=[('filter', {'type': ['Computer'], 'ids': [device_id]})], + ) assert "4692" in csv_user assert "8692" in csv_user @@ -195,11 +223,15 @@ def test_live_example2(user: UserClient, client: Client, app: Devicehub): acer = yaml2json('acer-happy.snapshot-test1') snapshot, _ = user.post(json_encode(acer), res=Snapshot) device_id = snapshot['device']['id'] - post_request = {"transaction": "ccc", "name": "John", "endUsers": 1, - "devices": [device_id], "description": "aaa", - "finalUserCode": "abcdefjhi", - "startTime": "2020-11-01T02:00:00+00:00", - "endTime": "2020-12-01T02:00:00+00:00" + post_request = { + "transaction": "ccc", + "name": "John", + "endUsers": 1, + "devices": [device_id], + "description": "aaa", + "finalUserCode": "abcdefjhi", + "startTime": "2020-11-01T02:00:00+00:00", + "endTime": "2020-12-01T02:00:00+00:00", } user.post(res=Allocate, data=post_request) @@ -217,27 +249,36 @@ def test_export_basic_snapshot(user: UserClient): """Test export device information in a csv file.""" snapshot, _ = user.post(file('basic.snapshot'), res=Snapshot) dev_id = snapshot['device']['id'] - csv_str, _ = user.get(res=documents.DocumentDef.t, - item='devices/', - accept='text/csv', - query=[('filter', {'type': ['Computer'], 'ids': [dev_id]})]) + csv_str, _ = user.get( + res=documents.DocumentDef.t, + item='devices/', + accept='text/csv', + query=[('filter', {'type': ['Computer'], 'ids': [dev_id]})], + ) f = StringIO(csv_str) obj_csv = csv.reader(f, f, delimiter=';', quotechar='"') export_csv = list(obj_csv) # Open fixture csv and transform to list - with Path(__file__).parent.joinpath('files').joinpath('basic.csv').open() as csv_file: + with Path(__file__).parent.joinpath('files').joinpath( + 'basic.csv' + ).open() as csv_file: obj_csv = csv.reader(csv_file, delimiter=';', quotechar='"') fixture_csv = list(obj_csv) - assert isinstance(datetime.strptime(export_csv[1][19], '%c'), datetime), \ - 'Register in field is not a datetime' + assert isinstance( + datetime.strptime(export_csv[1][19], '%c'), datetime + ), 'Register in field is not a datetime' assert fixture_csv[0] == export_csv[0], 'Headers are not equal' - assert fixture_csv[1][:19] == export_csv[1][:19], 'Computer information are not equal' + assert ( + fixture_csv[1][:19] == export_csv[1][:19] + ), 'Computer information are not equal' assert fixture_csv[1][20] == export_csv[1][20], 'Computer information are not equal' - assert fixture_csv[1][22:] == export_csv[1][22:], 'Computer information are not equal' + assert ( + fixture_csv[1][22:] == export_csv[1][22:] + ), 'Computer information are not equal' @pytest.mark.mvp @@ -245,13 +286,17 @@ def test_export_basic_snapshot(user: UserClient): def test_check_insert_hash(app: Devicehub, user: UserClient, client: Client): """Test export device information in a csv file.""" snapshot, _ = user.post(file('basic.snapshot'), res=Snapshot) - csv_str, _ = user.get(res=documents.DocumentDef.t, - item='devices/', - accept='text/csv', - query=[('filter', {'type': ['Computer']})]) + csv_str, _ = user.get( + res=documents.DocumentDef.t, + item='devices/', + accept='text/csv', + query=[('filter', {'type': ['Computer']})], + ) hash3 = hashlib.sha3_256(csv_str.encode('utf-8')).hexdigest() assert ReportHash.query.filter_by(hash3=hash3).count() == 1 - result, status = client.get(res=documents.DocumentDef.t, item='check/', query=[('hash', hash3)]) + result, status = client.get( + res=documents.DocumentDef.t, item='check/', query=[('hash', hash3)] + ) assert status.status_code == 200 assert result @@ -266,7 +311,9 @@ def test_check_insert_hash(app: Devicehub, user: UserClient, client: Client): @pytest.mark.mvp def test_export_extended(app: Devicehub, user: UserClient): """Test a export device with all information and a lot of components.""" - snapshot1, _ = user.post(file('real-eee-1001pxd.snapshot.12'), res=Snapshot, status=201) + snapshot1, _ = user.post( + file('real-eee-1001pxd.snapshot.12'), res=Snapshot, status=201 + ) snapshot2, _ = user.post(file('complete.export.snapshot'), res=Snapshot, status=201) dev1_id = snapshot1['device']['id'] dev2_id = snapshot2['device']['id'] @@ -276,10 +323,12 @@ def test_export_extended(app: Devicehub, user: UserClient): db.session.add(pc) db.session.commit() - csv_str, _ = user.get(res=documents.DocumentDef.t, - item='devices/', - accept='text/csv', - query=[('filter', {'type': ['Computer'], 'ids': [dev1_id, dev2_id]})]) + csv_str, _ = user.get( + res=documents.DocumentDef.t, + item='devices/', + accept='text/csv', + query=[('filter', {'type': ['Computer'], 'ids': [dev1_id, dev2_id]})], + ) f = StringIO(csv_str) obj_csv = csv.reader(f, f, delimiter=';', quotechar='"') @@ -287,28 +336,50 @@ def test_export_extended(app: Devicehub, user: UserClient): # Open fixture csv and transform to list with Path(__file__).parent.joinpath('files').joinpath( - 'proposal_extended_csv_report.csv').open() as csv_file: + 'proposal_extended_csv_report.csv' + ).open() as csv_file: obj_csv = csv.reader(csv_file, delimiter=';', quotechar='"') fixture_csv = list(obj_csv) - assert isinstance(datetime.strptime(export_csv[1][19], '%c'), datetime), \ - 'Register in field is not a datetime' + assert isinstance( + datetime.strptime(export_csv[1][19], '%c'), datetime + ), 'Register in field is not a datetime' assert fixture_csv[0] == export_csv[0], 'Headers are not equal' - assert fixture_csv[1][:19] == export_csv[1][:19], 'Computer information are not equal' + assert ( + fixture_csv[1][:19] == export_csv[1][:19] + ), 'Computer information are not equal' assert fixture_csv[1][20] == export_csv[1][20], 'Computer information are not equal' - assert fixture_csv[1][22:82] == export_csv[1][22:82], 'Computer information are not equal' - assert fixture_csv[1][83] == export_csv[1][83], 'Computer information are not equal' - assert fixture_csv[1][86:] == export_csv[1][86:], 'Computer information are not equal' - assert fixture_csv[2][:19] == export_csv[2][:19], 'Computer information are not equal' + assert ( + fixture_csv[1][22:83] == export_csv[1][22:83] + ), 'Computer information are not equal' + assert fixture_csv[1][84] == export_csv[1][84], 'Computer information are not equal' + assert ( + fixture_csv[1][87:] == export_csv[1][87:] + ), 'Computer information are not equal' + assert ( + fixture_csv[2][:19] == export_csv[2][:19] + ), 'Computer information are not equal' assert fixture_csv[2][20] == export_csv[2][20], 'Computer information are not equal' - assert fixture_csv[2][22:82] == export_csv[2][22:82], 'Computer information are not equal' - assert fixture_csv[2][83] == export_csv[2][83], 'Computer information are not equal' - assert fixture_csv[2][86:106] == export_csv[2][86:106], 'Computer information are not equal' - assert fixture_csv[2][109] == export_csv[2][109], 'Computer information are not equal' - assert fixture_csv[2][112:133] == export_csv[2][112:133], 'Computer information are not equal' - assert fixture_csv[2][135] == export_csv[2][135], 'Computer information are not equal' - assert fixture_csv[2][138:] == export_csv[2][138:], 'Computer information are not equal' + assert ( + fixture_csv[2][22:83] == export_csv[2][22:83] + ), 'Computer information are not equal' + assert fixture_csv[2][84] == export_csv[2][84], 'Computer information are not equal' + assert ( + fixture_csv[2][86:107] == export_csv[2][86:107] + ), 'Computer information are not equal' + assert ( + fixture_csv[2][110] == export_csv[2][110] + ), 'Computer information are not equal' + assert ( + fixture_csv[2][113:134] == export_csv[2][113:134] + ), 'Computer information are not equal' + assert ( + fixture_csv[2][136] == export_csv[2][136] + ), 'Computer information are not equal' + assert ( + fixture_csv[2][139:] == export_csv[2][139:] + ), 'Computer information are not equal' @pytest.mark.mvp @@ -316,9 +387,9 @@ def test_export_empty(user: UserClient): """Test to check works correctly exporting csv without any information, export a placeholder device. """ - csv_str, _ = user.get(res=documents.DocumentDef.t, - accept='text/csv', - item='devices/') + csv_str, _ = user.get( + res=documents.DocumentDef.t, accept='text/csv', item='devices/' + ) f = StringIO(csv_str) obj_csv = csv.reader(f, f) export_csv = list(obj_csv) @@ -330,16 +401,19 @@ def test_export_empty(user: UserClient): def test_export_computer_monitor(user: UserClient): """Test a export device type computer monitor.""" snapshot, _ = user.post(file('computer-monitor.snapshot'), res=Snapshot) - csv_str, _ = user.get(res=documents.DocumentDef.t, - item='devices/', - accept='text/csv', - query=[('filter', {'type': ['ComputerMonitor']})]) + csv_str, _ = user.get( + res=documents.DocumentDef.t, + item='devices/', + accept='text/csv', + query=[('filter', {'type': ['ComputerMonitor']})], + ) f = StringIO(csv_str) obj_csv = csv.reader(f, f) export_csv = list(obj_csv) # Open fixture csv and transform to list - with Path(__file__).parent.joinpath('files').joinpath('computer-monitor.csv').open() \ - as csv_file: + with Path(__file__).parent.joinpath('files').joinpath( + 'computer-monitor.csv' + ).open() as csv_file: obj_csv = csv.reader(csv_file) fixture_csv = list(obj_csv) @@ -355,15 +429,19 @@ def test_export_computer_monitor(user: UserClient): def test_export_keyboard(user: UserClient): """Test a export device type keyboard.""" snapshot, _ = user.post(file('keyboard.snapshot'), res=Snapshot) - csv_str, _ = user.get(res=documents.DocumentDef.t, - item='devices/', - accept='text/csv', - query=[('filter', {'type': ['Keyboard']})]) + csv_str, _ = user.get( + res=documents.DocumentDef.t, + item='devices/', + accept='text/csv', + query=[('filter', {'type': ['Keyboard']})], + ) f = StringIO(csv_str) obj_csv = csv.reader(f, f) export_csv = list(obj_csv) # Open fixture csv and transform to list - with Path(__file__).parent.joinpath('files').joinpath('keyboard.csv').open() as csv_file: + with Path(__file__).parent.joinpath('files').joinpath( + 'keyboard.csv' + ).open() as csv_file: obj_csv = csv.reader(csv_file) fixture_csv = list(obj_csv) @@ -381,8 +459,9 @@ def test_export_multiple_different_devices(user: UserClient): computers, keyboards, monitors, etc..) """ # Open fixture csv and transform to list - with Path(__file__).parent.joinpath('files').joinpath('multiples_devices.csv').open() \ - as csv_file: + with Path(__file__).parent.joinpath('files').joinpath( + 'multiples_devices.csv' + ).open() as csv_file: fixture_csv = list(csv.reader(csv_file)) for row in fixture_csv: del row[8] # We remove the 'Registered in' column @@ -393,10 +472,12 @@ def test_export_multiple_different_devices(user: UserClient): snapshot_keyboard, _ = user.post(file('keyboard.snapshot'), res=Snapshot) snapshot_monitor, _ = user.post(file('computer-monitor.snapshot'), res=Snapshot) - csv_str, _ = user.get(res=documents.DocumentDef.t, - item='devices/', - query=[('filter', {'type': ['Computer', 'Keyboard', 'Monitor']})], - accept='text/csv') + csv_str, _ = user.get( + res=documents.DocumentDef.t, + item='devices/', + query=[('filter', {'type': ['Computer', 'Keyboard', 'Monitor']})], + accept='text/csv', + ) f = StringIO(csv_str) obj_csv = csv.reader(f, f) export_csv = list(obj_csv) @@ -409,20 +490,25 @@ def test_export_multiple_different_devices(user: UserClient): @pytest.mark.mvp def test_report_devices_stock_control(user: UserClient, user2: UserClient): + # TODO """Test export device information in a csv file.""" snapshot, _ = user.post(file('basic.snapshot'), res=Snapshot) snapshot2, _ = user2.post(file('basic.snapshot2'), res=Snapshot) - csv_str, _ = user.get(res=documents.DocumentDef.t, - item='stock/', - accept='text/csv', - query=[('filter', {'type': ['Computer']})]) + csv_str, _ = user.get( + res=documents.DocumentDef.t, + item='stock/', + accept='text/csv', + query=[('filter', {'type': ['Computer']})], + ) f = StringIO(csv_str) obj_csv = csv.reader(f, f) export_csv = list(obj_csv) # Open fixture csv and transform to list - with Path(__file__).parent.joinpath('files').joinpath('basic-stock.csv').open() as csv_file: + with Path(__file__).parent.joinpath('files').joinpath( + 'basic-stock.csv' + ).open() as csv_file: obj_csv = csv.reader(csv_file) fixture_csv = list(obj_csv) @@ -431,8 +517,12 @@ def test_report_devices_stock_control(user: UserClient, user2: UserClient): export_csv[0] = export_csv[0][0].split(';') export_csv[1] = export_csv[1][0].split(';') - assert isinstance(datetime.strptime(export_csv[1][5], '%c'), datetime), \ - 'Register in field is not a datetime' + fixture_csv[0] = fixture_csv[0][0].split(';') + fixture_csv[1] = fixture_csv[1][0].split(';') + + assert isinstance( + datetime.strptime(export_csv[1][5], '%c'), datetime + ), 'Register in field is not a datetime' # Pop dates fields from csv lists to compare them fixture_csv[1] = fixture_csv[1][:5] + fixture_csv[1][6:] @@ -447,17 +537,22 @@ def test_report_devices_stock_control(user: UserClient, user2: UserClient): def test_get_document_lots(user: UserClient, user2: UserClient): """Tests submitting and retreiving all lots.""" - l, _ = user.post({'name': 'Lot1', 'description': 'comments,lot1,testcomment-lot1,'}, res=Lot) - l, _ = user.post({'name': 'Lot2', 'description': 'comments,lot2,testcomment-lot2,'}, res=Lot) - l, _ = user2.post({'name': 'Lot3-User2', 'description': 'comments,lot3,testcomment-lot3,'}, res=Lot) + l, _ = user.post( + {'name': 'Lot1', 'description': 'comments,lot1,testcomment-lot1,'}, res=Lot + ) + l, _ = user.post( + {'name': 'Lot2', 'description': 'comments,lot2,testcomment-lot2,'}, res=Lot + ) + l, _ = user2.post( + {'name': 'Lot3-User2', 'description': 'comments,lot3,testcomment-lot3,'}, + res=Lot, + ) - csv_str, _ = user.get(res=documents.DocumentDef.t, - item='lots/', - accept='text/csv') + csv_str, _ = user.get(res=documents.DocumentDef.t, item='lots/', accept='text/csv') - csv2_str, _ = user2.get(res=documents.DocumentDef.t, - item='lots/', - accept='text/csv') + csv2_str, _ = user2.get( + res=documents.DocumentDef.t, item='lots/', accept='text/csv' + ) f = StringIO(csv_str) obj_csv = csv.reader(f, f) @@ -470,10 +565,17 @@ def test_get_document_lots(user: UserClient, user2: UserClient): assert len(export_csv) == 3 assert len(export2_csv) == 2 - assert export_csv[0] == export2_csv[0] == ['Id', 'Name', 'Registered in', 'Description'] + assert ( + export_csv[0] + == export2_csv[0] + == ['Id', 'Name', 'Registered in', 'Description'] + ) assert export_csv[1][1] == 'Lot1' or 'Lot2' - assert export_csv[1][3] == 'comments,lot1,testcomment-lot1,' or 'comments,lot2,testcomment-lot2,' + assert ( + export_csv[1][3] == 'comments,lot1,testcomment-lot1,' + or 'comments,lot2,testcomment-lot2,' + ) assert export2_csv[1][1] == 'Lot3-User2' assert export2_csv[1][3] == 'comments,lot3,testcomment-lot3,' @@ -482,34 +584,39 @@ def test_get_document_lots(user: UserClient, user2: UserClient): def test_verify_stamp(user: UserClient, client: Client): """Test verify stamp of one export device information in a csv file.""" snapshot, _ = user.post(file('basic.snapshot'), res=Snapshot) - csv_str, _ = user.get(res=documents.DocumentDef.t, - item='devices/', - accept='text/csv', - query=[('filter', {'type': ['Computer']})]) + csv_str, _ = user.get( + res=documents.DocumentDef.t, + item='devices/', + accept='text/csv', + query=[('filter', {'type': ['Computer']})], + ) - response, _ = client.post(res=documents.DocumentDef.t, - item='stamps/', - content_type='multipart/form-data', - accept='text/html', - data={'docUpload': [(BytesIO(bytes(csv_str, 'utf-8')), 'example.csv')]}, - status=200) + response, _ = client.post( + res=documents.DocumentDef.t, + item='stamps/', + content_type='multipart/form-data', + accept='text/html', + data={'docUpload': [(BytesIO(bytes(csv_str, 'utf-8')), 'example.csv')]}, + status=200, + ) assert "alert alert-info" in response assert not "alert alert-danger" in response - response, _ = client.post(res=documents.DocumentDef.t, - item='stamps/', - content_type='multipart/form-data', - accept='text/html', - data={'docUpload': [(BytesIO(b'abc'), 'example.csv')]}, - status=200) + response, _ = client.post( + res=documents.DocumentDef.t, + item='stamps/', + content_type='multipart/form-data', + accept='text/html', + data={'docUpload': [(BytesIO(b'abc'), 'example.csv')]}, + status=200, + ) assert not "alert alert-info" in response assert "alert alert-danger" in response - response, _ = client.get(res=documents.DocumentDef.t, - item='stamps/', - accept='text/html', - status=200) + response, _ = client.get( + res=documents.DocumentDef.t, item='stamps/', accept='text/html', status=200 + ) assert not "alert alert-info" in response assert not "alert alert-danger" in response @@ -519,20 +626,23 @@ def test_verify_stamp(user: UserClient, client: Client): def test_verify_stamp_log_info(user: UserClient, client: Client): """Test verify stamp of one export lots-info in a csv file.""" - l, _ = user.post({'name': 'Lot1', 'description': 'comments,lot1,testcomment-lot1,'}, res=Lot) - l, _ = user.post({'name': 'Lot2', 'description': 'comments,lot2,testcomment-lot2,'}, res=Lot) + l, _ = user.post( + {'name': 'Lot1', 'description': 'comments,lot1,testcomment-lot1,'}, res=Lot + ) + l, _ = user.post( + {'name': 'Lot2', 'description': 'comments,lot2,testcomment-lot2,'}, res=Lot + ) - csv_str, _ = user.get(res=documents.DocumentDef.t, - item='lots/', - accept='text/csv') + csv_str, _ = user.get(res=documents.DocumentDef.t, item='lots/', accept='text/csv') - response, _ = client.post(res=documents.DocumentDef.t, - item='stamps/', - content_type='multipart/form-data', - accept='text/html', - data={'docUpload': [(BytesIO(bytes(csv_str, 'utf-8')), - 'example.csv')]}, - status=200) + response, _ = client.post( + res=documents.DocumentDef.t, + item='stamps/', + content_type='multipart/form-data', + accept='text/html', + data={'docUpload': [(BytesIO(bytes(csv_str, 'utf-8')), 'example.csv')]}, + status=200, + ) assert "alert alert-info" in response @@ -542,18 +652,21 @@ def test_verify_stamp_devices_stock(user: UserClient, client: Client): snapshot, _ = user.post(file('basic.snapshot'), res=Snapshot) - csv_str, _ = user.get(res=documents.DocumentDef.t, - item='stock/', - accept='text/csv', - query=[('filter', {'type': ['Computer']})]) + csv_str, _ = user.get( + res=documents.DocumentDef.t, + item='stock/', + accept='text/csv', + query=[('filter', {'type': ['Computer']})], + ) - response, _ = client.post(res=documents.DocumentDef.t, - item='stamps/', - content_type='multipart/form-data', - accept='text/html', - data={'docUpload': [(BytesIO(bytes(csv_str, 'utf-8')), - 'example.csv')]}, - status=200) + response, _ = client.post( + res=documents.DocumentDef.t, + item='stamps/', + content_type='multipart/form-data', + accept='text/html', + data={'docUpload': [(BytesIO(bytes(csv_str, 'utf-8')), 'example.csv')]}, + status=200, + ) assert "alert alert-info" in response @@ -563,11 +676,15 @@ def test_verify_stamp_csv_actions(user: UserClient, client: Client): acer = yaml2json('acer.happy.battery.snapshot') snapshot, _ = user.post(json_encode(acer), res=Snapshot) device_id = snapshot['device']['id'] - post_request = {"transaction": "ccc", "name": "John", "endUsers": 1, - "devices": [device_id], "description": "aaa", - "finalUserCode": "abcdefjhi", - "startTime": "2020-11-01T02:00:00+00:00", - "endTime": "2020-12-01T02:00:00+00:00" + post_request = { + "transaction": "ccc", + "name": "John", + "endUsers": 1, + "devices": [device_id], + "description": "aaa", + "finalUserCode": "abcdefjhi", + "startTime": "2020-11-01T02:00:00+00:00", + "endTime": "2020-12-01T02:00:00+00:00", } user.post(res=Allocate, data=post_request) @@ -578,18 +695,21 @@ def test_verify_stamp_csv_actions(user: UserClient, client: Client): acer['licence_version'] = '1.0.0' snapshot, _ = client.post(acer, res=Live) - csv_str, _ = user.get(res=documents.DocumentDef.t, - item='actions/', - accept='text/csv', - query=[('filter', {'type': ['Computer']})]) + csv_str, _ = user.get( + res=documents.DocumentDef.t, + item='actions/', + accept='text/csv', + query=[('filter', {'type': ['Computer']})], + ) - response, _ = client.post(res=documents.DocumentDef.t, - item='stamps/', - content_type='multipart/form-data', - accept='text/html', - data={'docUpload': [(BytesIO(bytes(csv_str, 'utf-8')), - 'example.csv')]}, - status=200) + response, _ = client.post( + res=documents.DocumentDef.t, + item='stamps/', + content_type='multipart/form-data', + accept='text/html', + data={'docUpload': [(BytesIO(bytes(csv_str, 'utf-8')), 'example.csv')]}, + status=200, + ) assert "alert alert-info" in response @@ -599,35 +719,38 @@ def test_verify_stamp_erasure_certificate(user: UserClient, client: Client): s = file('erase-sectors.snapshot') snapshot, response = user.post(s, res=Snapshot) - doc, _ = user.get(res=documents.DocumentDef.t, - item='erasures/', - query=[('filter', {'ids': [snapshot['device']['id']]})], - accept=ANY) + doc, _ = user.get( + res=documents.DocumentDef.t, + item='erasures/', + query=[('filter', {'ids': [snapshot['device']['id']]})], + accept=ANY, + ) - response, _ = client.post(res=documents.DocumentDef.t, - item='stamps/', - content_type='multipart/form-data', - accept='text/html', - data={'docUpload': [(BytesIO(bytes(doc, 'utf-8')), - 'example.csv')]}, - status=200) + response, _ = client.post( + res=documents.DocumentDef.t, + item='stamps/', + content_type='multipart/form-data', + accept='text/html', + data={'docUpload': [(BytesIO(bytes(doc, 'utf-8')), 'example.csv')]}, + status=200, + ) assert "alert alert-danger" in response - doc, _ = user.get(res=documents.DocumentDef.t, - item='erasures/', - query=[ - ('filter', {'ids': [snapshot['device']['id']]}), - ('format', 'PDF') - ], - accept='application/pdf') + doc, _ = user.get( + res=documents.DocumentDef.t, + item='erasures/', + query=[('filter', {'ids': [snapshot['device']['id']]}), ('format', 'PDF')], + accept='application/pdf', + ) - response, _ = client.post(res=documents.DocumentDef.t, - item='stamps/', - content_type='multipart/form-data', - accept='text/html', - data={'docUpload': [(BytesIO(doc), - 'example.csv')]}, - status=200) + response, _ = client.post( + res=documents.DocumentDef.t, + item='stamps/', + content_type='multipart/form-data', + accept='text/html', + data={'docUpload': [(BytesIO(doc), 'example.csv')]}, + status=200, + ) assert "alert alert-info" in response @@ -636,11 +759,10 @@ def test_get_document_internal_stats(user: UserClient, user2: UserClient): """Tests for get the internal stats.""" # csv_str, _ = user.get(res=documents.DocumentDef.t, - # item='internalstats/') - csv_str, _ = user.get(res=documents.DocumentDef.t, - item='internalstats/', - accept='text/csv', - query=[]) + # item='internalstats/') + csv_str, _ = user.get( + res=documents.DocumentDef.t, item='internalstats/', accept='text/csv', query=[] + ) f = StringIO(csv_str) obj_csv = csv.reader(f, f) @@ -648,10 +770,9 @@ def test_get_document_internal_stats(user: UserClient, user2: UserClient): assert len(export_csv) == 1 - csv_str, _ = user2.get(res=documents.DocumentDef.t, - item='internalstats/', - accept='text/csv', - query=[]) + csv_str, _ = user2.get( + res=documents.DocumentDef.t, item='internalstats/', accept='text/csv', query=[] + ) f = StringIO(csv_str) obj_csv = csv.reader(f, f) @@ -672,8 +793,9 @@ def test_get_wbconf(user: UserClient): assert 'WB_ERASE =' in env # assert 'WB_ERASE = True' in env - session = Session.query.filter_by(user_id=user.user['id'], - type=SessionType.Internal).first() + session = Session.query.filter_by( + user_id=user.user['id'], type=SessionType.Internal + ).first() token = session.token token = auth.Auth.encode(session.token) assert token in env @@ -691,7 +813,7 @@ def test_trade_documents(user: UserClient): 'filename': 'test.pdf', 'hash': 'bbbbbbbb', 'url': 'http://www.ereuse.org/', - 'lot': lot['id'] + 'lot': lot['id'], } doc, _ = user.post(res=TradeDocument, data=request_post) assert doc['filename'] == request_post['filename'] @@ -707,13 +829,15 @@ def test_trade_documents_with_weight(user: UserClient): lot, _ = user.post({'name': 'MyLot'}, res=Lot) # long url - url = 'http://www.ereuse.org/apapaapaapaapaapaapaapaapaapaapapaapaapaapaapaapaapaapaapaapapaapaapaapaapaapaapaapaapaapaaaa', + url = ( + 'http://www.ereuse.org/apapaapaapaapaapaapaapaapaapaapapaapaapaapaapaapaapaapaapaapapaapaapaapaapaapaapaapaapaapaaaa', + ) request_post = { 'filename': 'test.pdf', 'hash': 'bbbbbbbb', 'url': url, 'weight': 15, - 'lot': lot['id'] + 'lot': lot['id'], } doc, _ = user.post(res=TradeDocument, data=request_post) assert doc['weight'] == request_post['weight'] diff --git a/tests/test_render_2_0.py b/tests/test_render_2_0.py index 9a52b89f..62e7d4b5 100644 --- a/tests/test_render_2_0.py +++ b/tests/test_render_2_0.py @@ -224,11 +224,12 @@ def test_export_devices(user3: UserClientFlask): ), 'Computer information are not equal' assert fixture_csv[1][20] == export_csv[1][20], 'Computer information are not equal' assert ( - fixture_csv[1][22:82] == export_csv[1][22:82] + fixture_csv[1][22:83] == export_csv[1][22:83] ), 'Computer information are not equal' - assert fixture_csv[1][83] == export_csv[1][83], 'Computer information are not equal' + + assert fixture_csv[1][84] == export_csv[1][84], 'Computer information are not equal' assert ( - fixture_csv[1][86:] == export_csv[1][86:] + fixture_csv[1][88:] == export_csv[1][88:] ), 'Computer information are not equal' From f7b6bdccb623bc51ffc20bbd63c484334a7cecc1 Mon Sep 17 00:00:00 2001 From: Cayo Puigdefabregas Date: Mon, 16 May 2022 10:15:46 +0200 Subject: [PATCH 43/51] fix test_export_extended --- tests/test_documents.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_documents.py b/tests/test_documents.py index 538e2620..c00ee5e0 100644 --- a/tests/test_documents.py +++ b/tests/test_documents.py @@ -366,7 +366,7 @@ def test_export_extended(app: Devicehub, user: UserClient): ), 'Computer information are not equal' assert fixture_csv[2][84] == export_csv[2][84], 'Computer information are not equal' assert ( - fixture_csv[2][86:107] == export_csv[2][86:107] + fixture_csv[2][87:107] == export_csv[2][87:107] ), 'Computer information are not equal' assert ( fixture_csv[2][110] == export_csv[2][110] From 0b78178ff486c3fb9392f209390d0cc0b8acc30d Mon Sep 17 00:00:00 2001 From: Cayo Puigdefabregas Date: Mon, 16 May 2022 10:29:07 +0200 Subject: [PATCH 44/51] fixed device public page --- .../resources/device/templates/devices/layout.html | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/ereuse_devicehub/resources/device/templates/devices/layout.html b/ereuse_devicehub/resources/device/templates/devices/layout.html index 716d3159..e4474426 100644 --- a/ereuse_devicehub/resources/device/templates/devices/layout.html +++ b/ereuse_devicehub/resources/device/templates/devices/layout.html @@ -193,7 +193,10 @@ {% for action in device.public_actions %}
  • - {{ device.is_status(action) }} {{ action.type }} + {{ device.is_status(action) }} + {% if not device.is_status(action) %} + {{ action.type }} + {% endif %} — {% if device.is_status(action) %} From 3bd2dd79b1dab23d421400d774d6d6117beb6416 Mon Sep 17 00:00:00 2001 From: Cayo Puigdefabregas Date: Mon, 16 May 2022 11:01:52 +0200 Subject: [PATCH 45/51] remove all components in the filter of the inventory --- ereuse_devicehub/inventory/forms.py | 54 +---------------------------- 1 file changed, 1 insertion(+), 53 deletions(-) diff --git a/ereuse_devicehub/inventory/forms.py b/ereuse_devicehub/inventory/forms.py index bc26f600..72c8de86 100644 --- a/ereuse_devicehub/inventory/forms.py +++ b/ereuse_devicehub/inventory/forms.py @@ -52,7 +52,7 @@ from ereuse_devicehub.resources.user.exceptions import InsufficientPermission from ereuse_devicehub.resources.user.models import User DEVICES = { - "All": ["All Devices", "All Components"], + "All": ["All Devices"], "Computer": [ "All Computers", "Desktop", @@ -73,55 +73,12 @@ DEVICES = { "Smartphone", "Cellphone", ], - "DataStorage": ["All DataStorage", "HardDrive", "SolidStateDrive"], - "Accessories & Peripherals": [ - "All Peripherals", - "GraphicCard", - "Motherboard", - "NetworkAdapter", - "Processor", - "RamModule", - "SoundCard", - "Battery", - "Keyboard", - "Mouse", - "MemoryCardReader", - ], } COMPUTERS = ['Desktop', 'Laptop', 'Server'] -COMPONENTS = [ - 'GraphicCard', - 'DataStorage', - 'HardDrive', - 'DataStorage', - 'SolidStateDrive', - 'Motherboard', - 'NetworkAdapter', - 'Processor', - 'RamModule', - 'SoundCard', - 'Display', - 'Battery', - 'Camera', -] - MONITORS = ["ComputerMonitor", "Monitor", "TelevisionSet", "Projector"] MOBILE = ["Mobile", "Tablet", "Smartphone", "Cellphone"] -DATASTORAGE = ["HardDrive", "SolidStateDrive"] -PERIPHERALS = [ - "GraphicCard", - "Motherboard", - "NetworkAdapter", - "Processor", - "RamModule", - "SoundCard", - "Battery", - "Keyboard", - "Mouse", - "MemoryCardReader", -] class FilterForm(FlaskForm): @@ -166,9 +123,6 @@ class FilterForm(FlaskForm): if "All Devices" == self.device_type: filter_type = COMPUTERS + ["Monitor"] + MOBILE - elif "All Components" == self.device_type: - filter_type = COMPONENTS - elif "All Computers" == self.device_type: filter_type = COMPUTERS @@ -178,12 +132,6 @@ class FilterForm(FlaskForm): elif "All Mobile" == self.device_type: filter_type = MOBILE - elif "All DataStorage" == self.device_type: - filter_type = DATASTORAGE - - elif "All Peripherals" == self.device_type: - filter_type = PERIPHERALS - if filter_type: self.devices = self.devices.filter(Device.type.in_(filter_type)) From 727c06c3de7baba71143b99a88769a19779c2c68 Mon Sep 17 00:00:00 2001 From: Cayo Puigdefabregas Date: Mon, 16 May 2022 11:54:02 +0200 Subject: [PATCH 46/51] add public link to device list --- ereuse_devicehub/templates/inventory/device_list.html | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/ereuse_devicehub/templates/inventory/device_list.html b/ereuse_devicehub/templates/inventory/device_list.html index f6854da5..59050e5a 100644 --- a/ereuse_devicehub/templates/inventory/device_list.html +++ b/ereuse_devicehub/templates/inventory/device_list.html @@ -342,6 +342,7 @@ Allocated Status Physical Status Update + @@ -357,10 +358,10 @@ /> + {% if dev.get_type_logo() %} {% endif %} - {{ dev.verbose_name }} {% if dev.lots | length > 0 %} @@ -386,6 +387,11 @@ {% if dev.allocated_status %}{{ dev.allocated_status.type }}{% endif %} {% if dev.physical_status %}{{ dev.physical_status.type }}{% endif %} {{ dev.updated.strftime('%H:%M %d-%m-%Y') }} + + + + + {% endfor %} From d935c987b3d089bd596a75309345d1714ca49e99 Mon Sep 17 00:00:00 2001 From: Cayo Puigdefabregas Date: Mon, 16 May 2022 12:40:14 +0200 Subject: [PATCH 47/51] comiter monitor instead of monitor --- ereuse_devicehub/inventory/forms.py | 10 +++++----- ereuse_devicehub/static/js/create_device.js | 2 +- .../templates/inventory/device_create.html | 6 +++--- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/ereuse_devicehub/inventory/forms.py b/ereuse_devicehub/inventory/forms.py index 72c8de86..4f68c899 100644 --- a/ereuse_devicehub/inventory/forms.py +++ b/ereuse_devicehub/inventory/forms.py @@ -33,10 +33,10 @@ from ereuse_devicehub.resources.device.models import ( SAI, Cellphone, Computer, + ComputerMonitor, Device, Keyboard, MemoryCardReader, - Monitor, Mouse, Smartphone, Tablet, @@ -121,7 +121,7 @@ class FilterForm(FlaskForm): # Generic Filters if "All Devices" == self.device_type: - filter_type = COMPUTERS + ["Monitor"] + MOBILE + filter_type = COMPUTERS + MONITORS + MOBILE elif "All Computers" == self.device_type: filter_type = COMPUTERS @@ -334,7 +334,7 @@ class NewDeviceForm(FlaskForm): "Smartphone": Smartphone, "Tablet": Tablet, "Cellphone": Cellphone, - "Monitor": Monitor, + "ComputerMonitor": ComputerMonitor, "Mouse": Mouse, "Keyboard": Keyboard, "SAI": SAI, @@ -450,7 +450,7 @@ class NewDeviceForm(FlaskForm): path_snapshot = save_json(json_snapshot, self.tmp_snapshots, g.user.email) snapshot_json = schema.load(json_snapshot) - if self.type.data == 'Monitor': + if self.type.data == 'ComputerMonitor': snapshot_json['device'].resolution_width = self.resolution.data snapshot_json['device'].size = self.screen.data @@ -461,7 +461,7 @@ class NewDeviceForm(FlaskForm): snapshot = upload_form.build(snapshot_json) move_json(self.tmp_snapshots, path_snapshot, g.user.email) - if self.type.data == 'Monitor': + if self.type.data == 'ComputerMonitor': snapshot.device.resolution = self.resolution.data snapshot.device.screen = self.screen.data diff --git a/ereuse_devicehub/static/js/create_device.js b/ereuse_devicehub/static/js/create_device.js index a1b609b0..53d77dfa 100644 --- a/ereuse_devicehub/static/js/create_device.js +++ b/ereuse_devicehub/static/js/create_device.js @@ -4,7 +4,7 @@ $(document).ready(() => { }) function deviceInputs() { - if ($("#type").val() == "Monitor") { + if ($("#type").val() == "ComputerMonitor") { $("#screen").show(); $("#resolution").show(); $("#imei").hide(); diff --git a/ereuse_devicehub/templates/inventory/device_create.html b/ereuse_devicehub/templates/inventory/device_create.html index e3fa1cd6..1ad9a083 100644 --- a/ereuse_devicehub/templates/inventory/device_create.html +++ b/ereuse_devicehub/templates/inventory/device_create.html @@ -36,9 +36,9 @@