- remove VOLUME assignments from DOXKERFILE

- add package @opensearch-project/opensearch for manipulating opensearch index
- index tethys datasets via new command  IndexDatasets, callable node ace index:datasets or node ace index:datasets -p 193
- add mapping file for opensearch index in public/records.json
- added solr.xslt for transforming Datset model to json for opensearch adding in opensearch
- added route /editor/ dataset/:id/update (beginning of editor/DatasetController.ts
- npm updates
This commit is contained in:
Kaimbacher 2023-10-17 15:45:41 +02:00
parent 7915f66dd6
commit cf859ba402
21 changed files with 1357 additions and 280 deletions

View File

@ -44,7 +44,7 @@ USER node
# initial update of av databases # initial update of av databases
RUN freshclam RUN freshclam
VOLUME /var/lib/clamav # VOLUME /var/lib/clamav
COPY --chown=node:clamav docker-entrypoint.sh /home/node/app/docker-entrypoint.sh COPY --chown=node:clamav docker-entrypoint.sh /home/node/app/docker-entrypoint.sh
RUN chmod +x /home/node/app/docker-entrypoint.sh RUN chmod +x /home/node/app/docker-entrypoint.sh
ENV TZ="Europe/Vienna" ENV TZ="Europe/Vienna"

View File

@ -1,5 +1,17 @@
{ {
"commands": { "commands": {
"index:datasets": {
"settings": {
"loadApp": true,
"stayAlive": false
},
"commandPath": "./commands/IndexDatasets",
"commandName": "index:datasets",
"description": "",
"args": [],
"aliases": [],
"flags": []
},
"validate:checksum": { "validate:checksum": {
"settings": { "settings": {
"loadApp": true, "loadApp": true,

View File

@ -0,0 +1,175 @@
import type { HttpContextContract } from '@ioc:Adonis/Core/HttpContext';
import { Client } from '@opensearch-project/opensearch';
import Dataset from 'App/Models/Dataset';
import XmlModel from 'App/Library/XmlModel';
import { XMLBuilder } from 'xmlbuilder2/lib/interfaces';
import { create } from 'xmlbuilder2';
import { readFileSync } from 'fs';
import { transform } from 'saxon-js';
// Create a new instance of the client
const client = new Client({ node: 'http://localhost:9200' }); // replace with your OpenSearch endpoint
export default class DatasetsController {
private proc;
constructor() {
this.proc = readFileSync('public/assets2/solr.sef.json');
// Load the XSLT file
// this.proc = readFileSync('public/assets2/datasetxml2oai.sef.json');
}
public async index({}: HttpContextContract) {}
public async create({}: HttpContextContract) {}
public async store({}: HttpContextContract) {}
public async show({}: HttpContextContract) {}
public async edit({}: HttpContextContract) {}
// public async update({}: HttpContextContract) {}
public async update({ request, response }) {
const id = 273; //request.param('id');
const dataset = await Dataset.query().preload('xmlCache').where('id', id).firstOrFail();
// add xml elements
let xml = create({ version: '1.0', encoding: 'UTF-8', standalone: true }, '<root></root>');
const datasetNode = xml.root().ele('Dataset');
await this.createXmlRecord(dataset, datasetNode);
// const domNode = await this.getDatasetXmlDomNode(dataset);
// const xmlString = xml.end({ prettyPrint: true });
// const data = request.only(['field1', 'field2']); // get it from xslt
// Create an index with non-default settings.
var index_name = 'tethys-features';
const xmlString = xml.end({ prettyPrint: false });
let doc = '';
try {
const result = await transform({
// stylesheetFileName: `${config.TMP_BASE_DIR}/data-quality/rules/iati.sef.json`,
stylesheetText: this.proc,
destination: 'serialized',
// sourceFileName: sourceFile,
sourceText: xmlString,
// stylesheetParams: xsltParameter,
// logLevel: 10,
});
doc = result.principalResult;
} catch (error) {
return response.status(500).json({
message: 'An error occurred while creating the user',
error: error.message,
});
}
// var settings = {
// settings: {
// index: {
// number_of_shards: 4,
// number_of_replicas: 3,
// },
// },
// };
// var test = await client.indices.create({
// index: index_name,
// body: settings,
// });
// var document = {
// title: 'Sample Document',
// authors: [
// {
// first_name: 'John',
// last_name: 'Doe',
// },
// {
// first_name: 'Jane',
// last_name: 'Smith',
// },
// ],
// year: '2018',
// genre: 'Crime fiction',
// };
// http://localhost:9200/datastets/_doc/1
// var id = '1';
try {
// console.log(doc);
let document = JSON.parse(`${doc}`);
// https://opensearch.org/docs/2.1/opensearch/supported-field-types/geo-shape/
// Define the new document
// const document = {
// title: 'Your Document Name',
// id: dataset.publish_id,
// doctype: 'GIS',
// // "location" : {
// // "type" : "point",
// // "coordinates" : [74.00, 40.71]
// // },
// geo_location: {
// type: 'linestring',
// coordinates: [
// [-77.03653, 38.897676],
// [-77.009051, 38.889939],
// ],
// },
// // geo_location: 'BBOX (71.0589, 74.0060, 42.3601, 40.7128)'
// // geo_location: {
// // type: 'envelope',
// // coordinates: [
// // [13.0, 53.0],
// // [14.0, 52.0],
// // ], // Define your BBOX coordinates
// // },
// };
// Update the document
var test = await client.index({
id: dataset.publish_id,
index: index_name,
body: document,
refresh: true,
});
// Return the result
return response.json(test.body);
} catch (error) {
// Handle any errors
console.error(error);
return response.status(500).json({ error: 'An error occurred while updating the data.' });
}
}
public async destroy({}: HttpContextContract) {}
public async syncOpensearch({}: HttpContextContract) {}
private async createXmlRecord(dataset: Dataset, datasetNode: XMLBuilder) {
const domNode = await this.getDatasetXmlDomNode(dataset);
if (domNode) {
datasetNode.import(domNode);
}
}
private async getDatasetXmlDomNode(dataset: Dataset) {
const xmlModel = new XmlModel(dataset);
// xmlModel.setModel(dataset);
xmlModel.excludeEmptyFields();
xmlModel.caching = true;
// const cache = dataset.xmlCache ? dataset.xmlCache : null;
// dataset.load('xmlCache');
if (dataset.xmlCache) {
xmlModel.xmlCache = dataset.xmlCache;
}
// return cache.getDomDocument();
const domDocument: XMLBuilder | null = await xmlModel.getDomDocument();
return domDocument;
}
}

View File

@ -85,7 +85,7 @@ export default class XmlModel {
this.cache = this.cache || new DocumentXmlCache(); this.cache = this.cache || new DocumentXmlCache();
this.cache.document_id = dataset.id; this.cache.document_id = dataset.id;
this.cache.xml_version = 1; // (int)$this->strategy->getVersion(); this.cache.xml_version = 1; // (int)$this->strategy->getVersion();
this.cache.server_date_modified = dataset.server_date_modified.toFormat("yyyy-MM-dd HH:mm:ss"); this.cache.server_date_modified = dataset.server_date_modified.toFormat('yyyy-MM-dd HH:mm:ss');
this.cache.xml_data = domDocument.end(); this.cache.xml_data = domDocument.end();
await this.cache.save(); await this.cache.save();
} }
@ -97,7 +97,7 @@ export default class XmlModel {
false, false,
true, true,
)?.node; )?.node;
if(node != undefined) { if (node != undefined) {
domDocument = builder({ version: '1.0', encoding: 'UTF-8', standalone: true }, node); domDocument = builder({ version: '1.0', encoding: 'UTF-8', standalone: true }, node);
} }
} }

View File

@ -13,7 +13,6 @@ import { BaseModel as LucidBaseModel } from '@ioc:Adonis/Lucid/Orm';
// } // }
// } // }
/** /**
* Helper to find if value is a valid Object or * Helper to find if value is a valid Object or
* not * not
@ -22,7 +21,7 @@ export function isObject(value: any): boolean {
return value !== null && typeof value === 'object' && !Array.isArray(value); return value !== null && typeof value === 'object' && !Array.isArray(value);
} }
export default class BaseModel extends LucidBaseModel { export default class BaseModel extends LucidBaseModel {
/** /**
* When `fill` method is called, then we may have a situation where it * When `fill` method is called, then we may have a situation where it
* removed the values which exists in `original` and hence the dirty * removed the values which exists in `original` and hence the dirty
@ -117,7 +116,6 @@ export default class BaseModel extends LucidBaseModel {
return this; return this;
} }
} }
// export class DatasetRelatedBaseModel extends LucidBaseModel { // export class DatasetRelatedBaseModel extends LucidBaseModel {

View File

@ -49,5 +49,4 @@ export default class Collection extends BaseModel {
foreignKey: 'role_id', foreignKey: 'role_id',
}) })
public collectionRole: BelongsTo<typeof CollectionRole>; public collectionRole: BelongsTo<typeof CollectionRole>;
} }

View File

@ -30,7 +30,6 @@ export type DatasetRelatedModel =
| typeof DatasetIdentifier | typeof DatasetIdentifier
| typeof File; | typeof File;
export default abstract class DatasetExtension extends LucidBaseModel { export default abstract class DatasetExtension extends LucidBaseModel {
public abstract id; public abstract id;
public externalFields: Record<string, any> = this.getExternalFields(); public externalFields: Record<string, any> = this.getExternalFields();
@ -323,7 +322,7 @@ export default abstract class DatasetExtension extends LucidBaseModel {
private convertColumnToFieldname(columnName: string): string { private convertColumnToFieldname(columnName: string): string {
return columnName return columnName
.split(/[-_]/) .split(/[-_]/)
.map((word) => (word.charAt(0).toUpperCase() + word.slice(1))) .map((word) => word.charAt(0).toUpperCase() + word.slice(1))
.join(''); .join('');
} }
} }

115
commands/IndexDatasets.ts Normal file
View File

@ -0,0 +1,115 @@
import { BaseCommand, flags } from '@adonisjs/core/build/standalone';
import Logger from '@ioc:Adonis/Core/Logger';
import { XMLBuilder } from 'xmlbuilder2/lib/interfaces';
import { create } from 'xmlbuilder2';
import Dataset from 'App/Models/Dataset';
import XmlModel from 'App/Library/XmlModel';
import { readFileSync } from 'fs';
import { transform } from 'saxon-js';
import { Client } from '@opensearch-project/opensearch';
const client = new Client({ node: 'http://localhost:9200' }); // replace with your OpenSearch endpoint
export default class IndexDatasets extends BaseCommand {
public static commandName = 'index:datasets';
public static description = 'Index datasets based on publish_id';
@flags.number({ alias: 'p' })
public publish_id: number;
public static settings = {
/**
* Set the following value to true, if you want to load the application
* before running the command. Don't forget to call `node ace generate:manifest`
* afterwards.
*/
loadApp: true,
/**
* Set the following value to true, if you want this command to keep running until
* you manually decide to exit the process. Don't forget to call
* `node ace generate:manifest` afterwards.
*/
stayAlive: false,
};
public async run() {
this.logger.info('Hello world!');
// const { default: Dataset } = await import('App/Models/Dataset');
const datasets = await this.getDatasets();
const proc = readFileSync('public/assets2/solr.sef.json');
const index_name = 'tethys-records';
for (var dataset of datasets) {
// Logger.info(`File publish_id ${dataset.publish_id}`);
const jsonString = await this.getJsonString(dataset, proc);
// console.log(jsonString);
await this.indexDocument(dataset, index_name, jsonString);
}
}
private async getDatasets(): Promise<Dataset[]> {
const query = Dataset.query().preload('xmlCache').where('server_state', 'published');
if (this.publish_id) {
query.where('publish_id', this.publish_id);
}
return await query;
}
private async getJsonString(dataset, proc) {
let xml = create({ version: '1.0', encoding: 'UTF-8', standalone: true }, '<root></root>');
const datasetNode = xml.root().ele('Dataset');
await this.createXmlRecord(dataset, datasetNode);
const xmlString = xml.end({ prettyPrint: false });
try {
const result = await transform({
stylesheetText: proc,
destination: 'serialized',
sourceText: xmlString,
});
return result.principalResult;
} catch (error) {
Logger.error(`An error occurred while creating the user, error: ${error.message},`);
return '';
}
}
private async indexDocument(dataset: Dataset, index_name: string, doc: string): Promise<void> {
try {
let document = JSON.parse(doc);
await client.index({
id: dataset.publish_id?.toString(),
index: index_name,
body: document,
refresh: true,
});
Logger.info(`dataset with publish_id ${dataset.publish_id} successfully indexed`);
} catch (error) {
Logger.error(`An error occurred while uindexing datsaet with publish_id ${dataset.publish_id}.`);
}
}
private async createXmlRecord(dataset: Dataset, datasetNode: XMLBuilder): Promise<void> {
const domNode = await this.getDatasetXmlDomNode(dataset);
if (domNode) {
datasetNode.import(domNode);
}
}
private async getDatasetXmlDomNode(dataset: Dataset): Promise<XMLBuilder | null> {
const xmlModel = new XmlModel(dataset);
// xmlModel.setModel(dataset);
xmlModel.excludeEmptyFields();
xmlModel.caching = true;
// const cache = dataset.xmlCache ? dataset.xmlCache : null;
// dataset.load('xmlCache');
if (dataset.xmlCache) {
xmlModel.xmlCache = dataset.xmlCache;
}
// return cache.getDomDocument();
const domDocument: XMLBuilder | null = await xmlModel.getDomDocument();
return domDocument;
}
}

View File

@ -48,8 +48,7 @@ const databaseConfig: DatabaseConfig = {
}, },
healthCheck: false, healthCheck: false,
debug: false, debug: false,
pool: { min: 1, max: 100 }, pool: { min: 1, max: 100 },
}, },
}, },
}; };

