diff --git a/ereuse_devicehub/inventory/views.py b/ereuse_devicehub/inventory/views.py index 6a0f56a0..e43f764a 100644 --- a/ereuse_devicehub/inventory/views.py +++ b/ereuse_devicehub/inventory/views.py @@ -1,7 +1,12 @@ +import csv +from io import StringIO + import flask -from flask import Blueprint, g, request, url_for +import flask_weasyprint +from flask import Blueprint, g, make_response, request, url_for from flask.views import View from flask_login import current_user, login_required +from werkzeug.exceptions import NotFound from ereuse_devicehub import messages from ereuse_devicehub.inventory.forms import ( @@ -18,7 +23,10 @@ from ereuse_devicehub.inventory.forms import ( TradeForm, UploadSnapshotForm, ) -from ereuse_devicehub.resources.device.models import Device +from ereuse_devicehub.resources.action.models import Trade +from ereuse_devicehub.resources.device.models import Computer, DataStorage, Device +from ereuse_devicehub.resources.documents.device_row import ActionRow, DeviceRow +from ereuse_devicehub.resources.hash_reports import insert_hash from ereuse_devicehub.resources.lot.models import Lot from ereuse_devicehub.resources.tag.model import Tag @@ -38,7 +46,7 @@ class DeviceListMix(View): lot = None tags = ( Tag.query.filter(Tag.owner_id == current_user.id) - .filter(Tag.device_id == None) + .filter(Tag.device_id.is_(None)) .order_by(Tag.created.desc()) ) @@ -58,7 +66,7 @@ class DeviceListMix(View): devices = ( Device.query.filter(Device.owner_id == current_user.id) .filter(Device.type.in_(filter_types)) - .filter(Device.lots == None) + .filter_by(lots=None) .order_by(Device.updated.desc()) ) form_new_action = NewActionForm() @@ -330,7 +338,7 @@ class NewActionView(View): self.form = self.form_class() if self.form.validate_on_submit(): - instance = self.form.save() + self.form.save() messages.success( 'Action "{}" created successfully!'.format(self.form.type.data) ) @@ -355,7 +363,7 @@ class NewAllocateView(NewActionView, DeviceListMix): self.form = self.form_class() if self.form.validate_on_submit(): - instance = self.form.save() + self.form.save() messages.success( 'Action "{}" created successfully!'.format(self.form.type.data) ) @@ -377,7 +385,7 @@ class NewDataWipeView(NewActionView, DeviceListMix): self.form = self.form_class() if self.form.validate_on_submit(): - instance = self.form.save() + self.form.save() messages.success( 'Action "{}" created successfully!'.format(self.form.type.data) ) @@ -399,7 +407,7 @@ class NewTradeView(NewActionView, DeviceListMix): self.form = self.form_class() if self.form.validate_on_submit(): - instance = self.form.save() + self.form.save() messages.success( 'Action "{}" created successfully!'.format(self.form.type.data) ) @@ -434,6 +442,131 @@ class NewTradeDocumentView(View): ) +class ExportsView(View): + methods = ['GET'] + decorators = [login_required] + + def dispatch_request(self, export_id): + export_ids = { + 'metrics': self.metrics, + 'devices': self.devices_list, + 'certificates': self.erasure, + 'links': self.public_links, + } + + if export_id not in export_ids: + return NotFound() + return export_ids[export_id]() + + def find_devices(self): + args = request.args.get('ids') + ids = args.split(',') if args else [] + query = Device.query.filter(Device.owner == g.user) + return query.filter(Device.devicehub_id.in_(ids)) + + def response_csv(self, data, name): + bfile = data.getvalue().encode('utf-8') + # insert proof + insert_hash(bfile) + output = make_response(bfile) + output.headers['Content-Disposition'] = 'attachment; filename={}'.format(name) + output.headers['Content-type'] = 'text/csv' + return output + + def devices_list(self): + """Get device query and put information in csv format.""" + data = StringIO() + cw = csv.writer(data, delimiter=';', lineterminator="\n", quotechar='"') + first = True + + for device in self.find_devices(): + d = DeviceRow(device, {}) + if first: + cw.writerow(d.keys()) + first = False + cw.writerow(d.values()) + + return self.response_csv(data, "export.csv") + + def metrics(self): + """Get device query and put information in csv format.""" + data = StringIO() + cw = csv.writer(data, delimiter=';', lineterminator="\n", quotechar='"') + first = True + devs_id = [] + # Get the allocate info + for device in self.find_devices(): + devs_id.append(device.id) + for allocate in device.get_metrics(): + d = ActionRow(allocate) + if first: + cw.writerow(d.keys()) + first = False + cw.writerow(d.values()) + + # Get the trade info + query_trade = Trade.query.filter( + Trade.devices.any(Device.id.in_(devs_id)) + ).all() + + lot_id = request.args.get('lot') + if lot_id and not query_trade: + lot = Lot.query.filter_by(id=lot_id).one() + if hasattr(lot, "trade") and lot.trade: + if g.user in [lot.trade.user_from, lot.trade.user_to]: + query_trade = [lot.trade] + + for trade in query_trade: + data_rows = trade.get_metrics() + for row in data_rows: + d = ActionRow(row) + if first: + cw.writerow(d.keys()) + first = False + cw.writerow(d.values()) + + return self.response_csv(data, "actions_export.csv") + + def public_links(self): + # get a csv with the publink links of this devices + data = StringIO() + cw = csv.writer(data, delimiter=';', lineterminator="\n", quotechar='"') + cw.writerow(['links']) + host_url = request.host_url + for dev in self.find_devices(): + code = dev.devicehub_id + link = [f"{host_url}devices/{code}"] + cw.writerow(link) + + return self.response_csv(data, "links.csv") + + def erasure(self): + template = self.build_erasure_certificate() + res = flask_weasyprint.render_pdf( + flask_weasyprint.HTML(string=template), + download_filename='erasure-certificate.pdf', + ) + insert_hash(res.data) + return res + + def build_erasure_certificate(self): + erasures = [] + for device in self.find_devices(): + if isinstance(device, Computer): + for privacy in device.privacy: + erasures.append(privacy) + elif isinstance(device, DataStorage): + if device.privacy: + erasures.append(device.privacy) + + params = { + 'title': 'Erasure Certificate', + 'erasures': tuple(erasures), + 'url_pdf': '', + } + return flask.render_template('inventory/erasure.html', **params) + + devices.add_url_rule('/action/add/', view_func=NewActionView.as_view('action_add')) devices.add_url_rule('/action/trade/add/', view_func=NewTradeView.as_view('trade_add')) devices.add_url_rule( @@ -483,3 +616,6 @@ devices.add_url_rule( '/tag/devices//del/', view_func=TagUnlinkDeviceView.as_view('tag_devices_del'), ) +devices.add_url_rule( + '/export//', view_func=ExportsView.as_view('export') +) diff --git a/ereuse_devicehub/static/js/main_inventory.js b/ereuse_devicehub/static/js/main_inventory.js index 1147241d..d790af81 100644 --- a/ereuse_devicehub/static/js/main_inventory.js +++ b/ereuse_devicehub/static/js/main_inventory.js @@ -148,3 +148,14 @@ function get_device_list() { description = $.map(list_devices, function(x) { return x }).join(", "); $(".enumeration-devices").html(description); } + +function export_file(type_file) { + var devices = $(".deviceSelect").filter(':checked'); + var devices_id = $.map(devices, function(x) { return $(x).attr('data-device-dhid')}).join(","); + if (devices_id){ + var url = "/inventory/export/"+type_file+"/?ids="+devices_id; + window.location.href = url; + } else { + $("#exportAlertModal").click(); + } +} diff --git a/ereuse_devicehub/templates/inventory/alert_export_error.html b/ereuse_devicehub/templates/inventory/alert_export_error.html new file mode 100644 index 00000000..8212a364 --- /dev/null +++ b/ereuse_devicehub/templates/inventory/alert_export_error.html @@ -0,0 +1,21 @@ + diff --git a/ereuse_devicehub/templates/inventory/device_list.html b/ereuse_devicehub/templates/inventory/device_list.html index 9f8af548..63395f5a 100644 --- a/ereuse_devicehub/templates/inventory/device_list.html +++ b/ereuse_devicehub/templates/inventory/device_list.html @@ -205,10 +205,30 @@ Exports + @@ -353,6 +373,7 @@ {% include "inventory/allocate.html" %} {% include "inventory/data_wipe.html" %} {% include "inventory/trade.html" %} +{% include "inventory/alert_export_error.html" %} diff --git a/ereuse_devicehub/templates/inventory/erasure.html b/ereuse_devicehub/templates/inventory/erasure.html new file mode 100644 index 00000000..5a16390c --- /dev/null +++ b/ereuse_devicehub/templates/inventory/erasure.html @@ -0,0 +1,92 @@ +{% extends "documents/layout.html" %} +{% block body %} +
+

