From 1515302d9839f96dd1374f4f803f77814512b0cd Mon Sep 17 00:00:00 2001 From: Cayo Puigdefabregas Date: Tue, 28 Mar 2023 17:09:47 +0200 Subject: [PATCH 1/8] allow remove a document --- ereuse_devicehub/inventory/forms.py | 12 +++++++ ereuse_devicehub/inventory/views.py | 25 +++++++++++++ ereuse_devicehub/resources/models.py | 32 +++++++++++------ .../templates/inventory/device_list.html | 36 +++++++++++++++++++ 4 files changed, 94 insertions(+), 11 deletions(-) diff --git a/ereuse_devicehub/inventory/forms.py b/ereuse_devicehub/inventory/forms.py index ffb8d13c..fc5fb5c6 100644 --- a/ereuse_devicehub/inventory/forms.py +++ b/ereuse_devicehub/inventory/forms.py @@ -1274,8 +1274,14 @@ class TradeDocumentForm(FlaskForm): def __init__(self, *args, **kwargs): lot_id = kwargs.pop('lot') + doc_id = kwargs.pop('document', None) super().__init__(*args, **kwargs) self._lot = Lot.query.filter(Lot.id == lot_id).one() + self.object = None + if doc_id: + self.object = TradeDocument.query.filter_by( + id=doc_id, lot=self._lot, owner=g.user + ).one() if not self._lot.transfer: self.form_errors = ['Error, this lot is not a transfer lot.'] @@ -1307,6 +1313,12 @@ class TradeDocumentForm(FlaskForm): return self._obj + def remove(self): + if self.object: + self.object.delete() + db.session.commit() + return self.object + class TransferForm(FlaskForm): lot_name = StringField( diff --git a/ereuse_devicehub/inventory/views.py b/ereuse_devicehub/inventory/views.py index 79145f3c..73a55cc1 100644 --- a/ereuse_devicehub/inventory/views.py +++ b/ereuse_devicehub/inventory/views.py @@ -547,6 +547,27 @@ class LotDeleteView(View): return flask.redirect(next_url) +class DocumentDeleteView(View): + methods = ['GET'] + decorators = [login_required] + template_name = 'inventory/device_list.html' + form_class = TradeDocumentForm + + def dispatch_request(self, lot_id, doc_id): + next_url = url_for('inventory.lotdevicelist', lot_id=lot_id) + form = self.form_class(lot=lot_id, document=doc_id) + try: + form.remove() + except Exception as err: + msg = "{}".format(err) + messages.error(msg) + return flask.redirect(next_url) + + msg = "Document removed successfully." + messages.success(msg) + return flask.redirect(next_url) + + class UploadSnapshotView(GenericMixin): methods = ['GET', 'POST'] decorators = [login_required] @@ -1515,6 +1536,10 @@ devices.add_url_rule( '/lot//trade-document/add/', view_func=NewTradeDocumentView.as_view('trade_document_add'), ) +devices.add_url_rule( + '/lot//document/del/', + view_func=DocumentDeleteView.as_view('document_del'), +) devices.add_url_rule('/device/', view_func=DeviceListView.as_view('devicelist')) devices.add_url_rule( '/all/device/', view_func=AllDeviceListView.as_view('alldevicelist') diff --git a/ereuse_devicehub/resources/models.py b/ereuse_devicehub/resources/models.py index e079269f..485fc96e 100644 --- a/ereuse_devicehub/resources/models.py +++ b/ereuse_devicehub/resources/models.py @@ -1,4 +1,5 @@ from datetime import datetime, timezone + from flask_sqlalchemy import event from ereuse_devicehub.db import db @@ -16,18 +17,23 @@ class Thing(db.Model): `schema.org's Thing class `_ using only needed fields. """ + __abstract__ = True - updated = db.Column(db.TIMESTAMP(timezone=True), - nullable=False, - index=True, - server_default=db.text('CURRENT_TIMESTAMP')) - updated.comment = """The last time Devicehub recorded a change for + updated = db.Column( + db.TIMESTAMP(timezone=True), + nullable=False, + index=True, + server_default=db.text('CURRENT_TIMESTAMP'), + ) + updated.comment = """The last time Devicehub recorded a change for this thing. """ - created = db.Column(db.TIMESTAMP(timezone=True), - nullable=False, - index=True, - server_default=db.text('CURRENT_TIMESTAMP')) + created = db.Column( + db.TIMESTAMP(timezone=True), + nullable=False, + index=True, + server_default=db.text('CURRENT_TIMESTAMP'), + ) created.comment = """When Devicehub created this.""" def __init__(self, **kwargs) -> None: @@ -36,11 +42,15 @@ class Thing(db.Model): self.created = kwargs.get('created', datetime.now(timezone.utc)) super().__init__(**kwargs) + def delete(self): + db.session.delete(self) + def update_object_timestamp(mapper, connection, thing_obj): - """ This function update the stamptime of field updated """ + """This function update the stamptime of field updated""" thing_obj.updated = datetime.now(timezone.utc) + def listener_reset_field_updated_in_actual_time(thing_obj): - """ This function launch a event than listen like a signal when some object is saved """ + """This function launch a event than listen like a signal when some object is saved""" event.listen(thing_obj, 'before_update', update_object_timestamp, propagate=True) diff --git a/ereuse_devicehub/templates/inventory/device_list.html b/ereuse_devicehub/templates/inventory/device_list.html index 2e2bc7b6..bcc0f22f 100644 --- a/ereuse_devicehub/templates/inventory/device_list.html +++ b/ereuse_devicehub/templates/inventory/device_list.html @@ -530,6 +530,7 @@ File Uploaded on + @@ -545,6 +546,38 @@ {{ doc.created.strftime('%Y-%m-%d %H:%M')}} + + + + + + {% endfor %} {% for doc in lot.trade.documents %} @@ -559,6 +592,9 @@ {{ doc.created.strftime('%Y-%m-%d %H:%M')}} + + + {% endfor %} From d1c332e89196ff3b84491184e1517b66a72f6a25 Mon Sep 17 00:00:00 2001 From: Cayo Puigdefabregas Date: Wed, 29 Mar 2023 10:43:30 +0200 Subject: [PATCH 2/8] add edit document in template --- ereuse_devicehub/templates/inventory/device_list.html | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/ereuse_devicehub/templates/inventory/device_list.html b/ereuse_devicehub/templates/inventory/device_list.html index bcc0f22f..58a7fc4f 100644 --- a/ereuse_devicehub/templates/inventory/device_list.html +++ b/ereuse_devicehub/templates/inventory/device_list.html @@ -547,7 +547,12 @@ {{ doc.created.strftime('%Y-%m-%d %H:%M')}} - + + + + + + +
+ + +
Documents
+ + + + + + + + + + {% for doc in placeholder.documents %} + + + + + + + {% endfor %} + +
FileUploaded on
+ {% if doc.get_url() %} + {{ doc.file_name}} + {% else %} + {{ doc.file_name}} + {% endif %} + + {{ doc.created.strftime('%Y-%m-%d %H:%M')}} + + + + + + + + + +
+
+
Status Details
diff --git a/ereuse_devicehub/templates/inventory/device_document.html b/ereuse_devicehub/templates/inventory/device_document.html new file mode 100644 index 00000000..1991444f --- /dev/null +++ b/ereuse_devicehub/templates/inventory/device_document.html @@ -0,0 +1,70 @@ +{% extends "ereuse_devicehub/base_site.html" %} +{% block main %} + +
+

{{ title }}

+ +
+ +
+
+
+ +
+
+ +
+
{{ title }}
+ {% if form.form_errors %} +

+ {% for error in form.form_errors %} + {{ error }}
+ {% endfor %} +

+ {% endif %} +
+ + {% if form._obj or 1==2 %} + + {% else %} + + {% endif %} + {{ form.csrf_token }} + {% for field in form %} + {% if field != form.csrf_token %} +
+ {{ field.label(class_="form-label") }} + {{ field }} + {{ field.description }} + {% if field.errors %} +

+ {% for error in field.errors %} + {{ error }}
+ {% endfor %} +

+ {% endif %} +
+ {% endif %} + {% endfor %} + +
+ Cancel + +
+ + +
+
+ +
+
+
+{% endblock main %} From 2f27095c8442f810acf6a67d3279ba50f39490d9 Mon Sep 17 00:00:00 2001 From: Cayo Puigdefabregas Date: Tue, 4 Apr 2023 16:56:27 +0200 Subject: [PATCH 7/8] documents in devices --- ereuse_devicehub/inventory/forms.py | 4 +- ereuse_devicehub/inventory/models.py | 11 +++ ereuse_devicehub/inventory/views.py | 72 +++++++++++------ .../templates/inventory/device_detail.html | 5 +- tests/test_basic.py | 3 + tests/test_render_2_0.py | 79 +++++++++++++++++++ 6 files changed, 148 insertions(+), 26 deletions(-) diff --git a/ereuse_devicehub/inventory/forms.py b/ereuse_devicehub/inventory/forms.py index cc1fd447..47b07f91 100644 --- a/ereuse_devicehub/inventory/forms.py +++ b/ereuse_devicehub/inventory/forms.py @@ -1392,7 +1392,7 @@ class DeviceDocumentForm(FlaskForm): def validate(self, extra_validators=None): is_valid = super().validate(extra_validators) - if g.user == self._device.owner: + if g.user != self._device.owner: is_valid = False return is_valid @@ -1415,7 +1415,7 @@ class DeviceDocumentForm(FlaskForm): if not self._obj.id: db.session.add(self._obj) - self._device.documents.add(self._obj) + # self._device.documents.add(self._obj) if commit: db.session.commit() diff --git a/ereuse_devicehub/inventory/models.py b/ereuse_devicehub/inventory/models.py index 7073078e..1e51113e 100644 --- a/ereuse_devicehub/inventory/models.py +++ b/ereuse_devicehub/inventory/models.py @@ -1,6 +1,7 @@ from uuid import uuid4 from citext import CIText +from dateutil.tz import tzutc from flask import g from sortedcontainers import SortedSet from sqlalchemy import BigInteger, Column, Integer @@ -148,3 +149,13 @@ class DeviceDocument(Thing): # db.Index('document_id', id, postgresql_using='hash'), # db.Index('type_doc', type, postgresql_using='hash') # ) + + def get_url(self) -> str: + if self.url: + return self.url.to_text() + return '' + + def __lt__(self, other): + return self.created.replace(tzinfo=tzutc()) < other.created.replace( + tzinfo=tzutc() + ) diff --git a/ereuse_devicehub/inventory/views.py b/ereuse_devicehub/inventory/views.py index e6906564..bb1ac9a9 100644 --- a/ereuse_devicehub/inventory/views.py +++ b/ereuse_devicehub/inventory/views.py @@ -569,27 +569,6 @@ class DocumentDeleteView(View): return flask.redirect(next_url) -class DeviceDocumentDeleteView(View): - methods = ['GET'] - decorators = [login_required] - template_name = 'inventory/device_list.html' - form_class = TradeDocumentForm - - def dispatch_request(self, lot_id, doc_id): - next_url = url_for('inventory.lotdevicelist', lot_id=lot_id) - form = self.form_class(lot=lot_id, document=doc_id) - try: - form.remove() - except Exception as err: - msg = "{}".format(err) - messages.error(msg) - return flask.redirect(next_url) - - msg = "Document removed successfully." - messages.success(msg) - return flask.redirect(next_url) - - class UploadSnapshotView(GenericMixin): methods = ['GET', 'POST'] decorators = [login_required] @@ -853,6 +832,48 @@ class NewDeviceDocumentView(GenericMixin): return flask.render_template(self.template_name, **self.context) +class EditDeviceDocumentView(GenericMixin): + decorators = [login_required] + methods = ['POST', 'GET'] + template_name = 'inventory/device_document.html' + form_class = DeviceDocumentForm + title = "Edit document" + + def dispatch_request(self, dhid, doc_id): + self.form = self.form_class(dhid=dhid, document=doc_id) + self.get_context() + + if self.form.validate_on_submit(): + self.form.save() + messages.success('Edit document successfully!') + next_url = url_for('inventory.device_details', id=dhid) + return flask.redirect(next_url) + + self.context.update({'form': self.form, 'title': self.title}) + return flask.render_template(self.template_name, **self.context) + + +class DeviceDocumentDeleteView(View): + methods = ['GET'] + decorators = [login_required] + template_name = 'inventory/device_detail.html' + form_class = DeviceDocumentForm + + def dispatch_request(self, dhid, doc_id): + self.form = self.form_class(dhid=dhid, document=doc_id) + next_url = url_for('inventory.device_details', id=dhid) + try: + self.form.remove() + except Exception as err: + msg = "{}".format(err) + messages.error(msg) + return flask.redirect(next_url) + + msg = "Document removed successfully." + messages.success(msg) + return flask.redirect(next_url) + + class NewTradeDocumentView(GenericMixin): methods = ['POST', 'GET'] decorators = [login_required] @@ -875,7 +896,6 @@ class NewTradeDocumentView(GenericMixin): class EditTransferDocumentView(GenericMixin): - decorators = [login_required] methods = ['POST', 'GET'] template_name = 'inventory/trade_document.html' @@ -1601,6 +1621,14 @@ devices.add_url_rule( '/device//document/add/', view_func=NewDeviceDocumentView.as_view('device_document_add'), ) +devices.add_url_rule( + '/device//document/edit/', + view_func=EditDeviceDocumentView.as_view('device_document_edit'), +) +devices.add_url_rule( + '/device//document/del/', + view_func=DeviceDocumentDeleteView.as_view('device_document_del'), +) devices.add_url_rule( '/lot//transfer-document/add/', view_func=NewTradeDocumentView.as_view('transfer_document_add'), diff --git a/ereuse_devicehub/templates/inventory/device_detail.html b/ereuse_devicehub/templates/inventory/device_detail.html index a7025549..c5e65e37 100644 --- a/ereuse_devicehub/templates/inventory/device_detail.html +++ b/ereuse_devicehub/templates/inventory/device_detail.html @@ -216,6 +216,7 @@ File Uploaded on + @@ -232,7 +233,7 @@ {{ doc.created.strftime('%Y-%m-%d %H:%M')}} - + @@ -259,7 +260,7 @@ diff --git a/tests/test_basic.py b/tests/test_basic.py index e834cba2..bf78792d 100644 --- a/tests/test_basic.py +++ b/tests/test_basic.py @@ -55,6 +55,9 @@ def test_api_docs(client: Client): '/inventory/device/add/', '/inventory/device/{id}/', '/inventory/device/{dhid}/binding/', + '/inventory/device/{dhid}/document/del/{doc_id}', + '/inventory/device/{dhid}/document/edit/{doc_id}', + '/inventory/device/{dhid}/document/add/', '/inventory/device/erasure/', '/inventory/device/erasure/{orphans}/', '/inventory/all/device/', diff --git a/tests/test_render_2_0.py b/tests/test_render_2_0.py index bf127c14..d2842429 100644 --- a/tests/test_render_2_0.py +++ b/tests/test_render_2_0.py @@ -2774,3 +2774,82 @@ def test_reliable_device(user3: UserClientFlask): assert Snapshot.query.first() == snapshot assert len(snapshot.device.components) == 8 assert len(snapshot.device.actions) == 7 + + +@pytest.mark.mvp +@pytest.mark.usefixtures(conftest.app_context.__name__) +def test_add_device_document(user3: UserClientFlask): + snapshot = create_device(user3, 'real-eee-1001pxd.snapshot.12.json') + device = Device.query.filter_by(devicehub_id=snapshot.device.dhid).one() + uri = '/inventory/device/{}/document/add/'.format(device.dhid) + user3.get(uri) + + name = "doc1.pdf" + url = "https://www.usody.com/" + file_name = (BytesIO(b'1234567890'), name) + data = { + 'url': url, + 'file_name': file_name, + 'csrf_token': generate_csrf(), + } + + user3.post(uri, data=data, content_type="multipart/form-data") + assert device.documents[0].file_name == name + assert device.documents[0].url.to_text() == url + + +@pytest.mark.mvp +@pytest.mark.usefixtures(conftest.app_context.__name__) +def test_edit_device_document(user3: UserClientFlask): + snapshot = create_device(user3, 'real-eee-1001pxd.snapshot.12.json') + device = Device.query.filter_by(devicehub_id=snapshot.device.dhid).one() + uri = '/inventory/device/{}/document/add/'.format(device.dhid) + user3.get(uri) + + name = "doc1.pdf" + url = "https://www.usody.com/" + file_name = (BytesIO(b'1234567890'), name) + data = { + 'url': url, + 'file_name': file_name, + 'csrf_token': generate_csrf(), + } + + user3.post(uri, data=data, content_type="multipart/form-data") + + doc_id = str(device.documents[0].id) + uri = '/inventory/device/{}/document/edit/{}'.format(device.dhid, doc_id) + user3.get(uri) + + data['url'] = "https://www.ereuse.org/" + data['csrf_token'] = generate_csrf() + data['file_name'] = (BytesIO(b'1234567890'), name) + + user3.post(uri, data=data, content_type="multipart/form-data") + assert device.documents[0].file_name == name + assert device.documents[0].url.to_text() == data['url'] + + +@pytest.mark.mvp +@pytest.mark.usefixtures(conftest.app_context.__name__) +def test_delete_device_document(user3: UserClientFlask): + snapshot = create_device(user3, 'real-eee-1001pxd.snapshot.12.json') + device = Device.query.filter_by(devicehub_id=snapshot.device.dhid).one() + uri = '/inventory/device/{}/document/add/'.format(device.dhid) + user3.get(uri) + + name = "doc1.pdf" + url = "https://www.usody.com/" + file_name = (BytesIO(b'1234567890'), name) + data = { + 'url': url, + 'file_name': file_name, + 'csrf_token': generate_csrf(), + } + + user3.post(uri, data=data, content_type="multipart/form-data") + + doc_id = str(device.documents[0].id) + uri = '/inventory/device/{}/document/del/{}'.format(device.dhid, doc_id) + user3.get(uri) + assert len(device.documents) == 0 From cd4d1bb095bd763324fe9b8ee199ecc416fdab02 Mon Sep 17 00:00:00 2001 From: Cayo Puigdefabregas Date: Tue, 4 Apr 2023 17:23:50 +0200 Subject: [PATCH 8/8] add select field --- ereuse_devicehub/inventory/forms.py | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/ereuse_devicehub/inventory/forms.py b/ereuse_devicehub/inventory/forms.py index 47b07f91..bbc0ea1b 100644 --- a/ereuse_devicehub/inventory/forms.py +++ b/ereuse_devicehub/inventory/forms.py @@ -111,6 +111,15 @@ DEVICES = { "Other Devices": ["Other"], } +TYPES_DOCUMENTS = [ + ("", ""), + ("image", "Image"), + ("main_image", "Main Image"), + ("functionality_report", "Functionality Report"), + ("data_sanitization_report", "Data Sanitization Report"), + ("disposition_report", "Disposition Report"), +] + COMPUTERS = ['Desktop', 'Laptop', 'Server', 'Computer'] MONITORS = ["ComputerMonitor", "Monitor", "TelevisionSet", "Projector"] @@ -1352,11 +1361,12 @@ class DeviceDocumentForm(FlaskForm): render_kw={'class': "form-control"}, description="Identification number of document", ) - type = StringField( + type = SelectField( 'Type', [validators.Optional()], - render_kw={'class': "form-control"}, - description="Type of document", + choices=TYPES_DOCUMENTS, + default="", + render_kw={'class': "form-select"}, ) date = DateField( 'Date',