From cd9f8449b9924ef469cae8af87b61a3bdb19e3a2 Mon Sep 17 00:00:00 2001 From: Jake Rosenberg Date: Tue, 18 Feb 2025 12:41:57 -0600 Subject: [PATCH 1/3] Serve published projects at a world-readable /publications URL (#1046) Co-authored-by: Shayan Khan --- .../CombinedBreadcrumbs.jsx | 1 + .../DataFilesDropdown/DataFilesDropdown.jsx | 3 +- .../DataFilesListing/DataFilesListing.jsx | 4 +- .../DataFilesListingCells.jsx | 3 +- .../DataFilesProjectFileListing.jsx | 3 +- .../DataFilesPublicationsList.jsx | 9 ++-- .../PublicationDetailPublicView.jsx | 43 +++++++++++++++++++ .../Publications/PublicationsPublicView.jsx | 14 ++++++ client/src/components/Workbench/AppRouter.jsx | 15 ++++++- .../DataFilesProjectFileListingAddon.jsx | 2 +- ...esProjectFileListingMetadataTitleAddon.jsx | 2 +- server/conf/nginx/nginx.conf | 2 +- server/portal/apps/datafiles/views.py | 6 ++- server/portal/apps/projects/views.py | 9 ++-- server/portal/urls.py | 1 + 15 files changed, 102 insertions(+), 15 deletions(-) create mode 100644 client/src/components/Publications/PublicationDetailPublicView.jsx create mode 100644 client/src/components/Publications/PublicationsPublicView.jsx diff --git a/client/src/components/DataFiles/CombinedBreadcrumbs/CombinedBreadcrumbs.jsx b/client/src/components/DataFiles/CombinedBreadcrumbs/CombinedBreadcrumbs.jsx index 7f5ce80c5..914773b18 100644 --- a/client/src/components/DataFiles/CombinedBreadcrumbs/CombinedBreadcrumbs.jsx +++ b/client/src/components/DataFiles/CombinedBreadcrumbs/CombinedBreadcrumbs.jsx @@ -20,6 +20,7 @@ CombinedBreadcrumbs.propTypes = { path: PropTypes.string.isRequired, section: PropTypes.string.isRequired, isPublic: PropTypes.bool, + basePath: PropTypes.string, className: PropTypes.string, }; diff --git a/client/src/components/DataFiles/DataFilesDropdown/DataFilesDropdown.jsx b/client/src/components/DataFiles/DataFilesDropdown/DataFilesDropdown.jsx index 33affeef4..e91ebd317 100644 --- a/client/src/components/DataFiles/DataFilesDropdown/DataFilesDropdown.jsx +++ b/client/src/components/DataFiles/DataFilesDropdown/DataFilesDropdown.jsx @@ -17,6 +17,7 @@ const BreadcrumbsDropdown = ({ scheme, system, path, + basePath, section, isPublic, }) => { @@ -36,7 +37,7 @@ const BreadcrumbsDropdown = ({ : null; const handleNavigation = (targetPath) => { - const basePath = isPublic ? '/public-data' : '/workbench/data'; + if (!basePath) basePath = isPublic ? '/public-data' : '/workbench/data'; let url; if (scheme === 'projects' && targetPath === systemName) { diff --git a/client/src/components/DataFiles/DataFilesListing/DataFilesListing.jsx b/client/src/components/DataFiles/DataFilesListing/DataFilesListing.jsx index 0552a10a3..98991cda1 100644 --- a/client/src/components/DataFiles/DataFilesListing/DataFilesListing.jsx +++ b/client/src/components/DataFiles/DataFilesListing/DataFilesListing.jsx @@ -45,6 +45,7 @@ const DataFilesListing = ({ path, isPublic, rootSystem, + basePath, }) => { // Redux hooks const location = useLocation(); @@ -54,7 +55,7 @@ const DataFilesListing = ({ ); const sharedWorkspaces = systems.find((e) => e.scheme === 'projects'); const isPortalProject = scheme === 'projects'; - const hideSearchBar = isPortalProject && sharedWorkspaces.hideSearchBar; + const hideSearchBar = isPortalProject && sharedWorkspaces?.hideSearchBar; const showViewPath = useSelector( (state) => @@ -101,6 +102,7 @@ const DataFilesListing = ({ scheme={scheme} href={row.original._links.self.href} isPublic={isPublic} + basePath={basePath} length={row.original.length} metadata={row.original.metadata} /> diff --git a/client/src/components/DataFiles/DataFilesListing/DataFilesListingCells.jsx b/client/src/components/DataFiles/DataFilesListing/DataFilesListingCells.jsx index c3d3593b7..7899e3f8f 100644 --- a/client/src/components/DataFiles/DataFilesListing/DataFilesListingCells.jsx +++ b/client/src/components/DataFiles/DataFilesListing/DataFilesListingCells.jsx @@ -59,6 +59,7 @@ export const FileNavCell = React.memo( scheme, href, isPublic, + basePath, length, metadata, rootSystem, @@ -80,7 +81,7 @@ export const FileNavCell = React.memo( }); }; - const basePath = isPublic ? '/public-data' : '/workbench/data'; + if (!basePath) basePath = isPublic ? '/public-data' : '/workbench/data'; return ( <> diff --git a/client/src/components/DataFiles/DataFilesProjectFileListing/DataFilesProjectFileListing.jsx b/client/src/components/DataFiles/DataFilesProjectFileListing/DataFilesProjectFileListing.jsx index cde79cdee..95698a10a 100644 --- a/client/src/components/DataFiles/DataFilesProjectFileListing/DataFilesProjectFileListing.jsx +++ b/client/src/components/DataFiles/DataFilesProjectFileListing/DataFilesProjectFileListing.jsx @@ -54,7 +54,7 @@ const DataFilesProjectFileListing = ({ rootSystem, system, path }) => { metadata.members .filter((member) => member.user - ? member.user.username === state.authenticatedUser.user.username + ? member.user.username === state.authenticatedUser?.user?.username : { access: null } ) .map((currentUser) => currentUser.access === 'owner')[0] @@ -172,6 +172,7 @@ const DataFilesProjectFileListing = ({ rootSystem, system, path }) => { scheme="projects" system={system} path={path || '/'} + basePath="/publications" rootSystem={rootSystem} /> diff --git a/client/src/components/DataFiles/DataFilesPublicationsList/DataFilesPublicationsList.jsx b/client/src/components/DataFiles/DataFilesPublicationsList/DataFilesPublicationsList.jsx index e17c81016..85558fcd3 100644 --- a/client/src/components/DataFiles/DataFilesPublicationsList/DataFilesPublicationsList.jsx +++ b/client/src/components/DataFiles/DataFilesPublicationsList/DataFilesPublicationsList.jsx @@ -14,11 +14,13 @@ import './DataFilesPublicationsList.scss'; import Searchbar from '_common/Searchbar'; import { formatDate, formatDateTimeFromValue } from 'utils/timeFormat'; -const DataFilesPublicationsList = ({ rootSystem }) => { +const DataFilesPublicationsList = ({ rootSystem, basePath }) => { const { error, loading, publications } = useSelector( (state) => state.publications.listing ); + const _basePath = basePath ?? '/workbench/data'; + const query = queryStringParser.parse(useLocation().search); const systems = useSelector( @@ -38,7 +40,7 @@ const DataFilesPublicationsList = ({ rootSystem }) => { type: 'PUBLICATIONS_GET_PUBLICATIONS', payload: { queryString: query.query_string, - system: selectedSystem.system, + system: selectedSystem?.system, }, }); }, [dispatch, query.query_string]); @@ -60,7 +62,7 @@ const DataFilesPublicationsList = ({ rootSystem }) => { Cell: (el) => ( {el.value} @@ -141,6 +143,7 @@ const DataFilesPublicationsList = ({ rootSystem }) => { isLoading={loading} noDataText={noDataText} className="publications-listing" + columnMemoProps={[selectedSystem]} /> diff --git a/client/src/components/Publications/PublicationDetailPublicView.jsx b/client/src/components/Publications/PublicationDetailPublicView.jsx new file mode 100644 index 000000000..8db841f09 --- /dev/null +++ b/client/src/components/Publications/PublicationDetailPublicView.jsx @@ -0,0 +1,43 @@ +import React from 'react'; +import CombinedBreadcrumbs from '../DataFiles/CombinedBreadcrumbs/CombinedBreadcrumbs'; +import DataFilesPreviewModal from '../DataFiles/DataFilesModals/DataFilesPreviewModal'; +import DataFilesProjectCitationModal from '../DataFiles/DataFilesModals/DataFilesProjectCitationModal'; +import DataFilesProjectTreeModal from '../DataFiles/DataFilesModals/DataFilesProjectTreeModal'; +import DataFilesPublicationAuthorsModal from '../DataFiles/DataFilesModals/DataFilesPublicationAuthorsModal'; +import DataFilesShowPathModal from '../DataFiles/DataFilesModals/DataFilesShowPathModal'; +import DataFilesViewDataModal from '../DataFiles/DataFilesModals/DataFilesViewDataModal'; +import DataFilesProjectFileListing from '../DataFiles/DataFilesProjectFileListing/DataFilesProjectFileListing'; + +function PublicationDetailPublicView({params}) { + return ( +
+ + + + + + + + +
+ ); +} + +export default PublicationDetailPublicView; diff --git a/client/src/components/Publications/PublicationsPublicView.jsx b/client/src/components/Publications/PublicationsPublicView.jsx new file mode 100644 index 000000000..609ea73cf --- /dev/null +++ b/client/src/components/Publications/PublicationsPublicView.jsx @@ -0,0 +1,14 @@ +import React from 'react'; +import DataFilesPublicationsList from '../DataFiles/DataFilesPublicationsList/DataFilesPublicationsList'; +import DataFilesProjectDescriptionModal from '../DataFiles/DataFilesModals/DataFilesProjectDescriptionModal'; + +function PublicationsPublicView() { + return ( +
+ + +
+ ); +} + +export default PublicationsPublicView \ No newline at end of file diff --git a/client/src/components/Workbench/AppRouter.jsx b/client/src/components/Workbench/AppRouter.jsx index 945969607..66a8741bf 100644 --- a/client/src/components/Workbench/AppRouter.jsx +++ b/client/src/components/Workbench/AppRouter.jsx @@ -1,7 +1,7 @@ import React, { useEffect } from 'react'; import { useDispatch, useSelector } from 'react-redux'; import { useSystems } from 'hooks/datafiles'; -import { BrowserRouter as Router, Route } from 'react-router-dom'; +import { BrowserRouter as Router, Route, Redirect } from 'react-router-dom'; import Workbench from './Workbench'; import * as ROUTES from '../../constants/routes'; import TicketStandaloneCreate from '../Tickets/TicketStandaloneCreate'; @@ -9,6 +9,9 @@ import PublicData from '../PublicData/PublicData'; import RequestAccess from '../RequestAccess/RequestAccess'; import GoogleDrivePrivacyPolicy from '../ManageAccount/GoogleDrivePrivacyPolicy'; import SiteSearch from '../SiteSearch'; +import PublicationsPublicView from '../Publications/PublicationsPublicView'; +import PublicationDetailPublicView from '../Publications/PublicationDetailPublicView'; + function AppRouter() { const dispatch = useDispatch(); @@ -45,6 +48,16 @@ function AppRouter() { + + + + + { + return ; + }} + /> { const { canEditDataset, canRequestPublication, canReviewPublication } = useSelector((state) => { const { members } = state.projects.metadata; - const { username } = state.authenticatedUser.user; + const { username } = state.authenticatedUser?.user ?? {}; const currentUser = members.find( (member) => member.user?.username === username ); diff --git a/client/src/components/_custom/drp/DataFilesProjectFileListingMetadataTitleAddon/DataFilesProjectFileListingMetadataTitleAddon.jsx b/client/src/components/_custom/drp/DataFilesProjectFileListingMetadataTitleAddon/DataFilesProjectFileListingMetadataTitleAddon.jsx index a1acd41c6..24b01ded0 100644 --- a/client/src/components/_custom/drp/DataFilesProjectFileListingMetadataTitleAddon/DataFilesProjectFileListingMetadataTitleAddon.jsx +++ b/client/src/components/_custom/drp/DataFilesProjectFileListingMetadataTitleAddon/DataFilesProjectFileListingMetadataTitleAddon.jsx @@ -23,7 +23,7 @@ const DataFilesProjectFileListingMetadataTitleAddon = ({ const userAccess = state.projects.metadata.members .filter((member) => member.user - ? member.user.username === state.authenticatedUser.user.username + ? member.user.username === state.authenticatedUser?.user?.username : { access: null } ) .map((currentUser) => { diff --git a/server/conf/nginx/nginx.conf b/server/conf/nginx/nginx.conf index 2ca304cb1..23631de4b 100644 --- a/server/conf/nginx/nginx.conf +++ b/server/conf/nginx/nginx.conf @@ -80,7 +80,7 @@ http { alias /srv/www/portal/server/docs; } - location ~ ^/(core|auth|workbench|tickets|googledrive-privacy-policy|public-data|request-access|accounts|api|login|webhooks|search) { + location ~ ^/(core|auth|workbench|tickets|googledrive-privacy-policy|public-data|publications|request-access|accounts|api|login|webhooks|search) { proxy_pass http://portal_core; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header Host $host; diff --git a/server/portal/apps/datafiles/views.py b/server/portal/apps/datafiles/views.py index c3630ded1..76f8a6a7e 100644 --- a/server/portal/apps/datafiles/views.py +++ b/server/portal/apps/datafiles/views.py @@ -55,7 +55,7 @@ def get(self, request): response['default_host'] = system_def.host response['default_system'] = system_id else: - response['system_list'] = [sys for sys in portal_systems if sys['scheme'] == 'public'] + response['system_list'] = [sys for sys in portal_systems if sys['scheme'] == 'public' or sys['system'] == settings.PORTAL_PROJECTS_PUBLISHED_ROOT_SYSTEM_NAME] return JsonResponse(response) @@ -71,6 +71,8 @@ def get(self, request, systemId): public_sys = next((sys for sys in settings.PORTAL_DATAFILES_STORAGE_SYSTEMS if sys['scheme'] == 'public'), None) if public_sys and public_sys['system'] == systemId: client = service_account() + if systemId.startswith(settings.PORTAL_PROJECTS_PUBLISHED_SYSTEM_PREFIX): + client = service_account() else: return JsonResponse({'message': 'Unauthorized'}, status=401) system_def = client.systems.getSystem(systemId=systemId) @@ -92,6 +94,8 @@ def get(self, request, operation=None, scheme=None, system=None, path='/'): public_sys = next((sys for sys in settings.PORTAL_DATAFILES_STORAGE_SYSTEMS if sys['scheme'] == 'public'), None) if public_sys and public_sys['system'] == system and path.startswith(public_sys['homeDir'].strip('/')): client = service_account() + if system and system.startswith(settings.PORTAL_PROJECTS_PUBLISHED_SYSTEM_PREFIX): + client = service_account() else: return JsonResponse( {'message': 'This data requires authentication to view.'}, diff --git a/server/portal/apps/projects/views.py b/server/portal/apps/projects/views.py index 8350cdcd3..18bd9a155 100644 --- a/server/portal/apps/projects/views.py +++ b/server/portal/apps/projects/views.py @@ -10,6 +10,7 @@ from django.conf import settings from django.http import JsonResponse from django.utils.decorators import method_decorator +from portal.libs.agave.utils import service_account from portal.utils.decorators import agave_jwt_login from portal.exceptions.api import ApiException from portal.views.base import BaseApiView @@ -157,7 +158,6 @@ def post(self, request): # pylint: disable=no-self-use @method_decorator(agave_jwt_login, name='dispatch') -@method_decorator(login_required, name='dispatch') class ProjectInstanceApiView(BaseApiView): """Project Instance API view. @@ -178,8 +178,11 @@ def get(self, request, project_id=None, system_id=None): # Based on url mapping, either system_id or project_id is always available. if system_id is not None: project_id = system_id.split(f"{settings.PORTAL_PROJECTS_SYSTEM_PREFIX}.")[1] - - client = request.user.tapis_oauth.client + + if system_id and system_id.startswith(settings.PORTAL_PROJECTS_PUBLISHED_SYSTEM_PREFIX): + client = service_account() + else: + client = request.user.tapis_oauth.client prj = get_project(client, project_id) diff --git a/server/portal/urls.py b/server/portal/urls.py index f702a8c12..fa8294bae 100644 --- a/server/portal/urls.py +++ b/server/portal/urls.py @@ -103,6 +103,7 @@ namespace='googledrive-privacy-policy')), path('workbench/', include('portal.apps.workbench.urls', namespace='workbench')), path('public-data/', include('portal.apps.public_data.urls', namespace='public')), + path('publications/', include('portal.apps.public_data.urls', namespace='publications')), path('request-access/', include('portal.apps.request_access.urls', namespace='request_access')), path('search/', include('portal.apps.site_search.urls', namespace='site_search')), From bf07c6ad91d4ab555ad80f344deca57a3ae31352 Mon Sep 17 00:00:00 2001 From: Jake Rosenberg Date: Tue, 18 Feb 2025 13:49:55 -0600 Subject: [PATCH 2/3] Task/WC-120: Datacite operations and pipeline integration (#1037) * Rebase datacite operations on latest DRP branch * integrate datacite utils in pipeline --------- Co-authored-by: Shayan Khan --- .../datacite_operations.py | 130 ++++++++++++++++++ .../project_publish_operations.py | 11 +- server/portal/settings/settings.py | 15 ++ server/portal/settings/settings_default.py | 5 + .../settings/settings_secret.example.py | 3 + 5 files changed, 163 insertions(+), 1 deletion(-) create mode 100644 server/portal/apps/projects/workspace_operations/datacite_operations.py diff --git a/server/portal/apps/projects/workspace_operations/datacite_operations.py b/server/portal/apps/projects/workspace_operations/datacite_operations.py new file mode 100644 index 000000000..218e4b11c --- /dev/null +++ b/server/portal/apps/projects/workspace_operations/datacite_operations.py @@ -0,0 +1,130 @@ +import datetime +from typing import Optional +import json +import requests +import networkx as nx +from django.conf import settings + + +def get_datacite_json(pub_graph: nx.DiGraph): + """ + Generate datacite payload for a publishable entity. `pub_graph` is the output of + either `get_publication_subtree` or `get_publication_full_tree`. + """ + + datacite_json = {} + + base_meta_node = "NODE_ROOT" + + base_meta = pub_graph.nodes[base_meta_node]["value"] + + author_attr = [] + institutions = [] + for author in base_meta.get("authors", []): + author_attr.append( + { + "nameType": "Personal", + "givenName": author.get("first_name", ""), + "familyName": author.get("last_name", ""), + } + ) + institutions.append(author.get("inst", "")) + + datacite_json["contributors"] = [ + { + "contributorType": "HostingInstitution", + "nameType": "Organizational", + "name": institution, + } + for institution in list(set(institutions)) + ] + datacite_json["creators"] = author_attr + datacite_json["titles"] = [{"title": base_meta["title"]}] + + datacite_json["publisher"] = "Digital Rocks Portal" + + datacite_json["publicationYear"] = datetime.datetime.now().year + + project_id = base_meta["projectId"] + datacite_url = f"{settings.PORTAL_PUBLICATION_DATACITE_URL_PREFIX}/{project_id}" + + datacite_json["url"] = datacite_url + datacite_json["prefix"] = settings.PORTAL_PUBLICATION_DATACITE_SHOULDER + + return datacite_json + + +def upsert_datacite_json(datacite_json: dict, doi: Optional[str] = None): + """ + Create a draft DOI in datacite with the specified metadata. If a DOI is specified, + the metadata for that DOI is updated instead. + """ + if doi: + datacite_json.pop("publicationYear", None) + + datacite_payload = { + "data": { + "type": "dois", + "relationships": { + "client": {"data": {"type": "clients", "id": "tdl.tacc"}} + }, + "attributes": datacite_json, + } + } + if not doi: + res = requests.post( + f"{settings.DATACITE_URL.strip('/')}/dois", + auth=(settings.DATACITE_USER, settings.DATACITE_PASS), + data=json.dumps(datacite_payload), + headers={"Content-Type": "application/vnd.api+json"}, + timeout=30, + ) + else: + res = requests.put( + f"{settings.DATACITE_URL.strip('/')}/dois/{doi}", + auth=(settings.DATACITE_USER, settings.DATACITE_PASS), + data=json.dumps(datacite_payload), + headers={"Content-Type": "application/vnd.api+json"}, + timeout=30, + ) + + return res.json() + + +def publish_datacite_doi(doi: str): + """ + Set a DOI's status to `Findable` in Datacite. + """ + payload = {"data": {"type": "dois", "attributes": {"event": "publish"}}} + + res = requests.put( + f"{settings.DATACITE_URL.strip('/')}/dois/{doi}", + auth=(settings.DATACITE_USER, settings.DATACITE_PASS), + data=json.dumps(payload), + headers={"Content-Type": "application/vnd.api+json"}, + timeout=30, + ) + return res.json() + + +def hide_datacite_doi(doi: str): + """ + Remove a Datacite DOI from public consumption. + """ + payload = {"data": {"type": "dois", "attributes": {"event": "hide"}}} + + res = requests.put( + f"{settings.DATACITE_URL.strip('/')}/dois/{doi}", + auth=(settings.DATACITE_USER, settings.DATACITE_PASS), + data=json.dumps(payload), + headers={"Content-Type": "application/vnd.api+json"}, + timeout=30, + ) + return res.json() + + +def get_doi_publication_date(doi: str) -> str: + """Look up the publication date for a DOI""" + res = requests.get(f"{settings.DATACITE_URL.strip('/')}/dois/{doi}", timeout=30) + res.raise_for_status() + return res.json()["data"]["attributes"]["created"] diff --git a/server/portal/apps/projects/workspace_operations/project_publish_operations.py b/server/portal/apps/projects/workspace_operations/project_publish_operations.py index 1cd37ac71..b25d6dfd6 100644 --- a/server/portal/apps/projects/workspace_operations/project_publish_operations.py +++ b/server/portal/apps/projects/workspace_operations/project_publish_operations.py @@ -8,6 +8,7 @@ from portal.apps._custom.drp import constants from portal.libs.agave.utils import user_account, service_account from portal.apps.publications.models import Publication, PublicationRequest +from portal.apps.projects.workspace_operations.datacite_operations import get_datacite_json, upsert_datacite_json, publish_datacite_doi from django.db import transaction from portal.apps.projects.workspace_operations.graph_operations import remove_trash_nodes from portal.apps.search.tasks import index_publication @@ -118,7 +119,12 @@ def publish_project(self, project_id: str, version: Optional[int] = 1): value=nx.node_link_data(publication_tree), ) - doi = 'test_doi' # Replace with actual DOI retrieval logic + # Mint a DataCite DOI + existing_doi = source_project.value.get("doi", None) + + datacite_json = get_datacite_json(publication_tree) + datacite_resp = upsert_datacite_json(datacite_json, doi=existing_doi) + doi = datacite_resp["data"]["id"] # Update project metadata with datacite doi source_project_id = f'{settings.PORTAL_PROJECTS_SYSTEM_PREFIX}.{project_id}' @@ -140,6 +146,9 @@ def publish_project(self, project_id: str, version: Optional[int] = 1): defaults={"value": published_project.value, "tree": nx.node_link_data(pub_tree), "version": version}, ) + if not settings.DEBUG: + publish_datacite_doi(doi) + index_publication(project_id) # transfer files diff --git a/server/portal/settings/settings.py b/server/portal/settings/settings.py index 8daa52252..edab9df08 100644 --- a/server/portal/settings/settings.py +++ b/server/portal/settings/settings.py @@ -592,6 +592,21 @@ PORTAL_PUBLICATION_REVIEWERS_GROUP_NAME = settings_custom.\ _PORTAL_PUBLICATION_REVIEWERS_GROUP_NAME +PORTAL_PUBLICATION_DATACITE_SHOULDER = settings_custom.\ + _PORTAL_PUBLICATION_DATACITE_SHOULDER + +PORTAL_PUBLICATION_DATACITE_URL_PREFIX = settings_custom.\ + _PORTAL_PUBLICATION_DATACITE_URL_PREFIX + +DATACITE_URL = settings_custom.\ + _DATACITE_URL + +DATACITE_USER = settings_secret.\ + _DATACITE_USER + +DATACITE_PASS = settings_secret.\ + _DATACITE_PASS + PORTAL_PROJECTS_PRIVATE_KEY = settings_secret.\ _PORTAL_PROJECTS_PRIVATE_KEY diff --git a/server/portal/settings/settings_default.py b/server/portal/settings/settings_default.py index 6ba54927f..977988dcc 100644 --- a/server/portal/settings/settings_default.py +++ b/server/portal/settings/settings_default.py @@ -223,6 +223,11 @@ _PORTAL_PUBLICATION_REVIEWERS_GROUP_NAME = 'PROJECT_REVIEWER' +# Datacite +_PORTAL_PUBLICATION_DATACITE_SHOULDER = "10.80023" +_PORTAL_PUBLICATION_DATACITE_URL_PREFIX = "https://cep.test/data/tapis/projects/drp.project.published.test" +_DATACITE_URL = "https://api.test.datacite.org/" + ######################## # Custom Portal Template Assets # Asset path root is static files output dir. diff --git a/server/portal/settings/settings_secret.example.py b/server/portal/settings/settings_secret.example.py index 5c3d20d92..5260edc1b 100644 --- a/server/portal/settings/settings_secret.example.py +++ b/server/portal/settings/settings_secret.example.py @@ -116,3 +116,6 @@ "directory": "external-resources" } } + +_DATACITE_USER = "tdl.tacc" +_DATACITE_PASS = "CHANGEME" From 1a7a491b590ff34201d673387a6bc38ab357dca8 Mon Sep 17 00:00:00 2001 From: Jake Rosenberg Date: Tue, 18 Feb 2025 15:04:54 -0600 Subject: [PATCH 3/3] Fix workbench publications linking to public area (#1060) --- .../DataFilesProjectFileListing.jsx | 10 ++++++++-- .../Publications/PublicationDetailPublicView.jsx | 3 ++- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/client/src/components/DataFiles/DataFilesProjectFileListing/DataFilesProjectFileListing.jsx b/client/src/components/DataFiles/DataFilesProjectFileListing/DataFilesProjectFileListing.jsx index 95698a10a..cad7f104d 100644 --- a/client/src/components/DataFiles/DataFilesProjectFileListing/DataFilesProjectFileListing.jsx +++ b/client/src/components/DataFiles/DataFilesProjectFileListing/DataFilesProjectFileListing.jsx @@ -16,10 +16,16 @@ import { import DataFilesListing from '../DataFilesListing/DataFilesListing'; import styles from './DataFilesProjectFileListing.module.scss'; -const DataFilesProjectFileListing = ({ rootSystem, system, path }) => { +const DataFilesProjectFileListing = ({ + rootSystem, + system, + path, + basePath, +}) => { const dispatch = useDispatch(); const { fetchListing } = useFileListing('FilesListing'); const { isPublicationSystem, isReviewSystem } = useSystems(); + if (!basePath) basePath = '/workbench/data'; // logic to render addonComponents for DRP const portalName = useSelector((state) => state.workbench.portalName); @@ -172,7 +178,7 @@ const DataFilesProjectFileListing = ({ rootSystem, system, path }) => { scheme="projects" system={system} path={path || '/'} - basePath="/publications" + basePath={basePath} rootSystem={rootSystem} /> diff --git a/client/src/components/Publications/PublicationDetailPublicView.jsx b/client/src/components/Publications/PublicationDetailPublicView.jsx index 8db841f09..c7eb1f167 100644 --- a/client/src/components/Publications/PublicationDetailPublicView.jsx +++ b/client/src/components/Publications/PublicationDetailPublicView.jsx @@ -8,7 +8,7 @@ import DataFilesShowPathModal from '../DataFiles/DataFilesModals/DataFilesShowPa import DataFilesViewDataModal from '../DataFiles/DataFilesModals/DataFilesViewDataModal'; import DataFilesProjectFileListing from '../DataFiles/DataFilesProjectFileListing/DataFilesProjectFileListing'; -function PublicationDetailPublicView({params}) { +function PublicationDetailPublicView({ params }) { return (