diff --git a/src/app/mydata/components/profile/data-sources/data-sources-filters/data-sources-filters.component.ts b/src/app/mydata/components/profile/data-sources/data-sources-filters/data-sources-filters.component.ts index 2800d9e5c..384bf528c 100644 --- a/src/app/mydata/components/profile/data-sources/data-sources-filters/data-sources-filters.component.ts +++ b/src/app/mydata/components/profile/data-sources/data-sources-filters/data-sources-filters.component.ts @@ -64,8 +64,6 @@ export class DataSourcesFiltersComponent const handleDataFilter = (category: string) => filterData(profileData, this.activeFilters, category); - console.log('profileData', profileData); - // JSON structure mimics the one we got in Portal from Elasticsearch const filters = { aggregations: { diff --git a/src/app/mydata/services/search-portal.service.ts b/src/app/mydata/services/search-portal.service.ts index 48f2773fc..eebdc825e 100644 --- a/src/app/mydata/services/search-portal.service.ts +++ b/src/app/mydata/services/search-portal.service.ts @@ -12,7 +12,7 @@ export interface Respone { } @Injectable({ - providedIn: 'root', + providedIn: 'root' }) export class SearchPortalService { apiUrl: string; @@ -49,7 +49,7 @@ export class SearchPortalService { case 'name': { switch (groupId) { case 'publication': { - sortField = 'publicationName.keyword'; + sortField = 'publicationYear'; break; } case 'dataset': { @@ -66,8 +66,8 @@ export class SearchPortalService { this.currentSort = { [sortField]: { - order: sortSettings.direction, - }, + order: sortSettings.direction + } }; } @@ -105,26 +105,42 @@ export class SearchPortalService { } getData(term: string, groupId: string) { - // Default sort to descending - const sort = this.currentSort - ? this.currentSort - : { - [this.getDefaultSortField(groupId)]: { order: 'desc' }, + + // Leverage query generation method from portal + const finalQuery = { + 'bool': { + 'must': [{ 'term': { '_index': groupId } }, this.searchSettingsService.querySettings(groupId, term)] + } + }; + let sort = undefined; + + // Queries and sorts are different for publication category, default sort is descending + if (groupId === 'publication') { + const publicationSort = this.currentSort + ? this.currentSort + : { + [this.getDefaultSortField(groupId)]: { order: 'desc' } }; + sort = ['_score', publicationSort, '_score']; + } else { + const generalSort = this.currentSort + ? this.currentSort + : { + [this.getDefaultSortField(groupId)]: { order: 'desc', unmapped_type: 'long' } + }; + sort = [generalSort, '_score', '_score']; + } const pageSettings = this.pageSettings; - // Leverage query generation method from portal - const query = this.searchSettingsService.querySettings(groupId, term); - let payload = { track_total_hits: true, sort: sort, from: pageSettings ? pageSettings.pageIndex * pageSettings.pageSize : 0, - size: pageSettings ? pageSettings.pageSize : 10, + size: pageSettings ? pageSettings.pageSize : 10 }; - if (term?.length) payload = Object.assign(payload, { query: query }); + if (term?.length) payload = Object.assign(payload, { query: finalQuery }); // TODO: Map response return this.http diff --git a/src/app/portal/components/results/active-filters/active-filters.component.ts b/src/app/portal/components/results/active-filters/active-filters.component.ts index 3ca1f0252..638fdbad8 100644 --- a/src/app/portal/components/results/active-filters/active-filters.component.ts +++ b/src/app/portal/components/results/active-filters/active-filters.component.ts @@ -348,10 +348,14 @@ export class ActiveFiltersComponent // Field of science if (val.category === 'field' && source.field?.fields) { const result = source.field.fields.buckets.find( - (key) => - parseInt(key.fieldId.buckets[0].key, 10) === - parseInt(val.value, 10) + (key) => { + const res = key.fieldId.buckets.find(item => { + return parseInt(item.key, 10) === parseInt(val.value, 10) + }); + return res; + } ); + const foundIndex = this.activeFilters.findIndex( (x) => x.value === val.value ); diff --git a/src/app/portal/components/results/funding-call-results/funding-call-results.component.ts b/src/app/portal/components/results/funding-call-results/funding-call-results.component.ts index 0643285a3..a2e003a49 100644 --- a/src/app/portal/components/results/funding-call-results/funding-call-results.component.ts +++ b/src/app/portal/components/results/funding-call-results/funding-call-results.component.ts @@ -162,13 +162,13 @@ export class FundingCallResultsComponent }, callOpen: { label: call.openDate.getFullYear() - ? this.highlightPipe.transform(call.openDateString === '01.01.1900' ? '-' : call.openDateString, this.input) + ? call.openDateString === '01.01.1900' ? '-' : call.openDateString : '-', }, callDue: { label: call.dueDate.getFullYear() !== 2100 - ? this.highlightPipe.transform(call.dueDateString, this.input) + ? call.dueDateString : $localize`:@@continuous:Jatkuva`, }, })); diff --git a/src/app/portal/components/results/publications/publications.component.ts b/src/app/portal/components/results/publications/publications.component.ts index 627700178..f11528660 100644 --- a/src/app/portal/components/results/publications/publications.component.ts +++ b/src/app/portal/components/results/publications/publications.component.ts @@ -173,10 +173,7 @@ export class PublicationsComponent implements OnInit, OnDestroy, AfterViewInit { template: channelColumnArray[index], }, year: { - label: this.highlightPipe.transform( - publication.publicationYear, - this.input - ), + label: publication.publicationYear, }, })); diff --git a/src/app/portal/components/single/single-funding/single-funding.component.html b/src/app/portal/components/single/single-funding/single-funding.component.html index 247babfaa..cff153718 100644 --- a/src/app/portal/components/single/single-funding/single-funding.component.html +++ b/src/app/portal/components/single/single-funding/single-funding.component.html @@ -427,7 +427,8 @@