577
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -6,6 +6,7 @@
"type-check": "tsc --noEmit", "type-check": "tsc --noEmit",
"dev": "node ace serve --watch", "dev": "node ace serve --watch",
"compress:xslt": "./node_modules/xslt3/xslt3.js -xsl:public/assets2/datasetxml2oai-pmh.xslt -export:public/assets2/datasetxml2oai.sef.json -t -nogo '-ns:##html5'", "compress:xslt": "./node_modules/xslt3/xslt3.js -xsl:public/assets2/datasetxml2oai-pmh.xslt -export:public/assets2/datasetxml2oai.sef.json -t -nogo '-ns:##html5'",
"compress:solr": "./node_modules/xslt3/xslt3.js -xsl:public/assets2/solr.xslt -export:public/assets2/solr.sef.json -t -nogo '-ns:##html5'",
"build": "node ace build --production", "build": "node ace build --production",
"start": "node server.js", "start": "node server.js",
"lint": "eslint . --ext=.ts", "lint": "eslint . --ext=.ts",
@ -27,8 +28,8 @@
"@babel/plugin-transform-runtime": "^7.19.6", "@babel/plugin-transform-runtime": "^7.19.6",
"@babel/preset-env": "^7.20.2", "@babel/preset-env": "^7.20.2",
"@babel/preset-typescript": "^7.18.6", "@babel/preset-typescript": "^7.18.6",
"@japa/preset-adonis": "^1.0.16", "@japa/preset-adonis": "^1.2.0",
"@japa/runner": "^2.0.8", "@japa/runner": "^2.5.1",
"@mdi/js": "^7.1.96", "@mdi/js": "^7.1.96",
"@symfony/webpack-encore": "^4.2.0", "@symfony/webpack-encore": "^4.2.0",
"@tailwindcss/forms": "^0.5.2", "@tailwindcss/forms": "^0.5.2",
@ -46,7 +47,7 @@
"eslint-config-prettier": "^9.0.0", "eslint-config-prettier": "^9.0.0",
"eslint-plugin-adonis": "^2.1.1", "eslint-plugin-adonis": "^2.1.1",
"eslint-plugin-prettier": "^5.0.0-alpha.2", "eslint-plugin-prettier": "^5.0.0-alpha.2",
"naive-ui": "^2.34.3", "naive-ui": "^2.35.0",
"numeral": "^2.0.6", "numeral": "^2.0.6",
"pinia": "^2.0.30", "pinia": "^2.0.30",
"pino-pretty": "^10.0.0", "pino-pretty": "^10.0.0",
@ -77,6 +78,7 @@
"@fontsource/inter": "^5.0.1", "@fontsource/inter": "^5.0.1",
"@inertiajs/inertia": "^0.11.1", "@inertiajs/inertia": "^0.11.1",
"@inertiajs/vue3": "^1.0.0", "@inertiajs/vue3": "^1.0.0",
"@opensearch-project/opensearch": "^2.4.0",
"bcryptjs": "^2.4.3", "bcryptjs": "^2.4.3",
"clamscan": "^2.1.2", "clamscan": "^2.1.2",
"crypto": "^1.0.1", "crypto": "^1.0.1",

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

