From 8cabb787d17ba907768bbebf2964ef325a1e9afd Mon Sep 17 00:00:00 2001 From: James Kent Date: Wed, 13 Mar 2024 15:40:08 -0500 Subject: [PATCH] [REF] annotation analyses do not create id column (#739) * change how annotation_analysis id is made * run black * add performance test --- store/neurostore/models/data.py | 17 ++++++++++++++++- store/neurostore/openapi | 2 +- store/neurostore/resources/data.py | 1 - store/neurostore/schemas/data.py | 5 ++--- store/neurostore/tests/api/test_crud.py | 5 +++++ store/neurostore/tests/api/test_performance.py | 14 +++++++++++++- 6 files changed, 37 insertions(+), 7 deletions(-) diff --git a/store/neurostore/models/data.py b/store/neurostore/models/data.py index 9806a2a8..8270a56f 100644 --- a/store/neurostore/models/data.py +++ b/store/neurostore/models/data.py @@ -115,7 +115,7 @@ class Annotation(BaseMixin, db.Model): ) -class AnnotationAnalysis(BaseMixin, db.Model): +class AnnotationAnalysis(db.Model): __tablename__ = "annotation_analyses" __table_args__ = ( ForeignKeyConstraint( @@ -126,17 +126,24 @@ class AnnotationAnalysis(BaseMixin, db.Model): ) __mapper_args__ = {"confirm_deleted_rows": False} + created_at = db.Column( + db.DateTime(timezone=True), index=True, server_default=func.now() + ) + updated_at = db.Column(db.DateTime(timezone=True), index=True, onupdate=func.now()) + user_id = db.Column(db.Text, db.ForeignKey("users.external_id"), index=True) study_id = db.Column(db.Text, nullable=False) studyset_id = db.Column(db.Text, nullable=False) annotation_id = db.Column( db.Text, db.ForeignKey("annotations.id", ondelete="CASCADE"), + primary_key=True, index=True, ) analysis_id = db.Column( db.Text, db.ForeignKey("analyses.id", ondelete="CASCADE"), + primary_key=True, index=True, ) note = db.Column(MutableDict.as_mutable(JSONB)) @@ -145,6 +152,14 @@ class AnnotationAnalysis(BaseMixin, db.Model): "User", backref=backref("annotation_analyses", passive_deletes=True) ) + @hybrid_property + def id(self): + return f"{self.annotation_id}_{self.analysis_id}" + + @id.expression + def id(cls): + return cls.annotation_id + "_" + cls.analysis_id + class BaseStudy(BaseMixin, db.Model): __tablename__ = "base_studies" diff --git a/store/neurostore/openapi b/store/neurostore/openapi index 31b93b54..a37bd1a4 160000 --- a/store/neurostore/openapi +++ b/store/neurostore/openapi @@ -1 +1 @@ -Subproject commit 31b93b5414361124cb66f22c2e6dd9e8fb6cccde +Subproject commit a37bd1a403c6ceef7cd79866951dc5c356aba63b diff --git a/store/neurostore/resources/data.py b/store/neurostore/resources/data.py index ea0b4229..ce0194a7 100644 --- a/store/neurostore/resources/data.py +++ b/store/neurostore/resources/data.py @@ -257,7 +257,6 @@ def eager_load(self, q, args=None): .options(raiseload("*", sql_only=True)), selectinload(Annotation.annotation_analyses) .load_only( - AnnotationAnalysis.id, AnnotationAnalysis.analysis_id, AnnotationAnalysis.created_at, AnnotationAnalysis.study_id, diff --git a/store/neurostore/schemas/data.py b/store/neurostore/schemas/data.py index 96fa8020..93e5efb3 100644 --- a/store/neurostore/schemas/data.py +++ b/store/neurostore/schemas/data.py @@ -150,9 +150,6 @@ def __init__(self, *args, **kwargs): OPTIONS_CLASS = BaseSchemaOpts # normal return key - created_at = fields.DateTime(dump_only=True, metadata={"info_field": True}) - updated_at = fields.DateTime(dump_only=True, metadata={"info_field": True}) - id = fields.String(metadata={"info_field": True, "id_field": True}) def on_bind_field(self, field_name, field_obj): @@ -171,6 +168,8 @@ class BaseDataSchema(BaseSchema): metadata={"info_field": True}, default=None, ) + created_at = fields.DateTime(dump_only=True, metadata={"info_field": True}) + updated_at = fields.DateTime(dump_only=True, metadata={"info_field": True}) class ConditionSchema(BaseDataSchema): diff --git a/store/neurostore/tests/api/test_crud.py b/store/neurostore/tests/api/test_crud.py index 65997e7a..00ae00e3 100644 --- a/store/neurostore/tests/api/test_crud.py +++ b/store/neurostore/tests/api/test_crud.py @@ -112,6 +112,11 @@ def test_read(auth_client, user_data, endpoint, model, schema, session): resp_ids = set([res["id"] for res in resp.json()["results"]]) assert query_ids == resp_ids + # get specific record + record = expected_results[0] + get_resp = auth_client.get(f"/api/{endpoint}/{record.id}") + assert get_resp.status_code == 200 + @pytest.mark.parametrize( "endpoint,model,schema,update", diff --git a/store/neurostore/tests/api/test_performance.py b/store/neurostore/tests/api/test_performance.py index 1232f151..023bc9e6 100644 --- a/store/neurostore/tests/api/test_performance.py +++ b/store/neurostore/tests/api/test_performance.py @@ -108,11 +108,23 @@ def test_updating_annotation(assign_neurosynth_to_user, auth_client, session): q = AnnotationsView().eager_load(q) annotation = q.one() annotation_dict = AnnotationSchema().dump(annotation) - # with profiled_yappi("update_annotation_largs.prof"): + # with profiled_yappi("update_annotation_large.prof"): for i in range(len(annotation_dict["notes"])): annotation_dict["notes"][i]["note"]["_5"] = 1.0 auth_client.put(f"/api/annotations/{annotation.id}", data=annotation_dict) +@performance_test +def test_updating_annotation_analysis(assign_neurosynth_to_user, auth_client, session): + q = Annotation.query + q = AnnotationsView().eager_load(q) + annotation = q.one() + annotation_dict = AnnotationSchema().dump(annotation) + # with profiled_yappi("update_annotation_analysis_large.prof"): + for i in range(len(annotation_dict["notes"])): + annotation_analysis = annotation_dict["notes"][i] + annotation_analysis["note"]["_5"] = 1.0 + aa_id = annotation_analysis["id"] + auth_client.put(f"/api/annotation-analyses/{aa_id}", data=annotation_analysis) @performance_test def test_updating_annotation_one(assign_neurosynth_to_user, auth_client, session):