Skip to content

Commit

Permalink
Merge pull request #88 from nsidc/relationship_interfaces
Browse files Browse the repository at this point in the history
Relationship interfaces
  • Loading branch information
rmarow authored Aug 15, 2023
2 parents 0978859 + e68221d commit 3ae767e
Show file tree
Hide file tree
Showing 9 changed files with 404 additions and 62 deletions.
1 change: 1 addition & 0 deletions usaon_vta_survey/routes/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import usaon_vta_survey.routes.response.data_products
import usaon_vta_survey.routes.response.observing_systems
import usaon_vta_survey.routes.response.relationships.data_product_application
import usaon_vta_survey.routes.response.relationships.observing_system_data_product
import usaon_vta_survey.routes.login
import usaon_vta_survey.routes.logout
import usaon_vta_survey.routes.user
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,10 +56,7 @@ def _response_data_product(
data_product_id: int | None,
response_id: int,
) -> ResponseDataProduct:
"""Return a data product db object (or 404), and do some mutations.
TODO: Extract mutations to another function responsible for that.
"""
"""Return a data product db object (or 404)."""
if data_product_id is not None:
response_data_product = db.get_or_404(ResponseDataProduct, data_product_id)
else:
Expand All @@ -73,7 +70,7 @@ def _response_application(
application_id: int | None,
response_id: int,
) -> ResponseApplication:
"""Return an application db object (or 404), and do some mutations."""
"""Return an application db object (or 404)."""
if application_id is not None:
response_application = db.get_or_404(ResponseApplication, application_id)
else:
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,219 @@
from flask import Request, redirect, render_template, request, url_for
from flask_wtf import FlaskForm
from wtforms import FormField

from usaon_vta_survey import app, db
from usaon_vta_survey.forms import FORMS_BY_MODEL
from usaon_vta_survey.models.tables import (
ResponseDataProduct,
ResponseObservingSystem,
ResponseObservingSystemDataProduct,
Survey,
)
from usaon_vta_survey.util.authorization import limit_response_editors


def _update_super_form(
super_form: type[FlaskForm],
/,
*,
data_product_id: int | None,
observing_system_id: int | None,
) -> None:
"""Populate the form of forms with sub-forms depending on provided IDs.
When an ID for an object is not provided, we need to gather information from the
user to create that object.
TODO: Better function name.
"""
if observing_system_id is None:
super_form.observing_system = FormField(FORMS_BY_MODEL[ResponseObservingSystem])

if data_product_id is None:
super_form.data_product = FormField(FORMS_BY_MODEL[ResponseDataProduct])


def _update_relationship(
relationship: ResponseObservingSystemDataProduct,
*,
observing_system_id: int | None,
data_product_id: int | None,
) -> None:
"""Populate the relationship with any known identifiers.
TODO: Better function name.
"""
if observing_system_id:
relationship.response_observing_system_id = observing_system_id

if data_product_id:
relationship.response_data_product_id = data_product_id


# may not need to be internal
def _response_data_product(
*,
data_product_id: int | None,
response_id: int,
) -> ResponseDataProduct:
"""Return a data product db object (or 404)."""
if data_product_id is not None:
response_data_product = db.get_or_404(ResponseDataProduct, data_product_id)
else:
response_data_product = ResponseDataProduct(response_id=response_id)

return response_data_product


def _response_observing_system(
*,
observing_system_id: int | None,
response_id: int,
) -> ResponseObservingSystem:
"""Return an observing system db object (or 404)."""
if observing_system_id is not None:
response_observing_system = db.get_or_404(
ResponseObservingSystem, observing_system_id
)
else:
response_observing_system = ResponseObservingSystem(response_id=response_id)

return response_observing_system


