Skip to content

Commit 15dfa64

Browse files
committed
moving scicrungh unders the service layer
1 parent e571f8d commit 15dfa64

File tree

4 files changed

+90
-75
lines changed

4 files changed

+90
-75
lines changed

services/web/server/src/simcore_service_webserver/groups/_classifiers_rest.py

Lines changed: 5 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,8 @@
88

99
from .._meta import API_VTAG
1010
from ..login.decorators import login_required
11-
from ..scicrunch.models import ResearchResource, ResourceHit
11+
from ..scicrunch.models import ResourceHit
1212
from ..scicrunch.scicrunch_service import ScicrunchResourcesService
13-
from ..scicrunch.service_client import SciCrunch
1413
from ..security.decorators import permission_required
1514
from ..utils_aiohttp import envelope_json_response
1615
from ._classifiers_service import GroupClassifiersService
@@ -50,15 +49,9 @@ async def get_group_classifiers(request: web.Request):
5049
@handle_plugin_requests_exceptions
5150
async def get_scicrunch_resource(request: web.Request):
5251
rrid = request.match_info["rrid"]
53-
rrid = SciCrunch.validate_identifier(rrid)
5452

55-
# check if in database first
5653
service = ScicrunchResourcesService(request.app)
57-
resource: ResearchResource | None = await service.get_resource(rrid)
58-
if not resource:
59-
# otherwise, request to scicrunch service
60-
scicrunch = SciCrunch.get_instance(request.app)
61-
resource = await scicrunch.get_resource_fields(rrid)
54+
resource = await service.get_or_fetch_resource(rrid)
6255

6356
return envelope_json_response(resource.model_dump())
6457

@@ -73,16 +66,8 @@ async def get_scicrunch_resource(request: web.Request):
7366
async def add_scicrunch_resource(request: web.Request):
7467
rrid = request.match_info["rrid"]
7568

76-
# check if exists
7769
service = ScicrunchResourcesService(request.app)
78-
resource: ResearchResource | None = await service.get_resource(rrid)
79-
if not resource:
80-
# then request scicrunch service
81-
scicrunch = SciCrunch.get_instance(request.app)
82-
resource = await scicrunch.get_resource_fields(rrid)
83-
84-
# insert new or if exists, then update
85-
await service.upsert_resource(resource)
70+
resource = await service.add_resource(rrid)
8671

8772
return envelope_json_response(resource.model_dump())
8873

@@ -97,17 +82,7 @@ async def add_scicrunch_resource(request: web.Request):
9782
async def search_scicrunch_resources(request: web.Request):
9883
guess_name = str(request.query["guess_name"]).strip()
9984

100-
scicrunch = SciCrunch.get_instance(request.app)
101-
hits: list[ResourceHit] = await scicrunch.search_resource(guess_name)
102-
103-
return envelope_json_response([hit.model_dump() for hit in hits])
104-
105-
106-
@handle_plugin_requests_exceptions
107-
async def search_scicrunch_resources(request: web.Request):
108-
guess_name = str(request.query["guess_name"]).strip()
109-
110-
scicrunch = SciCrunch.get_instance(request.app)
111-
hits: list[ResourceHit] = await scicrunch.search_resource(guess_name)
85+
service = ScicrunchResourcesService(request.app)
86+
hits: list[ResourceHit] = await service.search_resources(guess_name)
11287

11388
return envelope_json_response([hit.model_dump() for hit in hits])

services/web/server/src/simcore_service_webserver/groups/_classifiers_service.py

Lines changed: 33 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,6 @@
2525

2626
from ..scicrunch.errors import ScicrunchError
2727
from ..scicrunch.scicrunch_service import ScicrunchResourcesService
28-
from ..scicrunch.service_client import SciCrunch
2928
from ._classifiers_repository import GroupClassifierRepository
3029