diff --git a/src/app/portal/components/single/single-publication/single-publication.component.ts b/src/app/portal/components/single/single-publication/single-publication.component.ts index 0b2636f03..2ad6aed77 100644 --- a/src/app/portal/components/single/single-publication/single-publication.component.ts +++ b/src/app/portal/components/single/single-publication/single-publication.component.ts @@ -31,51 +31,51 @@ import { Search } from 'src/app/portal/models/search.model'; import { MatSnackBar } from '@angular/material/snack-bar'; import MetaTags from 'src/assets/static-data/meta-tags.json'; import { AppSettingsService } from '@shared/services/app-settings.service'; -import { ModelUtilsService } from '@shared/services/model-util.service'; -import { CleanCitationPipe } from '../../../pipes/clean-citation'; -import { MatProgressSpinner } from '@angular/material/progress-spinner'; -import { CdkCopyToClipboard } from '@angular/cdk/clipboard'; -import { DialogComponent } from '../../../../shared/components/dialog/dialog.component'; -import { ShareComponent } from '../share/share.component'; -import { SecondaryButtonComponent } from '../../../../shared/components/buttons/secondary-button/secondary-button.component'; -import { RelatedLinksComponent } from '../related-links/related-links.component'; -import { MatCard, MatCardTitle } from '@angular/material/card'; -import { PublicationLinksComponent } from './publication-links/publication-links.component'; -import { OrcidComponent } from '../../../../shared/components/orcid/orcid.component'; -import { FontAwesomeModule } from '@fortawesome/angular-fontawesome'; -import { TooltipModule } from 'ngx-bootstrap/tooltip'; -import { BreadcrumbComponent } from '../../breadcrumb/breadcrumb.component'; +import { ModelUtilsService } from '@shared/services/model-util.service'; +import { CleanCitationPipe } from '../../../pipes/clean-citation'; +import { MatProgressSpinner } from '@angular/material/progress-spinner'; +import { CdkCopyToClipboard } from '@angular/cdk/clipboard'; +import { DialogComponent } from '../../../../shared/components/dialog/dialog.component'; +import { ShareComponent } from '../share/share.component'; +import { SecondaryButtonComponent } from '../../../../shared/components/buttons/secondary-button/secondary-button.component'; +import { RelatedLinksComponent } from '../related-links/related-links.component'; +import { MatCard, MatCardTitle } from '@angular/material/card'; +import { PublicationLinksComponent } from './publication-links/publication-links.component'; +import { OrcidComponent } from '../../../../shared/components/orcid/orcid.component'; +import { FontAwesomeModule } from '@fortawesome/angular-fontawesome'; +import { TooltipModule } from 'ngx-bootstrap/tooltip'; +import { BreadcrumbComponent } from '../../breadcrumb/breadcrumb.component'; import { SearchBarComponent } from '../../search-bar/search-bar.component'; -@Component({ - selector: 'app-single-publication', - templateUrl: './single-publication.component.html', - styleUrls: ['./single-publication.component.scss'], - standalone: true, - imports: [ - SearchBarComponent, - NgIf, - RouterLink, - BreadcrumbComponent, - NgFor, - TooltipModule, - FontAwesomeModule, - NgClass, - OrcidComponent, - NgSwitch, - NgSwitchCase, - NgSwitchDefault, - PublicationLinksComponent, - MatCard, - MatCardTitle, - RelatedLinksComponent, - SecondaryButtonComponent, - ShareComponent, - DialogComponent, - CdkCopyToClipboard, - MatProgressSpinner, - CleanCitationPipe, - ], +@Component({ + selector: 'app-single-publication', + templateUrl: './single-publication.component.html', + styleUrls: ['./single-publication.component.scss'], + standalone: true, + imports: [ + SearchBarComponent, + NgIf, + RouterLink, + BreadcrumbComponent, + NgFor, + TooltipModule, + FontAwesomeModule, + NgClass, + OrcidComponent, + NgSwitch, + NgSwitchCase, + NgSwitchDefault, + PublicationLinksComponent, + MatCard, + MatCardTitle, + RelatedLinksComponent, + SecondaryButtonComponent, + ShareComponent, + DialogComponent, + CdkCopyToClipboard, + MatProgressSpinner, + CleanCitationPipe, + ], }) export class SinglePublicationComponent implements OnInit, AfterViewInit, OnDestroy @@ -631,12 +631,17 @@ export class SinglePublicationComponent } filterData() { - // Helper function to check if the field exists and has data + // Helper function to check if the field exists and has valid data const checkEmpty = (item: { field: string }) => { return UtilityService.stringHasContent( this.responseData.publications[0][item.field] ); }; + // Helper function to check if the field has some data + const checkIfValueExists = (item: { field: string }) => { + return this.responseData.publications[0][item.field]?.length > 0; + }; + // Filter all the fields to only include properties with defined data this.infoFields = this.infoFields.filter((item) => checkEmpty(item)); // this.authorFields = this.authorFields.filter(item => checkEmpty(item)); @@ -644,7 +649,9 @@ export class SinglePublicationComponent checkEmpty(item) ); this.typeFields = this.typeFields.filter((item) => checkEmpty(item)); - this.mediumFields = this.mediumFields.filter((item) => checkEmpty(item)); + + // Jufo code 0 must be shown, so exception added. Check empty function cannot be used, since in interprets 0 as empty. + this.mediumFields = this.mediumFields.filter((item) => item.field !== 'jufoClassCode' ? checkEmpty(item) : checkIfValueExists(item)); this.linksFields = this.linksFields.filter((item) => checkEmpty(item)); this.otherFields = this.otherFields.filter((item) => checkEmpty(item)); this.open_accessFields = this.open_accessFields.filter((item) => diff --git a/src/app/portal/models/funding/funder.model.ts b/src/app/portal/models/funding/funder.model.ts index dfc858418..737b896ef 100644 --- a/src/app/portal/models/funding/funder.model.ts +++ b/src/app/portal/models/funding/funder.model.ts @@ -13,9 +13,11 @@ export class Funder { constructor( public name: string, public nameUnd: string, + public funderOrganizationId: string, public typeOfFundingId: string, public typeOfFundingName: string, public callProgrammeName: string, + public callProgrammeId: string, public callProgrammeNameUnd: string, public callProgrammeHomePage: string, public frameworkProgramme: string, @@ -44,12 +46,14 @@ export class FunderAdapter implements Adapter { return new Funder( this.utils.checkTranslation('funderName', item), - item.funderNameUnd, + item.funderNameUnd, + item.funderOrganizationId, item.typeOfFundingId, this.utils.checkTranslation('typeOfFundingName', item)?.trim().length > 0 ? this.utils.checkTranslation('typeOfFundingName', item) : item.typeOfFundingId, this.utils.checkTranslation('callProgrammeName', item), + item.callProgrammeId, item.callProgrammeNameUnd, item.callProgrammeHomePage, this.utils.checkTranslation( diff --git a/src/app/portal/pipes/highlight.pipe.ts b/src/app/portal/pipes/highlight.pipe.ts index b2ee1e4a8..1730ea8b3 100644 --- a/src/app/portal/pipes/highlight.pipe.ts +++ b/src/app/portal/pipes/highlight.pipe.ts @@ -9,8 +9,8 @@ import { Pipe, PipeTransform } from '@angular/core'; import { DomSanitizer } from '@angular/platform-browser'; @Pipe({ - name: 'highlight', - standalone: true, + name: 'highlight', + standalone: true, }) export class HighlightSearchPipe implements PipeTransform { @@ -26,7 +26,7 @@ export class HighlightSearchPipe implements PipeTransform { const LETTER_EXPRESSION = /^\p{L}$/u; const isLetter = (character) => { - return character && (LETTER_EXPRESSION.test(character) || !isNaN(character) || character === '.' || character === ','); + return character && (LETTER_EXPRESSION.test(character) || !isNaN(character)); }; // Replace coded umlauts @@ -79,7 +79,7 @@ export class HighlightSearchPipe implements PipeTransform { return processedHighlight; } } else { - return processedHighlight + stillToProcessInput; + return processedHighlight + stillToProcessInput; } } diff --git a/src/app/portal/services/filters/filter-method.service.ts b/src/app/portal/services/filters/filter-method.service.ts index 614951880..f4c3a43cb 100644 --- a/src/app/portal/services/filters/filter-method.service.ts +++ b/src/app/portal/services/filters/filter-method.service.ts @@ -19,26 +19,32 @@ export class FilterMethodService { // Map minor fields of science to arrays by major separateMinor(source) { let mapped: any; + const subFieldsMerged = []; this.combined = []; - // Map fields by field & nested id + + // Merge nested subfields of science if (source && source.length > 0) { - mapped = source.map((majorField) => ({ - key: majorField.fieldId.buckets[0]?.key.toString() || -1, - label: majorField.key, - // Invalid response if key is 0 - id: majorField.fieldId.buckets[0]?.key || -1, - doc_count: majorField.fieldId.buckets[0]?.key - // Separate logic for publications and datasets - ? majorField.filtered.filterCount.doc_count - : -1, - })); + source.forEach(item => { + item.fieldId.buckets.forEach(fieldId => { + subFieldsMerged.push({ + key: fieldId?.key.toString() || -1, + label: item.key, + // Invalid response if key is 0 + id: fieldId?.key || -1, + doc_count: fieldId?.key + // Separate logic for publications and datasets + ? fieldId?.doc_count + : -1, + }); + }); + }); } // Loop through major fields & push all instances as separate arrays this.staticDataService.majorFieldsOfScience.forEach((item) => { - let i = item.id; - if (mapped) { + const i = item.id; + if (subFieldsMerged) { this.combined.push( - mapped + subFieldsMerged .filter((obj) => { return obj.id.toString().charAt(0).includes(String(i)) }) diff --git a/src/app/portal/services/settings.service.ts b/src/app/portal/services/settings.service.ts index b543527a2..3f50df1dd 100644 --- a/src/app/portal/services/settings.service.ts +++ b/src/app/portal/services/settings.service.ts @@ -55,6 +55,7 @@ export class SettingsService { ? this.staticDataService.relatedFields(index) : this.staticDataService.queryFields(index); } + // Set analyzer & type onlyDigits = /^\d+$/.test(term); hasDigits = /\d/.test(term); @@ -75,8 +76,8 @@ export class SettingsService { must: [ { term: { - _index: index, - }, + _index: index + } }, { bool: { @@ -89,8 +90,8 @@ export class SettingsService { fields: targetFields.length > 0 ? targetFields : '', operator: 'AND', lenient: 'true', - max_expansions: 1024, - }, + max_expansions: 1024 + } }, { multi_match: { @@ -98,8 +99,8 @@ export class SettingsService { type: 'cross_fields', fields: targetFields.length > 0 ? targetFields : '', operator: 'AND', - lenient: 'true', - }, + lenient: 'true' + } }, // index === 'publication' was moved below the declaration // index === 'person' was moved below the declaration @@ -109,6 +110,8 @@ export class SettingsService { ...(index === 'dataset' ? [{ bool: { should: this.generateNested('dataset', term) } }] : []), + + // News content field has umlauts converted to coded characters, query needs to be made with both coded and decoded umlauts ...(index === 'news' ? [ @@ -122,8 +125,8 @@ export class SettingsService { fields: targetFields.length > 0 ? targetFields : '', operator: 'AND', lenient: 'true', - max_expansions: 1024, - }, + max_expansions: 1024 + } }, { multi_match: { @@ -133,16 +136,33 @@ export class SettingsService { type: 'cross_fields', fields: targetFields.length > 0 ? targetFields : '', operator: 'AND', - lenient: 'true', - }, - }, + lenient: 'true' + } + } ] - : []), - ], - }, + : []) + ] + } }, - ], - }, + ...(index === 'dataset' + ? [{ + bool: { + should: [ + { + term: { + isLatestVersion: 1 + } + } + ] + } + }] + : [{ + bool: { + should: [] + } + }]) + ] + } }; if (index === 'publication') { @@ -151,14 +171,14 @@ export class SettingsService { publicationNameFuzzy: 0.4, authorsTextSplitted: 1.25, authorsTextSplittedFuzzy: 0.4, - author: 0.4, + author: 0.4 }; const matchPublicationName = { match: { publicationName: { query: term, - boost: boosts.publicationName, + boost: boosts.publicationName } } }; @@ -169,7 +189,7 @@ export class SettingsService { query: term, fuzziness: 2, - boost: boosts.publicationNameFuzzy, + boost: boosts.publicationNameFuzzy } } }; @@ -179,7 +199,7 @@ export class SettingsService { authorsTextSplitted: { query: term, operator: 'and', - boost: boosts.authorsTextSplitted, + boost: boosts.authorsTextSplitted } } }; @@ -190,7 +210,7 @@ export class SettingsService { query: term, operator: 'and', fuzziness: 2, - boost: boosts.authorsTextSplittedFuzzy, + boost: boosts.authorsTextSplittedFuzzy } } }; @@ -204,16 +224,16 @@ export class SettingsService { const matchKeywords = { match: { - "keywords.keyword": { - query: term, + 'keywords.keyword': { + query: term } } - } + }; const matchJournalName = { match_phrase_prefix: { journalName: { - query: term, + query: term } } }; @@ -221,7 +241,7 @@ export class SettingsService { const matchJufo = { match: { jufoCode: { - query: term, + query: term } } }; @@ -233,7 +253,7 @@ export class SettingsService { boost: 2 } } - } + }; const matchISSN = { match_phrase: { @@ -242,7 +262,7 @@ export class SettingsService { boost: 2 } } - } + }; const matchParentPublicationName = { match_phrase: { @@ -251,10 +271,10 @@ export class SettingsService { boost: 2 } } - } + }; // New match statements - if (this.target === "name") { + if (this.target === 'name') { res.bool.must[1].bool.should = [ matchAuthorsTextSplitted, // matchAuthorsTextSplittedFuzzy, @@ -287,7 +307,7 @@ export class SettingsService { } } ]; - } else if (this.target === "keywords") { + } else if (this.target === 'keywords') { (res as any).bool.must[1].bool.should = [ { match: { @@ -339,7 +359,7 @@ export class SettingsService { 'activity.affiliations.positionNameFi', 'activity.affiliations.positionNameSv', - 'activity.affiliations.positionNameEn', + 'activity.affiliations.positionNameEn' ], lenient: 'true' } @@ -373,10 +393,10 @@ export class SettingsService { type: 'cross_fields', fields: targetFields.length > 0 ? targetFields : '', operator: 'AND', - lenient: 'true', - }, - }, - }, + lenient: 'true' + } + } + } }); let res; @@ -389,7 +409,7 @@ export class SettingsService { res = [ query('organizationConsortium'), query('fundingGroupPerson'), - query('keywords'), + query('keywords') ]; break; } @@ -410,41 +430,41 @@ export class SettingsService { bool: { must: [ { term: { _index: 'publication' } }, - this.querySettings('publication', term), - ], - }, + this.querySettings('publication', term) + ] + } }, { bool: { must: [ { term: { _index: 'funding' } }, - this.querySettings('funding', term), - ], - }, + this.querySettings('funding', term) + ] + } }, { bool: { must: [ { term: { _index: 'dataset' } }, - this.querySettings('dataset', term), - ], - }, + this.querySettings('dataset', term) + ] + } }, { bool: { must: [ { term: { _index: 'infrastructure' } }, - this.querySettings('infrastructure', term), - ], - }, + this.querySettings('infrastructure', term) + ] + } }, { bool: { must: [ { term: { _index: 'funding-call' } }, - this.querySettings('funding-call', term), - ], - }, + this.querySettings('funding-call', term) + ] + } }, { bool: { @@ -467,26 +487,26 @@ export class SettingsService { analyzer: 'standard', fields: ['firstName', 'lastName'], operator: 'and', - prefix_length: 1, - }, - }, - ], - }, - }, - ], - }, + prefix_length: 1 + } + } + ] + } + } + ] + } }, { bool: { must: [ { term: { _index: 'organization' } }, - this.querySettings('organization', term), - ], - }, - }, + this.querySettings('organization', term) + ] + } + } ], - boost: 1, - }, + boost: 1 + } }, aggs: { _index: { @@ -494,44 +514,44 @@ export class SettingsService { filters: { person: { match: { - _index: 'person', - }, + _index: 'person' + } }, publication: { match: { - _index: 'publication', - }, + _index: 'publication' + } }, funding: { match: { - _index: 'funding', - }, + _index: 'funding' + } }, dataset: { bool: { must: [ { match: { - _index: 'dataset', - }, + _index: 'dataset' + } }, { term: { - isLatestVersion: 1, - }, - }, - ], - }, + isLatestVersion: 1 + } + } + ] + } }, infrastructure: { match: { - _index: 'infrastructure', - }, + _index: 'infrastructure' + } }, organization: { match: { - _index: 'organization', - }, + _index: 'organization' + } }, fundingCalls: { match: { @@ -548,13 +568,13 @@ export class SettingsService { aggs: { index_results: { top_hits: { - size: 3, - }, - }, - }, - }, + size: 3 + } + } + } + } }, - ...(term ? this.completionsSettings(term) : []), + ...(term ? this.completionsSettings(term) : []) }; return res; } @@ -570,11 +590,11 @@ export class SettingsService { size: 5, skip_duplicates: true, fuzzy: { - fuzziness: 0, - }, - }, - }, - }, + fuzziness: 0 + } + } + } + } }; return res; } diff --git a/src/app/shared/components/table/table.component.scss b/src/app/shared/components/table/table.component.scss index cd11db819..6822f8e6c 100644 --- a/src/app/shared/components/table/table.component.scss +++ b/src/app/shared/components/table/table.component.scss @@ -84,6 +84,7 @@ app-table-cell { mat-cell:first-of-type, mat-footer-cell:first-of-type { padding-left: $row-x-padding !important; + text-overflow: clip; } mat-header-cell:last-of-type, diff --git a/src/i18n/messages.en.xlf b/src/i18n/messages.en.xlf index 8168d6e03..1745c9bcb 100644 --- a/src/i18n/messages.en.xlf +++ b/src/i18n/messages.en.xlf @@ -4220,7 +4220,7 @@ Puiteohjelma - Ramprogram + Framework programme Mitä hanketietoja palvelu sisältää? diff --git a/src/i18n/messages.sv.xlf b/src/i18n/messages.sv.xlf index 1a1531c0e..5e108b31f 100644 --- a/src/i18n/messages.sv.xlf +++ b/src/i18n/messages.sv.xlf @@ -4223,7 +4223,7 @@ Puiteohjelma - Framework programme + Ramprogram Mitä hanketietoja palvelu sisältää?