From a8df3f5e5afec38c03ff510a89d895a24a41fc79 Mon Sep 17 00:00:00 2001 From: Cayo Puigdefabregas Date: Wed, 16 Mar 2022 13:06:42 +0100 Subject: [PATCH 01/33] Render a template for get a labels of multiple devices --- ereuse_devicehub/inventory/forms.py | 15 +++ ereuse_devicehub/inventory/views.py | 35 ++++++ .../templates/inventory/device_list.html | 11 ++ .../templates/inventory/print_tags.html | 108 ++++++++++++++++++ 4 files changed, 169 insertions(+) create mode 100644 ereuse_devicehub/templates/inventory/print_tags.html diff --git a/ereuse_devicehub/inventory/forms.py b/ereuse_devicehub/inventory/forms.py index 5eab3236..3faa8751 100644 --- a/ereuse_devicehub/inventory/forms.py +++ b/ereuse_devicehub/inventory/forms.py @@ -1007,3 +1007,18 @@ class TradeDocumentForm(FlaskForm): db.session.commit() return self._obj + + +class PrintTagsForm(FlaskForm): + devices = StringField(render_kw={'class': "devicesList d-none"}) + + def validate(self, extra_validators=None): + is_valid = super().validate(extra_validators) + + if not self.devices.data: + return False + + device_ids = self.devices.data.split(",") + self._tags = Tag.query.filter(Tag.device_id.in_(device_ids)).all() + + return is_valid diff --git a/ereuse_devicehub/inventory/views.py b/ereuse_devicehub/inventory/views.py index 4e827be4..a438bf96 100644 --- a/ereuse_devicehub/inventory/views.py +++ b/ereuse_devicehub/inventory/views.py @@ -21,6 +21,7 @@ from ereuse_devicehub.inventory.forms import ( LotForm, NewActionForm, NewDeviceForm, + PrintTagsForm, TagDeviceForm, TagForm, TagUnnamedForm, @@ -118,6 +119,7 @@ class DeviceListMix(GenericMixView): 'form_new_datawipe': form_new_datawipe, 'form_new_trade': form_new_trade, 'form_filter': form_filter, + 'form_print_tags': PrintTagsForm(), 'lot': lot, 'tags': tags, 'list_devices': list_devices, @@ -381,6 +383,35 @@ class TagAddUnnamedView(View): return flask.render_template(self.template_name, form=form, **context) +class PrintTagsView(View): + """This View is used to print labels from multiple devices""" + + methods = ['POST', 'GET'] + decorators = [login_required] + template_name = 'inventory/print_tags.html' + title = 'Design and implementation of labels' + + def dispatch_request(self): + lots = Lot.query.filter(Lot.owner_id == current_user.id) + context = { + 'lots': lots, + 'page_title': self.title, + 'version': __version__, + 'referrer': request.referrer, + } + + form = PrintTagsForm() + if form.validate_on_submit(): + context['form'] = form + context['tags'] = form._tags + return flask.render_template(self.template_name, **context) + else: + messages.error('Error you need select one or more devices') + + next_url = request.referrer or url_for('inventory.devicelist') + return flask.redirect(next_url) + + class TagDetailView(View): decorators = [login_required] template_name = 'inventory/tag_detail.html' @@ -734,3 +765,7 @@ devices.add_url_rule( devices.add_url_rule( '/export//', view_func=ExportsView.as_view('export') ) +devices.add_url_rule( + '/tags/print', + view_func=PrintTagsView.as_view('print_tags'), +) diff --git a/ereuse_devicehub/templates/inventory/device_list.html b/ereuse_devicehub/templates/inventory/device_list.html index 157dda05..9c6ced51 100644 --- a/ereuse_devicehub/templates/inventory/device_list.html +++ b/ereuse_devicehub/templates/inventory/device_list.html @@ -235,6 +235,17 @@ Remove Tag from selected Device +
  • + +
  • diff --git a/ereuse_devicehub/templates/inventory/print_tags.html b/ereuse_devicehub/templates/inventory/print_tags.html new file mode 100644 index 00000000..421eecde --- /dev/null +++ b/ereuse_devicehub/templates/inventory/print_tags.html @@ -0,0 +1,108 @@ +{% extends "ereuse_devicehub/base_site.html" %} +{% block main %} + +
    +

    Print tags

    + +
    + +
    +
    +
    + +
    +
    +
    +
    Print Tags
    +

    {{ title }}

    +
    + +
    +
    + {% for tag in tags %} +
    +
    +
    +
    +
    +
    +
    +
    {{ tag.id }}
    +
    +
    +
    +
    + {% endfor %} +
    +
    +
    +
    + +
    +
    + +
    +
    + +
    +
    + + mm +
    +
    + +
    +
    + + mm +
    +
    +
    +
    +
    +
    + Print +
    +
    + Save +
    +
    + Reset +
    +
    + +
    + +
    +
    +
    + +
    + + + + +{% endblock main %} From a36fb0b0fc7f7acf4c8f8b21d572a154280d13f7 Mon Sep 17 00:00:00 2001 From: Cayo Puigdefabregas Date: Wed, 16 Mar 2022 14:27:55 +0100 Subject: [PATCH 02/33] generate pdf with multiple devices --- ereuse_devicehub/static/js/print.pdf.js | 26 +++++++++++++------ .../templates/inventory/print_tags.html | 13 +++------- .../templates/inventory/tag_detail.html | 6 ++--- 3 files changed, 25 insertions(+), 20 deletions(-) diff --git a/ereuse_devicehub/static/js/print.pdf.js b/ereuse_devicehub/static/js/print.pdf.js index 5316e1ef..0d6fe6d5 100644 --- a/ereuse_devicehub/static/js/print.pdf.js +++ b/ereuse_devicehub/static/js/print.pdf.js @@ -5,8 +5,8 @@ $(document).ready(function() { load_size(); }) -function qr_draw(url) { - var qrcode = new QRCode($("#qrcode")[0], { +function qr_draw(url, id) { + var qrcode = new QRCode($(id)[0], { text: url, width: 128, height: 128, @@ -54,16 +54,26 @@ function printpdf() { var border = 2; var height = parseInt($("#height-tag").val()); var width = parseInt($("#width-tag").val()); - var tag = $("#tag").text(); - var pdf = new jsPDF('l', 'mm', [width, height]); - var imgData = $('#qrcode img').attr("src"); img_side = Math.min(height, width) - 2*border; max_tag_side = (Math.max(height, width)/2) + border; if (max_tag_side < img_side) { max_tag_side = img_side+ 2*border; }; min_tag_side = (Math.min(height, width)/2) + border; - pdf.addImage(imgData, 'PNG', border, border, img_side, img_side); - pdf.text(tag, max_tag_side, min_tag_side); - pdf.save('Tag_'+tag+'.pdf'); + var last_tag_code = ''; + + var pdf = new jsPDF('l', 'mm', [width, height]); + $(".tag").map(function(x, y) { + if (x != 0){ + pdf.addPage(); + console.log(x) + }; + var tag = $(y).text(); + last_tag_code = tag; + var imgData = $('#'+tag+' img').attr("src"); + pdf.addImage(imgData, 'PNG', border, border, img_side, img_side); + pdf.text(tag, max_tag_side, min_tag_side); + }); + + pdf.save('Tag_'+last_tag_code+'.pdf'); } diff --git a/ereuse_devicehub/templates/inventory/print_tags.html b/ereuse_devicehub/templates/inventory/print_tags.html index 421eecde..25a43a9d 100644 --- a/ereuse_devicehub/templates/inventory/print_tags.html +++ b/ereuse_devicehub/templates/inventory/print_tags.html @@ -32,7 +32,9 @@
    -
    {{ tag.id }}
    +
    + {{ tag.id }} +
    @@ -95,14 +97,7 @@ {% endblock main %} diff --git a/ereuse_devicehub/templates/inventory/tag_detail.html b/ereuse_devicehub/templates/inventory/tag_detail.html index 691838df..3bc3863b 100644 --- a/ereuse_devicehub/templates/inventory/tag_detail.html +++ b/ereuse_devicehub/templates/inventory/tag_detail.html @@ -47,10 +47,10 @@
    -
    +
    -
    {{ tag.id }}
    +
    {{ tag.id }}
    @@ -109,6 +109,6 @@ {% endblock main %} From f8e1776ca840617ee52a6d0c7beb49728751c0d7 Mon Sep 17 00:00:00 2001 From: Cayo Puigdefabregas Date: Thu, 17 Mar 2022 09:41:03 +0100 Subject: [PATCH 03/33] Change names tags for labels in the labels sections --- ereuse_devicehub/templates/inventory/device_list.html | 2 +- ereuse_devicehub/templates/inventory/print_tags.html | 6 +++--- ereuse_devicehub/templates/inventory/tag_detail.html | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/ereuse_devicehub/templates/inventory/device_list.html b/ereuse_devicehub/templates/inventory/device_list.html index 9c6ced51..9e76fb46 100644 --- a/ereuse_devicehub/templates/inventory/device_list.html +++ b/ereuse_devicehub/templates/inventory/device_list.html @@ -242,7 +242,7 @@ {% endfor %} - Print tags + Print labels diff --git a/ereuse_devicehub/templates/inventory/print_tags.html b/ereuse_devicehub/templates/inventory/print_tags.html index 25a43a9d..ebafbe61 100644 --- a/ereuse_devicehub/templates/inventory/print_tags.html +++ b/ereuse_devicehub/templates/inventory/print_tags.html @@ -2,11 +2,11 @@ {% block main %}
    -

    Print tags

    +

    Print Labels

    @@ -18,7 +18,7 @@
    -
    Print Tags
    +
    Print Labels

    {{ title }}

    diff --git a/ereuse_devicehub/templates/inventory/tag_detail.html b/ereuse_devicehub/templates/inventory/tag_detail.html index 3bc3863b..7d07dc59 100644 --- a/ereuse_devicehub/templates/inventory/tag_detail.html +++ b/ereuse_devicehub/templates/inventory/tag_detail.html @@ -40,7 +40,7 @@
    -
    Print details
    +
    Print Label
    From 3ac380271952a2e5b2e6b73a762f554b42912816 Mon Sep 17 00:00:00 2001 From: Cayo Puigdefabregas Date: Thu, 17 Mar 2022 18:09:08 +0100 Subject: [PATCH 04/33] Filter for show only dhid tags --- ereuse_devicehub/inventory/forms.py | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/ereuse_devicehub/inventory/forms.py b/ereuse_devicehub/inventory/forms.py index 3faa8751..7108e3a5 100644 --- a/ereuse_devicehub/inventory/forms.py +++ b/ereuse_devicehub/inventory/forms.py @@ -1019,6 +1019,18 @@ class PrintTagsForm(FlaskForm): return False device_ids = self.devices.data.split(",") - self._tags = Tag.query.filter(Tag.device_id.in_(device_ids)).all() + self._devices = ( + Device.query.filter(Device.id.in_(device_ids)) + .filter(Device.owner_id == g.user.id) + .distinct() + .all() + ) + dhids = [x.devicehub_id for x in self._devices] + # filter for found all tags of a list of devices + # self._tags = Tag.query.filter(Tag.owner_id == g.user.id).filter(Tag.device_id.in_(dhids)).all() + # filter for found all tags equal to devicehub_id + self._tags = ( + Tag.query.filter(Tag.owner_id == g.user.id).filter(Tag.id.in_(dhids)).all() + ) return is_valid From 358c569eea90df086694c49300d1cd7aab072b2c Mon Sep 17 00:00:00 2001 From: Santiago Lamora Date: Tue, 22 Mar 2022 11:37:46 +0100 Subject: [PATCH 05/33] Rewrite comment to be more clear --- ereuse_devicehub/inventory/forms.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/ereuse_devicehub/inventory/forms.py b/ereuse_devicehub/inventory/forms.py index 7108e3a5..14f93516 100644 --- a/ereuse_devicehub/inventory/forms.py +++ b/ereuse_devicehub/inventory/forms.py @@ -1025,10 +1025,9 @@ class PrintTagsForm(FlaskForm): .distinct() .all() ) + + # print only tags that are DHID dhids = [x.devicehub_id for x in self._devices] - # filter for found all tags of a list of devices - # self._tags = Tag.query.filter(Tag.owner_id == g.user.id).filter(Tag.device_id.in_(dhids)).all() - # filter for found all tags equal to devicehub_id self._tags = ( Tag.query.filter(Tag.owner_id == g.user.id).filter(Tag.id.in_(dhids)).all() ) From 25bf04e93d373e8b5936d432aa0e4932e4dd534f Mon Sep 17 00:00:00 2001 From: Santiago Lamora Date: Tue, 22 Mar 2022 11:40:25 +0100 Subject: [PATCH 06/33] Add Python 3.8 to CI testing matrix --- .github/workflows/flask.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/flask.yml b/.github/workflows/flask.yml index 16086b5b..d668079e 100644 --- a/.github/workflows/flask.yml +++ b/.github/workflows/flask.yml @@ -32,7 +32,7 @@ jobs: strategy: max-parallel: 4 matrix: - python-version: [3.7] + python-version: [3.7, 3.8] steps: - uses: actions/checkout@v2 From 90b19972ba7343cf539e6102c7ca76d43636690b Mon Sep 17 00:00:00 2001 From: Santiago Lamora Date: Tue, 22 Mar 2022 11:48:43 +0100 Subject: [PATCH 07/33] Revert "Add Python 3.8 to CI testing matrix" This reverts commit 25bf04e93d373e8b5936d432aa0e4932e4dd534f. --- .github/workflows/flask.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/flask.yml b/.github/workflows/flask.yml index d668079e..16086b5b 100644 --- a/.github/workflows/flask.yml +++ b/.github/workflows/flask.yml @@ -32,7 +32,7 @@ jobs: strategy: max-parallel: 4 matrix: - python-version: [3.7, 3.8] + python-version: [3.7] steps: - uses: actions/checkout@v2 From aa5d93cf48d1c309778fad29355ccb188bef4040 Mon Sep 17 00:00:00 2001 From: Cayo Puigdefabregas Date: Thu, 31 Mar 2022 14:04:16 +0200 Subject: [PATCH 08/33] modularize labels instead of tags into inventory --- ereuse_devicehub/inventory/forms.py | 66 -------- ereuse_devicehub/inventory/views.py | 133 +--------------- ereuse_devicehub/label/__init__.py | 0 ereuse_devicehub/label/views.py | 142 ++++++++++++++++++ .../templates/ereuse_devicehub/base_site.html | 2 +- .../templates/inventory/device_list.html | 8 +- .../label_detail.html} | 2 +- .../tag_list.html => label/label_list.html} | 6 +- .../print_labels.html} | 2 +- .../{inventory => label}/tag_create.html | 4 +- .../tag_create_unnamed.html | 4 +- 11 files changed, 158 insertions(+), 211 deletions(-) create mode 100644 ereuse_devicehub/label/__init__.py create mode 100644 ereuse_devicehub/label/views.py rename ereuse_devicehub/templates/{inventory/tag_detail.html => label/label_detail.html} (97%) rename ereuse_devicehub/templates/{inventory/tag_list.html => label/label_list.html} (88%) rename ereuse_devicehub/templates/{inventory/print_tags.html => label/print_labels.html} (97%) rename ereuse_devicehub/templates/{inventory => label}/tag_create.html (90%) rename ereuse_devicehub/templates/{inventory => label}/tag_create_unnamed.html (90%) diff --git a/ereuse_devicehub/inventory/forms.py b/ereuse_devicehub/inventory/forms.py index 14f93516..48484a1e 100644 --- a/ereuse_devicehub/inventory/forms.py +++ b/ereuse_devicehub/inventory/forms.py @@ -485,46 +485,6 @@ class NewDeviceForm(FlaskForm): return snapshot -class TagForm(FlaskForm): - code = StringField('Code', [validators.length(min=1)]) - - def validate(self, extra_validators=None): - error = ["This value is being used"] - is_valid = super().validate(extra_validators) - if not is_valid: - return False - tag = Tag.query.filter(Tag.id == self.code.data).all() - if tag: - self.code.errors = error - return False - - return True - - def save(self): - self.instance = Tag(id=self.code.data) - db.session.add(self.instance) - db.session.commit() - return self.instance - - def remove(self): - if not self.instance.device and not self.instance.provider: - self.instance.delete() - db.session.commit() - return self.instance - - -class TagUnnamedForm(FlaskForm): - amount = IntegerField('amount') - - def save(self): - num = self.amount.data - tags_id, _ = g.tag_provider.post('/', {}, query=[('num', num)]) - tags = [Tag(id=tag_id, provider=g.inventory.tag_provider) for tag_id in tags_id] - db.session.add_all(tags) - db.session.commit() - return tags - - class TagDeviceForm(FlaskForm): tag = SelectField('Tag', choices=[]) device = StringField('Device', [validators.Optional()]) @@ -1007,29 +967,3 @@ class TradeDocumentForm(FlaskForm): db.session.commit() return self._obj - - -class PrintTagsForm(FlaskForm): - devices = StringField(render_kw={'class': "devicesList d-none"}) - - def validate(self, extra_validators=None): - is_valid = super().validate(extra_validators) - - if not self.devices.data: - return False - - device_ids = self.devices.data.split(",") - self._devices = ( - Device.query.filter(Device.id.in_(device_ids)) - .filter(Device.owner_id == g.user.id) - .distinct() - .all() - ) - - # print only tags that are DHID - dhids = [x.devicehub_id for x in self._devices] - self._tags = ( - Tag.query.filter(Tag.owner_id == g.user.id).filter(Tag.id.in_(dhids)).all() - ) - - return is_valid diff --git a/ereuse_devicehub/inventory/views.py b/ereuse_devicehub/inventory/views.py index a438bf96..f8df5b0a 100644 --- a/ereuse_devicehub/inventory/views.py +++ b/ereuse_devicehub/inventory/views.py @@ -7,7 +7,6 @@ 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 requests.exceptions import ConnectionError from sqlalchemy import or_ from werkzeug.exceptions import NotFound @@ -21,14 +20,12 @@ from ereuse_devicehub.inventory.forms import ( LotForm, NewActionForm, NewDeviceForm, - PrintTagsForm, TagDeviceForm, - TagForm, - TagUnnamedForm, TradeDocumentForm, TradeForm, UploadSnapshotForm, ) +from ereuse_devicehub.label.forms import PrintLabelsForm 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 @@ -119,7 +116,7 @@ class DeviceListMix(GenericMixView): 'form_new_datawipe': form_new_datawipe, 'form_new_trade': form_new_trade, 'form_filter': form_filter, - 'form_print_tags': PrintTagsForm(), + 'form_print_labels': PrintLabelsForm(), 'lot': lot, 'tags': tags, 'list_devices': list_devices, @@ -317,120 +314,6 @@ class DeviceCreateView(GenericMixView): return flask.render_template(self.template_name, **context) -class TagListView(View): - methods = ['GET'] - decorators = [login_required] - template_name = 'inventory/tag_list.html' - - 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) - context = { - 'lots': lots, - 'tags': tags, - 'page_title': 'Tags Management', - 'version': __version__, - } - return flask.render_template(self.template_name, **context) - - -class TagAddView(View): - methods = ['GET', 'POST'] - decorators = [login_required] - template_name = 'inventory/tag_create.html' - - def dispatch_request(self): - lots = Lot.query.filter(Lot.owner_id == current_user.id) - context = {'page_title': 'New Tag', 'lots': lots, 'version': __version__} - form = TagForm() - if form.validate_on_submit(): - form.save() - next_url = url_for('inventory.taglist') - return flask.redirect(next_url) - - return flask.render_template(self.template_name, form=form, **context) - - -class TagAddUnnamedView(View): - methods = ['GET', 'POST'] - decorators = [login_required] - template_name = 'inventory/tag_create_unnamed.html' - - def dispatch_request(self): - lots = Lot.query.filter(Lot.owner_id == current_user.id) - context = { - 'page_title': 'New Unnamed Tag', - 'lots': lots, - 'version': __version__, - } - form = TagUnnamedForm() - if form.validate_on_submit(): - try: - form.save() - except ConnectionError as e: - logger.error( - "Error while trying to connect to tag server: {}".format(e) - ) - msg = ( - "Sorry, we cannot create the unnamed tags requested because " - "some error happens while connecting to the tag server!" - ) - messages.error(msg) - - next_url = url_for('inventory.taglist') - return flask.redirect(next_url) - - return flask.render_template(self.template_name, form=form, **context) - - -class PrintTagsView(View): - """This View is used to print labels from multiple devices""" - - methods = ['POST', 'GET'] - decorators = [login_required] - template_name = 'inventory/print_tags.html' - title = 'Design and implementation of labels' - - def dispatch_request(self): - lots = Lot.query.filter(Lot.owner_id == current_user.id) - context = { - 'lots': lots, - 'page_title': self.title, - 'version': __version__, - 'referrer': request.referrer, - } - - form = PrintTagsForm() - if form.validate_on_submit(): - context['form'] = form - context['tags'] = form._tags - return flask.render_template(self.template_name, **context) - else: - messages.error('Error you need select one or more devices') - - next_url = request.referrer or url_for('inventory.devicelist') - return flask.redirect(next_url) - - -class TagDetailView(View): - decorators = [login_required] - template_name = 'inventory/tag_detail.html' - - def dispatch_request(self, id): - lots = Lot.query.filter(Lot.owner_id == current_user.id) - tag = ( - Tag.query.filter(Tag.owner_id == current_user.id).filter(Tag.id == id).one() - ) - - context = { - 'lots': lots, - 'tag': tag, - 'page_title': '{} Tag'.format(tag.code), - 'version': __version__, - } - return flask.render_template(self.template_name, **context) - - class TagLinkDeviceView(View): methods = ['POST'] decorators = [login_required] @@ -747,14 +630,6 @@ devices.add_url_rule( '/lot//device/add/', view_func=DeviceCreateView.as_view('lot_device_add'), ) -devices.add_url_rule('/tag/', view_func=TagListView.as_view('taglist')) -devices.add_url_rule('/tag/add/', view_func=TagAddView.as_view('tag_add')) -devices.add_url_rule( - '/tag/unnamed/add/', view_func=TagAddUnnamedView.as_view('tag_unnamed_add') -) -devices.add_url_rule( - '/tag//', view_func=TagDetailView.as_view('tag_details') -) devices.add_url_rule( '/tag/devices/add/', view_func=TagLinkDeviceView.as_view('tag_devices_add') ) @@ -765,7 +640,3 @@ devices.add_url_rule( devices.add_url_rule( '/export//', view_func=ExportsView.as_view('export') ) -devices.add_url_rule( - '/tags/print', - view_func=PrintTagsView.as_view('print_tags'), -) diff --git a/ereuse_devicehub/label/__init__.py b/ereuse_devicehub/label/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/ereuse_devicehub/label/views.py b/ereuse_devicehub/label/views.py new file mode 100644 index 00000000..e00a3d41 --- /dev/null +++ b/ereuse_devicehub/label/views.py @@ -0,0 +1,142 @@ +import logging + +import flask +from flask import Blueprint, request, url_for +from flask.views import View +from flask_login import current_user, login_required +from requests.exceptions import ConnectionError + +from ereuse_devicehub import __version__, messages +from ereuse_devicehub.label.forms import PrintLabelsForm, TagForm, TagUnnamedForm +from ereuse_devicehub.resources.lot.models import Lot +from ereuse_devicehub.resources.tag.model import Tag + +label = Blueprint('label', __name__, url_prefix='/label') + +logger = logging.getLogger(__name__) + + +class TagListView(View): + methods = ['GET'] + decorators = [login_required] + template_name = 'label/label_list.html' + + 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) + context = { + 'lots': lots, + 'tags': tags, + 'page_title': 'Tags Management', + 'version': __version__, + } + return flask.render_template(self.template_name, **context) + + +class TagAddView(View): + methods = ['GET', 'POST'] + decorators = [login_required] + template_name = 'label/tag_create.html' + + def dispatch_request(self): + lots = Lot.query.filter(Lot.owner_id == current_user.id) + context = {'page_title': 'New Tag', 'lots': lots, 'version': __version__} + form = TagForm() + if form.validate_on_submit(): + form.save() + next_url = url_for('label.label_list') + return flask.redirect(next_url) + + return flask.render_template(self.template_name, form=form, **context) + + +class TagAddUnnamedView(View): + methods = ['GET', 'POST'] + decorators = [login_required] + template_name = 'label/tag_create_unnamed.html' + + def dispatch_request(self): + lots = Lot.query.filter(Lot.owner_id == current_user.id) + context = { + 'page_title': 'New Unnamed Tag', + 'lots': lots, + 'version': __version__, + } + form = TagUnnamedForm() + if form.validate_on_submit(): + try: + form.save() + except ConnectionError as e: + logger.error( + "Error while trying to connect to tag server: {}".format(e) + ) + msg = ( + "Sorry, we cannot create the unnamed tags requested because " + "some error happens while connecting to the tag server!" + ) + messages.error(msg) + + next_url = url_for('label.label_list') + return flask.redirect(next_url) + + return flask.render_template(self.template_name, form=form, **context) + + +class PrintLabelsView(View): + """This View is used to print labels from multiple devices""" + + methods = ['POST', 'GET'] + decorators = [login_required] + template_name = 'label/print_labels.html' + title = 'Design and implementation of labels' + + def dispatch_request(self): + lots = Lot.query.filter(Lot.owner_id == current_user.id) + context = { + 'lots': lots, + 'page_title': self.title, + 'version': __version__, + 'referrer': request.referrer, + } + + form = PrintLabelsForm() + if form.validate_on_submit(): + context['form'] = form + context['tags'] = form._tags + return flask.render_template(self.template_name, **context) + else: + messages.error('Error you need select one or more devices') + + next_url = request.referrer or url_for('inventory.devicelist') + return flask.redirect(next_url) + + +class LabelDetailView(View): + decorators = [login_required] + template_name = 'label/label_detail.html' + + def dispatch_request(self, id): + lots = Lot.query.filter(Lot.owner_id == current_user.id) + tag = ( + Tag.query.filter(Tag.owner_id == current_user.id).filter(Tag.id == id).one() + ) + + context = { + 'lots': lots, + 'tag': tag, + 'page_title': '{} Tag'.format(tag.code), + 'version': __version__, + } + return flask.render_template(self.template_name, **context) + + +label.add_url_rule('/', view_func=TagListView.as_view('label_list')) +label.add_url_rule('/add/', view_func=TagAddView.as_view('tag_add')) +label.add_url_rule( + '/unnamed/add/', view_func=TagAddUnnamedView.as_view('tag_unnamed_add') +) +label.add_url_rule( + '/print', + view_func=PrintLabelsView.as_view('print_labels'), +) +label.add_url_rule('//', view_func=LabelDetailView.as_view('label_details')) diff --git a/ereuse_devicehub/templates/ereuse_devicehub/base_site.html b/ereuse_devicehub/templates/ereuse_devicehub/base_site.html index 5d87be53..9592978a 100644 --- a/ereuse_devicehub/templates/ereuse_devicehub/base_site.html +++ b/ereuse_devicehub/templates/ereuse_devicehub/base_site.html @@ -181,7 +181,7 @@
  • - diff --git a/ereuse_devicehub/templates/inventory/tag_create_unnamed.html b/ereuse_devicehub/templates/label/tag_create_unnamed.html similarity index 90% rename from ereuse_devicehub/templates/inventory/tag_create_unnamed.html rename to ereuse_devicehub/templates/label/tag_create_unnamed.html index d8eddde0..3e668312 100644 --- a/ereuse_devicehub/templates/inventory/tag_create_unnamed.html +++ b/ereuse_devicehub/templates/label/tag_create_unnamed.html @@ -5,7 +5,7 @@

    {{ title }}

    @@ -49,7 +49,7 @@
  • - Cancel + Cancel
    From 1df6303f21d1cd960df1448575b22ffcfcec3f18 Mon Sep 17 00:00:00 2001 From: Cayo Puigdefabregas Date: Thu, 31 Mar 2022 14:09:26 +0200 Subject: [PATCH 09/33] Jinja2==3.0.3 in requirements --- requirements.txt | 3 +++ 1 file changed, 3 insertions(+) diff --git a/requirements.txt b/requirements.txt index 59375c33..4dadabf0 100644 --- a/requirements.txt +++ b/requirements.txt @@ -15,6 +15,9 @@ Flask-WTF==1.0.0 hashids==1.2.0 inflection==0.3.1 itsdangerous==2.0.1 +# lock Jinja2 version because it's the latest compatible with Flask 1.0.X +# see related info on https://github.com/pallets/jinja/issues/1628 +Jinja2==3.0.3 marshmallow==3.0.0b11 marshmallow-enum==1.4.1 passlib==1.7.1 From 7a532fb02c7504ab2e14b5bf8f90d46943025a9a Mon Sep 17 00:00:00 2001 From: Cayo Puigdefabregas Date: Thu, 31 Mar 2022 16:12:35 +0200 Subject: [PATCH 10/33] change label for labels --- ereuse_devicehub/inventory/views.py | 2 +- .../{label => labels}/__init__.py | 0 ereuse_devicehub/labels/forms.py | 73 +++++++++++++++++++ ereuse_devicehub/{label => labels}/views.py | 28 +++---- .../templates/ereuse_devicehub/base_site.html | 2 +- .../templates/inventory/device_list.html | 4 +- .../{label => labels}/label_detail.html | 2 +- .../{label => labels}/label_list.html | 6 +- .../{label => labels}/print_labels.html | 2 +- .../{label => labels}/tag_create.html | 4 +- .../{label => labels}/tag_create_unnamed.html | 4 +- 11 files changed, 100 insertions(+), 27 deletions(-) rename ereuse_devicehub/{label => labels}/__init__.py (100%) create mode 100644 ereuse_devicehub/labels/forms.py rename ereuse_devicehub/{label => labels}/views.py (83%) rename ereuse_devicehub/templates/{label => labels}/label_detail.html (97%) rename ereuse_devicehub/templates/{label => labels}/label_list.html (88%) rename ereuse_devicehub/templates/{label => labels}/print_labels.html (97%) rename ereuse_devicehub/templates/{label => labels}/tag_create.html (90%) rename ereuse_devicehub/templates/{label => labels}/tag_create_unnamed.html (90%) diff --git a/ereuse_devicehub/inventory/views.py b/ereuse_devicehub/inventory/views.py index f8df5b0a..66b28563 100644 --- a/ereuse_devicehub/inventory/views.py +++ b/ereuse_devicehub/inventory/views.py @@ -25,7 +25,7 @@ from ereuse_devicehub.inventory.forms import ( TradeForm, UploadSnapshotForm, ) -from ereuse_devicehub.label.forms import PrintLabelsForm +from ereuse_devicehub.labels.forms import PrintLabelsForm 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 diff --git a/ereuse_devicehub/label/__init__.py b/ereuse_devicehub/labels/__init__.py similarity index 100% rename from ereuse_devicehub/label/__init__.py rename to ereuse_devicehub/labels/__init__.py diff --git a/ereuse_devicehub/labels/forms.py b/ereuse_devicehub/labels/forms.py new file mode 100644 index 00000000..cd4b5bec --- /dev/null +++ b/ereuse_devicehub/labels/forms.py @@ -0,0 +1,73 @@ +from flask import g +from flask_wtf import FlaskForm +from wtforms import IntegerField, StringField, validators + +from ereuse_devicehub.db import db +from ereuse_devicehub.resources.device.models import Device +from ereuse_devicehub.resources.tag.model import Tag + + +class TagForm(FlaskForm): + code = StringField('Code', [validators.length(min=1)]) + + def validate(self, extra_validators=None): + error = ["This value is being used"] + is_valid = super().validate(extra_validators) + if not is_valid: + return False + tag = Tag.query.filter(Tag.id == self.code.data).all() + if tag: + self.code.errors = error + return False + + return True + + def save(self): + self.instance = Tag(id=self.code.data) + db.session.add(self.instance) + db.session.commit() + return self.instance + + def remove(self): + if not self.instance.device and not self.instance.provider: + self.instance.delete() + db.session.commit() + return self.instance + + +class TagUnnamedForm(FlaskForm): + amount = IntegerField('amount') + + def save(self): + num = self.amount.data + tags_id, _ = g.tag_provider.post('/', {}, query=[('num', num)]) + tags = [Tag(id=tag_id, provider=g.inventory.tag_provider) for tag_id in tags_id] + db.session.add_all(tags) + db.session.commit() + return tags + + +class PrintLabelsForm(FlaskForm): + devices = StringField(render_kw={'class': "devicesList d-none"}) + + def validate(self, extra_validators=None): + is_valid = super().validate(extra_validators) + + if not self.devices.data: + return False + + device_ids = self.devices.data.split(",") + self._devices = ( + Device.query.filter(Device.id.in_(device_ids)) + .filter(Device.owner_id == g.user.id) + .distinct() + .all() + ) + + # print only tags that are DHID + dhids = [x.devicehub_id for x in self._devices] + self._tags = ( + Tag.query.filter(Tag.owner_id == g.user.id).filter(Tag.id.in_(dhids)).all() + ) + + return is_valid diff --git a/ereuse_devicehub/label/views.py b/ereuse_devicehub/labels/views.py similarity index 83% rename from ereuse_devicehub/label/views.py rename to ereuse_devicehub/labels/views.py index e00a3d41..445a4eb8 100644 --- a/ereuse_devicehub/label/views.py +++ b/ereuse_devicehub/labels/views.py @@ -7,11 +7,11 @@ from flask_login import current_user, login_required from requests.exceptions import ConnectionError from ereuse_devicehub import __version__, messages -from ereuse_devicehub.label.forms import PrintLabelsForm, TagForm, TagUnnamedForm +from ereuse_devicehub.labels.forms import PrintLabelsForm, TagForm, TagUnnamedForm from ereuse_devicehub.resources.lot.models import Lot from ereuse_devicehub.resources.tag.model import Tag -label = Blueprint('label', __name__, url_prefix='/label') +labels = Blueprint('labels', __name__, url_prefix='/labels') logger = logging.getLogger(__name__) @@ -19,7 +19,7 @@ logger = logging.getLogger(__name__) class TagListView(View): methods = ['GET'] decorators = [login_required] - template_name = 'label/label_list.html' + template_name = 'labels/label_list.html' def dispatch_request(self): lots = Lot.query.filter(Lot.owner_id == current_user.id) @@ -36,7 +36,7 @@ class TagListView(View): class TagAddView(View): methods = ['GET', 'POST'] decorators = [login_required] - template_name = 'label/tag_create.html' + template_name = 'labels/tag_create.html' def dispatch_request(self): lots = Lot.query.filter(Lot.owner_id == current_user.id) @@ -44,7 +44,7 @@ class TagAddView(View): form = TagForm() if form.validate_on_submit(): form.save() - next_url = url_for('label.label_list') + next_url = url_for('labels.label_list') return flask.redirect(next_url) return flask.render_template(self.template_name, form=form, **context) @@ -53,7 +53,7 @@ class TagAddView(View): class TagAddUnnamedView(View): methods = ['GET', 'POST'] decorators = [login_required] - template_name = 'label/tag_create_unnamed.html' + template_name = 'labels/tag_create_unnamed.html' def dispatch_request(self): lots = Lot.query.filter(Lot.owner_id == current_user.id) @@ -76,7 +76,7 @@ class TagAddUnnamedView(View): ) messages.error(msg) - next_url = url_for('label.label_list') + next_url = url_for('labels.label_list') return flask.redirect(next_url) return flask.render_template(self.template_name, form=form, **context) @@ -87,7 +87,7 @@ class PrintLabelsView(View): methods = ['POST', 'GET'] decorators = [login_required] - template_name = 'label/print_labels.html' + template_name = 'labels/print_labels.html' title = 'Design and implementation of labels' def dispatch_request(self): @@ -113,7 +113,7 @@ class PrintLabelsView(View): class LabelDetailView(View): decorators = [login_required] - template_name = 'label/label_detail.html' + template_name = 'labels/label_detail.html' def dispatch_request(self, id): lots = Lot.query.filter(Lot.owner_id == current_user.id) @@ -130,13 +130,13 @@ class LabelDetailView(View): return flask.render_template(self.template_name, **context) -label.add_url_rule('/', view_func=TagListView.as_view('label_list')) -label.add_url_rule('/add/', view_func=TagAddView.as_view('tag_add')) -label.add_url_rule( +labels.add_url_rule('/', view_func=TagListView.as_view('label_list')) +labels.add_url_rule('/add/', view_func=TagAddView.as_view('tag_add')) +labels.add_url_rule( '/unnamed/add/', view_func=TagAddUnnamedView.as_view('tag_unnamed_add') ) -label.add_url_rule( +labels.add_url_rule( '/print', view_func=PrintLabelsView.as_view('print_labels'), ) -label.add_url_rule('//', view_func=LabelDetailView.as_view('label_details')) +labels.add_url_rule('//', view_func=LabelDetailView.as_view('label_details')) diff --git a/ereuse_devicehub/templates/ereuse_devicehub/base_site.html b/ereuse_devicehub/templates/ereuse_devicehub/base_site.html index 9592978a..d55a1065 100644 --- a/ereuse_devicehub/templates/ereuse_devicehub/base_site.html +++ b/ereuse_devicehub/templates/ereuse_devicehub/base_site.html @@ -181,7 +181,7 @@
  • - diff --git a/ereuse_devicehub/templates/label/tag_create_unnamed.html b/ereuse_devicehub/templates/labels/tag_create_unnamed.html similarity index 90% rename from ereuse_devicehub/templates/label/tag_create_unnamed.html rename to ereuse_devicehub/templates/labels/tag_create_unnamed.html index 3e668312..fa59b44f 100644 --- a/ereuse_devicehub/templates/label/tag_create_unnamed.html +++ b/ereuse_devicehub/templates/labels/tag_create_unnamed.html @@ -5,7 +5,7 @@

    {{ title }}

    @@ -49,7 +49,7 @@
  • - Cancel + Cancel
    From 3967d6a0c4d6b44d4906909e4bf6398da9612135 Mon Sep 17 00:00:00 2001 From: RubenPX Date: Mon, 4 Apr 2022 12:34:33 +0200 Subject: [PATCH 11/33] Convert readme to markdown --- README.rst | 159 ----------------------------------------------------- readme.md | 140 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 140 insertions(+), 159 deletions(-) delete mode 100644 README.rst create mode 100644 readme.md diff --git a/README.rst b/README.rst deleted file mode 100644 index 1eba57c5..00000000 --- a/README.rst +++ /dev/null @@ -1,159 +0,0 @@ -Devicehub -######### -Devicehub is a distributed IT Asset Management System focused in reusing -devices, created under the project -`eReuse.org `__. - -This README explains how to install and use Devicehub. -`The documentation `_ explains the concepts -and the API. - -Devicehub is built with `Teal `__ and -`Flask `__. - -Installing -********** -The requirements are: - -- Python 3.7.3 or higher. In debian 10 is ``# apt install python3``. -- `PostgreSQL 11 or higher `__. -- Weasyprint - `dependencies `__. - -Install Devicehub with *pip*: -``pip3 install -U -r requirements.txt -e .``. - -Running -******* -Create a PostgreSQL database called *devicehub* by running -`create-db `__: - -- In Linux, execute the following two commands (adapt them to your distro): - - 1. ``sudo su - postgres``. - 2. ``bash examples/create-db.sh devicehub dhub``, and password - ``ereuse``. - -- In MacOS: ``bash examples/create-db.sh devicehub dhub``, and password - ``ereuse``. - -Configure project using environment file (you can use provided example as quickstart): -.. code:: bash - - $ cp examples/env.example .env - -Using the `dh` tool for set up with one or multiple inventories. -Create the tables in the database by executing: - -.. code:: bash - - $ export dhi=dbtest; dh inv add --common --name dbtest - -Finally, run the app: - -.. code:: bash - - $ export dhi=dbtest;dh run --debugger - -The error ‘bdist_wheel’ can happen when you work with a *virtual environment*. -To fix it, install in the *virtual environment* wheel -package. ``pip3 install wheel`` - -Multiple instances ------------------- -Devicehub can run as a single inventory or with multiple inventories, -each inventory being an instance of the ``devicehub``. To add a new inventory -execute: - -.. code:: bash - - $ export dhi=dbtest; dh inv add --name dbtest - -Note: The ``dh`` command is like ``flask``, but -it allows you to create and delete instances, and interface to them -directly. - - -Testing -******* -1. ``git clone`` this project. -2. Create a database for testing executing ``create-db.sh`` like the - normal installation but changing the first parameter from - ``devicehub`` to ``dh_test``: ``create-db.sh dh_test dhub`` and - password ``ereuse``. -3. Execute at the root folder of the project ``python3 setup.py test``. - - -Migrations -********** -At this stage, migration files are created manually. -Set up the database: - -.. code:: bash - - $ sudo su - postgres - $ bash $PATH_TO_DEVIHUBTEAL/examples/create-db.sh devicehub dhub - -Initialize the database: - -.. code:: bash - - $ export dhi=dbtest; dh inv add --common --name dbtest - -This command will create the schemas, tables in the specified database. -Then we need to stamp the initial migration. - -.. code:: bash - - $ alembic stamp head - - -This command will set the revision **fbb7e2a0cde0_initial** as our initial migration. -For more info in migration stamping please see https://alembic.sqlalchemy.org/en/latest/cookbook.html - - -Whenever a change needed eg to create a new schema, alter an existing table, column or perform any -operation on tables, create a new revision file: - -.. code:: bash - - $ alembic revision -m "A table change" - -This command will create a new revision file with name `_a_table_change`. -Edit the generated file with the necessary operations to perform the migration: - -.. code:: bash - - $ alembic edit - -Apply migrations using: - -.. code:: bash - - $ alembic -x inventory=dbtest upgrade head - -Then to go back to previous db version: - -.. code:: bash - - $ alembic -x inventory=dbtest downgrade - -To see a full list of migrations use - -.. code:: bash - - $ alembic history - - -Generating the docs -******************* - -1. ``git clone`` this project. -2. Install plantuml. In Debian 9 is ``# apt install plantuml``. -3. Execute ``pip3 install -e .[docs]`` in the project root folder. -4. Go to ``/docs`` and execute ``make html``. - Repeat this step to generate new docs. - -To auto-generate the docs do ``pip3 install -e .[docs-auto]``, then -execute, in the root folder of the project -``sphinx-autobuild docs docs/_build/html``. diff --git a/readme.md b/readme.md new file mode 100644 index 00000000..169bbe0d --- /dev/null +++ b/readme.md @@ -0,0 +1,140 @@ +#Devicehub + +Devicehub is a distributed IT Asset Management System focused in reusing devices, created under the project [eReuse.org](https://www.ereuse.org) + +This README explains how to install and use Devicehub. [The documentation](http://devicehub.ereuse.org) explains the concepts and the API. + +Devicehub is built with [Teal](https://github.com/ereuse/teal) and [Flask](http://flask.pocoo.org). + +# Installing +The requirements are: + +- Python 3.7.3 or higher. In debian 10 is `# apt install python3`. +- [PostgreSQL 11 or higher](https://www.postgresql.org/download/). +- Weasyprint [dependencie](http://weasyprint.readthedocs.io/en/stable/install.html>) + +Install Devicehub with *pip*: `pip3 install -U -r requirements.txt -e .` + +# Running +Create a PostgreSQL database called *devicehub* by running [create-db](examples/create-db.sh): + +- In Linux, execute the following two commands (adapt them to your distro): + + 1. ``sudo su - postgres``. + 2. ``bash examples/create-db.sh devicehub dhub``, and password ``ereuse``. + +- In MacOS: `bash examples/create-db.sh devicehub dhub`, and password `ereuse`. + +Configure project using environment file (you can use provided example as quickstart): +```bash +$ cp examples/env.example .env +``` + +Using the `dh` tool for set up with one or multiple inventories. +Create the tables in the database by executing: + +```bash +$ export dhi=dbtest; dh inv add --common --name dbtest +``` + +Finally, run the app: + +```bash +$ export dhi=dbtest;dh run --debugger +``` + +The error ‘bdist_wheel’ can happen when you work with a *virtual environment*. +To fix it, install in the *virtual environment* wheel +package. ``pip3 install wheel`` + +## Multiple instances + +Devicehub can run as a single inventory or with multiple inventories, +each inventory being an instance of the ``devicehub``. To add a new inventory +execute: +```bash +$ export dhi=dbtest; dh inv add --name dbtest +``` + +Note: The ``dh`` command is like ``flask``, but it allows you to create and delete instances, and interface to them +directly. + + +# Testing + +1. `git clone` this project. +2. Create a database for testing executing `create-db.sh` like the + normal installation but changing the first parameter from + `devicehub` to `dh_test`: `create-db.sh dh_test dhub` and + password `ereuse`. +3. Execute at the root folder of the project `python3 setup.py test`. + + +# Migrations + +At this stage, migration files are created manually. +Set up the database: + +```bash +$ sudo su - postgres +$ bash $PATH_TO_DEVIHUBTEAL/examples/create-db.sh devicehub dhub +``` + +Initialize the database: + +```bash +$ export dhi=dbtest; dh inv add --common --name dbtest +``` + +This command will create the schemas, tables in the specified database. +Then we need to stamp the initial migration. + +```bash +$ alembic stamp head +``` + + +This command will set the revision **fbb7e2a0cde0_initial** as our initial migration. +For more info in migration stamping please see https://alembic.sqlalchemy.org/en/latest/cookbook.html + + +Whenever a change needed eg to create a new schema, alter an existing table, column or perform any +operation on tables, create a new revision file: + +```bash +$ alembic revision -m "A table change" +``` + +This command will create a new revision file with name `_a_table_change`. +Edit the generated file with the necessary operations to perform the migration: + +```bash +$ alembic edit +``` + +Apply migrations using: + +```bash +$ alembic -x inventory=dbtest upgrade head +``` +Then to go back to previous db version: + +```bash +$ alembic -x inventory=dbtest downgrade +``` + +To see a full list of migrations use + +```bash +$ alembic history +``` + +## Generating the docs + + +1. `git clone` this project. +2. Install plantuml. In Debian 9 is `# apt install plantuml`. +3. Execute `pip3 install -e .[docs]` in the project root folder. +4. Go to `/docs` and execute `make html`. Repeat this step to generate new docs. + +To auto-generate the docs do `pip3 install -e .[docs-auto]`, then execute, in the root folder of the project `sphinx-autobuild docs docs/_build/html`. From e1821c3a8e305d07b2936b709cd57c96612ee328 Mon Sep 17 00:00:00 2001 From: RubenPX Date: Mon, 4 Apr 2022 12:35:32 +0200 Subject: [PATCH 12/33] Comment some app.py lines that cause an error Request by @slamora --- examples/app.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/app.py b/examples/app.py index 91b48a59..b0f3bad0 100644 --- a/examples/app.py +++ b/examples/app.py @@ -18,5 +18,5 @@ app.register_blueprint(devices) # NOTE: enable by blueprint to exclude API views # TODO(@slamora: enable by default & exclude API views when decouple of Teal is completed csrf = CSRFProtect(app) -csrf.protect(core) -csrf.protect(devices) +# csrf.protect(core) +# csrf.protect(devices) From 4656bbf8723f9158bf1ada011564c0502580fb69 Mon Sep 17 00:00:00 2001 From: RubenPX Date: Mon, 4 Apr 2022 12:35:52 +0200 Subject: [PATCH 13/33] add developement setup guide --- developement-setup.md | 55 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 55 insertions(+) create mode 100644 developement-setup.md diff --git a/developement-setup.md b/developement-setup.md new file mode 100644 index 00000000..d2684fbf --- /dev/null +++ b/developement-setup.md @@ -0,0 +1,55 @@ +# Setup developement project + +## Installing + +complete this steps from readme +- #installing + +## Setup project + +Create a PostgreSQL database called devicehub by running [create-db](examples/create-db.sh): + +- Start postgresDB +- `bash examples/create-db.sh devicehub dhub, and password ereuse.` +- `cp examples/env.example .env` + +Create a secretkey and add into `.env` + +```bash +echo "SECRET_KEY=$(python3 -c 'import secrets; print(secrets.token_hex())')" >> .env +``` + +Using the dh tool for set up with one or multiple inventories. Create the tables in the database by executing: + +```bash +export dhi=dbtest; dh inv add --common --name dbtest +``` + +Create a demo table + +```bash +export dhi=dbtest; dh dummy +``` + +copy `examples/app.py` to project directory: +```bash +copy examples/app.py . +``` + +## Run project + +Run the app + +```bash +export FLASK_APP=app.py; export FLASK_ENV=development; flask run --debugger +``` + +Finally login into `localhost:5000/login/` + +- User: user@dhub.com +- Pass: 1234 + +## Troubleshooting + +- If when execute dh command it thows an error, install this dependencies in your distro + - `sudo apt install python3-pip python3-cffi python3-brotli libpango-1.0-0 libpangoft2-1.0-0` From 960a4dc85f3a41c73b9358bb333194ee645a5eef Mon Sep 17 00:00:00 2001 From: Santiago Lamora Date: Mon, 4 Apr 2022 13:41:36 +0200 Subject: [PATCH 14/33] Enable labels Blueprint on examples/app.py --- examples/app.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/examples/app.py b/examples/app.py index 91b48a59..da6e2202 100644 --- a/examples/app.py +++ b/examples/app.py @@ -8,11 +8,13 @@ from flask_wtf.csrf import CSRFProtect from ereuse_devicehub.config import DevicehubConfig 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 app = Devicehub(inventory=DevicehubConfig.DB_SCHEMA) app.register_blueprint(core) app.register_blueprint(devices) +app.register_blueprint(labels) # configure & enable CSRF of Flask-WTF # NOTE: enable by blueprint to exclude API views From 3bb09c654cecbe3c21892984afc087c1424df313 Mon Sep 17 00:00:00 2001 From: Santiago Lamora Date: Mon, 4 Apr 2022 13:43:09 +0200 Subject: [PATCH 15/33] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4e7d9b0b..34987193 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ ml). ## master ## testing +- [changed] #2970 #2971 Print DHID-QR label for selected devices. ## [2.0.0] - 2022-03-15 First server render HTML version. Completely rewrites views of angular JS client on flask. From 3119658098902813b74d99cf80d02fc25fe58443 Mon Sep 17 00:00:00 2001 From: RubenPX Date: Mon, 4 Apr 2022 17:17:07 +0200 Subject: [PATCH 16/33] Update developement-setup.md Co-authored-by: Santiago L --- developement-setup.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/developement-setup.md b/developement-setup.md index d2684fbf..1bb00f18 100644 --- a/developement-setup.md +++ b/developement-setup.md @@ -2,8 +2,7 @@ ## Installing -complete this steps from readme -- #installing +complete this steps from [README - Installing](README.md#installing) ## Setup project From 584f8b990ac117e3b0ff0b0d86fb4ca4df2048b7 Mon Sep 17 00:00:00 2001 From: RubenPX Date: Mon, 4 Apr 2022 17:23:25 +0200 Subject: [PATCH 17/33] change config to set readme.md instead of rst Necesary to build wheel package --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 6a0db9b9..9dbedba9 100644 --- a/setup.py +++ b/setup.py @@ -24,7 +24,7 @@ setup( packages=find_packages(), include_package_data=True, python_requires='>=3.7.3', - long_description=Path('README.rst').read_text('utf8'), + long_description=Path('README.md').read_text('utf8'), install_requires=[ 'teal>=0.2.0a38', # teal always first 'click', From 6b7cf88a2f6c736a7cd614bc02074b5c804516b8 Mon Sep 17 00:00:00 2001 From: RubenPX Date: Mon, 4 Apr 2022 17:25:39 +0200 Subject: [PATCH 18/33] fix typo developement to development --- developement-setup.md => development-setup.md | 108 +++++++++--------- 1 file changed, 54 insertions(+), 54 deletions(-) rename developement-setup.md => development-setup.md (95%) diff --git a/developement-setup.md b/development-setup.md similarity index 95% rename from developement-setup.md rename to development-setup.md index 1bb00f18..4743f2a9 100644 --- a/developement-setup.md +++ b/development-setup.md @@ -1,54 +1,54 @@ -# Setup developement project - -## Installing - -complete this steps from [README - Installing](README.md#installing) - -## Setup project - -Create a PostgreSQL database called devicehub by running [create-db](examples/create-db.sh): - -- Start postgresDB -- `bash examples/create-db.sh devicehub dhub, and password ereuse.` -- `cp examples/env.example .env` - -Create a secretkey and add into `.env` - -```bash -echo "SECRET_KEY=$(python3 -c 'import secrets; print(secrets.token_hex())')" >> .env -``` - -Using the dh tool for set up with one or multiple inventories. Create the tables in the database by executing: - -```bash -export dhi=dbtest; dh inv add --common --name dbtest -``` - -Create a demo table - -```bash -export dhi=dbtest; dh dummy -``` - -copy `examples/app.py` to project directory: -```bash -copy examples/app.py . -``` - -## Run project - -Run the app - -```bash -export FLASK_APP=app.py; export FLASK_ENV=development; flask run --debugger -``` - -Finally login into `localhost:5000/login/` - -- User: user@dhub.com -- Pass: 1234 - -## Troubleshooting - -- If when execute dh command it thows an error, install this dependencies in your distro - - `sudo apt install python3-pip python3-cffi python3-brotli libpango-1.0-0 libpangoft2-1.0-0` +# Setup developement project + +## Installing + +complete this steps from [README - Installing](README.md#installing) + +## Setup project + +Create a PostgreSQL database called devicehub by running [create-db](examples/create-db.sh): + +- Start postgresDB +- `bash examples/create-db.sh devicehub dhub, and password ereuse.` +- `cp examples/env.example .env` + +Create a secretkey and add into `.env` + +```bash +echo "SECRET_KEY=$(python3 -c 'import secrets; print(secrets.token_hex())')" >> .env +``` + +Using the dh tool for set up with one or multiple inventories. Create the tables in the database by executing: + +```bash +export dhi=dbtest; dh inv add --common --name dbtest +``` + +Create a demo table + +```bash +export dhi=dbtest; dh dummy +``` + +copy `examples/app.py` to project directory: +```bash +copy examples/app.py . +``` + +## Run project + +Run the app + +```bash +export FLASK_APP=app.py; export FLASK_ENV=development; flask run --debugger +``` + +Finally login into `localhost:5000/login/` + +- User: user@dhub.com +- Pass: 1234 + +## Troubleshooting + +- If when execute dh command it thows an error, install this dependencies in your distro + - `sudo apt install python3-pip python3-cffi python3-brotli libpango-1.0-0 libpangoft2-1.0-0` From c5f006d40fd07ab7b60ccdf5e436550a0f81b846 Mon Sep 17 00:00:00 2001 From: RubenPX Date: Mon, 4 Apr 2022 17:45:04 +0200 Subject: [PATCH 19/33] fix troubleshooting and link --- development-setup.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) mode change 100644 => 100755 development-setup.md diff --git a/development-setup.md b/development-setup.md old mode 100644 new mode 100755 index 4743f2a9..c900574a --- a/development-setup.md +++ b/development-setup.md @@ -51,4 +51,4 @@ Finally login into `localhost:5000/login/` ## Troubleshooting - If when execute dh command it thows an error, install this dependencies in your distro - - `sudo apt install python3-pip python3-cffi python3-brotli libpango-1.0-0 libpangoft2-1.0-0` + - `sudo apt install -y libpango1.0-0 libcairo2 libpq-dev` From 57e35c016fc397c9d76fa94b1e4acf574f0ae991 Mon Sep 17 00:00:00 2001 From: RubenPX Date: Mon, 4 Apr 2022 17:59:51 +0200 Subject: [PATCH 20/33] fix link readme (reviewed) --- readme.md | 20 +++++++------------- 1 file changed, 7 insertions(+), 13 deletions(-) diff --git a/readme.md b/readme.md index 169bbe0d..37c85957 100644 --- a/readme.md +++ b/readme.md @@ -11,7 +11,7 @@ The requirements are: - Python 3.7.3 or higher. In debian 10 is `# apt install python3`. - [PostgreSQL 11 or higher](https://www.postgresql.org/download/). -- Weasyprint [dependencie](http://weasyprint.readthedocs.io/en/stable/install.html>) +- Weasyprint [dependencie](http://weasyprint.readthedocs.io/en/stable/install.html) Install Devicehub with *pip*: `pip3 install -U -r requirements.txt -e .` @@ -20,8 +20,8 @@ Create a PostgreSQL database called *devicehub* by running [create-db](examples/ - In Linux, execute the following two commands (adapt them to your distro): - 1. ``sudo su - postgres``. - 2. ``bash examples/create-db.sh devicehub dhub``, and password ``ereuse``. + 1. `sudo su - postgres`. + 2. `bash examples/create-db.sh devicehub dhub`, and password `ereuse`. - In MacOS: `bash examples/create-db.sh devicehub dhub`, and password `ereuse`. @@ -45,28 +45,22 @@ $ export dhi=dbtest;dh run --debugger The error ‘bdist_wheel’ can happen when you work with a *virtual environment*. To fix it, install in the *virtual environment* wheel -package. ``pip3 install wheel`` +package. `pip3 install wheel` ## Multiple instances -Devicehub can run as a single inventory or with multiple inventories, -each inventory being an instance of the ``devicehub``. To add a new inventory -execute: +Devicehub can run as a single inventory or with multiple inventories, each inventory being an instance of the `devicehub`. To add a new inventory execute: ```bash $ export dhi=dbtest; dh inv add --name dbtest ``` -Note: The ``dh`` command is like ``flask``, but it allows you to create and delete instances, and interface to them -directly. +Note: The `dh` command is like `flask`, but it allows you to create and delete instances, and interface to them directly. # Testing 1. `git clone` this project. -2. Create a database for testing executing `create-db.sh` like the - normal installation but changing the first parameter from - `devicehub` to `dh_test`: `create-db.sh dh_test dhub` and - password `ereuse`. +2. Create a database for testing executing `create-db.sh` like the normal installation but changing the first parameter from `devicehub` to `dh_test`: `create-db.sh dh_test dhub` and password `ereuse`. 3. Execute at the root folder of the project `python3 setup.py test`. From 348970946bc38df93d4aea2f7add9da176f51057 Mon Sep 17 00:00:00 2001 From: Santiago Lamora Date: Tue, 5 Apr 2022 10:36:04 +0200 Subject: [PATCH 21/33] Fix login_manager.user_loader Also redirect unathorized requests to login view --- ereuse_devicehub/devicehub.py | 170 ++++++++++++++++++++++------------ 1 file changed, 110 insertions(+), 60 deletions(-) diff --git a/ereuse_devicehub/devicehub.py b/ereuse_devicehub/devicehub.py index 7163173d..520ca132 100644 --- a/ereuse_devicehub/devicehub.py +++ b/ereuse_devicehub/devicehub.py @@ -10,7 +10,7 @@ from ereuse_utils.session import DevicehubClient from flask import _app_ctx_stack, g from flask_login import LoginManager, current_user from flask_sqlalchemy import SQLAlchemy -from teal.db import SchemaSQLAlchemy +from teal.db import ResourceNotFound, SchemaSQLAlchemy from teal.teal import Teal from ereuse_devicehub.auth import Auth @@ -29,32 +29,49 @@ class Devicehub(Teal): Dummy = Dummy jinja_environment = Environment - def __init__(self, - inventory: str, - config: DevicehubConfig = DevicehubConfig(), - db: SQLAlchemy = db, - import_name=__name__.split('.')[0], - static_url_path=None, - static_folder='static', - static_host=None, - host_matching=False, - subdomain_matching=False, - template_folder='templates', - instance_path=None, - instance_relative_config=False, - root_path=None, - Auth: Type[Auth] = Auth): + def __init__( + self, + inventory: str, + config: DevicehubConfig = DevicehubConfig(), + db: SQLAlchemy = db, + import_name=__name__.split('.')[0], + static_url_path=None, + static_folder='static', + static_host=None, + host_matching=False, + subdomain_matching=False, + template_folder='templates', + instance_path=None, + instance_relative_config=False, + root_path=None, + Auth: Type[Auth] = Auth, + ): assert inventory - super().__init__(config, db, inventory, import_name, static_url_path, static_folder, - static_host, - host_matching, subdomain_matching, template_folder, instance_path, - instance_relative_config, root_path, False, Auth) + super().__init__( + config, + db, + inventory, + import_name, + static_url_path, + static_folder, + static_host, + host_matching, + subdomain_matching, + template_folder, + instance_path, + instance_relative_config, + root_path, + False, + Auth, + ) self.id = inventory """The Inventory ID of this instance. In Teal is the app.schema.""" self.dummy = Dummy(self) - @self.cli.group(short_help='Inventory management.', - help='Manages the inventory {}.'.format(os.environ.get('dhi'))) + @self.cli.group( + short_help='Inventory management.', + help='Manages the inventory {}.'.format(os.environ.get('dhi')), + ) def inv(): pass @@ -69,43 +86,68 @@ class Devicehub(Teal): # configure Flask-Login login_manager = LoginManager() login_manager.init_app(self) + login_manager.login_view = "core.login" @login_manager.user_loader def load_user(user_id): - return User.query.get(user_id) + # TODO(@slamora) refactor when teal library has been drop. + # `load_user` expects None if the user ID is invalid or the + # session has expired so we need to handle Exception raised + # by teal (it's overriding default behaviour of flask-sqlalchemy + # which already returns None) + try: + return User.query.get(user_id) + except ResourceNotFound: + return None # noinspection PyMethodOverriding - @click.option('--name', '-n', - default='Test 1', - help='The human name of the inventory.') - @click.option('--org-name', '-on', - default='My Organization', - help='The name of the default organization that owns this inventory.') - @click.option('--org-id', '-oi', - default='foo-bar', - help='The Tax ID of the organization.') - @click.option('--tag-url', '-tu', - type=ereuse_utils.cli.URL(scheme=True, host=True, path=False), - default='http://example.com', - help='The base url (scheme and host) of the tag provider.') - @click.option('--tag-token', '-tt', - type=click.UUID, - default='899c794e-1737-4cea-9232-fdc507ab7106', - help='The token provided by the tag provider. It is an UUID.') - @click.option('--erase/--no-erase', - default=False, - help='Delete the schema before? ' - 'If --common is set this includes the common database.') - @click.option('--common/--no-common', - default=False, - help='Creates common databases. Only execute if the database is empty.') - def init_db(self, name: str, - org_name: str, - org_id: str, - tag_url: boltons.urlutils.URL, - tag_token: uuid.UUID, - erase: bool, - common: bool): + @click.option( + '--name', '-n', default='Test 1', help='The human name of the inventory.' + ) + @click.option( + '--org-name', + '-on', + default='My Organization', + help='The name of the default organization that owns this inventory.', + ) + @click.option( + '--org-id', '-oi', default='foo-bar', help='The Tax ID of the organization.' + ) + @click.option( + '--tag-url', + '-tu', + type=ereuse_utils.cli.URL(scheme=True, host=True, path=False), + default='http://example.com', + help='The base url (scheme and host) of the tag provider.', + ) + @click.option( + '--tag-token', + '-tt', + type=click.UUID, + default='899c794e-1737-4cea-9232-fdc507ab7106', + help='The token provided by the tag provider. It is an UUID.', + ) + @click.option( + '--erase/--no-erase', + default=False, + help='Delete the schema before? ' + 'If --common is set this includes the common database.', + ) + @click.option( + '--common/--no-common', + default=False, + help='Creates common databases. Only execute if the database is empty.', + ) + def init_db( + self, + name: str, + org_name: str, + org_id: str, + tag_url: boltons.urlutils.URL, + tag_token: uuid.UUID, + erase: bool, + common: bool, + ): """Creates an inventory. This creates the database and adds the inventory to the @@ -120,10 +162,14 @@ class Devicehub(Teal): with click_spinner.spinner(): if erase: self.db.drop_all(common_schema=common) - assert not db.has_schema(self.id), 'Schema {} already exists.'.format(self.id) + assert not db.has_schema(self.id), 'Schema {} already exists.'.format( + self.id + ) exclude_schema = 'common' if not common else None self._init_db(exclude_schema=exclude_schema) - InventoryDef.set_inventory_config(name, org_name, org_id, tag_url, tag_token) + InventoryDef.set_inventory_config( + name, org_name, org_id, tag_url, tag_token + ) DeviceSearch.set_all_devices_tokens_if_empty(self.db.session) self._init_resources(exclude_schema=exclude_schema) self.db.session.commit() @@ -138,8 +184,11 @@ class Devicehub(Teal): return True - @click.confirmation_option(prompt='Are you sure you want to delete the inventory {}?' - .format(os.environ.get('dhi'))) + @click.confirmation_option( + prompt='Are you sure you want to delete the inventory {}?'.format( + os.environ.get('dhi') + ) + ) def delete_inventory(self): """Erases an inventory. @@ -161,8 +210,9 @@ class Devicehub(Teal): def _prepare_request(self): """Prepares request stuff.""" inv = g.inventory = Inventory.current # type: Inventory - g.tag_provider = DevicehubClient(base_url=inv.tag_provider, - token=DevicehubClient.encode_token(inv.tag_token)) + g.tag_provider = DevicehubClient( + base_url=inv.tag_provider, token=DevicehubClient.encode_token(inv.tag_token) + ) # NOTE: models init methods expects that current user is # available on g.user (e.g. to initialize object owner) g.user = current_user From 66afb7b683c16bd0ab99cc336ad9430834976c07 Mon Sep 17 00:00:00 2001 From: Santiago Lamora Date: Tue, 5 Apr 2022 10:49:27 +0200 Subject: [PATCH 22/33] Configure index route (redirect to login) --- ereuse_devicehub/views.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/ereuse_devicehub/views.py b/ereuse_devicehub/views.py index 3ca1db3c..0c9b4361 100644 --- a/ereuse_devicehub/views.py +++ b/ereuse_devicehub/views.py @@ -11,6 +11,11 @@ from ereuse_devicehub.utils import is_safe_url core = Blueprint('core', __name__) +@core.route("/") +def index(): + return flask.redirect(flask.url_for('core.login')) + + class LoginView(View): methods = ['GET', 'POST'] template_name = 'ereuse_devicehub/user_login.html' From 88751b46bdd0143ec4790daab29de484f01fb355 Mon Sep 17 00:00:00 2001 From: Santiago Lamora Date: Tue, 5 Apr 2022 11:03:18 +0200 Subject: [PATCH 23/33] Update CHANGELOG.md - Add reference to fixed PR - Fix reference to PR instead of Taige --- CHANGELOG.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 34987193..542b5491 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,7 +8,8 @@ ml). ## master ## testing -- [changed] #2970 #2971 Print DHID-QR label for selected devices. +- [changed] #211 Print DHID-QR label for selected devices. +- [fixed] #214 Login workflow ## [2.0.0] - 2022-03-15 First server render HTML version. Completely rewrites views of angular JS client on flask. From b46551321266e96874d76063adea767fab809ae4 Mon Sep 17 00:00:00 2001 From: Santiago Lamora Date: Tue, 5 Apr 2022 11:27:21 +0200 Subject: [PATCH 24/33] Fix flask.messages render --- ereuse_devicehub/templates/ereuse_devicehub/base_site.html | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/ereuse_devicehub/templates/ereuse_devicehub/base_site.html b/ereuse_devicehub/templates/ereuse_devicehub/base_site.html index d55a1065..99bd68cf 100644 --- a/ereuse_devicehub/templates/ereuse_devicehub/base_site.html +++ b/ereuse_devicehub/templates/ereuse_devicehub/base_site.html @@ -195,7 +195,11 @@ {% block messages %} {% for level, message in get_flashed_messages(with_categories=true) %} From 683c7cff727249c088a3a7c00d85764a7b0a4691 Mon Sep 17 00:00:00 2001 From: Santiago Lamora Date: Tue, 5 Apr 2022 11:28:07 +0200 Subject: [PATCH 25/33] Hide user profile details untils is implemented --- ereuse_devicehub/templates/ereuse_devicehub/user_profile.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ereuse_devicehub/templates/ereuse_devicehub/user_profile.html b/ereuse_devicehub/templates/ereuse_devicehub/user_profile.html index 0a6b7710..7dcd62a8 100644 --- a/ereuse_devicehub/templates/ereuse_devicehub/user_profile.html +++ b/ereuse_devicehub/templates/ereuse_devicehub/user_profile.html @@ -28,7 +28,7 @@
    -
    +
    From 1e79948e57c66dc303e2b6f847afd02fef89bdfb Mon Sep 17 00:00:00 2001 From: Santiago Lamora Date: Tue, 5 Apr 2022 11:32:27 +0200 Subject: [PATCH 26/33] Drop broken link of breadcrumb --- ereuse_devicehub/templates/ereuse_devicehub/user_profile.html | 1 - 1 file changed, 1 deletion(-) diff --git a/ereuse_devicehub/templates/ereuse_devicehub/user_profile.html b/ereuse_devicehub/templates/ereuse_devicehub/user_profile.html index 7dcd62a8..71ab39fa 100644 --- a/ereuse_devicehub/templates/ereuse_devicehub/user_profile.html +++ b/ereuse_devicehub/templates/ereuse_devicehub/user_profile.html @@ -8,7 +8,6 @@

    Profile

    From 64182403a2214d8af270289941db0ba2c896779e Mon Sep 17 00:00:00 2001 From: Santiago Lamora Date: Tue, 5 Apr 2022 11:47:12 +0200 Subject: [PATCH 30/33] UI: update button colors --- ereuse_devicehub/templates/inventory/removelot.html | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/ereuse_devicehub/templates/inventory/removelot.html b/ereuse_devicehub/templates/inventory/removelot.html index 40cfc14d..1ee1558b 100644 --- a/ereuse_devicehub/templates/inventory/removelot.html +++ b/ereuse_devicehub/templates/inventory/removelot.html @@ -3,21 +3,21 @@