Skip to content

Commit

Permalink
Allow for getting status of children records
Browse files Browse the repository at this point in the history
  • Loading branch information
bennybp committed Nov 14, 2023
1 parent f843437 commit 763741f
Show file tree
Hide file tree
Showing 11 changed files with 100 additions and 13 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,9 @@ def test_gridoptimization_client_add_get(
assert r.record_type == "gridoptimization"
assert compare_gridoptimization_specs(spec, r.specification)

assert r.status == RecordStatusEnum.waiting
assert r.children_status == {}

assert r.service.tag == "tag1"
assert r.service.priority == PriorityEnum.low

Expand Down Expand Up @@ -195,6 +198,8 @@ def test_gridoptimization_client_delete(snowflake: QCATestingSnowflake):

child_recs = snowflake_client.get_records(child_ids, missing_ok=True)
assert all(x.status == RecordStatusEnum.complete for x in child_recs)
go_rec = snowflake_client.get_records(go_id)
assert go_rec.children_status == {RecordStatusEnum.complete: len(child_ids)}

snowflake_client.undelete_records(go_id)

Expand All @@ -205,6 +210,8 @@ def test_gridoptimization_client_delete(snowflake: QCATestingSnowflake):

child_recs = snowflake_client.get_records(child_ids, missing_ok=True)
assert all(x.status == RecordStatusEnum.deleted for x in child_recs)
go_rec = snowflake_client.get_records(go_id)
assert go_rec.children_status == {RecordStatusEnum.deleted: len(child_ids)}

meta = snowflake_client.delete_records(go_id, soft_delete=False, delete_children=True)
assert meta.success
Expand Down
7 changes: 7 additions & 0 deletions qcfractal/qcfractal/components/manybody/test_record_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,9 @@ def test_manybody_client_add_get(
assert r.record_type == "manybody"
assert compare_manybody_specs(spec, r.specification)

assert r.status == RecordStatusEnum.waiting
assert r.children_status == {}

assert r.service.tag == "tag1"
assert r.service.priority == PriorityEnum.low

Expand Down Expand Up @@ -177,6 +180,8 @@ def test_manybody_client_delete(snowflake: QCATestingSnowflake):

child_recs = snowflake_client.get_records(child_ids, missing_ok=True)
assert all(x.status == RecordStatusEnum.complete for x in child_recs)
mb_rec = snowflake_client.get_records(mb_id)
assert mb_rec.children_status == {RecordStatusEnum.complete: len(child_ids)}

snowflake_client.undelete_records(mb_id)

Expand All @@ -187,6 +192,8 @@ def test_manybody_client_delete(snowflake: QCATestingSnowflake):

child_recs = snowflake_client.get_records(child_ids, missing_ok=True)
assert all(x.status == RecordStatusEnum.deleted for x in child_recs)
mb_rec = snowflake_client.get_records(mb_id)
assert mb_rec.children_status == {RecordStatusEnum.deleted: len(child_ids)}

meta = snowflake_client.delete_records(mb_id, soft_delete=False, delete_children=True)
assert meta.success
Expand Down
28 changes: 17 additions & 11 deletions qcfractal/qcfractal/components/neb/test_record_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,9 @@ def test_neb_client_add_get(submitter_client: PortalClient, spec: NEBSpecificati
assert r.record_type == "neb"
assert compare_neb_specs(spec, r.specification)

assert r.status == RecordStatusEnum.waiting
assert r.children_status == {}

assert r.service.tag == "tag1"
assert r.service.priority == PriorityEnum.low

Expand Down Expand Up @@ -180,25 +183,28 @@ def test_neb_client_delete(snowflake: QCATestingSnowflake):
rec = session.get(NEBRecordORM, neb_id)

# Children are singlepoints, optimizations, and the trajectory of the optimizations (also singlepoints)
child_ids = [x.singlepoint_id for x in rec.singlepoints]
direct_child_ids = [x.singlepoint_id for x in rec.singlepoints]
opt_ids = [x.optimization_id for x in rec.optimizations]
child_ids.extend(opt_ids)
direct_child_ids.extend(opt_ids)

child_ids = direct_child_ids.copy()
for opt in rec.optimizations:
traj_ids = [x.singlepoint_id for x in opt.optimization_record.trajectory]
child_ids.extend(traj_ids)

# Some duplicates here
direct_child_ids = list(set(direct_child_ids))
child_ids = list(set(child_ids))

meta = snowflake_client.delete_records(neb_id, soft_delete=True, delete_children=False)
assert meta.success
assert meta.deleted_idx == [0]
assert meta.n_children_deleted == 0

for cid in child_ids:
child_rec = snowflake_client.get_records(cid, missing_ok=True)
assert child_rec.status == RecordStatusEnum.complete
child_recs = snowflake_client.get_records(child_ids, missing_ok=True)
assert all(x.status == RecordStatusEnum.complete for x in child_recs)
neb_rec = snowflake_client.get_records(neb_id)
assert neb_rec.children_status == {RecordStatusEnum.complete: len(direct_child_ids)}

snowflake_client.undelete_records(neb_id)

Expand All @@ -207,9 +213,10 @@ def test_neb_client_delete(snowflake: QCATestingSnowflake):
assert meta.deleted_idx == [0]
assert meta.n_children_deleted == len(child_ids)

for cid in child_ids:
child_rec = snowflake_client.get_records(cid, missing_ok=True)
assert child_rec.status == RecordStatusEnum.deleted
child_recs = snowflake_client.get_records(child_ids, missing_ok=True)
assert all(x.status == RecordStatusEnum.deleted for x in child_recs)
neb_rec = snowflake_client.get_records(neb_id)
assert neb_rec.children_status == {RecordStatusEnum.deleted: len(direct_child_ids)}

meta = snowflake_client.delete_records(neb_id, soft_delete=False, delete_children=True)
assert meta.success
Expand All @@ -219,9 +226,8 @@ def test_neb_client_delete(snowflake: QCATestingSnowflake):
recs = snowflake_client.get_nebs(neb_id, missing_ok=True)
assert recs is None

for cid in child_ids:
child_rec = snowflake_client.get_records(cid, missing_ok=True)
assert child_rec is None
child_recs = snowflake_client.get_records(child_ids, missing_ok=True)
assert all(x is None for x in child_recs)

# DB should be pretty empty now
query_res = snowflake_client.query_records()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,9 @@ def test_optimization_client_add_get(
assert r.record_type == "optimization"
assert compare_optimization_specs(spec, r.specification)

assert r.status == RecordStatusEnum.waiting
assert r.children_status == {}

assert r.task.function is None
assert r.task.tag == "tag1"
assert r.task.priority == PriorityEnum.low
Expand Down Expand Up @@ -198,6 +201,9 @@ def test_optimization_client_delete(snowflake: QCATestingSnowflake, opt_file: st

child_recs = snowflake_client.get_records(child_ids, missing_ok=True)
assert all(x.status == RecordStatusEnum.complete for x in child_recs)
opt_rec = snowflake_client.get_records(opt_id)
if child_ids:
assert opt_rec.children_status == {RecordStatusEnum.complete: len(child_ids)}

# Undo what we just did
snowflake_client.undelete_records(opt_id)
Expand All @@ -209,6 +215,9 @@ def test_optimization_client_delete(snowflake: QCATestingSnowflake, opt_file: st

child_recs = snowflake_client.get_records(child_ids, missing_ok=True)
assert all(x.status == RecordStatusEnum.deleted for x in child_recs)
opt_rec = snowflake_client.get_records(opt_id)
if child_ids:
assert opt_rec.children_status == {RecordStatusEnum.deleted: len(child_ids)}

meta = snowflake_client.delete_records(opt_id, soft_delete=False, delete_children=True)
assert meta.success
Expand Down
7 changes: 7 additions & 0 deletions qcfractal/qcfractal/components/reaction/test_record_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,9 @@ def test_reaction_client_add_get(
assert r.record_type == "reaction"
assert compare_reaction_specs(spec, r.specification)

assert r.status == RecordStatusEnum.waiting
assert r.children_status == {}

assert r.service.tag == "tag1"
assert r.service.priority == PriorityEnum.low

Expand Down Expand Up @@ -205,6 +208,8 @@ def test_reaction_client_delete(snowflake: QCATestingSnowflake):

child_recs = snowflake_client.get_records(child_ids, missing_ok=True)
assert all(x.status == RecordStatusEnum.complete for x in child_recs)
rxn_rec = snowflake_client.get_records(rxn_id)
assert rxn_rec.children_status == {RecordStatusEnum.complete: len(child_ids)}

snowflake_client.undelete_records(rxn_id)

Expand All @@ -215,6 +220,8 @@ def test_reaction_client_delete(snowflake: QCATestingSnowflake):

child_recs = snowflake_client.get_records(child_ids, missing_ok=True)
assert all(x.status == RecordStatusEnum.deleted for x in child_recs)
rxn_rec = snowflake_client.get_records(rxn_id)
assert rxn_rec.children_status == {RecordStatusEnum.deleted: len(child_ids)}

meta = snowflake_client.delete_records(rxn_id, soft_delete=False, delete_children=True)
assert meta.success
Expand Down
7 changes: 7 additions & 0 deletions qcfractal/qcfractal/components/record_routes.py
Original file line number Diff line number Diff line change
Expand Up @@ -197,3 +197,10 @@ def get_record_native_file_single_v1(record_id: int, name: str, record_type: Opt
def get_record_native_file_data_v1(record_id: int, name: str, record_type: Optional[str] = None):
record_socket = storage_socket.records.get_socket(record_type)
return record_socket.get_single_native_file_rawdata(record_id, name)


@api_v1.route("/records/<string:record_type>/<int:record_id>/children_status", methods=["GET"])
@wrap_route("READ")
def get_record_children_status_v1(record_id: int, record_type: Optional[str] = None):
record_socket = storage_socket.records.get_socket(record_type)
return record_socket.get_children_status(record_id)
21 changes: 20 additions & 1 deletion qcfractal/qcfractal/components/record_socket.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
from typing import TYPE_CHECKING

from qcelemental.models import FailedOperation
from sqlalchemy import select, union, or_
from sqlalchemy import select, union, or_, func
from sqlalchemy.orm import (
joinedload,
selectinload,
Expand Down Expand Up @@ -453,6 +453,25 @@ def get_single_native_file_rawdata(

return nf_data[0], nf_data[1]

def get_children_status(self, record_id: int, *, session: Optional[Session] = None) -> Dict[RecordStatusEnum, int]:

# Get the SQL 'select' statements from the handlers
select_stmts = self.get_children_select()

if not select_stmts:
return {}

select_cte = union(*select_stmts).cte()

stmt = select(BaseRecordORM.status, func.count())
stmt = stmt.join(select_cte, select_cte.c.child_id == BaseRecordORM.id)
stmt = stmt.where(select_cte.c.parent_id == record_id)
stmt = stmt.group_by(BaseRecordORM.status)

with self.root_socket.optional_session(session, True) as session:
res = session.execute(stmt).all()
return {x: y for x, y in res}


class RecordSocket:
"""
Expand Down
3 changes: 3 additions & 0 deletions qcfractal/qcfractal/components/services/test_socket.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,9 @@ def test_service_socket_error(storage_socket: SQLAlchemySocket, session: Session
rec = session.get(BaseRecordORM, id_1)

assert rec.status == RecordStatusEnum.error

child_stat = storage_socket.records.torsiondrive.get_children_status(id_1, session=session)
assert child_stat[RecordStatusEnum.error] == 1
assert len(rec.compute_history) == 1
assert len(rec.compute_history[-1].outputs) == 2 # stdout and error
assert rec.compute_history[-1].status == RecordStatusEnum.error
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
import pytest

from qcarchivetesting import load_molecule_data
from qcportal.record_models import PriorityEnum
from qcportal.record_models import PriorityEnum, RecordStatusEnum
from qcportal.singlepoint import QCSpecification, SinglepointDriver
from .testing_helpers import submit_test_data, run_test_data, compare_singlepoint_specs, test_specs

Expand Down Expand Up @@ -67,6 +67,10 @@ def test_singlepoint_client_add_get(submitter_client: PortalClient, spec: QCSpec
assert r.record_type == "singlepoint"
assert r.record_type == "singlepoint"
assert compare_singlepoint_specs(spec, r.specification)

assert r.status == RecordStatusEnum.waiting
assert r.children_status == {}

assert r.task.function is None
assert r.task.tag == "tag1"
assert r.task.priority == PriorityEnum.high
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,9 @@ def test_torsiondrive_client_add_get(
assert r.record_type == "torsiondrive"
assert compare_torsiondrive_specs(spec, r.specification)

assert r.status == RecordStatusEnum.waiting
assert r.children_status == {}

assert r.service.tag == "tag1"
assert r.service.priority == PriorityEnum.low

Expand Down Expand Up @@ -195,6 +198,8 @@ def test_torsiondrive_client_delete(snowflake: QCATestingSnowflake):

child_recs = snowflake_client.get_records(child_ids, missing_ok=True)
assert all(x.status == RecordStatusEnum.complete for x in child_recs)
td_rec = snowflake_client.get_records(td_id)
assert td_rec.children_status == {RecordStatusEnum.complete: len(child_ids)}

snowflake_client.undelete_records(td_id)

Expand All @@ -205,6 +210,8 @@ def test_torsiondrive_client_delete(snowflake: QCATestingSnowflake):

child_recs = snowflake_client.get_records(child_ids, missing_ok=True)
assert all(x.status == RecordStatusEnum.deleted for x in child_recs)
td_rec = snowflake_client.get_records(td_id)
assert td_rec.children_status == {RecordStatusEnum.deleted: len(child_ids)}

meta = snowflake_client.delete_records(td_id, soft_delete=False, delete_children=True)
assert meta.success
Expand Down
11 changes: 11 additions & 0 deletions qcportal/qcportal/record_models.py
Original file line number Diff line number Diff line change
Expand Up @@ -525,6 +525,17 @@ def _handle_includes(self, includes: Optional[Iterable[str]]):
def offline(self) -> bool:
return self._client is None

@property
def children_status(self) -> Dict[RecordStatusEnum, int]:
"""Returns a dicionary of the status of all children of this record"""
self._assert_online()

return self._client.make_request(
"get",
f"{self._base_url}/children_status",
Dict[RecordStatusEnum, int],
)

@property
def compute_history(self) -> List[ComputeHistory]:
if self.compute_history_ is None:
Expand Down

0 comments on commit 763741f

Please sign in to comment.