- first commit
This commit is contained in:
commit
407717d4b5
29
.babelrc
Normal file
29
.babelrc
Normal file
|
@ -0,0 +1,29 @@
|
|||
{
|
||||
"presets": [
|
||||
|
||||
[
|
||||
"@babel/preset-env",
|
||||
{
|
||||
"targets":{"node":"16"}
|
||||
// "useBuiltIns": "entry",
|
||||
// "targets": "> 0.25%, not dead"
|
||||
}
|
||||
],
|
||||
// "@babel/preset-env",
|
||||
"@babel/preset-typescript"
|
||||
],
|
||||
"plugins": [
|
||||
[
|
||||
"@babel/plugin-transform-typescript", {
|
||||
"allowDeclareFields": true
|
||||
}],
|
||||
["@babel/plugin-proposal-decorators", {
|
||||
"legacy": true
|
||||
}],
|
||||
|
||||
|
||||
"@babel/proposal-class-properties",
|
||||
"@babel/proposal-object-rest-spread"
|
||||
]
|
||||
}
|
||||
|
10
.env.example
Normal file
10
.env.example
Normal file
|
@ -0,0 +1,10 @@
|
|||
DB_SCHEMA=public
|
||||
DB_NAME=tethys_db
|
||||
DB_USER=your_db_user
|
||||
DB_PASSWORD=your_password
|
||||
DB_HOST=localhost
|
||||
DB_PORT=5432
|
||||
DB_DRIVER=postgres
|
||||
|
||||
|
||||
BASE_DOMAIN=https://tethys.at
|
43
.eslintrc.js
Normal file
43
.eslintrc.js
Normal file
|
@ -0,0 +1,43 @@
|
|||
module.exports = {
|
||||
root: true,
|
||||
parser: "@typescript-eslint/parser",
|
||||
plugins: ["@typescript-eslint", "prettier"],
|
||||
extends: [
|
||||
"eslint:recommended",
|
||||
"plugin:@typescript-eslint/recommended",
|
||||
// // 'prettier/@typescript-eslint',
|
||||
// Then you need to add plugin:prettier/recommended as the last extension in your .eslintrc.json:
|
||||
// 'prettier/@typescript-eslint',
|
||||
"plugin:prettier/recommended",
|
||||
],
|
||||
parserOptions: {
|
||||
ecmaVersion: 2020,
|
||||
sourceType: "module",
|
||||
},
|
||||
env: {
|
||||
es6: true,
|
||||
node: true,
|
||||
},
|
||||
|
||||
// "off" means 0 (turns the rule off completely)
|
||||
// "warn" means 1 (turns the rule on but won't make the linter fail)
|
||||
// "error" means 2 (turns the rule on and will make the linter fail)
|
||||
rules: {
|
||||
// "prettier/prettier": ["error", { "singleQuote": true }],
|
||||
"no-console": 0, // Remember, this means error!,
|
||||
"@typescript-eslint/no-unused-vars": "error",
|
||||
"@typescript-eslint/ban-ts-comment": "warn",
|
||||
"no-var": "error",
|
||||
semi: "error",
|
||||
// indent: ['error', 4, { SwitchCase: 1 }],
|
||||
"no-multi-spaces": "error",
|
||||
"space-in-parens": "error",
|
||||
"no-multiple-empty-lines": "error",
|
||||
"prefer-const": "error",
|
||||
"@typescript-eslint/indent": ["error", 4],
|
||||
"prettier/prettier": ["error", { singleQuote: false }],
|
||||
|
||||
// "arrow-body-style": "off",
|
||||
// "prefer-arrow-callback": "off"
|
||||
},
|
||||
};
|
73
.gitignore
vendored
Normal file
73
.gitignore
vendored
Normal file
|
@ -0,0 +1,73 @@
|
|||
# Node modules
|
||||
node_modules
|
||||
package-lock.json
|
||||
jspm_packages/
|
||||
|
||||
|
||||
# Linux
|
||||
*~
|
||||
*.swp
|
||||
|
||||
# Windows
|
||||
Thumbs.db
|
||||
desktop.ini
|
||||
|
||||
/.idea
|
||||
# /.vscode
|
||||
/.vs
|
||||
/.vagrant
|
||||
Homestead.json
|
||||
Homestead.yaml
|
||||
|
||||
|
||||
|
||||
yarn-error.log
|
||||
# Logs
|
||||
logs
|
||||
*.log
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
lerna-debug.log*
|
||||
.pnpm-debug.log*
|
||||
|
||||
# Environment variables, never commit this file
|
||||
.env
|
||||
.env.development.local
|
||||
.env.test.local
|
||||
.env.production.local
|
||||
.env.local
|
||||
|
||||
# VSCode & Webstorm history directories
|
||||
.history
|
||||
.idea
|
||||
|
||||
# Adonis directory for storing tmp files
|
||||
tmp
|
||||
.temp
|
||||
.cache
|
||||
|
||||
# Runtime data
|
||||
pids
|
||||
*.pid
|
||||
*.seed
|
||||
*.pid.lock
|
||||
|
||||
# TypeScript cache
|
||||
*.tsbuildinfo
|
||||
|
||||
# Optional eslint cache
|
||||
.eslintcache
|
||||
|
||||
# Optional stylelint cache
|
||||
.stylelintcache
|
||||
|
||||
# Output of 'npm pack'
|
||||
*.tgz
|
||||
|
||||
# Nuxt.js build / generate output
|
||||
.nuxt
|
||||
dist
|
||||
|
||||
# Stores VSCode versions used for testing VSCode extensions
|
||||
.vscode-test
|
9
.prettierrc
Normal file
9
.prettierrc
Normal file
|
@ -0,0 +1,9 @@
|
|||
{
|
||||
"trailingComma": "all",
|
||||
"tabWidth": 4,
|
||||
"semi": true,
|
||||
"singleQuote": false,
|
||||
"arrowParens": "always",
|
||||
"proseWrap": "preserve",
|
||||
"printWidth": 140
|
||||
}
|
45
.vscode/launch.json
vendored
Normal file
45
.vscode/launch.json
vendored
Normal file
|
@ -0,0 +1,45 @@
|
|||
{
|
||||
// Use IntelliSense to learn about possible attributes.
|
||||
// Hover to view descriptions of existing attributes.
|
||||
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
|
||||
"version": "0.2.0",
|
||||
"configurations": [
|
||||
{
|
||||
"type": "node",
|
||||
"request": "launch",
|
||||
"name": "launch nodemon via bable-node",
|
||||
"runtimeExecutable": "${workspaceFolder}/node_modules/nodemon/bin/nodemon.js",
|
||||
"runtimeArgs": [
|
||||
"--exec", "babel-node",
|
||||
"--nolazy",
|
||||
"--extensions", "\".ts,.tsx,.js\""
|
||||
// "--require",
|
||||
// "babel-register"
|
||||
],
|
||||
"env": {
|
||||
"NODE_ENV": "development"
|
||||
},
|
||||
"program": "${workspaceFolder}/src/server.ts",
|
||||
"restart": true,
|
||||
"console": "integratedTerminal",
|
||||
"internalConsoleOptions": "neverOpen"
|
||||
},
|
||||
// {
|
||||
// "type": "chrome",
|
||||
// "request": "launch",
|
||||
// "name": "Launch Edge against localhost",
|
||||
// "url": "http://localhost:3000",
|
||||
// "webRoot": "${workspaceFolder}"
|
||||
// },
|
||||
{
|
||||
"type": "node",
|
||||
"args": ["--trace-deprecation"],
|
||||
"request": "launch",
|
||||
"name": "Launch Program",
|
||||
"skipFiles": [
|
||||
"<node_internals>/**"
|
||||
],
|
||||
"program": "${workspaceFolder}/server.js"
|
||||
}
|
||||
]
|
||||
}
|
5
.vscode/settings.json
vendored
Normal file
5
.vscode/settings.json
vendored
Normal file
|
@ -0,0 +1,5 @@
|
|||
{
|
||||
"typescript.tsdk": "node_modules/typescript/lib",
|
||||
"prettier.configPath": ".prettierrc",
|
||||
"prettier.bracketSameLine": true
|
||||
}
|
18
.vscode/tasks.json
vendored
Normal file
18
.vscode/tasks.json
vendored
Normal file
|
@ -0,0 +1,18 @@
|
|||
{
|
||||
"version": "2.0.0",
|
||||
"tasks": [
|
||||
{
|
||||
"type": "xslt-js",
|
||||
"label": "xslt-js: Saxon-JS Transform (New)",
|
||||
"xsltFile": "${file}",
|
||||
"xmlSource": "${file}",
|
||||
"resultPath": "${workspaceFolder}/xslt-out/result1.xml",
|
||||
"group": {
|
||||
"kind": "build"
|
||||
},
|
||||
"problemMatcher": [
|
||||
"$saxon-xslt-js"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
22
LICENSE
Normal file
22
LICENSE
Normal file
|
@ -0,0 +1,22 @@
|
|||
MIT License
|
||||
-----------
|
||||
|
||||
Copyright (c) 2022, Geologische Bundesanstalt (GBA)
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
83
client/book-list.html
Normal file
83
client/book-list.html
Normal file
|
@ -0,0 +1,83 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta http-equiv="X-UA-Compatible" content="ie=edge">
|
||||
<title>Document</title>
|
||||
|
||||
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div class="container">
|
||||
<hr>
|
||||
<h1>List of books</h1>
|
||||
<hr>
|
||||
|
||||
<div>
|
||||
<div class="row" id="books">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="editBookModal" class="modal" tabindex="-1" role="dialog">
|
||||
<div class="modal-dialog" role="document">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title">Edit Book</h5>
|
||||
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
|
||||
<span aria-hidden="true">×</span>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="modal-body">
|
||||
<form id="editForm" method="POST">
|
||||
<div class="form-group">
|
||||
<label for="exampleInputPassword1">ISBN</label>
|
||||
<input class="form-control" name="isbn" id="isbn">
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="exampleInputPassword1">Title</label>
|
||||
<input class="form-control" name="title" id="title">
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="exampleInputPassword1">Author</label>
|
||||
<input class="form-control" name="author" id="author">
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="exampleInputPassword1">Published Date</label>
|
||||
<input type="date" class="form-control" name="publish_date" id="publish_date">
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="exampleInputPassword1">Publisher</label>
|
||||
<input class="form-control" name="publisher" id="publisher">
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="exampleInputPassword1">Number Of Pages</label>
|
||||
<input type="number" class="form-control" name="numOfPages" id="numOfPages">
|
||||
</div>
|
||||
|
||||
<button type="submit" class="btn btn-primary">Submit</button>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script src="https://code.jquery.com/jquery-3.3.1.slim.min.js"
|
||||
integrity="sha384-q8i/X+965DzO0rT7abK41JStQIAqVgRVzpbzo5smXKp4YfRvH+8abtTE1Pi6jizo"
|
||||
crossorigin="anonymous"></script>
|
||||
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/js/bootstrap.min.js"></script>
|
||||
|
||||
<script type="text/javascript" src="book-list.js"></script>
|
||||
</body>
|
||||
|
||||
</html>
|
70
client/book-list.js
Normal file
70
client/book-list.js
Normal file
|
@ -0,0 +1,70 @@
|
|||
const setEditModal = (isbn) => {
|
||||
const xhttp = new XMLHttpRequest();
|
||||
|
||||
xhttp.open("GET", `http://localhost:3000/book/${isbn}`, false);
|
||||
xhttp.send();
|
||||
|
||||
const book = JSON.parse(xhttp.responseText);
|
||||
|
||||
const {
|
||||
title,
|
||||
author,
|
||||
publisher,
|
||||
publish_date,
|
||||
numOfPages
|
||||
} = book;
|
||||
|
||||
document.getElementById('isbn').value = isbn;
|
||||
document.getElementById('title').value = title;
|
||||
document.getElementById('author').value = author;
|
||||
document.getElementById('publisher').value = publisher;
|
||||
document.getElementById('publish_date').value = publish_date;
|
||||
document.getElementById('numOfPages').value = numOfPages;
|
||||
|
||||
// setting up the action url for the book
|
||||
document.getElementById('editForm').action = `http://localhost:3000/book/${isbn}`;
|
||||
}
|
||||
|
||||
const deleteBook = (isbn) => {
|
||||
const xhttp = new XMLHttpRequest();
|
||||
|
||||
xhttp.open("DELETE", `http://localhost:3000/book/${isbn}`, false);
|
||||
xhttp.send();
|
||||
|
||||
location.reload();
|
||||
}
|
||||
|
||||
const loadBooks = () => {
|
||||
const xhttp = new XMLHttpRequest();
|
||||
|
||||
xhttp.open("GET", "http://localhost:3000/books", false);
|
||||
xhttp.send();
|
||||
|
||||
const books = JSON.parse(xhttp.responseText);
|
||||
|
||||
for (let book of books) {
|
||||
const x = `
|
||||
<div class="col-4">
|
||||
<div class="card">
|
||||
<div class="card-body">
|
||||
<h5 class="card-title">${book.title}</h5>
|
||||
<h6 class="card-subtitle mb-2 text-muted">${book.isbn}</h6>
|
||||
<div>Author: ${book.author}</div>
|
||||
<div>Publisher: ${book.publisher}</div>
|
||||
<div>Number Of Pages: ${book.numOfPages}</div>
|
||||
<hr>
|
||||
<button type="button" class="btn btn-danger">Delete</button>
|
||||
<button types="button" class="btn btn-primary" data-toggle="modal"
|
||||
data-target="#editBookModal" onClick="setEditModal(${book.isbn})">
|
||||
Edit
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`
|
||||
|
||||
document.getElementById('books').innerHTML += x;
|
||||
}
|
||||
}
|
||||
|
||||
loadBooks();
|
613
datasetxml2oai-pmh.xslt
Executable file
613
datasetxml2oai-pmh.xslt
Executable file
|
@ -0,0 +1,613 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
|
||||
<xsl:stylesheet version="1.0"
|
||||
xmlns="http://www.openarchives.org/OAI/2.0/"
|
||||
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:oai_dc="http://www.openarchives.org/OAI/2.0/oai_dc/">
|
||||
|
||||
<!--<xsl:param name="urnResolverUrl" />-->
|
||||
|
||||
<!-- add include here for each new metadata format -->
|
||||
<xsl:include href="prefixes/oai_datacite.xslt" />
|
||||
|
||||
|
||||
<xsl:output method="xml" indent="yes" encoding="utf-8" />
|
||||
|
||||
<xsl:param name="responseDate" />
|
||||
<xsl:param name="unixTimestamp" />
|
||||
<xsl:param name="email" />
|
||||
<xsl:param name="earliestDatestamp" />
|
||||
<xsl:param name="repositoryName" />
|
||||
<xsl:param name="repIdentifier" />
|
||||
<xsl:param name="doiPrefix" />
|
||||
<xsl:param name="sampleIdentifier" />
|
||||
<xsl:param name="dateDelete" />
|
||||
<xsl:param name="totalIds" />
|
||||
<xsl:param name="res" />
|
||||
<xsl:param name="cursor" />
|
||||
<xsl:param name="oai_verb" />
|
||||
<xsl:param name="oai_metadataPrefix" />
|
||||
<xsl:param name="oai_error_code" />
|
||||
<xsl:param name="oai_error_message" />
|
||||
<xsl:param name="baseURL" />
|
||||
<xsl:param name="setPubType" />
|
||||
<xsl:param name="downloadLink" />
|
||||
<xsl:param name="doiLink" />
|
||||
<xsl:param name="docId" />
|
||||
<xsl:param name="repURL" />
|
||||
<xsl:param name="oai_resumptionToken" />
|
||||
<xsl:param name="oai_identifier" />
|
||||
<xsl:param name="oai_from" />
|
||||
<xsl:param name="oai_until" />
|
||||
<xsl:param name="oai_set" />
|
||||
|
||||
<!--create the head of oai response -->
|
||||
<xsl:template match="/root">
|
||||
<!-- stylesheet for browser -->
|
||||
<xsl:processing-instruction name="xml-stylesheet">
|
||||
<xsl:text>type="text/xsl" href="../prefixes/oai2_style.xslt" title="Defaultstyle"</xsl:text>
|
||||
</xsl:processing-instruction>
|
||||
|
||||
<OAI-PMH xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns="http://www.openarchives.org/OAI/2.0/" xsi:schemaLocation="http://www.openarchives.org/OAI/2.0/ http://www.openarchives.org/OAI/2.0/OAI-PMH.xsd">
|
||||
<responseDate>
|
||||
<xsl:value-of select="$responseDate" />
|
||||
</responseDate>
|
||||
<request>
|
||||
<xsl:if test="$oai_verb != ''">
|
||||
<xsl:attribute name="verb">
|
||||
<xsl:value-of select="$oai_verb" />
|
||||
</xsl:attribute>
|
||||
</xsl:if>
|
||||
<xsl:if test="$oai_metadataPrefix != ''">
|
||||
<xsl:attribute name="metadataPrefix">
|
||||
<xsl:value-of select="$oai_metadataPrefix" />
|
||||
</xsl:attribute>
|
||||
</xsl:if>
|
||||
<xsl:value-of select="$baseURL" />
|
||||
</request>
|
||||
<xsl:if test="$oai_error_code!=''">
|
||||
<error>
|
||||
<xsl:attribute name="code">
|
||||
<xsl:value-of select="$oai_error_code" />
|
||||
</xsl:attribute>
|
||||
<xsl:value-of select="$oai_error_message" />
|
||||
</error>
|
||||
</xsl:if>
|
||||
|
||||
<!--create the rest of oai response depending on oai_verb -->
|
||||
<xsl:choose>
|
||||
<xsl:when test="$oai_verb='GetRecord'">
|
||||
<xsl:apply-templates select="Datasets" mode="GetRecord" />
|
||||
</xsl:when>
|
||||
<xsl:when test="$oai_verb='Identify'">
|
||||
<xsl:apply-templates select="Datasets" mode="Identify" />
|
||||
</xsl:when>
|
||||
<xsl:when test="$oai_verb='ListIdentifiers'">
|
||||
<xsl:apply-templates select="Datasets" mode="ListIdentifiers" />
|
||||
</xsl:when>
|
||||
<xsl:when test="$oai_verb='ListMetadataFormats'">
|
||||
<xsl:apply-templates select="Datasets" mode="ListMetadataFormats" />
|
||||
</xsl:when>
|
||||
<xsl:when test="$oai_verb='ListRecords'">
|
||||
<xsl:apply-templates select="Datasets" mode="ListRecords" />
|
||||
</xsl:when>
|
||||
<xsl:when test="$oai_verb='ListSets'">
|
||||
<xsl:apply-templates select="Datasets" mode="ListSets" />
|
||||
</xsl:when>
|
||||
</xsl:choose>
|
||||
</OAI-PMH>
|
||||
</xsl:template>
|
||||
|
||||
|
||||
<!-- template for Identiy -->
|
||||
<xsl:template match="Datasets" mode="Identify">
|
||||
<Identify>
|
||||
<repositoryName>
|
||||
<xsl:value-of select="$repositoryName" />
|
||||
</repositoryName>
|
||||
<baseURL>
|
||||
<xsl:value-of select="$baseURL" />
|
||||
</baseURL>
|
||||
<protocolVersion>
|
||||
<xsl:text>2.0</xsl:text>
|
||||
</protocolVersion>
|
||||
<adminEmail>
|
||||
<xsl:value-of select="$email" />
|
||||
</adminEmail>
|
||||
<earliestDatestamp>
|
||||
<xsl:value-of select="$earliestDatestamp" />
|
||||
</earliestDatestamp>
|
||||
<deletedRecord>
|
||||
<xsl:text>persistent</xsl:text>
|
||||
</deletedRecord>
|
||||
<granularity>
|
||||
<xsl:text>YYYY-MM-DDThh:mm:ssZ</xsl:text>
|
||||
</granularity>
|
||||
<description>
|
||||
<oai-identifier xmlns="http://www.openarchives.org/OAI/2.0/oai-identifier" xsi:schemaLocation="http://www.openarchives.org/OAI/2.0/oai-identifier http://www.openarchives.org/OAI/2.0/oai-identifier.xsd">
|
||||
<scheme>
|
||||
<xsl:text>oai</xsl:text>
|
||||
</scheme>
|
||||
<repositoryIdentifier>
|
||||
<xsl:value-of select="$repIdentifier" />
|
||||
</repositoryIdentifier>
|
||||
<delimiter>
|
||||
<xsl:text>:</xsl:text>
|
||||
</delimiter>
|
||||
<sampleIdentifier>
|
||||
<xsl:value-of select="$sampleIdentifier" />
|
||||
</sampleIdentifier>
|
||||
</oai-identifier>
|
||||
</description>
|
||||
</Identify>
|
||||
</xsl:template>
|
||||
|
||||
|
||||
<!-- template for ListMetadataFormats -->
|
||||
<xsl:template match="Datasets" mode="ListMetadataFormats">
|
||||
<ListMetadataFormats>
|
||||
<metadataFormat>
|
||||
<metadataPrefix>
|
||||
<xsl:text>oai_dc</xsl:text>
|
||||
</metadataPrefix>
|
||||
<schema>
|
||||
<xsl:text>http://www.openarchives.org/OAI/2.0/oai_dc.xsd</xsl:text>
|
||||
</schema>
|
||||
<metadataNamespace>
|
||||
<xsl:text>http://www.openarchives.org/OAI/2.0/oai_dc/</xsl:text>
|
||||
</metadataNamespace>
|
||||
</metadataFormat>
|
||||
<metadataFormat>
|
||||
<metadataPrefix>
|
||||
<xsl:text>oai_datacite</xsl:text>
|
||||
</metadataPrefix>
|
||||
<schema>
|
||||
<xsl:text>http://schema.datacite.org/meta/kernel-4.3/metadata.xsd</xsl:text>
|
||||
</schema>
|
||||
<metadataNamespace>
|
||||
<xsl:text>http://datacite.org/schema/kernel-4</xsl:text>
|
||||
</metadataNamespace>
|
||||
</metadataFormat>
|
||||
</ListMetadataFormats>
|
||||
</xsl:template>
|
||||
|
||||
<xsl:template match="Datasets" mode="ListSets">
|
||||
<ListSets>
|
||||
<xsl:apply-templates select="Rdr_Sets" />
|
||||
</ListSets>
|
||||
</xsl:template>
|
||||
|
||||
<xsl:template match="Rdr_Sets">
|
||||
<set>
|
||||
<setSpec>
|
||||
<xsl:value-of select="@Type" />
|
||||
</setSpec>
|
||||
<setName>
|
||||
<xsl:value-of select="@TypeName" />
|
||||
</setName>
|
||||
</set>
|
||||
</xsl:template>
|
||||
|
||||
<xsl:template match="Datasets" mode="ListIdentifiers">
|
||||
<xsl:if test="count(Rdr_Dataset) > 0">
|
||||
<ListIdentifiers>
|
||||
<xsl:apply-templates select="Rdr_Dataset" />
|
||||
<xsl:if test="$totalIds > 0">
|
||||
<resumptionToken>
|
||||
<xsl:attribute name="expirationDate">
|
||||
<xsl:value-of select="$dateDelete" />
|
||||
</xsl:attribute>
|
||||
<xsl:attribute name="completeListSize">
|
||||
<xsl:value-of select="$totalIds" />
|
||||
</xsl:attribute>
|
||||
<xsl:attribute name="cursor">
|
||||
<xsl:value-of select="$cursor" />
|
||||
</xsl:attribute>
|
||||
<xsl:value-of select="$res" />
|
||||
</resumptionToken>
|
||||
</xsl:if>
|
||||
</ListIdentifiers>
|
||||
</xsl:if>
|
||||
</xsl:template>
|
||||
|
||||
<xsl:template match="Datasets" mode="ListRecords">
|
||||
<xsl:if test="count(Rdr_Dataset) > 0">
|
||||
<ListRecords>
|
||||
<xsl:apply-templates select="Rdr_Dataset" />
|
||||
<xsl:if test="$totalIds > 0">
|
||||
<resumptionToken>
|
||||
<xsl:attribute name="expirationDate">
|
||||
<xsl:value-of select="$dateDelete" />
|
||||
</xsl:attribute>
|
||||
<xsl:attribute name="completeListSize">
|
||||
<xsl:value-of select="$totalIds" />
|
||||
</xsl:attribute>
|
||||
<xsl:attribute name="cursor">
|
||||
<xsl:value-of select="$cursor" />
|
||||
</xsl:attribute>
|
||||
<xsl:value-of select="$res" />
|
||||
</resumptionToken>
|
||||
</xsl:if>
|
||||
</ListRecords>
|
||||
</xsl:if>
|
||||
</xsl:template>
|
||||
|
||||
<xsl:template match="Datasets" mode="GetRecord">
|
||||
<GetRecord>
|
||||
<xsl:apply-templates select="Rdr_Dataset" />
|
||||
</GetRecord>
|
||||
</xsl:template>
|
||||
|
||||
<xsl:template match="Rdr_Dataset">
|
||||
<xsl:choose>
|
||||
<xsl:when test="$oai_verb='ListIdentifiers'">
|
||||
<xsl:call-template name="Rdr_Dataset_Data" />
|
||||
</xsl:when>
|
||||
<xsl:otherwise>
|
||||
<record>
|
||||
<xsl:call-template name="Rdr_Dataset_Data" />
|
||||
</record>
|
||||
</xsl:otherwise>
|
||||
</xsl:choose>
|
||||
</xsl:template>
|
||||
|
||||
<xsl:template name="Rdr_Dataset_Data">
|
||||
<header>
|
||||
<xsl:if test="@ServerState='deleted'">
|
||||
<xsl:attribute name="status">
|
||||
<xsl:text>deleted</xsl:text>
|
||||
</xsl:attribute>
|
||||
</xsl:if>
|
||||
<identifier>
|
||||
<xsl:text>oai:</xsl:text>
|
||||
<xsl:value-of select="$repIdentifier" />
|
||||
<xsl:text>:</xsl:text>
|
||||
<xsl:value-of select="@PublishId" />
|
||||
</identifier>
|
||||
<datestamp>
|
||||
<xsl:choose>
|
||||
<xsl:when test="ServerDateModified">
|
||||
<xsl:variable name="dateModified" select="concat(
|
||||
ServerDateModified/@Year, '-',
|
||||
format-number(number(ServerDateModified/@Month),'00'), '-',
|
||||
format-number(number(ServerDateModified/@Day),'00'), 'T',
|
||||
format-number(number(ServerDateModified/@Hour),'00'), ':',
|
||||
format-number(number(ServerDateModified/@Minute),'00'), ':',
|
||||
format-number(number(ServerDateModified/@Second),'00'), 'Z'
|
||||
)" />
|
||||
<xsl:value-of select="$dateModified" />
|
||||
</xsl:when>
|
||||
<xsl:otherwise>
|
||||
<xsl:variable name="datePublished" select="concat(
|
||||
ServerDatePublished/@Year, '-',
|
||||
format-number(number(ServerDatePublished/@Month),'00'), '-',
|
||||
format-number(number(ServerDatePublished/@Day),'00'), 'T',
|
||||
format-number(number(ServerDatePublished/@Hour),'00'), ':',
|
||||
format-number(number(ServerDatePublished/@Minute),'00'), ':',
|
||||
format-number(number(ServerDatePublished/@Second),'00'), 'Z'
|
||||
)" />
|
||||
<xsl:value-of select="$datePublished" />
|
||||
</xsl:otherwise>
|
||||
</xsl:choose>
|
||||
</datestamp>
|
||||
<!--<setSpec>
|
||||
<xsl:value-of select="SetSpec/@Value"/>
|
||||
</setSpec>-->
|
||||
<!--loop-->
|
||||
<xsl:apply-templates select="SetSpec" />
|
||||
</header>
|
||||
<xsl:choose>
|
||||
<!-- nicht bei ListIdentifiers und auch nicht bei gelöschten Datensätzen-->
|
||||
<xsl:when test="$oai_verb!='ListIdentifiers' and @ServerState!='deleted'">
|
||||
|
||||
<metadata>
|
||||
<!-- <xsl:value-of select="$oai_metadataPrefix" /> -->
|
||||
<xsl:choose>
|
||||
<xsl:when test="$oai_metadataPrefix='oai_dc'">
|
||||
<xsl:apply-templates select="." mode="oai_dc" />
|
||||
</xsl:when>
|
||||
<xsl:when test="$oai_metadataPrefix='oai_datacite'">
|
||||
<xsl:apply-templates select="." mode="oai_datacite" />
|
||||
</xsl:when>
|
||||
</xsl:choose>
|
||||
</metadata>
|
||||
|
||||
</xsl:when>
|
||||
</xsl:choose>
|
||||
</xsl:template>
|
||||
|
||||
<xsl:template match="SetSpec">
|
||||
<setSpec>
|
||||
<xsl:value-of select="@Value" />
|
||||
</setSpec>
|
||||
</xsl:template>
|
||||
|
||||
<xsl:template match="Rdr_Dataset" mode="oai_dc">
|
||||
<oai_dc:dc xsi:schemaLocation="http://www.openarchives.org/OAI/2.0/oai_dc/ http://www.openarchives.org/OAI/2.0/oai_dc.xsd">
|
||||
<!-- dc:title -->
|
||||
<xsl:apply-templates select="TitleMain" mode="oai_dc" />
|
||||
<!-- dc:title -->
|
||||
<xsl:apply-templates select="TitleAdditional" mode="oai_dc" />
|
||||
<!--<dc:creator>-->
|
||||
<!-- Creator: Autor (falls vorhanden), sonst Herausgeber (falls vorhanden), sonst Urhebende Koerperschaft -->
|
||||
<xsl:choose>
|
||||
<xsl:when test="PersonAuthor">
|
||||
<xsl:apply-templates select="PersonAuthor" mode="oai_dc" />
|
||||
</xsl:when>
|
||||
<xsl:when test="@CreatingCorporation">
|
||||
<dc:creator>
|
||||
<xsl:value-of select="@CreatingCorporation" />
|
||||
</dc:creator>
|
||||
</xsl:when>
|
||||
</xsl:choose>
|
||||
<!-- dc:subject -->
|
||||
<xsl:apply-templates select="Subject" mode="oai_dc" />
|
||||
<!-- dc:description -->
|
||||
<xsl:apply-templates select="TitleAbstract" mode="oai_dc" />
|
||||
<!-- dc:description -->
|
||||
<xsl:apply-templates select="TitleAbstractAdditional" mode="oai_dc" />
|
||||
<!-- dc:publisher -->
|
||||
<dc:publisher>
|
||||
<!-- <xsl:value-of select="@PublisherName" /> -->
|
||||
<xsl:value-of select="@CreatingCorporation" />
|
||||
</dc:publisher>
|
||||
|
||||
<!-- dc:contributor -->
|
||||
<xsl:apply-templates select="PersonContributor" mode="oai_dc" />
|
||||
<!-- dc:date (call-template, weil die 'Funktion' nur einmal aufgerufen werden soll, nicht einmal für jedes Date-->
|
||||
<xsl:call-template name="RdrDate" />
|
||||
<!-- dc:date: embargo date -->
|
||||
<xsl:apply-templates select="EmbargoDate" mode="oai_dc" />
|
||||
<!-- dc:type -->
|
||||
<!-- <xsl:apply-templates select="@Type" mode="oai_dc" /> -->
|
||||
<dc:type>Dataset</dc:type>
|
||||
<!-- dc:format -->
|
||||
<xsl:apply-templates select="File/@MimeType" mode="oai_dc" />
|
||||
<!-- <dc:format> -->
|
||||
<xsl:apply-templates select="File" mode="oai_dc" />
|
||||
<!-- dc:identifier -->
|
||||
<!-- <xsl:if test="Identifier">
|
||||
<xsl:apply-templates select="Identifier" mode="oai_dc" />
|
||||
</xsl:if> -->
|
||||
<dc:identifier>
|
||||
<xsl:value-of select="@landingpage" />
|
||||
</dc:identifier>
|
||||
<!-- dc:language -->
|
||||
<xsl:apply-templates select="@Language" mode="oai_dc" />
|
||||
<!-- dc:relation -->
|
||||
<xsl:if test="Identifier">
|
||||
<xsl:apply-templates select="Identifier" mode="oai_dc" />
|
||||
</xsl:if>
|
||||
<xsl:apply-templates select="Reference" mode="oai_dc" />
|
||||
<!-- dc:coverage -->
|
||||
<xsl:apply-templates select="Coverage" mode="oai_dc" />
|
||||
<!-- dc:rights -->
|
||||
<xsl:apply-templates select="Licence" mode="oai_dc" />
|
||||
<xsl:if test="EmbargoDate and ($unixTimestamp < EmbargoDate/@UnixTimestamp)">
|
||||
<dc:rights>embargo</dc:rights>
|
||||
</xsl:if>
|
||||
</oai_dc:dc>
|
||||
</xsl:template>
|
||||
|
||||
<xsl:template match="Coverage" mode="oai_dc">
|
||||
<dc:coverage>
|
||||
<xsl:variable name="geolocation" select="concat(
|
||||
'SOUTH-BOUND LATITUDE: ', @YMin,
|
||||
' * WEST-BOUND LONGITUDE: ', @XMin,
|
||||
' * NORTH-BOUND LATITUDE: ', @YMax,
|
||||
' * EAST-BOUND LONGITUDE: ', @XMax
|
||||
)" />
|
||||
<xsl:value-of select="$geolocation" />
|
||||
|
||||
<!-- <xsl:text>
</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>
</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>
</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>
|
||||
|
||||
</dc:coverage>
|
||||
</xsl:template>
|
||||
|
||||
<xsl:template match="TitleMain" mode="oai_dc">
|
||||
<dc:title>
|
||||
<xsl:attribute name="xml:lang">
|
||||
<xsl:value-of select="@Language" />
|
||||
</xsl:attribute>
|
||||
<xsl:value-of select="@Value" />
|
||||
</dc:title>
|
||||
</xsl:template>
|
||||
|
||||
<xsl:template match="TitleAdditional" mode="oai_dc">
|
||||
<dc:title>
|
||||
<xsl:attribute name="xml:lang">
|
||||
<xsl:value-of select="@Language" />
|
||||
</xsl:attribute>
|
||||
<xsl:value-of select="@Value" />
|
||||
</dc:title>
|
||||
</xsl:template>
|
||||
|
||||
<xsl:template match="TitleAbstract" mode="oai_dc">
|
||||
<dc:description>
|
||||
<xsl:attribute name="xml:lang">
|
||||
<xsl:value-of select="@Language" />
|
||||
</xsl:attribute>
|
||||
<xsl:value-of select="@Value" />
|
||||
</dc:description>
|
||||
</xsl:template>
|
||||
<xsl:template match="TitleAbstractAdditional" mode="oai_dc">
|
||||
<dc:description>
|
||||
<xsl:attribute name="xml:lang">
|
||||
<xsl:value-of select="@Language" />
|
||||
</xsl:attribute>
|
||||
<xsl:value-of select="@Value" />
|
||||
</dc:description>
|
||||
</xsl:template>
|
||||
|
||||
<xsl:template match="Subject" mode="oai_dc">
|
||||
<dc:subject>
|
||||
<xsl:if test="@Language != ''">
|
||||
<xsl:attribute name="xml:lang">
|
||||
<xsl:value-of select="@Language" />
|
||||
</xsl:attribute>
|
||||
</xsl:if>
|
||||
<xsl:value-of select="@Value" />
|
||||
</dc:subject>
|
||||
</xsl:template>
|
||||
|
||||
<xsl:template match="Reference" mode="oai_dc">
|
||||
<dc:relation>
|
||||
<xsl:value-of select="@Value" />
|
||||
</dc:relation>
|
||||
</xsl:template>
|
||||
|
||||
|
||||
<xsl:template match="PersonAuthor|PersonEditor" mode="oai_dc">
|
||||
<dc:creator>
|
||||
<xsl:value-of select="@LastName" />
|
||||
<xsl:if test="@FirstName != ''">
|
||||
<xsl:text>, </xsl:text>
|
||||
</xsl:if>
|
||||
<xsl:value-of select="@FirstName" />
|
||||
<xsl:if test="@AcademicTitle != ''">
|
||||
<xsl:text> (</xsl:text>
|
||||
<xsl:value-of select="@AcademicTitle" />
|
||||
<xsl:text>)</xsl:text>
|
||||
</xsl:if>
|
||||
</dc:creator>
|
||||
</xsl:template>
|
||||
|
||||
<xsl:template match="PersonContributor" mode="oai_dc">
|
||||
<dc:contributor>
|
||||
<xsl:value-of select="@LastName" />
|
||||
<xsl:if test="@FirstName != ''">
|
||||
<xsl:text>, </xsl:text>
|
||||
</xsl:if>
|
||||
<xsl:value-of select="@FirstName" />
|
||||
<xsl:if test="@AcademicTitle != ''">
|
||||
<xsl:text> (</xsl:text>
|
||||
<xsl:value-of select="@AcademicTitle" />
|
||||
<xsl:text>)</xsl:text>
|
||||
</xsl:if>
|
||||
</dc:contributor>
|
||||
</xsl:template>
|
||||
|
||||
<xsl:template name="RdrDate">
|
||||
<dc:date>
|
||||
<xsl:choose>
|
||||
<xsl:when test="PublishedDate">
|
||||
<xsl:variable name="publishedDate" select="concat(
|
||||
PublishedDate/@Year, '-',
|
||||
format-number(number(PublishedDate/@Month),'00'), '-',
|
||||
format-number(number(PublishedDate/@Day),'00')
|
||||
)" />
|
||||
<xsl:value-of select="$publishedDate" />
|
||||
</xsl:when>
|
||||
<xsl:otherwise>
|
||||
<xsl:variable name="serverDatePublished" select="concat(
|
||||
ServerDatePublished/@Year, '-',
|
||||
format-number(number(ServerDatePublished/@Month),'00'), '-',
|
||||
format-number(number(ServerDatePublished/@Day),'00')
|
||||
)" />
|
||||
<xsl:value-of select="$serverDatePublished" />
|
||||
</xsl:otherwise>
|
||||
</xsl:choose>
|
||||
</dc:date>
|
||||
</xsl:template>
|
||||
|
||||
<xsl:template match="EmbargoDate" mode="oai_dc">
|
||||
<xsl:if test="$unixTimestamp < ./@UnixTimestamp">
|
||||
<dc:date>
|
||||
<xsl:text>info:eu-repo/date/embargoEnd/</xsl:text>
|
||||
<xsl:value-of select="./@Year" />
|
||||
-
|
||||
<xsl:value-of select="format-number(number(./@Month),'00')" />
|
||||
-
|
||||
<xsl:value-of select="format-number(number(./@Day),'00')" />
|
||||
</dc:date>
|
||||
</xsl:if>
|
||||
</xsl:template>
|
||||
|
||||
|
||||
<!-- für ListRecords -->
|
||||
<!-- <xsl:template match="@Type" mode="oai_dc">
|
||||
<dc:type>
|
||||
<xsl:value-of select="." />
|
||||
</dc:type>
|
||||
<dc:type>
|
||||
<xsl:text>data-type:</xsl:text>
|
||||
<xsl:value-of select="." />
|
||||
</dc:type>
|
||||
</xsl:template> -->
|
||||
|
||||
<xsl:template match="File/@MimeType" mode="oai_dc">
|
||||
<dc:format>
|
||||
<xsl:choose>
|
||||
<xsl:when test=". = 'application/x-sqlite3'">
|
||||
<xsl:text>application/geopackage+sqlite3</xsl:text>
|
||||
</xsl:when>
|
||||
<xsl:otherwise>
|
||||
<xsl:value-of select="." />
|
||||
</xsl:otherwise>
|
||||
</xsl:choose>
|
||||
</dc:format>
|
||||
</xsl:template>
|
||||
|
||||
<!-- <xsl:template match="File" mode="oai_dc">
|
||||
<dc:identifier>
|
||||
<xsl:value-of select="@PathName" />
|
||||
<xsl:value-of select="concat($downloadLink, @Id)" />
|
||||
</dc:identifier>
|
||||
</xsl:template> -->
|
||||
|
||||
<xsl:template match="Identifier" mode="oai_dc">
|
||||
<dc:relation>
|
||||
<!-- <xsl:value-of select="concat($doiLink, @Value)" /> -->
|
||||
<xsl:value-of select="concat($doiPrefix, @Value)" />
|
||||
</dc:relation>
|
||||
</xsl:template>
|
||||
|
||||
<xsl:template match="@CreatingCorporation" mode="oai_dc">
|
||||
<dc:language>
|
||||
<xsl:value-of select="." />
|
||||
</dc:language>
|
||||
</xsl:template>
|
||||
|
||||
<xsl:template match="@Language" mode="oai_dc">
|
||||
<dc:language>
|
||||
<xsl:value-of select="." />
|
||||
</dc:language>
|
||||
</xsl:template>
|
||||
|
||||
<xsl:template match="Licence" mode="oai_dc">
|
||||
<dc:rights>
|
||||
<xsl:value-of select="@NameLong" />
|
||||
</dc:rights>
|
||||
<xsl:if test="@Name = 'CC-BY-4.0' or @Name = 'CC-BY-SA-4.0'">
|
||||
<dc:rights>
|
||||
<xsl:text>info:eu-repo/semantics/openAccess</xsl:text>
|
||||
</dc:rights>
|
||||
</xsl:if>
|
||||
</xsl:template>
|
||||
|
||||
</xsl:stylesheet>
|
80
db.config.js
Normal file
80
db.config.js
Normal file
|
@ -0,0 +1,80 @@
|
|||
import Sequelize from "sequelize";
|
||||
import * as dotenv from "dotenv"; // see https://github.com/motdotla/dotenv#how-do-i-use-dotenv-with-import
|
||||
dotenv.config();
|
||||
|
||||
// module.exports = {
|
||||
// HOST: "localhost",
|
||||
// USER: "tethys_admin",
|
||||
// PASSWORD: "tethys_admin007",
|
||||
// DB: "tethys",
|
||||
// dialect: "postgres",
|
||||
// pool: {
|
||||
// max: 5,
|
||||
// min: 0,
|
||||
// acquire: 30000,
|
||||
// idle: 10000
|
||||
// }
|
||||
// };
|
||||
|
||||
// const pg = require('pg');
|
||||
// pg.types.setTypeParser(1114, (str) => new Date((str.split(' ').join('T'))+'Z'));
|
||||
|
||||
|
||||
const dbSchema = process.env.DB_SCHEMA;
|
||||
const dbName = process.env.DB_NAME;
|
||||
const dbUser = process.env.DB_USER;
|
||||
const dbPassword = process.env.DB_PASSWORD;
|
||||
const dbHost = process.env.DB_HOST;
|
||||
// const dbDriver = process.env.DB_DRIVER; // as Dialect
|
||||
|
||||
const sequelizeConnection = new Sequelize(
|
||||
dbName,
|
||||
dbUser,
|
||||
dbPassword,
|
||||
{
|
||||
schema: dbSchema,
|
||||
host: dbHost || "localhost",
|
||||
port: process.env.DB_PORT || 5432,
|
||||
dialect: "postgres",
|
||||
dialectOptions: {
|
||||
ssl: process.env.DB_SSL == "true",
|
||||
},
|
||||
pool: {
|
||||
max: 10,
|
||||
min: 0,
|
||||
acquire: 30000,
|
||||
idle: 10000,
|
||||
},
|
||||
logging: false,
|
||||
dialectOptions: {
|
||||
useUTC: false, //for reading from database
|
||||
dateStrings: true,
|
||||
typeCast: true
|
||||
},
|
||||
timezone: '+02:00' //for writing to database
|
||||
// host: "localhost",
|
||||
// dialect: 'postgres',
|
||||
// logging: false
|
||||
}
|
||||
);
|
||||
|
||||
sequelizeConnection
|
||||
.authenticate()
|
||||
.then(() => {
|
||||
console.log("Connection has been established successfully.");
|
||||
})
|
||||
.catch((err) => {
|
||||
console.error("Unable to connect to the database:", err);
|
||||
});
|
||||
export default sequelizeConnection;
|
||||
// export default sequelizeConnection;
|
||||
|
||||
// // relations
|
||||
// Dataset.hasMany(Title, {
|
||||
// as: "titles",
|
||||
// foreignKey: "document_id"
|
||||
// });
|
||||
// Title.belongsTo(Dataset, {
|
||||
// foreignKey: "document_id",
|
||||
// as: "dataset",
|
||||
// });
|
55
new-book.html
Normal file
55
new-book.html
Normal file
|
@ -0,0 +1,55 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta http-equiv="X-UA-Compatible" content="ie=edge">
|
||||
<title>Create New Book</title>
|
||||
|
||||
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div class="container">
|
||||
<hr>
|
||||
<h1>Create New Book</h1>
|
||||
<hr>
|
||||
|
||||
<form action="http://localhost:3000/book" method="POST">
|
||||
<div class="form-group">
|
||||
<label for="exampleInputPassword1">ISBN</label>
|
||||
<input class="form-control" name="isbn">
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="exampleInputPassword1">Title</label>
|
||||
<input class="form-control" name="title">
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="exampleInputPassword1">Author</label>
|
||||
<input class="form-control" name="author">
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="exampleInputPassword1">Published Date</label>
|
||||
<input type="date" class="form-control" name="publish_date">
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="exampleInputPassword1">Publisher</label>
|
||||
<input class="form-control" name="publisher">
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="exampleInputPassword1">Number Of Pages</label>
|
||||
<input type="number" class="form-control" name="numOfPages">
|
||||
</div>
|
||||
|
||||
<button type="submit" class="btn btn-primary">Submit</button>
|
||||
</form>
|
||||
</div>
|
||||
</body>
|
||||
|
||||
</html>
|
95
notes.txt
Normal file
95
notes.txt
Normal file
|
@ -0,0 +1,95 @@
|
|||
https://stackabuse.com/building-a-rest-api-with-node-and-express/
|
||||
https://github.com/jkasun/simple-rest-sever-express
|
||||
|
||||
touch index.js
|
||||
|
||||
npm install --save express
|
||||
|
||||
node index.js
|
||||
|
||||
npm install --save cors
|
||||
|
||||
|
||||
npm install express sequelize pg pg-hstore --save
|
||||
|
||||
neu typescript: -----wieder deinstalliert
|
||||
npm install reflect-metadata sequelize-typescript --save
|
||||
|
||||
npm install ts-node --save-dev
|
||||
npm install typescript ts-node-dev @types/node --save-dev
|
||||
|
||||
npm install @types/express @types/validator --save-dev
|
||||
|
||||
|
||||
|
||||
============ es6:
|
||||
After creating project install node package run command:
|
||||
|
||||
npm install --save @babel/polyfill
|
||||
|
||||
install node dev dependencies package run command:
|
||||
|
||||
npm install --save-dev @babel/core @babel/cli @babel/preset-env @babel/node nodemon
|
||||
|
||||
npm install --save-dev @babel/preset-typescript
|
||||
npm install --save-dev @babel/plugin-proposal-class-properties @babel/plugin-proposal-object-rest-spread
|
||||
|
||||
|
||||
|
||||
|
||||
# install locally (recommended)
|
||||
npm install dotenv --save
|
||||
|
||||
import * as dotenv from "dotenv"; // see https://github.com/motdotla/dotenv#how-do-i-use-dotenv-with-import
|
||||
dotenv.config();
|
||||
|
||||
|
||||
|
||||
|
||||
npm install --save-dev webpack webpack-cli
|
||||
npm install --save-dev babel-loader css-loader style-loader
|
||||
npm install --save core-js
|
||||
|
||||
|
||||
npm install sequelize-typescript --save
|
||||
npm i --save-dev typescript @types/express @types/node tslib@latest
|
||||
npm install --save-dev @babel/plugin-transform-typescript
|
||||
|
||||
npm install --save core-js regenerator-runtime
|
||||
npm install --save @overnightjs/core
|
||||
npm install --save http-status-codes
|
||||
|
||||
====================== xslt ===========================================================
|
||||
|
||||
https://github.com/Saxonica/SaxonJS-Tutorial-2021/tree/main/exercises/ex03
|
||||
https://www.saxonica.com/saxon-js/documentation2/index.html#!starting/export/compiling-using-XX
|
||||
npm install --save-dev xslt3
|
||||
xslt3 -xsl:datasetxml2oai-pmh.xslt -export:datasetxml2oai.sef.json -t
|
||||
./node_modules/xslt3/xslt3.js -xsl:datasetxml2oai-pmh.xslt -export:src/controllers/datasetxml2oai.sef.json -t
|
||||
|
||||
npm install --save saxon-js
|
||||
=============================================================================================
|
||||
|
||||
|
||||
npm install --save-dev prettier
|
||||
npm install --save-dev eslint eslint-config-prettier eslint-plugin-prettier @typescript-eslint/eslint-plugin @typescript-eslint/parser
|
||||
|
||||
https://khalilstemmler.com/blogs/tooling/prettier/ :
|
||||
eslint-config-prettier: Turns off all ESLint rules that have the potential to interfere with Prettier rules.
|
||||
eslint-plugin-prettier: Turns Prettier rules into ESLint rules.
|
||||
==========================================================================================
|
||||
|
||||
|
||||
|
||||
npm install --save xmlbuilder2
|
||||
|
||||
|
||||
|
||||
|
||||
====================== redis
|
||||
npm install --save redis
|
||||
|
||||
|
||||
|
||||
npm i sprintf-js --save
|
||||
npm i --save-dev @types/sprintf-js
|
70
package.json
Normal file
70
package.json
Normal file
|
@ -0,0 +1,70 @@
|
|||
{
|
||||
"name": "api",
|
||||
"version": "1.0.0",
|
||||
"description": "",
|
||||
"scripts": {
|
||||
"type-check": "tsc --noEmit",
|
||||
"test": "echo \"Error: no test specified\" && exit 1",
|
||||
"babel-node": "npx babel-node --extensions \".ts,.tsx,.js\" -- src/server.ts",
|
||||
"dev": "nodemon ./src/server.ts --exec babel-node --extensions \".ts,.tsx,.js\"",
|
||||
"build:types": "tsc --emitDeclarationOnly",
|
||||
"compress:xslt": "./node_modules/xslt3/xslt3.js -xsl:datasetxml2oai-pmh.xslt -export:dist/controllers/datasetxml2oai.sef.json -t",
|
||||
"build:js": "rm -rf dist && babel src --out-dir dist --extensions \".ts,.tsx,.js\" & npm run compress:xslt",
|
||||
"build": "rm -rf dist && tsc --build",
|
||||
"start": "node dist/server.js",
|
||||
"format": "prettier --config .prettierrc --check src/**/*.ts",
|
||||
"eslint:fix": "eslint ./src/ --fix --ext .js,.ts",
|
||||
"lint": "eslint ./src --ext .js,.ts"
|
||||
},
|
||||
"keywords": [
|
||||
"nodemon",
|
||||
"node",
|
||||
"babel",
|
||||
"babel/preset-typescript"
|
||||
],
|
||||
"author": "",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@overnightjs/core": "^1.7.6",
|
||||
"body-parser": "^1.20.0",
|
||||
"cd": "^0.3.3",
|
||||
"core-js": "^3.25.5",
|
||||
"cors": "^2.8.5",
|
||||
"dayjs": "^1.11.5",
|
||||
"dotenv": "^16.0.2",
|
||||
"express": "^4.18.1",
|
||||
"http-status-codes": "^2.2.0",
|
||||
"jet-logger": "^1.2.6",
|
||||
"pg": "^8.8.0",
|
||||
"pg-hstore": "^2.3.4",
|
||||
"redis": "^4.3.1",
|
||||
"regenerator-runtime": "^0.13.9",
|
||||
"saxon-js": "^2.5.0",
|
||||
"sequelize": "^6.21.6",
|
||||
"sequelize-typescript": "^2.1.3",
|
||||
"sprintf-js": "^1.1.2",
|
||||
"xmlbuilder2": "^3.0.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/cli": "^7.18.10",
|
||||
"@babel/core": "^7.19.1",
|
||||
"@babel/node": "^7.19.1",
|
||||
"@babel/plugin-proposal-class-properties": "^7.18.6",
|
||||
"@babel/plugin-proposal-decorators": "^7.19.3",
|
||||
"@babel/plugin-proposal-object-rest-spread": "^7.18.9",
|
||||
"@babel/preset-env": "^7.19.1",
|
||||
"@babel/preset-typescript": "^7.18.6",
|
||||
"@types/express": "^4.17.14",
|
||||
"@types/node": "^18.7.23",
|
||||
"@types/sprintf-js": "^1.1.2",
|
||||
"@typescript-eslint/eslint-plugin": "^5.40.1",
|
||||
"@typescript-eslint/parser": "^5.40.1",
|
||||
"eslint": "^8.25.0",
|
||||
"eslint-config-prettier": "^8.5.0",
|
||||
"eslint-plugin-prettier": "^4.2.1",
|
||||
"nodemon": "^2.0.20",
|
||||
"prettier": "^2.7.1",
|
||||
"typescript": "^4.8.4",
|
||||
"xslt3": "^2.5.0"
|
||||
}
|
||||
}
|
655
prefixes/oai2_style.xslt
Executable file
655
prefixes/oai2_style.xslt
Executable file
|
@ -0,0 +1,655 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
|
||||
<!--
|
||||
http://ws.pangaea.de/oai/oai2.xsl
|
||||
|
||||
XSL Transform to convert OAI 2.0 responses into XHTML
|
||||
|
||||
By Christopher Gutteridge, University of Southampton
|
||||
|
||||
-->
|
||||
|
||||
<!--
|
||||
|
||||
Copyright (c) 2000-2004 University of Southampton, UK. SO17 1BJ.
|
||||
|
||||
EPrints 2 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 License, or
|
||||
(at your option) any later version.
|
||||
|
||||
EPrints 2 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 EPrints 2; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
-->
|
||||
|
||||
|
||||
<!--
|
||||
|
||||
All the elements really needed for EPrints are done but if
|
||||
you want to use this XSL for other OAI archive you may want
|
||||
to make some minor changes or additions.
|
||||
|
||||
Not Done
|
||||
The 'about' section of 'record'
|
||||
The 'compession' part of 'identify'
|
||||
The optional attributes of 'resumptionToken'
|
||||
The optional 'setDescription' container of 'set'
|
||||
|
||||
All the links just link to oai_dc versions of records.
|
||||
|
||||
-->
|
||||
<xsl:stylesheet
|
||||
version="1.0"
|
||||
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
|
||||
xmlns:oai="http://www.openarchives.org/OAI/2.0/"
|
||||
>
|
||||
|
||||
<xsl:output method="html"/>
|
||||
|
||||
|
||||
|
||||
<xsl:template name="style">
|
||||
html, button, input, select, textarea, .pure-g [class *= "pure-u"] {
|
||||
font-family: 'Open Sans', sans-serif;
|
||||
}
|
||||
td.value {
|
||||
vertical-align: top;
|
||||
padding-left: 1em;
|
||||
padding: 3px;
|
||||
}
|
||||
td.key {
|
||||
background-color: #3abac4;
|
||||
padding: 3px;
|
||||
text-align: right;
|
||||
border: 1px solid #c0c0c0;
|
||||
white-space: nowrap;
|
||||
font-weight: bold;
|
||||
vertical-align: top;
|
||||
}
|
||||
.dcdata td.key {
|
||||
background-color: #ffffe0;
|
||||
}
|
||||
body {
|
||||
margin: 1em 2em 1em 2em;
|
||||
}
|
||||
h1, h2, h3 {
|
||||
font-family: sans-serif;
|
||||
clear: left;
|
||||
}
|
||||
h1 {
|
||||
padding-bottom: 4px;
|
||||
margin-bottom: 0px;
|
||||
}
|
||||
h2 {
|
||||
margin-bottom: 0.5em;
|
||||
}
|
||||
h3 {
|
||||
margin-bottom: 0.3em;
|
||||
font-size: medium;
|
||||
}
|
||||
.link {
|
||||
border: 1px outset #88f;
|
||||
background-color: #c0c0ff;
|
||||
padding: 1px 4px 1px 4px;
|
||||
font-size: 80%;
|
||||
text-decoration: none;
|
||||
font-weight: bold;
|
||||
font-family: sans-serif;
|
||||
color: black;
|
||||
}
|
||||
.link:hover {
|
||||
color: red;
|
||||
}
|
||||
.link:active {
|
||||
color: red;
|
||||
border: 1px inset #88f;
|
||||
background-color: #a0a0df;
|
||||
}
|
||||
.oaiRecord, .oaiRecordTitle {
|
||||
background-color: #eee;
|
||||
border-style: solid;
|
||||
border-color: #d0d0d0;
|
||||
}
|
||||
h2.oaiRecordTitle {
|
||||
background-color: #51565c;
|
||||
color: #fff;
|
||||
font-size: medium;
|
||||
font-weight: bold;
|
||||
padding: 10px;
|
||||
border-width: 0px 0px 0px 0px;
|
||||
margin: 0px;
|
||||
}
|
||||
.oaiRecord {
|
||||
margin-bottom: 3em;
|
||||
border-width: 2px;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.results {
|
||||
margin-bottom: 1.5em;
|
||||
}
|
||||
ul.quicklinks {
|
||||
margin-top: 2px;
|
||||
padding: 4px;
|
||||
text-align: left;
|
||||
border-bottom: 2px solid #ccc;
|
||||
border-top: 2px solid #ccc;
|
||||
clear: left;
|
||||
}
|
||||
ul.quicklinks li {
|
||||
font-size: 80%;
|
||||
display: inline;
|
||||
list-style: none;
|
||||
font-family: sans-serif;
|
||||
}
|
||||
p.intro {
|
||||
font-size: 80%;
|
||||
}
|
||||
<xsl:call-template name='xmlstyle' />
|
||||
</xsl:template>
|
||||
|
||||
<xsl:variable name='identifier' select="substring-before(concat(substring-after(/oai:OAI-PMH/oai:request,'identifier='),'&'),'&')" />
|
||||
|
||||
<xsl:template match="/">
|
||||
<html>
|
||||
<head>
|
||||
<title>TETHYS OAI 2.0 Request Results</title>
|
||||
<style><xsl:call-template name="style"/></style>
|
||||
<!-- Favicon
|
||||
–––––––––––––––––––––––––––––––––––––––––––––––––– -->
|
||||
<link rel="apple-touch-icon" sizes="180x180" href="/images/favicon/apple-touch-icon.png"/>
|
||||
<link rel="icon" type="image/png" sizes="32x32" href="/images/favicon/favicon-32x32.png"/>
|
||||
<link rel="icon" type="image/png" sizes="16x16" href="/images/favicon/favicon-16x16.png"/>
|
||||
<link rel="manifest" href="/images/favicon/site.webmanifest"/>
|
||||
<link rel="mask-icon" href="/images/favicon/safari-pinned-tab.svg" color="#5bbad5"/>
|
||||
<link rel="shortcut icon" href="/images/favicon/favicon.ico"/>
|
||||
</head>
|
||||
<body>
|
||||
<h1>TETHYS OAI 2.0 Request Results</h1>
|
||||
<xsl:call-template name="quicklinks"/>
|
||||
<p class="intro">You are viewing an HTML version of the XML OAI response. To see the underlying XML use your web browsers view source option. More information about this XSLT is at the <a href="#moreinfo">bottom of the page</a>.</p>
|
||||
<xsl:apply-templates select="/oai:OAI-PMH" />
|
||||
<xsl:call-template name="quicklinks"/>
|
||||
<h2><a name="moreinfo">About the XSLT</a></h2>
|
||||
<p>An XSLT file has converted the <a href="http://www.openarchives.org">OAI-PMH 2.0</a> responses into XHTML which looks nice in a browser which supports XSLT such as Mozilla, Firebird and Internet Explorer. The XSLT file was created by <a href="http://www.ecs.soton.ac.uk/people/cjg">Christopher Gutteridge</a> at the University of Southampton as part of the <a href="http://software.eprints.org">GNU EPrints system</a>, and is freely redistributable under the <a href="http://www.gnu.org">GPL</a>.</p><p>If you want to use the XSL file on your own OAI interface you may but due to the way XSLT works you must install the XSL file on the same server as the OAI script, you can't just link to this copy.</p><p>For more information or to download the XSL file please see the <a href="http://software.eprints.org/xslt.php">OAI to XHTML XSLT homepage</a>.</p>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
</xsl:template>
|
||||
|
||||
<xsl:template name="quicklinks">
|
||||
<ul class="quicklinks">
|
||||
<li><a href="?verb=Identify">Identify</a> | </li>
|
||||
<li><a href="?verb=ListRecords&metadataPrefix=oai_dc">ListRecords</a> | </li>
|
||||
<li><a href="?verb=ListSets">ListSets</a> | </li>
|
||||
<li><a href="?verb=ListMetadataFormats">ListMetadataFormats</a> | </li>
|
||||
<li><a href="?verb=ListIdentifiers&metadataPrefix=oai_dc">ListIdentifiers</a></li>
|
||||
</ul>
|
||||
</xsl:template>
|
||||
|
||||
|
||||
<xsl:template match="/oai:OAI-PMH">
|
||||
<table class="values">
|
||||
<tr><td class="key">Datestamp of response</td>
|
||||
<td class="value"><xsl:value-of select="oai:responseDate"/></td></tr>
|
||||
<tr><td class="key">Request URL</td>
|
||||
<td class="value"><xsl:value-of select="oai:request"/></td></tr>
|
||||
</table>
|
||||
<!-- verb: [<xsl:value-of select="oai:request/@verb" />]<br /> -->
|
||||
<xsl:choose>
|
||||
<xsl:when test="oai:error">
|
||||
<h2>OAI Error(s)</h2>
|
||||
<p>The request could not be completed due to the following error or errors.</p>
|
||||
<div class="results">
|
||||
<xsl:apply-templates select="oai:error"/>
|
||||
</div>
|
||||
</xsl:when>
|
||||
<xsl:otherwise>
|
||||
<p>Request was of type <xsl:value-of select="oai:request/@verb"/>.</p>
|
||||
<div class="results">
|
||||
<xsl:apply-templates select="oai:Identify" />
|
||||
<xsl:apply-templates select="oai:GetRecord"/>
|
||||
<xsl:apply-templates select="oai:ListRecords"/>
|
||||
<xsl:apply-templates select="oai:ListSets"/>
|
||||
<xsl:apply-templates select="oai:ListMetadataFormats"/>
|
||||
<xsl:apply-templates select="oai:ListIdentifiers"/>
|
||||
</div>
|
||||
</xsl:otherwise>
|
||||
</xsl:choose>
|
||||
</xsl:template>
|
||||
|
||||
|
||||
<!-- ERROR -->
|
||||
<xsl:template match="/oai:OAI-PMH/oai:error">
|
||||
<table class="values">
|
||||
<tr><td class="key">Error Code</td>
|
||||
<td class="value"><xsl:value-of select="@code"/></td></tr>
|
||||
</table>
|
||||
<p class="error"><xsl:value-of select="." /></p>
|
||||
</xsl:template>
|
||||
|
||||
<!-- IDENTIFY -->
|
||||
<xsl:template match="/oai:OAI-PMH/oai:Identify">
|
||||
<table class="values">
|
||||
<tr><td class="key">Repository Name</td>
|
||||
<td class="value"><xsl:value-of select="oai:repositoryName"/></td></tr>
|
||||
<tr><td class="key">Base URL</td>
|
||||
<td class="value"><xsl:value-of select="oai:baseURL"/></td></tr>
|
||||
<tr><td class="key">Protocol Version</td>
|
||||
<td class="value"><xsl:value-of select="oai:protocolVersion"/></td></tr>
|
||||
<tr><td class="key">Earliest Datestamp</td>
|
||||
<td class="value"><xsl:value-of select="oai:earliestDatestamp"/></td></tr>
|
||||
<tr><td class="key">Deleted Record Policy</td>
|
||||
<td class="value"><xsl:value-of select="oai:deletedRecord"/></td></tr>
|
||||
<tr><td class="key">Granularity</td>
|
||||
<td class="value"><xsl:value-of select="oai:granularity"/></td></tr>
|
||||
<xsl:apply-templates select="oai:adminEmail"/>
|
||||
</table>
|
||||
<xsl:apply-templates select="oai:description"/>
|
||||
<!--no warning about unsupported descriptions -->
|
||||
</xsl:template>
|
||||
|
||||
<xsl:template match="/oai:OAI-PMH/oai:Identify/oai:adminEmail">
|
||||
<tr><td class="key">Admin Email</td>
|
||||
<td class="value"><xsl:value-of select="."/></td></tr>
|
||||
</xsl:template>
|
||||
|
||||
<!--
|
||||
Identify / Unsupported Description
|
||||
-->
|
||||
<xsl:template match="oai:description/*" priority="-100">
|
||||
<h2>Unsupported Description Type</h2>
|
||||
<p>The XSL currently does not support this type of description.</p>
|
||||
<div class="xmlSource">
|
||||
<xsl:apply-templates select="." mode='xmlMarkup' />
|
||||
</div>
|
||||
</xsl:template>
|
||||
|
||||
|
||||
<!--
|
||||
Identify / OAI-Identifier
|
||||
-->
|
||||
<xsl:template match="id:oai-identifier" xmlns:id="http://www.openarchives.org/OAI/2.0/oai-identifier">
|
||||
<h2>OAI-Identifier</h2>
|
||||
<table class="values">
|
||||
<tr><td class="key">Scheme</td>
|
||||
<td class="value"><xsl:value-of select="id:scheme"/></td></tr>
|
||||
<tr><td class="key">Repository Identifier</td>
|
||||
<td class="value"><xsl:value-of select="id:repositoryIdentifier"/></td></tr>
|
||||
<tr><td class="key">Delimiter</td>
|
||||
<td class="value"><xsl:value-of select="id:delimiter"/></td></tr>
|
||||
<tr><td class="key">Sample OAI Identifier</td>
|
||||
<td class="value"><xsl:value-of select="id:sampleIdentifier"/></td></tr>
|
||||
</table>
|
||||
</xsl:template>
|
||||
|
||||
|
||||
<!--
|
||||
Identify / EPrints
|
||||
-->
|
||||
<xsl:template match="ep:eprints" xmlns:ep="http://www.openarchives.org/OAI/1.1/eprints">
|
||||
<h2>EPrints Description</h2>
|
||||
<h3>Content</h3>
|
||||
<xsl:apply-templates select="ep:content"/>
|
||||
<xsl:if test="ep:submissionPolicy">
|
||||
<h3>Submission Policy</h3>
|
||||
<xsl:apply-templates select="ep:submissionPolicy"/>
|
||||
</xsl:if>
|
||||
<h3>Metadata Policy</h3>
|
||||
<xsl:apply-templates select="ep:metadataPolicy"/>
|
||||
<h3>Data Policy</h3>
|
||||
<xsl:apply-templates select="ep:dataPolicy"/>
|
||||
<xsl:if test="ep:content">
|
||||
<h3>Content</h3>
|
||||
<xsl:apply-templates select="ep:content"/>
|
||||
</xsl:if>
|
||||
<xsl:apply-templates select="ep:comment"/>
|
||||
</xsl:template>
|
||||
|
||||
<xsl:template match="ep:content|ep:dataPolicy|ep:metadataPolicy|ep:submissionPolicy" xmlns:ep="http://www.openarchives.org/OAI/1.1/eprints">
|
||||
<xsl:if test="ep:text">
|
||||
<p><xsl:value-of select="ep:text" /></p>
|
||||
</xsl:if>
|
||||
<xsl:if test="ep:URL">
|
||||
<div><a href="{ep:URL}"><xsl:value-of select="ep:URL" /></a></div>
|
||||
</xsl:if>
|
||||
</xsl:template>
|
||||
|
||||
<xsl:template match="ep:comment" xmlns:ep="http://www.openarchives.org/OAI/1.1/eprints">
|
||||
<h3>Comment</h3>
|
||||
<div><xsl:value-of select="."/></div>
|
||||
</xsl:template>
|
||||
|
||||
|
||||
<!--
|
||||
Identify / Friends
|
||||
-->
|
||||
<xsl:template match="fr:friends" xmlns:fr="http://www.openarchives.org/OAI/2.0/friends/">
|
||||
<h2>Friends</h2>
|
||||
<ul>
|
||||
<xsl:apply-templates select="fr:baseURL"/>
|
||||
</ul>
|
||||
</xsl:template>
|
||||
|
||||
<xsl:template match="fr:baseURL" xmlns:fr="http://www.openarchives.org/OAI/2.0/friends/">
|
||||
<li><xsl:value-of select="."/>
|
||||
<xsl:text> </xsl:text>
|
||||
<a class="link" href="{.}?verb=Identify">Identify</a></li>
|
||||
</xsl:template>
|
||||
|
||||
|
||||
<!--
|
||||
Identify / Branding
|
||||
-->
|
||||
<xsl:template match="br:branding" xmlns:br="http://www.openarchives.org/OAI/2.0/branding/">
|
||||
<h2>Branding</h2>
|
||||
<xsl:apply-templates select="br:collectionIcon"/>
|
||||
<xsl:apply-templates select="br:metadataRendering"/>
|
||||
</xsl:template>
|
||||
|
||||
<xsl:template match="br:collectionIcon" xmlns:br="http://www.openarchives.org/OAI/2.0/branding/">
|
||||
<h3>Icon</h3>
|
||||
<xsl:choose>
|
||||
<xsl:when test="link!=''">
|
||||
<a href="{br:link}"><img src="{br:url}" alt="{br:title}" width="{br:width}" height="{br:height}" border="0" /></a>
|
||||
</xsl:when>
|
||||
<xsl:otherwise>
|
||||
<img src="{br:url}" alt="{br:title}" width="{br:width}" height="{br:height}" border="0" />
|
||||
</xsl:otherwise>
|
||||
</xsl:choose>
|
||||
</xsl:template>
|
||||
|
||||
<xsl:template match="br:metadataRendering" xmlns:br="http://www.openarchives.org/OAI/2.0/branding/">
|
||||
<h3>Metadata Rendering Rule</h3>
|
||||
<table class="values">
|
||||
<tr><td class="key">URL</td>
|
||||
<td class="value"><xsl:value-of select="."/></td></tr>
|
||||
<tr><td class="key">Namespace</td>
|
||||
<td class="value"><xsl:value-of select="@metadataNamespace"/></td></tr>
|
||||
<tr><td class="key">Mime Type</td>
|
||||
<td class="value"><xsl:value-of select="@mimetype"/></td></tr>
|
||||
</table>
|
||||
</xsl:template>
|
||||
|
||||
|
||||
|
||||
<!--
|
||||
Identify / Gateway
|
||||
-->
|
||||
<xsl:template match="gw:gateway" xmlns:gw="http://www.openarchives.org/OAI/2.0/gateway/x">
|
||||
<h2>Gateway Information</h2>
|
||||
<table class="values">
|
||||
<tr><td class="key">Source</td>
|
||||
<td class="value"><xsl:value-of select="gw:source"/></td></tr>
|
||||
<tr><td class="key">Description</td>
|
||||
<td class="value"><xsl:value-of select="gw:gatewayDescription"/></td></tr>
|
||||
<xsl:apply-templates select="gw:gatewayAdmin"/>
|
||||
<xsl:if test="gw:gatewayURL">
|
||||
<tr><td class="key">URL</td>
|
||||
<td class="value"><xsl:value-of select="gw:gatewayURL"/></td></tr>
|
||||
</xsl:if>
|
||||
<xsl:if test="gw:gatewayNotes">
|
||||
<tr><td class="key">Notes</td>
|
||||
<td class="value"><xsl:value-of select="gw:gatewayNotes"/></td></tr>
|
||||
</xsl:if>
|
||||
</table>
|
||||
</xsl:template>
|
||||
|
||||
<xsl:template match="gw:gatewayAdmin" xmlns:gw="http://www.openarchives.org/OAI/2.0/gateway/">
|
||||
<tr><td class="key">Admin</td>
|
||||
<td class="value"><xsl:value-of select="."/></td></tr>
|
||||
</xsl:template>
|
||||
|
||||
|
||||
<!-- GetRecord -->
|
||||
<xsl:template match="oai:GetRecord">
|
||||
<xsl:apply-templates select="oai:record" />
|
||||
</xsl:template>
|
||||
|
||||
<!-- ListRecords -->
|
||||
<xsl:template match="oai:ListRecords">
|
||||
<xsl:apply-templates select="oai:record" />
|
||||
<xsl:apply-templates select="oai:resumptionToken" />
|
||||
</xsl:template>
|
||||
|
||||
<!-- ListIdentifiers -->
|
||||
<xsl:template match="oai:ListIdentifiers">
|
||||
<xsl:apply-templates select="oai:header" />
|
||||
<xsl:apply-templates select="oai:resumptionToken" />
|
||||
</xsl:template>
|
||||
|
||||
<!-- ListSets -->
|
||||
<xsl:template match="oai:ListSets">
|
||||
<xsl:apply-templates select="oai:set" />
|
||||
<xsl:apply-templates select="oai:resumptionToken" />
|
||||
</xsl:template>
|
||||
|
||||
<xsl:template match="oai:set">
|
||||
<h2>Set</h2>
|
||||
<table class="values">
|
||||
<tr><td class="key">setName</td>
|
||||
<td class="value"><xsl:value-of select="oai:setName"/></td></tr>
|
||||
<xsl:apply-templates select="oai:setSpec" />
|
||||
</table>
|
||||
</xsl:template>
|
||||
|
||||
<!-- ListMetadataFormats -->
|
||||
<xsl:template match="oai:ListMetadataFormats">
|
||||
<xsl:choose>
|
||||
<xsl:when test="$identifier">
|
||||
<p>This is a list of metadata formats available for the record "<xsl:value-of select='$identifier' />". Use these links to view the metadata: <xsl:apply-templates select="oai:metadataFormat/oai:metadataPrefix" /></p>
|
||||
</xsl:when>
|
||||
<xsl:otherwise>
|
||||
<p>This is a list of metadata formats available from this archive.</p>
|
||||
</xsl:otherwise>
|
||||
</xsl:choose>
|
||||
<xsl:apply-templates select="oai:metadataFormat" />
|
||||
</xsl:template>
|
||||
|
||||
<xsl:template match="oai:metadataFormat">
|
||||
<h2>Metadata Format</h2>
|
||||
<table class="values">
|
||||
<tr>
|
||||
<td class="key">metadataPrefix</td>
|
||||
<!-- <td class="value"><xsl:value-of select="oai:metadataPrefix"/></td> -->
|
||||
<td class="value">
|
||||
<a class="link" href="?verb=ListRecords&metadataPrefix={oai:metadataPrefix}"><xsl:value-of select="oai:metadataPrefix"/></a>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="key">metadataNamespace</td>
|
||||
<td class="value"><xsl:value-of select="oai:metadataNamespace"/></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="key">schema</td>
|
||||
<td class="value"><a href="{oai:schema}"><xsl:value-of select="oai:schema"/></a></td>
|
||||
</tr>
|
||||
</table>
|
||||
</xsl:template>
|
||||
|
||||
<xsl:template match="oai:metadataPrefix">
|
||||
<xsl:text> </xsl:text><a class="link" href="?verb=GetRecord&metadataPrefix={.}&identifier={$identifier}"><xsl:value-of select='.' /></a>
|
||||
</xsl:template>
|
||||
|
||||
<!-- record object -->
|
||||
<xsl:template match="oai:record">
|
||||
<h2 class="oaiRecordTitle">OAI Record: <xsl:value-of select="oai:header/oai:identifier"/></h2>
|
||||
<div class="oaiRecord">
|
||||
<xsl:apply-templates select="oai:header" />
|
||||
<xsl:apply-templates select="oai:metadata" />
|
||||
<xsl:apply-templates select="oai:about" />
|
||||
</div>
|
||||
</xsl:template>
|
||||
|
||||
<xsl:template match="oai:header">
|
||||
<h3>OAI Record Header</h3>
|
||||
<table class="values">
|
||||
<tr><td class="key">OAI Identifier</td>
|
||||
<td class="value">
|
||||
<xsl:value-of select="oai:identifier"/>
|
||||
<xsl:text> </xsl:text><a class="link" href="?verb=GetRecord&metadataPrefix=oai_dc&identifier={oai:identifier}">oai_dc</a>
|
||||
<xsl:text> </xsl:text><a class="link" href="?verb=ListMetadataFormats&identifier={oai:identifier}">formats</a>
|
||||
</td></tr>
|
||||
<tr><td class="key">Datestamp</td>
|
||||
<td class="value"><xsl:value-of select="oai:datestamp"/></td></tr>
|
||||
<xsl:apply-templates select="oai:setSpec" />
|
||||
</table>
|
||||
<xsl:if test="@status='deleted'">
|
||||
<p>This record has been deleted.</p>
|
||||
</xsl:if>
|
||||
</xsl:template>
|
||||
|
||||
<xsl:template match="oai:about">
|
||||
<p>"about" part of record container not supported by the XSL</p>
|
||||
</xsl:template>
|
||||
|
||||
<xsl:template match="oai:metadata">
|
||||
 
|
||||
<div class="metadata">
|
||||
<xsl:apply-templates select="*" />
|
||||
</div>
|
||||
</xsl:template>
|
||||
|
||||
<!-- oai setSpec object -->
|
||||
<xsl:template match="oai:setSpec">
|
||||
<tr><td class="key">setSpec</td>
|
||||
<td class="value"><xsl:value-of select="."/>
|
||||
<xsl:text> </xsl:text><a class="link" href="?verb=ListIdentifiers&metadataPrefix=oai_dc&set={.}">Identifiers</a>
|
||||
<xsl:text> </xsl:text><a class="link" href="?verb=ListRecords&metadataPrefix=oai_dc&set={.}">Records</a>
|
||||
</td></tr>
|
||||
</xsl:template>
|
||||
|
||||
<!-- oai resumptionToken -->
|
||||
<xsl:template match="oai:resumptionToken">
|
||||
<p>There are more results.</p>
|
||||
<table class="values">
|
||||
<tr><td class="key">resumptionToken:</td>
|
||||
<td class="value"><xsl:value-of select="."/>
|
||||
<xsl:text> </xsl:text>
|
||||
<a class="link" href="?verb={/oai:OAI-PMH/oai:request/@verb}&resumptionToken={.}">Resume</a></td></tr>
|
||||
</table>
|
||||
</xsl:template>
|
||||
|
||||
<!-- unknown metadata format -->
|
||||
<xsl:template match="oai:metadata/*" priority='-100'>
|
||||
<h3>Unknown Metadata Format</h3>
|
||||
<div class="xmlSource">
|
||||
<xsl:apply-templates select="." mode='xmlMarkup' />
|
||||
</div>
|
||||
</xsl:template>
|
||||
|
||||
<!-- oai_dc record -->
|
||||
<xsl:template match="oai_dc:dc" xmlns:oai_dc="http://www.openarchives.org/OAI/2.0/oai_dc/" >
|
||||
<div class="dcdata">
|
||||
<h3>Dublin Core Metadata (oai_dc)</h3>
|
||||
<table class="dcdata">
|
||||
<xsl:apply-templates select="*" />
|
||||
</table>
|
||||
</div>
|
||||
</xsl:template>
|
||||
|
||||
<xsl:template match="dc:title" xmlns:dc="http://purl.org/dc/elements/1.1/">
|
||||
<tr><td class="key">Title</td><td class="value"><xsl:value-of select="."/></td></tr></xsl:template>
|
||||
|
||||
<xsl:template match="dc:creator" xmlns:dc="http://purl.org/dc/elements/1.1/">
|
||||
<tr><td class="key">Author or Creator</td><td class="value"><xsl:value-of select="."/></td></tr></xsl:template>
|
||||
|
||||
<xsl:template match="dc:subject" xmlns:dc="http://purl.org/dc/elements/1.1/">
|
||||
<tr><td class="key">Subject and Keywords</td><td class="value"><xsl:value-of select="."/></td></tr></xsl:template>
|
||||
|
||||
<xsl:template match="dc:description" xmlns:dc="http://purl.org/dc/elements/1.1/">
|
||||
<tr><td class="key">Description</td><td class="value"><xsl:value-of select="."/></td></tr></xsl:template>
|
||||
|
||||
<xsl:template match="dc:publisher" xmlns:dc="http://purl.org/dc/elements/1.1/">
|
||||
<tr><td class="key">Publisher</td><td class="value"><xsl:value-of select="."/></td></tr></xsl:template>
|
||||
|
||||
<xsl:template match="dc:contributor" xmlns:dc="http://purl.org/dc/elements/1.1/">
|
||||
<tr><td class="key">Other Contributor</td><td class="value"><xsl:value-of select="."/></td></tr></xsl:template>
|
||||
|
||||
<xsl:template match="dc:date" xmlns:dc="http://purl.org/dc/elements/1.1/">
|
||||
<tr><td class="key">Date</td><td class="value"><xsl:value-of select="."/></td></tr></xsl:template>
|
||||
|
||||
<xsl:template match="dc:type" xmlns:dc="http://purl.org/dc/elements/1.1/">
|
||||
<tr><td class="key">Resource Type</td><td class="value"><xsl:value-of select="."/></td></tr></xsl:template>
|
||||
|
||||
<xsl:template match="dc:format" xmlns:dc="http://purl.org/dc/elements/1.1/">
|
||||
<tr><td class="key">Format</td><td class="value"><xsl:value-of select="."/></td></tr></xsl:template>
|
||||
|
||||
<xsl:template match="dc:identifier" xmlns:dc="http://purl.org/dc/elements/1.1/">
|
||||
<tr><td class="key">Resource Identifier</td><td class="value"><xsl:value-of select="."/></td></tr></xsl:template>
|
||||
|
||||
<xsl:template match="dc:source" xmlns:dc="http://purl.org/dc/elements/1.1/">
|
||||
<tr><td class="key">Source</td><td class="value"><xsl:value-of select="."/></td></tr></xsl:template>
|
||||
|
||||
<xsl:template match="dc:language" xmlns:dc="http://purl.org/dc/elements/1.1/">
|
||||
<tr><td class="key">Language</td><td class="value"><xsl:value-of select="."/></td></tr></xsl:template>
|
||||
|
||||
<xsl:template match="dc:relation" xmlns:dc="http://purl.org/dc/elements/1.1/">
|
||||
<tr><td class="key">Relation</td><td class="value">
|
||||
<xsl:choose>
|
||||
<xsl:when test='starts-with(.,"http" )'>
|
||||
<xsl:choose>
|
||||
<xsl:when test='string-length(.) > 50'>
|
||||
<a class="link" href="{.}">URL</a>
|
||||
<i> URL not shown as it is very long.</i>
|
||||
</xsl:when>
|
||||
<xsl:otherwise>
|
||||
<a href="{.}"><xsl:value-of select="."/></a>
|
||||
</xsl:otherwise>
|
||||
</xsl:choose>
|
||||
</xsl:when>
|
||||
<xsl:otherwise>
|
||||
<xsl:value-of select="."/>
|
||||
</xsl:otherwise>
|
||||
</xsl:choose>
|
||||
</td></tr></xsl:template>
|
||||
|
||||
<xsl:template match="dc:coverage" xmlns:dc="http://purl.org/dc/elements/1.1/">
|
||||
<tr><td class="key">Coverage</td><td class="value"><xsl:value-of select="."/></td></tr></xsl:template>
|
||||
|
||||
<xsl:template match="dc:rights" xmlns:dc="http://purl.org/dc/elements/1.1/">
|
||||
<tr><td class="key">Rights Management</td><td class="value"><xsl:value-of select="."/></td></tr></xsl:template>
|
||||
|
||||
<!-- XML Pretty Maker -->
|
||||
<xsl:template match="node()" mode='xmlMarkup'>
|
||||
<div class="xmlBlock">
|
||||
<<span class="xmlTagName"><xsl:value-of select='name(.)' /></span><xsl:apply-templates select="@*" mode='xmlMarkup'/>><xsl:apply-templates select="node()" mode='xmlMarkup' /></<span class="xmlTagName"><xsl:value-of select='name(.)' /></span>>
|
||||
</div>
|
||||
</xsl:template>
|
||||
|
||||
<xsl:template match="text()" mode='xmlMarkup'><span class="xmlText"><xsl:value-of select='.' /></span></xsl:template>
|
||||
|
||||
<xsl:template match="@*" mode='xmlMarkup'>
|
||||
<xsl:text> </xsl:text><span class="xmlAttrName"><xsl:value-of select='name()' /></span>="<span class="xmlAttrValue"><xsl:value-of select='.' /></span>"
|
||||
</xsl:template>
|
||||
|
||||
<xsl:template name="xmlstyle">
|
||||
.xmlSource {
|
||||
font-size: 70%;
|
||||
border: solid #c0c0a0 1px;
|
||||
background-color: #ffffe0;
|
||||
padding: 2em 2em 2em 0em;
|
||||
}
|
||||
.xmlBlock {
|
||||
padding-left: 2em;
|
||||
}
|
||||
.xmlTagName {
|
||||
color: #800000;
|
||||
font-weight: bold;
|
||||
}
|
||||
.xmlAttrName {
|
||||
font-weight: bold;
|
||||
}
|
||||
.xmlAttrValue {
|
||||
color: #0000c0;
|
||||
}
|
||||
</xsl:template>
|
||||
|
||||
</xsl:stylesheet>
|
426
prefixes/oai_datacite.xslt
Executable file
426
prefixes/oai_datacite.xslt
Executable file
|
@ -0,0 +1,426 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
/**
|
||||
* This file is part of TETHYS.
|
||||
*
|
||||
* 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.
|
||||
* OPUS 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 OPUS; if not, write to the Free Software Foundation, Inc., 51
|
||||
* Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* @category Application
|
||||
* @package Module_Oai
|
||||
* @author Arno Kaimbacher <arno.kaimbacher@geologie.ac.at>
|
||||
* @copyright Copyright (c) 2018-2019, GBA TETHYS development team
|
||||
* @license http://www.gnu.org/licenses/gpl.html General Public License
|
||||
*/
|
||||
-->
|
||||
|
||||
<!--
|
||||
/**
|
||||
* Transforms the xml representation of an TETHYS model dataset to datacite
|
||||
* xml as required by the OAI-PMH protocol.
|
||||
*/
|
||||
-->
|
||||
<xsl:stylesheet version="1.0"
|
||||
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
|
||||
xmlns:oai_dc="http://www.openarchives.org/OAI/2.0/oai_dc/"
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
|
||||
|
||||
<xsl:output method="xml" indent="yes" />
|
||||
|
||||
<xsl:template match="Rdr_Dataset" mode="oai_datacite">
|
||||
<resource xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns="http://datacite.org/schema/kernel-4" xsi:schemaLocation="http://datacite.org/schema/kernel-4 http://schema.datacite.org/meta/kernel-4.3/metadata.xsd">
|
||||
<!-- <isReferenceQuality>true</isReferenceQuality>
|
||||
<schemaVersion>4.3</schemaVersion>
|
||||
<datacentreSymbol>RDR.GBA</datacentreSymbol> -->
|
||||
<xsl:choose>
|
||||
<xsl:when test="Identifier">
|
||||
<xsl:apply-templates select="Identifier" mode="oai_datacite" />
|
||||
</xsl:when>
|
||||
<xsl:otherwise>
|
||||
<identifier>
|
||||
<xsl:text>oai:</xsl:text>
|
||||
<xsl:value-of select="$repIdentifier" />
|
||||
<xsl:text>:</xsl:text>
|
||||
<xsl:value-of select="@PublishId" />
|
||||
</identifier>
|
||||
</xsl:otherwise>
|
||||
</xsl:choose>
|
||||
|
||||
<!--<datacite:creator>-->
|
||||
<creators>
|
||||
<xsl:apply-templates select="PersonAuthor" mode="oai_datacite" />
|
||||
</creators>
|
||||
<titles>
|
||||
<xsl:apply-templates select="TitleMain" mode="oai_datacite" />
|
||||
<xsl:apply-templates select="TitleAdditional" mode="oai_datacite" />
|
||||
</titles>
|
||||
<publisher>
|
||||
<!-- <xsl:value-of select="@PublisherName" /> -->
|
||||
<xsl:value-of select="@CreatingCorporation" />
|
||||
</publisher>
|
||||
<publicationYear>
|
||||
<xsl:value-of select="ServerDatePublished/@Year" />
|
||||
</publicationYear>
|
||||
<subjects>
|
||||
<xsl:apply-templates select="Subject" mode="oai_datacite" />
|
||||
</subjects>
|
||||
<language>
|
||||
<xsl:value-of select="@Language" />
|
||||
</language>
|
||||
<xsl:if test="PersonContributor">
|
||||
<contributors>
|
||||
<xsl:apply-templates select="PersonContributor" mode="oai_datacite" />
|
||||
</contributors>
|
||||
</xsl:if>
|
||||
<dates>
|
||||
<xsl:call-template name="RdrDate2" />
|
||||
</dates>
|
||||
<resourceType resourceTypeGeneral="Dataset">
|
||||
<xsl:text>Dataset</xsl:text>
|
||||
<!-- <xsl:value-of select="@Type" /> -->
|
||||
</resourceType>
|
||||
|
||||
<alternateIdentifiers>
|
||||
<xsl:call-template name="AlternateIdentifier" />
|
||||
</alternateIdentifiers>
|
||||
|
||||
<xsl:if test="Reference">
|
||||
<relatedIdentifiers>
|
||||
<xsl:apply-templates select="Reference" mode="oai_datacite" />
|
||||
</relatedIdentifiers>
|
||||
</xsl:if>
|
||||
<rightsList>
|
||||
<xsl:apply-templates select="Licence" mode="oai_datacite" />
|
||||
</rightsList>
|
||||
<sizes>
|
||||
<size>
|
||||
<xsl:value-of select="count(File)" />
|
||||
<xsl:text> datasets</xsl:text>
|
||||
</size>
|
||||
</sizes>
|
||||
<formats>
|
||||
<xsl:apply-templates select="File/@MimeType" mode="oai_datacite" />
|
||||
</formats>
|
||||
<descriptions>
|
||||
<xsl:apply-templates select="TitleAbstract" mode="oai_datacite" />
|
||||
<xsl:apply-templates select="TitleAbstractAdditional" mode="oai_datacite" />
|
||||
</descriptions>
|
||||
<geoLocations>
|
||||
<xsl:apply-templates select="Coverage" mode="oai_datacite" />
|
||||
<!-- <geoLocation>
|
||||
<geoLocationBox>
|
||||
<westBoundLongitude>6.58987</westBoundLongitude>
|
||||
<eastBoundLongitude>6.83639</eastBoundLongitude>
|
||||
<southBoundLatitude>50.16</southBoundLatitude>
|
||||
<northBoundLatitude>50.18691</northBoundLatitude>
|
||||
</geoLocationBox>
|
||||
</geoLocation> -->
|
||||
</geoLocations>
|
||||
</resource>
|
||||
</xsl:template>
|
||||
|
||||
<xsl:template name="RdrDate2"
|
||||
xmlns="http://datacite.org/schema/kernel-4">
|
||||
<xsl:if test="EmbargoDate and ($unixTimestamp < EmbargoDate/@UnixTimestamp)">
|
||||
<date>
|
||||
<xsl:attribute name="dateType">Available</xsl:attribute>
|
||||
<xsl:variable name="embargoDate" select="concat(
|
||||
EmbargoDate/@Year, '-',
|
||||
format-number(number(EmbargoDate/@Month),'00'), '-',
|
||||
format-number(number(EmbargoDate/@Day),'00')
|
||||
)" />
|
||||
<xsl:value-of select="$embargoDate" />
|
||||
</date>
|
||||
</xsl:if>
|
||||
<xsl:if test="CreatedAt">
|
||||
<date>
|
||||
<xsl:attribute name="dateType">created</xsl:attribute>
|
||||
<xsl:variable name="createdAt" select="concat(
|
||||
CreatedAt/@Year, '-',
|
||||
format-number(number(CreatedAt/@Month),'00'), '-',
|
||||
format-number(number(CreatedAt/@Day),'00')
|
||||
)" />
|
||||
<xsl:value-of select="$createdAt" />
|
||||
</date>
|
||||
</xsl:if>
|
||||
</xsl:template>
|
||||
|
||||
<xsl:template match="Coverage" mode="oai_datacite"
|
||||
xmlns="http://datacite.org/schema/kernel-4">
|
||||
<geoLocation>
|
||||
<geoLocationBox>
|
||||
<westBoundLongitude>
|
||||
<xsl:value-of select="@XMin" />
|
||||
</westBoundLongitude>
|
||||
<eastBoundLongitude>
|
||||
<xsl:value-of select="@XMax" />
|
||||
</eastBoundLongitude>
|
||||
<southBoundLatitude>
|
||||
<xsl:value-of select="@YMin" />
|
||||
</southBoundLatitude>
|
||||
<northBoundLatitude>
|
||||
<xsl:value-of select="@YMax" />
|
||||
</northBoundLatitude>
|
||||
</geoLocationBox>
|
||||
</geoLocation>
|
||||
</xsl:template>
|
||||
|
||||
<xsl:template match="TitleAbstract" mode="oai_datacite"
|
||||
xmlns="http://datacite.org/schema/kernel-4">
|
||||
<description>
|
||||
<xsl:attribute name="xml:lang">
|
||||
<xsl:value-of select="@Language" />
|
||||
</xsl:attribute>
|
||||
<xsl:if test="@Type != ''">
|
||||
<xsl:attribute name="descriptionType">
|
||||
<!-- <xsl:value-of select="@Type" /> -->
|
||||
<xsl:text>Abstract</xsl:text>
|
||||
</xsl:attribute>
|
||||
</xsl:if>
|
||||
<xsl:value-of select="@Value" />
|
||||
</description>
|
||||
</xsl:template>
|
||||
<xsl:template match="TitleAbstractAdditional" mode="oai_datacite"
|
||||
xmlns="http://datacite.org/schema/kernel-4">
|
||||
<description>
|
||||
<xsl:attribute name="xml:lang">
|
||||
<xsl:value-of select="@Language" />
|
||||
</xsl:attribute>
|
||||
<xsl:if test="@Type != ''">
|
||||
<xsl:attribute name="descriptionType">
|
||||
<xsl:call-template name="CamelCaseWord">
|
||||
<xsl:with-param name="text" select="@Type" />
|
||||
</xsl:call-template>
|
||||
</xsl:attribute>
|
||||
</xsl:if>
|
||||
<xsl:value-of select="@Value" />
|
||||
</description>
|
||||
</xsl:template>
|
||||
|
||||
|
||||
<xsl:template name="CamelCaseWord">
|
||||
<xsl:param name="text" />
|
||||
<xsl:param name="firstLower" select="true()" />
|
||||
<xsl:variable name="Upper">ABCDEFGHIJKLMNOPQRSTUVQXYZ</xsl:variable>
|
||||
<xsl:variable name="Lower">abcdefghijklmnopqrstuvwxyz</xsl:variable>
|
||||
<xsl:for-each select="tokenize($text,'_')">
|
||||
<xsl:choose>
|
||||
<xsl:when test="position()=1 and $firstLower = true()">
|
||||
<xsl:value-of select="substring(.,1,1)" />
|
||||
</xsl:when>
|
||||
<xsl:otherwise>
|
||||
<xsl:value-of select="translate(substring(.,1,1),$Lower,$Upper)" />
|
||||
</xsl:otherwise>
|
||||
</xsl:choose>
|
||||
<xsl:value-of select="substring(.,2,string-length(.))" />
|
||||
</xsl:for-each>
|
||||
</xsl:template>
|
||||
|
||||
|
||||
<xsl:template match="Identifier" mode="oai_datacite"
|
||||
xmlns="http://datacite.org/schema/kernel-4">
|
||||
<identifier>
|
||||
<xsl:attribute name="identifierType">
|
||||
<xsl:text>DOI</xsl:text>
|
||||
</xsl:attribute>
|
||||
<xsl:value-of select="@Value" />
|
||||
</identifier>
|
||||
</xsl:template>
|
||||
|
||||
<xsl:template match="TitleMain" mode="oai_datacite"
|
||||
xmlns="http://datacite.org/schema/kernel-4">
|
||||
<title>
|
||||
<xsl:if test="@Language != ''">
|
||||
<xsl:attribute name="xml:lang">
|
||||
<xsl:value-of select="@Language" />
|
||||
</xsl:attribute>
|
||||
</xsl:if>
|
||||
<xsl:if test="@Type != '' and @Type != 'Main'">
|
||||
<xsl:attribute name="titleType">
|
||||
<xsl:value-of select="@Type" />
|
||||
</xsl:attribute>
|
||||
</xsl:if>
|
||||
<xsl:value-of select="@Value" />
|
||||
</title>
|
||||
</xsl:template>
|
||||
<xsl:template match="TitleAdditional" mode="oai_datacite"
|
||||
xmlns="http://datacite.org/schema/kernel-4">
|
||||
<title>
|
||||
<xsl:if test="@Language != ''">
|
||||
<xsl:attribute name="xml:lang">
|
||||
<xsl:value-of select="@Language" />
|
||||
</xsl:attribute>
|
||||
</xsl:if>
|
||||
<xsl:choose>
|
||||
<xsl:when test="@Type != '' and @Type != 'Sub' and @Type != 'Main'">
|
||||
<xsl:attribute name="titleType">
|
||||
<xsl:value-of select="@Type" />
|
||||
<xsl:text>Title</xsl:text>
|
||||
</xsl:attribute>
|
||||
</xsl:when>
|
||||
<xsl:when test="@Type = 'Sub'">
|
||||
<xsl:attribute name="titleType">
|
||||
<xsl:value-of select="@Type" />
|
||||
<xsl:text>title</xsl:text>
|
||||
</xsl:attribute>
|
||||
</xsl:when>
|
||||
</xsl:choose>
|
||||
<xsl:value-of select="@Value" />
|
||||
</title>
|
||||
</xsl:template>
|
||||
|
||||
<xsl:template match="Subject" mode="oai_datacite"
|
||||
xmlns="http://datacite.org/schema/kernel-4">
|
||||
<subject>
|
||||
<xsl:if test="@Language != ''">
|
||||
<xsl:attribute name="xml:lang">
|
||||
<xsl:value-of select="@Language" />
|
||||
</xsl:attribute>
|
||||
</xsl:if>
|
||||
<xsl:value-of select="@Value" />
|
||||
</subject>
|
||||
</xsl:template>
|
||||
|
||||
<xsl:template name="AlternateIdentifier" match="AlternateIdentifier" mode="oai_datacite"
|
||||
xmlns="http://datacite.org/schema/kernel-4">
|
||||
<alternateIdentifier>
|
||||
<xsl:attribute name="alternateIdentifierType">
|
||||
<xsl:text>url</xsl:text>
|
||||
</xsl:attribute>
|
||||
<!-- <xsl:variable name="identifier" select="concat($repURL, '/dataset/', @Id)" /> -->
|
||||
<xsl:value-of select="@landingpage" />
|
||||
</alternateIdentifier>
|
||||
</xsl:template>
|
||||
|
||||
<xsl:template match="Reference" mode="oai_datacite"
|
||||
xmlns="http://datacite.org/schema/kernel-4">
|
||||
<relatedIdentifier>
|
||||
<xsl:attribute name="relatedIdentifierType">
|
||||
<xsl:value-of select="@Type" />
|
||||
</xsl:attribute>
|
||||
<xsl:attribute name="relationType">
|
||||
<xsl:value-of select="@Relation" />
|
||||
</xsl:attribute>
|
||||
<xsl:value-of select="@Value" />
|
||||
</relatedIdentifier>
|
||||
</xsl:template>
|
||||
|
||||
<xsl:template match="PersonContributor" mode="oai_datacite"
|
||||
xmlns="http://datacite.org/schema/kernel-4">
|
||||
<contributor>
|
||||
<xsl:if test="@ContributorType != ''">
|
||||
<xsl:attribute name="contributorType">
|
||||
<xsl:value-of select="@ContributorType" />
|
||||
</xsl:attribute>
|
||||
</xsl:if>
|
||||
<contributorName>
|
||||
<!-- <xsl:if test="@NameType != ''">
|
||||
<xsl:attribute name="nameType">
|
||||
<xsl:value-of select="@NameType" />
|
||||
</xsl:attribute>
|
||||
</xsl:if> -->
|
||||
<xsl:value-of select="concat(@FirstName, ' ',@LastName)" />
|
||||
</contributorName>
|
||||
</contributor>
|
||||
</xsl:template>
|
||||
|
||||
<xsl:template match="PersonAuthor" mode="oai_datacite"
|
||||
xmlns="http://datacite.org/schema/kernel-4">
|
||||
<creator>
|
||||
<creatorName>
|
||||
<xsl:if test="@NameType != ''">
|
||||
<xsl:attribute name="nameType">
|
||||
<xsl:value-of select="@NameType" />
|
||||
</xsl:attribute>
|
||||
</xsl:if>
|
||||
<xsl:value-of select="@LastName" />
|
||||
<xsl:if test="@FirstName != ''">
|
||||
<xsl:text>, </xsl:text>
|
||||
</xsl:if>
|
||||
<xsl:value-of select="@FirstName" />
|
||||
<xsl:if test="@AcademicTitle != ''">
|
||||
<xsl:text> (</xsl:text>
|
||||
<xsl:value-of select="@AcademicTitle" />
|
||||
<xsl:text>)</xsl:text>
|
||||
</xsl:if>
|
||||
</creatorName>
|
||||
|
||||
<xsl:if test="@NameType = 'Personal'">
|
||||
<givenName>
|
||||
<xsl:value-of select="@FirstName" />
|
||||
</givenName>
|
||||
<familyName>
|
||||
<xsl:value-of select="@LastName" />
|
||||
</familyName>
|
||||
<affiliation>GBA</affiliation>
|
||||
</xsl:if>
|
||||
|
||||
<xsl:if test="@IdentifierOrcid != ''">
|
||||
<nameIdentifier schemeURI="http://orcid.org/" nameIdentifierScheme="ORCID">
|
||||
<xsl:value-of select="@IdentifierOrcid" />
|
||||
</nameIdentifier>
|
||||
</xsl:if>
|
||||
<!--
|
||||
<nameType><xsl:value-of select="@NameType" /></nameType>
|
||||
</xsl:if> -->
|
||||
|
||||
</creator>
|
||||
</xsl:template>
|
||||
|
||||
<xsl:template match="File/@MimeType" mode="oai_datacite"
|
||||
xmlns="http://datacite.org/schema/kernel-4">
|
||||
<format>
|
||||
<xsl:choose>
|
||||
<xsl:when test=". = 'application/x-sqlite3'">
|
||||
<xsl:text>application/geopackage+sqlite3</xsl:text>
|
||||
</xsl:when>
|
||||
<xsl:otherwise>
|
||||
<xsl:value-of select="." />
|
||||
</xsl:otherwise>
|
||||
</xsl:choose>
|
||||
</format>
|
||||
</xsl:template>
|
||||
|
||||
<xsl:template match="Licence" mode="oai_datacite"
|
||||
xmlns="http://datacite.org/schema/kernel-4">
|
||||
<rights>
|
||||
<xsl:attribute name="xml:lang">
|
||||
<xsl:value-of select="@Language" />
|
||||
</xsl:attribute>
|
||||
<xsl:if test="@LinkLicence != ''">
|
||||
<xsl:attribute name="rightsURI">
|
||||
<xsl:value-of select="@LinkLicence" />
|
||||
</xsl:attribute>
|
||||
</xsl:if>
|
||||
<xsl:attribute name="schemeURI">
|
||||
<xsl:text>https://spdx.org/licenses/</xsl:text>
|
||||
</xsl:attribute>
|
||||
<xsl:attribute name="rightsIdentifierScheme">
|
||||
<xsl:text>SPDX</xsl:text>
|
||||
</xsl:attribute>
|
||||
<xsl:attribute name="rightsIdentifier">
|
||||
<xsl:value-of select="@Name" />
|
||||
</xsl:attribute>
|
||||
<xsl:value-of select="@NameLong" />
|
||||
</rights>
|
||||
<xsl:if test="@Name = 'CC-BY-4.0' or @Name = 'CC-BY-SA-4.0'">
|
||||
<rights>
|
||||
<xsl:attribute name="rightsURI">
|
||||
<xsl:text>info:eu-repo/semantics/openAccess</xsl:text>
|
||||
</xsl:attribute>
|
||||
<xsl:text>Open Access</xsl:text>
|
||||
</rights>
|
||||
</xsl:if>
|
||||
</xsl:template>
|
||||
|
||||
</xsl:stylesheet>
|
17
readme.md
Normal file
17
readme.md
Normal file
|
@ -0,0 +1,17 @@
|
|||
# TETHYS
|
||||
|
||||
TETHYS - Data Publisher for Geoscience Austria is a digital data library and a data publisher for earth system science. Data can be georeferenced in time (date/time) and space (latitude, longitude, depth/height).\
|
||||
Please watch our website - [www.tethys.at](https://www.tethys.at)
|
||||
|
||||
----------
|
||||
|
||||
## Getting Started
|
||||
|
||||
These instructions will get you a copy of the project up and running on your local machine for development and testing purposes. See deployment for notes on how to deploy the project on a live system.
|
||||
|
||||
|
||||
|
||||
|
||||
## License
|
||||
|
||||
This project is licensed under the MIT License - see the [license](LICENSE) file for details
|
76
src/app.ts
Normal file
76
src/app.ts
Normal file
|
@ -0,0 +1,76 @@
|
|||
import { Server } from "@overnightjs/core";
|
||||
import express from "express";
|
||||
import bodyParser from "body-parser";
|
||||
import HomeRoutes from "./routes/home.routes.js";
|
||||
import { initDB } from "./config/db.config";
|
||||
import { DatasetController } from "./controllers/dataset.controller";
|
||||
import { OaiController } from "./controllers/oai.controller";
|
||||
import * as path from 'path';
|
||||
|
||||
export class App extends Server {
|
||||
// private app;
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
// this.app = express();
|
||||
this.app.use('/prefixes', express.static(path.join(__dirname, '../prefixes')));
|
||||
this.applyMiddleWares();
|
||||
// init db and add routes
|
||||
this.boostrap();
|
||||
}
|
||||
|
||||
public start(): void {
|
||||
const port = process.env.PORT || 3000;
|
||||
this.app.set("port", port);
|
||||
this.app.listen(port, () => {
|
||||
console.log("Server listening on port: " + port);
|
||||
});
|
||||
}
|
||||
|
||||
private applyMiddleWares(): void {
|
||||
this.app.all("*", function (req, res, next) {
|
||||
res.setHeader("Access-Control-Allow-Origin", "*");
|
||||
res.header("Access-Control-Allow-Methods", "POST, PUT, OPTIONS, DELETE, GET");
|
||||
res.header("Access-Control-Max-Age", "3600");
|
||||
res.header(
|
||||
"Access-Control-Allow-Headers",
|
||||
"Content-Type, Access-Control-Allow-Headers, Authorization, X-Requested-With, x-access-token",
|
||||
);
|
||||
next();
|
||||
});
|
||||
|
||||
// this.app.use(bodyParser.json());
|
||||
// this.app.use(bodyParser.urlencoded({ extended: true }));
|
||||
this.app.use(bodyParser.json({ limit: "100mb" }));
|
||||
this.app.use(bodyParser.urlencoded({ limit: "50mb", extended: true }));
|
||||
this.app.use(bodyParser.json({ type: "application/vnd.api+json" }));
|
||||
}
|
||||
|
||||
private boostrap(): void {
|
||||
// Connect to db
|
||||
initDB()
|
||||
.then(() => {
|
||||
console.log("Connection has been established successfully.");
|
||||
|
||||
// addroutes
|
||||
// this.app.use('/api/dataset', DatasetRoutes);
|
||||
super.addControllers([new DatasetController(), new OaiController()]);
|
||||
|
||||
this.app.use("/api/", HomeRoutes);
|
||||
this.app.get("/", (request, response) => {
|
||||
// response.send('Hello World, from express');
|
||||
response.sendFile("/home/administrator/api/new-book.html");
|
||||
});
|
||||
|
||||
// Error handling middleware
|
||||
// this.app.use(errorHandler); // registration of handler
|
||||
// this.app.use((err: HTTPException, req: Request, res: Response, next: NextFunction) => {
|
||||
// console.log('Oops !!, Error occured in req->res cycle ', err.message);
|
||||
// res.status(err.status).json({ err }); // Send back Error to the Frontend
|
||||
// })
|
||||
})
|
||||
.catch((err) => {
|
||||
console.error("Unable to connect to the database:", err);
|
||||
});
|
||||
}
|
||||
}
|
60
src/config/db.config.ts
Normal file
60
src/config/db.config.ts
Normal file
|
@ -0,0 +1,60 @@
|
|||
import { Dialect } from "sequelize";
|
||||
import { Sequelize } from "sequelize-typescript";
|
||||
// import * as dotenv from "dotenv"; // see https://github.com/motdotla/dotenv#how-do-i-use-dotenv-with-import
|
||||
// dotenv.config();
|
||||
// import 'dotenv/config';
|
||||
import dotenv from "dotenv";
|
||||
dotenv.config();
|
||||
// import { Dataset } from '../models/Dataset';
|
||||
// import { Abstract } from "../models/Abstract";
|
||||
// import DocumentXmlCache from '../models/DocumentXmlCache';
|
||||
|
||||
// const dbName = process.env.DB_NAME as string
|
||||
// const dbUser = process.env.DB_USER as string
|
||||
// const dbHost = process.env.DB_HOST
|
||||
// const dbDriver = process.env.DB_DRIVER as Dialect
|
||||
// const dbPassword = process.env.DB_PASSWORD
|
||||
|
||||
const dbSchema = process.env.DB_SCHEMA;
|
||||
const dbName = process.env.DB_NAME as string;
|
||||
const dbUser = process.env.DB_USER as string;
|
||||
const dbPassword = process.env.DB_PASSWORD;
|
||||
const dbHost = process.env.DB_HOST;
|
||||
const dbDriver = process.env.DB_DRIVER as Dialect;
|
||||
|
||||
const sequelizeConnection = new Sequelize(dbName, dbUser, dbPassword, {
|
||||
schema: dbSchema,
|
||||
host: dbHost || "localhost",
|
||||
port: 5432,
|
||||
dialect: dbDriver || "postgres",
|
||||
dialectOptions: {
|
||||
ssl: process.env.DB_SSL == "true",
|
||||
useUTC: false, //for reading from database
|
||||
dateStrings: true,
|
||||
typeCast: true,
|
||||
},
|
||||
pool: {
|
||||
max: 10,
|
||||
min: 0,
|
||||
acquire: 30000,
|
||||
idle: 10000,
|
||||
},
|
||||
logging: false,
|
||||
timezone: "+02:00", //for writing to database
|
||||
});
|
||||
// sequelizeConnection.addModels([Dataset, Abstract]);
|
||||
// sequelizeConnection.addModels([DocumentXmlCache]);
|
||||
|
||||
export const initDB = async () => {
|
||||
await sequelizeConnection.authenticate();
|
||||
// .then(() => {
|
||||
// console.log("Connection has been established successfully.");
|
||||
// })
|
||||
// .catch((err) => {
|
||||
// console.error("Unable to connect to the database:", err);
|
||||
// });
|
||||
};
|
||||
|
||||
export default sequelizeConnection;
|
||||
|
||||
// export { Dataset, Abstract };
|
12
src/config/oai.config.ts
Normal file
12
src/config/oai.config.ts
Normal file
|
@ -0,0 +1,12 @@
|
|||
const config = {
|
||||
max: {
|
||||
listidentifiers: 100,
|
||||
listrecords: 50,
|
||||
},
|
||||
workspacePath: "workspace",
|
||||
};
|
||||
export default config;
|
||||
// config.api = {};
|
||||
// config.api.url = process.env.API_URL || http://my.api.com;
|
||||
// config.axios = 'General http send information'
|
||||
// module.exports = config;
|
160
src/controllers/dataset.controller.ts
Normal file
160
src/controllers/dataset.controller.ts
Normal file
|
@ -0,0 +1,160 @@
|
|||
import { Controller, Get } from "@overnightjs/core";
|
||||
import dbContext from "../models/init-models.js";
|
||||
import { Dataset, User, Person } from "../models/init-models.js";
|
||||
import Sequelize from "sequelize";
|
||||
const Op = Sequelize.Op;
|
||||
import { Request, Response } from "express";
|
||||
// import Logger from 'jet-logger';
|
||||
import { StatusCodes } from "http-status-codes";
|
||||
|
||||
@Controller("api/dataset")
|
||||
export class DatasetController {
|
||||
@Get("")
|
||||
public async findAll(req: Request, res: Response) {
|
||||
// const type = req.query.type;
|
||||
// var condition = type ? { type: { [Op.iLike]: `%${type}%` } } : null;,
|
||||
const server_state = "published";
|
||||
const condition = { server_state: { [Op.eq]: `${server_state}` } };
|
||||
|
||||
Dataset.findAll({
|
||||
where: condition,
|
||||
include: ["abstracts"],
|
||||
order: ["server_date_published"],
|
||||
})
|
||||
.then((data) => {
|
||||
res.send(data);
|
||||
})
|
||||
.catch((err) => {
|
||||
res.status(500).send({
|
||||
message: err.message || "Some error occurred while retrieving datasets.",
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@Get(":publish_id")
|
||||
public async findOne(req: Request, res: Response) {
|
||||
const publish_id = req.params.publish_id;
|
||||
|
||||
const dataset = await dbContext.Dataset.findOne({
|
||||
where: { publish_id: publish_id },
|
||||
include: [
|
||||
"titles",
|
||||
"abstracts",
|
||||
{
|
||||
model: User,
|
||||
as: "user",
|
||||
},
|
||||
{
|
||||
model: Person,
|
||||
through: { where: { role: "author" } },
|
||||
as: "authors",
|
||||
},
|
||||
{
|
||||
model: Person,
|
||||
through: { where: { role: "contributor" } },
|
||||
as: "contributors",
|
||||
},
|
||||
"subjects",
|
||||
"coverage",
|
||||
"licenses",
|
||||
"project",
|
||||
"files",
|
||||
"identifier",
|
||||
],
|
||||
// order: ['server_date_published'],
|
||||
});
|
||||
// .then((data) => {
|
||||
// if (data) {
|
||||
// res.send(data);
|
||||
// } else {
|
||||
// res.status(404).send({
|
||||
// message: `Cannot find Dataset with publish_id=${publish_id}.`,
|
||||
// });
|
||||
// }
|
||||
// })
|
||||
// .catch((err) => {
|
||||
// res.status(500).send({
|
||||
// message: "Error retrieving Dataset with publish_id=" + publish_id,
|
||||
// });
|
||||
// });
|
||||
if (dataset) {
|
||||
res.status(StatusCodes.OK).send(dataset);
|
||||
} else {
|
||||
res.status(StatusCodes.NOT_FOUND).send({
|
||||
message: `Cannot find Dataset with publish_id=${publish_id}.`,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Retrieve all Tutorials from the database.
|
||||
// export async function findAll(req: Request, res: Response) {
|
||||
// // const type = req.query.type;
|
||||
// // var condition = type ? { type: { [Op.iLike]: `%${type}%` } } : null;,
|
||||
// const server_state = "published";
|
||||
// var condition = { server_state: { [Op.eq]: `${server_state}` } };
|
||||
|
||||
// Dataset.findAll({
|
||||
// where: condition,
|
||||
// include: [
|
||||
// "abstracts"
|
||||
// ],
|
||||
// order: ['server_date_published'],
|
||||
// })
|
||||
// .then((data) => {
|
||||
// res.send(data);
|
||||
// })
|
||||
// .catch((err) => {
|
||||
// res.status(500).send({
|
||||
// message:
|
||||
// err.message || "Some error occurred while retrieving datasets.",
|
||||
// });
|
||||
// });
|
||||
// }
|
||||
|
||||
// export async function findOne(req: Request, res: Response) {
|
||||
// const publish_id = req.params.publish_id;
|
||||
|
||||
// dbContext.Dataset.findOne({
|
||||
// where: { publish_id: publish_id },
|
||||
// include: [
|
||||
// "titles",
|
||||
// "abstracts",
|
||||
// {
|
||||
// model: User,
|
||||
// as: "user",
|
||||
// },
|
||||
// {
|
||||
// model: Person,
|
||||
// through: { where: { role: "author" } },
|
||||
// as: "authors",
|
||||
// },
|
||||
// {
|
||||
// model: Person,
|
||||
// through: { where: { role: "contributor" } },
|
||||
// as: "contributors",
|
||||
// },
|
||||
// "subjects",
|
||||
// "coverage",
|
||||
// "licenses",
|
||||
// "project",
|
||||
// "files",
|
||||
// "identifier"
|
||||
// ],
|
||||
// // order: ['server_date_published'],
|
||||
// })
|
||||
// .then((data) => {
|
||||
// if (data) {
|
||||
// res.send(data);
|
||||
// } else {
|
||||
// res.status(404).send({
|
||||
// message: `Cannot find Dataset with publish_id=${publish_id}.`,
|
||||
// });
|
||||
// }
|
||||
// })
|
||||
// .catch((err) => {
|
||||
// res.status(500).send({
|
||||
// message: "Error retrieving Dataset with publish_id=" + publish_id,
|
||||
// });
|
||||
// });
|
||||
// }
|
1
src/controllers/datasetxml2oai.sef.json
Normal file
1
src/controllers/datasetxml2oai.sef.json
Normal file
File diff suppressed because one or more lines are too long
77
src/controllers/home.controller.js
Normal file
77
src/controllers/home.controller.js
Normal file
|
@ -0,0 +1,77 @@
|
|||
import sequelizeConnection from "../config/db.config";
|
||||
import dbContext from "../models/init-models.js";
|
||||
import Sequelize from "sequelize";
|
||||
const Op = Sequelize.Op;
|
||||
import { Person } from "../models/init-models.js";
|
||||
|
||||
export async function findYears(req, res) {
|
||||
const serverState = "published";
|
||||
// Use raw SQL queries to select all cars which belongs to the user
|
||||
const datasets = await sequelizeConnection.query(
|
||||
"SELECT distinct EXTRACT(YEAR FROM server_date_published) as published_date FROM gba.documents WHERE server_state = (:serverState)",
|
||||
{
|
||||
replacements: { serverState: serverState },
|
||||
type: sequelizeConnection.QueryTypes.SELECT,
|
||||
// attributes: [[sequelizeConnection.fn('DISTINCT', sequelizeConnection.col('published_date')), 'alias_name']],
|
||||
},
|
||||
);
|
||||
// Pluck the ids of the cars
|
||||
const years = datasets.map((dataset) => dataset.published_date);
|
||||
// check if the cars is returned
|
||||
if (years.length > 0) {
|
||||
return res.status(200).json(years);
|
||||
}
|
||||
}
|
||||
|
||||
export async function findDocumentsPerYear(req, res) {
|
||||
const year = req.params.year;
|
||||
const from = parseInt(year);
|
||||
const serverState = "published";
|
||||
|
||||
const conditions = {
|
||||
[Op.and]: [
|
||||
{
|
||||
server_state: `${serverState}`,
|
||||
},
|
||||
{
|
||||
[Op.eq]: sequelizeConnection.where(
|
||||
sequelizeConnection.fn("date_part", "year", sequelizeConnection.col("server_date_published")),
|
||||
from,
|
||||
),
|
||||
// [Op.eq]: sequelizeConnection.where(sequelizeConnection.fn('date', sequelizeConnection.col('server_date_published')), '>=', fromYear)
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
dbContext.Dataset.findAll({
|
||||
attributes: [
|
||||
"publish_id",
|
||||
"server_date_published",
|
||||
[sequelizeConnection.fn("date_part", "year", sequelizeConnection.col("server_date_published")), "pub_year"],
|
||||
],
|
||||
where: conditions,
|
||||
include: [
|
||||
"titles",
|
||||
{
|
||||
model: Person,
|
||||
through: { where: { role: "author" } },
|
||||
as: "authors",
|
||||
},
|
||||
],
|
||||
order: ["server_date_published"],
|
||||
})
|
||||
.then((data) => {
|
||||
res.send(data);
|
||||
})
|
||||
.catch((err) => {
|
||||
res.status(500).send({
|
||||
message: err.message || "Some error occurred while retrieving datasets.",
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// function sendToElasticAndLogToConsole(sql, queryObject) {
|
||||
// // save the `sql` query in Elasticsearch
|
||||
// console.log(sql);
|
||||
// // use the queryObject if needed (e.g. for debugging)
|
||||
// }
|
738
src/controllers/oai.controller.ts
Normal file
738
src/controllers/oai.controller.ts
Normal file
|
@ -0,0 +1,738 @@
|
|||
import { Controller, Get } from "@overnightjs/core";
|
||||
import Sequelize from "sequelize";
|
||||
import { NextFunction, Request, Response } from "express";
|
||||
import { StatusCodes } from "http-status-codes";
|
||||
import { create } from "xmlbuilder2";
|
||||
import { XMLBuilder } from "xmlbuilder2/lib/interfaces";
|
||||
import { readFileSync } from "fs";
|
||||
// @ts-ignore
|
||||
import { transform } from "saxon-js";
|
||||
import dayjs, { Dayjs, OpUnitType } from "dayjs";
|
||||
import { Dataset, Project, License } from "../models/init-models";
|
||||
import Logger from "jet-logger";
|
||||
import { BadOaiModelException, OaiModelException } from "../exceptions/OaiModelException";
|
||||
import PageNotFoundException from "../exceptions/PageNotFoundException";
|
||||
import { OaiErrorCodes } from "../exceptions/OaiErrorCodes";
|
||||
import XmlModel from "../library/XmlModel";
|
||||
import Configuration from "../library/oai/OaiConfiguration";
|
||||
import ResumptionToken from "../library/oai/ResumptionToken";
|
||||
import TokenWorker from "../library/oai/TokenWorker";
|
||||
|
||||
interface XslTParameter {
|
||||
[key: string]: any;
|
||||
}
|
||||
interface OaiParameter {
|
||||
[key: string]: any;
|
||||
}
|
||||
interface IDictionary {
|
||||
[index: string]: string;
|
||||
}
|
||||
|
||||
function preg_match(regex: RegExp, str: string) {
|
||||
const result: boolean = regex.test(str);
|
||||
return result;
|
||||
}
|
||||
|
||||
@Controller("oai")
|
||||
export class OaiController {
|
||||
private deliveringDocumentStates = ["published", "deleted"];
|
||||
private sampleRegEx = /^[A-Za-zäüÄÜß0-9\-_.!~]+$/;
|
||||
private xsltParameter: XslTParameter;
|
||||
private configuration: Configuration;
|
||||
private tokenWorker: TokenWorker;
|
||||
|
||||
/**
|
||||
* Holds xml representation of document information to be processed.
|
||||
*
|
||||
* @var xmlbuilder.XMLDocument | null Defaults to null.
|
||||
*/
|
||||
private xml: XMLBuilder;
|
||||
|
||||
private proc;
|
||||
|
||||
constructor() {
|
||||
this.proc = readFileSync(__dirname + "/datasetxml2oai.sef.json");
|
||||
this.configuration = new Configuration();
|
||||
}
|
||||
|
||||
@Get("")
|
||||
public async index(request: Request, response: Response, next: NextFunction) {
|
||||
this.xml = create(
|
||||
{ version: "1.0", encoding: "UTF-8", standalone: true },
|
||||
"<root></root>",
|
||||
// {
|
||||
// keepNullNodes: false,
|
||||
// keepNullAttributes: false,
|
||||
// headless: false,
|
||||
// ignoreDecorators: false,
|
||||
// separateArrayItems: false,
|
||||
// noDoubleEncoding: false,
|
||||
// noValidation: false,
|
||||
// invalidCharReplacement: undefined,
|
||||
// stringify: {},
|
||||
// },
|
||||
);
|
||||
|
||||
// this.proc = new XSLTProcessor();
|
||||
// const stylesheet = readFileSync(__dirname + "/datasetxml2oai.sef.json");
|
||||
const xsltParameter = (this.xsltParameter = {});
|
||||
|
||||
let earliestDateFromDb;
|
||||
const firstPublishedDataset: Dataset | null = await Dataset.earliestPublicationDate();
|
||||
firstPublishedDataset != null &&
|
||||
(earliestDateFromDb = dayjs(firstPublishedDataset.server_date_published).format("YYYY-MM-DDThh:mm:ss[Z]"));
|
||||
this.xsltParameter["earliestDatestamp"] = earliestDateFromDb;
|
||||
|
||||
const oaiRequest: OaiParameter = request.query;
|
||||
try {
|
||||
await this.handleRequest(oaiRequest, request);
|
||||
} catch (error) {
|
||||
// return next(error);
|
||||
if (error instanceof OaiModelException) {
|
||||
this.xsltParameter["oai_error_code"] = error.oaiCode;
|
||||
this.xsltParameter["oai_error_message"] = error.message;
|
||||
} else {
|
||||
// return next(error); // passing to default express middleware error handler
|
||||
this.xsltParameter["oai_error_code"] = "unknown";
|
||||
this.xsltParameter["oai_error_message"] = "An internal error occured.";
|
||||
}
|
||||
}
|
||||
// catch (error) { // manually catching
|
||||
// return next(error); // passing to default express middleware error handler
|
||||
// }
|
||||
const xmlString = this.xml.end({ prettyPrint: true });
|
||||
|
||||
// let data = await transform({
|
||||
// stylesheetText: stylesheet,
|
||||
// // stylesheetBaseURI: "my-stylesheet.sef.json",
|
||||
// sourceText: xmlString,
|
||||
// destination: "serialized"
|
||||
// });
|
||||
// .then((data: any) => {
|
||||
// response.writeHead(200, {'Content-Type': 'application/xml'});
|
||||
// response.write(data.principalResult);
|
||||
// response.end();
|
||||
// });
|
||||
let xmlOutput;
|
||||
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,
|
||||
});
|
||||
xmlOutput = result.principalResult;
|
||||
} catch (error) {
|
||||
// return next(error);
|
||||
// if (error instanceof OaiModelException) {
|
||||
// this.xsltParameter["oai_error_code"] = error.oaiCode;
|
||||
// this.xsltParameter["oai_error_message"] = error.message;
|
||||
// } else {
|
||||
// // return next(error); // passing to default express middleware error handler
|
||||
// this.xsltParameter["oai_error_code"] = "unknown";
|
||||
// this.xsltParameter["oai_error_message"] = "An internal error occured.";
|
||||
// }
|
||||
return next(error);
|
||||
}
|
||||
|
||||
response
|
||||
.header("Content-Type", "application/xml")
|
||||
.header("Access-Control-Allow-Origin", "*")
|
||||
.header("Access-Control-Allow-Methods", "GET,POST");
|
||||
response.status(StatusCodes.OK).send(xmlOutput);
|
||||
// response.end();
|
||||
}
|
||||
|
||||
protected async handleRequest(oaiRequest: OaiParameter, request: Request) {
|
||||
// Setup stylesheet
|
||||
// $this->loadStyleSheet('datasetxml2oai-pmh.xslt');
|
||||
|
||||
// Set response time
|
||||
const now: dayjs.Dayjs = dayjs();
|
||||
this.xsltParameter["responseDate"] = now.format("YYYY-MM-DDThh:mm:ss[Z]");
|
||||
this.xsltParameter["unixTimestamp"] = now.unix();
|
||||
|
||||
// set OAI base url
|
||||
const baseDomain = process.env.BASE_DOMAIN || "localhost";
|
||||
this.xsltParameter["baseURL"] = baseDomain + "/oai";
|
||||
this.xsltParameter["repURL"] = request.protocol + "://" + request.get("host");
|
||||
this.xsltParameter["downloadLink"] = request.protocol + "://" + request.get("host") + "/file/download/";
|
||||
this.xsltParameter["doiLink"] = "https://doi.org/";
|
||||
this.xsltParameter["doiPrefix"] = "info:eu-repo/semantics/altIdentifier/doi/";
|
||||
|
||||
if (oaiRequest["verb"]) {
|
||||
const verb = oaiRequest["verb"];
|
||||
this.xsltParameter["oai_verb"] = verb;
|
||||
if (verb == "Identify") {
|
||||
this.handleIdentify();
|
||||
} else if (verb == "ListMetadataFormats") {
|
||||
this.handleListMetadataFormats();
|
||||
} else if (verb == "GetRecord") {
|
||||
await this.handleGetRecord(oaiRequest);
|
||||
} else if (verb == "ListRecords") {
|
||||
await this.handleListRecords(oaiRequest);
|
||||
} else if (verb == "ListIdentifiers") {
|
||||
await this.handleListIdentifiers(oaiRequest);
|
||||
} else if (verb == "ListSets") {
|
||||
await this.handleListSets();
|
||||
} else {
|
||||
this.handleIllegalVerb();
|
||||
}
|
||||
} else {
|
||||
// const err = new HttpException(404, 'Not Found')
|
||||
// next(err);
|
||||
|
||||
// try {
|
||||
// console.log("Async code example.")
|
||||
// const err = new HttpException(404, 'Not Found');
|
||||
const err = new PageNotFoundException("verb not found");
|
||||
throw err;
|
||||
// } catch (error) { // manually catching
|
||||
// next(error); // passing to default middleware error handler
|
||||
// }
|
||||
}
|
||||
}
|
||||
|
||||
protected handleIdentify() {
|
||||
const email = "repository@geologie.ac.at";
|
||||
const repositoryName = "Tethys RDR";
|
||||
const repIdentifier = "tethys.at";
|
||||
const sampleIdentifier = "oai:" + repIdentifier + ":1"; //$this->_configuration->getSampleIdentifier();
|
||||
|
||||
// Dataset::earliestPublicationDate()->server_date_published->format('Y-m-d\TH:i:s\Z') : null;
|
||||
// earliestDateFromDb!= null && (this.xsltParameter['earliestDatestamp'] = earliestDateFromDb?.server_date_published);
|
||||
|
||||
// set parameters for oai-pmh.xslt
|
||||
this.xsltParameter["email"] = email;
|
||||
this.xsltParameter["repositoryName"] = repositoryName;
|
||||
this.xsltParameter["repIdentifier"] = repIdentifier;
|
||||
this.xsltParameter["sampleIdentifier"] = sampleIdentifier;
|
||||
// $this->proc->setParameter('', 'earliestDatestamp', $earliestDateFromDb);
|
||||
|
||||
this.xml.root().ele("Datasets");
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements response for OAI-PMH verb 'ListMetadataFormats'.
|
||||
*
|
||||
* @param array &$oaiRequest Contains full request information
|
||||
* @return void
|
||||
*/
|
||||
protected handleListMetadataFormats() {
|
||||
this.xml.root().ele("Datasets");
|
||||
}
|
||||
|
||||
protected async handleListSets() {
|
||||
const repIdentifier = "tethys.at";
|
||||
this.xsltParameter["repIdentifier"] = repIdentifier;
|
||||
const datasetElement = this.xml.root().ele("Datasets");
|
||||
|
||||
const sets: { [key: string]: string } = {
|
||||
open_access: "Set for open access licenses",
|
||||
// 'bibliography:true' => 'Set for bibliographic entries',
|
||||
// 'bibliography:false' => 'Set for non-bibliographic entries',
|
||||
...(await this.getSetsForDatasetTypes()),
|
||||
// ... await this.getSetsForProjects(),
|
||||
} as IDictionary;
|
||||
|
||||
for (const [key, value] of Object.entries(sets)) {
|
||||
const setElement = datasetElement.ele("Rdr_Sets");
|
||||
setElement.att("Type", key);
|
||||
setElement.att("TypeName", value);
|
||||
}
|
||||
}
|
||||
|
||||
protected async handleGetRecord(oaiRequest: OaiParameter) {
|
||||
// GetRecord&metadataPrefix=oai_dc&identifier=oai:tethys.at:1
|
||||
|
||||
const repIdentifier = "tethys.at";
|
||||
this.xsltParameter["repIdentifier"] = repIdentifier;
|
||||
|
||||
// Identifier references metadata Urn, not plain Id!
|
||||
// Currently implemented as 'oai:foo.bar.de:{docId}' or 'urn:nbn...-123'
|
||||
if (!("identifier" in oaiRequest)) {
|
||||
// throw new BadOaiModelException('The prefix of the identifier argument is unknown.');
|
||||
throw new BadOaiModelException("The prefix of the identifier argument is unknown.");
|
||||
}
|
||||
const dataId = Number(this.getDocumentIdByIdentifier(oaiRequest.identifier));
|
||||
|
||||
// let dataset: Dataset | null;
|
||||
|
||||
const dataset = await Dataset.findOne({
|
||||
where: { publish_id: dataId },
|
||||
include: ["xmlCache"],
|
||||
// order: ['server_date_published'],
|
||||
});
|
||||
if (!dataset || !dataset.publish_id) {
|
||||
throw new OaiModelException(
|
||||
StatusCodes.INTERNAL_SERVER_ERROR,
|
||||
"The value of the identifier argument is unknown or illegal in this repository.",
|
||||
OaiErrorCodes.IDDOESNOTEXIST,
|
||||
);
|
||||
}
|
||||
|
||||
let metadataPrefix = null;
|
||||
if ("metadataPrefix" in oaiRequest) {
|
||||
metadataPrefix = oaiRequest["metadataPrefix"];
|
||||
} else {
|
||||
throw new OaiModelException(
|
||||
StatusCodes.INTERNAL_SERVER_ERROR,
|
||||
"The prefix of the metadata argument is unknown.",
|
||||
OaiErrorCodes.BADARGUMENT,
|
||||
);
|
||||
}
|
||||
this.xsltParameter["oai_metadataPrefix"] = metadataPrefix;
|
||||
|
||||
// do not deliver datasets which are restricted by document state
|
||||
|
||||
if (dataset.server_state == null || !this.deliveringDocumentStates.includes(dataset.server_state)) {
|
||||
throw new OaiModelException(
|
||||
StatusCodes.INTERNAL_SERVER_ERROR,
|
||||
"Document is not available for OAI export!",
|
||||
OaiErrorCodes.NORECORDSMATCH,
|
||||
);
|
||||
}
|
||||
|
||||
// add xml elements
|
||||
const datasetNode = this.xml.root().ele("Datasets");
|
||||
|
||||
await this.createXmlRecord(dataset, datasetNode);
|
||||
// let domNode = await this.getDatasetXmlDomNode(dataset);
|
||||
// // add frontdoor url
|
||||
// // dataset.publish_id = dataset.publish_id != null ? dataset.publish_id : 0;
|
||||
// this.addLandingPageAttribute(domNode, dataset.publish_id.toString());
|
||||
// this.addSpecInformation(domNode, "data-type:" + dataset.type);
|
||||
// datasetNode.import(domNode);
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements response for OAI-PMH verb 'ListRecords'.
|
||||
*
|
||||
* @param array &$oaiRequest Contains full request information
|
||||
* @return void
|
||||
*/
|
||||
protected async handleListRecords(oaiRequest: OaiParameter) {
|
||||
if (!this.tokenWorker) {
|
||||
this.tokenWorker = new TokenWorker(86400);
|
||||
}
|
||||
!this.tokenWorker.Connected && (await this.tokenWorker.connect());
|
||||
|
||||
//$maxRecords = 30; //$this->_configuration->getMaxListRecords();
|
||||
const maxRecords = this.configuration.maxListRecs;
|
||||
await this.handlingOfLists(oaiRequest, maxRecords);
|
||||
|
||||
await this.tokenWorker.close();
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements response for OAI-PMH verb 'ListIdentifiers'.
|
||||
*
|
||||
* @param array &$oaiRequest Contains full request information
|
||||
* @return void
|
||||
*/
|
||||
protected async handleListIdentifiers(oaiRequest: OaiParameter) {
|
||||
// if ("resumptionToken" in oaiRequest) {
|
||||
if (!this.tokenWorker) {
|
||||
this.tokenWorker = new TokenWorker(86400);
|
||||
}
|
||||
!this.tokenWorker.Connected && (await this.tokenWorker.connect());
|
||||
|
||||
//$maxIdentifier = 5; //$this->_configuration->getMaxListIdentifiers();
|
||||
const maxIdentifier = this.configuration.maxListIds; //->getMaxListIdentifiers();
|
||||
await this.handlingOfLists(oaiRequest, maxIdentifier);
|
||||
|
||||
await this.tokenWorker.close();
|
||||
}
|
||||
|
||||
private async handlingOfLists(oaiRequest: OaiParameter, maxRecords: number) {
|
||||
if (!maxRecords) {
|
||||
maxRecords = 100;
|
||||
}
|
||||
const repIdentifier = "tethys.at";
|
||||
// //$this->_configuration->getResumptionTokenPath();
|
||||
// $tokenTempPath = storage_path('app' . DIRECTORY_SEPARATOR . 'resumption');
|
||||
|
||||
this.xsltParameter["repIdentifier"] = repIdentifier;
|
||||
const datasetNode = this.xml.root().ele("Datasets");
|
||||
|
||||
// // do some initialisation
|
||||
let cursor = 0;
|
||||
let totalIds = 0;
|
||||
let start = maxRecords + 1;
|
||||
let reldocIds: (number | null)[] = [];
|
||||
|
||||
let metadataPrefix = null;
|
||||
|
||||
// const tokenWorker = new TokenWorker(86400);
|
||||
// await tokenWorker.connect();
|
||||
// $tokenWorker->setResumptionPath($tokenTempPath);
|
||||
// const url = process.env.REDIS_URL || "redis://redis:6379";
|
||||
// const redisClient = createClient({
|
||||
// url
|
||||
// });
|
||||
// redisClient.on('error', (error) => {
|
||||
// const err = new InternalServerErrorException("Error occured while connecting or accessing redis server'");
|
||||
// throw err;
|
||||
// });
|
||||
|
||||
// resumptionToken is defined
|
||||
if ("resumptionToken" in oaiRequest) {
|
||||
const resParam = oaiRequest["resumptionToken"]; //e.g. "158886496600000"
|
||||
// let token = await tokenWorker.getResumptionToken(resParam);
|
||||
const token = await this.tokenWorker.get(resParam);
|
||||
|
||||
if (!token) {
|
||||
throw new OaiModelException(StatusCodes.INTERNAL_SERVER_ERROR, "cache is outdated.", OaiErrorCodes.BADRESUMPTIONTOKEN);
|
||||
}
|
||||
|
||||
cursor = token.StartPosition - 1; //startet dann bei Index 10
|
||||
start = token.StartPosition + maxRecords;
|
||||
totalIds = token.TotalIds;
|
||||
reldocIds = token.DocumentIds;
|
||||
metadataPrefix = token.MetadataPrefix;
|
||||
|
||||
this.xsltParameter["oai_metadataPrefix"] = metadataPrefix;
|
||||
} else {
|
||||
// no resumptionToken is given
|
||||
if ("metadataPrefix" in oaiRequest) {
|
||||
metadataPrefix = oaiRequest["metadataPrefix"];
|
||||
} else {
|
||||
throw new OaiModelException(
|
||||
StatusCodes.INTERNAL_SERVER_ERROR,
|
||||
"The prefix of the metadata argument is unknown.",
|
||||
OaiErrorCodes.BADARGUMENT,
|
||||
);
|
||||
}
|
||||
this.xsltParameter["oai_metadataPrefix"] = metadataPrefix;
|
||||
|
||||
// add server state restrictions
|
||||
const includeArray: Array<any> = [];
|
||||
const andArray: Array<any> = new Array({
|
||||
server_state: {
|
||||
[Sequelize.Op.in]: this.deliveringDocumentStates,
|
||||
},
|
||||
});
|
||||
// andArray.push({
|
||||
// server_state: {
|
||||
// [Sequelize.Op.in]: this.deliveringDocumentStates,
|
||||
// },
|
||||
// });
|
||||
if ("set" in oaiRequest) {
|
||||
const set = oaiRequest["set"] as string;
|
||||
const setArray = set.split(":");
|
||||
if (setArray[0] == "data-type") {
|
||||
if (setArray.length == 2 && setArray[1]) {
|
||||
andArray.push({
|
||||
type: {
|
||||
[Sequelize.Op.eq]: setArray[1],
|
||||
},
|
||||
});
|
||||
}
|
||||
} else if (setArray[0] == "open_access") {
|
||||
const openAccessLicences = ["CC-BY-4.0", "CC-BY-SA-4.0"];
|
||||
let icncludeFilter = {
|
||||
model: License,
|
||||
as: "licenses",
|
||||
required: true, //return only records which have an associated model INNER JOIN
|
||||
where: {
|
||||
name: {
|
||||
[Sequelize.Op.in]: openAccessLicences,
|
||||
},
|
||||
},
|
||||
};
|
||||
includeArray.push(icncludeFilter);
|
||||
}
|
||||
}
|
||||
|
||||
// &from=2020-09-03&until2020-09-03
|
||||
// &from=2020-09-11&until=2021-05-11
|
||||
if ("from" in oaiRequest && "until" in oaiRequest) {
|
||||
const from = oaiRequest["from"] as string;
|
||||
let fromDate = dayjs(from);
|
||||
const until = oaiRequest["until"] as string;
|
||||
let untilDate = dayjs(until);
|
||||
if (from.length != until.length) {
|
||||
throw new OaiModelException(
|
||||
StatusCodes.INTERNAL_SERVER_ERROR,
|
||||
"The request has different granularities for the from and until parameters.",
|
||||
OaiErrorCodes.BADARGUMENT,
|
||||
);
|
||||
}
|
||||
fromDate.hour() == 0 && (fromDate = fromDate.startOf("day"));
|
||||
untilDate.hour() == 0 && (untilDate = untilDate.endOf("day"));
|
||||
|
||||
andArray.push({
|
||||
server_date_published: {
|
||||
// [Sequelize.Op.between]: [fromDate, untilDate]
|
||||
[Sequelize.Op.and]: {
|
||||
[Sequelize.Op.gte]: fromDate.format("YYYY-MM-DD HH:mm:ss"),
|
||||
[Sequelize.Op.lte]: untilDate.format("YYYY-MM-DD HH:mm:ss"),
|
||||
},
|
||||
},
|
||||
});
|
||||
} else if ("from" in oaiRequest && !("until" in oaiRequest)) {
|
||||
const from = oaiRequest["from"] as string;
|
||||
let fromDate = dayjs(from);
|
||||
fromDate.hour() == 0 && (fromDate = fromDate.startOf("day"));
|
||||
|
||||
const now = dayjs();
|
||||
if (fromDate.isAfter(now)) {
|
||||
throw new OaiModelException(
|
||||
StatusCodes.INTERNAL_SERVER_ERROR,
|
||||
"Given from date is greater than now. The given values results in an empty list.",
|
||||
OaiErrorCodes.NORECORDSMATCH,
|
||||
);
|
||||
} else {
|
||||
// $finder->where('server_date_published', '>=', $fromDate);
|
||||
andArray.push({
|
||||
server_date_published: {
|
||||
[Sequelize.Op.gte]: fromDate.format("YYYY-MM-DD HH:mm:ss"),
|
||||
},
|
||||
});
|
||||
}
|
||||
} else if (!("from" in oaiRequest) && "until" in oaiRequest) {
|
||||
const until = oaiRequest["until"] as string;
|
||||
let untilDate = dayjs(until);
|
||||
untilDate.hour() == 0 && (untilDate = untilDate.endOf("day"));
|
||||
|
||||
const firstPublishedDataset: Dataset = (await Dataset.earliestPublicationDate()) as Dataset;
|
||||
const earliestPublicationDate = dayjs(firstPublishedDataset.server_date_published); //format("YYYY-MM-DDThh:mm:ss[Z]"));
|
||||
if (earliestPublicationDate.isAfter(untilDate)) {
|
||||
throw new OaiModelException(
|
||||
StatusCodes.INTERNAL_SERVER_ERROR,
|
||||
`earliestDatestamp is greater than given until date.
|
||||
The given values results in an empty list.`,
|
||||
OaiErrorCodes.NORECORDSMATCH,
|
||||
);
|
||||
} else {
|
||||
// $finder->where('server_date_published', '<=', $untilDate);
|
||||
andArray.push({
|
||||
server_date_published: {
|
||||
[Sequelize.Op.lte]: untilDate.format("YYYY-MM-DD HH:mm:ss"),
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
reldocIds = (
|
||||
await Dataset.findAll({
|
||||
attributes: ["publish_id"],
|
||||
where: andArray,
|
||||
order: ["publish_id"],
|
||||
include: includeArray,
|
||||
raw: true,
|
||||
// logging: (sql, queryObject) => {
|
||||
// const test = sql;
|
||||
// },
|
||||
})
|
||||
).map((dat) => dat.publish_id);
|
||||
// reldocIds = await Dataset.findAll({
|
||||
// // attributes: ["publish_id"],
|
||||
// where: andArray,
|
||||
// include: ["xmlCache"],
|
||||
// order: ["server_date_published"],
|
||||
|
||||
// // logging: (sql, queryObject) => {
|
||||
// // const test = sql;
|
||||
// // },
|
||||
// });
|
||||
totalIds = reldocIds.length; //184
|
||||
} //else resumptionToekn
|
||||
|
||||
// // handling of document ids
|
||||
const restIds = reldocIds as number[];
|
||||
const workIds = restIds.splice(0, maxRecords) as number[]; // array_splice(restIds, 0, maxRecords);
|
||||
|
||||
// no records returned
|
||||
if (workIds.length == 0) {
|
||||
// await tokenWorker.close();
|
||||
throw new OaiModelException(
|
||||
StatusCodes.INTERNAL_SERVER_ERROR,
|
||||
"The combination of the given values results in an empty list.",
|
||||
OaiErrorCodes.NORECORDSMATCH,
|
||||
);
|
||||
}
|
||||
|
||||
//foreach ($datasets as $dataset)
|
||||
const datasets: Dataset[] = await Dataset.findAll({
|
||||
// attributes: ["publish_id"],
|
||||
where: {
|
||||
publish_id: {
|
||||
[Sequelize.Op.in]: workIds,
|
||||
},
|
||||
},
|
||||
include: ["xmlCache"],
|
||||
order: ["publish_id"],
|
||||
});
|
||||
for (const dataset of datasets) {
|
||||
// let dataset = Dataset.findOne({
|
||||
// where: {'publish_id': dataId}
|
||||
// });
|
||||
await this.createXmlRecord(dataset, datasetNode);
|
||||
}
|
||||
|
||||
// store the further Ids in a resumption-file
|
||||
const countRestIds = restIds.length; //84
|
||||
if (countRestIds > 0) {
|
||||
const token = new ResumptionToken();
|
||||
token.StartPosition = start; //101
|
||||
token.TotalIds = totalIds; //184
|
||||
token.DocumentIds = restIds; //101 -184
|
||||
token.MetadataPrefix = metadataPrefix;
|
||||
|
||||
// $tokenWorker->storeResumptionToken($token);
|
||||
const res = await this.tokenWorker.set(token);
|
||||
|
||||
// set parameters for the resumptionToken-node
|
||||
// const res = token.ResumptionId;
|
||||
this.setParamResumption(res, cursor, totalIds);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set parameters for resumptionToken-line.
|
||||
*
|
||||
* @param string $res value of the resumptionToken
|
||||
* @param int $cursor value of the cursor
|
||||
* @param int $totalIds value of the total Ids
|
||||
*/
|
||||
private setParamResumption(res: string, cursor: number, totalIds: number) {
|
||||
const tomorrow = dayjs().add(1, "day").format("YYYY-MM-DDThh:mm:ss[Z]");
|
||||
this.xsltParameter["dateDelete"] = tomorrow;
|
||||
this.xsltParameter["res"] = res;
|
||||
this.xsltParameter["cursor"] = cursor;
|
||||
this.xsltParameter["totalIds"] = totalIds;
|
||||
}
|
||||
|
||||
private addSpecInformation(domNode: XMLBuilder, information: string) {
|
||||
domNode.ele("SetSpec").att("Value", information);
|
||||
}
|
||||
|
||||
private addLandingPageAttribute(domNode: XMLBuilder, dataid: string) {
|
||||
const baseDomain = process.env.BASE_DOMAIN || "localhost";
|
||||
const url = "https://" + this.getDomain(baseDomain) + "/dataset/" + dataid;
|
||||
// add attribute du dataset xml element
|
||||
domNode.att("landingpage", url);
|
||||
}
|
||||
|
||||
private getDomain(host: string): string {
|
||||
// $myhost = strtolower(trim($host));
|
||||
let myHost: string = host.trim().toLocaleLowerCase();
|
||||
// $count = substr_count($myhost, '.');
|
||||
const count: number = myHost.split(",").length - 1;
|
||||
|
||||
if (count == 2) {
|
||||
const words = myHost.split(".");
|
||||
if (words[1].length > 3) {
|
||||
myHost = myHost.split(".", 2)[1];
|
||||
}
|
||||
} else if (count > 2) {
|
||||
myHost = this.getDomain(myHost.split(".", 2)[1]);
|
||||
}
|
||||
myHost = myHost.replace(new RegExp(/^.*:\/\//i, "g"), "");
|
||||
return myHost;
|
||||
}
|
||||
|
||||
private getDocumentIdByIdentifier(oaiIdentifier: string): string {
|
||||
const identifierParts: string[] = oaiIdentifier.split(":"); // explode(":", $oaiIdentifier);
|
||||
const dataId: string = identifierParts[2];
|
||||
// switch (identifierParts[0]) {
|
||||
// case 'oai':
|
||||
// if (isset($identifierParts[2])) {
|
||||
// $dataId = $identifierParts[2];
|
||||
// }
|
||||
// break;
|
||||
// default:
|
||||
// throw new OaiModelException(
|
||||
// 'The prefix of the identifier argument is unknown.',
|
||||
// OaiModelError::BADARGUMENT
|
||||
// );
|
||||
// break;
|
||||
// }
|
||||
|
||||
// if (empty($dataId) or !preg_match('/^\d+$/', $dataId)) {
|
||||
// throw new OaiModelException(
|
||||
// 'The value of the identifier argument is unknown or illegal in this repository.',
|
||||
// OaiModelError::IDDOESNOTEXIST
|
||||
// );
|
||||
|
||||
return dataId;
|
||||
}
|
||||
|
||||
private async createXmlRecord(dataset: Dataset, datasetNode: XMLBuilder) {
|
||||
const domNode = await this.getDatasetXmlDomNode(dataset);
|
||||
|
||||
// add frontdoor url and data-type
|
||||
// if (dataset.publish_id) {
|
||||
dataset.publish_id && this.addLandingPageAttribute(domNode, dataset.publish_id.toString());
|
||||
// }
|
||||
this.addSpecInformation(domNode, "data-type:" + dataset.type);
|
||||
datasetNode.import(domNode);
|
||||
}
|
||||
|
||||
private async getDatasetXmlDomNode(dataset: Dataset) {
|
||||
// dataset.fetchValues();
|
||||
const xmlModel = new XmlModel(dataset);
|
||||
// xmlModel.setModel(dataset);
|
||||
xmlModel.excludeEmptyFields();
|
||||
// const cache = dataset.xmlCache ? dataset.xmlCache : new DocumentXmlCache();
|
||||
if (dataset.xmlCache) {
|
||||
xmlModel.setXmlCache = dataset.xmlCache;
|
||||
}
|
||||
xmlModel.caching = true;
|
||||
// return cache.getDomDocument();
|
||||
const domDocument = await xmlModel.getDomDocument();
|
||||
return domDocument;
|
||||
}
|
||||
|
||||
private async getSetsForProjects(): Promise<IDictionary> {
|
||||
// const setSpecPattern = this.SET_SPEC_PATTERN;
|
||||
const sets: { [key: string]: string } = {} as IDictionary;
|
||||
|
||||
const projects: Array<Project> = await Project.findAll({
|
||||
attributes: ["label"],
|
||||
raw: true,
|
||||
});
|
||||
projects.forEach((project) => {
|
||||
if (false == preg_match(this.sampleRegEx, project.label)) {
|
||||
const msg = `Invalid SetSpec (project='${project.label}').
|
||||
Allowed characters are [${this.sampleRegEx}].`;
|
||||
Logger.err(`OAI: ${msg}`);
|
||||
// Log::error("OAI-PMH: $msg");
|
||||
return;
|
||||
}
|
||||
const setSpec = "project:" + project.label;
|
||||
sets[setSpec] = `Set for project '${project.label}'`;
|
||||
});
|
||||
return sets;
|
||||
}
|
||||
|
||||
private async getSetsForDatasetTypes(): Promise<IDictionary> {
|
||||
const sets: { [key: string]: string } = {} as IDictionary;
|
||||
|
||||
const datasets: Array<Dataset> = await Dataset.findAll({
|
||||
attributes: ["type"],
|
||||
where: { server_state: { [Sequelize.Op.eq]: "published" } },
|
||||
});
|
||||
datasets.forEach((dataset) => {
|
||||
if (dataset.type && false == preg_match(this.sampleRegEx, dataset.type)) {
|
||||
const msg = `Invalid SetSpec (data-type='${dataset.type}').
|
||||
Allowed characters are [${this.sampleRegEx}].`;
|
||||
Logger.err(`OAI: ${msg}`);
|
||||
// Log::error("OAI-PMH: $msg");
|
||||
return;
|
||||
}
|
||||
const setSpec = "data-type:" + dataset.type;
|
||||
sets[setSpec] = `Set for document type '${dataset.type}'`;
|
||||
});
|
||||
return sets;
|
||||
}
|
||||
|
||||
private handleIllegalVerb() {
|
||||
this.xsltParameter["oai_error_code"] = "badVerb";
|
||||
this.xsltParameter["oai_error_message"] = "The verb provided in the request is illegal.";
|
||||
}
|
||||
}
|
11
src/exceptions/BadRequest.ts
Normal file
11
src/exceptions/BadRequest.ts
Normal file
|
@ -0,0 +1,11 @@
|
|||
import { StatusCodes } from "http-status-codes";
|
||||
import HTTPException from "./HttpException";
|
||||
|
||||
class BadRequestException extends HTTPException {
|
||||
constructor(message?: string) {
|
||||
super(StatusCodes.BAD_REQUEST, message || "bad Request");
|
||||
this.stack = "";
|
||||
}
|
||||
}
|
||||
|
||||
export default BadRequestException;
|
12
src/exceptions/HttpException.ts
Normal file
12
src/exceptions/HttpException.ts
Normal file
|
@ -0,0 +1,12 @@
|
|||
class HTTPException extends Error {
|
||||
public status: number;
|
||||
public message: string;
|
||||
|
||||
constructor(status: number, message: string) {
|
||||
super(message);
|
||||
this.status = status;
|
||||
this.message = message;
|
||||
}
|
||||
}
|
||||
|
||||
export default HTTPException;
|
11
src/exceptions/InternalServerError.ts
Normal file
11
src/exceptions/InternalServerError.ts
Normal file
|
@ -0,0 +1,11 @@
|
|||
import { StatusCodes } from "http-status-codes";
|
||||
import HTTPException from "./HttpException";
|
||||
|
||||
class InternalServerErrorException extends HTTPException {
|
||||
constructor(message?: string) {
|
||||
super(StatusCodes.INTERNAL_SERVER_ERROR, message || "Server Error");
|
||||
this.stack = "";
|
||||
}
|
||||
}
|
||||
|
||||
export default InternalServerErrorException;
|
11
src/exceptions/OaiErrorCodes.ts
Normal file
11
src/exceptions/OaiErrorCodes.ts
Normal file
|
@ -0,0 +1,11 @@
|
|||
export enum OaiErrorCodes {
|
||||
BADVERB = 1010,
|
||||
BADARGUMENT = 1011,
|
||||
CANNOTDISSEMINATEFORMAT = 1012,
|
||||
BADRESUMPTIONTOKEN = 1013,
|
||||
NORECORDSMATCH = 1014,
|
||||
IDDOESNOTEXIST = 1015,
|
||||
}
|
||||
|
||||
// 👇️ default export
|
||||
// export { OaiErrorCodes };
|
77
src/exceptions/OaiModelException.ts
Normal file
77
src/exceptions/OaiModelException.ts
Normal file
|
@ -0,0 +1,77 @@
|
|||
import { StatusCodes } from "http-status-codes";
|
||||
// import HTTPException from './HttpException';
|
||||
import { OaiErrorCodes } from "./OaiErrorCodes";
|
||||
|
||||
export class ErrorCode {
|
||||
public static readonly Unauthenticated = "Unauthenticated";
|
||||
public static readonly NotFound = "NotFound";
|
||||
public static readonly MaximumAllowedGrade = "MaximumAllowedGrade";
|
||||
public static readonly AsyncError = "AsyncError";
|
||||
public static readonly UnknownError = "UnknownError";
|
||||
}
|
||||
|
||||
export class ErrorModel {
|
||||
/**
|
||||
* Unique error code which identifies the error.
|
||||
*/
|
||||
public code: string;
|
||||
/**
|
||||
* Status code of the error.
|
||||
*/
|
||||
public status: number;
|
||||
/**
|
||||
* Any additional data that is required for translation.
|
||||
*/
|
||||
// public metaData?: any;
|
||||
}
|
||||
|
||||
export class OaiModelException extends Error {
|
||||
public status: number;
|
||||
public message: string;
|
||||
public oaiCode: number;
|
||||
|
||||
// constructor(status: number, message: string) {
|
||||
// super(message);
|
||||
// this.status = status;
|
||||
// this.message = message;
|
||||
// }
|
||||
constructor(status: number, message: string, oaiCode: number) {
|
||||
super(message);
|
||||
this.status = status;
|
||||
this.message = message;
|
||||
this.oaiCode = oaiCode;
|
||||
}
|
||||
// constructor(code: string = ErrorCode.UnknownError, message: any = null) {
|
||||
// super(code);
|
||||
// Object.setPrototypeOf(this, new.target.prototype);
|
||||
// this.name = code;
|
||||
// this.status = 500;
|
||||
// this.message = message;
|
||||
// // switch (code) {
|
||||
// // case ErrorCode.Unauthenticated:
|
||||
// // this.status = 401;
|
||||
// // break;
|
||||
// // case ErrorCode.MaximumAllowedGrade:
|
||||
// // this.status = 400;
|
||||
// // break;
|
||||
// // case ErrorCode.AsyncError:
|
||||
// // this.status = 400;
|
||||
// // break;
|
||||
// // case ErrorCode.NotFound:
|
||||
// // this.status = 404;
|
||||
// // break;
|
||||
// // default:
|
||||
// // this.status = 500;
|
||||
// // break;
|
||||
// // }
|
||||
// }
|
||||
}
|
||||
|
||||
export class BadOaiModelException extends OaiModelException {
|
||||
constructor(message?: string) {
|
||||
super(StatusCodes.INTERNAL_SERVER_ERROR, message || "bad Request", OaiErrorCodes.BADARGUMENT);
|
||||
this.stack = "";
|
||||
}
|
||||
}
|
||||
|
||||
// export default OaiModelexception;
|
11
src/exceptions/PageNotFoundException.ts
Normal file
11
src/exceptions/PageNotFoundException.ts
Normal file
|
@ -0,0 +1,11 @@
|
|||
import { StatusCodes } from "http-status-codes";
|
||||
import HTTPException from "./HttpException";
|
||||
|
||||
class PageNotFoundException extends HTTPException {
|
||||
constructor(message?: string) {
|
||||
super(StatusCodes.NOT_FOUND, message || "Page not found");
|
||||
this.stack = "";
|
||||
}
|
||||
}
|
||||
|
||||
export default PageNotFoundException;
|
23
src/exceptions/error-handler.ts
Normal file
23
src/exceptions/error-handler.ts
Normal file
|
@ -0,0 +1,23 @@
|
|||
import { Request, Response } from "express";
|
||||
// import { ErrorCode } from './error-code';
|
||||
// import { ErrorException } from './error-exception';
|
||||
// import { ErrorModel } from './error-model';
|
||||
import HTTPException from "./HttpException";
|
||||
import { StatusCodes } from "http-status-codes";
|
||||
import { OaiModelException } from "./OaiModelException";
|
||||
|
||||
export const errorHandler = (err: HTTPException | OaiModelException, req: Request, res: Response) => {
|
||||
console.log("Error handling middleware called.");
|
||||
console.log("Path:", req.path);
|
||||
console.error("Error occured:", err);
|
||||
if (err instanceof HTTPException) {
|
||||
console.log("Http Error is known.");
|
||||
res.status(err.status).send(err);
|
||||
} else if (err instanceof OaiModelException) {
|
||||
console.log("Oai-Error is known.");
|
||||
res.status(err.status).send(err);
|
||||
} else {
|
||||
// For unhandled errors.
|
||||
res.status(500).send({ code: StatusCodes.INTERNAL_SERVER_ERROR, status: 500 });
|
||||
}
|
||||
};
|
184
src/library/XmlModel.ts
Normal file
184
src/library/XmlModel.ts
Normal file
|
@ -0,0 +1,184 @@
|
|||
import DocumentXmlCache from "../models/DocumentXmlCache";
|
||||
// import { XMLDocument } from "xmlbuilder";
|
||||
import { XMLBuilder } from "xmlbuilder2/lib/interfaces";
|
||||
import Dataset from "../models/Dataset";
|
||||
import Logger from "jet-logger";
|
||||
import { create } from "xmlbuilder2";
|
||||
import dayjs from "dayjs";
|
||||
|
||||
/**
|
||||
* This is the description of the interface
|
||||
*
|
||||
* @interface Conf
|
||||
* @member {Model} model holds the current dataset model
|
||||
* @member {XMLBuilder} dom holds the current DOM representation
|
||||
*/
|
||||
export interface Conf {
|
||||
/**
|
||||
* Holds the current model either directly set or deserialized from XML.
|
||||
*/
|
||||
model: Dataset;
|
||||
|
||||
/**
|
||||
* Holds the current DOM representation.
|
||||
*/
|
||||
dom?: XMLBuilder;
|
||||
|
||||
/**
|
||||
* List of fields to skip on serialization.
|
||||
*/
|
||||
excludeFields: Array<string>;
|
||||
|
||||
/**
|
||||
* True, if empty fields get excluded from serialization.
|
||||
*/
|
||||
excludeEmpty: boolean;
|
||||
|
||||
/**
|
||||
* Base URI for xlink:ref elements
|
||||
*/
|
||||
baseUri: string;
|
||||
}
|
||||
|
||||
export default class XmlModel {
|
||||
// private config: { [key: string]: any } = {};
|
||||
private config: Conf; // = { excludeEmpty: false, baseUri: "" };
|
||||
// private strategy = null;
|
||||
private cache: DocumentXmlCache;
|
||||
private _caching = false;
|
||||
|
||||
constructor(dataset: Dataset) {
|
||||
// $this->strategy = new Strategy();// Opus_Model_Xml_Version1;
|
||||
// $this->config = new Conf();
|
||||
// $this->strategy->setup($this->config);
|
||||
|
||||
// this.strategy = new Strategy();
|
||||
this.config = {
|
||||
excludeEmpty: false,
|
||||
baseUri: "",
|
||||
excludeFields: [],
|
||||
model: dataset,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the Model for XML generation.
|
||||
*
|
||||
* @param \App\Models\Dataset model Model to serialize.
|
||||
*
|
||||
*/
|
||||
set setModel(model: Dataset) {
|
||||
this.config.model = model;
|
||||
// return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Define that empty fields (value===null) shall be excluded.
|
||||
*
|
||||
*/
|
||||
public excludeEmptyFields(): void {
|
||||
this.config.excludeEmpty = true;
|
||||
// return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return cache table.
|
||||
*
|
||||
* @returns {DocumentXmlCache}
|
||||
*/
|
||||
get getXmlCache(): DocumentXmlCache {
|
||||
return this.cache;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a new XML version with current configuration up.
|
||||
*
|
||||
* @param { DocumentXmlCache } cache table
|
||||
*/
|
||||
set setXmlCache(cache: DocumentXmlCache) {
|
||||
this.cache = cache;
|
||||
}
|
||||
|
||||
get caching(): boolean {
|
||||
return this._caching;
|
||||
}
|
||||
set caching(caching: boolean) {
|
||||
this._caching = caching;
|
||||
}
|
||||
|
||||
public async getDomDocument() {
|
||||
const dataset = this.config.model;
|
||||
|
||||
let domDocument: XMLBuilder | null = await this.getDomDocumentFromXmlCache();
|
||||
if (domDocument) {
|
||||
return domDocument;
|
||||
} else {
|
||||
//create domDocument during runtime
|
||||
// domDocument = $this->strategy->getDomDocument();
|
||||
domDocument = create({ version: "1.0", encoding: "UTF-8", standalone: true }, "<root></root>");
|
||||
}
|
||||
|
||||
//if caching isn't wanted return only dom Document
|
||||
if (this._caching != true) {
|
||||
return domDocument;
|
||||
//otherwise caching is desired:
|
||||
// save xml cache to db and return domDocument
|
||||
} else {
|
||||
// save new DocumentXmlCache
|
||||
if (!this.cache) {
|
||||
this.cache = new DocumentXmlCache();
|
||||
this.cache.document_id = dataset.id;
|
||||
}
|
||||
// if (!this.cache.document_id) {
|
||||
// this.cache.document_id = dataset.id;
|
||||
// }
|
||||
this.cache.xml_version = 1; // (int)$this->strategy->getVersion();
|
||||
this.cache.server_date_modified = dayjs(dataset.server_date_modified).format("YYYY-MM-DD HH:mm:ss");
|
||||
this.cache.xml_data = domDocument.end();
|
||||
|
||||
//save xml cache
|
||||
this.cache.save();
|
||||
// return newly created xml cache
|
||||
return domDocument;
|
||||
}
|
||||
}
|
||||
|
||||
private async getDomDocumentFromXmlCache(): Promise<XMLBuilder | null> {
|
||||
const dataset: Dataset = this.config.model;
|
||||
if (null == this.cache) {
|
||||
//$logger->debug(__METHOD__ . ' skipping cache for ' . get_class($model));
|
||||
// Log::debug(__METHOD__ . ' skipping cache for ' . get_class($dataset));
|
||||
Logger.warn(`__METHOD__ . skipping cache for ${dataset}`);
|
||||
return null;
|
||||
}
|
||||
// const dataset: Dataset = this.config.model;
|
||||
const actuallyCached: boolean = await DocumentXmlCache.hasValidEntry(dataset.id, dataset.server_date_modified);
|
||||
|
||||
//no cache or no actual cache
|
||||
if (true != actuallyCached) {
|
||||
Logger.warn(" cache missing for " + "#" + dataset.id);
|
||||
return null;
|
||||
}
|
||||
|
||||
//cache is actual return it for oai:
|
||||
// Log::debug(__METHOD__ . ' cache hit for ' . get_class($dataset) . '#' . $dataset->id);
|
||||
try {
|
||||
// return $this->_cache->get($model->getId(), (int) $this->_strategy->getVersion());
|
||||
// const cache = await DocumentXmlCache.findOne({
|
||||
// where: { document_id: dataset.id },
|
||||
// });
|
||||
if (this.cache) {
|
||||
return this.cache.getDomDocument();
|
||||
} else {
|
||||
Logger.warn(" Access to XML cache failed on " + dataset + "#" + dataset.id + ". Trying to recover.");
|
||||
return null;
|
||||
}
|
||||
// return this.cache.getDomDocument();
|
||||
} catch (error) {
|
||||
Logger.warn(" Access to XML cache failed on " + dataset + "#" + dataset.id + ". Trying to recover.");
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// export default XmlModel;
|
71
src/library/oai/OaiConfiguration.ts
Normal file
71
src/library/oai/OaiConfiguration.ts
Normal file
|
@ -0,0 +1,71 @@
|
|||
import config from "../../config/oai.config";
|
||||
|
||||
export default class Configuration {
|
||||
/**
|
||||
* Hold path where to store temporary resumption token files.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private _pathTokens = "";
|
||||
|
||||
private _maxListIds = 15;
|
||||
|
||||
/**
|
||||
* Return maximum number of listable identifiers per request.
|
||||
*
|
||||
* @return {number} Maximum number of listable identifiers per request.
|
||||
*/
|
||||
public get maxListIds(): number {
|
||||
return this._maxListIds;
|
||||
}
|
||||
public set maxListIds(value: number) {
|
||||
this._maxListIds = value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds maximum number of records to list per request.
|
||||
*
|
||||
* @var number
|
||||
*/
|
||||
private _maxListRecs = 15;
|
||||
|
||||
/**
|
||||
* Return maximum number of listable records per request.
|
||||
*
|
||||
* @return {number} Maximum number of listable records per request.
|
||||
*/
|
||||
public get maxListRecs() {
|
||||
return this._maxListRecs;
|
||||
}
|
||||
public set maxListRecs(value) {
|
||||
this._maxListRecs = value;
|
||||
}
|
||||
|
||||
constructor() {
|
||||
this._maxListIds = config.max.listidentifiers as number;
|
||||
this._maxListRecs = config.max.listrecords as number;
|
||||
// $this->maxListIds = config('oai.max.listidentifiers');
|
||||
// $this->maxListRecs = config('oai.max.listrecords');
|
||||
// $this->pathTokens = config('app.workspacePath')
|
||||
// . DIRECTORY_SEPARATOR .'tmp'
|
||||
// . DIRECTORY_SEPARATOR . 'resumption';
|
||||
}
|
||||
|
||||
/**
|
||||
* Return temporary path for resumption tokens.
|
||||
*
|
||||
* @returns {string} token path.
|
||||
*/
|
||||
get getResumptionTokenPath(): string {
|
||||
return this._pathTokens;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return maximum number of listable records per request.
|
||||
*
|
||||
* @return {number} Maximum number of listable records per request.
|
||||
*/
|
||||
// get getMaxListRecords(): number {
|
||||
// return this._maxListRecs;
|
||||
// }
|
||||
}
|
107
src/library/oai/ResumptionToken.ts
Normal file
107
src/library/oai/ResumptionToken.ts
Normal file
|
@ -0,0 +1,107 @@
|
|||
export default class ResumptionToken {
|
||||
/**
|
||||
* Holds dcoument ids
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private _documentIds: number[] = [];
|
||||
|
||||
/**
|
||||
* Holds metadata prefix information
|
||||
*
|
||||
* @var {string}
|
||||
*/
|
||||
private _metadataPrefix = "";
|
||||
|
||||
/**
|
||||
* Holds resumption id (only if token is stored)
|
||||
*
|
||||
* @var {string}
|
||||
*/
|
||||
private _resumptionId = "";
|
||||
|
||||
/**
|
||||
* Holds start postion
|
||||
*
|
||||
* @var {number}
|
||||
*/
|
||||
private _startPosition = 0;
|
||||
|
||||
/**
|
||||
* Holds total amount of document ids
|
||||
*
|
||||
* @var {number}
|
||||
*/
|
||||
private _totalIds = 0;
|
||||
|
||||
//#region properties
|
||||
|
||||
get Key(): string{
|
||||
return this.MetadataPrefix + this.StartPosition + this.TotalIds;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns current holded document ids.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public get DocumentIds(): number[] {
|
||||
return this._documentIds;
|
||||
}
|
||||
public set DocumentIds(idsToStore: number | number[]) {
|
||||
if (!Array.isArray(idsToStore)) {
|
||||
idsToStore = new Array(idsToStore);
|
||||
}
|
||||
this._documentIds = idsToStore;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns metadata prefix information.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public get MetadataPrefix(): string {
|
||||
return this._metadataPrefix;
|
||||
}
|
||||
public set MetadataPrefix(value) {
|
||||
this._metadataPrefix = value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return setted resumption id after successful storing of resumption token.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public get ResumptionId() {
|
||||
return this._resumptionId;
|
||||
}
|
||||
public set ResumptionId(resumptionId) {
|
||||
this._resumptionId = resumptionId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns start position.
|
||||
*
|
||||
* @return in
|
||||
*/
|
||||
public get StartPosition() {
|
||||
return this._startPosition;
|
||||
}
|
||||
public set StartPosition(startPosition) {
|
||||
this._startPosition = startPosition;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns total number of document ids for this request
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public get TotalIds() {
|
||||
return this._totalIds;
|
||||
}
|
||||
public set TotalIds(totalIds) {
|
||||
this._totalIds = totalIds;
|
||||
}
|
||||
|
||||
//#endregion properties
|
||||
}
|
194
src/library/oai/TokenWorker.ts
Normal file
194
src/library/oai/TokenWorker.ts
Normal file
|
@ -0,0 +1,194 @@
|
|||
import ResumptionToken from "./ResumptionToken";
|
||||
import { realpathSync } from "fs";
|
||||
import { createClient, RedisClientType } from "redis";
|
||||
import InternalServerErrorException from "../../exceptions/InternalServerError";
|
||||
import { sprintf } from "sprintf-js";
|
||||
import dayjs from "dayjs";
|
||||
import * as crypto from "crypto";
|
||||
|
||||
export default class TokenWorker {
|
||||
private resumptionPath = "";
|
||||
// private resumptionId = null;
|
||||
protected filePrefix = "rs_";
|
||||
protected fileExtension = "txt";
|
||||
|
||||
private cache: RedisClientType;
|
||||
private ttl: number;
|
||||
private url: string;
|
||||
private connected: boolean = false;
|
||||
|
||||
constructor(ttl: number) {
|
||||
// if (resPath) {
|
||||
// this.setResumptionPath(resPath);
|
||||
// }
|
||||
// [1] define ttl and create redis connection
|
||||
this.ttl = ttl;
|
||||
this.url = process.env.REDIS_URL || "redis://127.0.0.1:6379";
|
||||
|
||||
// this.cache.on("connect", () => {
|
||||
// console.log(`Redis connection established`);
|
||||
// });
|
||||
|
||||
// this.cache.on("error", (error: string) => {
|
||||
// console.error(`Redis error, service degraded: ${error}`);
|
||||
// });
|
||||
// The Redis client must be created in an async closure
|
||||
// (async () => {
|
||||
// this.cache = createClient({
|
||||
// url,
|
||||
// });
|
||||
// this.cache.on("error", (err) => console.log("[Redis] Redis Client Error: ", err));
|
||||
// await this.cache.connect();
|
||||
// console.log("[Redis]: Successfully connected to the Redis server");
|
||||
// })();
|
||||
}
|
||||
public async connect() {
|
||||
const url = process.env.REDIS_URL || "redis://localhost:6379";
|
||||
this.cache = createClient({
|
||||
url,
|
||||
});
|
||||
this.cache.on("error", (err) => {
|
||||
this.connected = false;
|
||||
console.log("[Redis] Redis Client Error: ", err);
|
||||
});
|
||||
this.cache.on("connect", () => {
|
||||
this.connected = true;
|
||||
// console.log(`Redis connection established`);
|
||||
});
|
||||
await this.cache.connect();
|
||||
}
|
||||
|
||||
public get Connected(): boolean {
|
||||
return this.connected;
|
||||
}
|
||||
|
||||
public async has(key: string): Promise<boolean> {
|
||||
const result = await this.cache.get(key);
|
||||
return result !== undefined && result !== null;
|
||||
}
|
||||
|
||||
public async set(token: ResumptionToken) {
|
||||
let fc = 0;
|
||||
const uniqueId = dayjs().unix().toString(); // 1548381600;
|
||||
let uniqueName: string;
|
||||
let cacheKeyExists = true;
|
||||
do {
|
||||
// format values
|
||||
// %s - String
|
||||
// %d - Signed decimal number (negative, zero or positive)
|
||||
// [0-9] (Specifies the minimum width held of to the variable value)
|
||||
uniqueName = sprintf("%s%05d", uniqueId, fc++);
|
||||
// let file = uniqueName;
|
||||
cacheKeyExists = await this.has(uniqueName);
|
||||
} while (cacheKeyExists);
|
||||
// uniqueName = this.checksum(token.Key);
|
||||
|
||||
const serialToken = JSON.stringify(token);
|
||||
await this.cache.setEx(uniqueName, this.ttl, serialToken);
|
||||
return uniqueName;
|
||||
|
||||
// token.ResumptionId = uniqueName;
|
||||
}
|
||||
|
||||
// public connected(): boolean {
|
||||
// return this.cache.connected;
|
||||
// }
|
||||
|
||||
public async get(key: string): Promise<ResumptionToken | null> {
|
||||
if (!this.cache) {
|
||||
throw new InternalServerErrorException("Dataset is not available for OAI export!");
|
||||
}
|
||||
|
||||
const result = await this.cache.get(key);
|
||||
if (result) {
|
||||
const rToken: ResumptionToken = new ResumptionToken();
|
||||
const parsed = JSON.parse(result);
|
||||
Object.assign(rToken, parsed);
|
||||
return rToken;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public del(key: string) {
|
||||
this.cache.del(key);
|
||||
}
|
||||
|
||||
public flush() {
|
||||
this.cache.flushAll();
|
||||
}
|
||||
public async close() {
|
||||
await this.cache.disconnect();
|
||||
this.connected = false;
|
||||
}
|
||||
|
||||
private checksum(str: string, algorithm?: string, encoding?: string): string {
|
||||
/**
|
||||
* @type {BinaryToTextEncoding}
|
||||
*/
|
||||
const ENCODING_OUT = "hex"; // Initializer type string is not assignable to variable type BinaryToTextEncoding
|
||||
|
||||
return crypto
|
||||
.createHash(algorithm || 'md5')
|
||||
.update(str, 'utf8')
|
||||
.digest(ENCODING_OUT)
|
||||
}
|
||||
|
||||
/**
|
||||
* Set resumption path where the resumption token files are stored.
|
||||
*
|
||||
* @throws Oai_Model_ResumptionTokenException Thrown if directory operations failed.
|
||||
* @return void
|
||||
*/
|
||||
public setResumptionPath(resPath: string): void {
|
||||
// expanding all symbolic links and resolving references
|
||||
const realPath = realpathSync(resPath);
|
||||
|
||||
// if (empty($realPath) or false === is_dir($realPath)) {
|
||||
// throw new Oai_Model_ResumptionTokenException(
|
||||
// 'Given resumption path "' . $resPath . '" (real path: "' . $realPath . '") is not a directory.'
|
||||
// );
|
||||
// }
|
||||
|
||||
// if (false === is_writable($realPath)) {
|
||||
// throw new Oai_Model_ResumptionTokenException(
|
||||
// 'Given resumption path "' . $resPath . '" (real path: "' . $realPath . '") is not writeable.'
|
||||
// );
|
||||
// }
|
||||
this.resumptionPath = realPath;
|
||||
}
|
||||
|
||||
/**
|
||||
* Store a resumption token
|
||||
*
|
||||
* @param Oai_Model_Resumptiontoken $token Token to store.
|
||||
* @throws Oai_Model_ResumptionTokenException Thrown on file operation error.
|
||||
* @return void
|
||||
*/
|
||||
public storeResumptionToken(token: ResumptionToken): void {
|
||||
// $fileName = $this->generateResumptionName();
|
||||
const uniqueName = "100";
|
||||
const serialToken = JSON.stringify(token);
|
||||
|
||||
// Cache::put($uniqueName, $serialToken, now()->addMinutes(60));
|
||||
this.cache.setEx(uniqueName, 86400, serialToken);
|
||||
// $token->setResumptionId($this->resumptionId);
|
||||
}
|
||||
|
||||
// private async get(key: string) {
|
||||
// return await this.redisClient.get(key);
|
||||
// }
|
||||
|
||||
// public async getResumptionToken(resId: string): Promise<ResumptionToken | null> {
|
||||
// let token: ResumptionToken | null = null;
|
||||
|
||||
// var data = await this.get(resId);
|
||||
// if (data) {
|
||||
// token = JSON.parse(data);
|
||||
// if (token instanceof ResumptionToken) {
|
||||
// return token;
|
||||
// }
|
||||
// }
|
||||
// return token;
|
||||
// }
|
||||
}
|
155
src/models/Dataset.ts
Normal file
155
src/models/Dataset.ts
Normal file
|
@ -0,0 +1,155 @@
|
|||
// import {
|
||||
// Table,
|
||||
// Column,
|
||||
// DataType,
|
||||
// IsEmail,
|
||||
// HasMany,
|
||||
// Model,
|
||||
// CreatedAt, UpdatedAt,
|
||||
// BelongsToMany,
|
||||
// BelongsTo
|
||||
// } from "sequelize-typescript";
|
||||
// import { Abstract } from "./Abstract";
|
||||
|
||||
// @Table({
|
||||
// tableName: "documents"
|
||||
// })
|
||||
// export class Dataset extends Model<Dataset> {
|
||||
|
||||
// @Column({
|
||||
// // field: 'type',
|
||||
// type: DataType.STRING(255),
|
||||
// allowNull: false,
|
||||
// })
|
||||
// type!: string;
|
||||
|
||||
// @CreatedAt
|
||||
// @Column({
|
||||
// field: 'created_at',
|
||||
// type: DataType.DATE,
|
||||
// defaultValue: DataType.NOW
|
||||
// })
|
||||
// created_at!: Date;
|
||||
|
||||
// @UpdatedAt
|
||||
// @Column({
|
||||
// field: 'server_date_modified',
|
||||
// type: DataType.DATE,
|
||||
// defaultValue: DataType.NOW
|
||||
// })
|
||||
// server_date_modified!: Date;
|
||||
|
||||
// @Column({
|
||||
// field: 'server_state',
|
||||
// type: DataType.STRING(),
|
||||
// })
|
||||
// serverState!: string;
|
||||
|
||||
// @HasMany(() => Abstract, "document_id")
|
||||
// public readonly abstracts?: Abstract[];
|
||||
// }
|
||||
|
||||
import { Op, Model, DataTypes, InferAttributes, InferCreationAttributes, CreationOptional, NonAttribute, Association } from "sequelize";
|
||||
import sequelizeConnection from "../config/db.config";
|
||||
import DocumentXmlCache from "./DocumentXmlCache";
|
||||
|
||||
class Dataset extends Model<InferAttributes<Dataset>, InferCreationAttributes<Dataset>> {
|
||||
// id can be undefined during creation when using `autoIncrement`
|
||||
declare id: CreationOptional<number>;
|
||||
declare contributing_corporation: string | null; // for nullable fields
|
||||
declare creating_corporation: string; // not nullable fields
|
||||
declare publisher_name: string | null;
|
||||
declare embargo_date: Date | null;
|
||||
declare publish_id: number | null;
|
||||
|
||||
declare type: string | null;
|
||||
declare language: string | null; // for nullable fields
|
||||
declare server_state: string | null;
|
||||
declare server_date_published: Date | null;
|
||||
|
||||
// createdAt can be undefined during creation
|
||||
declare created_at: CreationOptional<Date>;
|
||||
// updatedAt can be undefined during creation
|
||||
declare server_date_modified: CreationOptional<Date>;
|
||||
|
||||
// You can also pre-declare possible inclusions, these will only be populated if you
|
||||
// actively include a relation.
|
||||
declare xmlCache?: NonAttribute<DocumentXmlCache>; // Note this is optional since it's only populated when explicitly requested in code
|
||||
|
||||
// getters that are not attributes should be tagged using NonAttribute
|
||||
// to remove them from the model's Attribute Typings.
|
||||
get fullName(): NonAttribute<string | null> {
|
||||
return this.type;
|
||||
}
|
||||
|
||||
declare static associations: {
|
||||
xmlCache: Association<Dataset, DocumentXmlCache>;
|
||||
};
|
||||
|
||||
public static async earliestPublicationDate(): Promise<Dataset | null> {
|
||||
const server_state = "published";
|
||||
const condition = {
|
||||
[Op.and]: [
|
||||
{
|
||||
server_state: { [Op.eq]: server_state },
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
const model = await this.findOne({
|
||||
attributes: ["server_date_published"],
|
||||
where: condition,
|
||||
order: [["server_date_published", "asc"]],
|
||||
});
|
||||
if (model) {
|
||||
return model;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Dataset.init(
|
||||
{
|
||||
id: {
|
||||
type: DataTypes.INTEGER,
|
||||
primaryKey: true,
|
||||
},
|
||||
contributing_corporation: { type: DataTypes.STRING(255) },
|
||||
creating_corporation: { type: DataTypes.STRING(255), allowNull: false },
|
||||
publisher_name: DataTypes.STRING(255),
|
||||
embargo_date: DataTypes.DATE,
|
||||
publish_id: DataTypes.INTEGER,
|
||||
|
||||
type: {
|
||||
type: DataTypes.STRING,
|
||||
},
|
||||
language: DataTypes.STRING,
|
||||
server_state: {
|
||||
type: DataTypes.STRING,
|
||||
},
|
||||
server_date_published: DataTypes.DATE,
|
||||
|
||||
// project_id: DataTypes.INTEGER,
|
||||
// embargo_date: DataTypes.DATE,
|
||||
// belongs_to_bibliography: DataTypes.BOOLEAN,
|
||||
// editor_id: DataTypes.INTEGER,
|
||||
// preferred_reviewer: DataTypes.STRING,
|
||||
// preferred_reviewer_email: DataTypes.STRING,
|
||||
// reviewer_id: DataTypes.INTEGER,
|
||||
// reject_reviewer_note: DataTypes.STRING,
|
||||
// reject_editor_note: DataTypes.STRING,
|
||||
// reviewer_note_visible: DataTypes.BOOLEAN,
|
||||
|
||||
created_at: DataTypes.DATE,
|
||||
server_date_modified: DataTypes.DATE,
|
||||
},
|
||||
{
|
||||
createdAt: "created_at",
|
||||
updatedAt: "server_date_modified",
|
||||
sequelize: sequelizeConnection,
|
||||
tableName: "documents",
|
||||
},
|
||||
);
|
||||
|
||||
export default Dataset;
|
167
src/models/DocumentXmlCache.ts
Normal file
167
src/models/DocumentXmlCache.ts
Normal file
|
@ -0,0 +1,167 @@
|
|||
// import {
|
||||
// Table,
|
||||
// PrimaryKey,
|
||||
// Column,
|
||||
// DataType,
|
||||
// Model,
|
||||
// UpdatedAt,
|
||||
// } from "sequelize-typescript";
|
||||
|
||||
// @Table({
|
||||
// tableName: "document_xml_cache",
|
||||
// createdAt: false,
|
||||
// })
|
||||
// export default class DocumentXmlCache extends Model<DocumentXmlCache> {
|
||||
|
||||
// @PrimaryKey
|
||||
// @Column({
|
||||
// type: DataType.INTEGER,
|
||||
// allowNull: false,
|
||||
// })
|
||||
// declare document_id: number;
|
||||
|
||||
// @Column({
|
||||
// type: DataType.INTEGER,
|
||||
// allowNull: false,
|
||||
// })
|
||||
// declare xml_version: number;
|
||||
|
||||
// @UpdatedAt
|
||||
// @Column({
|
||||
// field: 'server_date_modified',
|
||||
// type: DataType.DATE,
|
||||
// defaultValue: DataType.NOW
|
||||
// })
|
||||
// declare server_date_modified: Date;
|
||||
|
||||
// @Column({
|
||||
// type: DataType.TEXT(),
|
||||
// })
|
||||
// declare xml_data: string;
|
||||
// }
|
||||
|
||||
import { Op, Model, DataTypes, InferAttributes, InferCreationAttributes } from "sequelize";
|
||||
import sequelizeConnection from "../config/db.config";
|
||||
import { builder, create } from "xmlbuilder2";
|
||||
import { XMLBuilder } from "xmlbuilder2/lib/interfaces";
|
||||
// import { select, SelectedValue } from "xpath";
|
||||
import dayjs from "dayjs";
|
||||
|
||||
class DocumentXmlCache extends Model<InferAttributes<DocumentXmlCache>, InferCreationAttributes<DocumentXmlCache>> {
|
||||
declare document_id: number;
|
||||
declare xml_version: number;
|
||||
|
||||
// updatedAt can be undefined during creation
|
||||
// declare server_date_modified: CreationOptional<Date>;
|
||||
declare server_date_modified: string;
|
||||
|
||||
declare xml_data: string;
|
||||
|
||||
// // getters that are not attributes should be tagged using NonAttribute
|
||||
// // to remove them from the model's Attribute Typings.
|
||||
// get fullName(): NonAttribute<string | null> {
|
||||
// return this.type;
|
||||
// }
|
||||
|
||||
/**
|
||||
* Check if a dataset in a specific xml version is already cached or not.
|
||||
*
|
||||
* @param mixed datasetId
|
||||
* @param mixed serverDateModified
|
||||
* @returns {bool} Returns true on cached hit else false.
|
||||
*/
|
||||
public static async hasValidEntry(datasetId: number, datasetServerDateModified: Date): Promise<boolean> {
|
||||
const condition = {
|
||||
[Op.and]: [
|
||||
{
|
||||
document_id: { [Op.eq]: datasetId },
|
||||
server_date_modified: { [Op.eq]: dayjs(datasetServerDateModified).format("YYYY-MM-DD HH:mm:ss") },
|
||||
},
|
||||
],
|
||||
};
|
||||
// $select = DB::table('document_xml_cache');
|
||||
// $select->where('document_id', '=', $datasetId)
|
||||
// ->where('server_date_modified', '=', $serverDateModified);
|
||||
|
||||
const model = await this.findOne({
|
||||
where: condition,
|
||||
order: [["server_date_modified", "asc"]],
|
||||
});
|
||||
if (model) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get dom document of 'xml_data' string
|
||||
*
|
||||
* @returns {XMLBuilder}
|
||||
*/
|
||||
public getDomDocument(): XMLBuilder {
|
||||
// const dom = xmlbuilder.create({ version: "1.0", encoding: "UTF-8", standalone: true });
|
||||
let dom: XMLBuilder = create({ version: "1.0", encoding: "UTF-8", standalone: true }, this.xml_data);
|
||||
// return dom.first();
|
||||
|
||||
const rdrDataset = dom.find(
|
||||
(n) => {
|
||||
const test = n.node.nodeName == "Rdr_Dataset";
|
||||
return test;
|
||||
},
|
||||
false,
|
||||
true,
|
||||
)?.node;
|
||||
|
||||
if (rdrDataset == undefined) {
|
||||
return dom.first();
|
||||
} else {
|
||||
dom = builder({ version: "1.0", encoding: "UTF-8", standalone: true }, rdrDataset);
|
||||
return dom;
|
||||
}
|
||||
// const doc2 = create().doc();
|
||||
// rdrDataset && doc2.import(rdrDataset);
|
||||
// return doc2.first();
|
||||
// rdrDataset && (dom = builder({ version: '1.0', encoding: 'UTF-8', standalone : true }, rdrDataset));
|
||||
|
||||
// let domNode: Node = dom.node;
|
||||
// // run the xpath query
|
||||
// const result: Array<SelectedValue> = select("//Rdr_Dataset", domNode);
|
||||
// if (result && result.length && result.length > 0) {
|
||||
// // convert the DOM node to a builder object
|
||||
|
||||
// const recordNode = builder(result.at(0));
|
||||
// const doc2 = create().doc();
|
||||
// // import into result document
|
||||
// doc2.import(recordNode);
|
||||
// // console.log(doc2.end({ prettyPrint: true }));
|
||||
// return doc2;
|
||||
// }
|
||||
// return dom.first();
|
||||
}
|
||||
}
|
||||
|
||||
DocumentXmlCache.init(
|
||||
{
|
||||
document_id: {
|
||||
type: DataTypes.INTEGER,
|
||||
primaryKey: true,
|
||||
},
|
||||
xml_version: {
|
||||
type: DataTypes.INTEGER,
|
||||
allowNull: false,
|
||||
},
|
||||
server_date_modified: DataTypes.STRING(50),
|
||||
xml_data: {
|
||||
type: DataTypes.TEXT,
|
||||
},
|
||||
},
|
||||
{
|
||||
createdAt: false,
|
||||
updatedAt: false, //"server_date_modified",
|
||||
sequelize: sequelizeConnection,
|
||||
tableName: "document_xml_cache",
|
||||
},
|
||||
);
|
||||
|
||||
export default DocumentXmlCache;
|
43
src/models/Identifier.ts
Normal file
43
src/models/Identifier.ts
Normal file
|
@ -0,0 +1,43 @@
|
|||
import { Model, DataTypes, InferAttributes, InferCreationAttributes, CreationOptional } from "sequelize";
|
||||
import sequelizeConnection from "../config/db.config";
|
||||
|
||||
class Identifier extends Model<InferAttributes<Identifier>, InferCreationAttributes<Identifier>> {
|
||||
// id can be undefined during creation when using `autoIncrement`
|
||||
declare id: CreationOptional<number>;
|
||||
declare value: string; // not nullable fields
|
||||
declare type: string;
|
||||
declare status: string | null;
|
||||
|
||||
// createdAt can be undefined during creation
|
||||
declare created_at: CreationOptional<Date>;
|
||||
// updatedAt can be undefined during creation
|
||||
declare updated_at: CreationOptional<Date>;
|
||||
}
|
||||
|
||||
Identifier.init(
|
||||
{
|
||||
id: {
|
||||
type: DataTypes.INTEGER,
|
||||
primaryKey: true,
|
||||
},
|
||||
value: {
|
||||
type: DataTypes.STRING(255),
|
||||
allowNull: false,
|
||||
},
|
||||
type: {
|
||||
type: DataTypes.STRING(255),
|
||||
allowNull: false,
|
||||
},
|
||||
status: DataTypes.STRING(255),
|
||||
created_at: DataTypes.DATE,
|
||||
updated_at: DataTypes.DATE,
|
||||
},
|
||||
{
|
||||
createdAt: "created_at",
|
||||
updatedAt: "updated_at",
|
||||
tableName: "dataset_identifiers",
|
||||
sequelize: sequelizeConnection,
|
||||
},
|
||||
);
|
||||
|
||||
export default Identifier;
|
64
src/models/Person.ts
Normal file
64
src/models/Person.ts
Normal file
|
@ -0,0 +1,64 @@
|
|||
import { Model, DataTypes, InferAttributes, InferCreationAttributes, CreationOptional } from "sequelize";
|
||||
import sequelizeConnection from "../config/db.config";
|
||||
|
||||
class Person extends Model<InferAttributes<Person>, InferCreationAttributes<Person>> {
|
||||
// id can be undefined during creation when using `autoIncrement`
|
||||
declare id: CreationOptional<number>;
|
||||
declare academic_title: string | null; // for nullable fields
|
||||
declare email: string; // not nullable fields
|
||||
declare first_name: string | null; // for nullable fields
|
||||
declare last_name: string;
|
||||
declare place_of_birth: string | null;
|
||||
declare identifier_orcid: string | null;
|
||||
declare identifier_gnd: string | null;
|
||||
declare identifier_misc: string | null;
|
||||
declare status: boolean | null;
|
||||
declare name_type: string | null; // for nullable fields
|
||||
|
||||
// getters that are not attributes should be tagged using NonAttribute
|
||||
// to remove them from the model's Attribute Typings.
|
||||
get full_name(): string {
|
||||
return this.first_name + " " + this.last_name;
|
||||
}
|
||||
}
|
||||
|
||||
Person.init(
|
||||
{
|
||||
id: {
|
||||
type: DataTypes.INTEGER,
|
||||
primaryKey: true,
|
||||
},
|
||||
academic_title: DataTypes.STRING(255),
|
||||
email: {
|
||||
type: DataTypes.STRING(100),
|
||||
allowNull: false,
|
||||
},
|
||||
first_name: DataTypes.STRING(255),
|
||||
last_name: {
|
||||
type: DataTypes.STRING(255),
|
||||
allowNull: false,
|
||||
},
|
||||
place_of_birth: DataTypes.STRING(255),
|
||||
identifier_orcid: DataTypes.STRING(50),
|
||||
identifier_gnd: DataTypes.STRING(50),
|
||||
identifier_misc: DataTypes.STRING(50),
|
||||
status: {
|
||||
type: DataTypes.BOOLEAN,
|
||||
defaultValue: true,
|
||||
},
|
||||
name_type: DataTypes.STRING(255),
|
||||
full_name: {
|
||||
type: new DataTypes.VIRTUAL(DataTypes.STRING(255), ["first_name", "last_name"]),
|
||||
get(): string {
|
||||
return this.first_name + " " + this.last_name;
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
timestamps: false,
|
||||
tableName: "persons",
|
||||
sequelize: sequelizeConnection,
|
||||
},
|
||||
);
|
||||
|
||||
export default Person;
|
41
src/models/Project.ts
Normal file
41
src/models/Project.ts
Normal file
|
@ -0,0 +1,41 @@
|
|||
// import Sequelize from "sequelize";
|
||||
import { Model, DataTypes, InferAttributes, InferCreationAttributes, CreationOptional } from "sequelize";
|
||||
import sequelizeConnection from "../config/db.config";
|
||||
|
||||
class Project extends Model<InferAttributes<Project>, InferCreationAttributes<Project>> {
|
||||
// id can be undefined during creation when using `autoIncrement`
|
||||
declare id: CreationOptional<number>;
|
||||
declare label: string; // not nullable fields
|
||||
declare name: string;
|
||||
|
||||
// createdAt can be undefined during creation
|
||||
declare created_at: CreationOptional<Date>;
|
||||
// updatedAt can be undefined during creation
|
||||
declare updated_at: CreationOptional<Date>;
|
||||
}
|
||||
|
||||
Project.init(
|
||||
{
|
||||
id: {
|
||||
type: DataTypes.INTEGER,
|
||||
primaryKey: true,
|
||||
},
|
||||
label: {
|
||||
type: DataTypes.STRING(50),
|
||||
allowNull: false,
|
||||
},
|
||||
name: {
|
||||
type: DataTypes.STRING(255),
|
||||
allowNull: false,
|
||||
},
|
||||
created_at: DataTypes.DATE,
|
||||
updated_at: DataTypes.DATE,
|
||||
},
|
||||
{
|
||||
createdAt: "created_at",
|
||||
updatedAt: "updated_at",
|
||||
sequelize: sequelizeConnection,
|
||||
tableName: "projects",
|
||||
},
|
||||
);
|
||||
export default Project;
|
32
src/models/abstract.model.js
Normal file
32
src/models/abstract.model.js
Normal file
|
@ -0,0 +1,32 @@
|
|||
import Sequelize from "sequelize";
|
||||
import sequelizeConnection from "../config/db.config";
|
||||
|
||||
const Abstract = sequelizeConnection.define(
|
||||
"dataset_abstracts",
|
||||
{
|
||||
type: {
|
||||
type: Sequelize.STRING,
|
||||
allowNull: false,
|
||||
length: 255,
|
||||
},
|
||||
value: {
|
||||
type: Sequelize.TEXT,
|
||||
allowNull: false,
|
||||
},
|
||||
language: {
|
||||
type: Sequelize.STRING,
|
||||
allowNull: false,
|
||||
length: 3,
|
||||
},
|
||||
},
|
||||
{
|
||||
timestamps: false,
|
||||
tableName: "dataset_abstracts",
|
||||
},
|
||||
);
|
||||
// Abstract.belongsTo(Dataset, {
|
||||
// foreignKey: "document_id",
|
||||
// as: "dataset",
|
||||
// });
|
||||
|
||||
export default Abstract;
|
34
src/models/coverage.model.js
Normal file
34
src/models/coverage.model.js
Normal file
|
@ -0,0 +1,34 @@
|
|||
import Sequelize from "sequelize";
|
||||
import sequelizeConnection from "../config/db.config";
|
||||
|
||||
// module.exports = (sequelize, DataTypes) => {
|
||||
|
||||
const Coverage = sequelizeConnection.define(
|
||||
"coverage",
|
||||
{
|
||||
elevation_min: Sequelize.INTEGER,
|
||||
elevation_max: Sequelize.INTEGER,
|
||||
elevation_absolut: Sequelize.INTEGER,
|
||||
depth_min: Sequelize.INTEGER,
|
||||
depth_max: Sequelize.INTEGER,
|
||||
depth_absolut: Sequelize.INTEGER,
|
||||
time_min: Sequelize.INTEGER,
|
||||
time_max: Sequelize.INTEGER,
|
||||
time_absolut: Sequelize.INTEGER,
|
||||
x_min: Sequelize.DOUBLE,
|
||||
x_max: Sequelize.DOUBLE,
|
||||
y_min: Sequelize.DOUBLE,
|
||||
y_max: Sequelize.DOUBLE,
|
||||
created_at: Sequelize.DATE,
|
||||
updated_at: Sequelize.DATE,
|
||||
},
|
||||
{
|
||||
createdAt: "created_at",
|
||||
updatedAt: "updated_at",
|
||||
// schema: 'public',
|
||||
tableName: "coverage",
|
||||
},
|
||||
);
|
||||
|
||||
export default Coverage;
|
||||
// };
|
49
src/models/dataset.model.js
Normal file
49
src/models/dataset.model.js
Normal file
|
@ -0,0 +1,49 @@
|
|||
import Sequelize from "sequelize";
|
||||
import sequelizeConnection from "../config/db.config";
|
||||
|
||||
const Dataset = sequelizeConnection.define(
|
||||
"documents",
|
||||
{
|
||||
type: {
|
||||
type: Sequelize.STRING,
|
||||
},
|
||||
language: Sequelize.STRING,
|
||||
server_state: {
|
||||
type: Sequelize.STRING,
|
||||
},
|
||||
server_date_published: Sequelize.DATE,
|
||||
publisher_name: Sequelize.STRING,
|
||||
publish_id: Sequelize.INTEGER,
|
||||
creating_corporation: Sequelize.STRING,
|
||||
project_id: Sequelize.INTEGER,
|
||||
embargo_date: Sequelize.DATE,
|
||||
belongs_to_bibliography: Sequelize.BOOLEAN,
|
||||
editor_id: Sequelize.INTEGER,
|
||||
preferred_reviewer: Sequelize.STRING,
|
||||
preferred_reviewer_email: Sequelize.STRING,
|
||||
reviewer_id: Sequelize.INTEGER,
|
||||
reject_reviewer_note: Sequelize.STRING,
|
||||
reject_editor_note: Sequelize.STRING,
|
||||
reviewer_note_visible: Sequelize.BOOLEAN,
|
||||
|
||||
created_at: Sequelize.DATE,
|
||||
server_date_modified: Sequelize.DATE,
|
||||
},
|
||||
{
|
||||
createdAt: "created_at",
|
||||
updatedAt: "server_date_modified",
|
||||
// schema: 'public',
|
||||
tableName: "documents",
|
||||
},
|
||||
);
|
||||
// // relations
|
||||
// Dataset.hasMany(Title, {
|
||||
// as: "titles",
|
||||
// foreignKey: "document_id"
|
||||
// });
|
||||
// Title.belongsTo(Dataset, {
|
||||
// foreignKey: "document_id",
|
||||
// as: "dataset",
|
||||
// });
|
||||
|
||||
export default Dataset;
|
44
src/models/file.model.js
Normal file
44
src/models/file.model.js
Normal file
|
@ -0,0 +1,44 @@
|
|||
import Sequelize from "sequelize";
|
||||
import sequelizeConnection from "../config/db.config";
|
||||
|
||||
const File = sequelizeConnection.define(
|
||||
"document_files",
|
||||
{
|
||||
path_name: {
|
||||
type: Sequelize.STRING,
|
||||
allowNull: false,
|
||||
length: 100,
|
||||
},
|
||||
label: Sequelize.STRING(100),
|
||||
comment: Sequelize.STRING(255),
|
||||
mime_type: Sequelize.STRING(255),
|
||||
language: Sequelize.STRING(3),
|
||||
file_size: {
|
||||
type: Sequelize.BIGINT,
|
||||
allowNull: false,
|
||||
},
|
||||
visible_in_frontdoor: {
|
||||
type: Sequelize.BOOLEAN,
|
||||
allowNull: false,
|
||||
defaultValue: true,
|
||||
},
|
||||
visible_in_oai: {
|
||||
type: Sequelize.BOOLEAN,
|
||||
allowNull: false,
|
||||
defaultValue: true,
|
||||
},
|
||||
sort_order: {
|
||||
type: Sequelize.INTEGER,
|
||||
allowNull: false,
|
||||
},
|
||||
created_at: Sequelize.DATE,
|
||||
updated_at: Sequelize.DATE,
|
||||
},
|
||||
{
|
||||
createdAt: "created_at",
|
||||
updatedAt: "updated_at",
|
||||
tableName: "document_files",
|
||||
},
|
||||
);
|
||||
|
||||
export default File;
|
210
src/models/init-models.js
Normal file
210
src/models/init-models.js
Normal file
|
@ -0,0 +1,210 @@
|
|||
// import Sequelize from 'sequelize'
|
||||
import sequelizeConnection from "../config/db.config";
|
||||
import Dataset from "./Dataset";
|
||||
import Title from "./title.model.js";
|
||||
import Abstract from "./abstract.model.js";
|
||||
import Coverage from "./coverage.model.js";
|
||||
import License from "./license.model.js";
|
||||
import User from "./user.model.js";
|
||||
// import Person from "./person.model.js";
|
||||
import Person from "./Person";
|
||||
import { Sequelize } from "sequelize";
|
||||
import Subject from "./subject.model.js";
|
||||
import Project from "./Project";
|
||||
import File from "./file.model.js";
|
||||
import Identifier from "./Identifier";
|
||||
import DocumentXmlCache from "./DocumentXmlCache";
|
||||
|
||||
const dbContext = initModels();
|
||||
|
||||
export { Dataset, Title, Abstract, User, Person, Subject, Coverage, License, Project, Identifier, DocumentXmlCache };
|
||||
export default dbContext;
|
||||
|
||||
export function initModels() {
|
||||
// title identifier
|
||||
Dataset.hasOne(Identifier, {
|
||||
as: "identifier",
|
||||
foreignKey: "dataset_id",
|
||||
});
|
||||
Identifier.belongsTo(Dataset, {
|
||||
foreignKey: "dataset_id",
|
||||
as: "dataset",
|
||||
});
|
||||
|
||||
// title relations
|
||||
Dataset.hasMany(Title, {
|
||||
as: "titles",
|
||||
foreignKey: "document_id",
|
||||
});
|
||||
Title.belongsTo(Dataset, {
|
||||
foreignKey: "document_id",
|
||||
as: "dataset",
|
||||
});
|
||||
|
||||
// abstract relations
|
||||
Dataset.hasMany(Abstract, {
|
||||
as: "abstracts",
|
||||
foreignKey: "document_id",
|
||||
});
|
||||
Abstract.belongsTo(Dataset, {
|
||||
foreignKey: "document_id",
|
||||
as: "dataset",
|
||||
});
|
||||
|
||||
//user relations
|
||||
User.hasMany(Dataset, {
|
||||
foreignKey: "account_id",
|
||||
as: "datasets",
|
||||
});
|
||||
Dataset.belongsTo(User, {
|
||||
foreignKey: "account_id",
|
||||
as: "user",
|
||||
});
|
||||
|
||||
// authors and contributors
|
||||
const DocumentPersons = sequelizeConnection.define(
|
||||
"link_documents_persons",
|
||||
{
|
||||
person_id: { type: Sequelize.INTEGER, primaryKey: true },
|
||||
document_id: { type: Sequelize.INTEGER, primaryKey: true },
|
||||
// role: { type: Sequelize.STRING(255), defaultValue: "other", primaryKey: true},
|
||||
role: {
|
||||
type: Sequelize.ENUM("author", "contributor", "other"),
|
||||
// scopes: {
|
||||
// author: ["author"],
|
||||
// contributor: ["contributor"],
|
||||
// },
|
||||
},
|
||||
sort_order: Sequelize.INTEGER,
|
||||
allow_email_contact: Sequelize.BOOLEAN,
|
||||
contributor_type: Sequelize.STRING(255),
|
||||
},
|
||||
{
|
||||
// schema: 'public',
|
||||
tableName: "link_documents_persons",
|
||||
timestamps: false,
|
||||
},
|
||||
);
|
||||
|
||||
// relation authors
|
||||
Dataset.belongsToMany(Person, {
|
||||
through: {
|
||||
model: DocumentPersons,
|
||||
// scope: { role: "author" },
|
||||
},
|
||||
as: "authors",
|
||||
foreignKey: "document_id",
|
||||
});
|
||||
Person.belongsToMany(Dataset, {
|
||||
through: {
|
||||
model: DocumentPersons,
|
||||
},
|
||||
foreignKey: "person_id",
|
||||
as: "a_datasets",
|
||||
});
|
||||
|
||||
// relation contributors
|
||||
Dataset.belongsToMany(Person, {
|
||||
through: {
|
||||
model: DocumentPersons,
|
||||
},
|
||||
as: "contributors",
|
||||
foreignKey: "document_id",
|
||||
});
|
||||
Person.belongsToMany(Dataset, {
|
||||
through: {
|
||||
model: DocumentPersons,
|
||||
},
|
||||
foreignKey: "person_id",
|
||||
as: "c_datasets",
|
||||
});
|
||||
|
||||
//subjects
|
||||
const DatasetSubject = sequelizeConnection.define(
|
||||
"link_dataset_subjects",
|
||||
{},
|
||||
{
|
||||
tableName: "link_dataset_subjects",
|
||||
timestamps: false,
|
||||
},
|
||||
);
|
||||
Dataset.belongsToMany(Subject, {
|
||||
through: DatasetSubject,
|
||||
as: "subjects",
|
||||
foreignKey: "document_id",
|
||||
});
|
||||
Subject.belongsToMany(Dataset, {
|
||||
through: DatasetSubject,
|
||||
foreignKey: "subject_id",
|
||||
as: "datasets",
|
||||
});
|
||||
|
||||
// coverage
|
||||
Dataset.hasOne(Coverage, {
|
||||
as: "coverage",
|
||||
foreignKey: "dataset_id",
|
||||
});
|
||||
Coverage.belongsTo(Dataset, {
|
||||
foreignKey: "dataset_id",
|
||||
as: "dataset",
|
||||
});
|
||||
|
||||
// xmlCache
|
||||
Dataset.hasOne(DocumentXmlCache, {
|
||||
as: "xmlCache",
|
||||
foreignKey: "document_id",
|
||||
});
|
||||
// Coverage.belongsTo(Dataset, {
|
||||
// foreignKey: "dataset_id",
|
||||
// as: "dataset",
|
||||
// });
|
||||
|
||||
//licences
|
||||
const DatasetLicense = sequelizeConnection.define(
|
||||
"link_documents_licences",
|
||||
{},
|
||||
{
|
||||
tableName: "link_documents_licences",
|
||||
timestamps: false,
|
||||
},
|
||||
);
|
||||
Dataset.belongsToMany(License, {
|
||||
through: DatasetLicense,
|
||||
as: "licenses",
|
||||
foreignKey: "document_id",
|
||||
});
|
||||
License.belongsToMany(Dataset, {
|
||||
through: DatasetLicense,
|
||||
foreignKey: "licence_id",
|
||||
as: "datasets",
|
||||
});
|
||||
|
||||
//project relations
|
||||
Project.hasMany(Dataset, {
|
||||
foreignKey: "project_id",
|
||||
as: "datasets",
|
||||
});
|
||||
Dataset.belongsTo(Project, {
|
||||
foreignKey: "project_id",
|
||||
as: "project",
|
||||
});
|
||||
|
||||
//file relations
|
||||
// title relations
|
||||
Dataset.hasMany(File, {
|
||||
as: "files",
|
||||
foreignKey: "document_id",
|
||||
});
|
||||
File.belongsTo(Dataset, {
|
||||
foreignKey: "document_id",
|
||||
as: "dataset",
|
||||
});
|
||||
|
||||
return {
|
||||
Dataset: Dataset,
|
||||
Title: Title,
|
||||
Coverage: Coverage,
|
||||
Subject: Subject,
|
||||
License: License,
|
||||
};
|
||||
}
|
51
src/models/license.model.js
Normal file
51
src/models/license.model.js
Normal file
|
@ -0,0 +1,51 @@
|
|||
import Sequelize from "sequelize";
|
||||
import sequelizeConnection from "../config/db.config";
|
||||
|
||||
// module.exports = (sequelize, DataTypes) => {
|
||||
|
||||
const License = sequelizeConnection.define(
|
||||
"coverage",
|
||||
{
|
||||
active: {
|
||||
type: Sequelize.BOOLEAN,
|
||||
allowNull: false,
|
||||
defaultValue: true,
|
||||
},
|
||||
comment_internal: Sequelize.STRING,
|
||||
desc_markup: Sequelize.STRING,
|
||||
desc_text: Sequelize.STRING,
|
||||
language: Sequelize.STRING,
|
||||
link_licence: {
|
||||
type: Sequelize.STRING,
|
||||
allowNull: false,
|
||||
},
|
||||
link_logo: Sequelize.STRING,
|
||||
link_sign: Sequelize.STRING,
|
||||
mime_type: Sequelize.STRING,
|
||||
name_long: {
|
||||
type: Sequelize.STRING,
|
||||
allowNull: false,
|
||||
},
|
||||
name: {
|
||||
type: Sequelize.STRING,
|
||||
allowNull: false,
|
||||
},
|
||||
pod_allowed: {
|
||||
type: Sequelize.BOOLEAN,
|
||||
allowNull: false,
|
||||
defaultValue: false,
|
||||
},
|
||||
sort_order: {
|
||||
type: Sequelize.BOOLEAN,
|
||||
allowNull: false,
|
||||
defaultValue: false,
|
||||
},
|
||||
},
|
||||
{
|
||||
timestamps: false,
|
||||
tableName: "document_licences",
|
||||
},
|
||||
);
|
||||
|
||||
export default License;
|
||||
// };
|
20
src/models/person.model.js
Normal file
20
src/models/person.model.js
Normal file
|
@ -0,0 +1,20 @@
|
|||
import Sequelize from "sequelize";
|
||||
import sequelizeConnection from "../config/db.config";
|
||||
|
||||
const Person = sequelizeConnection.define(
|
||||
"persons",
|
||||
{
|
||||
email: {
|
||||
type: Sequelize.STRING,
|
||||
allowNull: false,
|
||||
length: 255,
|
||||
},
|
||||
first_name: Sequelize.STRING(255),
|
||||
last_name: Sequelize.STRING(255),
|
||||
},
|
||||
{
|
||||
timestamps: false,
|
||||
tableName: "persons",
|
||||
},
|
||||
);
|
||||
export default Person;
|
33
src/models/subject.model.js
Normal file
33
src/models/subject.model.js
Normal file
|
@ -0,0 +1,33 @@
|
|||
import Sequelize from "sequelize";
|
||||
import sequelizeConnection from "../config/db.config";
|
||||
|
||||
const Subject = sequelizeConnection.define(
|
||||
"dataset_subjects",
|
||||
{
|
||||
type: {
|
||||
type: Sequelize.STRING,
|
||||
allowNull: false,
|
||||
length: 255,
|
||||
},
|
||||
value: {
|
||||
type: Sequelize.STRING,
|
||||
allowNull: false,
|
||||
length: 255,
|
||||
},
|
||||
external_key: {
|
||||
type: Sequelize.STRING,
|
||||
allowNull: true,
|
||||
length: 255,
|
||||
},
|
||||
language: Sequelize.STRING(3),
|
||||
created_at: Sequelize.DATE,
|
||||
updated_at: Sequelize.DATE,
|
||||
},
|
||||
{
|
||||
createdAt: "created_at",
|
||||
updatedAt: "updated_at",
|
||||
tableName: "dataset_subjects",
|
||||
},
|
||||
);
|
||||
|
||||
export default Subject;
|
26
src/models/title.model.js
Normal file
26
src/models/title.model.js
Normal file
|
@ -0,0 +1,26 @@
|
|||
import Sequelize from "sequelize";
|
||||
import sequelizeConnection from "../config/db.config";
|
||||
|
||||
// module.exports = (sequelize, Sequelize) => {
|
||||
const Title = sequelizeConnection.define(
|
||||
"dataset_titles",
|
||||
{
|
||||
type: Sequelize.STRING,
|
||||
value: Sequelize.STRING,
|
||||
language: Sequelize.STRING,
|
||||
},
|
||||
{
|
||||
// schema: 'public',
|
||||
timestamps: false,
|
||||
tableName: "dataset_titles",
|
||||
},
|
||||
);
|
||||
// Title.belongsTo(Dataset, {
|
||||
// foreignKey: "document_id",
|
||||
// as: "dataset",
|
||||
// });
|
||||
|
||||
export default Title;
|
||||
|
||||
// return Title;
|
||||
// };
|
39
src/models/user.model.js
Normal file
39
src/models/user.model.js
Normal file
|
@ -0,0 +1,39 @@
|
|||
import Sequelize from "sequelize";
|
||||
import sequelizeConnection from "../config/db.config";
|
||||
|
||||
const User = sequelizeConnection.define(
|
||||
"accounts",
|
||||
{
|
||||
login: {
|
||||
type: Sequelize.STRING,
|
||||
allowNull: false,
|
||||
length: 20,
|
||||
},
|
||||
password: {
|
||||
type: Sequelize.STRING,
|
||||
allowNull: false,
|
||||
length: 60,
|
||||
},
|
||||
email: {
|
||||
type: Sequelize.STRING,
|
||||
allowNull: false,
|
||||
length: 255,
|
||||
},
|
||||
first_name: Sequelize.STRING(255),
|
||||
last_name: Sequelize.STRING(255),
|
||||
remember_token: Sequelize.STRING(255),
|
||||
created_at: Sequelize.DATE,
|
||||
updated_at: Sequelize.DATE,
|
||||
},
|
||||
{
|
||||
defaultScope: {
|
||||
attributes: {
|
||||
exclude: ["password", "remember_token"],
|
||||
},
|
||||
},
|
||||
createdAt: "created_at",
|
||||
updatedAt: "updated_at",
|
||||
tableName: "accounts",
|
||||
},
|
||||
);
|
||||
export default User;
|
38
src/routes/dataset.routes.js
Normal file
38
src/routes/dataset.routes.js
Normal file
|
@ -0,0 +1,38 @@
|
|||
// module.exports = app => {
|
||||
// const datasetController = require("../controllers/dataset.controller.js");
|
||||
|
||||
// var router = require("express").Router();
|
||||
|
||||
// // // Create a new Dataset
|
||||
// // router.post("/", datasetController.create);
|
||||
|
||||
// // Retrieve all Tutorials
|
||||
// router.get("/", datasetController.findAll);
|
||||
|
||||
// // // Retrieve all published Dataset
|
||||
// // router.get("/published", tutorials.findAllPublished);
|
||||
|
||||
// // Retrieve a single Dataset with publish_id
|
||||
// router.get("/:publish_id", datasetController.findOne);
|
||||
|
||||
// app.use('/api/dataset', router);
|
||||
// };
|
||||
|
||||
import { Router } from "express";
|
||||
// import "@babel/polyfill"
|
||||
import { findAll, findOne } from "../controllers/dataset.controller";
|
||||
|
||||
const router = new Router();
|
||||
|
||||
// Retrieve all Tutorials
|
||||
router.get("/", findAll);
|
||||
// router.get("/years/", findYears);
|
||||
// Retrieve a single Dataset with publish_id
|
||||
router.get("/:publish_id", findOne);
|
||||
// router.post('/getUsers', getUsers);
|
||||
// router.post('/getUser', getUser);
|
||||
// router.post('/create', createUser);
|
||||
// router.delete('/removeUser', deleteUser);
|
||||
// router.put('/updateUser', updateUsers);
|
||||
|
||||
export default router;
|
12
src/routes/home.routes.js
Normal file
12
src/routes/home.routes.js
Normal file
|
@ -0,0 +1,12 @@
|
|||
import { Router } from "express";
|
||||
// import "@babel/polyfill"
|
||||
import { findYears, findDocumentsPerYear } from "../controllers/home.controller.js";
|
||||
|
||||
const router = Router();
|
||||
|
||||
// Retrieve all Tutorials
|
||||
// router.get("/", findAll);
|
||||
router.get("/years", findYears);
|
||||
router.get("/sitelinks/:year", findDocumentsPerYear);
|
||||
|
||||
export default router;
|
158
src/server.ts
Normal file
158
src/server.ts
Normal file
|
@ -0,0 +1,158 @@
|
|||
// const express = require('express');
|
||||
// // const bodyParser = require('body-parser');
|
||||
// // const cors = require('cors');
|
||||
// // https://www.bezkoder.com/node-express-sequelize-postgresql/
|
||||
// // Next, we'll want to instantiate the Express app:
|
||||
// const app = express();
|
||||
// app.use(express.static(__dirname + '/client'));
|
||||
|
||||
// https://github.com/ivan-shaban/nodemon-babel-preset-typescript/blob/master/README.md
|
||||
// https://github.com/microsoft/TypeScript-Babel-Starter
|
||||
import "core-js/stable";
|
||||
import "regenerator-runtime/runtime";
|
||||
|
||||
import { App } from "./app";
|
||||
const app: App = new App();
|
||||
app.start();
|
||||
// import express, { Express, Request, Response } from 'express';
|
||||
// import bodyParser from 'body-parser';
|
||||
|
||||
// //Importing Routes
|
||||
// import DatasetRoutes from './routes/dataset.routes.js';
|
||||
// import HomeRoutes from './routes/home.routes.js';
|
||||
|
||||
// const app: Express = express();
|
||||
//middlewares
|
||||
// app.all('*', function(req, res, next) {
|
||||
// res.setHeader("Access-Control-Allow-Origin", "*");
|
||||
// res.header("Access-Control-Allow-Methods", "POST, PUT, OPTIONS, DELETE, GET");
|
||||
// res.header("Access-Control-Max-Age", "3600");
|
||||
// res.header("Access-Control-Allow-Headers", "Content-Type, Access-Control-Allow-Headers, Authorization, X-Requested-With, x-access-token");
|
||||
// next();
|
||||
// });
|
||||
|
||||
// app.use(bodyParser.json({limit: '100mb'}));
|
||||
// app.use(bodyParser.urlencoded({limit: '50mb', extended: true}));
|
||||
// app.use(bodyParser.json({type: 'application/vnd.api+json'}));
|
||||
|
||||
// //routes
|
||||
// app.use('/api/dataset', DatasetRoutes);
|
||||
// app.use('/api/', HomeRoutes);
|
||||
|
||||
// Where we will keep books
|
||||
// const books = [
|
||||
// {
|
||||
// isbn: "9781593275846",
|
||||
// title: "Eloquent JavaScript, Second Edition",
|
||||
// author: "Marijn Haverbeke",
|
||||
// publish_date: "2014-12-14",
|
||||
// publisher: "No Starch Press",
|
||||
// numOfPages: 472,
|
||||
// },
|
||||
// {
|
||||
// isbn: "9781449331818",
|
||||
// title: "Learning JavaScript Design Patterns",
|
||||
// author: "Addy Osmani",
|
||||
// publish_date: "2012-07-01",
|
||||
// publisher: "O'Reilly Media",
|
||||
// numOfPages: 254,
|
||||
// },
|
||||
// {
|
||||
// isbn: "9781449365035",
|
||||
// title: "Speaking JavaScript",
|
||||
// author: "Axel Rauschmayer",
|
||||
// publish_date: "2014-02-01",
|
||||
// publisher: "O'Reilly Media",
|
||||
// numOfPages: 460,
|
||||
// },
|
||||
// ];
|
||||
|
||||
// load middlewar:
|
||||
// app.use(cors());
|
||||
// Configuring body parser middleware
|
||||
// app.use(bodyParser.urlencoded({ extended: false }));
|
||||
// app.use(bodyParser.json());
|
||||
// app.use(express.json());
|
||||
|
||||
// const db = require("./models");
|
||||
// db.sequelize.sync()
|
||||
// .then(() => {
|
||||
// console.log("Synced db.");
|
||||
// })
|
||||
// .catch((err) => {
|
||||
// console.log("Failed to sync db: " + err.message);
|
||||
// });
|
||||
|
||||
// app.post('/book', (req, res) => {
|
||||
// const book = req.body;
|
||||
|
||||
// // Output the book to the console for debugging
|
||||
// console.log(book);
|
||||
// books.push(book);
|
||||
|
||||
// res.send('Book is added to the database');
|
||||
// });
|
||||
|
||||
// app.get('/books', async (req, res) => {
|
||||
// res.json(books);
|
||||
// // const allDogs = await Dataset.findAll();
|
||||
// // return res.status(200).json(allDogs);
|
||||
// });
|
||||
|
||||
// app.get('/book/:isbn', (req, res) => {
|
||||
// // reading isbn from the URL
|
||||
// const isbn = req.params.isbn;
|
||||
|
||||
// // searching books for the isbn
|
||||
// for (let book of books) {
|
||||
// if (book.isbn === isbn) {
|
||||
// res.json(book);
|
||||
// return;
|
||||
// }
|
||||
// }
|
||||
|
||||
// // sending 404 when not found something is a good practice
|
||||
// res.status(404).send('Book not found');
|
||||
// });
|
||||
|
||||
// app.post('/book/:isbn', (req, res) => {
|
||||
// // reading isbn from the URL
|
||||
// const isbn = req.params.isbn;
|
||||
// const newBook = req.body;
|
||||
|
||||
// // remove item from the books array
|
||||
// for (let i = 0; i < books.length; i++) {
|
||||
// let book = books[i]
|
||||
|
||||
// if (book.isbn === isbn) {
|
||||
// books[i] = newBook;
|
||||
// }
|
||||
// }
|
||||
|
||||
// // sending 404 when not found something is a good practice
|
||||
// res.send('Book is edited');
|
||||
// });
|
||||
|
||||
// // ow, we can create a simple GET endpoint right beneath the boilerplate.
|
||||
// // We'd like to set it to be on the home page, so the URL for the endpoint is /:
|
||||
// app.get('/', (request, response) => {
|
||||
// // response.send('Hello World, from express');
|
||||
// response.sendFile('/home/administrator/api/new-book.html');
|
||||
// });
|
||||
|
||||
// app.get('/booklist', (request, response) => {
|
||||
// // response.send('Hello World, from express');
|
||||
// response.sendFile('/home/administrator/api/client/book-list.html');
|
||||
// });
|
||||
|
||||
// // require("./routes/dataset.routes")(app);
|
||||
|
||||
// // set port, listen for requests
|
||||
// const port = process.env.PORT || 3000;
|
||||
// app.set('port', port);
|
||||
// // At this point, let's start our clients:
|
||||
// app.listen(app.get('port'), function () {
|
||||
// console.log('Express server listening on port ' + port);
|
||||
// });
|
||||
|
||||
// export default app;
|
38
tsconfig.json
Normal file
38
tsconfig.json
Normal file
|
@ -0,0 +1,38 @@
|
|||
{
|
||||
// This ensures typescript only does type checking and doesn't output js.
|
||||
// "noEmit": true,
|
||||
"compilerOptions": {
|
||||
"module": "commonjs",
|
||||
"allowSyntheticDefaultImports": true,
|
||||
"noImplicitAny": true,
|
||||
"sourceMap": true,
|
||||
// "importHelpers": true,
|
||||
"outDir": "dist",
|
||||
"baseUrl": ".",
|
||||
// Target latest version 'esnext' of ECMAScript or minimal 'es6'.
|
||||
"target": "esnext",
|
||||
// Search under node_modules for non-relative imports.
|
||||
"moduleResolution": "node",
|
||||
// Process & infer types from .js files.
|
||||
"allowJs": true,
|
||||
// // Don't emit; allow Babel to transform files.
|
||||
// "noEmit": true,
|
||||
// Enable strictest settings like strictNullChecks & noImplicitAny.
|
||||
"strict": true,
|
||||
// Disallow features that require cross-file information for emit.
|
||||
"isolatedModules": true,
|
||||
// Import non-ES modules as default imports.
|
||||
"esModuleInterop": true,
|
||||
// for sequelize-typescript: Enables experimental support for ES7 decorators. ,
|
||||
"experimentalDecorators": true,
|
||||
"strictPropertyInitialization": false,
|
||||
|
||||
"paths": {
|
||||
"*": [
|
||||
"node_modules/*"
|
||||
]
|
||||
},
|
||||
},
|
||||
"include": ["src/**/*.ts"],
|
||||
"exclude": ["node_modules", "**/*.spec.ts", "dist", "test"]
|
||||
}
|
Loading…
Reference in New Issue
Block a user