import type { HttpContextContract } from '@ioc:Adonis/Core/HttpContext'; import User from 'App/Models/User'; import Dataset from 'App/Models/Dataset'; import License from 'App/Models/License'; import Project from 'App/Models/Project'; import Title from 'App/Models/Title'; import Description from 'App/Models/Description'; import Language from 'App/Models/Language'; // import CreateUserValidator from 'App/Validators/CreateUserValidator'; // import UpdateUserValidator from 'App/Validators/UpdateUserValidator'; import { schema, CustomMessages, rules } from '@ioc:Adonis/Core/Validator'; import dayjs from 'dayjs'; import Person from 'App/Models/Person'; import Database from '@ioc:Adonis/Lucid/Database'; import { TransactionClientContract } from '@ioc:Adonis/Lucid/Database'; import Subject from 'App/Models/Subject'; import CreateDatasetValidator from 'App/Validators/CreateDatasetValidator'; import { TitleTypes, DescriptionTypes, ContributorTypes, PersonNameTypes } from 'Contracts/enums'; import type { ModelQueryBuilderContract } from '@ioc:Adonis/Lucid/Orm'; export default class DatasetController { public async index({ auth, request, inertia }: HttpContextContract) { const user = (await User.find(auth.user?.id)) as User; const page = request.input('page', 1); let datasets: ModelQueryBuilderContract = Dataset.query(); // if (request.input('search')) { // // users = users.whereRaw('name like %?%', [request.input('search')]) // const searchTerm = request.input('search'); // datasets.where('name', 'ilike', `%${searchTerm}%`); // } if (request.input('sort')) { type SortOrder = 'asc' | 'desc' | undefined; let attribute = request.input('sort'); let sortOrder: SortOrder = 'asc'; if (attribute.substr(0, 1) === '-') { sortOrder = 'desc'; // attribute = substr(attribute, 1); attribute = attribute.substr(1); } datasets.orderBy(attribute, sortOrder); } else { // users.orderBy('created_at', 'desc'); datasets.orderBy('id', 'asc'); } // const users = await User.query().orderBy('login').paginate(page, limit); const myDatasets = await datasets .whereIn('server_state', [ 'inprogress', 'released', 'editor_accepted', 'approved', 'reviewed', 'rejected_editor', 'rejected_reviewer', ]) .where('account_id', user.id) .preload('titles') .preload('user', (query) => query.select('id', 'login')) // .preload('titles', (builder) => { // // pull the actual preload data // builder.where('type', 'Main'); // }) .paginate(page, 10); return inertia.render('Submitter/Dataset/Index', { // testing: 'this is a test', datasets: myDatasets.serialize(), filters: request.all(), can: { // create: await auth.user?.can(['dataset-submit']), edit: await auth.user?.can(['dataset-edit']), delete: await auth.user?.can(['dataset-delete']), }, }); } public async create({ inertia }: HttpContextContract) { const licenses = await License.query().select('id', 'name_long').pluck('name_long', 'id'); const projects = await Project.query().pluck('label', 'id'); const doctypes = { analysisdata: { label: 'Analysis', value: 'analysisdata' }, measurementdata: { label: 'Measurements', value: 'measurementdata' }, monitoring: 'Monitoring', remotesensing: 'Remote Sensing', gis: 'GIS', models: 'Models', mixedtype: 'Mixed Type', }; // const titletypes = { // Sub: 'Sub', // Alternative: 'Alternative', // Translated: 'Translated', // Other: 'Other', // }; // let test = Object.entries(TitleTypes).map(([key, value]) => ({id: key, value: value})) // const languages = await Database.from('languages').select('*').where('active', true); return inertia.render('Submitter/Dataset/Create', { licenses: licenses, doctypes: doctypes, titletypes: Object.entries(TitleTypes) .filter(([value]) => value !== 'Main') .map(([key, value]) => ({ value: key, label: value })), descriptiontypes: Object.entries(DescriptionTypes) .filter(([value]) => value !== 'Abstract') .map(([key, value]) => ({ value: key, label: value })), // descriptiontypes: DescriptionTypes projects: projects, }); } public async firstStep({ request, response }: HttpContextContract) { const newDatasetSchema = schema.create({ language: schema.string({ trim: true }, [ rules.regex(/^[a-zA-Z0-9-_]+$/), //Must be alphanumeric with hyphens or underscores ]), licenses: schema.array([rules.minLength(1)]).members(schema.number()), // define at least one license for the new dataset rights: schema.string([rules.equalTo('true')]), }); try { // Step 2 - Validate request body against the schema await request.validate({ schema: newDatasetSchema, messages: this.messages }); // console.log({ payload }); } catch (error) { // Step 3 - Handle errors // return response.badRequest(error.messages); throw error; } return response.redirect().back(); } public async secondStep({ request, response }: HttpContextContract) { const newDatasetSchema = schema.create({ // first step language: schema.string({ trim: true }, [ rules.regex(/^[a-zA-Z0-9-_]+$/), //Must be alphanumeric with hyphens or underscores ]), licenses: schema.array([rules.minLength(1)]).members(schema.number()), // define at least one license for the new dataset rights: schema.string([rules.equalTo('true')]), // second step type: schema.string({ trim: true }, [rules.minLength(3), rules.maxLength(255)]), creating_corporation: schema.string({ trim: true }, [rules.minLength(3), rules.maxLength(255)]), titles: schema.array([rules.minLength(1)]).members( schema.object().members({ value: schema.string({ trim: true }, [rules.minLength(3), rules.maxLength(255)]), type: schema.enum(Object.values(TitleTypes)), language: schema.string({ trim: true }, [ rules.minLength(2), rules.maxLength(255), rules.translatedLanguage('/language', 'type'), ]), }), ), descriptions: schema.array([rules.minLength(1)]).members( schema.object().members({ value: schema.string({ trim: true }, [rules.minLength(3), rules.maxLength(255)]), type: schema.enum(Object.values(DescriptionTypes)), language: schema.string({ trim: true }, [ rules.minLength(2), rules.maxLength(255), rules.translatedLanguage('/language', 'type'), ]), }), ), authors: schema.array([rules.minLength(1)]).members(schema.object().members({ email: schema.string({ trim: true }) })), // project_id: schema.number(), }); try { // Step 2 - Validate request body against the schema await request.validate({ schema: newDatasetSchema, messages: this.messages }); // console.log({ payload }); } catch (error) { // Step 3 - Handle errors // return response.badRequest(error.messages); throw error; } return response.redirect().back(); } public async thirdStep({ request, response }: HttpContextContract) { const newDatasetSchema = schema.create({ // first step language: schema.string({ trim: true }, [ rules.regex(/^[a-zA-Z0-9-_]+$/), //Must be alphanumeric with hyphens or underscores ]), licenses: schema.array([rules.minLength(1)]).members(schema.number()), // define at least one license for the new dataset rights: schema.string([rules.equalTo('true')]), // second step type: schema.string({ trim: true }, [rules.minLength(3), rules.maxLength(255)]), creating_corporation: schema.string({ trim: true }, [rules.minLength(3), rules.maxLength(255)]), titles: schema.array([rules.minLength(1)]).members( schema.object().members({ value: schema.string({ trim: true }, [rules.minLength(3), rules.maxLength(255)]), type: schema.enum(Object.values(TitleTypes)), language: schema.string({ trim: true }, [ rules.minLength(2), rules.maxLength(255), rules.translatedLanguage('/language', 'type'), ]), }), ), descriptions: schema.array([rules.minLength(1)]).members( schema.object().members({ value: schema.string({ trim: true }, [rules.minLength(3), rules.maxLength(255)]), type: schema.enum(Object.values(DescriptionTypes)), language: schema.string({ trim: true }, [ rules.minLength(2), rules.maxLength(255), rules.translatedLanguage('/language', 'type'), ]), }), ), authors: schema.array([rules.minLength(1)]).members(schema.object().members({ email: schema.string({ trim: true }) })), // third step project_id: schema.number.optional(), embargo_date: schema.date.optional({ format: 'yyyy-MM-dd' }, [rules.after(10, 'days')]), coverage: schema.object().members({ x_min: schema.number(), x_max: schema.number(), y_min: schema.number(), y_max: schema.number(), elevation_absolut: schema.number.optional(), elevation_min: schema.number.optional([rules.requiredIfExists('elevation_max')]), elevation_max: schema.number.optional([rules.requiredIfExists('elevation_min')]), depth_absolut: schema.number.optional(), depth_min: schema.number.optional([rules.requiredIfExists('depth_max')]), depth_max: schema.number.optional([rules.requiredIfExists('depth_min')]), }), subjects: schema.array([rules.minLength(3), rules.uniqueArray('value')]).members( schema.object().members({ value: schema.string({ trim: true }, [ rules.minLength(3), rules.maxLength(255), // rules.unique({ table: 'dataset_subjects', column: 'value' }), ]), // type: schema.enum(Object.values(TitleTypes)), language: schema.string({ trim: true }, [rules.minLength(2), rules.maxLength(255)]), }), ), }); try { // Step 2 - Validate request body against the schema await request.validate({ schema: newDatasetSchema, messages: this.messages }); // console.log({ payload }); } catch (error) { // Step 3 - Handle errors // return response.badRequest(error.messages); throw error; } return response.redirect().back(); } public async store({ auth, request, response, session }: HttpContextContract) { // node ace make:validator CreateDataset try { // Step 2 - Validate request body against the schema // await request.validate(CreateUserValidator); // await request.validate({ schema: newDatasetSchema, messages: this.messages }); await request.validate(CreateDatasetValidator); // console.log({ payload }); } catch (error) { // Step 3 - Handle errors // return response.badRequest(error.messages); throw error; } // const user = new User(); // user.email = 'example@example.com'; // user.password = 'password'; // await user.useTransaction(trx).save(); let trx: TransactionClientContract | null = null; try { trx = await Database.transaction(); const user = (await User.find(auth.user?.id)) as User; // const dataset = await user.related('datasets').create({ // type: request.input('type'), // creatingCorporation: request.input('creating_corporation'), // language: request.input('language'), // }); // Create a new instance of the Dataset model: const dataset = new Dataset(); dataset.type = request.input('type'); dataset.creatingCorporation = request.input('creating_corporation'); dataset.language = request.input('language'); dataset.embargoDate = request.input('embargo_date'); //await dataset.related('user').associate(user); // speichert schon ab // Dataset.$getRelation('user').boot(); // Dataset.$getRelation('user').setRelated(dataset, user); // dataset.$setRelated('user', user); await user.useTransaction(trx).related('datasets').save(dataset); //store licenses: const licenses: number[] = request.input('licenses', []); dataset.useTransaction(trx).related('licenses').sync(licenses); const authors = request.input('authors', []); for (const [key, person] of authors.entries()) { const pivotData = { role: 'author', sort_order: key + 1 }; if (person.id !== undefined) { await dataset .useTransaction(trx) .related('persons') .attach({ [person.id]: { role: pivotData.role, sort_order: pivotData.sort_order, allow_email_contact: false, }, }); } else { const dataPerson = new Person(); // use overwritten fill method dataPerson.fill(person); await dataset.useTransaction(trx).related('persons').save(dataPerson, false, { role: pivotData.role, sort_order: pivotData.sort_order, allow_email_contact: false, }); } } const contributors = request.input('contributors', []); for (const [key, person] of contributors.entries()) { const pivotData = { role: 'contributor', sort_order: key + 1 }; if (person.id !== undefined) { await dataset .useTransaction(trx) .related('persons') .attach({ [person.id]: { role: pivotData.role, sort_order: pivotData.sort_order, allow_email_contact: false, }, }); } else { const dataPerson = new Person(); // use overwritten fill method dataPerson.fill(person); await dataset.useTransaction(trx).related('persons').save(dataPerson, false, { role: pivotData.role, sort_order: pivotData.sort_order, allow_email_contact: false, }); } } //save main and additional titles const titles = request.input('titles', []); for (const titleData of titles) { const title = new Title(); title.value = titleData.value; title.language = titleData.language; title.type = titleData.type; await dataset.useTransaction(trx).related('titles').save(title); } //save abstract and additional descriptions const descriptions = request.input('descriptions', []); for (const descriptionData of descriptions) { // $descriptionReference = new Description($description); // $dataset->abstracts()->save($descriptionReference); const description = new Description(); description.value = descriptionData.value; description.language = descriptionData.language; description.type = descriptionData.type; await dataset.useTransaction(trx).related('descriptions').save(description); } //save keywords const keywords = request.input('subjects', []); for (const keywordData of keywords) { // $dataKeyword = new Subject($keyword); // $dataset->subjects()->save($dataKeyword); const keyword = await Subject.firstOrNew({ value: keywordData.value, type: keywordData.type }, keywordData); if (keyword.$isNew === true) { await dataset.useTransaction(trx).related('subjects').save(keyword); } else { await dataset.useTransaction(trx).related('subjects').attach([keyword.id]); } } // Dataset.$getRelation('persons').boot(); // Dataset.$getRelation('persons').pushRelated(dataset, person) // dataset.$pushRelated('persons', person); await trx.commit(); console.log('Users and posts created successfully'); } catch (error) { if (trx !== null) { await trx.rollback(); } console.error('Failed to create dataset and related models:', error); // Handle the error and exit the controller code accordingly // session.flash('message', 'Failed to create dataset and relaed models'); // return response.redirect().back(); throw error; } // save data files: const coverImage = request.files('files')[0]; if (coverImage) { // clientName: 'Gehaltsschema.png' // extname: 'png' // fieldName: 'file' // size: 135624 //const datasetFolder = 'files/' . dataset->id; // await coverImage.moveToDisk('./') await coverImage.moveToDisk( '/test_dataset2', { name: 'renamed-file-name.jpg', overwrite: true, // overwrite in case of conflict }, 'local', ); // let path = coverImage.filePath; } session.flash('message', 'Dataset has been created successfully'); // return response.redirect().toRoute('user.index'); return response.redirect().back(); } public messages: CustomMessages = { 'minLength': '{{ field }} must be at least {{ options.minLength }} characters long', 'maxLength': '{{ field }} must be less then {{ options.maxLength }} characters long', 'required': '{{ field }} is required', 'unique': '{{ field }} must be unique, and this value is already taken', // 'confirmed': '{{ field }} is not correct', 'licences.minLength': 'at least {{ options.minLength }} permission must be defined', 'licences.*.number': 'Define roles as valid numbers', 'rights.equalTo': 'you must agree to continue', 'titles.0.value.minLength': 'Main Title must be at least {{ options.minLength }} characters long', 'titles.0.value.required': 'Main Title is required', 'titles.*.value.required': 'Additional title is required, if defined', 'titles.*.type.required': 'Additional title type is required', 'titles.*.language.required': 'Additional title language is required', 'titles.*.language.translatedLanguage': 'The language of the translated title must be different from the language of the dataset', 'descriptions.0.value.minLength': 'Main Abstract must be at least {{ options.minLength }} characters long', 'descriptions.0.value.required': 'Main Abstract is required', 'descriptions.*.value.required': 'Additional description is required, if defined', 'descriptions.*.type.required': 'Additional description type is required', 'descriptions.*.language.required': 'Additional description language is required', 'descriptions.*.language.translatedLanguage': 'The language of the translated description must be different from the language of the dataset', 'authors.minLength': 'at least {{ options.minLength }} author must be defined', 'after': `{{ field }} must be older than ${dayjs().add(10, 'day')}`, 'subjects.minLength': 'at least {{ options.minLength }} keywords must be defined', 'subjects.uniqueArray': 'The {{ options.array }} array must have unique values based on the {{ options.field }} attribute.', 'subjects.*.value.required': 'keyword value is required', 'subjects.*.value.minLength': 'keyword value must be at least {{ options.minLength }} characters long', 'subjects.*.type.required': 'keyword type is required', 'subjects.*.language.required': 'language of keyword is required', 'files.*.size': 'file size is to big', 'files.extnames': 'file extension is not supported', }; // public async release({ params, view }) { public async release({ request, inertia, response }: HttpContextContract) { const id = request.param('id'); const dataset = await Dataset.query() .preload('user', (builder) => { builder.select('id', 'login'); }) .where('id', id) .firstOrFail(); const validStates = ['inprogress', 'rejected_editor']; if (!validStates.includes(dataset.server_state)) { // session.flash('errors', 'Invalid server state!'); return response .flash( 'warning', `Invalid server state. Dataset with id ${id} cannot be released to editor. Datset has server state ${dataset.server_state}.`, ) .redirect() .back(); } return inertia.render('Submitter/Dataset/Release', { dataset, }); } public async releaseUpdate({ request, response }: HttpContextContract) { const id = request.param('id'); const dataset = await Dataset.query().preload('files').where('id', id).firstOrFail(); const validStates = ['inprogress', 'rejected_editor']; if (!validStates.includes(dataset.server_state)) { // throw new Error('Invalid server state!'); // return response.flash('warning', 'Invalid server state. Dataset cannot be released to editor').redirect().back(); return response .flash( 'warning', `Invalid server state. Dataset with id ${id} cannot be released to editor. Datset has server state ${dataset.server_state}.`, ) .redirect() .toRoute('dataset.list'); } if (dataset.files.length === 0) { return response.flash('warning', 'At least minimum one file is required.').redirect('back'); } const preferation = request.input('preferation', ''); const preferredReviewer = request.input('preferred_reviewer'); const preferredReviewerEmail = request.input('preferred_reviewer_email'); if (preferation === 'yes_preferation') { const newSchema = schema.create({ preferred_reviewer: schema.string({ trim: true }, [rules.minLength(3), rules.maxLength(255)]), preferred_reviewer_email: schema.string([rules.email()]), }); try { await request.validate({ schema: newSchema }); } catch (error) { // return response.badRequest(error.messages); throw error; } } const input = { preferred_reviewer: preferredReviewer || null, preferred_reviewer_email: preferredReviewerEmail || null, server_state: 'released', editor_id: null, reviewer_id: null, reject_editor_note: null, reject_reviewer_note: null, }; // Clear editor_id if it exists if (dataset.editor_id !== null) { input.editor_id = null; } // Clear reject_editor_note if it exists if (dataset.reject_editor_note !== null) { input.reject_editor_note = null; } // Clear reviewer_id if it exists if (dataset.reviewer_id !== null) { input.reviewer_id = null; } // Clear reject_reviewer_note if it exists if (dataset.reject_reviewer_note !== null) { input.reject_reviewer_note = null; } if (await dataset.merge(input).save()) { return response.toRoute('dataset.list').flash('message', 'You have released your dataset!'); } // throw new GeneralException(trans('exceptions.publish.release.update_error')); } public async edit({ params, inertia }) { const datasetQuery = Dataset.query().where('id', params.id); datasetQuery.preload('titles').preload('descriptions').preload('coverage'); const dataset = await datasetQuery.firstOrFail(); // await dataset.loadMany([ // 'licenses', // 'authors', // 'contributors', // 'titles', // 'abstracts', // 'files', // 'coverage', // 'subjects', // 'references', // ]); const titleTypes = Object.entries(TitleTypes) // .filter(([value]) => value !== 'Main') .map(([key, value]) => ({ value: key, label: value })); const descriptionTypes = Object.entries(DescriptionTypes) // .filter(([value]) => value !== 'Abstract') .map(([key, value]) => ({ value: key, label: value })); const languages = await Language.query().where('active', true).pluck('part1', 'part1'); // const contributorTypes = Config.get('enums.contributor_types'); const contributorTypes = Object.entries(ContributorTypes).map(([key, value]) => ({ value: key, label: value })); // const nameTypes = Config.get('enums.name_types'); const nameTypes = Object.entries(PersonNameTypes).map(([key, value]) => ({ value: key, label: value })); // const messages = await Database.table('messages') // .pluck('help_text', 'metadata_element'); const projects = await Project.query().pluck('label', 'id'); const currentDate = new Date(); const currentYear = currentDate.getFullYear(); const years = Array.from({ length: currentYear - 1990 + 1 }, (_, index) => 1990 + index); // const licenses = await License.query() // .select('id', 'name_long', 'link_licence') // .orderBy('sort_order') // .fetch(); const licenses = await License.query().select('id', 'name_long', 'link_licence').orderBy('sort_order'); // const checkeds = dataset.licenses.first().id; const keywordTypes = { uncontrolled: 'uncontrolled', swd: 'swd', }; const referenceTypes = ['DOI', 'Handle', 'ISBN', 'ISSN', 'URL', 'URN']; const relationTypes = [ 'IsSupplementTo', 'IsSupplementedBy', 'IsContinuedBy', 'Continues', 'IsNewVersionOf', 'IsPartOf', 'HasPart', 'Compiles', 'IsVariantFormOf', ]; const doctypes = { analysisdata: { label: 'Analysis', value: 'analysisdata' }, measurementdata: { label: 'Measurements', value: 'measurementdata' }, monitoring: 'Monitoring', remotesensing: 'Remote Sensing', gis: 'GIS', models: 'Models', mixedtype: 'Mixed Type', }; return inertia.render('Submitter/Dataset/Edit', { dataset, titletypes: titleTypes, descriptiontypes: descriptionTypes, contributorTypes, nameTypes, languages, // messages, projects, licenses, // checkeds, years, // languages, keywordTypes, referenceTypes, relationTypes, doctypes, }); } }