Summary

+ + + + + + + + + + + {% for erasure in erasures %} + + + + + + + {% endfor %} + +
S/N Data StorageType of erasureResultDate
+ {{ erasure.device.serial_number.upper() }} + + {{ erasure.type }} + + {{ erasure.severity }} + + {{ erasure.date_str }} +
+
+
+

Details

+ {% for erasure in erasures %} +
+

{{ erasure.device.__format__('t') }}

+
+
Data storage:
+
{{ erasure.device.__format__('ts') }}
+ +
Computer where was erase:
+
Title: {{ erasure.parent.__format__('ts') }}
+
DevicehubID: {{ erasure.parent.devicehub_id }}
+
Hid: {{ erasure.parent.hid }}
+
Tags: {{ erasure.parent.tags }}
+ +
Computer where it resides:
+
Title: {{ erasure.device.parent.__format__('ts') }}
+
DevicehubID: {{ erasure.device.parent.devicehub_id }}
+
Hid: {{ erasure.device.parent.hid }}
+
Tags: {{ erasure.device.parent.tags }}
+ +
Erasure:
+
{{ erasure.__format__('ts') }}
+ {% if erasure.steps %} +
Erasure steps:
+
+
    + {% for step in erasure.steps %} +
  1. {{ step.__format__('') }}
  2. + {% endfor %} +
+
+ {% endif %} +
+
+ {% endfor %} +
+
+

Glossary

+
+
Erase Basic
+
+ A software-based fast non-100%-secured way of erasing data storage, + using shred. +
+
Erase Sectors
+
+ A secured-way of erasing data storages, checking sector-by-sector + the erasure, using badblocks. +
+
+
+ + +{% endblock %}