3130
_logger = logging.getLogger(__name__)
@@ -108,46 +107,41 @@ async def get_group_classifiers(
108107

109108
# otherwise, build dynamic tree with RRIDs
110109
try:
111-
return await build_rrids_tree_view(self.app, tree_view_mode=tree_view_mode)
110+
return await self._build_rrids_tree_view(tree_view_mode=tree_view_mode)
112111
except ScicrunchError:
113112
# Return empty view on any error (including ScicrunchError)
114113
return {}
115114

116-
117-
# HELPERS FOR API HANDLERS --------------
118-
119-
120-
async def build_rrids_tree_view(
121-
app: web.Application, tree_view_mode: Literal["std"] = "std"
122-
) -> dict[str, Any]:
123-
if tree_view_mode != "std":
124-
raise web.HTTPNotImplemented(
125-
text="Currently only 'std' option for the classifiers tree view is implemented"
115+
async def _build_rrids_tree_view(
116+
self, tree_view_mode: Literal["std"] = "std"
117+
) -> dict[str, Any]:
118+
if tree_view_mode != "std":
119+
msg = "Currently only 'std' option for the classifiers tree view is implemented"
120+
raise NotImplementedError(msg)
121+
122+
service = ScicrunchResourcesService(self.app)
123+
124+
flat_tree_view: dict[TreePath, ClassifierItem] = {}
125+
for resource in await service.list_resources(include_url=True):
126+
try:
127+
validated_item = ClassifierItem(
128+
classifier=resource.rrid,
129+
display_name=resource.name.title(),
130+
short_description=resource.description,
131+
url=resource.url,
132+
)
133+
134+
node = TypeAdapter(TreePath).validate_python(
135+
validated_item.display_name.replace(":", " ")
136+
)
137+
flat_tree_view[node] = validated_item
138+
139+
except ValidationError as err:
140+
_logger.warning(
141+
"Cannot convert RRID into a classifier item. Skipping. Details: %s",
142+
err,
143+
)
144+
145+
return Classifiers.model_construct(classifiers=flat_tree_view).model_dump(
146+
exclude_unset=True
126147
)
127-
128-
scicrunch = SciCrunch.get_instance(app)
129-
service = ScicrunchResourcesService(app)
130-
131-
flat_tree_view: dict[TreePath, ClassifierItem] = {}
132-
for resource in await service.list_resources():
133-
try:
134-
validated_item = ClassifierItem(
135-
classifier=resource.rrid,
136-
display_name=resource.name.title(),
137-
short_description=resource.description,
138-
url=scicrunch.get_resolver_web_url(resource.rrid),
139-
)
140-
141-
node = TypeAdapter(TreePath).validate_python(
142-
validated_item.display_name.replace(":", " ")
143-
)
144-
flat_tree_view[node] = validated_item
145-
146-
except ValidationError as err:
147-
_logger.warning(
148-
"Cannot convert RRID into a classifier item. Skipping. Details: %s", err
149-
)
150-
151-
return Classifiers.model_construct(classifiers=flat_tree_view).model_dump(
152-
exclude_unset=True
153-
)

services/web/server/src/simcore_service_webserver/scicrunch/_service.py

Lines changed: 47 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,8 @@
99
from pydantic import ValidationError
1010

1111
from ._repository import ScicrunchResourcesRepository
12-
from .models import ResearchResource, ResearchResourceAtdB
12+
from .models import ResearchResource, ResearchResourceAtdB, ResourceHit
13+
from .service_client import SciCrunch
1314

1415
_logger = logging.getLogger(__name__)
1516

@@ -20,8 +21,9 @@ class ScicrunchResourcesService:
2021
def __init__(self, app: web.Application):
2122
self.app = app
2223
self._repo = ScicrunchResourcesRepository.create_from_app(app)
24+
self._scicrunch = SciCrunch.get_instance(self.app)
2325

24-
async def list_resources(self) -> list[ResearchResource]:
26+
async def list_resources(self, include_url: bool = False) -> list[ResearchResource]:
2527
"""List all research resources as domain models."""
2628
rows = await self._repo.list_all_resources()
2729
if not rows:
@@ -30,7 +32,15 @@ async def list_resources(self) -> list[ResearchResource]:
3032
resources = []
3133
for row in rows:
3234
try:
33-
resource = ResearchResource.model_validate(dict(row))
35+
resource_data = dict(row)
36+
37+
# Add resolver URL if requested
38+
if include_url:
39+
resource_data["url"] = self._scicrunch.get_resolver_web_url(
40+
row.rrid
41+
)
42+
43+
resource = ResearchResource.model_validate(resource_data)
3444
resources.append(resource)
3545
except ValidationError as err:
3646
_logger.warning(
@@ -88,3 +98,37 @@ async def upsert_resource(self, resource: ResearchResource) -> ResearchResource:
8898
values = resource.model_dump(exclude_unset=True)
8999
row = await self._repo.upsert_resource(values)
90100
return ResearchResource.model_validate(dict(row))
101+
102+
async def search_resources(self, guess_name: str) -> list[ResourceHit]:
103+
"""Search for research resources using SciCrunch API."""
104+
guess_name = guess_name.strip()
105+
if not guess_name:
106+
return []
107+
108+
return await self._scicrunch.search_resource(guess_name)
109+
110+
async def add_resource(self, rrid: str) -> ResearchResource:
111+
"""Add a research resource by RRID, fetching from SciCrunch if not in database."""
112+
# Check if exists in database first
113+
resource = await self.get_resource(rrid)
114+
if resource:
115+
return resource
116+
117+
# If not found, request from scicrunch service
118+
resource = await self._scicrunch.get_resource_fields(rrid)
119+
120+
# Insert new or update if exists
121+
return await self.upsert_resource(resource)
122+
123+
async def get_or_fetch_resource(self, rrid: str) -> ResearchResource:
124+
"""Get resource from database first, fetch from SciCrunch API if not found."""
125+
# Validate the RRID format first
126+
validated_rrid = SciCrunch.validate_identifier(rrid)
127+
128+
# Check if in database first
129+
resource = await self.get_resource(validated_rrid)
130+
if resource:
131+
return resource
132+
133+
# Otherwise, request from scicrunch service
134+
return await self._scicrunch.get_resource_fields(validated_rrid)

services/web/server/src/simcore_service_webserver/scicrunch/models.py

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
"""
2-
Domain models at every interface: scicrunch API, pg database and webserver API
2+
Domain models at every interface: scicrunch API, pg database and webserver API
33
"""
44

55
import logging
66
import re
77
from datetime import datetime
88

9-
from pydantic import field_validator, ConfigDict, BaseModel, Field
9+
from pydantic import BaseModel, ConfigDict, Field, HttpUrl, field_validator
1010

1111
logger = logging.getLogger(__name__)
1212

@@ -62,11 +62,13 @@ class ResearchResource(BaseModel):
6262
)
6363
name: str
6464
description: str
65+
url: HttpUrl | None = None
6566

6667
@field_validator("rrid", mode="before")
6768
@classmethod
68-
def format_rrid(cls, v):
69+
def _format_rrid(cls, v):
6970
return normalize_rrid_tags(v, with_prefix=True)
71+
7072
model_config = ConfigDict(from_attributes=True, str_strip_whitespace=True)
7173

7274

0 commit comments

Comments
 (0)