Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Generalised Custom Attribute #20

Merged
merged 10 commits into from
Apr 28, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 3 additions & 4 deletions limbus/app/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
from .misc import misc as misc_blueprint
from .setup import setup as setup_blueprint
from .auth import auth as auth_blueprint
from .attribute import attribute as attribute_blueprint
from .document import document as doc_blueprint
from .sample import sample as sample_blueprint
from .donor import donor as donor_blueprint
Expand All @@ -24,8 +25,6 @@
from .processing import processing as processing_blueprint
from .storage import storage as storage_blueprint

from .demo import demo as demo_blueprint


def create_app():
app = Flask(__name__, instance_relative_config=True)
Expand All @@ -52,10 +51,12 @@ def create_app():
from app.patientconsentform import models as pcf_models
from app.processing import models as processing_models
from app.storage import models as storage_models
from app.attribute import models as attribute_models

app.register_blueprint(misc_blueprint)
app.register_blueprint(setup_blueprint, url_prefix="/setup")
app.register_blueprint(auth_blueprint)
app.register_blueprint(attribute_blueprint, url_prefix="/attributes")
app.register_blueprint(processing_blueprint, url_prefix="/processing")
app.register_blueprint(doc_blueprint, url_prefix="/documents")
app.register_blueprint(sample_blueprint, url_prefix="/samples")
Expand All @@ -64,8 +65,6 @@ def create_app():
app.register_blueprint(pcf_blueprint, url_prefix="/pcf")
app.register_blueprint(storage_blueprint, url_prefix="/storage")

app.register_blueprint(demo_blueprint, url_prefix="/demo")

from app.admin import add_admin_views

add_admin_views()
Expand Down
6 changes: 6 additions & 0 deletions limbus/app/attribute/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
from flask import Blueprint

attribute = Blueprint("attribute", __name__)

from .routes import *
from .api import *
Empty file added limbus/app/attribute/api.py
Empty file.
14 changes: 14 additions & 0 deletions limbus/app/attribute/enums.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
from ..FormEnum import FormEnum

class CustomAttributeTypes(FormEnum):
TEXT = "Text"
NUMERIC = "Numeric"
OPTION = "Option"

class CustomAttributeElementTypes(FormEnum):
ALL = "All"
SAMPLE = "Sample"
PROTOCOL = "Protocol"
TRANSFER = "Transfer"
DONOR = "Donor"
STORAGE = "Storage"
126 changes: 126 additions & 0 deletions limbus/app/attribute/forms.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
from flask_wtf import FlaskForm

from wtforms import (
StringField,
SelectField,
SubmitField,
DateField,
BooleanField,
IntegerField,
)

from wtforms.validators import DataRequired, Length

from .enums import *

from .. import db
from .models import CustomAttributes, CustomAttributeOption, CustomAttributeTextSetting


# Pronto stuff here.
import pronto

# Loading UO into memory upon creation of flask instance.
uo = pronto.Ontology.from_obo_library("uo.obo")

def create_uo_params():
def _get_leafs(node):
leafs = []
def _traverse(n):
n = [x for x in n.subclasses()]
if len(n[1:]) > 1:
for _n in n[1:]:
_traverse(_n)
elif len(n) == 1:
leafs.extend(n)
else:
pass

_traverse(node)
return list(set(leafs))


# Loading UO into memory upon creation of flask instance.
uo_ontology = pronto.Ontology.from_obo_library("uo.obo")
units = _get_leafs(uo_ontology["UO:0000000"])
prefixs = _get_leafs(uo_ontology["UO:0000046"])

return units, prefixs

units, prefixs = create_uo_params()


class EnumFromOntology:
def __init__(self, ontology_list):
self.ontology_list = ontology_list

def choices(self):
return [(term.id, term.name) for term in self.ontology_list]

class CustomAttributeCreationForm(FlaskForm):
term = StringField("Attribute Term", validators=[DataRequired()])
description = StringField("Attribute Description")
accession = StringField("Ontology Accession")
ref = StringField("Ontology Reference")
type = SelectField("Attribute Type", choices=CustomAttributeTypes.choices())
element = SelectField("Element", choices=CustomAttributeElementTypes.choices())
required = BooleanField("Required")
submit = SubmitField("Submit")

class CustomTextAttributeCreationForm(FlaskForm):
max_length = IntegerField("Max Length", validators=[DataRequired()])
submit = SubmitField("Submit")

class CustomNumericAttributionCreationForm(FlaskForm):
requires_measurement = BooleanField("Measurement?")
requires_prefix = BooleanField("Prefix?")
measurement = SelectField("Measurement", choices=EnumFromOntology(units).choices())
prefix = SelectField("Prefix", choices=EnumFromOntology(prefixs).choices())
submit = SubmitField("Submit")