def _response_observing_system_data_product(
*,
observing_system_id: int | None,
data_product_id: int | None,
) -> ResponseObservingSystemDataProduct:
"""Return a relationship db object.
Returned object may be transient or persistent depending on whether a match exists
in the db.
"""
if data_product_id and observing_system_id:
# If not found, will be `None`
response_observing_system_data_product = db.session.get(
ResponseObservingSystemDataProduct,
(data_product_id, observing_system_id),
)
else:
response_observing_system_data_product = None

if response_observing_system_data_product is not None:
return response_observing_system_data_product
else:
return ResponseObservingSystemDataProduct()


def _request_args(request: Request) -> tuple[int | None, int | None]:
data_product_id: int | str | None = request.args.get('data_product_id')
if data_product_id is not None:
data_product_id = int(data_product_id)

observing_system_id: int | str | None = request.args.get('observing_system_id')
if observing_system_id is not None:
observing_system_id = int(observing_system_id)

return data_product_id, observing_system_id


@app.route(
'/response/<string:survey_id>/observing_system_data_product_relationships',
methods=['GET', 'POST'],
)
def view_response_observing_system_data_product_relationships(survey_id: str):
"""View and add observing system/dataproduct relationships to a response.
TODO: Refactor this whole pile of stuff. Less string magic. Less cyclomatic
complexity.
"""
data_product_id, observing_system_id = _request_args(request)
survey = db.get_or_404(Survey, survey_id)

class SuperForm(FlaskForm):
"""Combine all necessary forms into one super-form.
NOTE: Additional class attributes are added dynamically below.
"""

relationship = FormField(FORMS_BY_MODEL[ResponseObservingSystemDataProduct])

response_observing_system_data_product = _response_observing_system_data_product(
data_product_id=data_product_id,
observing_system_id=observing_system_id,
)

response_data_product = _response_data_product(
data_product_id=data_product_id,
response_id=survey.response_id,
)

response_observing_system = _response_observing_system(
observing_system_id=observing_system_id,
response_id=survey.response_id,
)

_update_super_form(
SuperForm,
observing_system_id=observing_system_id,
data_product_id=data_product_id,
)
_update_relationship(
response_observing_system_data_product,
observing_system_id=observing_system_id,
data_product_id=data_product_id,
)

form_obj: dict[
str,
ResponseObservingSystem
| ResponseDataProduct
| ResponseObservingSystemDataProduct,
] = {
'observing_system': response_observing_system,
'data_product': response_data_product,
# NOTE: Logic below depends on relationship being last in this dict
'relationship': response_observing_system_data_product,
}

if request.method == 'POST':
limit_response_editors()
form = SuperForm(request.form, obj=form_obj)

if form.validate():
# Add only submitted sub-forms into the db session
for key, obj in form_obj.items():
if hasattr(form, key):
form[key].form.populate_obj(obj)
db.session.add(obj)

# Update the relationship object with the ids of any new entities
if type(obj) is not ResponseObservingSystemDataProduct:
# Get the db object's new ID
db.session.flush()
db.session.refresh(obj)

# Update the relationship db object
setattr(
response_observing_system_data_product,
f'response_{key}_id',
obj.id,
)

db.session.commit()

return redirect(url_for('view_response_data_products', survey_id=survey.id))

form = SuperForm(obj=form_obj)
return render_template(
'response/relationships/observing_system_data_product.html',
form=form,
survey=survey,
observing_system=response_observing_system,
observing_systems=survey.response.observing_systems,
data_product=response_data_product,
data_products=survey.response.data_products,
relationship=response_observing_system_data_product,
)
42 changes: 0 additions & 42 deletions usaon_vta_survey/templates/macros/forms/misc.j2
Original file line number Diff line number Diff line change
Expand Up @@ -113,45 +113,3 @@
</ul>
{% endif %}
{%- endmacro -%}


{% macro data_product_application_fields(form) -%}
{{form.criticality_rating.label}} {{form.criticality_rating(size=5)}}
{% if form.criticality_rating.errors %}
<ul class="errors">
{% for error in form.criticality_rating.errors %}
<li>{{ error }}</li>
{% endfor %}
</ul>
{% endif %}
<br />