553
public/assets2/solr.xslt Normal file
View File

@ -0,0 +1,553 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
/**
* This file is part of TETHYS. The software TETHYS has been originally developed
* at
*
* LICENCE
* TETHYS is free software; you can redistribute it and/or modify it under the
* terms of the GNU General Public License as published by the Free Software
* Foundation; either version 2 of the Licence, or any later version.
* TETHYS is distributed in the hope that it will be useful, but WITHOUT ANY
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
* details. You should have received a copy of the GNU General Public License
* along with TETHYS; if not, write to the Free Software Foundation, Inc., 51
* Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
*/
-->
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:fn="http://example.com/functions"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns:gmd="http://www.isotc211.org/2005/gmd"
xmlns:gco="http://www.isotc211.org/2005/gco"
xmlns:json="http://www.w3.org/2013/XSL/json"
version="3.0">
<xsl:output method="text"/>
<!-- <xsl:mode on-no-match="shallow-copy"/> -->
<!-- Suppress output for all elements that don't have an explicit template. -->
<!-- <xsl:template match="*" /> -->
<!-- <xsl:output method="text"/> -->
<!-- <xsl:mode on-no-match="shallow-copy"/> -->
<!-- Define a custom function to escape double quotes -->
<xsl:function name="fn:escapeQuotes">
<xsl:param name="input"/>
<!-- <xsl:sequence select="translate($input, 'quot;', '`')"/> -->
<xsl:sequence select="replace($input, '&quot;', '''')"/>
</xsl:function>
<xsl:template match="Rdr_Dataset">
<!-- <xsl:variable name="xml">
<xsl:element name="field">
<xsl:attribute name="name">id</xsl:attribute>
<xsl:attribute name="value">
<xsl:value-of select="@Id"/>
</xsl:attribute>
</xsl:element>
</xsl:variable> -->
<!-- OUTPUT -->
<!-- Construct JSON directly -->
<!-- <xsl:text>{"field": {"name": "id", "value": "</xsl:text>
<xsl:value-of select="@Id"/>
<xsl:text>"}}</xsl:text> -->
<!-- Use CDATA to preserve curly braces -->
<!-- <object>
<string key="hello">world!</string>
<number key="answer">42</number>
<number key="lightspeed">3e8</number>
<array key="urls">
<string>http://example.com/</string>
<string>http://example.org/</string>
<string>http://example.net/</string>
</array>
</object> -->
<!-- 1 id -->
<xsl:text>{</xsl:text>
<xsl:text>"id": "</xsl:text>
<xsl:value-of select="@PublishId"/>
<xsl:text>",</xsl:text>
<!-- 2 year -->
<xsl:variable name="year">
<xsl:choose>
<xsl:when test="ServerDatePublished/@Year != ''">
<xsl:value-of select="ServerDatePublished/@Year" />
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="@PublishedYear" />
</xsl:otherwise>
</xsl:choose>
</xsl:variable>
<xsl:text>"year": "</xsl:text>
<xsl:value-of select="$year"/>
<xsl:text>",</xsl:text>
<!--3 year inverted -->
<xsl:if test="$year">
<xsl:variable name="yearInverted" select="65535 - $year"/>
<xsl:text>"year_inverted": "</xsl:text>
<xsl:value-of select="$yearInverted"/>
<xsl:text>",</xsl:text>
</xsl:if>
<!--4 server_date_published -->
<xsl:if test="ServerDatePublished/@UnixTimestamp != ''">
<xsl:text>"server_date_published": "</xsl:text>
<xsl:value-of select="ServerDatePublished/@UnixTimestamp" />
<xsl:text>",</xsl:text>
</xsl:if>
<!--5 server_date_modified -->
<xsl:if test="ServerDateModified/@UnixTimestamp != ''">
<xsl:text>"server_date_modified": "</xsl:text>
<xsl:value-of select="/ServerDateModified/@UnixTimestamp" />
<xsl:text>",</xsl:text>
</xsl:if>
<!--6 language -->
<xsl:variable name="language" select="@Language" />
<xsl:text>"language": "</xsl:text>
<xsl:value-of select="$language"/>
<xsl:text>",</xsl:text>
<!--7 title / title_output -->
<xsl:for-each select="TitleMain">
<xsl:text>"title": "</xsl:text>
<xsl:value-of select="fn:escapeQuotes(@Value)"/>
<xsl:text>",</xsl:text>
<xsl:if test="@Language = $language">
<xsl:text>"title_output": "</xsl:text>
<xsl:value-of select="@Value"/>
<xsl:text>",</xsl:text>
</xsl:if>
</xsl:for-each>
<!--8 abstract / abstract_output -->
<xsl:variable name="abstracts">
<xsl:for-each select="TitleAbstract">
<xsl:text>"</xsl:text>
<!-- <xsl:value-of select="translate(@Value, '&quot;', '&apos;')" /> -->
<xsl:value-of select="fn:escapeQuotes(@Value)"/>
<xsl:text>"</xsl:text>
<xsl:if test="position() != last()">,</xsl:if>
</xsl:for-each>
</xsl:variable>
<xsl:text>"abstract": [</xsl:text>
<xsl:value-of select="$abstracts" />
<xsl:text>],</xsl:text>
<!-- <xsl:for-each select="TitleAbstract">
<xsl:if test="@Language = $language">
<xsl:text>"abstract_output": "</xsl:text>
<xsl:value-of select="translate(@Value, '&quot;', '`')"/>
<xsl:text>",</xsl:text>
</xsl:if>
</xsl:for-each> -->
<!--9 author -->
<xsl:variable name="authors">
<xsl:for-each select="PersonAuthor">
<xsl:sort select="@SortOrder"/>
<xsl:text>"</xsl:text>
<!-- <xsl:value-of select="translate(@Value, '&quot;', '`')"/> -->
<xsl:value-of select="@LastName" />
<xsl:text>, </xsl:text>
<xsl:value-of select="@FirstName" />
<xsl:text>"</xsl:text>
<xsl:if test="position() != last()">,</xsl:if>
</xsl:for-each>
</xsl:variable>
<xsl:text>"author": [</xsl:text>
<xsl:value-of select="$authors" />
<xsl:text>],</xsl:text>
<!-- <xsl:for-each select="PersonAuthor">
<xsl:sort select="@SortOrder"/>
<xsl:element name="field">
<xsl:attribute name="name">author</xsl:attribute>
<xsl:value-of select="@LastName" />
<xsl:text>, </xsl:text>
<xsl:value-of select="@FirstName" />
</xsl:element>
</xsl:for-each> -->
<!-- author_sort -->
<!-- <xsl:element name="field">
<xsl:attribute name="name">author_sort</xsl:attribute>
<xsl:for-each select="/Opus/Rdr_Dataset/PersonAuthor">
<xsl:sort select="@SortOrder"/>
<xsl:value-of select="@LastName" />
<xsl:text></xsl:text>
<xsl:value-of select="@FirstName" />
<xsl:text></xsl:text>
</xsl:for-each>
</xsl:element> -->
<!--16 doctype -->
<xsl:text>"doctype": "</xsl:text>
<xsl:value-of select="@Type" />
<xsl:text>",</xsl:text>
<!--17 +18 uncontrolled subject (swd) -->
<xsl:variable name="subjects">
<xsl:for-each select="Subject[@Type = 'Uncontrolled']">
<xsl:text>"</xsl:text>
<xsl:value-of select="fn:escapeQuotes(@Value)"/>
<xsl:text>"</xsl:text>
<xsl:if test="position() != last()">,</xsl:if>
</xsl:for-each>
</xsl:variable>
<xsl:text>"subjects": [</xsl:text>
<xsl:value-of select="$subjects" />
<xsl:text>],</xsl:text>
<!--19 belongs_to_bibliography -->
<xsl:text>"belongs_to_bibliography": </xsl:text>
<xsl:choose>
<xsl:when test="@BelongsToBibliography = 1">
<xsl:text>true</xsl:text>
</xsl:when>
<xsl:otherwise>
<xsl:text>false</xsl:text>
</xsl:otherwise>
</xsl:choose>
<xsl:text>,</xsl:text>
<!--21 title parent -->
<!--22 title sub -->
<xsl:if test="TitleSub">
<xsl:variable name="title_sub">
<xsl:for-each select="TitleSub">
<xsl:text>"</xsl:text>
<xsl:value-of select="fn:escapeQuotes(@Value)"/>
<xsl:text>"</xsl:text>
<xsl:if test="position() != last()">
<xsl:text>, </xsl:text>
</xsl:if>
</xsl:for-each>
</xsl:variable>
<xsl:text>"title_sub": [</xsl:text>
<xsl:value-of select="$title_sub" />
<xsl:text>],</xsl:text>
</xsl:if>
<!--23 title additional -->
<xsl:variable name="title_additional">
<xsl:for-each select="TitleAdditional">
<xsl:text>"</xsl:text>
<!-- <xsl:value-of select="@Value"/> -->
<xsl:value-of select="fn:escapeQuotes(@Value)"/>
<xsl:text>"</xsl:text>
<xsl:if test="position() != last()">
<xsl:text>, </xsl:text>
</xsl:if>
</xsl:for-each>
</xsl:variable>
<xsl:text>"title_additional": [</xsl:text>
<xsl:value-of select="string($title_additional)" />
<xsl:text>],</xsl:text>
<!--24 abstract additional -->
<xsl:variable name="abstract_additional">
<xsl:for-each select="TitleAbstractAdditional">
<xsl:text>"</xsl:text>
<xsl:value-of select="fn:escapeQuotes(@Value)"/>
<xsl:text>"</xsl:text>
<xsl:if test="position() != last()">
<xsl:text>, </xsl:text>
</xsl:if>
</xsl:for-each>
</xsl:variable>
<xsl:text>"abstract_additional": [</xsl:text>
<xsl:value-of select="$abstract_additional" />
<xsl:text>],</xsl:text>
<!--25 licences -->
<xsl:if test="Licence">
<xsl:text>"licence": "</xsl:text>
<xsl:value-of select="fn:escapeQuotes(Licence/@Name)" />
<xsl:text>",</xsl:text>
</xsl:if>
<!--27 creating corporation (single valued) -->
<xsl:if test="@CreatingCorporation">
<xsl:text>"creating_corporation": "</xsl:text>
<xsl:value-of select="@CreatingCorporation"/>
<xsl:text>",</xsl:text>
</xsl:if>
<!--28 contributing corporation (single valued) -->
<xsl:if test="@ContributingCorporation">
<xsl:text>"contributing_corporation": "</xsl:text>
<xsl:value-of select="@ContributingCorporation"/>
<xsl:text>",</xsl:text>
</xsl:if>
<!--29 publisher name (single valued) -->
<xsl:if test="@PublisherName">
<xsl:text>"publisher_name": "</xsl:text>
<xsl:value-of select="@PublisherName"/>
<xsl:text>",</xsl:text>
</xsl:if>
<!--30 publisher place (single valued) -->
<xsl:if test="@PublisherPlace">
<xsl:text>"publisher_place": "</xsl:text>
<xsl:value-of select="@PublisherPlace"/>
<xsl:text>",</xsl:text>
</xsl:if>
<!--31 publisher place (single valued) -->
<xsl:if test="Coverage">
<!-- In WKT format, use BBOX (minLon, maxLon, maxLat, minLat). -->
<xsl:variable name="geolocation" select="concat(
Coverage/@XMin, ', ', Coverage/@XMax, ', ', Coverage/@YMax, ', ', Coverage/@YMin)
" />
<xsl:text>"geo_location": </xsl:text>
<xsl:text>"BBOX (</xsl:text>
<xsl:value-of select="$geolocation"/>
<xsl:text>)",</xsl:text>
<xsl:text>"bbox_xmin": </xsl:text>
<xsl:value-of select="Coverage/@XMin"/>
<xsl:text>,</xsl:text>
<xsl:text>"bbox_xmax": </xsl:text>
<xsl:value-of select="Coverage/@XMax"/>
<xsl:text>,</xsl:text>
<xsl:text>"bbox_ymin": </xsl:text>
<xsl:value-of select="Coverage/@YMin"/>
<xsl:text>,</xsl:text>
<xsl:text>"bbox_ymax": </xsl:text>
<xsl:value-of select="Coverage/@YMax"/>
<xsl:text>,</xsl:text>
</xsl:if>
<!--32 identifier (multi valued) -->
<xsl:variable name="identifier">
<xsl:for-each select="Identifier">
<xsl:text>"</xsl:text>
<xsl:value-of select="fn:escapeQuotes(@Value)"/>
<xsl:text>"</xsl:text>
<xsl:if test="position() != last()">
<xsl:text>,</xsl:text>
</xsl:if>
</xsl:for-each>
</xsl:variable>
<xsl:text>"identifier": [</xsl:text>
<xsl:value-of select="$identifier" />
<xsl:text>],</xsl:text>
<!--33 reference (multi valued) -->
<xsl:variable name="reference">
<xsl:for-each select="Reference">
<xsl:text>"</xsl:text>
<xsl:value-of select="fn:escapeQuotes(@Value)"/>
<xsl:text>"</xsl:text>
<xsl:if test="position() != last()">
<xsl:text>,</xsl:text>
</xsl:if>
</xsl:for-each>
</xsl:variable>
<xsl:text>"reference": [</xsl:text>
<xsl:value-of select="$reference" />
<xsl:text>]</xsl:text>
<xsl:text>}</xsl:text>
</xsl:template>
<xsl:template match="/root2">
<xsl:element name="add">
<xsl:element name="doc">
<!--10 fulltext -->
<xsl:for-each select="/Opus/Rdr_Dataset/Fulltext_Index">
<xsl:element name="field">
<xsl:attribute name="name">fulltext</xsl:attribute>
<xsl:value-of select="." />
</xsl:element>
</xsl:for-each>
<!--11 has fulltext -->
<xsl:element name="field">
<xsl:attribute name="name">has_fulltext</xsl:attribute>
<xsl:value-of select="/Opus/Rdr_Dataset/Has_Fulltext" />
</xsl:element>
<!--12 IDs der Dateien, die mit nicht leerem Resultat extrahiert werden konnten -->
<xsl:for-each select="/Opus/Rdr_Dataset/Fulltext_ID_Success">
<xsl:element name="field">
<xsl:attribute name="name">fulltext_id_success</xsl:attribute>
<xsl:value-of select="."/>
</xsl:element>
</xsl:for-each>
<!--13 IDs der Dateien, die nicht erfolgreich extrahiert werden konnten -->
<xsl:for-each select="/Opus/Rdr_Dataset/Fulltext_ID_Failure">
<xsl:element name="field">
<xsl:attribute name="name">fulltext_id_failure</xsl:attribute>
<xsl:value-of select="."/>
</xsl:element>
</xsl:for-each>
<!--14 referee -->
<xsl:for-each select="/Opus/Rdr_Dataset/PersonReferee">
<xsl:element name="field">
<xsl:attribute name="name">referee</xsl:attribute>
<xsl:value-of select="@FirstName" />
<xsl:text></xsl:text>
<xsl:value-of select="@LastName" />
</xsl:element>
</xsl:for-each>
<!--15 other persons (non-authors) -->
<xsl:for-each select="/Opus/Rdr_Dataset/*">
<xsl:if test="local-name() != 'Person' and local-name() != 'PersonAuthor' and local-name() != 'PersonSubmitter' and substring(local-name(), 1, 6) = 'Person'">
<xsl:element name="field">
<xsl:attribute name="name">persons</xsl:attribute>
<xsl:value-of select="@FirstName" />
<xsl:text></xsl:text>
<xsl:value-of select="@LastName" />
</xsl:element>
</xsl:if>
</xsl:for-each>
<!--20 collections: project, app_area, institute, ids -->
<xsl:for-each select="/Opus/Rdr_Dataset/Collection">
<xsl:choose>
<xsl:when test="@RoleName = 'projects'">
<xsl:element name="field">
<xsl:attribute name="name">project</xsl:attribute>
<xsl:value-of select="@Number" />
</xsl:element>
<xsl:element name="field">
<xsl:attribute name="name">app_area</xsl:attribute>
<xsl:value-of select="substring(@Number, 0, 2)" />
</xsl:element>
</xsl:when>
<xsl:when test="@RoleName = 'institutes'">
<xsl:element name="field">
<xsl:attribute name="name">institute</xsl:attribute>
<xsl:value-of select="@Name" />
</xsl:element>
</xsl:when>
</xsl:choose>
<xsl:element name="field">
<xsl:attribute name="name">collection_ids</xsl:attribute>
<xsl:value-of select="@Id" />
</xsl:element>
</xsl:for-each>
<!--26 series ids and series number per id (modeled as dynamic field) -->
<xsl:for-each select="/Opus/Rdr_Dataset/Series">
<xsl:element name="field">
<xsl:attribute name="name">series_ids</xsl:attribute>
<xsl:value-of select="@Id"/>
</xsl:element>
<xsl:element name="field">
<xsl:attribute name="name">
<xsl:text>series_number_for_id_</xsl:text>
<xsl:value-of select="@Id"/>
</xsl:attribute>
<xsl:value-of select="@Number"/>
</xsl:element>
<xsl:element name="field">
<xsl:attribute name="name">
<xsl:text>doc_sort_order_for_seriesid_</xsl:text>
<xsl:value-of select="@Id"/>
</xsl:attribute>
<xsl:value-of select="@DocSortOrder"/>
</xsl:element>
</xsl:for-each>
<!--31 publisher place (single valued) -->
<xsl:if test="/Opus/Rdr_Dataset/Coverage">
<xsl:element name="field">
<xsl:attribute name="name">geo_location</xsl:attribute>
<xsl:variable name="geolocation" select="concat(
'SOUTH-BOUND LATITUDE: ', /Opus/Rdr_Dataset/Coverage/@XMin,
' * WEST-BOUND LONGITUDE: ', /Opus/Rdr_Dataset/Coverage/@YMin,
' * NORTH-BOUND LATITUDE: ', /Opus/Rdr_Dataset/Coverage/@XMax,
' * EAST-BOUND LONGITUDE: ', /Opus/Rdr_Dataset/Coverage/@YMax
)" />
<!-- <xsl:variable name="geolocation" select="concat('test', /Opus/Rdr_Dataset/Coverage/@XMin)" /> -->
<xsl:value-of select="$geolocation" />
<xsl:text>&#xA;</xsl:text>
<xsl:if test="@ElevationMin != '' and @ElevationMax != ''">
<xsl:value-of select="concat(' * ELEVATION MIN: ', @ElevationMin, ' * ELEVATION MAX: ', @ElevationMax)" />
</xsl:if>
<xsl:if test="@ElevationAbsolut != ''">
<xsl:value-of select="concat(' * ELEVATION ABSOLUT: ', @ElevationAbsolut)" />
</xsl:if>
<xsl:text>&#xA;</xsl:text>
<xsl:if test="@DepthMin != '' and @DepthMax != ''">
<xsl:value-of select="concat(' * DEPTH MIN: ', @DepthMin, ' * DEPTH MAX: ', @DepthMax)" />
</xsl:if>
<xsl:if test="@DepthAbsolut != ''">
<xsl:value-of select="concat(' * DEPTH ABSOLUT: ', @DepthAbsolut)" />
</xsl:if>
<xsl:text>&#xA;</xsl:text>
<xsl:if test="@TimeMin != '' and @TimeMax != ''">
<xsl:value-of select="concat(' * TIME MIN: ', @TimeMin, ' * TIME MAX: ', @TimeMax)" />
</xsl:if>
<xsl:if test="@TimeAbsolut != ''">
<xsl:value-of select="concat(' * TIME ABSOLUT: ', @TimeAbsolut)" />
</xsl:if>
</xsl:element>
</xsl:if>
</xsl:element>
</xsl:element>
</xsl:template>
</xsl:stylesheet>

