diff --git a/ereuse_devicehub/inventory/forms.py b/ereuse_devicehub/inventory/forms.py index ed69f202..69f0b4ba 100644 --- a/ereuse_devicehub/inventory/forms.py +++ b/ereuse_devicehub/inventory/forms.py @@ -86,12 +86,18 @@ DEVICES = { "Smartphone", "Cellphone", ], + "Drives & Storage": [ + "All DataStorage", + "HardDrives", + "SolidStageDrive", + ], } COMPUTERS = ['Desktop', 'Laptop', 'Server', 'Computer'] MONITORS = ["ComputerMonitor", "Monitor", "TelevisionSet", "Projector"] MOBILE = ["Mobile", "Tablet", "Smartphone", "Cellphone"] +STORAGE = ["HardDrive", "SolidStateDrive"] class AdvancedSearchForm(FlaskForm): @@ -175,9 +181,16 @@ class FilterForm(FlaskForm): elif "All Mobile" == self.device_type: filter_type = MOBILE + elif "All DataStorage" == self.device_type: + filter_type = STORAGE + if filter_type: self.devices = self.devices.filter(Device.type.in_(filter_type)) + # if self.device_type in STORAGE + ["All DataStorage"]: + # import pdb; pdb.set_trace() + # self.devices = self.devices.filter(Component.parent_id.is_(None)) + return self.devices.order_by(Device.updated.desc()) @@ -1259,6 +1272,12 @@ class TradeDocumentForm(FlaskForm): class TransferForm(FlaskForm): + lot_name = StringField( + 'Lot Name', + [validators.DataRequired()], + render_kw={'class': "form-control"}, + description="You need put a lot name", + ) code = StringField( 'Code', [validators.DataRequired()], @@ -1303,9 +1322,7 @@ class TransferForm(FlaskForm): return self._obj def set_obj(self): - name = self.code.data - if self._tmp_lot: - name = self._tmp_lot.name + name = self.lot_name.data self.newlot = Lot(name=name) if self._tmp_lot: self.newlot.devices = self._tmp_lot.devices @@ -1339,6 +1356,7 @@ class EditTransferForm(TransferForm): self.code.data = self._obj.code self.description.data = self._obj.description self.date.data = self._obj.date + self.lot_name.data = self._obj.lot.name def validate(self, extra_validators=None): is_valid = super().validate(extra_validators) @@ -1350,6 +1368,7 @@ class EditTransferForm(TransferForm): def set_obj(self, commit=True): self.populate_obj(self._obj) + self._obj.lot.name = self.lot_name.data class NotesForm(FlaskForm): diff --git a/ereuse_devicehub/inventory/views.py b/ereuse_devicehub/inventory/views.py index 63d9294f..0267db00 100644 --- a/ereuse_devicehub/inventory/views.py +++ b/ereuse_devicehub/inventory/views.py @@ -296,6 +296,8 @@ class BindingView(GenericMixin): self.real_phid = self.new_placeholder.phid self.abstract_dhid = self.old_device.devicehub_id self.abstract_phid = self.old_placeholder.phid + if self.old_placeholder.kangaroo: + self.new_placeholder.kangaroo = True # to do a backup of abstract_dhid and abstract_phid in # workbench device @@ -383,6 +385,9 @@ class UnBindingView(GenericMixin): if device.binding.is_abstract: return + kangaroo = device.binding.kangaroo + device.binding.kangaroo = False + dict_device = copy.copy(device.__dict__) dict_device.pop('_sa_instance_state') dict_device.pop('id', None) @@ -402,7 +407,10 @@ class UnBindingView(GenericMixin): if c.binding: c.binding.device.parent = new_device - placeholder = Placeholder(device=new_device, binding=device, is_abstract=True) + placeholder = Placeholder( + device=new_device, binding=device, is_abstract=True, kangaroo=kangaroo + ) + if ( device.dhid_bk and not Device.query.filter_by(devicehub_id=device.dhid_bk).first() @@ -756,6 +764,8 @@ class NewTransferView(GenericMixin): def dispatch_request(self, type_id, lot_id=None): self.form = self.form_class(lot_id=lot_id, type=type_id) self.get_context() + referrer = request.referrer or url_for('inventory.devicelist') + self.context.update({'referrer': referrer}) if self.form.validate_on_submit(): self.form.save() @@ -767,7 +777,12 @@ class NewTransferView(GenericMixin): next_url = url_for('inventory.lotdevicelist', lot_id=str(new_lot_id)) return flask.redirect(next_url) - self.context.update({'form': self.form, 'title': self.title}) + self.context.update( + { + 'form': self.form, + 'title': self.title, + } + ) return flask.render_template(self.template_name, **self.context) diff --git a/ereuse_devicehub/migrations/versions/a13ed6ad0e3e_add_kangaroo_in_placeholder.py b/ereuse_devicehub/migrations/versions/a13ed6ad0e3e_add_kangaroo_in_placeholder.py new file mode 100644 index 00000000..bc3b6790 --- /dev/null +++ b/ereuse_devicehub/migrations/versions/a13ed6ad0e3e_add_kangaroo_in_placeholder.py @@ -0,0 +1,34 @@ +"""add kangaroo in placeholder + +Revision ID: a13ed6ad0e3e +Revises: 626c17026ca7 +Create Date: 2022-10-13 11:56:15.303218 + +""" +import sqlalchemy as sa +from alembic import context, op + +# revision identifiers, used by Alembic. +revision = 'a13ed6ad0e3e' +down_revision = '626c17026ca7' +branch_labels = None +depends_on = None + + +def get_inv(): + INV = context.get_x_argument(as_dictionary=True).get('inventory') + if not INV: + raise ValueError("Inventory value is not specified") + return INV + + +def upgrade(): + op.add_column( + 'placeholder', + sa.Column('kangaroo', sa.Boolean(), nullable=True), + schema=f'{get_inv()}', + ) + + +def downgrade(): + op.drop_column('placeholder', 'kangaroo', schema=f'{get_inv()}') diff --git a/ereuse_devicehub/resources/device/models.py b/ereuse_devicehub/resources/device/models.py index 5f1ccb20..86d8c834 100644 --- a/ereuse_devicehub/resources/device/models.py +++ b/ereuse_devicehub/resources/device/models.py @@ -932,6 +932,7 @@ class Placeholder(Thing): ) id_device_internal = db.Column(CIText()) id_device_internal.comment = "Identification used internaly for the user" + kangaroo = db.Column(Boolean, default=False, nullable=True) device_id = db.Column( BigInteger, diff --git a/ereuse_devicehub/static/css/devicehub.css b/ereuse_devicehub/static/css/devicehub.css index 84771144..46fae783 100644 --- a/ereuse_devicehub/static/css/devicehub.css +++ b/ereuse_devicehub/static/css/devicehub.css @@ -29,4 +29,7 @@ .doTransfer { margin-top: -8px; margin-right: 15px;" +} +.printLabelForm { + margin-left: 10px; } \ No newline at end of file diff --git a/ereuse_devicehub/templates/inventory/device_list.html b/ereuse_devicehub/templates/inventory/device_list.html index 2132f7c7..3bb32fc5 100644 --- a/ereuse_devicehub/templates/inventory/device_list.html +++ b/ereuse_devicehub/templates/inventory/device_list.html @@ -357,6 +357,7 @@ {% for dev in devices %} + {% if dev.placeholder and (not dev.parent_id or dev.parent.placeholder.kangaroo) %} + {% endif %} {% endfor %} diff --git a/ereuse_devicehub/templates/inventory/new_transfer.html b/ereuse_devicehub/templates/inventory/new_transfer.html index f3776c41..f76bb525 100644 --- a/ereuse_devicehub/templates/inventory/new_transfer.html +++ b/ereuse_devicehub/templates/inventory/new_transfer.html @@ -38,7 +38,7 @@
{% if field != form.type %} {{ field.label(class_="form-label") }} - {% if field == form.code %} + {% if field in [form.code, form.lot_name] %} * {% endif %} {{ field }} @@ -55,7 +55,7 @@ {% endfor %}
- Cancel + Cancel
diff --git a/ereuse_devicehub/templates/labels/print_labels.html b/ereuse_devicehub/templates/labels/print_labels.html index df419a5f..0e36d68d 100644 --- a/ereuse_devicehub/templates/labels/print_labels.html +++ b/ereuse_devicehub/templates/labels/print_labels.html @@ -13,7 +13,7 @@
-
+
@@ -91,8 +91,7 @@
{% endfor %}
-
-
+
diff --git a/ereuse_devicehub/templates/workbench/settings.html b/ereuse_devicehub/templates/workbench/settings.html index a498eebc..c302f0b7 100644 --- a/ereuse_devicehub/templates/workbench/settings.html +++ b/ereuse_devicehub/templates/workbench/settings.html @@ -11,6 +11,62 @@
+
+
+ +
+
+ +
+
+
+
+ + + + + + + + + {% for host in form.kangaroos %} + + + + + {% endfor %} + + + + + +
PHIDErasure Host
{{ host.phid }} + +
+ {% for f in form %} + {{ f }} + {% if f == form.phid and f.errors %} +

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

