solved timeout error via fetch response

This commit is contained in:
Robert Nasarek 2022-08-31 11:03:41 +02:00
parent ba9d9e6abb
commit 1d68336a06
9 changed files with 246 additions and 110 deletions

View file

@ -44,6 +44,7 @@ function createWindow() {
mainWindow = new BrowserWindow({
width: 400,
height: 600,
icon: __dirname + '/marvin.ico',
show: false,
webPreferences: {
preload: path.join(__dirname, 'preload.js'),

BIN
marvin.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 41 KiB

View file

@ -1,3 +1,3 @@
{
"rootDir": "/home/rbrt/Schreibtisch"
"rootDir": "/home/rbrt/Schreibtisch/marvin"
}

View file

@ -1,5 +1,3 @@
// Modules
import createReport from 'docx-templates';
import fs from 'fs';
@ -7,23 +5,49 @@ import { mkdir } from 'node:fs/promises';
import path from 'path';
import replaceSpecialCharacters from "replace-special-characters";
// Components
import ObjektkatalogApi from './ObjektkatalogApi';
////////////////////
// Main //
////////////////////
// Fills the docx template.
export async function fillTemplate(log, objectData) {
// Variables
let buffer;
let configJson
// Create docx document.
// Create docx document if there was a response.
if (objectData.httpStatus === 200) {
let documentInfo;
try {
// Load config file.
let ConfigFile = fs.readFileSync('resources/config/config.json')
configJson = JSON.parse(ConfigFile)
// Choose the template for selected document type.
switch (objectData.dokumenttyp) {
case 'rp':
documentInfo = {
documentType: 'restaurierungsprotokolle',
templateFile: 'rp-template.docx',
}
break;
case 'lbb':
documentInfo = {
documentType: 'leihgabenbegleitblaetter',
templateFile: 'lbb-template.docx',
}
break;
case 'a':
documentInfo = {
documentType: 'analysen',
templateFile: 'a-template.docx',
}
break;
}
// Read template.
const template = fs.readFileSync('resources/templates/rp-template.docx');
const template = fs.readFileSync(path.join('resources/templates/', documentInfo.templateFile));
// Create report.
buffer = await createReport({
template,
@ -38,32 +62,40 @@ export async function fillTemplate(log, objectData) {
}
});
} catch (err) {
// Set error log.
log = {
...log,
status: 'red',
message: 'Konnte Template nicht erstellen: ' + err,
tip: 'Ist das Template vorhanden?',
};
return log
}
const folderPath = path.join(configJson.rootDir, objectData.inventarnummer);
// Create Folder if necessary.
// Create folder (if necessary).
const objectPath = path.join(configJson.rootDir, objectData.inventarnummer);
const documentPath = path.join(objectPath, documentInfo.documentType);
const temporaryWorkDirPath = path.join(objectPath, 'werkstatt');
try {
const createDir = await mkdir(folderPath, {recursive: true});
const createDocumentPath = await mkdir(documentPath, {recursive: true});
const createTemporaryWorkDirPath = await mkdir(temporaryWorkDirPath, {recursive: true});
} catch (err) {
// Set error log.
log = {
...log,
status: 'red',
message: 'Konnte den Pfad nicht erstellen' + err,
tip: 'Bestehen Schreibrechte auf dem Ordner?'
}
return log
}
// Write document to disk.
// Write document to disk.
if (buffer) {
const normCharacterTitle = replaceSpecialCharacters(objectData.titel)
const normSpacingTitle = normCharacterTitle.replace(/[^A-Z0-9]+/ig, "_");
const filename = objectData.datum + '_' + normSpacingTitle + '.docx'
fs.writeFileSync(path.join(folderPath, filename), buffer)
fs.writeFileSync(path.join(documentPath, filename), buffer)
log = {
...log,
status: 'green',

View file

@ -1,9 +1,11 @@
const {parseString} = require("xml2js");
/*
* Sends Object ID to Objektkatalog-API and receives JSON
* Properties and functions for communication with Objektkatalog-API.
*/
class ObjektkatalogApi {
// Variables
json;
raw;
receivedData = {
@ -17,62 +19,97 @@ class ObjektkatalogApi {
materialTechnik: '',
titel: '',
};
response;
visibility;
// Get data from objektkatalog.gnm.de.
async getData(objectId) {
if (objectId) {
const response = await fetch('https://objektkatalog.gnm.de/rest_export/' + objectId);
if (response.status === 200) {
const responseJson = await response.json(); //extract JSON from the http response
if (responseJson.length !== 0) {
this.raw = {
eid: responseJson[0]['eid'],
allgemeineBezeichnung: responseJson[0]['fc6392714594e73ddb2fa363815a8fdf'],
beschreibung: responseJson[0]['f81f557caccf45074edfb65ff077011f'],
darstellung: responseJson[0]['f217e1053b1e29411d4b00f3b1e1d52b'],
erwerbsmethode: responseJson[0]['fa4aa035d411275cd36a2fbb5c159a2c'],
fruehstes: responseJson[0]['ff4a178095c895a12fce6320c04ba0b0'],
fundort: responseJson[0]['f1c2bf9f6f3d78302bbfa9cc8b3e439c'],
gehoertZuAggregation: responseJson[0]['fca2cff9e713e02b93be1f9f19dff88e'],
herstellerString: responseJson[0]['f9f9408fdacc1230497c591c89655777'],
herstellungsdatum: responseJson[0]['fd9f0912229c78d94dd6f807e683d06e'],
herstellungsort: responseJson[0]['fbbccf2979c1143b0fa25325a849b121'],
individuelleEinordnung: responseJson[0]['f8e7a568a6a0fb8907b41f5ba8a9cabe'],
inventarnummer: responseJson[0]['f9830c2c7747a792ebbe1ca2587d1f26'],
klassifikation: responseJson[0]['fdcd0101d5c77884d185560aa7521645'],
masse: responseJson[0]['ffdac65ffbb12438e2fef5b26533c909'],
materialTechnik: responseJson[0]['f23b67bd18913642fc3936c1f2af5682'],
muster: responseJson[0]['f88917aeadae224c8d2c1fec35720dc8'],
provisio: responseJson[0]['f930a6ca774cc5640694c5aa081c1e66'],
sammlung: responseJson[0]['ffd22ba4399f8d62e61c4e99463dddaa'],
spaetestens: responseJson[0]['f1c2bf9f6f3d78302bbfa9cc8b3e439c'],
standort: responseJson[0]['f95e9be48ed110cf4f92298712d364bd'],
titel: responseJson[0]['f2049b2456b20f8fd80f714e154f1d47'],
unterbringung: responseJson[0]['f15265e39237868a568ee63453492b17'],
vitrinentext: responseJson[0]['fe5e3ff18aedf1d25c639dc79fb24ad1'],
zustandsbeschreibung: responseJson[0]['f54b6da6006ea444c33a348b8c4370a8']
// RESTful GET.
this.response = await fetch('https://objektkatalog.gnm.de/rest_export/' + objectId).then((response) => {
if (response.status >= 400 && response.status < 600) {
throw new Error("Bad response from server: " + response.status);
}
return response;
}).then((returnedResponse) => {
// Everything went well.
return returnedResponse
}).catch((error) => {
// Error while fetch.
return {status: 503};
});
switch (this.response.status) {
case 200:
// Parse to json.
const responseJson = await this.response.json(); //extract JSON from the http response
if (responseJson.length !== 0) {
// Json with raw data (LIDO inside!)
this.raw = {
eid: responseJson[0]['eid'],
allgemeineBezeichnung: responseJson[0]['fc6392714594e73ddb2fa363815a8fdf'],
beschreibung: responseJson[0]['f81f557caccf45074edfb65ff077011f'],
darstellung: responseJson[0]['f217e1053b1e29411d4b00f3b1e1d52b'],
erwerbsmethode: responseJson[0]['fa4aa035d411275cd36a2fbb5c159a2c'],
fruehstes: responseJson[0]['ff4a178095c895a12fce6320c04ba0b0'],
fundort: responseJson[0]['f1c2bf9f6f3d78302bbfa9cc8b3e439c'],
gehoertZuAggregation: responseJson[0]['fca2cff9e713e02b93be1f9f19dff88e'],
herstellerString: responseJson[0]['f9f9408fdacc1230497c591c89655777'],
herstellungsdatum: responseJson[0]['fd9f0912229c78d94dd6f807e683d06e'],
herstellungsort: responseJson[0]['fbbccf2979c1143b0fa25325a849b121'],
individuelleEinordnung: responseJson[0]['f8e7a568a6a0fb8907b41f5ba8a9cabe'],
inventarnummer: responseJson[0]['f9830c2c7747a792ebbe1ca2587d1f26'],
klassifikation: responseJson[0]['fdcd0101d5c77884d185560aa7521645'],
masse: responseJson[0]['ffdac65ffbb12438e2fef5b26533c909'],
materialTechnik: responseJson[0]['f23b67bd18913642fc3936c1f2af5682'],
muster: responseJson[0]['f88917aeadae224c8d2c1fec35720dc8'],
provisio: responseJson[0]['f930a6ca774cc5640694c5aa081c1e66'],
sammlung: responseJson[0]['ffd22ba4399f8d62e61c4e99463dddaa'],
spaetestens: responseJson[0]['f1c2bf9f6f3d78302bbfa9cc8b3e439c'],
standort: responseJson[0]['f95e9be48ed110cf4f92298712d364bd'],
titel: responseJson[0]['f2049b2456b20f8fd80f714e154f1d47'],
unterbringung: responseJson[0]['f15265e39237868a568ee63453492b17'],
vitrinentext: responseJson[0]['fe5e3ff18aedf1d25c639dc79fb24ad1'],
zustandsbeschreibung: responseJson[0]['f54b6da6006ea444c33a348b8c4370a8']
}
break;
} else {
// No json, no data form.
this.receivedData = {
...this.receivedData,
httpStatus: 404,
}
this.visibility = false
return [this.receivedData, this.visibility]
}
} else {
// No JSON
case 408:
// No response, no data form.
this.receivedData = {
...this.receivedData,
httpStatus: 404,
httpStatus: 408,
}
this.visibility = false
return [this.receivedData, this.visibility]
case 503:
// Objektkatalog not reachable.
this.receivedData = {
...this.receivedData,
httpStatus: 503,
}
this.visibility = false
return [this.receivedData, this.visibility]
default:
// Any other error
this.receivedData = {
...this.receivedData,
httpStatus: 500,
}
this.visibility = false
return [this.receivedData, this.visibility]
}
} else {
// No response
this.receivedData= {
...this.receivedData,
httpStatus: 404,
}
this.visibility = false
return [this.receivedData, this.visibility]
}
} else {
// No ObjectId
// No ObjectId, no data form.
this.receivedData = {
...this.receivedData,
httpStatus: 500,
@ -84,7 +121,8 @@ class ObjektkatalogApi {
// Datum
// Default date of today
let today = new Date()
let todayFormat = today.getFullYear() + '-' + (String(today.getMonth() + 1).padStart(2, '0')) + '-' + String(today.getDate()).padStart(2, '0'); ;
let todayFormat = today.getFullYear() + '-' + (String(today.getMonth() + 1).padStart(2, '0')) + '-' + String(today.getDate()).padStart(2, '0');
;
// Inventarnummer
let inventarnummer;
@ -102,7 +140,7 @@ class ObjektkatalogApi {
titel = 'unbekannt';
}
// Hersteller
// Hersteller (LIDO format!)
let herstellerArray = [];
let hersteller;
@ -141,7 +179,7 @@ class ObjektkatalogApi {
materialTechnik = 'unbekannt';
}
// Maße
// Maße (LIDO format!)
let masse;
if (this.raw.masse.length !== 0) {
parseString(this.raw.masse[0].value, (err, result) => {
@ -149,18 +187,19 @@ class ObjektkatalogApi {
})
}
this.receivedData ={
...this.receivedData,
datum: todayFormat,
titel: titel,
hersteller: hersteller,
herstellungsort: herstellungsort,
herstellungsdatum: herstellungsdatum,
inventarnummer: inventarnummer,
materialTechnik: materialTechnik,
masse: masse,
httpStatus: 200,
}
// Json with important data.
this.receivedData = {
...this.receivedData,
datum: todayFormat,
titel: titel,
hersteller: hersteller,
herstellungsort: herstellungsort,
herstellungsdatum: herstellungsdatum,
inventarnummer: inventarnummer,
materialTechnik: materialTechnik,
masse: masse,
httpStatus: 200,
}
this.visibility = true
return [this.receivedData, this.visibility]
}

View file

@ -167,6 +167,7 @@ label {
.open {
height: 550px;
overflow: hidden;
}
p {
margin: 0;

View file

@ -41,6 +41,7 @@ export const App = () => {
const [objectData, setObjectData] = useState(
{
datum: '',
dokumenttyp: '',
hersteller: '',
herstellungsdatum: '',
herstellungsort: '',
@ -57,64 +58,110 @@ export const App = () => {
);
// Handler
// Get userinput from main form, ask Objektkatalog API for Data and fill check up form.
const getDataAndAskForCheckUpClickHandler = async (e) => {
// Show loading widget.
setIsLoading(true)
// Prevent page reload.
e.preventDefault()
let objectId = ''
objectId = e.target.form.objectId.value
// Get objectId from user input.
// Get objectId and document type from user input.
let objectId = e.target.form.objectId.value
let documentType = e.target.form.documentType.value
// Set object state.
await setObjectData((prevState) => {
return {
...prevState,
inventarnummer: objectId,
dokumenttyp: documentType,
}
}
)
if (objectId) {
// Get object data from objektkatalog.gnm.de
// Get object data from objektkatalog.gnm.de.
const [receivedObjectData, receivedVisibility] = await objektkatalogApi.getData(objectId);
if (receivedObjectData.httpStatus !== 200) {
await setLogState({
log: {
status: 'red',
message: 'Kein Objekt mit dieser Inventarnummer gefunden!',
code: '',
tip: '',
}, // with message, code, tip
logClass: 'active'
})
await setCheckUpVisibility(false)
} else {
// Fill and open check up form
await setObjectData(receivedObjectData)
await setCheckUpVisibility(true)
await setLogState({
log: {
status: '',
message: '',
code: '',
tip: '',
}, // with message, code, tip
logClass: 'inactive'
})
// If no response from Objektkatalog.
switch (receivedObjectData.httpStatus) {
// Server not reachable.
case 503:
await setLogState({
log: {
status: 'red',
message: 'Objektkatalog nicht erreichbar!',
code: '503',
tip: 'Sind sie mit dem Internet verbunden und ist der Objektkatalog online?',
}, // with message, code, tip
logClass: 'active'
})
// Hide Loading
setIsLoading(false)
return;
case 200:
// Fill and open check up form.
await setObjectData(prevState => {
return {
...prevState,
...receivedObjectData
}
})
// Show check up form.
await setCheckUpVisibility(true)
// Set success log but hide.
await setLogState({
log: {
status: '',
message: '',
code: '',
tip: '',
},
logClass: 'inactive'
})
break;
default:
// Set error log state.
await setLogState({
log: {
status: 'red',
message: 'Kein Objekt mit dieser Inventarnummer gefunden!',
code: '',
tip: 'Groß-/Kleinschreibung beachten und keine Leerzeichen verwenden!',
}, // with message, code, tip
logClass: 'active'
})
// Hide Loading
setIsLoading(false)
// Hide check up form.
await setCheckUpVisibility(false)
}
} else {
// Set error log.
await setLogState({
log: {
status: 'red',
message: 'Bitte Inventarnummer eingeben!',
code: '',
tip: '',
}, // with message, code, tip
},
logClass: 'active'
})
// Hide check up form.
await setCheckUpVisibility(false)
}
// Hide loading widget.
setIsLoading(false)
}
// Component with
// * Navigation (Nav) *
// * Main form (this)*
// * Loading widget (LoadingIcon)*
// * Checkup form (DataForm)*
// * Log (Log)*
return (
<div className="App">
@ -128,10 +175,10 @@ export const App = () => {
<form id={"object-id-form"} className={"flex-wrap"}>
<div className={"center column flex justify-content-space-between "}>
<label htmlFor="document-type" className={"center cut v-distance"}>Dokumenttyp:</label>
<select name="document-type" id="document-type" className={"input-field center cut"}>
<option value="restaurierungsprotokoll">Restaurierungsprotokoll</option>
<option value="leihgabenbegleitblatt">Leihgabenbegleitblatt</option>
<option value="analyse">Analyse</option>
<select name="documentType" id="document-type" className={"input-field center cut"}>
<option value="rp">Restaurierungsprotokoll</option>
<option value="lbb">Leihgabenbegleitblatt</option>
<option value="a">Analyse</option>
</select>
</div>
<div className={"center column flex full justify-content-space-between"}>

View file

@ -8,10 +8,17 @@ import React from 'react'
// Main //
////////////////////
// Renders check up form and gives the possibility to change received values.
export const DataForm = ({checkUpVisibility, objectData, setObjectData, logState, setLogState}) => {
// Variables
let log;
// Handlers
// Change handler response to every keystroke!
// Change date.
const datumChangeHandler = (e) => {
setObjectData((prevState) => {
return {
@ -20,7 +27,7 @@ export const DataForm = ({checkUpVisibility, objectData, setObjectData, logState
}
})
}
// Change identifier.
const inventarnummerChangeHandler = (e) => {
setObjectData((prevState) => {
return {
@ -29,7 +36,7 @@ export const DataForm = ({checkUpVisibility, objectData, setObjectData, logState
}
})
}
// Change title.
const titelChangeHandler = (e) => {
setObjectData((prevState) => {
return {
@ -39,6 +46,7 @@ export const DataForm = ({checkUpVisibility, objectData, setObjectData, logState
})
}
// Change creator.
const herstellerChangeHandler = (e) => {
setObjectData((prevState) => {
return {
@ -47,7 +55,7 @@ export const DataForm = ({checkUpVisibility, objectData, setObjectData, logState
}
})
}
// Change creation place.
const herstellungsortChangeHandler = (e) => {
setObjectData((prevState) => {
return {
@ -57,6 +65,7 @@ export const DataForm = ({checkUpVisibility, objectData, setObjectData, logState
})
}
// Change creation date.
const herstellungsdatumChangeHandler = (e) => {
setObjectData((prevState) => {
return {
@ -66,6 +75,7 @@ export const DataForm = ({checkUpVisibility, objectData, setObjectData, logState
})
}
// Change material and technic.
const materialTechnikChangeHandler = (e) => {
setObjectData((prevState) => {
return {
@ -75,6 +85,7 @@ export const DataForm = ({checkUpVisibility, objectData, setObjectData, logState
})
}
// Change sizes.
const masseChangeHandler = (e) => {
setObjectData((prevState) => {
return {
@ -84,6 +95,7 @@ export const DataForm = ({checkUpVisibility, objectData, setObjectData, logState
})
}
// Fill the template and save it.
const fillTemplateClickHandler = async (e) => {
e.preventDefault();
log = await fillTemplate(logState, objectData);
@ -94,6 +106,7 @@ export const DataForm = ({checkUpVisibility, objectData, setObjectData, logState
});
}
// Renders data form
return (<div className={'checkup ' + (checkUpVisibility ? 'open' : 'closed')}>
<form className={'flex column '}>
<div >

View file

@ -42,11 +42,14 @@ module.exports = {
],
devtool: 'cheap-source-map',
devServer: {
static: path.resolve(__dirname, 'dist'),
// Tell the server where to serve the content from.
static: {
directory: path.resolve(__dirname, 'dist'),
},
onBeforeSetupMiddleware() {
spawn(
'electron',
['.'],
['--trace-warnings .'],
{ shell: true, env: process.env, stdio: 'inherit' }
)
.on('close', code => process.exit(0))