146
public/records.json Normal file
View File

@ -0,0 +1,146 @@
{
"settings": {
"analysis": {
"analyzer": {
"pathAnalyzer": {
"tokenizer": "pathTokenizer"
}
},
"tokenizer": {
"pathTokenizer": {
"type": "path_hierarchy",
"delimiter": "/",
"replacement": "/",
"skip": 0,
"reverse": false
}
}
}
},
"mappings": {
"properties": {
"id": {
"type": "keyword"
},
"year": {
"type": "integer"
},
"year_iverted": {
"type": "double"
},
"server_date_published": {
"type": "double"
},
"server_date_modified": {
"type": "double"
},
"language": {
"type": "keyword"
},
"title": {
"type": "text"
},
"title_output": {
"type": "keyword",
"index": false
},
"abstract": {
"type": "text"
},
"abstract_output": {
"type": "keyword",
"index": false
},
"author": {
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"ignore_above": 256
}
}
},
"doctype": {
"type": "keyword"
},
"subjects": {
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"ignore_above": 256
}
}
},
"belongs_to_bibliography": {
"type": "boolean"
},
"title_additional": {
"type": "text",
"index": false,
"fields": {
"keyword": {
"type": "keyword",
"ignore_above": 256
}
}
},
"abstract_additional": {
"type": "text",
"index": false,
"fields": {
"keyword": {
"type": "keyword",
"ignore_above": 256
}
}
},
"licence": {
"type": "text",
"index": false,
"fields": {
"keyword": {
"type": "keyword",
"ignore_above": 256
}
}
},
"creating_corporation": {
"type": "text"
},
"contributing_corporation": {
"type": "text"
},
"publisher_name": {
"type": "text"
},
"publisher_place": {
"type": "text"
},
"geo_location": {
"type": "geo_shape",
"tree": "quadtree",
"precision": "10km"
},
"bbox_xmin": {
"type": "float"
},
"bbox_xmax": {
"type": "float"
},
"bbox_ymin": {
"type": "float"
},
"bbox_ymax": {
"type": "float"
},
"status": {
"type": "keyword"
}
}
}
}