{{form.performance_rating.label}} {{form.performance_rating(size=50)}}
{% if form.performance_rating.errors %}
<ul class="errors">
{% for error in form.performance_rating.errors %}
<li>{{ error }}</li>
{% endfor %}
</ul>
{% endif %}
<br />

{{form.rationale.label}} {{form.rationale(size=50)}}
{% if form.rationale.errors %}
<ul class="errors">
{% for error in form.rationale.errors %}
<li>{{ error }}</li>
{% endfor %}
</ul>
{% endif %}
<br />

{{form.needed_improvements.label}} {{form.needed_improvements(size=50)}}
{% if form.needed_improvements.errors %}
<ul class="errors">
{% for error in form.needed_improvements.errors %}
<li>{{ error }}</li>
{% endfor %}
</ul>
{% endif %}
{%- endmacro -%}
81 changes: 81 additions & 0 deletions usaon_vta_survey/templates/macros/forms/relationships/app_sba.j2
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
{% macro data_product_application_fields(form) -%}
{{form.criticality_rating.label}} {{form.criticality_rating(size=5)}}
{% if form.criticality_rating.errors %}
<ul class="errors">
{% for error in form.criticality_rating.errors %}
<li>{{ error }}</li>
{% endfor %}
</ul>
{% endif %}
<br />

{{form.performance_rating.label}} {{form.performance_rating(size=50)}}
{% if form.performance_rating.errors %}
<ul class="errors">
{% for error in form.performance_rating.errors %}
<li>{{ error }}</li>
{% endfor %}
</ul>
{% endif %}
<br />

{{form.rationale.label}} {{form.rationale(size=50)}}
{% if form.rationale.errors %}
<ul class="errors">
{% for error in form.rationale.errors %}
<li>{{ error }}</li>
{% endfor %}
</ul>
{% endif %}
<br />

{{form.needed_improvements.label}} {{form.needed_improvements(size=50)}}
{% if form.needed_improvements.errors %}
<ul class="errors">
{% for error in form.needed_improvements.errors %}
<li>{{ error }}</li>
{% endfor %}
</ul>
{% endif %}
{%- endmacro -%}

{% macro observing_system_data_product_fields(form) -%}
{{form.criticality_rating.label}} {{form.criticality_rating(size=5)}}
{% if form.criticality_rating.errors %}
<ul class="errors">
{% for error in form.criticality_rating.errors %}
<li>{{ error }}</li>
{% endfor %}
</ul>
{% endif %}
<br />

{{form.performance_rating.label}} {{form.performance_rating(size=50)}}
{% if form.performance_rating.errors %}
<ul class="errors">
{% for error in form.performance_rating.errors %}
<li>{{ error }}</li>
{% endfor %}
</ul>
{% endif %}
<br />

{{form.rationale.label}} {{form.rationale(size=50)}}
{% if form.rationale.errors %}
<ul class="errors">
{% for error in form.rationale.errors %}
<li>{{ error }}</li>
{% endfor %}
</ul>
{% endif %}
<br />

{{form.needed_improvements.label}} {{form.needed_improvements(size=50)}}
{% if form.needed_improvements.errors %}
<ul class="errors">
{% for error in form.needed_improvements.errors %}
<li>{{ error }}</li>
{% endfor %}
</ul>
{% endif %}
{%- endmacro -%}
8 changes: 7 additions & 1 deletion usaon_vta_survey/templates/response/data_products.html
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,13 @@ <h3>Data products</h3>
<br />
{% endfor %}

<a href="#">Add relationship</a>
<a href="{{url_for(
'view_response_observing_system_data_product_relationships',
survey_id=survey.id,
data_product_id=data_product.id,
)}}">
Add relationship
</a>
</td>
</tr>
{% endfor %}
Expand Down
Loading

0 comments on commit 3ae767e

Please sign in to comment.