diff --git a/CHANGELOG.md b/CHANGELOG.md index 4e7d9b0b..542b5491 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,8 @@ ml). ## master ## testing +- [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. 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 diff --git a/ereuse_devicehub/inventory/forms.py b/ereuse_devicehub/inventory/forms.py index 2508d621..724d3124 100644 --- a/ereuse_devicehub/inventory/forms.py +++ b/ereuse_devicehub/inventory/forms.py @@ -502,46 +502,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()]) diff --git a/ereuse_devicehub/inventory/views.py b/ereuse_devicehub/inventory/views.py index 4e827be4..66b28563 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 @@ -22,12 +21,11 @@ from ereuse_devicehub.inventory.forms import ( NewActionForm, NewDeviceForm, TagDeviceForm, - TagForm, - TagUnnamedForm, TradeDocumentForm, TradeForm, UploadSnapshotForm, ) +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 @@ -118,6 +116,7 @@ class DeviceListMix(GenericMixView): 'form_new_datawipe': form_new_datawipe, 'form_new_trade': form_new_trade, 'form_filter': form_filter, + 'form_print_labels': PrintLabelsForm(), 'lot': lot, 'tags': tags, 'list_devices': list_devices, @@ -315,91 +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 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] @@ -716,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') ) diff --git a/ereuse_devicehub/labels/__init__.py b/ereuse_devicehub/labels/__init__.py new file mode 100644 index 00000000..e69de29b 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/labels/views.py b/ereuse_devicehub/labels/views.py new file mode 100644 index 00000000..445a4eb8 --- /dev/null +++ b/ereuse_devicehub/labels/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.labels.forms import PrintLabelsForm, TagForm, TagUnnamedForm +from ereuse_devicehub.resources.lot.models import Lot +from ereuse_devicehub.resources.tag.model import Tag + +labels = Blueprint('labels', __name__, url_prefix='/labels') + +logger = logging.getLogger(__name__) + + +class TagListView(View): + methods = ['GET'] + decorators = [login_required] + template_name = 'labels/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 = 'labels/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('labels.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 = 'labels/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('labels.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 = 'labels/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 = 'labels/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) + + +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') +) +labels.add_url_rule( + '/print', + view_func=PrintLabelsView.as_view('print_labels'), +) +labels.add_url_rule('//', view_func=LabelDetailView.as_view('label_details')) 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/ereuse_devicehub/base_site.html b/ereuse_devicehub/templates/ereuse_devicehub/base_site.html index 5d87be53..1ffddfa0 100644 --- a/ereuse_devicehub/templates/ereuse_devicehub/base_site.html +++ b/ereuse_devicehub/templates/ereuse_devicehub/base_site.html @@ -53,7 +53,7 @@
  • - + Need Help? @@ -181,7 +181,7 @@
  • +
  • + +
  • @@ -250,7 +261,7 @@ {% else %} {% endif %} - + Upload a new Snapshot @@ -335,7 +346,7 @@ {% for t in dev.tags | sort(attribute="id") %} - {{ t.id }} + {{ t.id }} {% if not loop.last %},{% endif %} {% endfor %} @@ -389,7 +400,7 @@ {% include "inventory/addDeviceslot.html" %} {% include "inventory/addDevicestag.html" %} {% include "inventory/removeDeviceslot.html" %} -{% include "inventory/removelot.html" %} +{% include "inventory/lot_delete_modal.html" %} {% include "inventory/actions.html" %} {% include "inventory/allocate.html" %} {% include "inventory/data_wipe.html" %} diff --git a/ereuse_devicehub/templates/inventory/removelot.html b/ereuse_devicehub/templates/inventory/lot_delete_modal.html similarity index 62% rename from ereuse_devicehub/templates/inventory/removelot.html rename to ereuse_devicehub/templates/inventory/lot_delete_modal.html index 40cfc14d..1ee1558b 100644 --- a/ereuse_devicehub/templates/inventory/removelot.html +++ b/ereuse_devicehub/templates/inventory/lot_delete_modal.html @@ -3,21 +3,21 @@ -
    Print details
    +
    Print Label
    -
    +
    -
    {{ tag.id }}
    +
    {{ tag.id }}
    @@ -109,6 +109,6 @@ {% endblock main %} diff --git a/ereuse_devicehub/templates/inventory/tag_list.html b/ereuse_devicehub/templates/labels/label_list.html similarity index 88% rename from ereuse_devicehub/templates/inventory/tag_list.html rename to ereuse_devicehub/templates/labels/label_list.html index 28ecc8f8..810db5cf 100644 --- a/ereuse_devicehub/templates/inventory/tag_list.html +++ b/ereuse_devicehub/templates/labels/label_list.html @@ -20,7 +20,7 @@ + + + + + +{% endblock main %} diff --git a/ereuse_devicehub/templates/inventory/tag_create.html b/ereuse_devicehub/templates/labels/tag_create.html similarity index 92% rename from ereuse_devicehub/templates/inventory/tag_create.html rename to ereuse_devicehub/templates/labels/tag_create.html index 70d71f6a..a2579258 100644 --- a/ereuse_devicehub/templates/inventory/tag_create.html +++ b/ereuse_devicehub/templates/labels/tag_create.html @@ -5,7 +5,7 @@

    {{ title }}

    @@ -49,7 +49,7 @@
    - Cancel + Cancel
    diff --git a/ereuse_devicehub/templates/inventory/tag_create_unnamed.html b/ereuse_devicehub/templates/labels/tag_create_unnamed.html similarity index 92% rename from ereuse_devicehub/templates/inventory/tag_create_unnamed.html rename to ereuse_devicehub/templates/labels/tag_create_unnamed.html index d8eddde0..fa59b44f 100644 --- a/ereuse_devicehub/templates/inventory/tag_create_unnamed.html +++ b/ereuse_devicehub/templates/labels/tag_create_unnamed.html @@ -5,7 +5,7 @@

    {{ title }}

    @@ -49,7 +49,7 @@
    - Cancel + Cancel
    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' 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