+ {% endif %} + {% endfor %} +
+ +
+
+
+
+
+ +
+
+ +
+
+
diff --git a/ereuse_devicehub/workbench/forms.py b/ereuse_devicehub/workbench/forms.py new file mode 100644 index 00000000..648f5ceb --- /dev/null +++ b/ereuse_devicehub/workbench/forms.py @@ -0,0 +1,47 @@ +from flask import g +from flask_wtf import FlaskForm +from wtforms import StringField, validators + +from ereuse_devicehub.db import db +from ereuse_devicehub.resources.device.models import Placeholder + + +class KangarooForm(FlaskForm): + phid = StringField('Phid', [validators.length(min=1)]) + + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + + self.placeholder = None + self.kangaroos = Placeholder.query.filter( + Placeholder.kangaroo.is_(True) + ).filter(Placeholder.owner_id == g.user.id) + + def validate(self, extra_validators=None): + is_valid = super().validate(extra_validators) + if not is_valid: + return False + + if not self.placeholder: + self.placeholder = ( + Placeholder.query.filter(Placeholder.phid == self.phid.data) + .filter(Placeholder.owner_id == g.user.id) + .first() + ) + if self.placeholder: + if self.placeholder.status not in ['Snapshot', 'Twin']: + self.placeholder = None + + if not self.placeholder: + self.phid.errors = ["Device not exist"] + return False + + return True + + def save(self): + if not self.placeholder or self.placeholder.kangaroo: + return + + self.placeholder.kangaroo = True + db.session.commit() + return self.placeholder diff --git a/ereuse_devicehub/workbench/views.py b/ereuse_devicehub/workbench/views.py index 3640ab5b..60c34fd3 100644 --- a/ereuse_devicehub/workbench/views.py +++ b/ereuse_devicehub/workbench/views.py @@ -3,34 +3,43 @@ import time import flask from flask import Blueprint from flask import current_app as app -from flask import g, make_response, request +from flask import g, make_response, request, url_for +from flask.views import View from flask_login import login_required from ereuse_devicehub import auth from ereuse_devicehub.db import db +from ereuse_devicehub.resources.device.models import Placeholder from ereuse_devicehub.resources.enums import SessionType from ereuse_devicehub.resources.user.models import Session from ereuse_devicehub.views import GenericMixin from ereuse_devicehub.workbench import isos +from ereuse_devicehub.workbench.forms import KangarooForm workbench = Blueprint('workbench', __name__, url_prefix='/workbench') class SettingsView(GenericMixin): decorators = [login_required] + methods = ['GET', 'POST'] template_name = 'workbench/settings.html' page_title = "Workbench" def dispatch_request(self): self.get_context() + form_kangaroo = KangarooForm() self.context.update( { 'page_title': self.page_title, 'demo': g.user.email == app.config['EMAIL_DEMO'], 'iso': isos, + 'form': form_kangaroo, } ) + if form_kangaroo.validate_on_submit(): + form_kangaroo.save() + self.opt = request.values.get('opt') if self.opt in ['register', 'erease_basic', 'erease_sectors']: return self.download() @@ -86,4 +95,24 @@ class SettingsView(GenericMixin): return token +class ErasureHostView(View): + decorators = [login_required] + methods = ['GET'] + + def dispatch_request(self, id): + self.placeholder = ( + Placeholder.query.filter(Placeholder.id == id) + .filter(Placeholder.kangaroo.is_(True)) + .filter(Placeholder.owner_id == g.user.id) + .one() + ) + self.placeholder.kangaroo = False + db.session.commit() + + return flask.redirect(url_for('workbench.settings')) + + workbench.add_url_rule('/', view_func=SettingsView.as_view('settings')) +workbench.add_url_rule( + '/erasure_host//', view_func=ErasureHostView.as_view('erasure_host') +) diff --git a/tests/test_basic.py b/tests/test_basic.py index 43c93d3c..3d7a0ad8 100644 --- a/tests/test_basic.py +++ b/tests/test_basic.py @@ -105,6 +105,7 @@ def test_api_docs(client: Client): '/users/logout/', '/versions/', '/workbench/', + '/workbench/erasure_host/{id}/', } assert docs['info'] == {'title': 'Devicehub', 'version': '0.2'} assert docs['components']['securitySchemes']['bearerAuth'] == { diff --git a/tests/test_render_2_0.py b/tests/test_render_2_0.py index f918cb80..2dcd7100 100644 --- a/tests/test_render_2_0.py +++ b/tests/test_render_2_0.py @@ -1374,6 +1374,7 @@ def test_wb_settings_register(user3: UserClientFlask): def test_create_transfer(user3: UserClientFlask): user3.get('/inventory/lot/add/') lot_name = 'lot1' + lot_name2 = 'lot2' data = { 'name': lot_name, 'csrf_token': generate_csrf(), @@ -1390,13 +1391,14 @@ def test_create_transfer(user3: UserClientFlask): assert 'Description' in body assert 'Save' in body - data = {'csrf_token': generate_csrf(), 'code': 'AAA'} + data = {'csrf_token': generate_csrf(), 'code': 'AAA', 'lot_name': lot_name2} body, status = user3.post(uri, data=data) assert status == '200 OK' assert 'Transfer created successfully!' in body assert 'Delete Lot' in body assert 'Incoming Lot' in body + assert lot_name2 in body @pytest.mark.mvp @@ -1405,6 +1407,8 @@ def test_edit_transfer(user3: UserClientFlask): # create lot user3.get('/inventory/lot/add/') lot_name = 'lot1' + lot_name2 = 'lot2' + lot_name3 = 'lot3' data = { 'name': lot_name, 'csrf_token': generate_csrf(), @@ -1422,12 +1426,13 @@ def test_edit_transfer(user3: UserClientFlask): # create new incoming lot uri = f'/inventory/lot/{lot_id}/transfer/incoming/' - data = {'csrf_token': generate_csrf(), 'code': 'AAA'} + data = {'csrf_token': generate_csrf(), 'code': 'AAA', 'lot_name': lot_name2} body, status = user3.post(uri, data=data) assert 'Transfer (Open)' in body assert ' Delete Lot' in body lot = Lot.query.filter()[1] assert lot.transfer is not None + assert lot_name2 in body # edit transfer with errors lot_id = lot.id @@ -1436,6 +1441,7 @@ def test_edit_transfer(user3: UserClientFlask): 'csrf_token': generate_csrf(), 'code': 'AAA', 'description': 'one one one', + 'lot_name': lot_name3, 'date': datetime.datetime.now().date() + datetime.timedelta(15), } body, status = user3.post(uri, data=data) @@ -1450,6 +1456,7 @@ def test_edit_transfer(user3: UserClientFlask): 'csrf_token': generate_csrf(), 'code': 'AAA', 'description': 'one one one', + 'lot_name': lot_name3, 'date': datetime.datetime.now().date() - datetime.timedelta(15), } body, status = user3.post(uri, data=data) @@ -1458,6 +1465,7 @@ def test_edit_transfer(user3: UserClientFlask): assert 'one one one' in body assert ' Delete Lot' not in body assert 'Transfer (Closed)' in body + assert lot_name3 in body @pytest.mark.mvp @@ -1476,7 +1484,7 @@ def test_edit_deliverynote(user3: UserClientFlask): # create new incoming lot uri = f'/inventory/lot/{lot_id}/transfer/incoming/' - data = {'csrf_token': generate_csrf(), 'code': 'AAA'} + data = {'csrf_token': generate_csrf(), 'code': 'AAA', 'lot_name': lot_name} user3.post(uri, data=data) lot = Lot.query.filter()[1] lot_id = lot.id @@ -1517,7 +1525,7 @@ def test_edit_receivernote(user3: UserClientFlask): # create new incoming lot uri = f'/inventory/lot/{lot_id}/transfer/incoming/' - data = {'csrf_token': generate_csrf(), 'code': 'AAA'} + data = {'csrf_token': generate_csrf(), 'code': 'AAA', 'lot_name': lot_name} user3.post(uri, data=data) lot = Lot.query.filter()[1] lot_id = lot.id @@ -1558,7 +1566,7 @@ def test_edit_notes_with_closed_transfer(user3: UserClientFlask): # create new incoming lot uri = f'/inventory/lot/{lot_id}/transfer/incoming/' - data = {'csrf_token': generate_csrf(), 'code': 'AAA'} + data = {'csrf_token': generate_csrf(), 'code': 'AAA', 'lot_name': lot_name} user3.post(uri, data=data) lot = Lot.query.filter()[1] lot_id = lot.id @@ -2403,7 +2411,7 @@ def test_bug_3831_documents(user3: UserClientFlask): uri = f'/inventory/lot/{lot_id}/transfer/incoming/' user3.get(uri) - data = {'csrf_token': generate_csrf(), 'code': 'AAA'} + data = {'csrf_token': generate_csrf(), 'code': 'AAA', 'lot_name': lot_name} body, status = user3.post(uri, data=data) assert status == '200 OK' @@ -2430,3 +2438,99 @@ def test_bug_3831_documents(user3: UserClientFlask): uri = f'/inventory/lot/{lot_id}/trade-document/add/' # body, status = user3.post(uri, data=data, content_type="multipart/form-data") # assert status == '200 OK' + + +@pytest.mark.mvp +@pytest.mark.usefixtures(conftest.app_context.__name__) +def test_hdd_filter(user3: UserClientFlask): + create_device(user3, 'real-eee-1001pxd.snapshot.12.json') + + hdds = Device.query.filter_by(type='HardDrive').all() + for hdd in hdds: + hdd.parent = None + db.session.commit() + + csrf = generate_csrf() + uri = f'/inventory/device/?filter=All+DataStorage&csrf_token={csrf}' + body, status = user3.get(uri) + + assert status == '200 OK' + for hdd in hdds: + assert hdd.dhid in body + + +@pytest.mark.mvp +@pytest.mark.usefixtures(conftest.app_context.__name__) +def test_add_kangaroo(user3: UserClientFlask): + create_device(user3, 'real-eee-1001pxd.snapshot.12.json') + + body, status = user3.get('/workbench/') + + assert status == '200 OK' + + pc = Device.query.filter_by(type='Laptop').first() + data = { + 'csrf_token': generate_csrf(), + 'phid': pc.phid(), + } + + body, status = user3.post('/workbench/', data=data) + assert status == '200 OK' + assert pc.phid() in body + + +@pytest.mark.mvp +@pytest.mark.usefixtures(conftest.app_context.__name__) +def test_drop_kangaroo(user3: UserClientFlask): + create_device(user3, 'real-eee-1001pxd.snapshot.12.json') + pc = Device.query.filter_by(type='Laptop').first() + phid = 'AAA' + pc.placeholder.phid = phid + db.session.commit() + + body, status = user3.get('/workbench/') + assert phid not in body + + data = { + 'csrf_token': generate_csrf(), + 'phid': phid, + } + + body, status = user3.post('/workbench/', data=data) + assert status == '200 OK' + assert phid in body + + placeholder_id = pc.placeholder.id + uri = f'/workbench/erasure_host/{placeholder_id}/' + body, status = user3.get(uri) + assert status == '200 OK' + assert phid not in body + + +@pytest.mark.mvp +@pytest.mark.usefixtures(conftest.app_context.__name__) +def test_filter_hdd_in_kangaroo(user3: UserClientFlask): + create_device(user3, 'real-eee-1001pxd.snapshot.12.json') + csrf = generate_csrf() + uri = f'/inventory/device/?filter=All+DataStorage&csrf_token={csrf}' + body, status = user3.get(uri) + + assert status == '200 OK' + for hdd in Device.query.filter_by(type='HardDrive').all(): + assert hdd.dhid not in body + + user3.get('/workbench/') + pc = Device.query.filter_by(type='Laptop').first() + data = { + 'csrf_token': generate_csrf(), + 'phid': pc.phid(), + } + user3.post('/workbench/', data=data) + + csrf = generate_csrf() + uri = f'/inventory/device/?filter=All+DataStorage&csrf_token={csrf}' + body, status = user3.get(uri) + + assert status == '200 OK' + for hdd in Device.query.filter_by(type='HardDrive').all(): + assert hdd.dhid in body