View File

@ -2,7 +2,7 @@
<div class="mb-3"> <div class="mb-3">
<label> <label>
<span v-if="label" class="block font-semibold">{{ label }}</span> <span v-if="label" class="block font-semibold">{{ label }}</span>
<n-input v-bind:type="type" v-model:value="internalValue" v-bind:placeholder="placeholder"/> <!-- <n-input v-bind:type="type" v-model:value="internalValue" v-bind:placeholder="placeholder"/> -->
</label> </label>
<div v-if="Array.isArray(errors)" class="text-xs text-red-500"> <div v-if="Array.isArray(errors)" class="text-xs text-red-500">
{{ errors.join(', ') }} {{ errors.join(', ') }}
@ -12,7 +12,7 @@
<script setup> <script setup>
import { computed } from 'vue'; import { computed } from 'vue';
import { NInput } from 'naive-ui'; // import { NInput } from 'naive-ui';
const props = defineProps({ const props = defineProps({
type: { type: {

View File

@ -2,8 +2,8 @@
<div> <div>
<Link href="/app/login">Login</Link> <Link href="/app/login">Login</Link>
<h1 class="text-red-500">Welcome, {{ testing }}</h1> <h1 class="text-red-500">Welcome, {{ testing }}</h1>
<n-button>Testing</n-button> <!-- <n-button>Testing</n-button>
<n-input v-bind:value="testing"></n-input> <n-input v-bind:value="testing"></n-input> -->
<div class="features"> <div class="features">
<ul> <ul>
@ -57,7 +57,7 @@ import { Component, Vue, Prop } from 'vue-facing-decorator';
import type User from "App/Models/User"; import type User from "App/Models/User";
import { Link } from '@inertiajs/vue3'; import { Link } from '@inertiajs/vue3';
import DefaultLayout from '@/Layouts/Default.vue'; import DefaultLayout from '@/Layouts/Default.vue';
import { NInput, NButton } from 'naive-ui'; // import { NInput, NButton } from 'naive-ui';
@Component({ @Component({
options: { options: {
@ -66,8 +66,8 @@ import { NInput, NButton } from 'naive-ui';
name: 'AppComponent', name: 'AppComponent',
components: { components: {
Link, Link,
NInput, // NInput,
NButton, // NButton,
}, },
}) })
export default class AppComponent extends Vue { export default class AppComponent extends Vue {

View File

@ -10,7 +10,7 @@
<form-input v-bind:label="'Emai22l'" v-bind:type="'email'" v-model="form.email" /> <form-input v-bind:label="'Emai22l'" v-bind:type="'email'" v-model="form.email" />
<form-input v-bind:label="'Password'" v-bind:type="'password'" v-model="form.password" /> <form-input v-bind:label="'Password'" v-bind:type="'password'" v-model="form.password" />
<n-button attr-type="submit"> Register </n-button> <!-- <n-button attr-type="submit"> Register </n-button> -->
</form> </form>
</div> </div>
</template> </template>
@ -21,7 +21,7 @@ import AuthLayout from '@/Layouts/Auth.vue';
import { reactive } from 'vue'; import { reactive } from 'vue';
import { useForm } from '@inertiajs/vue3'; import { useForm } from '@inertiajs/vue3';
import { Inertia } from '@inertiajs/inertia'; import { Inertia } from '@inertiajs/inertia';
import { NButton, NInput } from 'naive-ui'; // import { NButton, NInput } from 'naive-ui';
// import { useForm } from '@inertiajs/inertia-vue3' // import { useForm } from '@inertiajs/inertia-vue3'
import FormInput from '@/Components/FormInput.vue' import FormInput from '@/Components/FormInput.vue'

View File

@ -45,6 +45,7 @@ const handleSubmit = async (e) => {
e.preventDefault(); e.preventDefault();
await form.put(stardust.route('dataset.releaseUpdate', [props.dataset.id])); await form.put(stardust.route('dataset.releaseUpdate', [props.dataset.id]));
// await form.put(stardust.route('editor.dataset.update', [props.dataset.id]));
}; };
</script> </script>

View File

@ -1,7 +1,7 @@
import { Component, Vue, Prop } from 'vue-facing-decorator'; import { Component, Vue, Prop } from 'vue-facing-decorator';
import AuthLayout from '@/Layouts/Auth.vue'; import AuthLayout from '@/Layouts/Auth.vue';
// import { Inertia } from '@inertiajs/inertia'; // import { Inertia } from '@inertiajs/inertia';
import { NButton } from 'naive-ui'; // import { NButton } from 'naive-ui';
import { useForm, InertiaForm, router } from '@inertiajs/vue3'; import { useForm, InertiaForm, router } from '@inertiajs/vue3';
import FormInput from '@/Components/FormInput.vue'; // @/Components/FormInput.vue' import FormInput from '@/Components/FormInput.vue'; // @/Components/FormInput.vue'
// import { defineComponent, reactive } from 'vue'; // import { defineComponent, reactive } from 'vue';
@ -42,7 +42,7 @@ export interface IErrorMessage {
}, },
name: 'RegisterViewComponent', name: 'RegisterViewComponent',
components: { components: {
NButton, // NButton,
FormInput, FormInput,
}, },
}) })

View File

@ -183,3 +183,11 @@ Route.group(() => {
.prefix('submitter'); .prefix('submitter');
// .middleware(['auth', 'can:dataset-list,dataset-publish']); // .middleware(['auth', 'can:dataset-list,dataset-publish']);
// .middleware(['auth', 'is:submitter']); // .middleware(['auth', 'is:submitter']);
Route.group(() => {
Route.put('/dataset/:id/update', 'DatasetsController.update')
.as('editor.dataset.update')
.middleware(['auth', 'can:dataset-submit']);
})
.namespace('App/Controllers/Http/Editor')
.prefix('editor');