- add references, collections and identifiers to dataset model
All checks were successful
CI Pipeline / japa-tests (push) Successful in 52s

- npm updates
This commit is contained in:
Kaimbacher 2023-08-23 17:07:26 +02:00
parent f6d735d0fd
commit 5f8fe1c16d
9 changed files with 459 additions and 346 deletions

View File

@ -7,6 +7,7 @@ import Title from 'App/Models/Title';
import Description from 'App/Models/Description'; import Description from 'App/Models/Description';
import Language from 'App/Models/Language'; import Language from 'App/Models/Language';
import Coverage from 'App/Models/Coverage'; import Coverage from 'App/Models/Coverage';
import Collection from 'App/Models/Collection';
// import CreateUserValidator from 'App/Validators/CreateUserValidator'; // import CreateUserValidator from 'App/Validators/CreateUserValidator';
// import UpdateUserValidator from 'App/Validators/UpdateUserValidator'; // import UpdateUserValidator from 'App/Validators/UpdateUserValidator';
import { schema, CustomMessages, rules } from '@ioc:Adonis/Core/Validator'; import { schema, CustomMessages, rules } from '@ioc:Adonis/Core/Validator';
@ -48,6 +49,14 @@ export default class DatasetController {
datasets.orderBy('id', 'asc'); datasets.orderBy('id', 'asc');
} }
// const results = await Database
// .query()
// .select(Database.raw("CONCAT('https://doi.org/', b.value) AS concatenated_value"))
// .from('documents as doc')
// .innerJoin('dataset_identifiers as b', 'doc.id', 'b.dataset_id')
// .groupBy('a.id').toQuery();
// const users = await User.query().orderBy('login').paginate(page, limit); // const users = await User.query().orderBy('login').paginate(page, limit);
const myDatasets = await datasets const myDatasets = await datasets
.whereIn('server_state', [ .whereIn('server_state', [
@ -117,10 +126,8 @@ export default class DatasetController {
.map(([key, value]) => ({ value: key, label: value })), .map(([key, value]) => ({ value: key, label: value })),
// descriptiontypes: DescriptionTypes // descriptiontypes: DescriptionTypes
projects: projects, projects: projects,
referenceIdentifierTypes: Object.entries(ReferenceIdentifierTypes) referenceIdentifierTypes: Object.entries(ReferenceIdentifierTypes).map(([key, value]) => ({ value: key, label: value })),
.map(([key, value]) => ({ value: key, label: value })), relationTypes: Object.entries(RelationTypes).map(([key, value]) => ({ value: key, label: value })),
relationTypes: Object.entries(RelationTypes)
.map(([key, value]) => ({ value: key, label: value })),
}); });
} }
@ -245,15 +252,12 @@ export default class DatasetController {
depth_min: schema.number.optional([rules.requiredIfExists('depth_max')]), depth_min: schema.number.optional([rules.requiredIfExists('depth_max')]),
depth_max: schema.number.optional([rules.requiredIfExists('depth_min')]), depth_max: schema.number.optional([rules.requiredIfExists('depth_min')]),
}), }),
references: schema.array([rules.uniqueArray('value')]).members( references: schema.array.optional([rules.uniqueArray('value')]).members(
schema.object().members({ schema.object().members({
value: schema.string({ trim: true }, [ value: schema.string({ trim: true }, [rules.minLength(3), rules.maxLength(255)]),
rules.minLength(3),
rules.maxLength(255),
]),
type: schema.enum(Object.values(ReferenceIdentifierTypes)), type: schema.enum(Object.values(ReferenceIdentifierTypes)),
relation: schema.enum(Object.values(RelationTypes)), relation: schema.enum(Object.values(RelationTypes)),
// language: schema.string({ trim: true }, [rules.minLength(2), rules.maxLength(255)]), label: schema.string({ trim: true }, [rules.minLength(2), rules.maxLength(255)]),
}), }),
), ),
subjects: schema.array([rules.minLength(3), rules.uniqueArray('value')]).members( subjects: schema.array([rules.minLength(3), rules.uniqueArray('value')]).members(
@ -404,6 +408,10 @@ export default class DatasetController {
} }
} }
// save collection
const collection: Collection | null = await Collection.query().where('id', 21).first();
collection && (await dataset.useTransaction(trx).related('collections').attach([collection.id]));
// save coverage // save coverage
const coverageData = request.input('coverage'); const coverageData = request.input('coverage');
if (coverageData) { if (coverageData) {
@ -481,6 +489,12 @@ export default class DatasetController {
'subjects.*.type.required': 'keyword type is required', 'subjects.*.type.required': 'keyword type is required',
'subjects.*.language.required': 'language of keyword is required', 'subjects.*.language.required': 'language of keyword is required',
'references.*.value.required': 'Additional reference value is required, if defined',
'references.*.type.required': 'Additional reference identifier type is required',
'references.*.relation.required': 'Additional reference relation type is required',
'references.*.label.required': 'Additional reference label is required',
'files.minLength': 'At least {{ options.minLength }} file upload is required.',
'files.*.size': 'file size is to big', 'files.*.size': 'file size is to big',
'files.extnames': 'file extension is not supported', 'files.extnames': 'file extension is not supported',
}; };

45
app/Models/Collection.ts Normal file
View File

@ -0,0 +1,45 @@
import { column, BaseModel, SnakeCaseNamingStrategy, manyToMany, ManyToMany } from '@ioc:Adonis/Lucid/Orm';
import Dataset from './Dataset';
export default class Collection extends BaseModel {
public static namingStrategy = new SnakeCaseNamingStrategy();
public static primaryKey = 'id';
public static table = 'collections';
public static fillable: string[] = ['name', 'number', 'role_id'];
@column({
isPrimary: true,
})
public id: number;
@column({})
public document_id: number;
@column({})
public role_id?: number;
@column({})
public number?: string;
@column({})
public name: string;
@column({})
public oai_subset?: string;
@column({})
public parent_id?: number;
@column({})
public visible: boolean;
@column({})
public visible_publish: boolean;
@manyToMany(() => Dataset, {
pivotForeignKey: 'collection_id',
pivotRelatedForeignKey: 'document_id',
pivotTable: 'link_documents_collections',
})
public datasets: ManyToMany<typeof Dataset>;
}

View File

@ -23,6 +23,8 @@ import Subject from './Subject';
import File from './File'; import File from './File';
import Coverage from './Coverage'; import Coverage from './Coverage';
import DatasetReference from './DatasetReference'; import DatasetReference from './DatasetReference';
import Collection from './Collection';
import DatasetIdentifier from './DatasetIdentifier';
export default class Dataset extends BaseModel { export default class Dataset extends BaseModel {
public static namingStrategy = new SnakeCaseNamingStrategy(); public static namingStrategy = new SnakeCaseNamingStrategy();
@ -144,6 +146,23 @@ export default class Dataset extends BaseModel {
}) })
public references: HasMany<typeof DatasetReference>; public references: HasMany<typeof DatasetReference>;
// public function collections()
// {
// return $this
// ->belongsToMany(Collection::class, 'link_documents_collections', 'document_id', 'collection_id');
// }
@manyToMany(() => Collection, {
pivotForeignKey: 'document_id',
pivotRelatedForeignKey: 'collection_id',
pivotTable: 'link_documents_collections',
})
public collections: ManyToMany<typeof Collection>;
@hasOne(() => DatasetIdentifier, {
foreignKey: 'document_id',
})
public identifier: HasOne<typeof DatasetIdentifier>;
@computed({ @computed({
serializeAs: 'main_title', serializeAs: 'main_title',

View File

@ -0,0 +1,40 @@
import { column, BaseModel, SnakeCaseNamingStrategy, belongsTo, BelongsTo } from '@ioc:Adonis/Lucid/Orm';
import { DateTime } from 'luxon';
import Dataset from './Dataset';
export default class DatasetIdentifier extends BaseModel {
public static namingStrategy = new SnakeCaseNamingStrategy();
public static primaryKey = 'id';
public static table = 'dataset_identifiers';
public static fillable: string[] = ['value', 'label', 'type', 'relation'];
@column({
isPrimary: true,
})
public id: number;
@column({})
public document_id: number;
@column({})
public type: string;
@column({})
public value: string;
@column.dateTime({
autoCreate: true,
})
public created_at?: DateTime;
@column.dateTime({
autoCreate: true,
autoUpdate: true,
})
public updated_at?: DateTime;
@belongsTo(() => Dataset, {
foreignKey: 'document_id',
})
public dataset: BelongsTo<typeof Dataset>;
}

View File

@ -73,15 +73,12 @@ export default class CreateDatasetValidator {
depth_min: schema.number.optional([rules.requiredIfExists('depth_max')]), depth_min: schema.number.optional([rules.requiredIfExists('depth_max')]),
depth_max: schema.number.optional([rules.requiredIfExists('depth_min')]), depth_max: schema.number.optional([rules.requiredIfExists('depth_min')]),
}), }),
references: schema.array([rules.uniqueArray('value')]).members( references: schema.array.optional([rules.uniqueArray('value')]).members(
schema.object().members({ schema.object().members({
value: schema.string({ trim: true }, [ value: schema.string({ trim: true }, [rules.minLength(3), rules.maxLength(255)]),
rules.minLength(3),
rules.maxLength(255),
]),
type: schema.enum(Object.values(ReferenceIdentifierTypes)), type: schema.enum(Object.values(ReferenceIdentifierTypes)),
relation: schema.enum(Object.values(RelationTypes)), relation: schema.enum(Object.values(RelationTypes)),
// language: schema.string({ trim: true }, [rules.minLength(2), rules.maxLength(255)]), label: schema.string({ trim: true }, [rules.minLength(2), rules.maxLength(255)]),
}), }),
), ),
subjects: schema.array([rules.minLength(3), rules.uniqueArray('value')]).members( subjects: schema.array([rules.minLength(3), rules.uniqueArray('value')]).members(
@ -102,7 +99,7 @@ export default class CreateDatasetValidator {
files: schema.array([rules.minLength(1)]).members( files: schema.array([rules.minLength(1)]).members(
schema.file({ schema.file({
size: '100mb', size: '100mb',
extnames: ['jpg', 'gif', 'png'], extnames: ['jpg', 'gif', 'png', 'tif'],
}), }),
), ),
// upload: schema.object().members({ // upload: schema.object().members({
@ -162,6 +159,12 @@ export default class CreateDatasetValidator {
'subjects.*.type.required': 'keyword type is required', 'subjects.*.type.required': 'keyword type is required',
'subjects.*.language.required': 'language of keyword is required', 'subjects.*.language.required': 'language of keyword is required',
'references.*.value.required': 'Additional reference value is required, if defined',
'references.*.type.required': 'Additional reference identifier type is required',
'references.*.relation.required': 'Additional reference relation type is required',
'references.*.label.required': 'Additional reference label is required',
'files.minLength': 'At least {{ options.minLength }} file upload is required.',
'files.*.size': 'file size is to big', 'files.*.size': 'file size is to big',
'files.extnames': 'file extension is not supported', 'files.extnames': 'file extension is not supported',
}; };

View File

@ -11,7 +11,7 @@ export default class DocumentFiles extends BaseSchema {
.foreign('document_id', 'document_files_document_id_foreign') .foreign('document_id', 'document_files_document_id_foreign')
.references('id') .references('id')
.inTable('documents') .inTable('documents')
.onDelete('CASCADE') // delete this when document is deleted .onDelete('CASCADE') // delete this file when document is deleted
.onUpdate('CASCADE'); .onUpdate('CASCADE');
table.string('path_name').notNullable(); table.string('path_name').notNullable();
table.string('label'); table.string('label');

607
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -41,7 +41,7 @@
"babel-preset-typescript-vue3": "^2.0.17", "babel-preset-typescript-vue3": "^2.0.17",
"chart.js": "^4.2.0", "chart.js": "^4.2.0",
"eslint": "^8.32.0", "eslint": "^8.32.0",
"eslint-config-prettier": "^8.6.0", "eslint-config-prettier": "^9.0.0",
"eslint-plugin-adonis": "^2.1.1", "eslint-plugin-adonis": "^2.1.1",
"eslint-plugin-prettier": "^5.0.0-alpha.2", "eslint-plugin-prettier": "^5.0.0-alpha.2",
"naive-ui": "^2.34.3", "naive-ui": "^2.34.3",

View File

@ -907,26 +907,33 @@ Removes a selected keyword
<CardBox class="mb-6 shadow" has-table title="Dataset References" :header-icon="mdiPlusCircle" <CardBox class="mb-6 shadow" has-table title="Dataset References" :header-icon="mdiPlusCircle"
v-on:header-icon-click="addReference"> v-on:header-icon-click="addReference">
<table class="table table-hover" v-if="form.references.length"> <table class="table-fixed border-green-900" v-if="form.references.length">
<thead> <thead>
<tr> <tr>
<th>Value of the identifier</th> <th class="w-4/12">Value</th>
<th>Type</th> <th class="w-2/12">Type</th>
<th>Relation</th> <th class="w-3/12">Relation</th>
<th>Label</th> <th class="w-2/12">Label</th>
<th></th> <th class="w-1/12"></th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
<tr v-for="(item, index) in form.references"> <tr v-for="(item, index) in form.references">
<td data-label="Reference Value"> <td data-label="Reference Value">
<input name="Reference Value" class="form-control" <!-- <input name="Reference Value" class="form-control"
placeholder="[VALUE]" v-model="item.value" /> placeholder="[VALUE]" v-model="item.value" /> -->
<FormControl required v-model="item.value" :type="'text'" placeholder="[VALUE]"
:errors="form.errors.embargo_date">
<div class="text-red-400 text-sm"
v-if="form.errors[`references.${index}.value`] && Array.isArray(form.errors[`references.${index}.value`])">
{{ form.errors[`references.${index}.value`].join(', ') }}
</div>
</FormControl>
</td> </td>
<td> <td>
<FormControl required v-model="form.references[index].type" type="select" <FormControl required v-model="form.references[index].type" type="select"
:options="referenceIdentifierTypes" :options="referenceIdentifierTypes" placeholder="[type]">
placeholder="[identifier type]">
<div class="text-red-400 text-sm" <div class="text-red-400 text-sm"
v-if="Array.isArray(form.errors[`references.${index}.type`])"> v-if="Array.isArray(form.errors[`references.${index}.type`])">
{{ form.errors[`references.${index}.type`].join(', ') }} {{ form.errors[`references.${index}.type`].join(', ') }}
@ -940,8 +947,7 @@ Removes a selected keyword
'data-vv-scope' => 'step-2']) 'data-vv-scope' => 'step-2'])
!!} --> !!} -->
<FormControl required v-model="form.references[index].relation" type="select" <FormControl required v-model="form.references[index].relation" type="select"
:options="relationTypes" :options="relationTypes" placeholder="[relation type]">
placeholder="[relation type]">
<div class="text-red-400 text-sm" <div class="text-red-400 text-sm"
v-if="Array.isArray(form.errors[`references.${index}.relation`])"> v-if="Array.isArray(form.errors[`references.${index}.relation`])">
{{ form.errors[`references.${index}.relation`].join(', ') }} {{ form.errors[`references.${index}.relation`].join(', ') }}
@ -949,7 +955,14 @@ Removes a selected keyword
</FormControl> </FormControl>
</td> </td>
<td data-label="Reference Label"> <td data-label="Reference Label">
<input name="Reference Label" class="form-control" v-model="item.label" /> <!-- <input name="Reference Label" class="form-control" v-model="item.label" /> -->
<FormControl required v-model="form.references[index].label" type="text"
placeholder="[reference label]">
<div class="text-red-400 text-sm"
v-if="form.errors[`references.${index}.label`] && Array.isArray(form.errors[`references.${index}.label`])">
{{ form.errors[`references.${index}.label`].join(', ') }}
</div>
</FormControl>
</td> </td>
<td class="before:hidden lg:w-1 whitespace-nowrap"> <td class="before:hidden lg:w-1 whitespace-nowrap">
<!-- <BaseButton color="info" :icon="mdiEye" small @click="isModalActive = true" /> --> <!-- <BaseButton color="info" :icon="mdiEye" small @click="isModalActive = true" /> -->