def CustomAttributeSelectForm(element: CustomAttributeElementTypes = CustomAttributeElementTypes.ALL) -> FlaskForm:
class StaticForm(FlaskForm):
submit = SubmitField("Submit")

attrs = db.session.query(CustomAttributes).filter(CustomAttributes.element.in_([element, CustomAttributeElementTypes.ALL])).all()

for attr in attrs:
bf = BooleanField(
attr.term,
render_kw={
"required": attr.required,
"_type" : attr.type.value
})

setattr(StaticForm, str(attr.id), bf)

return StaticForm()


def CustomAttributeGeneratedForm(form, attribute_ids: [] = []) -> FlaskForm:
class StaticForm(FlaskForm):
pass

for id, element in form.elements.items():
setattr(StaticForm, id, element)

attrs = db.session.query(CustomAttributes).filter(CustomAttributes.id.in_(attribute_ids)).all()

for attr in attrs:
if attr.type == CustomAttributeTypes.NUMERIC:
field = IntegerField(attr.term, render_kw={"_custom_val": True})
elif attr.type == CustomAttributeTypes.TEXT:
text_settings = db.session.query(CustomAttributeTextSetting).filter(CustomAttributeTextSetting.custom_attribute_id == attr.id).first_or_404()
field = StringField(attr.term, render_kw={"_custom_val": True}, validators=[Length(max=text_settings.max_length)])
else:
options = db.session.query(CustomAttributeOption).filter(CustomAttributeOption.custom_attribute_id == attr.id).all()
choices = [(x.id, x.term) for x in options]
field = SelectField(attr.term, choices=choices, coerce=int, render_kw={"_custom_val": True})

if attr.required:
field.validators.append(DataRequired())

setattr(StaticForm, str(attr.id), field)

return StaticForm()
67 changes: 67 additions & 0 deletions limbus/app/attribute/models.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
from .enums import CustomAttributeTypes, CustomAttributeElementTypes
from app import db

class CustomAttributes(db.Model):
__tablename__ = "custom_attributes"

id = db.Column(db.Integer, primary_key=True)
description = db.Column(db.String(1024))

term = db.Column(db.String(128))
accession = db.Column(db.String(64))
ref = db.Column(db.String(64))

element = db.Column(db.Enum(CustomAttributeElementTypes))

required = db.Column(db.Boolean(), default=False)

type = db.Column(db.Enum(CustomAttributeTypes))

author_id = db.Column(db.Integer, db.ForeignKey("users.id"))
creation_date = db.Column(db.DateTime, server_default=db.func.now(), nullable=False)
update_date = db.Column(db.DateTime, server_default=db.func.now(), server_onupdate=db.func.now(), nullable=False)



class CustomAttributeTextSetting(db.Model):
__tablename__ = "custom_attribute_text_settings"

id = db.Column(db.Integer, primary_key=True)
max_length = db.Column(db.Integer, nullable=False)

custom_attribute_id = db.Column(db.Integer, db.ForeignKey("custom_attributes.id"))

author_id = db.Column(db.Integer, db.ForeignKey("users.id"))
creation_date = db.Column(db.DateTime, server_default=db.func.now(), nullable=False)
update_date = db.Column(db.DateTime, server_default=db.func.now(), server_onupdate=db.func.now(), nullable=False)


class CustomAttributeNumericSetting(db.Model):
__tablename__ = "custom_attribute_numeric_settings"

id = db.Column(db.Integer, primary_key=True)

custom_attribute_id = db.Column(db.Integer, db.ForeignKey("custom_attributes.id"))

measurement = db.Column(db.String(32))
prefix = db.Column(db.String(32))

creation_date = db.Column(db.DateTime, server_default=db.func.now(), nullable=False)
update_date = db.Column(db.DateTime, server_default=db.func.now(), server_onupdate=db.func.now(), nullable=False)


class CustomAttributeOption(db.Model):
__tablename__ = "custom_attribute_options"

id = db.Column(db.Integer, primary_key=True)

term = db.Column(db.String(128))
accession = db.Column(db.String(64))
ref = db.Column(db.String(64))

custom_attribute_id = db.Column(db.Integer, db.ForeignKey("custom_attributes.id"))

author_id = db.Column(db.Integer, db.ForeignKey("users.id"))
creation_date = db.Column(db.DateTime, server_default=db.func.now(), nullable=False)

update_date = db.Column(db.DateTime, server_default=db.func.now(), server_onupdate=db.func.now(), nullable=False)
Loading