Skip to content

Commit

Permalink
Merge pull request #75 from SINTEF-9012/learning-factory
Browse files Browse the repository at this point in the history
Learning factory
  • Loading branch information
tiptr committed Sep 27, 2022
2 parents 7976091 + 58bf85a commit 48ec5a5
Show file tree
Hide file tree
Showing 25 changed files with 2,062 additions and 1,197 deletions.
6 changes: 5 additions & 1 deletion backend/api/python_endpoints/status_endpoints.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
from datetime import datetime
from backend.api.python_endpoints import asset_endpoints
from backend.api.python_endpoints import timeseries_endpoints
from backend.knowledge_graph.dao.AnnotationNodesDao import AnnotationNodesDao
from backend.knowledge_graph.dao.BaseNodesDao import BaseNodeDao
from backend.knowledge_graph.dao.DatabaseConnectionsDao import DatabaseConnectionsDao
from backend.knowledge_graph.dao.RuntimeConnectionsDao import RuntimeConnectionsDao
Expand All @@ -9,6 +10,7 @@
)

BASE_NODE_DAO: BaseNodeDao = BaseNodeDao.instance()
ANNOTATIONS_DAO: AnnotationNodesDao = AnnotationNodesDao.instance()
DB_CON_DAO: DatabaseConnectionsDao = DatabaseConnectionsDao.instance()
RT_CON_DAO: RuntimeConnectionsDao = RuntimeConnectionsDao.instance()

Expand Down Expand Up @@ -37,7 +39,9 @@ def get_status():
Returns:
_type_: dict
"""
unconfirmed_detection = False
unconfirmed_detection = (
ANNOTATIONS_DAO.get_oldest_unconfirmed_detection() is not None
)

status_dict = {
"system_time": get_system_time(),
Expand Down
187 changes: 183 additions & 4 deletions backend/api/rest_endpoints/annotation_endpoints.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,14 @@
from datetime import datetime
from datetime import datetime, timedelta
import json
from dateutil import tz
from fastapi import HTTPException
from typing import List
from backend.api.api import app
from backend.knowledge_graph.dao.BaseNodesDao import BaseNodeDao
from graph_domain.expert_annotations.AnnotationDetectionNode import (
AnnotationDetectionNodeFlat,
)
from util.environment_and_configuration import ConfigGroups, get_configuration
from backend.knowledge_graph.dao.AnnotationNodesDao import AnnotationNodesDao
from pydantic import BaseModel
from backend.knowledge_graph.dao.TimeseriesNodesDao import TimeseriesNodesDao
Expand All @@ -14,6 +20,9 @@

ANNOTATIONS_DAO: AnnotationNodesDao = AnnotationNodesDao.instance()
TIMESERIES_NODES_DAO: TimeseriesNodesDao = TimeseriesNodesDao.instance()
BASE_NODE_DAO: BaseNodeDao = BaseNodeDao.instance()

DATETIME_STRF_FORMAT = "%Y_%m_%d_%H_%M_%S_%f"


class AnnotationDefinitionArguments(BaseModel):
Expand Down Expand Up @@ -45,8 +54,7 @@ class AnnotationInstanceArguments(BaseModel):
description: str | None = None


@app.post("/annotation/instance")
async def post_annotation_instance(instance: AnnotationInstanceArguments):
def create_annotation_instance(instance: AnnotationInstanceArguments):
logger.info(f"Creating new annotation instance: {instance.id_short}...")
instance_iri = ANNOTATIONS_DAO.create_annotation_instance(
id_short=instance.id_short,
Expand Down Expand Up @@ -82,6 +90,11 @@ async def post_annotation_instance(instance: AnnotationInstanceArguments):
return instance_iri


@app.post("/annotation/instance")
async def post_annotation_instance(instance: AnnotationInstanceArguments):
return create_annotation_instance(instance)


@app.delete("/annotation/definition")
async def delete_annotation_definition(definition_iri: str):
logger.info(f"Deleting annotation definition: {definition_iri}...")
Expand Down Expand Up @@ -112,7 +125,7 @@ async def delete_annotation_instance(instance_iri: str):
logger.info(f"Deleting time-series matcher: {matcher.id_short}")
ANNOTATIONS_DAO.delete_annotation_ts_matcher(matcher.iri)

logger.info(f"Deleting annotation definition: {instance_iri}...")
logger.info(f"Deleting annotation instance: {instance_iri}...")
ANNOTATIONS_DAO.delete_annotation_instance(instance_iri)


Expand All @@ -136,3 +149,169 @@ async def get_ts_matcher_annotation_instance(iri: str):
:return:
"""
return ANNOTATIONS_DAO.get_matcher_annotation_instance(iri)


@app.get("/annotation/instance")
async def get_annotation_instance_for_definition(definition_iri: str):
"""
Returns the list of annotation instances with the given definition
:raises IdNotFoundException: If the file is not found
:param iri:
:return:
"""
return ANNOTATIONS_DAO.get_annotation_instance_for_definition(definition_iri)


@app.get("/annotation/instance/count")
async def get_annotation_instance_count_for_definition(definition_iri: str):
"""
Returns the count of annotation instances with the given definition
:raises IdNotFoundException: If the file is not found
:param iri:
:return:
"""
return ANNOTATIONS_DAO.get_annotation_instance_count_for_definition(definition_iri)


def get_annotation_detection_details(detection_iri: None | str = None):
"""
Returns details of the currently detected annotation or the given one
(or of one of them, if multiple unconfirmed yet).
:param iri:
:return:
"""
if detection_iri is None:
detection_node: AnnotationDetectionNodeFlat = (
ANNOTATIONS_DAO.get_oldest_unconfirmed_detection()
)
if detection_node is None:
logger.info(
"Annotation detection details for current detection "
"ordered when there was no new detection!"
)
return None
else:
detection_node: AnnotationDetectionNodeFlat = BASE_NODE_DAO.get_generic_node(
detection_iri
)

matched_ts_nodes = ANNOTATIONS_DAO.get_matched_ts_for_detection(detection_node.iri)
asset = ANNOTATIONS_DAO.get_asset_for_detection(detection_node.iri)
instance = ANNOTATIONS_DAO.get_annotation_instance_for_detection(detection_node.iri)
definition = ANNOTATIONS_DAO.get_annotation_definition_for_instance(instance.iri)
orig_asset = ANNOTATIONS_DAO.get_asset_for_annotation_instance(instance.iri)

details_dict = {
"iri": detection_node.iri,
"asset_iri": asset.iri,
"asset_caption": asset.caption,
"occurance_start": detection_node.occurance_start_date_time,
"occurance_end": detection_node.occurance_end_date_time,
"definition_iri": definition.iri,
"definition_caption": definition.caption,
"definition_description": definition.description,
"instance_iri": instance.iri,
"instance_id_short": instance.id_short,
"instance_caption": instance.caption,
"instance_description": instance.description,
"solution_proposal": definition.solution_proposal,
"detected_timeseries_iris": [ts.iri for ts in matched_ts_nodes],
"original_annotated_asset_iri": orig_asset.iri,
}

return details_dict


@app.get("/annotation/detection/details")
async def get_current_annotation_detection_details_endpoint():
"""
Returns details of the currently detected annotation
(or of one of them, if multiple unconfirmed yet).
:param iri:
:return:
"""
return get_annotation_detection_details()


@app.delete("/annotation/detection")
async def delete_annotation_detection(detection_iri: str):
"""Deletes a detection and the relationships
Args:
instance_iri (str): _description_
"""

logger.info(f"Deleting annotation detection: {detection_iri}...")

ANNOTATIONS_DAO.delete_annotation_detection(detection_iri)


class AnnotationDetectionConfirmationArguments(BaseModel):
detection_iri: str


@app.post("/annotation/detection/confirm")
async def confirm_annotation_detection(
detection: AnnotationDetectionConfirmationArguments,
):
"""Confirms a detection.
Args:
instance_iri (str): _description_
"""

logger.info(f"Confirming annotation detection: {detection.detection_iri}...")

ANNOTATIONS_DAO.set_detection_confirmation_date_time(
detection_iri=detection.detection_iri
)

details_dict = get_annotation_detection_details(detection.detection_iri)
caption = f"Confirmed Occurance of: {details_dict.get('instance_caption')}"
description = (
f"This annotation instance has been automatically created when "
f"the related detection of the instance '{details_dict.get('instance_caption')}' has been confirmed."
)
timestamp = datetime.now().astimezone(
tz.gettz(get_configuration(group=ConfigGroups.FRONTEND, key="timezone"))
)
id_short = f"confirmed_occurance_of_{details_dict.get('instance_id_short')}_at_{timestamp.strftime(DATETIME_STRF_FORMAT)}"

instance_iri = create_annotation_instance(
AnnotationInstanceArguments(
id_short=id_short,
asset_iri=details_dict.get("asset_iri"),
definition_iri=details_dict.get("definition_iri"),
ts_iri_list=details_dict.get("detected_timeseries_iris"),
start_datetime=details_dict.get("occurance_start"),
end_datetime=details_dict.get("occurance_end"),
caption=caption,
description=description,
)
)

# Relationship to detection
ANNOTATIONS_DAO.create_confirmed_detection_instance_relationship(
detection_iri=details_dict.get("iri"), instance_iri=instance_iri
)


@app.get("/annotation/status")
async def get_annotation_status():
"""Combined status endpoint. Should be preferred to use less API calls.
Returns:
_type_: dict
"""
status_dict = {
"total_annotations_count": ANNOTATIONS_DAO.get_annotation_instance_count(),
"sum_of_scans": "?", # TODO
"unconfirmed_detections": ANNOTATIONS_DAO.get_annotation_detections_count(
confirmed=False
),
"confirmed_detections": ANNOTATIONS_DAO.get_annotation_detections_count(
confirmed=True
),
}

return status_dict
Loading

0 comments on commit 48ec5a5

Please sign in to comment.