diff --git a/src/app/geosource/components/dataset-detail/dataset-detail.component.ts b/src/app/geosource/components/dataset-detail/dataset-detail.component.ts index a79c10db6ec7007f538dfc72d82a45a96f3f08bf..cfe0c004eaeb574cbef71f280acb320801cabe26 100644 --- a/src/app/geosource/components/dataset-detail/dataset-detail.component.ts +++ b/src/app/geosource/components/dataset-detail/dataset-detail.component.ts @@ -104,7 +104,8 @@ export class DatasetDetailComponent implements OnInit, OnDestroy { } get datasetUpdateFrequency() { - return geosource.updateFrequencies[this.metadata.updateFrequency]; + const keyUpdate = this.metadata.updateFrequency ? this.metadata.updateFrequency : 'other'; + return geosource.updateFrequencies[keyUpdate]; } get datasetDataRepresentationType(): string { diff --git a/src/app/geosource/components/dataset-detail/dataset-info/dataset-info.component.html b/src/app/geosource/components/dataset-detail/dataset-info/dataset-info.component.html index cf120b2deea1c9104b81f91e302774bba28d388c..f64e53aaf285cca09a245bcc9ca0d3c416a0358b 100644 --- a/src/app/geosource/components/dataset-detail/dataset-info/dataset-info.component.html +++ b/src/app/geosource/components/dataset-detail/dataset-info/dataset-info.component.html @@ -177,17 +177,15 @@ <span>{{ categories }}</span> </div> - <div class="info-section" *ngIf="legalConstraints !== null"> + <div class="info-section"> <span class="info-title" i18n="@@dataset.info.legalContraints"> Legal constraints </span> - <p> - {{ legalConstraints.license }} - </p> - <p> - {{ legalConstraints.restriction }} + <p *ngFor="let constraint of metadata.legalConstraints"> + {{ constraint }} </p> - <a [href]="legalConstraints.url" target="_blank" class="button btn-blue-text" i18n="@@dataset.info.consultLicense"> + <a [href]="licenceUrl" target="_blank" class="button btn-blue-text" *ngIf="licenceUrl" + i18n="@@dataset.info.consultLicense"> Consult the license </a> </div> diff --git a/src/app/geosource/components/dataset-detail/dataset-info/dataset-info.component.ts b/src/app/geosource/components/dataset-detail/dataset-info/dataset-info.component.ts index ba45827074ad1b357877ccdf5c2a8790265a5f6d..79c7b6063f580c20cfb9a23333c7405c77b3df2d 100644 --- a/src/app/geosource/components/dataset-detail/dataset-info/dataset-info.component.ts +++ b/src/app/geosource/components/dataset-detail/dataset-info/dataset-info.component.ts @@ -120,7 +120,8 @@ export class DatasetInfoComponent implements OnInit, OnDestroy { restriction: this._datasetDetailService.datasetEditorialMetadata.isOpenAccess ? geosource.accessRestriction.open : geosource.accessRestriction.restricted, publicationDate: this.metadata.publicationDate, viewsNumber: this._datasetDetailService.datasetNumberOfViews, - updateFrequency: geosource.updateFrequencies[this.metadata.updateFrequency], + updateFrequency: this.metadata.updateFrequency ? + geosource.updateFrequencies[this.metadata.updateFrequency] : geosource.updateFrequencies.other, dataType: geosource.datasetTypes[this.metadata.type], }; } @@ -133,18 +134,11 @@ export class DatasetInfoComponent implements OnInit, OnDestroy { return this.metadata.getCategories().join(', '); } - get legalConstraints(): ILegalConstraints { - let obj = null; - // tslint:disable-next-line:max-line-length - if (Array.isArray(this.metadata.link) && Array.isArray(this.metadata.legalConstraints) && this.metadata.legalConstraints.length >= 2) { - const license = this.metadata.link.find(l => l.name.includes('Licence')); - obj = { - license: this.metadata.legalConstraints[1], - restriction: this.metadata.legalConstraints[0], - url: license && license.url ? license.url : null, - }; - } - return obj; + get licenceUrl(): string { + + const license = this.metadata.link.find(l => l.name.includes('Licence')); + const url = license && license.url ? license.url : null; + return url; } get keywords() { @@ -165,12 +159,6 @@ interface IGeographicalInfo { coordinatesSystem: string; } -interface ILegalConstraints { - license: string; - restriction: string; - url: string; -} - interface ISummaryInfo { isOpenAccess: boolean; restriction: string; diff --git a/src/app/geosource/components/dataset-detail/dataset-resources/dataset-resources.component.html b/src/app/geosource/components/dataset-detail/dataset-resources/dataset-resources.component.html index 10875fc69ecbd75eb7bc312fdaf53c5193d7012e..5c3f32c85b840a4601e72cd062b84128db7fcb06 100644 --- a/src/app/geosource/components/dataset-detail/dataset-resources/dataset-resources.component.html +++ b/src/app/geosource/components/dataset-detail/dataset-resources/dataset-resources.component.html @@ -67,15 +67,18 @@ <span class="resource-header-title" i18n="@@dataset.resources.other.title"> Downloadable Resources </span> </div> - <div class="resource-other" *ngFor="let other of otherResources | keyvalue"> - <div class="resource-other-title"> - <span>{{ other.key }}</span> - </div> - <div class="resource-other-link" *ngFor="let resource of other.value"> - <a [href]="resource.url" target="_blank">{{ resource.url }}</a> - </div> - - </div> + <ng-container *ngIf="otherResources"> + <div class="resource-other" *ngFor="let other of otherResources"> + <div class="resource-other-title"> + <span>{{ other.name }}</span> + </div> + <div class="resource-other-link"> + <a [href]="other.url" target="_blank">{{ other.url }}</a> + </div> + + </div> + </ng-container> + </div> diff --git a/src/app/geosource/components/dataset-detail/dataset-resources/dataset-resources.component.ts b/src/app/geosource/components/dataset-detail/dataset-resources/dataset-resources.component.ts index 0c2d3cdc6cef5f1b906ec22686548a54a0087015..34e47030cd21d90f254ec93e603f518a4c36c7b1 100644 --- a/src/app/geosource/components/dataset-detail/dataset-resources/dataset-resources.component.ts +++ b/src/app/geosource/components/dataset-detail/dataset-resources/dataset-resources.component.ts @@ -92,10 +92,8 @@ export class DatasetResourcesComponent implements OnInit, OnDestroy { if (this.metadata.link) { this.metadata.link.forEach((link) => { if (link.service) { - let metadataLinkFound = false; this.resources.forEach((resource) => { const resourceCopy = Object.assign({}, resource); - metadataLinkFound = true; resourceCopy.metadataLink = link; if (link.service === resource.type && resourceCopy.isQueryable) { if (!this.queryableResources[link.description]) { @@ -104,14 +102,15 @@ export class DatasetResourcesComponent implements OnInit, OnDestroy { this.queryableResources[link.description].push(resourceCopy); } }); - if (!metadataLinkFound) { - this.otherResources.push(link); - } } else if (link.service === undefined) { - if (!this.downloadableResources[link.description]) { - this.downloadableResources[link.description] = []; + if (link.formats) { + if (!this.downloadableResources[link.description]) { + this.downloadableResources[link.description] = []; + } + this.downloadableResources[link.description].push(link); + } else { + this.otherResources.push(link); } - this.downloadableResources[link.description].push(link); } }); } diff --git a/src/app/geosource/components/results/results.component.ts b/src/app/geosource/components/results/results.component.ts index a3440b4112ebf87bf3be3e1953243bd213bc9256..bf783d22f9f54f65ebcaffbae782a0d7fbe5b730 100644 --- a/src/app/geosource/components/results/results.component.ts +++ b/src/app/geosource/components/results/results.component.ts @@ -83,21 +83,6 @@ export class ResultsComponent implements OnInit, OnDestroy { this.paginator.length = this.countResults[this.selectedScope.key]; this._viewportScroller.scrollToPosition(position); - // Get the page views for each dataset - if (this.results[0] instanceof Dataset) { - const list = this.results as Dataset[]; - list.forEach((e) => { - const dataset = e as Dataset; - if (dataset.metadata) { - this._matomoService.getPageMetadataPageMetrics(dataset.metadata.geonet.uuid).subscribe( - (views) => { - dataset.nbViews = views; - }, - (err) => { - }); - } - }); - } }, (err) => { this._notificationService.notify(new Notification({ diff --git a/src/app/geosource/models/elasticsearch-options.model.ts b/src/app/geosource/models/elasticsearch-options.model.ts index 8478f50acdcd0666c4421e0e314179c8088db303..1bff06efeb9b0fe201f14b7192ba1da6cf341616 100644 --- a/src/app/geosource/models/elasticsearch-options.model.ts +++ b/src/app/geosource/models/elasticsearch-options.model.ts @@ -47,6 +47,7 @@ export class ElasticsearchOptions { 'shouldAggregateFilters': boolean; 'fromAutocompletion': boolean; 'scope': IScope; + otherLicenceAggregations: string[]; constructor(data?) { this.from = (data && data.from != null) ? data.from : 0; @@ -57,6 +58,7 @@ export class ElasticsearchOptions { this.shouldAggregateFilters = true; this.fromAutocompletion = false; this.scope = (data.scope) ? data.scope : scopesResearch.all; + this.otherLicenceAggregations = []; // Need to be done in order to avoid object reference copy this._sortOptions = { @@ -92,7 +94,7 @@ export class ElasticsearchOptions { index: 'dataset', }), new Filter({ - field: 'metadata-fr.legalConstraints', + field: 'metadata-fr.license', label: geosource.filterCategories.licences, aggregations: [], type: 'string', @@ -122,6 +124,11 @@ export class ElasticsearchOptions { ]; } + // Known licences + get knownLicences() { + return ['Licence Ouverte', 'Licence Engagée', 'Licence Associée']; + } + // Getters get pageIndex(): number { diff --git a/src/app/geosource/models/filter.model.ts b/src/app/geosource/models/filter.model.ts index 8ab689d87005aff35ff0077a1329113a9d2f8735..62f6e01895bd43d1aea917eb028b982dc17944be 100644 --- a/src/app/geosource/models/filter.model.ts +++ b/src/app/geosource/models/filter.model.ts @@ -35,12 +35,34 @@ export class Aggregation { this.label = this.key; } + // The label display needs some treatment if too long or if have special pattern: + // If the label is text1 (acronym1) / text2 (acronym) / text3 + // -> acronym1 / acronym2 / text3 if (typeof this.label === 'string') { - this.troncatedLabel = this.label.substring(0, 30); - if (this.label.length > 30) { + const splitLabel = this.label.split('/'); + if (splitLabel && splitLabel.length > 1) { + let newLabel = ''; + splitLabel.forEach((element) => { + const regex = new RegExp(/.*? \((.*?)\)/g); + const execRegex = regex.exec(element); + if (execRegex !== null) { + const matches = Array.from(execRegex); + newLabel += `${matches[1]} / `; + } else { + newLabel += element; + } + + }); + this.troncatedLabel = newLabel; + } else { + this.troncatedLabel = this.label; + } + + if (this.troncatedLabel.length > 30) { this.troncatedLabel = `${this.troncatedLabel.substring(0, 30)}...`; } } + } findInSubAggregations(key: string) { diff --git a/src/app/geosource/models/metadata.model.ts b/src/app/geosource/models/metadata.model.ts index 345a824863528a3a72e4c5c96daea543e7ce0a1d..a290126516871371463721ef8f83a17e1f9299f1 100644 --- a/src/app/geosource/models/metadata.model.ts +++ b/src/app/geosource/models/metadata.model.ts @@ -13,6 +13,7 @@ export interface IMetadata { 'topic_category': string[]; 'highlight'?: any; 'legalConstraints': string[]; + 'license': string; 'rights': string[]; 'total_documents': number; 'lineage': string; @@ -121,6 +122,7 @@ export class Metadata { 'topic_category': string[]; highlight: any; legalConstraints: string[]; + license: string; rights: string[]; 'total_documents': number; lineage: string; @@ -183,6 +185,7 @@ export class Metadata { this.topic_category = data.topic_category; this.highlight = data.highlight; this.legalConstraints = data.legalConstraints; + this.license = data.license; this.rights = data.rights; this.total_documents = data.total_documents; this.lineage = data.lineage; @@ -190,16 +193,7 @@ export class Metadata { this.denominator = data.denominator; this.resolution = data.resolution; this.popularity = data.popularity; - // This verification has to be removed when the updateFrequency will always be indexed as a string - if (data.updateFrequency) { - if (typeof data.updateFrequency === 'string') { - this.updateFrequency = data.updateFrequency; - } else { - this.updateFrequency = data.updateFrequency[0]; - } - } else { - this.updateFrequency = null; - } + this.updateFrequency = data.updateFrequency; this.geonet = data['geonet\:info']; this.crs = data.crs; if (data.category) { diff --git a/src/app/geosource/models/scopes-research.ts b/src/app/geosource/models/scopes-research.ts index a215d185a5dcfd5df07d8be21ff52551c5f07f82..4313cd5abf331a8ecb9736c12045512057f9c3fd 100644 --- a/src/app/geosource/models/scopes-research.ts +++ b/src/app/geosource/models/scopes-research.ts @@ -29,4 +29,11 @@ export const scopesResearch = { elasticType: ['post'], errorItem: geosource.errorItem.posts, }, + page: + { + key: 'page', + label: geosource.researchScope.pages, + elasticType: ['page'], + errorItem: geosource.errorItem.pages, + }, }; diff --git a/src/app/geosource/services/dataset-research.service.ts b/src/app/geosource/services/dataset-research.service.ts index 784a94408715b018c56bf3e01330ed1134a7ec43..fa68bed6bade828998b8f98e016dcd3b87732f08 100644 --- a/src/app/geosource/services/dataset-research.service.ts +++ b/src/app/geosource/services/dataset-research.service.ts @@ -8,7 +8,7 @@ import { } from '../models'; import { ElasticsearchService } from './elasticsearch.service'; import { map, catchError, tap } from 'rxjs/operators'; -import { notificationMessages } from '../../../i18n/traductions'; +import { notificationMessages, geosource } from '../../../i18n/traductions'; import { ErrorService } from '../../core/services'; import { Aggregation, Filter } from '../models/filter.model'; import { CMSContent } from '../../editorialisation/models'; @@ -71,14 +71,22 @@ export class DatasetResearchService { }); // All scope + const countAll = this._resultsCount.reduce((acc, obj) => { + const count = obj.scopeKey !== scopesResearch.page.key ? acc + obj.count : 0; + return count; + // tslint:disable-next-line:align + }, 0); this._resultsCount.push({ scopeKey: scopesResearch.all.key, - count: this._resultsCount.reduce((acc, obj) => { return acc + obj.count; }, 0), + count: countAll, }); } if (this._elasticsearchOptions.shouldAggregateFilters) { // Get the aggregations that will be used to display the filters - const aggregations = (e.hits.hits) ? e.aggregations : []; + let aggregations = (e.hits.hits) ? e.aggregations : []; + + aggregations = this.manageLicenceAggregations(aggregations); + this._elasticsearchOptions.filters.forEach((filter) => { // We check which one filter was lasted clicked. @@ -216,6 +224,38 @@ export class DatasetResearchService { ); } + private manageLicenceAggregations(aggregations) { + const hasAlreadyOtherLicenses = this._elasticsearchOptions.otherLicenceAggregations.length > 0; + for (const property in aggregations) { + if (aggregations.hasOwnProperty(property)) { + + if (property === 'metadata-fr.license') { + + let countOtherBucket = 0; + + for (let i = aggregations[property]['buckets'].length - 1; i >= 0; i = i - 1) { + const bucket = aggregations[property]['buckets'][i]; + if (!this._elasticsearchOptions.knownLicences.includes(bucket.key)) { + countOtherBucket += bucket.count_per_metadata.value; + aggregations[property]['buckets'].splice(i, 1); + if (! hasAlreadyOtherLicenses) { + this._elasticsearchOptions.otherLicenceAggregations.push(bucket.key); + } + } + } + + aggregations[property]['buckets'].push({ + key: 'Autres', + count_per_metadata: { + value: countOtherBucket, + }, + }); + } + } + } + return aggregations; + } + private sortAggregations(aggregations: Aggregation[], type: string) { if (type === 'date') { // Sort by date from the newest to the lastest diff --git a/src/app/geosource/services/elasticsearch.service.ts b/src/app/geosource/services/elasticsearch.service.ts index 8def563cb11eacbaaf71328e70e1177fe3ef9f24..ad5452c7fdc99c25702f852913a139ad6b06f6d5 100644 --- a/src/app/geosource/services/elasticsearch.service.ts +++ b/src/app/geosource/services/elasticsearch.service.ts @@ -7,7 +7,7 @@ import { HttpClient } from '@angular/common/http'; import { Observable } from 'rxjs'; import { map, catchError } from 'rxjs/operators'; import { ErrorService } from '../../core/services'; -import { notificationMessages } from '../../../i18n/traductions'; +import { notificationMessages, geosource } from '../../../i18n/traductions'; import { IPostsESOptions } from '../models/elasticsearch-options.model'; import { Aggregation } from '../models/filter.model'; import { scopesResearch } from '../models/scopes-research'; @@ -16,6 +16,7 @@ import { APP_CONFIG } from '../../core/services/app-config.service'; @Injectable() export class ElasticsearchService { + geosource = geosource; elasticSearchUrl: string; constructor( @@ -35,24 +36,8 @@ export class ElasticsearchService { getAllEntities(options: ElasticsearchOptions): Observable<IElasticsearchResponse> { const requestOptions = this.constructElasticsearchRequest(options); - // For now ES cache is not used anymore. Nginx cache should be enough To remove in - // the future if this choice is validated. - // We check if request AND result are already in cache - // if (options.scope) { - // const request = this._storageService.get(`requestES${options.scope.elasticType[0]}`); - // if (environment.enableEsFrontCache && request === JSON.stringify(requestOptions) - // && this._storageService.get(`resultES${options.scope.elasticType[0]}`)) { - - // const jsonESResponse = - // JSON.parse(this._storageService.get(`resultES${options.scope.elasticType[0]}`)) as IElasticsearchResponse; - // return of(jsonESResponse); - // } - // } - return this._http.request<IElasticsearchResponse>('POST', this.elasticSearchUrl, requestOptions).pipe( map((e) => { - // this._storageService.set(`requestES${options.scope.elasticType[0]}`, JSON.stringify(requestOptions), 600000); - // this._storageService.set(`resultES${options.scope.elasticType[0]}`, JSON.stringify(e), 600000); return e; }), catchError( @@ -169,7 +154,7 @@ export class ElasticsearchService { }, fields: { '*data-fr.*': { - fragment_size: 10, + fragment_size: 50, fragmenter: 'span', type: 'unified', require_field_match: false, @@ -500,6 +485,7 @@ export class ElasticsearchService { // Filters by aggregations let shouldExpression: Object[] = []; + options.filters.forEach((filter) => { const activeAggregations = filter.findActiveAggregations(); if (activeAggregations.length > 0) { @@ -531,13 +517,24 @@ export class ElasticsearchService { }, }); } else { - shouldExpression.push({ - term: { - [field]: activeAgg.key, - }, - }); + // Special treatment for the licences + if (filter.label === geosource.filterCategories.licences && + !options.knownLicences.includes(activeAgg.key)) { + options.otherLicenceAggregations.forEach((agg) => { + shouldExpression.push({ + term: { + 'metadata-fr.license.keyword': agg, + }, + }); + }); + } else { + shouldExpression.push({ + term: { + [field]: activeAgg.key, + }, + }); + } } - } }); } @@ -551,7 +548,6 @@ export class ElasticsearchService { }, }, ); - shouldExpression = []; } }); diff --git a/src/app/map/components/data-details/data-detail-properties/data-detail-properties.component.html b/src/app/map/components/data-details/data-detail-properties/data-detail-properties.component.html index b88242851867ee815711d53d75713c2d17c494bd..2b502c4e73dddb5c72a2e3584d8c568d90affe61 100644 --- a/src/app/map/components/data-details/data-detail-properties/data-detail-properties.component.html +++ b/src/app/map/components/data-details/data-detail-properties/data-detail-properties.component.html @@ -2,8 +2,8 @@ <p class="property-key" *ngIf="!inputIsArray()" [ngClass]="{'is-parent': isParent && isPropertyObjectOrArray(key)}" (click)="toggleProperty(key)"> <ng-container *ngIf="isParent && isPropertyObjectOrArray(key)"> - <span class="icon-minus" *ngIf="isOpenProperties[key]"></span> - <span class="icon-plus" *ngIf="!isOpenProperties[key]"></span> + <span class="icon-minus" *ngIf="isOpenProperties[key]"></span> + <span class="icon-plus" *ngIf="!isOpenProperties[key]"></span> </ng-container> <span>{{ key }}</span> </p> @@ -15,7 +15,11 @@ <div *ngSwitchCase="'array'"> <app-data-detail-properties [properties]="properties[key]" [isParent]="false"></app-data-detail-properties> </div> - <div *ngSwitchDefault class="property-value">{{ properties[key] }}</div> + <!-- Display the value. If it is url, display a link --> + <div *ngSwitchDefault class="property-value"> + <span *ngIf="!isUrl(properties[key])">{{ properties[key] }}</span> + <a [href]="properties[key]" *ngIf="isUrl(properties[key])">{{ properties[key] }}</a> + </div> </ng-container> </div> </div> diff --git a/src/app/map/components/data-details/data-detail-properties/data-detail-properties.component.ts b/src/app/map/components/data-details/data-detail-properties/data-detail-properties.component.ts index 51b3922ef32341430b76944f45f03c1f1f59f7a3..50ec942898da4055e64bc7b592e1bd6901ae1432 100644 --- a/src/app/map/components/data-details/data-detail-properties/data-detail-properties.component.ts +++ b/src/app/map/components/data-details/data-detail-properties/data-detail-properties.component.ts @@ -72,4 +72,11 @@ export class DataDetailPropertiesComponent implements OnInit { } return true; } + + isUrl(value: any) { + const expression = 'https?:\/\/(www\.)?[-a-zA-Z0-9@:%._\+~#=]{2,256}\.[a-z]{2,6}\b([-a-zA-Z0-9@:%_\+.~#?&//=]*)'; + const regex = new RegExp(expression); + const isStringAndNotNull = value ? typeof value === 'string' : false; + return isStringAndNotNull ? value.match(regex) : false; + } } diff --git a/src/assets/config/config.json b/src/assets/config/config.json index ff4f3a16c2f474b17e821160de4e14a85120d18a..c73e91f96a4bd4572782415170ff816ba56a3d89 100644 --- a/src/assets/config/config.json +++ b/src/assets/config/config.json @@ -3,8 +3,8 @@ "organizations": "http://localhost:3000/organizations", "resources": "http://localhost:3003", "credits": "http://localhost:3005/credits", - "auth": "http://localhost:4200/authentication/", - "middlewareLegacyAuth": "http://localhost:4200/middleware/", + "auth": "https://kong-dev.alpha.grandlyon.com/authentication/", + "middlewareLegacyAuth": "https://kong-dev.alpha.grandlyon.com/middleware-legacy/", "email": "http://localhost:3001/email", "matomo": "https://matomo-intothesky.alpha.grandlyon.com", "elasticsearch": "https://kong-dev.alpha.grandlyon.com/es-consumer-aware", diff --git a/src/i18n/traductions.fr.ts b/src/i18n/traductions.fr.ts index af97bd1dd53097df053efb736a8fd0cabcf2cb42..cffd91b2a9550e94d98f75850eeebe45a40ad93c 100644 --- a/src/i18n/traductions.fr.ts +++ b/src/i18n/traductions.fr.ts @@ -169,6 +169,7 @@ export const geosource = { series: 'Séries de données', services: 'Services', articles: 'Articles', + pages: 'Pages', }, placeholders: { all: 'ex: cadastre, randonnée, bibliothèque', @@ -183,6 +184,7 @@ export const geosource = { series: 'série', services: 'service', posts: 'article', + pages: 'page', }, mapMessages: { copied: 'Copié !', diff --git a/src/i18n/traductions.ts b/src/i18n/traductions.ts index ea6d414e0a7952ff6ed12fd19870882f9bfcfada..b070c9372a32f49e170bbf6b0588be68f45ff9b1 100644 --- a/src/i18n/traductions.ts +++ b/src/i18n/traductions.ts @@ -166,6 +166,7 @@ export const geosource = { series: 'Data series', services: 'Services', articles: 'Articles', + pages: 'Pages', }, placeholders: { all: 'e.g. cadastre, randonnée, bibliothèque', @@ -179,7 +180,9 @@ export const geosource = { datasets: 'data', series: 'serie', services: 'service', + articles: 'Articles', posts: 'post', + pages: 'page', }, mapMessages: { copied: 'Copied !',