- add citation for dc:source inside datasetxml2oai.pmh.xslt
- add ddc collection for ListSets request - add ddc category into the header of GetReord and GetRecords requests - npm updates
This commit is contained in:
parent
ab57fabc36
commit
b1d62e14f7
|
@ -30,7 +30,7 @@
|
|||
expand-text="yes"
|
||||
version="3.0">
|
||||
|
||||
<xsl:output method="xml" encoding="iso-8859-1" indent="yes"/>
|
||||
<xsl:output method="xml" encoding="utf-8" indent="yes"/>
|
||||
<xsl:mode on-no-match="shallow-copy"/>
|
||||
|
||||
<xsl:import href="functions.xslt"/>
|
||||
|
|
|
@ -34,7 +34,7 @@
|
|||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
|
||||
|
||||
<xsl:output method="xml" encoding="iso-8859-1" indent="yes" />
|
||||
<xsl:output method="xml" encoding="utf-8" indent="yes" />
|
||||
|
||||
<xsl:template match="Rdr_Dataset" mode="oai_datacite">
|
||||
<resource xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
<xsl:include href="assets/oai_2_iso19139.xslt" />
|
||||
|
||||
|
||||
<xsl:output method="xml" indent="yes" encoding="iso-8859-1" />
|
||||
<xsl:output method="xml" indent="yes" encoding="utf-8" />
|
||||
|
||||
<xsl:param name="responseDate" />
|
||||
<xsl:param name="unixTimestamp" />
|
||||
|
@ -431,9 +431,45 @@
|
|||
<xsl:if test="EmbargoDate and ($unixTimestamp < EmbargoDate/@UnixTimestamp)">
|
||||
<dc:rights>embargo</dc:rights>
|
||||
</xsl:if>
|
||||
<!-- dc:source -->
|
||||
<xsl:call-template name="citation"/>
|
||||
</oai_dc:dc>
|
||||
</xsl:template>
|
||||
|
||||
<xsl:template name="citation">
|
||||
<dc:source>
|
||||
<xsl:variable name="creatorName">
|
||||
<xsl:for-each select="*[name() = 'PersonAuthor']">
|
||||
<xsl:variable name="person" select="."/>
|
||||
<xsl:variable name="uppercase" select="'ABC..XYZ'"/>
|
||||
<xsl:variable name="lowercase" select="'abc..zyz'"/>
|
||||
<xsl:variable name="authorName">
|
||||
<xsl:choose>
|
||||
<xsl:when test="string($person/@FirstName)">
|
||||
<xsl:variable name="name" select="concat(
|
||||
$person/@LastName, ', ', concat(translate(substring($person/@FirstName, 1, 1), $lowercase, $uppercase), '.')
|
||||
)" />
|
||||
<xsl:value-of select="$name"/>
|
||||
</xsl:when>
|
||||
<xsl:otherwise>
|
||||
<xsl:value-of select="$person/@LastName"/>
|
||||
</xsl:otherwise>
|
||||
</xsl:choose>
|
||||
</xsl:variable>
|
||||
<xsl:value-of select="normalize-space($authorName)"/>
|
||||
<xsl:choose>
|
||||
<xsl:when test="position() != last()">,</xsl:when>
|
||||
</xsl:choose>
|
||||
</xsl:for-each>
|
||||
</xsl:variable>
|
||||
<xsl:variable name="year" select="concat( ' (', string(ServerDatePublished/@Year), '): ' )" />
|
||||
<xsl:variable name="mainTitle" select="string(TitleMain/@Value)" />
|
||||
<xsl:variable name="creatingCorporation" select="concat('.', string(@CreatingCorporation), ', ')" />
|
||||
<xsl:variable name="publisherName" select="string(@PublisherName)" />
|
||||
<xsl:value-of select="concat($creatorName, $year, $mainTitle, $creatingCorporation, $publisherName, ', Wien')"/>
|
||||
</dc:source>
|
||||
</xsl:template>
|
||||
|
||||
<xsl:template match="Coverage" mode="oai_dc">
|
||||
<dc:coverage>
|
||||
<xsl:variable name="geolocation" select="concat(
|
||||
|
|
File diff suppressed because one or more lines are too long
|
@ -8,7 +8,7 @@ import { readFileSync } from "fs";
|
|||
// @ts-ignore
|
||||
import { transform } from "saxon-js";
|
||||
import dayjs, { Dayjs } from "dayjs";
|
||||
import { Dataset, Project, License } from "../models/init-models";
|
||||
import { Dataset, Project, License, Collection, CollectionRole } from "../models/init-models";
|
||||
import Logger from "jet-logger";
|
||||
import { BadOaiModelException, OaiModelException } from "../exceptions/OaiModelException";
|
||||
import PageNotFoundException from "../exceptions/PageNotFoundException";
|
||||
|
@ -255,6 +255,7 @@ export class OaiController {
|
|||
// 'bibliography:true' => 'Set for bibliographic entries',
|
||||
// 'bibliography:false' => 'Set for non-bibliographic entries',
|
||||
...(await this.getSetsForDatasetTypes()),
|
||||
...(await this.getSetsForCollections()),
|
||||
// ... await this.getSetsForProjects(),
|
||||
} as IDictionary;
|
||||
|
||||
|
@ -437,6 +438,7 @@ export class OaiController {
|
|||
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({
|
||||
|
@ -458,6 +460,26 @@ export class OaiController {
|
|||
},
|
||||
};
|
||||
includeArray.push(icncludeFilter);
|
||||
} else if (setArray[0] == "ddc") {
|
||||
// const openAccessLicences = ["CC-BY-4.0", "CC-BY-SA-4.0"];
|
||||
if (setArray.length == 2 && setArray[1] != "") {
|
||||
const icncludeFilter = {
|
||||
model: Collection,
|
||||
as: "collections",
|
||||
required: true, //return only records which have an associated model INNER JOIN
|
||||
where: {
|
||||
number: setArray[1],
|
||||
},
|
||||
// include: [
|
||||
// {
|
||||
// model: CollectionRole,
|
||||
// attributes: ["oai_name"],
|
||||
// as: "collectionRole",
|
||||
// },
|
||||
// ],
|
||||
};
|
||||
includeArray.push(icncludeFilter);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -612,7 +634,7 @@ export class OaiController {
|
|||
[Sequelize.Op.in]: workIds,
|
||||
},
|
||||
},
|
||||
include: ["xmlCache"],
|
||||
include: ["xmlCache", { model: Collection, as: "collections" }],
|
||||
order: ["publish_id"],
|
||||
});
|
||||
for (const dataset of datasets) {
|
||||
|
@ -718,6 +740,14 @@ export class OaiController {
|
|||
dataset.publish_id && this.addLandingPageAttribute(domNode, dataset.publish_id.toString());
|
||||
// }
|
||||
this.addSpecInformation(domNode, "data-type:" + dataset.type);
|
||||
|
||||
if (dataset.collections) {
|
||||
for (const coll of dataset.collections) {
|
||||
const collRole = await coll.getCollectionRole();
|
||||
this.addSpecInformation(domNode, collRole.oai_name + ":" + coll.number);
|
||||
}
|
||||
}
|
||||
|
||||
datasetNode.import(domNode);
|
||||
}
|
||||
|
||||
|
@ -758,6 +788,33 @@ export class OaiController {
|
|||
return sets;
|
||||
}
|
||||
|
||||
private async getSetsForCollections(): Promise<IDictionary> {
|
||||
const sets: { [key: string]: string } = {} as IDictionary;
|
||||
|
||||
const collections: Array<Collection> = await Collection.findAll({
|
||||
attributes: ["name", "number"],
|
||||
include: [
|
||||
{
|
||||
model: CollectionRole,
|
||||
attributes: ["oai_name"],
|
||||
as: "collectionRole",
|
||||
required: true, //return only records which have an associated model role -> INNER JOIN
|
||||
where: {
|
||||
visible_oai: true,
|
||||
},
|
||||
},
|
||||
],
|
||||
});
|
||||
collections.forEach((collection) => {
|
||||
// if collection has a collection role (classification like ddc):
|
||||
if (collection.number) {
|
||||
const setSpec = collection.collectionRole?.oai_name + ":" + collection.number;
|
||||
sets[setSpec] = `Set ${collection.number} '${collection.name}'`;
|
||||
}
|
||||
});
|
||||
return sets;
|
||||
}
|
||||
|
||||
private async getSetsForDatasetTypes(): Promise<IDictionary> {
|
||||
const sets: { [key: string]: string } = {} as IDictionary;
|
||||
|
||||
|
|
|
@ -12,12 +12,12 @@ export enum OaiErrorCodes {
|
|||
|
||||
// https://medium.com/@juliapassynkova/map-your-typescript-enums-e402d406b229
|
||||
export const OaiModelError = new Map<number, string>([
|
||||
[OaiErrorCodes.BADVERB, 'badVerb'],
|
||||
[OaiErrorCodes.BADARGUMENT, 'badArgument'],
|
||||
[OaiErrorCodes.NORECORDSMATCH, 'noRecordsMatch'],
|
||||
[OaiErrorCodes.CANNOTDISSEMINATEFORMAT, 'cannotDisseminateFormat'],
|
||||
[OaiErrorCodes.BADRESUMPTIONTOKEN, 'badResumptionToken'],
|
||||
[OaiErrorCodes.IDDOESNOTEXIST, 'idDoesNotExist']
|
||||
[OaiErrorCodes.BADVERB, "badVerb"],
|
||||
[OaiErrorCodes.BADARGUMENT, "badArgument"],
|
||||
[OaiErrorCodes.NORECORDSMATCH, "noRecordsMatch"],
|
||||
[OaiErrorCodes.CANNOTDISSEMINATEFORMAT, "cannotDisseminateFormat"],
|
||||
[OaiErrorCodes.BADRESUMPTIONTOKEN, "badResumptionToken"],
|
||||
[OaiErrorCodes.IDDOESNOTEXIST, "idDoesNotExist"],
|
||||
]);
|
||||
|
||||
// class OaiModelError {
|
||||
|
@ -28,7 +28,6 @@ export const OaiModelError = new Map<number, string>([
|
|||
// // const NORECORDSMATCH = 1014;
|
||||
// // const IDDOESNOTEXIST = 1015;
|
||||
|
||||
|
||||
// protected static $oaiErrorCodes = {
|
||||
// OaiErrorCodes. 'badVerb',
|
||||
// BADARGUMENT : 'badArgument',
|
||||
|
@ -38,7 +37,6 @@ export const OaiModelError = new Map<number, string>([
|
|||
// IDDOESNOTEXIST: 'idDoesNotExist',
|
||||
// };
|
||||
|
||||
|
||||
// public static function mapCode($code)
|
||||
// {
|
||||
// if (false === array_key_exists($code, self::$oaiErrorCodes)) {
|
||||
|
|
91
src/models/Collection.ts
Normal file
91
src/models/Collection.ts
Normal file
|
@ -0,0 +1,91 @@
|
|||
// import Sequelize from "sequelize";
|
||||
import {
|
||||
Model,
|
||||
DataTypes,
|
||||
InferAttributes,
|
||||
InferCreationAttributes,
|
||||
CreationOptional,
|
||||
NonAttribute,
|
||||
Association,
|
||||
BelongsToGetAssociationMixin,
|
||||
} from "sequelize";
|
||||
import sequelizeConnection from "../config/db.config";
|
||||
import CollectionRole from "./CollectionRole";
|
||||
import Dataset from "./Dataset";
|
||||
|
||||
class Collection extends Model<InferAttributes<Collection>, InferCreationAttributes<Collection>> {
|
||||
// id can be undefined during creation when using `autoIncrement`
|
||||
declare id: CreationOptional<number>;
|
||||
declare role_id: number | null;
|
||||
declare number: string | null; // nullable fields
|
||||
declare name: string; // not nullable fields
|
||||
declare oai_subset: string | null; // nullable fields
|
||||
declare parent_id: number | null;
|
||||
declare visible: boolean;
|
||||
declare visible_publish: boolean;
|
||||
|
||||
// https://sequelize.org/docs/v6/other-topics/typescript/
|
||||
// Since TS cannot determine model association at compile time
|
||||
// we have to declare them here purely virtually
|
||||
// these will not exist until `Model.init` was called.
|
||||
declare getCollectionRole: BelongsToGetAssociationMixin<CollectionRole>;
|
||||
|
||||
// You can also pre-declare possible inclusions, these will only be populated if you
|
||||
// actively include a relation.belongsTo one collectionRole
|
||||
declare collectionRole?: NonAttribute<CollectionRole>;
|
||||
declare datasets?: NonAttribute<Dataset>;
|
||||
|
||||
declare static associations: {
|
||||
collectionRole: Association<Collection, CollectionRole>;
|
||||
datasets: Association<Collection, Dataset>;
|
||||
};
|
||||
|
||||
// getters that are not attributes should be tagged using NonAttribute
|
||||
// to remove them from the model's Attribute Typings.
|
||||
get fullName(): NonAttribute<string> {
|
||||
return this.name + ":" + this.number;
|
||||
}
|
||||
}
|
||||
|
||||
Collection.init(
|
||||
{
|
||||
id: {
|
||||
type: DataTypes.INTEGER,
|
||||
primaryKey: true,
|
||||
},
|
||||
role_id: {
|
||||
type: DataTypes.INTEGER,
|
||||
allowNull: true,
|
||||
},
|
||||
number: {
|
||||
type: DataTypes.STRING(255),
|
||||
allowNull: true,
|
||||
},
|
||||
name: {
|
||||
type: DataTypes.STRING(255),
|
||||
allowNull: false,
|
||||
},
|
||||
oai_subset: {
|
||||
type: DataTypes.STRING(255),
|
||||
allowNull: true,
|
||||
},
|
||||
parent_id: {
|
||||
type: DataTypes.INTEGER,
|
||||
allowNull: true,
|
||||
},
|
||||
visible: {
|
||||
type: DataTypes.BOOLEAN,
|
||||
defaultValue: true,
|
||||
},
|
||||
visible_publish: {
|
||||
type: DataTypes.BOOLEAN,
|
||||
defaultValue: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
timestamps: false,
|
||||
sequelize: sequelizeConnection,
|
||||
tableName: "collections",
|
||||
},
|
||||
);
|
||||
export default Collection;
|
62
src/models/CollectionRole.ts
Normal file
62
src/models/CollectionRole.ts
Normal file
|
@ -0,0 +1,62 @@
|
|||
// import Sequelize from "sequelize";
|
||||
import { Model, DataTypes, InferAttributes, InferCreationAttributes, CreationOptional, Association, NonAttribute } from "sequelize";
|
||||
import sequelizeConnection from "../config/db.config";
|
||||
import Collection from "./Collection";
|
||||
|
||||
class CollectionRole extends Model<InferAttributes<CollectionRole>, InferCreationAttributes<CollectionRole>> {
|
||||
// id can be undefined during creation when using `autoIncrement`
|
||||
declare id: CreationOptional<number>;
|
||||
declare name: string; // not nullable fields
|
||||
declare oai_name: string; // not nullable fields
|
||||
declare position: number;
|
||||
declare visible: boolean;
|
||||
declare visible_frontdoor: boolean;
|
||||
declare visible_oai: boolean;
|
||||
|
||||
// You can also pre-declare possible inclusions, these will only be populated if you
|
||||
// actively include a relation.
|
||||
declare collections?: NonAttribute<Collection>;
|
||||
declare static associations: {
|
||||
collections: Association<CollectionRole, Collection>;
|
||||
};
|
||||
}
|
||||
|
||||
CollectionRole.init(
|
||||
{
|
||||
id: {
|
||||
type: DataTypes.INTEGER,
|
||||
primaryKey: true,
|
||||
},
|
||||
|
||||
name: {
|
||||
type: DataTypes.STRING(255),
|
||||
allowNull: false,
|
||||
},
|
||||
oai_name: {
|
||||
type: DataTypes.STRING(255),
|
||||
allowNull: false,
|
||||
},
|
||||
position: {
|
||||
type: DataTypes.INTEGER,
|
||||
allowNull: false,
|
||||
},
|
||||
visible: {
|
||||
type: DataTypes.BOOLEAN,
|
||||
defaultValue: true,
|
||||
},
|
||||
visible_frontdoor: {
|
||||
type: DataTypes.BOOLEAN,
|
||||
defaultValue: true,
|
||||
},
|
||||
visible_oai: {
|
||||
type: DataTypes.BOOLEAN,
|
||||
defaultValue: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
timestamps: false,
|
||||
sequelize: sequelizeConnection,
|
||||
tableName: "collections_roles",
|
||||
},
|
||||
);
|
||||
export default CollectionRole;
|
|
@ -52,6 +52,7 @@
|
|||
import { Op, Model, DataTypes, InferAttributes, InferCreationAttributes, CreationOptional, NonAttribute, Association } from "sequelize";
|
||||
import sequelizeConnection from "../config/db.config";
|
||||
import DocumentXmlCache from "./DocumentXmlCache";
|
||||
import Collection from "./Collection";
|
||||
|
||||
class Dataset extends Model<InferAttributes<Dataset>, InferCreationAttributes<Dataset>> {
|
||||
// id can be undefined during creation when using `autoIncrement`
|
||||
|
@ -76,6 +77,11 @@ class Dataset extends Model<InferAttributes<Dataset>, InferCreationAttributes<Da
|
|||
// actively include a relation.
|
||||
declare xmlCache?: NonAttribute<DocumentXmlCache>; // Note this is optional since it's only populated when explicitly requested in code
|
||||
|
||||
declare collections?: NonAttribute<Collection[]>;
|
||||
// declare static associations: {
|
||||
|
||||
// };
|
||||
|
||||
// getters that are not attributes should be tagged using NonAttribute
|
||||
// to remove them from the model's Attribute Typings.
|
||||
get fullName(): NonAttribute<string | null> {
|
||||
|
@ -84,6 +90,7 @@ class Dataset extends Model<InferAttributes<Dataset>, InferCreationAttributes<Da
|
|||
|
||||
declare static associations: {
|
||||
xmlCache: Association<Dataset, DocumentXmlCache>;
|
||||
collections: Association<Dataset, Collection>;
|
||||
};
|
||||
|
||||
public static async earliestPublicationDate(): Promise<Dataset | null> {
|
||||
|
|
|
@ -15,10 +15,27 @@ import Project from "./Project";
|
|||
import File from "./File";
|
||||
import Identifier from "./Identifier";
|
||||
import DocumentXmlCache from "./DocumentXmlCache";
|
||||
import CollectionRole from "./CollectionRole";
|
||||
import Collection from "./Collection";
|
||||
|
||||
const dbContext = initModels();
|
||||
|
||||
export { Dataset, Title, Abstract, User, Person, Subject, Coverage, License, Project, Identifier, DocumentXmlCache, File };
|
||||
export {
|
||||
Dataset,
|
||||
Title,
|
||||
Abstract,
|
||||
User,
|
||||
Person,
|
||||
Subject,
|
||||
Coverage,
|
||||
License,
|
||||
Project,
|
||||
Identifier,
|
||||
DocumentXmlCache,
|
||||
File,
|
||||
Collection,
|
||||
CollectionRole,
|
||||
};
|
||||
export default dbContext;
|
||||
|
||||
export function initModels() {
|
||||
|
@ -201,6 +218,37 @@ export function initModels() {
|
|||
as: "dataset",
|
||||
});
|
||||
|
||||
// collection an collectionRole relations
|
||||
CollectionRole.hasMany(Collection, {
|
||||
as: "collections",
|
||||
foreignKey: "role_id",
|
||||
});
|
||||
Collection.belongsTo(CollectionRole, {
|
||||
foreignKey: "role_id",
|
||||
as: "collectionRole",
|
||||
});
|
||||
|
||||
// dataset and collections
|
||||
//licences
|
||||
const DatasetCollection = sequelizeConnection.define(
|
||||
"link_documents_collections",
|
||||
{},
|
||||
{
|
||||
tableName: "link_documents_collections",
|
||||
timestamps: false,
|
||||
},
|
||||
);
|
||||
Dataset.belongsToMany(Collection, {
|
||||
through: DatasetCollection,
|
||||
as: "collections",
|
||||
foreignKey: "document_id",
|
||||
});
|
||||
Collection.belongsToMany(Dataset, {
|
||||
through: DatasetCollection,
|
||||
foreignKey: "collection_id",
|
||||
as: "datasets",
|
||||
});
|
||||
|
||||
return {
|
||||
Dataset: Dataset,
|
||||
Title: Title,
|
||||
|
|
Loading…
Reference in New Issue
Block a user