diff --git a/client/src/components/tag-input/index.jsx b/client/src/components/tag-input/index.jsx index 357b59fe..aeb6c13b 100644 --- a/client/src/components/tag-input/index.jsx +++ b/client/src/components/tag-input/index.jsx @@ -11,9 +11,11 @@ export default function TagInput({ onTagsChange, placeholder, tags, + deletedTags, }) { const [input, setInput] = useState(''); const [values, setValues] = useState(tags); + const [excludedValues, setExcludedValues] = useState(deletedTags); const handleKeyDown = (e) => { if ([9, 13].includes(e.keyCode) && input) { @@ -25,7 +27,7 @@ export default function TagInput({ const newValues = [...values, { label: input.trim(), source: 'user' }]; setValues(newValues); setInput(''); - onTagsChange(newValues); + onTagsChange(newValues, excludedValues); } }; @@ -38,13 +40,18 @@ export default function TagInput({ }, [input, onInputHandler]); const handleDeleteClick = (tag) => { + const deletedValues = excludedValues; + console.log('handleDeleteClick - deletedValues', deletedValues); + deletedValues.push(tag); const newValues = [...values.filter((el) => el !== tag)]; - // TODO this is buggy now, deleted tags should be memorized in the URL (searchParams) setValues(newValues); - onTagsChange(newValues); + setExcludedValues(deletedValues); + console.log('deletedValues', deletedValues); + onTagsChange(newValues, deletedValues); }; useEffect(() => setValues(tags), [tags]); + useEffect(() => setExcludedValues(deletedTags), [deletedTags]); return (
@@ -95,6 +102,7 @@ TagInput.propTypes = { onTagsChange: PropTypes.func.isRequired, placeholder: PropTypes.string, tags: PropTypes.arrayOf(PropTypes.object), + deletedTags: PropTypes.arrayOf(PropTypes.object), }; TagInput.defaultProps = { @@ -104,4 +112,5 @@ TagInput.defaultProps = { onInputHandler: () => { }, placeholder: '', tags: [], + deletedTags: [], }; diff --git a/client/src/pages/actions.jsx b/client/src/pages/actions.jsx index e28b3881..5cb7e2f5 100644 --- a/client/src/pages/actions.jsx +++ b/client/src/pages/actions.jsx @@ -80,7 +80,7 @@ Actions.propTypes = { worksNumber: PropTypes.number.isRequired, })).isRequired, allDatasets: PropTypes.arrayOf(PropTypes.shape({ - affiliations: PropTypes.arrayOf(PropTypes.object).isRequired, + affiliations: PropTypes.arrayOf(PropTypes.object), allIds: PropTypes.arrayOf(PropTypes.object).isRequired, datasource: PropTypes.arrayOf(PropTypes.string).isRequired, id: PropTypes.string.isRequired, diff --git a/client/src/pages/affiliationsView.jsx b/client/src/pages/affiliationsView.jsx index 40a69b9e..30f66217 100644 --- a/client/src/pages/affiliationsView.jsx +++ b/client/src/pages/affiliationsView.jsx @@ -2,7 +2,7 @@ import { Column } from 'primereact/column'; import { DataTable } from 'primereact/datatable'; import PropTypes from 'prop-types'; -import { nameTemplate, rorTemplate, statusTemplate } from '../utils/templates'; +import { nameTemplate, rorTemplate, statusTemplate, worksExampleTemplate } from '../utils/templates'; export default function AffiliationsView({ allAffiliations, @@ -33,10 +33,11 @@ export default function AffiliationsView({ value={allAffiliations} > - - - - + + + + + ); } diff --git a/client/src/pages/datasetsTab.jsx b/client/src/pages/datasetsTab.jsx index 546f429c..35e8c199 100644 --- a/client/src/pages/datasetsTab.jsx +++ b/client/src/pages/datasetsTab.jsx @@ -199,7 +199,7 @@ export default function DatasetsTab({ datasets, publishers, selectedDatasets, se DatasetsTab.propTypes = { datasets: PropTypes.arrayOf(PropTypes.shape({ - affiliations: PropTypes.arrayOf(PropTypes.object).isRequired, + affiliations: PropTypes.arrayOf(PropTypes.object), allIds: PropTypes.arrayOf(PropTypes.object).isRequired, authors: PropTypes.arrayOf(PropTypes.string).isRequired, datasource: PropTypes.arrayOf(PropTypes.string).isRequired, @@ -210,7 +210,7 @@ DatasetsTab.propTypes = { })).isRequired, publishers: PropTypes.object.isRequired, selectedDatasets: PropTypes.arrayOf(PropTypes.shape({ - affiliations: PropTypes.arrayOf(PropTypes.object).isRequired, + affiliations: PropTypes.arrayOf(PropTypes.object), allIds: PropTypes.arrayOf(PropTypes.object).isRequired, authors: PropTypes.arrayOf(PropTypes.string).isRequired, datasource: PropTypes.arrayOf(PropTypes.string).isRequired, diff --git a/client/src/pages/filters.jsx b/client/src/pages/filters.jsx index 8481d7ad..4ec357ee 100644 --- a/client/src/pages/filters.jsx +++ b/client/src/pages/filters.jsx @@ -31,21 +31,25 @@ export default function Filters({ sendQuery }) { if (searchParams.size === 0) { setSearchParams({ affiliations: [], + deletedAffiliations: [], datasets: false, - endYear: '2021', - startYear: '2021', + endYear: '2023', + startYear: '2023', }); setTags([]); } else { setCurrentSearchParams({ affiliations: searchParams.getAll('affiliations'), + deletedAffiliations: searchParams.getAll('deletedAffiliations'), datasets: searchParams.get('datasets') === 'true', - endYear: searchParams.get('endYear'), - startYear: searchParams.get('startYear'), + endYear: searchParams.get('endYear', '2023'), + startYear: searchParams.get('startYear', '2023'), }); const affiliations = searchParams.getAll('affiliations'); + const deletedAffiliations = searchParams.getAll('deletedAffiliations') || []; const queries = affiliations.map((affiliation) => getRorData(affiliation)); - const rorNames = await Promise.all(queries); + let rorNames = await Promise.all(queries); + rorNames = rorNames.filter((aff) => !deletedAffiliations.includes(aff)); const allTags = []; const knownTags = {}; affiliations.forEach((affiliation) => { @@ -58,14 +62,18 @@ export default function Filters({ sendQuery }) { }); rorNames.flat().forEach((rorElt) => { if (knownTags[rorElt.rorId.toLowerCase()] === undefined) { - allTags.push({ label: rorElt.rorId, source: 'ror', type: 'rorId' }); - knownTags[rorElt.rorId.toLowerCase()] = 1; + if (!deletedAffiliations.includes(rorElt.rorId)) { + allTags.push({ label: rorElt.rorId, source: 'ror', type: 'rorId' }); + knownTags[rorElt.rorId.toLowerCase()] = 1; + } } rorElt.names.forEach((rorName) => { if (knownTags[rorName.toLowerCase()] === undefined) { - const isDangerous = rorName.length < 4; - allTags.push({ label: rorName, source: 'ror', type: 'affiliationString', rorId: rorElt.rorId, isDangerous }); - knownTags[rorName.toLowerCase()] = 1; + if (!deletedAffiliations.includes(rorName)) { + const isDangerous = rorName.length < 4; + allTags.push({ label: rorName, source: 'ror', type: 'affiliationString', rorId: rorElt.rorId, isDangerous }); + knownTags[rorName.toLowerCase()] = 1; + } } }); }); @@ -75,8 +83,13 @@ export default function Filters({ sendQuery }) { getData(); }, [searchParams, setSearchParams]); - const onTagsChange = async (affiliations) => { - setSearchParams({ ...currentSearchParams, affiliations: affiliations.filter((affiliation) => affiliation.source === 'user').map((affiliation) => affiliation.label) }); + const onTagsChange = async (affiliations, deletedAffiliations) => { + const previousDeleted = currentSearchParams.deletedAffiliations || []; + setSearchParams({ + ...currentSearchParams, + affiliations: affiliations.filter((affiliation) => affiliation.source === 'user').map((affiliation) => affiliation.label), + deletedAffiliations: deletedAffiliations.filter((affiliation) => affiliation.source !== 'user').map((affiliation) => affiliation.label).concat(previousDeleted), + }); }; const checkAndSendQuery = () => { @@ -141,7 +154,7 @@ export default function Filters({ sendQuery }) { { if (data) { // TODO do it on the API - const allDatasetsTmp = data.datasets.results - .map((dataset) => ({ + const allDatasetsTmp = data.datasets?.results + ?.map((dataset) => ({ ...dataset, affiliationsHtml: getAffiliationsHtmlField(dataset, regexp), affiliationsTooltip: getAffiliationsTooltipField(dataset), status: status.tobedecided.id, })); - const allPublicationsTmp = data.publications.results - .map((publication) => ({ + const allPublicationsTmp = data.publications?.results + ?.map((publication) => ({ ...publication, affiliationsHtml: getAffiliationsHtmlField(publication, regexp), affiliationsTooltip: getAffiliationsTooltipField(publication), @@ -164,7 +164,7 @@ export default function Home() { )} - {!isFetching && (allAffiliations.length > 0 || allDatasets.length > 0 || allPublications.length > 0) && ( + {!isFetching && (allAffiliations?.length > 0 || allDatasets?.length > 0 || allPublications?.length > 0) && ( diff --git a/client/src/utils/templates.jsx b/client/src/utils/templates.jsx index deedd836..91036a56 100644 --- a/client/src/utils/templates.jsx +++ b/client/src/utils/templates.jsx @@ -40,6 +40,19 @@ const linkedDOITemplate = (rowData) => { return ; }; +//TODO: is there a way not to duplicate code : linkedDOITemplate and allIdsTemplate are the same but do not use the same field +const worksExampleTemplate = (rowData) => { + let html = '
    '; + rowData.worksExample.filter((e) => ['doi', 'hal_id', 'crossref', 'datacite'].includes(e.id_type)).slice(0, 5).forEach((id) => { + html += `
  • ${id.id_type}:`; + const idLink = getIdLink(id.id_type, id.id_value); + html += idLink ? `${id.id_value}` : `${id.id_value}`; + html += '
  • '; + }); + html += '
'; + return ; +}; + const rorTemplate = (rowData) => { let html = '
    '; rowData.rors.forEach((id) => { @@ -116,4 +129,5 @@ export { nameTemplate, rorTemplate, statusTemplate, + worksExampleTemplate }; diff --git a/server/src/utils/works.js b/server/src/utils/works.js index 0e0d8dcf..db33482e 100644 --- a/server/src/utils/works.js +++ b/server/src/utils/works.js @@ -283,6 +283,9 @@ const groupByAffiliations = ({ options, works }) => { if (displayAffiliation.includes('')) { if (deduplicatedAffiliations?.[normalizedAffiliation]) { deduplicatedAffiliations[normalizedAffiliation].works.push(id); + if (deduplicatedAffiliations[normalizedAffiliation].worksExample.length < 10) { + deduplicatedAffiliations[normalizedAffiliation].worksExample.push(work.allIds); + } } else { // eslint-disable-next-line no-param-reassign deduplicatedAffiliations[normalizedAffiliation] = { @@ -293,10 +296,8 @@ const groupByAffiliations = ({ options, works }) => { key: affiliation.key, status: 'tobedecided', works: [id], + worksExample: [work.allIds], }; - if (affiliation.key.includes('essec business school cergy [ source: OpenAlex ]')) { - console.log('ttttt', affiliation.key); - } } } } @@ -306,10 +307,12 @@ const groupByAffiliations = ({ options, works }) => { allAffiliationsTmp = Object.values(allAffiliationsTmp) .map((affiliation, index) => { const uniqueWorks = [...new Set(affiliation.works)]; + const uniqueWorksExample = [...new Set(affiliation.worksExample.flat())]; return ({ ...affiliation, id: index.toString(), works: uniqueWorks, + worksExample: uniqueWorksExample, worksNumber: uniqueWorks.length, }); });