diff --git a/dspback/api.py b/dspback/api.py index 3b8d775..d164173 100644 --- a/dspback/api.py +++ b/dspback/api.py @@ -3,11 +3,10 @@ from fastapi import FastAPI, status from fastapi.openapi.utils import get_openapi from fastapi.staticfiles import StaticFiles +from pydantic import ValidationError from starlette.middleware.cors import CORSMiddleware from starlette.middleware.sessions import SessionMiddleware from starlette.responses import PlainTextResponse -from pydantic import ValidationError - from dspback.config import get_settings from dspback.dependencies import RepositoryException @@ -52,10 +51,10 @@ async def http_exception_handler(request, exc): return PlainTextResponse(f"Repository exception response[{str(exc.detail)}]", status_code=exc.status_code) + @app.exception_handler(ValidationError) async def validation_exception_handler(request, exc: ValidationError): - return PlainTextResponse(f"Request data validation errors: {str(exc)}", - status_code=status.HTTP_400_BAD_REQUEST) + return PlainTextResponse(f"Request data validation errors: {str(exc)}", status_code=status.HTTP_400_BAD_REQUEST) @app.on_event("startup") diff --git a/dspback/config/__init__.py b/dspback/config/__init__.py index 7377578..b4df631 100644 --- a/dspback/config/__init__.py +++ b/dspback/config/__init__.py @@ -44,6 +44,7 @@ class Settings(BaseSettings): zenodo_file_delete_url: HttpUrl zenodo_file_read_url: HttpUrl zenodo_view_url: HttpUrl + zenodo_public_view_url: HttpUrl zenodo_move_or_rename_url: HttpUrl zenodo_health_url: HttpUrl @@ -145,6 +146,7 @@ def get_settings(): "file_read": settings.zenodo_file_read_url, "move_or_rename_url": settings.zenodo_move_or_rename_url, "view_url": settings.zenodo_view_url, + "public_view_url": settings.zenodo_public_view_url, "schema": "/api/schema/zenodo/schema.json", "uischema": "/api/schema/zenodo/uischema.json", "schema_defaults": "/api/schema/zenodo/defaults.json", @@ -164,6 +166,7 @@ def get_settings(): "folder_delete": settings.hydroshare_folder_read_url, "move_or_rename_url": settings.hydroshare_move_or_rename_url, "view_url": settings.hydroshare_view_url, + "public_view_url": settings.hydroshare_view_url, "schema": "/api/schema/hydroshare/schema.json", "uischema": "/api/schema/hydroshare/uischema.json", "schema_defaults": "/api/schema/hydroshare/defaults.json", @@ -179,6 +182,7 @@ def get_settings(): "file_delete": settings.earthchem_file_delete_url, "file_read": settings.earthchem_file_read_url, "view_url": settings.earthchem_view_url, + "public_view_url": settings.earthchem_public_view_url, "schema": "/api/schema/earthchem/schema.json", "uischema": "/api/schema/earthchem/uischema.json", "schema_defaults": "/api/schema/earthchem/defaults.json", diff --git a/dspback/pydantic_schemas.py b/dspback/pydantic_schemas.py index c1cbd3f..f0218ec 100644 --- a/dspback/pydantic_schemas.py +++ b/dspback/pydantic_schemas.py @@ -141,7 +141,11 @@ def parse_publication_date(cls, value): def to_submission(self, identifier) -> Submission: settings = get_settings() - view_url = settings.zenodo_view_url % identifier + view_url = ( + settings.zenodo_public_view_url % identifier + if self.publication_date + else settings.zenodo_view_url % identifier + ) return Submission( title=self.title, authors=[creator.name for creator in self.creators], @@ -264,7 +268,11 @@ class License(BaseModel): def to_submission(self, identifier) -> Submission: settings = get_settings() - view_url = settings.earthchem_public_view_url % identifier + view_url = ( + settings.earthchem_public_view_url % identifier + if self.datePublished + else settings.earthchem_view_url % identifier + ) authors = [contributor.name for contributor in self.contributors] authors.insert(0, self.leadAuthor.name) return Submission( diff --git a/dspback/routers/earthchem.py b/dspback/routers/earthchem.py index c4fc824..e5c46e9 100644 --- a/dspback/routers/earthchem.py +++ b/dspback/routers/earthchem.py @@ -131,6 +131,16 @@ async def get_metadata_repository(self, request: Request, identifier) -> respons await self.submit(request, identifier=identifier, json_metadata=json_metadata) return json_metadata + @router.get( + '/submission/earthchem/{identifier}', + tags=["EarthChem"], + summary="Update and get an EarthChem record Submission", + description="Retrieves the metadata for the EarthChem record and returns the updated Submission in the database.", + ) + async def update_and_get_submission(self, request: Request, identifier): + await self.get_metadata_repository(request, identifier) + return self.user.submission(identifier) + @router.delete( '/metadata/earthchem/{identifier}', tags=["EarthChem"], diff --git a/dspback/routers/hydroshare.py b/dspback/routers/hydroshare.py index a26df99..c27f3da 100644 --- a/dspback/routers/hydroshare.py +++ b/dspback/routers/hydroshare.py @@ -142,6 +142,16 @@ async def _retrieve_metadata_from_repository(self, request: Request, identifier) if response.status_code >= 300: raise RepositoryException(status_code=response.status_code, detail=response.text) + """ + HydroShare maintenance mode + Parsing the response can fail if HydroShare is in maintenance mode. + The request to HydroShare will return status code 200, but contain an HTML response. + """ + try: + json_metadata = json.loads(response.text) + except: + raise RepositoryException(status_code=500, detail="Failed to parse JSON response") + json_metadata = json.loads(response.text) json_metadata = from_hydroshare_format(json_metadata) return self.wrap_metadata(json_metadata, exists_and_is("published", json_metadata)) @@ -159,6 +169,16 @@ async def get_metadata_repository(self, request: Request, identifier): await self.submit(request, identifier=identifier, json_metadata=json_metadata) return json_metadata + @router.get( + '/submission/hydroshare/{identifier}', + tags=["HydroShare"], + summary="Update and get a HydroShare resource Submission", + description="Retrieves the metadata for the HydroShare resource and returns the updated Submission in the database.", + ) + async def update_and_get_submission(self, request: Request, identifier): + await self.get_metadata_repository(request, identifier) + return self.user.submission(identifier) + @router.delete( '/metadata/hydroshare/{identifier}', tags=["HydroShare"], diff --git a/dspback/routers/zenodo.py b/dspback/routers/zenodo.py index 7a9cd26..f9ef615 100644 --- a/dspback/routers/zenodo.py +++ b/dspback/routers/zenodo.py @@ -123,6 +123,16 @@ async def get_metadata_repository(self, request: Request, identifier): return json_metadata + @router.get( + '/submission/zenodo/{identifier}', + tags=["Zenodo"], + summary="Update and get a Zenodo record Submission", + description="Retrieves the metadata for the Zenodo record and returns the updated Submission in the database.", + ) + async def update_and_get_submission(self, request: Request, identifier): + await self.get_metadata_repository(request, identifier) + return self.user.submission(identifier) + @router.delete( '/metadata/zenodo/{identifier}', tags=["Zenodo"], diff --git a/dspback/schemas/earthchem/schema.json b/dspback/schemas/earthchem/schema.json index 9d603de..ab60043 100644 --- a/dspback/schemas/earthchem/schema.json +++ b/dspback/schemas/earthchem/schema.json @@ -28,8 +28,7 @@ "description": { "title": "Abstract or Description", "type": "string", - "description": "Describe measurements, location, and purpose of the dataset", - "maxLength": 250 + "description": "Describe measurements, location, and purpose of the dataset" }, "community": { "title": "Community", diff --git a/dspback/schemas/external/schema.json b/dspback/schemas/external/schema.json index f2017ac..5ef48f8 100644 --- a/dspback/schemas/external/schema.json +++ b/dspback/schemas/external/schema.json @@ -172,9 +172,6 @@ "format": "date-time", "options": { "placeholder": "YYYY-MM-DDTHH:MM" - }, - "formatMinimum": { - "$data": "1/start" } } }, diff --git a/dspback/schemas/external/uischema.json b/dspback/schemas/external/uischema.json index 4bf2b99..cef2277 100644 --- a/dspback/schemas/external/uischema.json +++ b/dspback/schemas/external/uischema.json @@ -149,7 +149,31 @@ { "type": "Control", "label": "Temporal coverage", - "scope": "#/properties/temporalCoverage" + "scope": "#/properties/temporalCoverage", + "options": { + "detail": { + "type": "VerticalLayout", + "elements": [ + { + "type": "Control", + "scope": "#/properties/name" + }, + { + "type": "HorizontalLayout", + "elements": [ + { + "type": "Control", + "scope": "#/properties/start" + }, + { + "type": "Control", + "scope": "#/properties/end" + } + ] + } + ] + } + } }, { "type": "Control", diff --git a/dspback/schemas/hydroshare/schema.json b/dspback/schemas/hydroshare/schema.json index 1d1fa8c..c6b2608 100644 --- a/dspback/schemas/hydroshare/schema.json +++ b/dspback/schemas/hydroshare/schema.json @@ -624,9 +624,6 @@ "format": "date-time", "options": { "placeholder": "YYYY-MM-DDTHH:MM" - }, - "formatMinimum": { - "$data": "1/start" } } }, diff --git a/dspback/schemas/hydroshare/uischema.json b/dspback/schemas/hydroshare/uischema.json index 1e5192e..e1951fa 100644 --- a/dspback/schemas/hydroshare/uischema.json +++ b/dspback/schemas/hydroshare/uischema.json @@ -316,6 +316,7 @@ "scope": "#/properties/relations", "options": { "showSortButtons": true, + "collapsed": true, "elementLabelProp": [ "type", "value" diff --git a/tests/test_records.py b/tests/test_records.py index 0efe46c..e46f69e 100644 --- a/tests/test_records.py +++ b/tests/test_records.py @@ -24,6 +24,7 @@ async def test_hydroshare_to_submission(hydroshare): async def test_zenodo_to_submission(zenodo): zenodo_record = ZenodoRecord(**zenodo) + zenodo_record.publication_date = None zenodo_submission = zenodo_record.to_submission("947940") assert zenodo_submission.title == zenodo_record.title @@ -34,6 +35,18 @@ async def test_zenodo_to_submission(zenodo): assert zenodo_submission.url == get_settings().zenodo_view_url % zenodo_record.record_id +async def test_zenodo_published_to_submission(zenodo): + zenodo_record = ZenodoRecord(**zenodo) + zenodo_submission = zenodo_record.to_submission("947940") + + assert zenodo_submission.title == zenodo_record.title + assert zenodo_submission.authors == [creator.name for creator in zenodo_record.creators] + assert zenodo_submission.repo_type == RepositoryType.ZENODO + assert zenodo_submission.submitted <= datetime.utcnow() + assert zenodo_submission.identifier == zenodo_record.record_id + assert zenodo_submission.url == get_settings().zenodo_public_view_url % zenodo_record.record_id + + async def test_external_to_submission(external): external_record = ExternalRecord(**external) external_submission = external_record.to_submission("947940") @@ -71,6 +84,21 @@ async def test_external_to_jsonld(external): async def test_earthchem_to_submission(earthchem): + earthchem_record = EarthChemRecord(**earthchem) + earthchem_record.datePublished = None + earthchem_submission = earthchem_record.to_submission("947940") + + assert earthchem_submission.title == earthchem_record.title + authors = [f"{contributor.familyName}, {contributor.givenName}" for contributor in earthchem_record.contributors] + authors.insert(0, f"{earthchem_record.leadAuthor.familyName}, {earthchem_record.leadAuthor.givenName}") + assert earthchem_submission.authors == authors + assert earthchem_submission.repo_type == RepositoryType.EARTHCHEM + assert earthchem_submission.submitted <= datetime.utcnow() + assert earthchem_submission.identifier == "947940" + assert earthchem_submission.url == get_settings().earthchem_view_url % "947940" + + +async def test_earthchem_published_to_submission(earthchem): earthchem_record = EarthChemRecord(**earthchem) earthchem_submission = earthchem_record.to_submission("947940")