Skip to content

Commit

Permalink
Animal model section backend (#145)
Browse files Browse the repository at this point in the history
* First push of the animal model systems script to insert sections into the analysis for the intercenter meeting

* changed supporting-evidence values to be arrays not objects

* Updating the analysis fixtures to include a mouse model system in CPAM0002, fixed some wording in the migration script, and updated the supplemental-evidence section to be an array instead of an object

* First push for the endpoints to attach imaging and reports

* Re-structured endpoints and collection functions to adding supporting evidence to section fields, linting and tests

* yapf formatting for python

* Fixed a test for adding a link to an analysis section

* Renamed endpoints to be consistent
  • Loading branch information
JmScherer authored Oct 23, 2023
1 parent 44746ac commit 76465cc
Show file tree
Hide file tree
Showing 5 changed files with 358 additions and 1 deletion.
114 changes: 113 additions & 1 deletion backend/src/repository/analysis_collection.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
from ..models.event import Event
from ..enums import EventType

# pylint: disable=too-few-public-methods
# pylint: disable=too-many-public-methods
# Disabling too few public metods due to utilizing Pydantic/FastAPI BaseSettings class


Expand Down Expand Up @@ -374,3 +374,115 @@ def update_event(self, analysis_name: str, username: str, event_type: EventType)
# remove the _id field from the returned document since it is not JSON serializable
updated_document.pop("_id", None)
return updated_document

def attach_section_supporting_evidence_file(
self, analysis_name: str, section_name: str, field_name: str, field_value_file: object
):
"""
Attaches a file to a field within an analysis section and returns only the updated field within that section
"""

updated_document = self.collection.find_one({"name": analysis_name})

if "_id" in updated_document:
updated_document.pop("_id", None)

updated_section = None
for section in updated_document['sections']:
if section_name == section['header']:
updated_section = section

if None is updated_section:
raise ValueError(
f"'{section_name}' does not exist within '{analysis_name}'. Unable to attach report to '{section_name}'\
section."
)

updated_field = None
for field in updated_section['content']:
if field['field'] == field_name:
field['value'] = [field_value_file]
updated_field = field

self.collection.find_one_and_update(
{"name": analysis_name},
{'$set': updated_document},
return_document=ReturnDocument.AFTER,
)

return_field = {"header": section_name, "field": field_name, "updated_row": updated_field}

return return_field

def attach_section_supporting_evidence_link(
self, analysis_name: str, section_name: str, field_name: str, field_value_link: object
):
"""
Attaches a link to a section field within an analysis and returns only the updated field for that section
"""
updated_document = self.collection.find_one({"name": analysis_name})

if "_id" in updated_document:
updated_document.pop("_id", None)

updated_section = None
for section in updated_document['sections']:
if section_name == section['header']:
updated_section = section

if None is updated_section:
raise ValueError(
f"'{section_name}' does not exist within '{analysis_name}'. Unable to attach report to '{section_name}'\
section."
)

new_uuid = str(uuid4())
field_value_link['attachment_id'] = new_uuid

updated_field = None
for field in updated_section['content']:
if field['field'] == 'Veterinary Pathology Imaging':
field['value'] = [field_value_link]
updated_field = field

self.collection.find_one_and_update(
{"name": analysis_name},
{'$set': updated_document},
return_document=ReturnDocument.AFTER,
)

return_updated_field = {"header": section_name, "field": field_name, "updated_row": updated_field}

return return_updated_field

def remove_section_supporting_evidence(self, analysis_name: str, section_name: str, field_name: str):
""" Removes a section field's supporting evidence """
updated_document = self.collection.find_one({"name": analysis_name})

if "_id" in updated_document:
updated_document.pop("_id", None)

updated_section = None
for section in updated_document['sections']:
if section_name == section['header']:
updated_section = section

if None is updated_section:
raise ValueError(
f"'{section_name}' does not exist within '{analysis_name}'. Unable to attach report to '{section_name}'\
section."
)

for field in updated_section['content']:
if field['field'] == field_name:
field['value'] = []

self.collection.find_one_and_update(
{"name": analysis_name},
{'$set': updated_document},
return_document=ReturnDocument.AFTER,
)

return_field = {"header": section_name, "field": field_name}

return return_field
78 changes: 78 additions & 0 deletions backend/src/routers/analysis_router.py
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,84 @@ def update_analysis_sections(
return repositories["analysis"].find_by_name(analysis_name)


@router.put("/{analysis_name}/section/attach/file")
def attach_animal_model_system_report(
analysis_name: str,
section_name: str = Form(...),
field_name: str = Form(...),
comments: str = Form(...),
upload_file: UploadFile = File(...),
repositories=Depends(database),
authorized=Security(get_authorization, scopes=["write"]) #pylint: disable=unused-argument
):
""" Attaches a file as supporting evidence to a section in an Analysis """

try:
new_file_object_id = repositories["bucket"].save_file(
upload_file.file, upload_file.filename, upload_file.content_type
)
except Exception as exception:
raise HTTPException(status_code=500, detail=str(exception)) from exception

field_value_file = {
"name": upload_file.filename, "attachment_id": str(new_file_object_id), "type": "file", "comments": comments
}

return repositories['analysis'].attach_section_supporting_evidence_file(
analysis_name, section_name, field_name, field_value_file
)


@router.put("/{analysis_name}/section/remove/file")
def remove_animal_model_system_report(
analysis_name: str,
section_name: str = Form(...),
field_name: str = Form(...),
attachment_id: str = Form(...),
repositories=Depends(database),
authorized=Security(get_authorization, scopes=["write"]) #pylint: disable=unused-argument
):
""" Removes a supporting evidence file from an analysis section """

if repositories["bucket"].id_exists(attachment_id):
repositories["bucket"].delete_file(attachment_id)

return repositories['analysis'].remove_section_supporting_evidence(analysis_name, section_name, field_name)


@router.put("/{analysis_name}/section/attach/link")
def attach_animal_model_system_imaging(
analysis_name: str,
section_name: str = Form(...),
field_name: str = Form(...),
link_name: str = Form(...),
link: str = Form(...),
comments: str = Form(...),
repositories=Depends(database),
authorized=Security(get_authorization, scopes=["write"]) #pylint: disable=unused-argument
):
""" Attaches a link as supporting evidence to an analysis section """

field_value_link = {"name": link_name, "data": link, "type": "link", "comments": comments}

return repositories["analysis"].attach_section_supporting_evidence_link(
analysis_name, section_name, field_name, field_value_link
)


@router.put("/{analysis_name}/section/remove/link")
def remove_animal_model_system_imaging(
analysis_name: str,
section_name: str = Form(...),
field_name: str = Form(...),
repositories=Depends(database),
authorized=Security(get_authorization, scopes=["write"]) #pylint: disable=unused-argument
):
""" Removes a supporting evidence link from an analysis section """

return repositories["analysis"].remove_section_supporting_evidence(analysis_name, section_name, field_name)


@router.get("/download/{file_id}")
def download_file_by_id(file_id: str, repositories=Depends(database)):
""" Returns a file from GridFS using the file's id """
Expand Down
55 changes: 55 additions & 0 deletions backend/tests/fixtures/analysis-CPAM0002.json
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,61 @@
}
]
},
{
"header":"Mus musculus (Mouse) Model System",
"content":[
{
"type":"section-text",
"field":"Mutation",
"value":[]
},
{
"type":"section-text",
"field":"Pathogenicity Test",
"value":[]
},
{
"type":"section-text",
"field":"Design",
"value":[]
},
{
"type":"section-text",
"field":"Founder Screening/Expansion",
"value":[]
},
{
"type":"section-text",
"field":"Screening",
"value":[]
},
{
"type":"section-text",
"field":"History",
"value":[]
},
{
"type":"section-text",
"field":"Diagnoses",
"value":[]
},
{
"type":"section-text",
"field":"Remarks",
"value":[]
},
{
"type":"supporting-evidence",
"field":"Veterinary Histology Report",
"value":[]
},
{
"type":"supporting-evidence",
"field":"Veterinary Pathology Imaging",
"value":[]
}
]
},
{
"header": "Medical Summary",
"content": [
Expand Down
55 changes: 55 additions & 0 deletions backend/tests/fixtures/empty-pedigree.json
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,61 @@
}
]
},
{
"header":"Mus musculus (Mouse) Model System",
"content":[
{
"type":"section-text",
"field":"Mutation",
"value":[]
},
{
"type":"section-text",
"field":"Pathogenicity Test",
"value":[]
},
{
"type":"section-text",
"field":"Design",
"value":[]
},
{
"type":"section-text",
"field":"Founder Screening/Expansion",
"value":[]
},
{
"type":"section-text",
"field":"Screening",
"value":[]
},
{
"type":"section-text",
"field":"History",
"value":[]
},
{
"type":"section-text",
"field":"Diagnoses",
"value":[]
},
{
"type":"section-text",
"field":"Remarks",
"value":[]
},
{
"type":"supporting-evidence",
"field":"Veterinary Histology Report",
"value":[]
},
{
"type":"supporting-evidence",
"field":"Veterinary Pathology Imaging",
"value":[]
}
]
},
{
"header": "Medical Summary",
"content": [
Expand Down
57 changes: 57 additions & 0 deletions backend/tests/unit/repository/test_analysis_collection.py
Original file line number Diff line number Diff line change
Expand Up @@ -323,6 +323,63 @@ def test_mark_ready_analysis_does_not_exist(analysis_collection):
assert str(error) == "Analysis with name CPAM2222 does not exist."


def test_attach_section_supporting_evidence_file(analysis_collection):
""" Tests adding a file as supporting evidence to an analysis section field"""
analysis_collection.collection.find_one.return_value = read_test_fixture("analysis-CPAM0002.json")

expected = {
"header": "Mus musculus (Mouse) Model System", "field": "Veterinary Histology Report", "updated_row": {
'type': 'supporting-evidence', 'field': 'Veterinary Histology Report', 'value': [{
'name': 'fake-cpam0002-histology-report.pdf', 'attachment_id': 'fake-file-report-id', 'type': 'file',
'comments': 'These are comments'
}]
}
}

field_value_file = {
"name": "fake-cpam0002-histology-report.pdf", "attachment_id": "fake-file-report-id", "type": "file",
"comments": "These are comments"
}

actual = analysis_collection.attach_section_supporting_evidence_file(
"CPAM0002", "Mus musculus (Mouse) Model System", "Veterinary Histology Report", field_value_file
)

assert expected == actual


def test_attach_section_supporting_evidence_link(analysis_collection):
""" Tests adding a link as supporting evidence to an analysis section field """
analysis_collection.collection.find_one.return_value = read_test_fixture("analysis-CPAM0002.json")

field_value_link = {
"name": "Google Link", "data": "https://www.google.com", "type": "link", "comments": "nothing to do with google"
}

actual = analysis_collection.attach_section_supporting_evidence_link(
"CPAM0002", "Mus musculus (Mouse) Model System", "Veterinary Pathology Imaging", field_value_link
)

new_evidence = next((evidence for evidence in actual['updated_row']['value'] if evidence['name'] == "Google Link"),
None)
assert new_evidence['type'] == 'link'
assert 'attachment_id' in new_evidence
assert new_evidence['data'] == 'https://www.google.com'


def test_remove_section_supporting_evidence(analysis_collection):
""" Tests removing supporting evidence from an analysis section field """
analysis_collection.collection.find_one.return_value = read_test_fixture("analysis-CPAM0002.json")

expected = {"header": "Mus musculus (Mouse) Model System", "field": "Veterinary Histology Report"}

actual = analysis_collection.remove_section_supporting_evidence(
"CPAM0002", "Mus musculus (Mouse) Model System", "Veterinary Histology Report"
)

assert expected == actual


@pytest.fixture(name="analysis_with_no_p_dot")
def fixture_analysis_with_no_p_dot():
"""Returns an analysis with no p. in the genomic unit"""
Expand Down

0 comments on commit 76465cc

Please sign in to comment.