Skip to content

Commit

Permalink
Merge pull request #843 from geonetwork/implement-iso19115-3-converter
Browse files Browse the repository at this point in the history
Metadata Editor / Implement iso19115-3 converter
  • Loading branch information
jahow authored Apr 12, 2024
2 parents 28c8df1 + 5ec96b6 commit 376e0e9
Show file tree
Hide file tree
Showing 50 changed files with 5,363 additions and 637 deletions.
4 changes: 2 additions & 2 deletions apps/metadata-converter/src/app/app.component.html
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ <h1 class="text-[50px] font-bold font-title my-6 mx-10">
<gn-ui-status
#status
class="font-main font-normal text-gray-900 text-[16px]"
(newRecordIso19139)="onRecordOutputReceived($event)"
(newMetadata)="onRecordOutputReceived($event)"
(newRecordNative)="onRecordNativeReceived($event)"
></gn-ui-status>
</h1>
Expand All @@ -20,7 +20,7 @@ <h1 class="text-[50px] font-bold font-title my-6 mx-10">
placeholder="Upload a record from your computer"
/>
<input
type="string"
type="text"
class="border border-gray-500 rounded-sm w-full bg-white p-2"
(change)="onFileUrlInput($event.target.value)"
placeholder="...or enter a URL pointing to a metadata record"
Expand Down
8 changes: 4 additions & 4 deletions apps/metadata-converter/src/app/app.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,8 @@ export class AppComponent {
reader.readAsText(file, 'UTF-8')
reader.onload = (evt) => {
const text = evt.target.result as string
this.statusComponent.referenceIso19139 = text
this.statusComponent.recordIso19139 = text
this.statusComponent.referenceMetadata = text
this.statusComponent.currentMetadata = text
}
reader.onerror = () => this.statusComponent.errorReadingFile()
}
Expand All @@ -47,8 +47,8 @@ export class AppComponent {
fetch(url)
.then((resp) => resp.text())
.then((text) => {
this.statusComponent.referenceIso19139 = text
this.statusComponent.recordIso19139 = text
this.statusComponent.referenceMetadata = text
this.statusComponent.currentMetadata = text
})
.catch((e) => this.statusComponent.errorLoadingFile(e.message))
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -68,72 +68,88 @@
role: 'unspecified'
}"
>
<ng-template #contactTpl let-get="getValue" let-set="setValue">
<gn-ui-record-field-group label="Contact">
<gn-ui-record-field-simple
label="First Name"
[fieldValue]="get().firstName"
(fieldValueChange)="set($event, 'firstName')"
(confirm)="emitChangedRecord()"
></gn-ui-record-field-simple>
<gn-ui-record-field-simple
label="Last Name"
[fieldValue]="get().lastName"
(fieldValueChange)="set($event, 'lastName')"
(confirm)="emitChangedRecord()"
></gn-ui-record-field-simple>
<gn-ui-record-field-simple
label="Email"
[fieldValue]="get().email"
(fieldValueChange)="set($event, 'email')"
(confirm)="emitChangedRecord()"
></gn-ui-record-field-simple>
<gn-ui-record-field-simple
label="Role"
[fieldValue]="get().role"
(fieldValueChange)="set($event, 'role')"
(confirm)="emitChangedRecord()"
[options]="roleOptions"
></gn-ui-record-field-simple>
<gn-ui-record-field-simple
label="Position"
[fieldValue]="get().position"
(fieldValueChange)="set($event, 'position')"
(confirm)="emitChangedRecord()"
></gn-ui-record-field-simple>
<gn-ui-record-field-group label="Organisation">
<gn-ui-record-field-simple
label="Name"
[fieldValue]="get().organization.name"
(fieldValueChange)="set($event, 'organization', 'name')"
(confirm)="emitChangedRecord()"
></gn-ui-record-field-simple>
<gn-ui-record-field-simple
label="Description"
[fieldValue]="get().organization.description"
(fieldValueChange)="set($event, 'organization', 'description')"
(confirm)="emitChangedRecord()"
></gn-ui-record-field-simple>
<gn-ui-record-field-simple
label="Website"
[fieldValue]="get().organization.website"
(fieldValueChange)="set($event, 'organization', 'website')"
(confirm)="emitChangedRecord()"
[type]="'url'"
></gn-ui-record-field-simple>
<gn-ui-record-field-simple
label="Logo URL"
[fieldValue]="get().organization.logoUrl"
(fieldValueChange)="set($event, 'organization', 'logoUrl')"
(confirm)="emitChangedRecord()"
[type]="'url'"
></gn-ui-record-field-simple>
</gn-ui-record-field-group>
</gn-ui-record-field-group>
</ng-template>
</gn-ui-record-field-array>
</p>
<p class="grid grid-cols-2">
<p>
<gn-ui-record-field-array
label="Contacts for resource"
[(fieldValue)]="record.contactsForResource"
[itemTemplate]="contactTpl"
(confirm)="emitChangedRecord()"
[defaultItem]="{
firstName: 'first',
lastName: 'last',
email: '[email protected]',
organization: { name: 'org' },
role: 'unspecified'
}"
>
</gn-ui-record-field-array>
</p>
<ng-template #contactTpl let-get="getValue" let-set="setValue">
<gn-ui-record-field-group label="Contact">
<gn-ui-record-field-simple
label="First Name"
[fieldValue]="get().firstName"
(fieldValueChange)="set($event, 'firstName')"
(confirm)="emitChangedRecord()"
></gn-ui-record-field-simple>
<gn-ui-record-field-simple
label="Last Name"
[fieldValue]="get().lastName"
(fieldValueChange)="set($event, 'lastName')"
(confirm)="emitChangedRecord()"
></gn-ui-record-field-simple>
<gn-ui-record-field-simple
label="Email"
[fieldValue]="get().email"
(fieldValueChange)="set($event, 'email')"
(confirm)="emitChangedRecord()"
></gn-ui-record-field-simple>
<gn-ui-record-field-simple
label="Role"
[fieldValue]="get().role"
(fieldValueChange)="set($event, 'role')"
(confirm)="emitChangedRecord()"
[options]="roleOptions"
></gn-ui-record-field-simple>
<gn-ui-record-field-simple
label="Position"
[fieldValue]="get().position"
(fieldValueChange)="set($event, 'position')"
(confirm)="emitChangedRecord()"
></gn-ui-record-field-simple>
<gn-ui-record-field-group label="Organisation">
<gn-ui-record-field-simple
label="Name"
[fieldValue]="get().organization.name"
(fieldValueChange)="set($event, 'organization', 'name')"
(confirm)="emitChangedRecord()"
></gn-ui-record-field-simple>
<gn-ui-record-field-simple
label="Description"
[fieldValue]="get().organization.description"
(fieldValueChange)="set($event, 'organization', 'description')"
(confirm)="emitChangedRecord()"
></gn-ui-record-field-simple>
<gn-ui-record-field-simple
label="Website"
[fieldValue]="get().organization.website"
(fieldValueChange)="set($event, 'organization', 'website')"
(confirm)="emitChangedRecord()"
[type]="'url'"
></gn-ui-record-field-simple>
<gn-ui-record-field-simple
label="Logo URL"
[fieldValue]="get().organization.logoUrl"
(fieldValueChange)="set($event, 'organization', 'logoUrl')"
(confirm)="emitChangedRecord()"
[type]="'url'"
></gn-ui-record-field-simple>
</gn-ui-record-field-group>
</gn-ui-record-field-group>
</ng-template>
<p class="grid grid-cols-3">
<gn-ui-record-field-simple
label="Record created on"
[(fieldValue)]="record.recordCreated"
Expand All @@ -146,6 +162,32 @@
(confirm)="emitChangedRecord()"
[type]="'date'"
></gn-ui-record-field-simple>
<gn-ui-record-field-simple
label="Record published on"
[(fieldValue)]="record.recordPublished"
(confirm)="emitChangedRecord()"
[type]="'date'"
></gn-ui-record-field-simple>
</p>
<p class="grid grid-cols-3">
<gn-ui-record-field-simple
label="Resource created on"
[(fieldValue)]="record.resourceCreated"
(confirm)="emitChangedRecord()"
[type]="'date'"
></gn-ui-record-field-simple>
<gn-ui-record-field-simple
label="Resource updated on"
[(fieldValue)]="record.resourceUpdated"
(confirm)="emitChangedRecord()"
[type]="'date'"
></gn-ui-record-field-simple>
<gn-ui-record-field-simple
label="Resource published on"
[(fieldValue)]="record.resourcePublished"
(confirm)="emitChangedRecord()"
[type]="'date'"
></gn-ui-record-field-simple>
</p>
<p>
<gn-ui-record-field-array
Expand Down Expand Up @@ -314,20 +356,6 @@
[type]="'rich'"
></gn-ui-record-field-simple>
</p>
<p class="grid grid-cols-2">
<gn-ui-record-field-simple
label="Dataset created on"
[(fieldValue)]="record.datasetCreated"
(confirm)="emitChangedRecord()"
[type]="'date'"
></gn-ui-record-field-simple>
<gn-ui-record-field-simple
label="Dataset updated on"
[(fieldValue)]="record.datasetUpdated"
(confirm)="emitChangedRecord()"
[type]="'date'"
></gn-ui-record-field-simple>
</p>
<p>
<gn-ui-record-field-array
label="Distributions"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,7 @@ export class RecordFormComponent implements AfterViewInit {
? this.record.ownerOrganization
: { name: 'My Organization' },
contacts: hasPrevious ? this.record.contacts : [],
contactsForResource: hasPrevious ? this.record.contactsForResource : [],
licenses: hasPrevious ? [...this.record.licenses] : [],
legalConstraints: hasPrevious ? [...this.record.legalConstraints] : [],
securityConstraints: hasPrevious
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { Component, EventEmitter, Input, Output } from '@angular/core'
import { toModel, toXml } from '@geonetwork-ui/api/metadata-converter'
import { CatalogRecord } from '@geonetwork-ui/common/domain/model/record'
import { findConverterForDocument } from '@geonetwork-ui/api/metadata-converter'
import { Iso191153Converter } from '@geonetwork-ui/api/metadata-converter'

@Component({
selector: 'gn-ui-status',
Expand All @@ -11,39 +12,41 @@ export class StatusComponent {
@Input() set recordNative(value: CatalogRecord) {
const start = performance.now()
this.status = 'Converting to ISO9139...'
this.newRecordIso19139.emit('')
try {
const output = toXml(value, this.referenceIso19139)
this.newRecordIso19139.emit(output)
const time = Math.round(performance.now() - start)
this.status = `Converting to ISO9139... Done (${time} ms).`
} catch (e) {
this.status = `Converting to ISO9139... Failed: ${
e instanceof Error ? e.message : e
}`
console.error(e)
}
}
@Input() set recordIso19139(value: string) {
this.newMetadata.emit('')
this.recordToXml(value)
.then((output) => {
this.newMetadata.emit(output)
const time = Math.round(performance.now() - start)
this.status = `Converting to ISO9139... Done (${time} ms).`
})
.catch((e) => {
this.status = `Converting to ISO9139... Failed: ${
e instanceof Error ? e.message : e
}`
console.error(e)
})
}
@Input() set currentMetadata(value: string) {
const start = performance.now()
this.status = 'Converting to native format...'
try {
const output = toModel(value)
this.newRecordNative.emit(output)
this.newRecordIso19139.emit(value)
const time = Math.round(performance.now() - start)
this.status = `Converting to native format... Done (${time} ms).`
} catch (e) {
this.status = `Converting to native format... Failed: ${
e instanceof Error ? e.message : e
}`
console.error(e)
}
}
@Input() referenceIso19139: string
this.xmlToRecord(value)
.then((output) => {
this.newRecordNative.emit(output)
this.newMetadata.emit(value)
const time = Math.round(performance.now() - start)
this.status = `Converting to native format... Done (${time} ms).`
})
.catch((e) => {
this.status = `Converting to native format... Failed: ${
e instanceof Error ? e.message : e
}`
console.error(e)
})
}
@Input() referenceMetadata: string

@Output() newRecordNative = new EventEmitter<CatalogRecord>()
@Output() newRecordIso19139 = new EventEmitter<string>()
@Output() newMetadata = new EventEmitter<string>()

status = 'Standing by.'

Expand All @@ -60,4 +63,16 @@ export class StatusComponent {
errorReadingFile() {
this.status = `Reading file... Failed`
}

private recordToXml(record: CatalogRecord) {
const converter = this.referenceMetadata
? findConverterForDocument(this.referenceMetadata)
: new Iso191153Converter()
return converter.writeRecord(record, this.referenceMetadata)
}

private xmlToRecord(metadata: string) {
const converter = findConverterForDocument(metadata)
return converter.readRecord(metadata)
}
}
8 changes: 4 additions & 4 deletions libs/api/metadata-converter/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
export * from './lib/iso19139/converter'
export * from './lib/gn4/gn4.metadata.mapper'
export * from './lib/gn4/atomic-operations'
export * from './lib/gn4/types'
export * from './lib/iso19139'
export * from './lib/iso19115-3'
export * from './lib/find-converter'
export * from './lib/gn4'
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,13 @@ export class MetadataMapperContext {
readonly location?
}

export abstract class MetadataBaseMapper<F> {
export abstract class BaseConverter<F> {
constructor(
protected ctx: MetadataMapperContext = new MetadataMapperContext()
) {}

abstract readRecord(document: F): Promise<CatalogRecord>
abstract writeRecord(record: CatalogRecord): Promise<F>
abstract writeRecord(record: CatalogRecord, reference?: F): Promise<F>
readRecords(documents: F[]): Promise<CatalogRecord[]> {
return Promise.all(documents.map((doc) => this.readRecord(doc)))
}
Expand Down
16 changes: 16 additions & 0 deletions libs/api/metadata-converter/src/lib/find-converter.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { Iso19139Converter } from './iso19139'
import { BaseConverter } from './base.converter'
import { Iso191153Converter } from './iso19115-3'

export function findConverterForDocument(
document: string
): BaseConverter<string> {
if (document.indexOf('mdb:MD_Metadata') > 0) {
return new Iso191153Converter()
} else if (document.indexOf('gmd:MD_Metadata') > 0) {
return new Iso19139Converter()
} else {
throw new Error(`No suitable converter found for the following document:
${document.substring(0, 400)}...`)
}
}
Loading

0 comments on commit 376e0e9

Please sign in to comment.