diff --git a/apps/datahub/src/styles.css b/apps/datahub/src/styles.css
index f290ad9d70..31624948a3 100644
--- a/apps/datahub/src/styles.css
+++ b/apps/datahub/src/styles.css
@@ -7,6 +7,9 @@ body {
margin: 0;
}
+.container-xs {
+ max-width: calc(100% - 170px);
+}
.container-sm {
max-width: 640px;
}
diff --git a/apps/search/src/assets/i18n/en.json b/apps/search/src/assets/i18n/en.json
index ddc1c8a84a..93e3da443c 100644
--- a/apps/search/src/assets/i18n/en.json
+++ b/apps/search/src/assets/i18n/en.json
@@ -25,4 +25,4 @@
"search.field.any.placeholder": "Search datasets, services and maps ...",
"search.field.sortBy": "Sort by",
"search.loading": "Loading ..."
-}
+}
\ No newline at end of file
diff --git a/apps/webcomponents/src/assets/i18n/en.json b/apps/webcomponents/src/assets/i18n/en.json
index ddc1c8a84a..4375eb8625 100644
--- a/apps/webcomponents/src/assets/i18n/en.json
+++ b/apps/webcomponents/src/assets/i18n/en.json
@@ -22,6 +22,7 @@
"results.sortBy.dateStamp": "Most recent",
"results.sortBy.popularity": "Popularity",
"results.sortBy.relevancy": "Relevancy",
+ "results.sortBy.qualityScore": "Quality score",
"search.field.any.placeholder": "Search datasets, services and maps ...",
"search.field.sortBy": "Sort by",
"search.loading": "Loading ..."
diff --git a/conf/default.toml b/conf/default.toml
index 44903cbe7f..21f6f46036 100644
--- a/conf/default.toml
+++ b/conf/default.toml
@@ -59,6 +59,9 @@ background_color = "#fdfbff"
# title_font = "'My Custom Title Font', fallback-font-title"
# fonts_stylesheet_url = "https://fonts.googleapis.com/css2?family=Open+Sans:wght@400;700&family=Permanent+Marker&display=swap"
+# This optional parameter allow you to change the default class on progress bar, by default is font-bold
+# progress_bar_text_class = 'font-normal'
+
### SEARCH SETTINGS
# This section contains settings used for fine-tuning the search experience
@@ -95,6 +98,34 @@ background_color = "#fdfbff"
# Search presets will be advertised to the user along the main search field.
+
+### METADATA QUALITY SETTINGS
+
+# This section contains settings used for fine-tuning the metadata quality experience
+[metadata-quality]
+# By default the widget is not activated to enable it, just add this parameter.
+# enabled = true
+# If u want to use metadata quality widget this configuration is required
+
+# if you add an indexed field to calculate the qualityScore, the datahub search allow you to sort on this field with this parameter
+# sortable = true
+
+# by default the wudget appear in 2 location in search list and in detail page
+# display_widget_in_detail = false // allow you to hide the widget in detail
+# display_widget_in_search = false // allow you to hide the widget in search list
+# If you want see the widget in the two location, don't fill theses configurations
+
+# By default the window popup all fields to view if they are filled or not but you can hide some
+# display_title = false
+# display_description = false
+# display_topic = false
+# display_keywords = false
+# display_legal_constraints = false
+# display_contact = false
+# display_update_frequency = false
+# display_organisation = false
+# If you want see all fields, don't fill theses configurations
+
### MAP SETTINGS
# The map section allows to customize how maps are configured.
diff --git a/libs/feature/record/src/lib/record-metadata/record-metadata.component.html b/libs/feature/record/src/lib/record-metadata/record-metadata.component.html
index 11d5a65327..eb5d208453 100644
--- a/libs/feature/record/src/lib/record-metadata/record-metadata.component.html
+++ b/libs/feature/record/src/lib/record-metadata/record-metadata.component.html
@@ -26,6 +26,7 @@
+
= {
id: (output, source) => ({
@@ -42,7 +44,8 @@ export class ElasticsearchFieldMapper {
uuid: (output, source) => {
const uuid = selectField(source, 'uuid')
const metadataUrl = this.metadataUrlService.getUrl(uuid)
- return { ...output, uuid, metadataUrl }
+ const qualityScore = this.calculateQualityScore(source)
+ return { ...output, uuid, metadataUrl, qualityScore }
},
resourceTitleObject: (output, source) => ({
...output,
@@ -71,6 +74,12 @@ export class ElasticsearchFieldMapper {
)
),
}),
+ cl_topic: (output, source) => ({
+ ...output,
+ topic: getAsArray(
+ selectField(source, 'cl_topic')
+ ).map((cl_topic) => selectTranslatedValue(cl_topic)),
+ }),
cl_status: (output, source) => ({
...output,
updateStatus: selectTranslatedValue(
@@ -130,12 +139,21 @@ export class ElasticsearchFieldMapper {
}),
MD_ConstraintsUseLimitationObject: (output, source) =>
this.constraintField('MD_ConstraintsUseLimitationObject', output, source),
- MD_LegalConstraintsUseLimitationObject: (output, source) =>
- this.constraintField(
- 'MD_LegalConstraintsUseLimitationObject',
- output,
- source
- ),
+ MD_LegalConstraintsUseLimitationObject: (output, source) => {
+ const legalConstraints = getAsArray(
+ selectField(source, 'MD_LegalConstraintsUseLimitationObject')
+ ).map((MD_LegalConstraintsUseLimitationObject) => selectTranslatedValue(MD_LegalConstraintsUseLimitationObject));
+ let prevConstraints = output.constraints || [];
+ const constraints = {
+ ...prevConstraints,
+ ...legalConstraints
+ };
+ return {
+ ...output,
+ legalConstraints,
+ constraints
+ }
+ },
MD_LegalConstraintsOtherConstraintsObject: (output, source) =>
this.constraintField(
'MD_LegalConstraintsOtherConstraintsObject',
@@ -171,16 +189,78 @@ export class ElasticsearchFieldMapper {
}),
}
- private genericField = (output) => output
+ private calculateQualityScore = (source) => {
+ const qualityScore: number = selectField(source, 'qualityScore');
+ if (qualityScore != null) {
+ return qualityScore;
+ }
+ const metadataQualityConfig: MetadataQualityConfig = getMetadataQualityConfig();
+ let total = 0;
+ let success = 0;
+ const check = (name: string) => {
+ const display = metadataQualityConfig[`DISPLAY_${name}`] !== false;
+ if (display) total++;
+ return display;
+ }
+ if (check('TITLE')) {
+ if (selectField(source, 'resourceTitleObject')) {
+ success++;
+ }
+ }
+ if (check('DESCRIPTION')) {
+ if (selectFallback(selectTranslatedField(source, 'resourceAbstractObject'), 'no title')) {
+ success++;
+ }
+ }
+ const contact = mapContact(
+ getFirstValue(selectField(source, 'contact')),
+ source
+ );
+ if (check('ORGANISATION')) {
+ if (contact?.organisation) {
+ success++;
+ }
+ }
+ if (check('CONTACT')) {
+ if (contact?.email) {
+ success++;
+ }
+ }
+ if (check('TOPIC')) {
+ if (selectField(source, 'cl_topic')?.length > 0) {
+ success++;
+ }
+ }
+ if (check('KEYWORDS')) {
+ if (selectField(source, 'tag')?.length > 0) {
+ success++;
+ }
+ }
+ if (check('UPDATE_FREQUENCY')) {
+ if (getFirstValue(selectField(source, 'cl_maintenanceAndUpdateFrequency'))) {
+ success++;
+ }
+ }
+ if (check("LEGAL_CONSTRAINTS")) {
+ if (selectField(source, 'MD_LegalConstraintsUseLimitationObject')?.length > 0) {
+ success++;
+ }
+ }
+ return Math.round(success * 100 / total);
+ }
- private constraintField = (fieldName: string, output, source) => ({
- ...output,
- constraints: [
- ...(output.constraints || []),
- ...selectField(source, fieldName).map(selectTranslatedValue),
- ],
- })
+ private genericField = (output) => output
+ private constraintField = (fieldName: string, output, source) => {
+ const constraints = Array.isArray(output.constraints) ? output.constraints : [];
+ const fieldValues = selectField(source, fieldName);
+ const translatedValues = fieldValues.map(selectTranslatedValue);
+ const updatedConstraints = [...constraints, ...translatedValues];
+ return {
+ ...output,
+ constraints: updatedConstraints,
+ };
+ }
getMappingFn(fieldName: string) {
return fieldName in this.fields ? this.fields[fieldName] : this.genericField
}
diff --git a/libs/feature/search/src/lib/utils/mapper/elasticsearch.mapper.spec.ts b/libs/feature/search/src/lib/utils/mapper/elasticsearch.mapper.spec.ts
index 1774166759..bba8d7cc93 100644
--- a/libs/feature/search/src/lib/utils/mapper/elasticsearch.mapper.spec.ts
+++ b/libs/feature/search/src/lib/utils/mapper/elasticsearch.mapper.spec.ts
@@ -20,23 +20,23 @@ class OrganisationsServiceMock {
of(
'contact' in source
? {
- ...record,
- contact: {
- name: 'Main Contact',
+ ...record,
+ contact: {
+ name: 'Main Contact',
+ email: 'q2suppor@ifremer.fr',
+ organisation: source.contact[0].organisation,
+ },
+ resourceContacts: [
+ {
+ name: 'Resource Contact 1',
email: 'q2suppor@ifremer.fr',
- organisation: source.contact[0].organisation,
},
- resourceContacts: [
- {
- name: 'Resource Contact 1',
- email: 'q2suppor@ifremer.fr',
- },
- {
- name: 'Resource Contact 2',
- email: 'q2suppor@ifremer.fr',
- },
- ],
- }
+ {
+ name: 'Resource Contact 2',
+ email: 'q2suppor@ifremer.fr',
+ },
+ ],
+ }
: record
)
)
@@ -78,8 +78,13 @@ describe('ElasticsearchMapper', () => {
abstract: 'The grid is based on proposal ',
id: '12456',
metadataUrl: 'url',
+<<<<<<< HEAD
thumbnailUrl:
'https://sdi.eea.europa.eu/public/catalogue-graphic-overview/20e9e1a1-83c1-4f13-89ef-c19767d6ee18f.png',
+=======
+ qualityScore: 25,
+ thumbnailUrl: 'data:image/png;base64,',
+>>>>>>> 00e9c4b0 (fix asked by camp2camp)
title: 'EEA reference grid for Germany (10km), May 2013',
uuid: '20e9e1a1-83c1-4f13-89ef-c19767d6ee18f',
catalogUuid: '6731be1e-6533-44e0-9b8a-580b45e36e80',
@@ -101,6 +106,7 @@ describe('ElasticsearchMapper', () => {
hasDownloads: false,
hasMaps: false,
links: [],
+ qualityScore: 25
},
])
})
@@ -432,9 +438,13 @@ describe('ElasticsearchMapper', () => {
],
metadataUrl: 'url',
ownerInfo: 'testadmin|ADMIN|Test|Administrator',
+ qualityScore: 100,
thumbnailUrl:
'https://sextant.ifremer.fr/geonetwork/srv/api/records/cf5048f6-5bbf-4e44-ba74-e6f429af51ea/attachments/parametres.gif',
title: 'Surval - Données par paramètre',
+ topic: [
+ 'Océans',
+ ],
uuid: 'cf5048f6-5bbf-4e44-ba74-e6f429af51ea',
contact: {
name: 'Main Contact',
@@ -516,11 +526,12 @@ describe('ElasticsearchMapper', () => {
'Sète',
'La Rochelle',
],
+ legalConstraints: [
+ "Restriction légale d'utilisation à préciser",
+ ],
lineage:
'Les données sont bancarisées dans la base de données Quadrige.',
constraints: [
- 'Restriction lié à l’exercice du droit moral',
- "Restriction légale d'utilisation à préciser",
'Pas de restriction d’accès public',
'Licence Ouverte version 2.0 https://www.etalab.gouv.fr/wp-content/uploads/2017/04/ETALAB-Licence-Ouverte-v2.0.pdf',
],
diff --git a/libs/ui/elements/src/index.ts b/libs/ui/elements/src/index.ts
index 289d360ce2..91cd1d7172 100644
--- a/libs/ui/elements/src/index.ts
+++ b/libs/ui/elements/src/index.ts
@@ -2,6 +2,8 @@ export * from './lib/ui-elements.module'
export * from './lib/metadata-info/metadata-info.component'
export * from './lib/metadata-contact/metadata-contact.component'
export * from './lib/metadata-catalog/metadata-catalog.component'
+export * from './lib/metadata-quality/metadata-quality.component'
+export * from './lib/metadata-quality-info/metadata-quality-info.component'
export * from './lib/search-results-error/search-results-error.component'
export * from './lib/thumbnail/thumbnail.component'
export * from './lib/content-ghost/content-ghost.component'
diff --git a/libs/ui/elements/src/lib/metadata-quality-info/metadata-quality-info.component.html b/libs/ui/elements/src/lib/metadata-quality-info/metadata-quality-info.component.html
new file mode 100644
index 0000000000..43ffb3bef6
--- /dev/null
+++ b/libs/ui/elements/src/lib/metadata-quality-info/metadata-quality-info.component.html
@@ -0,0 +1,6 @@
+
\ No newline at end of file
diff --git a/libs/ui/elements/src/lib/metadata-quality-info/metadata-quality-info.component.spec.ts b/libs/ui/elements/src/lib/metadata-quality-info/metadata-quality-info.component.spec.ts
new file mode 100644
index 0000000000..c0f0db07fc
--- /dev/null
+++ b/libs/ui/elements/src/lib/metadata-quality-info/metadata-quality-info.component.spec.ts
@@ -0,0 +1,27 @@
+import { ComponentFixture, TestBed } from '@angular/core/testing'
+import { UtilSharedModule } from '@geonetwork-ui/util/shared'
+import { TranslateModule } from '@ngx-translate/core'
+import { ContentGhostComponent } from '../content-ghost/content-ghost.component'
+import { MetadataQualityInfoComponent } from './metadata-quality-info.component'
+
+describe('MetadataQualityInfoComponent', () => {
+ let component: MetadataQualityInfoComponent
+ let fixture: ComponentFixture
+
+ beforeEach(async () => {
+ await TestBed.configureTestingModule({
+ imports: [TranslateModule.forRoot(), UtilSharedModule],
+ declarations: [MetadataQualityInfoComponent, ContentGhostComponent],
+ }).compileComponents()
+ })
+
+ beforeEach(() => {
+ fixture = TestBed.createComponent(MetadataQualityInfoComponent)
+ component = fixture.componentInstance
+ fixture.detectChanges()
+ })
+
+ it('should create', () => {
+ expect(component).toBeTruthy()
+ })
+})
diff --git a/libs/ui/elements/src/lib/metadata-quality-info/metadata-quality-info.component.ts b/libs/ui/elements/src/lib/metadata-quality-info/metadata-quality-info.component.ts
new file mode 100644
index 0000000000..ce803a4443
--- /dev/null
+++ b/libs/ui/elements/src/lib/metadata-quality-info/metadata-quality-info.component.ts
@@ -0,0 +1,34 @@
+import {
+ ChangeDetectionStrategy,
+ Component,
+ Input
+} from '@angular/core'
+import { MetadataQualityConfig, getMetadataQualityConfig } from '@geonetwork-ui/util/app-config';
+
+@Component({
+ selector: 'gn-ui-metadata-quality-info',
+ templateUrl: './metadata-quality-info.component.html',
+ changeDetection: ChangeDetectionStrategy.OnPush,
+})
+export class MetadataQualityInfoComponent {
+
+ metadataQualityConfig: MetadataQualityConfig = getMetadataQualityConfig();
+ @Input() name: string
+ @Input() value: boolean
+
+ get display() {
+ if (this.name) {
+ const nameSnakeUpper = this.name.replace(/[A-Z]/g, (letter) => `_${letter.toLowerCase()}`).toUpperCase();
+ return this.metadataQualityConfig['DISPLAY_' + nameSnakeUpper] !== false;
+ }
+ return false;
+ }
+
+ get icon() {
+ return this.value ? 'check' : 'warning_amber'
+ }
+
+ get labelKey() {
+ return `record.metadata.quality.${this.name}.${(this.value ? 'success' : 'failed')}`
+ }
+}
diff --git a/libs/ui/elements/src/lib/metadata-quality/metadata-quality.component.css b/libs/ui/elements/src/lib/metadata-quality/metadata-quality.component.css
new file mode 100644
index 0000000000..a03aeeb609
--- /dev/null
+++ b/libs/ui/elements/src/lib/metadata-quality/metadata-quality.component.css
@@ -0,0 +1,26 @@
+:host-context(.picto) > .metadata-quality > .widget > p.text {
+ display: none;
+}
+
+:host-context(.picto) > .metadata-quality > .menu {
+ right: 0px;
+}
+:host-context(.picto)> .metadata-quality > .widget gn-ui-progress-bar {
+ line-height: 8px;
+}
+
+:host-context(.picto) ::ng-deep gn-ui-progress-bar .text-4 {
+ font-weight: normal;
+ font-size: 15px;
+}
+
+.menu {
+ position: absolute;
+ z-index: 1;
+ background-color: white;
+ border: 1px solid rgba(0, 0, 0, 0.35);
+ border-radius: 5px;
+ box-shadow: rgba(0, 0, 0, 0.35) 0px 5px 15px;
+ padding: 20px;
+ white-space: nowrap;
+}
\ No newline at end of file
diff --git a/libs/ui/elements/src/lib/metadata-quality/metadata-quality.component.html b/libs/ui/elements/src/lib/metadata-quality/metadata-quality.component.html
new file mode 100644
index 0000000000..6c85219693
--- /dev/null
+++ b/libs/ui/elements/src/lib/metadata-quality/metadata-quality.component.html
@@ -0,0 +1,21 @@
+
+
+
diff --git a/libs/ui/elements/src/lib/metadata-quality/metadata-quality.component.spec.ts b/libs/ui/elements/src/lib/metadata-quality/metadata-quality.component.spec.ts
new file mode 100644
index 0000000000..d3af3ac346
--- /dev/null
+++ b/libs/ui/elements/src/lib/metadata-quality/metadata-quality.component.spec.ts
@@ -0,0 +1,54 @@
+import { ComponentFixture, TestBed } from '@angular/core/testing'
+import { UtilSharedModule } from '@geonetwork-ui/util/shared'
+import { RECORDS_FULL_FIXTURE } from '@geonetwork-ui/util/shared/fixtures'
+import { TranslateModule } from '@ngx-translate/core'
+import { ContentGhostComponent } from '../content-ghost/content-ghost.component'
+import { MetadataQualityComponent } from './metadata-quality.component'
+import { By } from '@angular/platform-browser'
+
+describe('MetadataQualityComponent', () => {
+ let component: MetadataQualityComponent
+ let fixture: ComponentFixture
+
+ beforeEach(async () => {
+ await TestBed.configureTestingModule({
+ imports: [TranslateModule.forRoot(), UtilSharedModule],
+ declarations: [MetadataQualityComponent, ContentGhostComponent],
+ }).compileComponents()
+ })
+
+ beforeEach(() => {
+ fixture = TestBed.createComponent(MetadataQualityComponent)
+ component = fixture.componentInstance
+ component.metadata = RECORDS_FULL_FIXTURE[0]
+ fixture.detectChanges()
+ })
+
+ it('should create', () => {
+ expect(component).toBeTruthy()
+ })
+
+ it('focus should show menu / blur should hide', () => {
+ const progressBar = fixture.debugElement.query(By.css('gn-ui-progress-bar'))
+ progressBar.nativeElement.focus();
+ expect(component.isMenuShown).toBe(true)
+ progressBar.nativeElement.blur();
+ expect(component.isMenuShown).toBe(false)
+ })
+
+ it('mouseenter should show menu / mouseleave should hide', () => {
+ const metadataQuality = fixture.debugElement.query(By.css('.metadata-quality'))
+
+ const mouseEnterEvent = new Event('mouseenter');
+ metadataQuality.nativeElement.dispatchEvent(mouseEnterEvent);
+ expect(component.isMenuShown).toBe(true)
+
+ const mouseLeaveEvent = new Event('mouseleave');
+ metadataQuality.nativeElement.dispatchEvent(mouseLeaveEvent);
+ expect(component.isMenuShown).toBe(false)
+ })
+
+ it('content', () => {
+ expect(component.metadata?.contact?.organisation).toBe("Ifremer");
+ })
+})
diff --git a/libs/ui/elements/src/lib/metadata-quality/metadata-quality.component.stories.ts b/libs/ui/elements/src/lib/metadata-quality/metadata-quality.component.stories.ts
new file mode 100644
index 0000000000..c4caf98707
--- /dev/null
+++ b/libs/ui/elements/src/lib/metadata-quality/metadata-quality.component.stories.ts
@@ -0,0 +1,35 @@
+import {
+ TRANSLATE_DEFAULT_CONFIG,
+ UtilI18nModule,
+} from '@geonetwork-ui/util/i18n'
+import { TranslateModule } from '@ngx-translate/core'
+import { Meta, moduleMetadata, Story } from '@storybook/angular'
+import { MetadataQualityComponent } from './metadata-quality.component'
+import { UiElementsModule } from '../ui-elements.module'
+import { RECORDS_FULL_FIXTURE } from '@geonetwork-ui/util/shared/fixtures'
+
+export default {
+ title: 'Elements/MetadataQualityComponent',
+ component: MetadataQualityComponent,
+ decorators: [
+ moduleMetadata({
+ imports: [
+ UiElementsModule,
+ UtilI18nModule,
+ TranslateModule.forRoot(TRANSLATE_DEFAULT_CONFIG),
+ ],
+ }),
+ ],
+} as Meta
+
+const Template: Story = (
+ args: MetadataQualityComponent
+) => ({
+ component: MetadataQualityComponent,
+ props: args,
+})
+
+export const Primary = Template.bind({})
+Primary.args = {
+ metadata: RECORDS_FULL_FIXTURE[1],
+}
diff --git a/libs/ui/elements/src/lib/metadata-quality/metadata-quality.component.ts b/libs/ui/elements/src/lib/metadata-quality/metadata-quality.component.ts
new file mode 100644
index 0000000000..3f6feece60
--- /dev/null
+++ b/libs/ui/elements/src/lib/metadata-quality/metadata-quality.component.ts
@@ -0,0 +1,29 @@
+import {
+ ChangeDetectionStrategy,
+ Component,
+ EventEmitter,
+ Input,
+ Output,
+} from '@angular/core'
+import { MetadataLink, MetadataRecord } from '@geonetwork-ui/util/shared'
+
+@Component({
+ selector: 'gn-ui-metadata-quality',
+ templateUrl: './metadata-quality.component.html',
+ styleUrls: ['./metadata-quality.component.css'],
+ changeDetection: ChangeDetectionStrategy.OnPush,
+})
+export class MetadataQualityComponent {
+ @Input() metadata: MetadataRecord
+ @Input() landingPages: MetadataLink[]
+ @Output() keyword = new EventEmitter()
+ isMenuShown = false
+
+ showMenu() {
+ this.isMenuShown = true
+ }
+
+ hideMenu() {
+ this.isMenuShown = false
+ }
+}
diff --git a/libs/ui/elements/src/lib/ui-elements.module.ts b/libs/ui/elements/src/lib/ui-elements.module.ts
index d67290b3b3..44ed460169 100644
--- a/libs/ui/elements/src/lib/ui-elements.module.ts
+++ b/libs/ui/elements/src/lib/ui-elements.module.ts
@@ -16,6 +16,8 @@ import { LinkCardComponent } from './link-card/link-card.component'
import { RelatedRecordCardComponent } from './related-record-card/related-record-card.component'
import { MetadataContactComponent } from './metadata-contact/metadata-contact.component'
import { MetadataCatalogComponent } from './metadata-catalog/metadata-catalog.component'
+import { MetadataQualityComponent } from './metadata-quality/metadata-quality.component'
+import { MetadataQualityInfoComponent } from './metadata-quality-info/metadata-quality-info.component'
import { SearchResultsErrorComponent } from './search-results-error/search-results-error.component'
import { PaginationComponent } from './pagination/pagination.component'
import { ThumbnailComponent } from './thumbnail/thumbnail.component'
@@ -48,6 +50,8 @@ import { UserPreviewComponent } from './user-preview/user-preview.component'
RelatedRecordCardComponent,
MetadataContactComponent,
MetadataCatalogComponent,
+ MetadataQualityComponent,
+ MetadataQualityInfoComponent,
SearchResultsErrorComponent,
PaginationComponent,
ThumbnailComponent,
@@ -64,6 +68,8 @@ import { UserPreviewComponent } from './user-preview/user-preview.component'
RelatedRecordCardComponent,
MetadataContactComponent,
MetadataCatalogComponent,
+ MetadataQualityComponent,
+ MetadataQualityInfoComponent,
SearchResultsErrorComponent,
PaginationComponent,
ThumbnailComponent,
diff --git a/libs/ui/search/src/lib/record-preview-row/record-preview-row.component.html b/libs/ui/search/src/lib/record-preview-row/record-preview-row.component.html
index 3fdc6362fc..abbcbf7bd0 100644
--- a/libs/ui/search/src/lib/record-preview-row/record-preview-row.component.html
+++ b/libs/ui/search/src/lib/record-preview-row/record-preview-row.component.html
@@ -35,6 +35,7 @@
{{ contact?.organisation }}
@@ -52,10 +53,11 @@
>map
+
+
+
+ class="text-right col-start-3 row-start-4 sm:absolute sm:col-start-2 sm:row-start-1 sm:top-[-1.125em] sm:right-[0.4em]">
-
diff --git a/libs/ui/widgets/src/lib/progress-bar/progress-bar.component.spec.ts b/libs/ui/widgets/src/lib/progress-bar/progress-bar.component.spec.ts
index c54f3ae446..9b7b3e1ef0 100644
--- a/libs/ui/widgets/src/lib/progress-bar/progress-bar.component.spec.ts
+++ b/libs/ui/widgets/src/lib/progress-bar/progress-bar.component.spec.ts
@@ -2,6 +2,13 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'
import { ProgressBarComponent } from './progress-bar.component'
+jest.mock('@geonetwork-ui/util/app-config', () => ({
+ getThemeConfig: () => ({
+ PROGRESS_BAR_TEXT_CLASS: '',
+ }),
+ isConfigLoaded: jest.fn(() => true),
+}))
+
describe('ProgressBarComponent', () => {
let component: ProgressBarComponent
let fixture: ComponentFixture
diff --git a/libs/ui/widgets/src/lib/progress-bar/progress-bar.component.stories.ts b/libs/ui/widgets/src/lib/progress-bar/progress-bar.component.stories.ts
index 4a93e79ca0..a7d790c460 100644
--- a/libs/ui/widgets/src/lib/progress-bar/progress-bar.component.stories.ts
+++ b/libs/ui/widgets/src/lib/progress-bar/progress-bar.component.stories.ts
@@ -3,6 +3,7 @@ import { ProgressBarComponent } from './progress-bar.component'
import { importProvidersFrom } from '@angular/core'
import { BrowserAnimationsModule } from '@angular/platform-browser/animations'
+
export default {
title: 'Widgets/ProgressBarComponent',
component: ProgressBarComponent,
diff --git a/libs/ui/widgets/src/lib/progress-bar/progress-bar.component.ts b/libs/ui/widgets/src/lib/progress-bar/progress-bar.component.ts
index 167921bcc6..e1b5f1f94e 100644
--- a/libs/ui/widgets/src/lib/progress-bar/progress-bar.component.ts
+++ b/libs/ui/widgets/src/lib/progress-bar/progress-bar.component.ts
@@ -1,8 +1,10 @@
import { Component, Input } from '@angular/core'
+import { getThemeConfig, isConfigLoaded } from '@geonetwork-ui/util/app-config'
interface ColorScheme {
outerBar: string
innerBar: string
+ text: string
}
@Component({
@@ -13,6 +15,7 @@ interface ColorScheme {
export class ProgressBarComponent {
@Input() value = 0
@Input() type: 'primary' | 'secondary' | 'default' = 'default'
+ textClass = isConfigLoaded() ? getThemeConfig().PROGRESS_BAR_TEXT_CLASS : 'font-bold'
get progress() {
return this.value > 0 ? (this.value < 100 ? this.value : 100) : 0
@@ -24,16 +27,19 @@ export class ProgressBarComponent {
return {
outerBar: 'bg-gray-200',
innerBar: 'bg-gray-100',
+ text: 'text-gray-900',
}
case 'primary':
return {
outerBar: 'bg-primary',
innerBar: 'bg-primary-lighter',
+ text: 'text-white',
}
case 'secondary':
return {
outerBar: 'bg-secondary',
innerBar: 'bg-secondary-lighter',
+ text: 'text-white',
}
}
}
diff --git a/libs/util/app-config/src/lib/app-config.ts b/libs/util/app-config/src/lib/app-config.ts
index 68644108ed..0a0fc33cd2 100644
--- a/libs/util/app-config/src/lib/app-config.ts
+++ b/libs/util/app-config/src/lib/app-config.ts
@@ -12,6 +12,7 @@ import {
LayerConfig,
MapConfig,
SearchConfig,
+ MetadataQualityConfig,
ThemeConfig,
} from './model'
@@ -46,6 +47,13 @@ export function getOptionalSearchConfig(): SearchConfig | null {
return searchConfig
}
+let metadataQualityConfig: MetadataQualityConfig = null
+export function getMetadataQualityConfig(): MetadataQualityConfig | null {
+ return metadataQualityConfig || ({
+ ENABLED: false
+ } as MetadataQualityConfig);
+}
+
let customTranslations: CustomTranslationsAllLanguages = null
export function getCustomTranslations(langCode: string): CustomTranslations {
@@ -167,6 +175,7 @@ export function loadAppConfig() {
'fonts_stylesheet_url',
'thumbnail_placeholder',
'header_background',
+ 'progress_bar_text_class'
],
warnings,
errors
@@ -186,6 +195,7 @@ export function loadAppConfig() {
TITLE_FONT: parsedThemeSection.title_font,
MAIN_FONT: parsedThemeSection.main_font,
FONTS_STYLESHEET_URL: parsedThemeSection.fonts_stylesheet_url,
+ PROGRESS_BAR_TEXT_CLASS: parsedThemeSection.progress_bar_text_class
} as ThemeConfig)
const parsedSearchSection = parseConfigSection(
@@ -223,6 +233,53 @@ export function loadAppConfig() {
ADVANCED_FILTERS: parsedSearchSection.advanced_filters,
} as SearchConfig)
+ const parsedMetadataQualitySection = parseConfigSection(
+ parsed,
+ 'metadata-quality',
+ [],
+ [
+ 'enabled',
+ 'sortable',
+ 'display_widget_in_detail',
+ 'display_widget_in_search',
+ 'display_title',
+ 'display_description',
+ 'display_topic',
+ 'display_keywords',
+ 'display_legal_constraints',
+ 'display_contact',
+ 'display_update_frequency',
+ 'display_organisation'
+ ],
+ warnings,
+ errors
+ )
+ metadataQualityConfig =
+ parsedMetadataQualitySection === null
+ ? null
+ : ({
+ ENABLED: parsedMetadataQualitySection.enabled,
+ SORTABLE: parsedMetadataQualitySection.sortable,
+ DISPLAY_WIDGET_IN_RECORD_METADATA: parsedMetadataQualitySection.display_widget_in_detail,
+ DISPLAY_WIDGET_IN_PREVIEW_ROW: parsedMetadataQualitySection.display_widget_in_search,
+ DISPLAY_TITLE: parsedMetadataQualitySection.display_title,
+ DISPLAY_DESCRIPTION: parsedMetadataQualitySection.display_description,
+ DISPLAY_TOPIC: parsedMetadataQualitySection.display_topic,
+ DISPLAY_KEYWORDS: parsedMetadataQualitySection.display_keywords,
+ DISPLAY_LEGAL_CONSTRAINTS: parsedMetadataQualitySection.display_legal_constraints,
+ DISPLAY_CONTACT: parsedMetadataQualitySection.display_contact,
+ DISPLAY_UPDATE_FREQUENCY: parsedMetadataQualitySection.display_update_frequency,
+ DISPLAY_ORGANISATION: parsedMetadataQualitySection.display_organisation,
+ } as MetadataQualityConfig)
+
+ searchConfig =
+ parsedSearchSection === null
+ ? null
+ : ({
+ FILTER_GEOMETRY_DATA: parsedSearchSection.filter_geometry_data,
+ FILTER_GEOMETRY_URL: parsedSearchSection.filter_geometry_url,
+ } as SearchConfig)
+
customTranslations = parseTranslationsConfigSection(
parsed,
'translations'
diff --git a/libs/util/app-config/src/lib/model.ts b/libs/util/app-config/src/lib/model.ts
index a259d16b99..2f7b90a0e7 100644
--- a/libs/util/app-config/src/lib/model.ts
+++ b/libs/util/app-config/src/lib/model.ts
@@ -36,6 +36,7 @@ export interface ThemeConfig {
MAIN_FONT?: string
TITLE_FONT?: string
FONTS_STYLESHEET_URL?: string
+ PROGRESS_BAR_TEXT_CLASS?: string
}
export interface SearchPreset {
@@ -51,6 +52,21 @@ export interface SearchConfig {
ADVANCED_FILTERS?: []
}
+export interface MetadataQualityConfig {
+ ENABLED: boolean
+ SORTABLE: boolean
+ DISPLAY_WIDGET_IN_RECORD_METADATA: boolean
+ DISPLAY_WIDGET_IN_PREVIEW_ROW: boolean
+ DISPLAY_TITLE: boolean
+ DISPLAY_DESCRIPTION: boolean
+ DISPLAY_TOPIC: boolean
+ DISPLAY_KEYWORDS: boolean
+ DISPLAY_LEGAL_CONSTRAINTS: boolean
+ DISPLAY_CONTACT: boolean
+ DISPLAY_UPDATE_FREQUENCY: boolean
+ DISPLAY_ORGANISATION: boolean
+}
+
export type CustomTranslations = { [translationKey: string]: string }
export type CustomTranslationsAllLanguages = {
[lang: string]: CustomTranslations
diff --git a/libs/util/shared/src/lib/elasticsearch/constant.ts b/libs/util/shared/src/lib/elasticsearch/constant.ts
index 380530a1ff..88bcef31d4 100644
--- a/libs/util/shared/src/lib/elasticsearch/constant.ts
+++ b/libs/util/shared/src/lib/elasticsearch/constant.ts
@@ -17,7 +17,14 @@ export const ES_SOURCE_SUMMARY = [
'linkProtocol',
'contactForResource.organisation',
'contact.organisation',
+ 'contact.email',
'userSavedCount',
+ 'updateFrequency',
+ 'cl_topic',
+ 'cl_maintenanceAndUpdateFrequency',
+ 'tag',
+ 'MD_LegalConstraintsUseLimitationObject',
+ 'qualityScore'
]
export const ES_SOURCE_BRIEF = [
diff --git a/libs/util/shared/src/lib/elasticsearch/elasticsearch.service.spec.ts b/libs/util/shared/src/lib/elasticsearch/elasticsearch.service.spec.ts
index 52a3665544..90121b0de5 100644
--- a/libs/util/shared/src/lib/elasticsearch/elasticsearch.service.spec.ts
+++ b/libs/util/shared/src/lib/elasticsearch/elasticsearch.service.spec.ts
@@ -482,7 +482,14 @@ describe('ElasticsearchService', () => {
'linkProtocol',
'contactForResource.organisation',
'contact.organisation',
+ 'contact.email',
'userSavedCount',
+ "updateFrequency",
+ "cl_topic",
+ "cl_maintenanceAndUpdateFrequency",
+ "tag",
+ "MD_LegalConstraintsUseLimitationObject",
+ "qualityScore",
],
query: {
bool: {
diff --git a/libs/util/shared/src/lib/fixtures/records.ts b/libs/util/shared/src/lib/fixtures/records.ts
index 3b3ec174ad..319fca61de 100644
--- a/libs/util/shared/src/lib/fixtures/records.ts
+++ b/libs/util/shared/src/lib/fixtures/records.ts
@@ -159,4 +159,21 @@ export const RECORDS_FULL_FIXTURE: MetadataRecord[] = deepFreeze([
},
catalogUuid: '6731be1e-6533-44e0-9b8a-580b45e36e80',
},
+ {
+ id: '10421',
+ uuid: 'cf5048f6-5bbf-4e44-ba74-e6f429af51eb',
+ metadataUrl: 'url',
+ title: 'Test',
+ abstract: "La description du test",
+ updateFrequency: null,
+ keywords: [],
+ contact: {
+ name: 'Jean-Michel',
+ organisation: 'Ifremer',
+ email: 'q2suppor@ifremer.fr',
+ },
+ topic: [],
+ legalConstraints: [],
+ qualityScore: 50
+ }
])
diff --git a/libs/util/shared/src/lib/models/search.model.ts b/libs/util/shared/src/lib/models/search.model.ts
index a25d02130a..b9a56b9bd8 100644
--- a/libs/util/shared/src/lib/models/search.model.ts
+++ b/libs/util/shared/src/lib/models/search.model.ts
@@ -63,6 +63,9 @@ export interface MetadataRecord {
isOpenData?: boolean
ownerInfo?: string
isPublishedToAll?: boolean
+ topic?: string[],
+ legalConstraints?: string[]
+ qualityScore?: number
}
export enum MetadataLinkType {
diff --git a/libs/util/shared/src/lib/models/sort-by.model.ts b/libs/util/shared/src/lib/models/sort-by.model.ts
index 08ec0b7ce1..2a6716c4dd 100644
--- a/libs/util/shared/src/lib/models/sort-by.model.ts
+++ b/libs/util/shared/src/lib/models/sort-by.model.ts
@@ -2,4 +2,5 @@ export enum SortByEnum {
CREATE_DATE = '-createDate',
POPULARITY = '-userSavedCount',
RELEVANCY = '_score',
+ QUALITY_SCORE = '-qualityScore'
}
diff --git a/translations/en.json b/translations/en.json
index 5d87b7d235..1b034d9e6b 100644
--- a/translations/en.json
+++ b/translations/en.json
@@ -186,12 +186,30 @@
"record.metadata.related": "Related records",
"record.metadata.sheet": "Original metadata sheet",
"record.metadata.status": "status",
- "record.metadata.title": "title",
+ "record.metadata.title": "Title",
"record.metadata.updateFrequency": "Update Frequency",
"record.metadata.updateStatus": "Update Status",
"record.metadata.updatedOn": "Updated On",
"record.metadata.usage": "Usage & constraints",
"record.metadata.noUsage": "No usage conditions specified for this record.",
+ "record.metadata.quality": "Metadata Quality",
+ "record.metadata.quality.details": "Details",
+ "record.metadata.quality.title.success": "Title is completed",
+ "record.metadata.quality.title.failed": "Title is not completed",
+ "record.metadata.quality.description.success": "Description is completed",
+ "record.metadata.quality.description.failed": "Description is not completed",
+ "record.metadata.quality.topic.success": "Topic is completed",
+ "record.metadata.quality.topic.failed": "Topic is not completed",
+ "record.metadata.quality.keywords.success": "Keywords are completed",
+ "record.metadata.quality.keywords.failed": "Keywords are not completed",
+ "record.metadata.quality.legalConstraints.success": "Legal constraints are completed",
+ "record.metadata.quality.legalConstraints.failed": "Legal constraints are not completed",
+ "record.metadata.quality.contact.success": "Contact is completed",
+ "record.metadata.quality.contact.failed": "Contact is not completed",
+ "record.metadata.quality.updateFrequency.success": "Update frequency is completed",
+ "record.metadata.quality.updateFrequency.failed": "Update frequency is not completed",
+ "record.metadata.quality.organisation.success": "Organisation is completed",
+ "record.metadata.quality.organisation.failed": "Organisation is not completed",
"record.more.details": "Read more",
"record.tab.chart": "Chart",
"record.tab.data": "Table",
@@ -206,6 +224,7 @@
"results.sortBy.dateStamp": "Most recent",
"results.sortBy.popularity": "Popularity",
"results.sortBy.relevancy": "Relevancy",
+ "results.sortBy.qualityScore": "",
"search.autocomplete.error": "Suggestions could not be fetched:",
"search.error.couldNotReachApi": "GeoNetwork API could not be reached",
"search.error.receivedError": "An error was received",
diff --git a/translations/es.json b/translations/es.json
index 4efd6e5dd6..5db5548e26 100644
--- a/translations/es.json
+++ b/translations/es.json
@@ -192,6 +192,24 @@
"record.metadata.updatedOn": "",
"record.metadata.usage": "",
"record.metadata.noUsage": "",
+ "record.metadata.quality": "",
+ "record.metadata.quality.details": "",
+ "record.metadata.quality.title.success": "",
+ "record.metadata.quality.title.failed": "",
+ "record.metadata.quality.description.success": "",
+ "record.metadata.quality.description.failed": "",
+ "record.metadata.quality.topic.success": "",
+ "record.metadata.quality.topic.failed": "",
+ "record.metadata.quality.keywords.success": "",
+ "record.metadata.quality.keywords.failed": "",
+ "record.metadata.quality.legalConstraints.success": "",
+ "record.metadata.quality.legalConstraints.failed": "",
+ "record.metadata.quality.contact.success": "",
+ "record.metadata.quality.contact.failed": "",
+ "record.metadata.quality.updateFrequency.success": "",
+ "record.metadata.quality.updateFrequency.failed": "",
+ "record.metadata.quality.organisation.success": "",
+ "record.metadata.quality.organisation.failed": "",
"record.more.details": "",
"record.tab.chart": "",
"record.tab.data": "",
@@ -206,6 +224,7 @@
"results.sortBy.dateStamp": "",
"results.sortBy.popularity": "",
"results.sortBy.relevancy": "",
+ "results.sortBy.qualityScore": "",
"search.autocomplete.error": "",
"search.error.couldNotReachApi": "",
"search.error.receivedError": "",
diff --git a/translations/fr.json b/translations/fr.json
index 7b38af7817..aa893253e2 100644
--- a/translations/fr.json
+++ b/translations/fr.json
@@ -186,12 +186,30 @@
"record.metadata.related": "Voir aussi",
"record.metadata.sheet": "Fiche de métadonnées d'origine",
"record.metadata.status": "statut",
- "record.metadata.title": "titre",
+ "record.metadata.title": "Titre",
"record.metadata.updateFrequency": "Fréquence de mise à jour",
"record.metadata.updateStatus": "Statut de mise à jour",
"record.metadata.updatedOn": "Dernière mise à jour",
"record.metadata.usage": "Conditions d'utilisation",
"record.metadata.noUsage": "Aucune condition d'utilisation spécifiée pour ces données",
+ "record.metadata.quality": "Qualité des métadonnées",
+ "record.metadata.quality.details": "Détails",
+ "record.metadata.quality.title.success": "Titre est renseigné",
+ "record.metadata.quality.title.failed": "Titre n'est pas renseigné",
+ "record.metadata.quality.description.success": "Description est renseignée",
+ "record.metadata.quality.description.failed": "Description n'est pas renseignée",
+ "record.metadata.quality.topic.success": "Thème est renseigné",
+ "record.metadata.quality.topic.failed": "Thème n'est pas renseigné",
+ "record.metadata.quality.keywords.success": "Mots clés sont renseignés",
+ "record.metadata.quality.keywords.failed": "Mots clés ne sont pas renseignés",
+ "record.metadata.quality.legalConstraints.success": "Contraintes légales sont renseignées",
+ "record.metadata.quality.legalConstraints.failed": "Contraintes légales ne sont pas renseignées",
+ "record.metadata.quality.contact.success": "Contact est renseigné",
+ "record.metadata.quality.contact.failed": "Contact n'est pas renseigné",
+ "record.metadata.quality.updateFrequency.success": "Fréquence de mise à jour est renseignée",
+ "record.metadata.quality.updateFrequency.failed": "Fréquence de mise à jour n'est pas renseignée",
+ "record.metadata.quality.organisation.success": "Producteur est renseigné",
+ "record.metadata.quality.organisation.failed": "Producteur n'est pas renseigné",
"record.more.details": "Détails",
"record.tab.chart": "Graphique",
"record.tab.data": "Tableau",
@@ -206,6 +224,7 @@
"results.sortBy.dateStamp": "Plus récent",
"results.sortBy.popularity": "Popularité",
"results.sortBy.relevancy": "Pertinence",
+ "results.sortBy.qualityScore": "Indicateur de qualité",
"search.autocomplete.error": "Les suggestions ne peuvent pas être récupérées",
"search.error.couldNotReachApi": "Problème de connexion à l'API GeoNetwork",
"search.error.receivedError": "Erreur retournée",
diff --git a/translations/it.json b/translations/it.json
index 7a8dc9ccdf..b8dbdab515 100644
--- a/translations/it.json
+++ b/translations/it.json
@@ -192,6 +192,24 @@
"record.metadata.updatedOn": "",
"record.metadata.usage": "",
"record.metadata.noUsage": "",
+ "record.metadata.quality": "",
+ "record.metadata.quality.details": "",
+ "record.metadata.quality.title.success": "",
+ "record.metadata.quality.title.failed": "",
+ "record.metadata.quality.description.success": "",
+ "record.metadata.quality.description.failed": "",
+ "record.metadata.quality.topic.success": "",
+ "record.metadata.quality.topic.failed": "",
+ "record.metadata.quality.keywords.success": "",
+ "record.metadata.quality.keywords.failed": "",
+ "record.metadata.quality.legalConstraints.success": "",
+ "record.metadata.quality.legalConstraints.failed": "",
+ "record.metadata.quality.contact.success": "",
+ "record.metadata.quality.contact.failed": "",
+ "record.metadata.quality.updateFrequency.success": "",
+ "record.metadata.quality.updateFrequency.failed": "",
+ "record.metadata.quality.organisation.success": "",
+ "record.metadata.quality.organisation.failed": "",
"record.more.details": "",
"record.tab.chart": "",
"record.tab.data": "",
@@ -206,6 +224,7 @@
"results.sortBy.dateStamp": "",
"results.sortBy.popularity": "",
"results.sortBy.relevancy": "",
+ "results.sortBy.qualityScore": "",
"search.autocomplete.error": "",
"search.error.couldNotReachApi": "",
"search.error.receivedError": "",
diff --git a/translations/nl.json b/translations/nl.json
index 34d4420419..98c31dd2ca 100644
--- a/translations/nl.json
+++ b/translations/nl.json
@@ -192,6 +192,24 @@
"record.metadata.updatedOn": "",
"record.metadata.usage": "",
"record.metadata.noUsage": "",
+ "record.metadata.quality": "",
+ "record.metadata.quality.details": "",
+ "record.metadata.quality.title.success": "",
+ "record.metadata.quality.title.failed": "",
+ "record.metadata.quality.description.success": "",
+ "record.metadata.quality.description.failed": "",
+ "record.metadata.quality.topic.success": "",
+ "record.metadata.quality.topic.failed": "",
+ "record.metadata.quality.keywords.success": "",
+ "record.metadata.quality.keywords.failed": "",
+ "record.metadata.quality.legalConstraints.success": "",
+ "record.metadata.quality.legalConstraints.failed": "",
+ "record.metadata.quality.contact.success": "",
+ "record.metadata.quality.contact.failed": "",
+ "record.metadata.quality.updateFrequency.success": "",
+ "record.metadata.quality.updateFrequency.failed": "",
+ "record.metadata.quality.organisation.success": "",
+ "record.metadata.quality.organisation.failed": "",
"record.more.details": "",
"record.tab.chart": "",
"record.tab.data": "",
@@ -206,6 +224,7 @@
"results.sortBy.dateStamp": "",
"results.sortBy.popularity": "",
"results.sortBy.relevancy": "",
+ "results.sortBy.qualityScore": "",
"search.autocomplete.error": "",
"search.error.couldNotReachApi": "",
"search.error.receivedError": "",
diff --git a/translations/pt.json b/translations/pt.json
index 0291fee6f1..8ff6c8189e 100644
--- a/translations/pt.json
+++ b/translations/pt.json
@@ -192,6 +192,24 @@
"record.metadata.updatedOn": "",
"record.metadata.usage": "",
"record.metadata.noUsage": "",
+ "record.metadata.quality": "",
+ "record.metadata.quality.details": "",
+ "record.metadata.quality.title.success": "",
+ "record.metadata.quality.title.failed": "",
+ "record.metadata.quality.description.success": "",
+ "record.metadata.quality.description.failed": "",
+ "record.metadata.quality.topic.success": "",
+ "record.metadata.quality.topic.failed": "",
+ "record.metadata.quality.keywords.success": "",
+ "record.metadata.quality.keywords.failed": "",
+ "record.metadata.quality.legalConstraints.success": "",
+ "record.metadata.quality.legalConstraints.failed": "",
+ "record.metadata.quality.contact.success": "",
+ "record.metadata.quality.contact.failed": "",
+ "record.metadata.quality.updateFrequency.success": "",
+ "record.metadata.quality.updateFrequency.failed": "",
+ "record.metadata.quality.organisation.success": "",
+ "record.metadata.quality.organisation.failed": "",
"record.more.details": "",
"record.tab.chart": "",
"record.tab.data": "",
@@ -206,6 +224,7 @@
"results.sortBy.dateStamp": "",
"results.sortBy.popularity": "",
"results.sortBy.relevancy": "",
+ "results.sortBy.qualityScore": "",
"search.autocomplete.error": "",
"search.error.couldNotReachApi": "",
"search.error.receivedError": "",