Skip to content
Snippets Groups Projects
Commit 1fbdf91e authored by Benjamin Bertrand's avatar Benjamin Bertrand
Browse files

Automatically assign temporary ics_id if not given

This allows to easily register items via the API without giving an ICS
id.
parent 77468350
No related branches found
No related tags found
No related merge requests found
......@@ -9,7 +9,7 @@ This module implements the inventory API.
:license: BSD 2-Clause, see LICENSE for more details.
"""
from flask import Blueprint, jsonify, request
from flask import Blueprint, jsonify, request, current_app
from flask_jwt_extended import jwt_required
from .. import utils, models
from ..decorators import jwt_groups_accepted
......@@ -59,6 +59,7 @@ def create_item():
# an item so ics_id should also be a mandatory field.
# But there are existing items (in confluence and JIRA) that we want to
# import and associate after they have been created.
# In that case a temporary id is automatically assigned.
return create_generic_model(models.Item, mandatory_fields=('serial_number',))
......@@ -70,7 +71,7 @@ def patch_item(id_):
id_ can be the primary key or the ics_id field
Fields allowed to update are:
- ics_id ONLY if currently null (422 returned otherwise)
- ics_id ONLY if current is temporary (422 returned otherwise)
- manufacturer
- model
- location
......@@ -89,9 +90,9 @@ def patch_item(id_):
'location', 'status', 'parent'):
raise utils.CSEntryError(f"Invalid field '{key}'", status_code=422)
item = get_item_by_id_or_ics_id(id_)
# Only allow to set ICS id if it's null
if item.ics_id is None:
item.ics_id = data.get('ics_id')
# Only allow to set ICS id if the current id is a temporary one
if item.ics_id.startswith(current_app.config['TEMPORARY_ICS_ID']):
item.ics_id = data.get('ics_id', item.ics_id)
elif 'ics_id' in data:
raise utils.CSEntryError("'ics_id' can't be changed", status_code=422)
item.manufacturer = utils.convert_to_model(data.get('manufacturer', item.manufacturer), models.Manufacturer)
......
......@@ -9,10 +9,7 @@ This module implements helpers functions for the models.
:license: BSD 2-Clause, see LICENSE for more details.
"""
import string
from flask import current_app
from flask_wtf import FlaskForm
from . import models
class CSEntryForm(FlaskForm):
......@@ -28,27 +25,3 @@ class CSEntryForm(FlaskForm):
super().__init__(obj=obj, **kwargs)
else:
super().__init__(formdata=formdata, obj=obj, **kwargs)
def temporary_ics_ids():
"""Generator that returns the full list of temporary ICS ids"""
return (f'{current_app.config["TEMPORARY_ICS_ID"]}{letter}{number:0=3d}'
for letter in string.ascii_uppercase
for number in range(0, 1000))
def used_temporary_ics_ids():
"""Generator that returns the list of temporary ICS ids used"""
temporary_items = models.Item.query.filter(
models.Item.ics_id.startswith(
current_app.config['TEMPORARY_ICS_ID'])).all()
return (item.ics_id for item in temporary_items)
def get_temporary_ics_id():
"""Return a temporary ICS id that is available"""
for ics_id in temporary_ics_ids():
if ics_id not in used_temporary_ics_ids():
return ics_id
else:
raise ValueError('No temporary ICS id available')
......@@ -10,6 +10,7 @@ This module implements the models used in the app.
"""
import ipaddress
import string
import qrcode
import sqlalchemy as sa
from sqlalchemy.ext.declarative import declared_attr
......@@ -40,6 +41,30 @@ def pg_utcnow(element, compiler, **kw):
return "TIMEZONE('utc', CURRENT_TIMESTAMP)"
def temporary_ics_ids():
"""Generator that returns the full list of temporary ICS ids"""
return (f'{current_app.config["TEMPORARY_ICS_ID"]}{letter}{number:0=3d}'
for letter in string.ascii_uppercase
for number in range(0, 1000))
def used_temporary_ics_ids():
"""Generator that returns the list of temporary ICS ids used"""
temporary_items = Item.query.filter(
Item.ics_id.startswith(
current_app.config['TEMPORARY_ICS_ID'])).all()
return (item.ics_id for item in temporary_items)
def get_temporary_ics_id():
"""Return a temporary ICS id that is available"""
for ics_id in temporary_ics_ids():
if ics_id not in used_temporary_ics_ids():
return ics_id
else:
raise ValueError('No temporary ICS id available')
@login_manager.user_loader
def load_user(user_id):
"""User loader callback for flask-login
......@@ -247,7 +272,8 @@ class Item(CreatedMixin, db.Model):
# WARNING! Inheriting id from CreatedMixin doesn't play well with
# SQLAlchemy-Continuum. It has to be defined here.
id = db.Column(db.Integer, primary_key=True)
ics_id = db.Column(db.Text, unique=True, nullable=False, index=True)
ics_id = db.Column(db.Text, unique=True, nullable=False,
index=True, default=get_temporary_ics_id)
serial_number = db.Column(db.Text, nullable=False)
manufacturer_id = db.Column(db.Integer, db.ForeignKey('manufacturer.id'))
model_id = db.Column(db.Integer, db.ForeignKey('model.id'))
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment