Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Vite app #233

Closed
wants to merge 37 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
37 commits
Select commit Hold shift + click to select a range
17071b9
Merge pull request #211 from SmartAPI/redesign
marcodarko Nov 9, 2023
780d062
upgraded controller tests to check last_updated date, checked with fo…
NikkiBytes Nov 10, 2023
ec8525f
simple fix for last_updated setting
NikkiBytes Nov 10, 2023
954ff90
Merge pull request #217 from SmartAPI/redesign
marcodarko Jan 25, 2024
8c78e11
feat: :sparkles: metakg overview
marcodarko Feb 8, 2024
c736513
Merge pull request #219 from SmartAPI/redesign
marcodarko Feb 8, 2024
57a3ced
feat: add sigmajs for graph viz
marcodarko Feb 22, 2024
2098297
feat: :sparkles: add sigma viz
marcodarko Feb 22, 2024
da670de
added refresh_has_metakg() module
NikkiBytes Feb 23, 2024
f41dd40
added has_metakg variable to controller
NikkiBytes Feb 23, 2024
55701f8
added has_metakg to model
NikkiBytes Feb 23, 2024
d27639f
Merge pull request #223 from SmartAPI/fix-updated-date
marcodarko Feb 23, 2024
c533148
Merge pull request #225 from SmartAPI/add-metakg-flag-v2
marcodarko Feb 23, 2024
c5e6a83
Merge branch 'vite-app' into redesign
marcodarko Feb 23, 2024
7729988
feat: :sparkles: add meta field has_metakg, last_updated fix, new met…
marcodarko Feb 23, 2024
443335c
Merge pull request #226 from SmartAPI/redesign
marcodarko Feb 23, 2024
45f4a90
fix: color blind friendly colors in viz
marcodarko Feb 23, 2024
6b4d682
feat: :sparkles: Cosmo viz
marcodarko Mar 1, 2024
c87a752
Merge pull request #227 from SmartAPI/redesign
marcodarko Mar 1, 2024
1e68629
feat: :sparkles: Cosmo viz add pckg
marcodarko Mar 1, 2024
00e8ff9
Merge pull request #228 from SmartAPI/redesign
marcodarko Mar 1, 2024
72b2c70
fix: :hammer: change last updated field
marcodarko Mar 5, 2024
24d4409
last updated change
marcodarko Mar 5, 2024
3fa5dfb
fix: :hammer: mobile friendly layout
marcodarko Mar 6, 2024
3d8fde2
Merge pull request #229 from SmartAPI/redesign
marcodarko Mar 6, 2024
1b36bde
dev: :hammer: update metakg layout
marcodarko Mar 14, 2024
61e5217
Merge pull request #230 from SmartAPI/redesign
marcodarko Mar 14, 2024
bf3c25a
added predicate filtering and expansion capability
NikkiBytes Mar 19, 2024
d6b6e8c
adding in biolink_helper utility function for resuable code
NikkiBytes Mar 19, 2024
e9d8d96
metakgpathfinder handler updated to work with the expand update
NikkiBytes Mar 19, 2024
34cf4e4
upgraded pf package with new expansion function, predicate filtering,…
NikkiBytes Mar 19, 2024
2cf50b8
remove cosmo animation
marcodarko Mar 20, 2024
a09330b
updated issue template, link selected visibility
marcodarko Mar 20, 2024
b94b8fb
doc update
NikkiBytes Mar 20, 2024
a910f0b
Merge pull request #231 from SmartAPI/add-feature-expand
NikkiBytes Mar 20, 2024
29110a2
change pck json
marcodarko Mar 20, 2024
5620076
Merge branch 'vite-app' of github.com:SmartAPI/smartAPI into vite-app
marcodarko Mar 20, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .github/ISSUE_TEMPLATE/transfer.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ Fill this out if you would like to transfer ownership of an API entry to someone

* **I'm am ...**

- [ ] the person transferring ownership of my API
- [ ] the current owner transferring ownership of my API

- [ ] the person that this API is being transferred to

Expand All @@ -26,7 +26,7 @@ Fill this out if you would like to transfer ownership of an API entry to someone



* What is the ***name*** and ***username*** of the person that this API currently belongs to? *eg. Jane Doe @janedoe1
* What is the ***name*** and ***username*** of the Responsible Developer that currently maintains this API? *eg. Jane Doe @janedoe1



Expand Down
22 changes: 22 additions & 0 deletions src/admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -195,6 +195,26 @@ def consolidate_metakg(reset=True):
SmartAPI.index_metakg_consolidation()


def refresh_has_metakg():
"""
Refreshes the 'has_metakg' attribute for SmartAPI objects.
This function iterates through all SmartAPI objects, checks if there's a corresponding entry in the ConsolidatedMetaKGDoc
collection based on the SmartAPI ID, and updates the 'has_metakg' attribute accordingly.
Note:
- This function assumes the existence of the SmartAPI and ConsolidatedMetaKGDoc classes.
- 'has_metakg' attribute is a boolean value indicating whether a SmartAPI has corresponding metadata in the Meta-Knowledge Graph.
Returns:
None
"""
for smartapi in SmartAPI.get_all(1000):
value = ConsolidatedMetaKGDoc.exists(smartapi._id, field="api.smartapi.id")
if value:
smartapi.has_metakg = True
else:
smartapi.has_metakg = False
smartapi.save()


restore = restore_from_file
backup = backup_to_file

Expand Down Expand Up @@ -229,6 +249,8 @@ def routine():
refresh_metakg()
logger.info("consolidate_metakg()")
consolidate_metakg()
logger.info("refresh_has_metakg()")
refresh_has_metakg()


if __name__ == "__main__":
Expand Down
2 changes: 1 addition & 1 deletion src/controller/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -377,4 +377,4 @@ def update(self, content):
self._status = self.STATUS.NOT_MODIFIED.value
else: # raw field changed
self._status = self.STATUS.UPDATED.value
self._entity.raw = content.raw
self._entity.raw = content.raw
13 changes: 12 additions & 1 deletion src/controller/smartapi.py
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,7 @@ def __init__(self, url):
self.slug = None
self.date_created = None
self.last_updated = None
self.has_metakgq = None

self.uptime = APIMonitorStatus(self)
self.webdoc = APIRefreshStatus(self)
Expand All @@ -117,6 +118,8 @@ def get(cls, _id):
obj.date_created = obj._doc._meta.date_created
obj.last_updated = obj._doc._meta.last_updated

obj.has_metakg = obj._doc._meta.has_metakg

obj.uptime = APIMonitorStatus(
obj,
(
Expand Down Expand Up @@ -289,6 +292,14 @@ def save(self, force_save=True):
if self.date_created > self.last_updated:
raise ControllerError("Invalid timestamps.")


if not self.has_metakg:
value = ConsolidatedMetaKGDoc.exists(self._id, field="api.smartapi.id")
if value:
self.has_metakg = True
else:
self.has_metakg = False

# NOTE
# if the slug of another document changed at this point
# it's possible to have two documents with the same slug
Expand Down Expand Up @@ -326,7 +337,7 @@ def save(self, force_save=True):
doc._meta.date_created = self.date_created
doc._meta.last_updated = self.last_updated
doc._meta.slug = self.slug

doc._meta.has_metakg = self.has_metakg
doc.save(skip_empty=False)

return self._id
Expand Down
116 changes: 96 additions & 20 deletions src/handlers/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
from utils.metakg.export import edges2graphml
from utils.metakg.path_finder import MetaKGPathFinder
from utils.metakg.cytoscape_formatter import CytoscapeDataFormatter
from utils.metakg.biolink_helpers import get_expanded_values
from utils.notification import SlackNewAPIMessage, SlackNewTranslatorAPIMessage

logger = logging.getLogger("smartAPI")
Expand Down Expand Up @@ -433,20 +434,6 @@ def initialize(self, *args, **kwargs):
self.pipeline = MetaKGQueryPipeline(ns=self.biothings)
self.biolink_model_toolkit = bmt.Toolkit()

def get_expanded_values(self, value: Union[str, List[str]]) -> List[str]:
"""return exapnded value list for a given biolink class name"""
if isinstance(value, str):
value = [value]
_out = []
for v in value:
try:
v = self.biolink_model_toolkit.get_descendants(v, reflexive=True, formatted=True)
v = [x.split(":")[-1] for x in v] # remove biolink: prefix
except ValueError:
v = [v]
_out.extend(v)
return _out

@capture_exceptions
async def get(self, *args, **kwargs):
expanded_fields = {"subject": False, "object": False, "predicate": False, "node": False}
Expand All @@ -463,7 +450,7 @@ async def get(self, *args, **kwargs):
value_list = getattr(self.args, field)
if not value_list:
continue
value_list = self.get_expanded_values(value_list) if expanded_fields[field] else value_list
value_list = get_expanded_values(value_list, self.biolink_model_toolkit) if expanded_fields[field] else value_list
setattr(self.args, field, value_list)

await super().get(*args, **kwargs)
Expand Down Expand Up @@ -539,22 +526,111 @@ class MetaKGPathFinderHandler(QueryHandler):
**QUERY_KWARGS.get("GET", {}),
"subject": {"type": str, "required": True, "max": 1000},
"object": {"type": str, "required": True, "max": 1000},
"predicate": {"type": list, "max": 10, "default": []},
"cutoff": {"type": int, "default": 3, "max": 5},
"api_details": {"type": bool, "default": False},
"expand": {
"type": list,
"max": 6,
"default": [],
"enum": ["subject", "object", "predicate", "node", "edge", "all"]
},
"rawquery": {"type": bool, "default": False},
},
}

def initialize(self, *args, **kwargs):
super().initialize(*args, **kwargs)
# change the default query pipeline from self.biothings.pipeline
self.pipeline = MetaKGQueryPipeline(ns=self.biothings)
self.biolink_model_toolkit = bmt.Toolkit()

def setup_pathfinder_rawquery(self, expanded_fields):
# JSON-structured summary of operations and criteria applied
operations_summary = {
"input_parameters": {},
"expansion_logic": {},
"search_criteria": []
}

# Include original query parameters
operations_summary["input_parameters"] = {
"subject": self.args.subject,
"object": self.args.object,
"predicate": getattr(self.args, 'predicate', None) # Including predicate if provided
}

# Detail the expansion logic in a way that explains what expansions are applied
operations_summary["expansion_logic"] = {
"expand_subject": "subject" in self.args.expand or "all" in self.args.expand or "node" in self.args.expand,
"expand_object": "object" in self.args.expand or "all" in self.args.expand or "node" in self.args.expand,
"expand_predicate": "predicate" in self.args.expand,
}

# Summarize the search criteria based on expanded fields
for field, values in expanded_fields.items():
if values: # Ensure values exist for the field before adding
operations_summary["search_criteria"].append({
"field": field,
"description": f"Expanding '{field}' to include {len(values)} variant(s)",
"values": values
})

# The operations_summary is already in a format that can be directly returned as JSON
return operations_summary

@capture_exceptions
async def get(self, *args, **kwargs):
query_data = {"q": self.args.q}
pathfinder = MetaKGPathFinder(query_data=query_data)

# Initialize with the original subject and object, and setup for expansion
expanded_fields = {
"subject": [self.args.subject],
"object": [self.args.object],
}

# Check if expansion is requested
if self.args.expand:
# Define a set for fields affected by 'node' and 'all' for simpler updates
common_fields = {"subject", "object"}

# Initialize expandable_fields based on 'node' or 'all' presence
expandable_fields = set()
if "node" in self.args.expand or "all" in self.args.expand:
expandable_fields.update(common_fields)
if "edge" in self.args.expand or "all" in self.args.expand:
expandable_fields.add("predicate")

# Add specific fields if mentioned explicitly
expandable_fields.update({field for field in ["subject", "object", "predicate"] if field in self.args.expand})

# Expand the fields as required
for field in expandable_fields:
# Use the built-in utility function, get_expanded_values, to expand the fields
expanded_fields[field] = get_expanded_values(getattr(self.args, field), self.biolink_model_toolkit)

# Initalize pathfinder
pathfinder = MetaKGPathFinder(query_data=query_data, expanded_fields=expanded_fields)

# Initialize the pathfinder results list
paths_with_edges = []

# Run get_paths method to retrieve paths and edges
paths_with_edges = pathfinder.get_paths(
subject=self.args.subject,
object=self.args.object,
expanded_fields=expanded_fields,
cutoff=self.args.cutoff,
api_details=self.args.api_details,
predicate_filter=self.args.predicate
)
# Return the result in JSON format
res = {"paths_with_edges": paths_with_edges}

# Check if rawquery parameter is true -- respond with correct output
if self.args.rawquery:
raw_query_output = self.setup_pathfinder_rawquery(expanded_fields)
self.write(raw_query_output)
return
res = {
"total": len(paths_with_edges),
"paths": paths_with_edges,
}
await asyncio.sleep(0.01)
self.finish(res)
4 changes: 2 additions & 2 deletions src/model/smartapi.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
"""
Elasticsearch Document Object Model for SmartAPI
"""
from elasticsearch_dsl import Binary, Date, InnerDoc, Integer, Keyword, Object, Text
from elasticsearch_dsl import Binary, Date, InnerDoc, Integer, Keyword, Object, Text, Boolean

from config import SMARTAPI_ES_INDEX

Expand All @@ -27,7 +27,7 @@ class UserMeta(InnerDoc):
username = Keyword(required=True)
date_created = Date(default_timezone="UTC")
last_updated = Date(default_timezone="UTC")

has_metakg = Boolean()

class SmartAPIDoc(BaseDoc):
_status = Object(StatMeta)
Expand Down
18 changes: 17 additions & 1 deletion src/tests/test_controller.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
import json
import os
import time
from datetime import datetime, timezone
from datetime import datetime, timedelta, timezone

import elasticsearch
import pytest
Expand Down Expand Up @@ -380,6 +380,10 @@ def test_refresh_status():
assert mygene.webdoc.timestamp > datetime(2020, 1, 1)
_ts0 = mygene.webdoc.timestamp

original_last_updated = mygene.last_updated.replace(microsecond=0, tzinfo=None)
one_hour_before = (datetime.now() - timedelta(hours=1)).replace(microsecond=0)
assert original_last_updated > one_hour_before

mygene.save()
refresh()

Expand All @@ -393,9 +397,15 @@ def test_refresh_status():
assert "components" in mygene
assert mygene.webdoc.timestamp > _ts0

current_last_updated = mygene.last_updated.replace(microsecond=0, tzinfo=None)
assert current_last_updated == original_last_updated

mygene.save()
refresh()

# confirm last_updated is not changed after refresh
assert mygene.last_updated.replace(microsecond=0, tzinfo=None) == current_last_updated

mygene_doc = SmartAPIDoc.get(MYGENE_ID)
assert mygene_doc._status.refresh_status == 200
assert "components" in mygene_doc
Expand All @@ -408,6 +418,9 @@ def test_refresh_status():
mygene.save()
refresh()

# confirm last_updated is not changed after refresh
assert mygene.last_updated.replace(microsecond=0, tzinfo=None) == current_last_updated

mygene_doc = SmartAPIDoc.get(MYGENE_ID)
assert mygene_doc._status.refresh_status == 404
assert "components" in mygene_doc
Expand All @@ -432,6 +445,9 @@ def test_refresh_status():
mygene.save()
refresh()

# confirm last_updated is not changed after refresh
assert mygene.last_updated.replace(microsecond=0, tzinfo=None) == current_last_updated

mygene_doc = SmartAPIDoc.get(MYGENE_ID)
assert mygene_doc._status.refresh_status == 499
assert "components" in mygene_doc
Expand Down
20 changes: 20 additions & 0 deletions src/utils/metakg/biolink_helpers.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
from typing import Union, List
import bmt

# Initialize the Biolink Model Toolkit instance globally if it's used frequently
# or pass it as a parameter to functions that require it.
toolkit = bmt.Toolkit()

def get_expanded_values(value: Union[str, List[str]], toolkit_instance=toolkit) -> List[str]:
"""Return expanded value list for a given Biolink class name."""
if isinstance(value, str):
value = [value]
_out = []
for v in value:
try:
v = toolkit_instance.get_descendants(v, reflexive=True, formatted=True)
v = [x.split(":")[-1] for x in v] # Remove 'biolink:' prefix
except ValueError:
v = [v]
_out.extend(v)
return _out
Loading
Loading