dataeditor api endpoints

This commit is contained in:
mrfry 2021-02-21 14:47:55 +01:00
parent 3e4fda9974
commit 5ca0abbabf
8 changed files with 373 additions and 116 deletions

View file

@ -37,6 +37,7 @@ import {
shouldSearchDataFile, shouldSearchDataFile,
loadJSON, loadJSON,
Result, Result,
isQuestionValid,
} from '../../utils/actions' } from '../../utils/actions'
import dbtools from '../../utils/dbtools' import dbtools from '../../utils/dbtools'
import auth from '../../middlewares/auth.middleware' import auth from '../../middlewares/auth.middleware'
@ -58,7 +59,6 @@ import {
// files // files
const msgFile = 'stats/msgs' const msgFile = 'stats/msgs'
const passwordFile = 'data/dataEditorPasswords.json'
const dataEditsLog = 'stats/dataEdits' const dataEditsLog = 'stats/dataEdits'
const dailyDataCountFile = 'stats/dailyDataCount' const dailyDataCountFile = 'stats/dailyDataCount'
const usersDbBackupPath = 'data/dbs/backup' const usersDbBackupPath = 'data/dbs/backup'
@ -262,8 +262,7 @@ function GetApp(): ModuleType {
// ------------------------------------------------------------- // -------------------------------------------------------------
app.get('/getDbs', (req: Request, res: any) => { app.get('/getDbs', (req: Request, res: any) => {
logger.LogReq(req) // logger.LogReq(req)
res.json( res.json(
questionDbs.map((qdb) => { questionDbs.map((qdb) => {
return { return {
@ -824,114 +823,6 @@ function GetApp(): ModuleType {
}) })
// ------------------------------------------------------------------------------------------- // -------------------------------------------------------------------------------------------
// API
app.post('/uploaddata', (req: Request, res: any) => {
// body: JSON.stringify({
// newData: data,
// count: getCount(data),
// initialCount: initialCount,
// password: password,
// editedQuestions: editedQuestions
// })
const {
count,
initialCount,
editedQuestions,
password /*, newData*/,
} = req.body
const respStatuses = {
invalidPass: 'invalidPass',
ok: 'ok',
error: 'error',
}
logger.LogReq(req)
try {
// finding user
const pwds = JSON.parse(utils.ReadFile(passwordFile))
const userKey = Object.keys(pwds).find((key) => {
const userKey = pwds[key]
return userKey.password === password
})
// FIXME: check user type in dataeditorPW-s json
const user: any = pwds[userKey]
// logging and stuff
logger.Log(`Data upload`, logger.GetColor('bluebg'))
logger.Log(`PWD: ${password}`, logger.GetColor('bluebg'))
// returning if user password is not ok
if (!user) {
logger.Log(
`Data upload: invalid password ${password}`,
logger.GetColor('red')
)
utils.AppendToFile(
utils.GetDateString() +
'\n' +
password +
'(FAILED PASSWORD)\n' +
JSON.stringify(editedQuestions) +
'\n\n',
dataEditsLog
)
res.json({ status: respStatuses.invalidPass })
return
}
logger.Log(
`Password accepted for ${user.name}`,
logger.GetColor('bluebg')
)
logger.Log(
`Old Subjects/Questions: ${initialCount.subjectCount} / ${
initialCount.questionCount
} | New: ${count.subjectCount} / ${
count.questionCount
} | Edited question count: ${Object.keys(editedQuestions).length}`,
logger.GetColor('bluebg')
)
// saving detailed editedCount
utils.AppendToFile(
utils.GetDateString() +
'\n' +
JSON.stringify(user) +
'\n' +
JSON.stringify(editedQuestions) +
'\n\n',
dataEditsLog
)
// making backup
// TODO
// utils.CopyFile(
// './' + dataFile,
// `./publicDirs/qminingPublic/backs/data_before_${
// user.name
// }_${utils.GetDateString().replace(/ /g, '_')}`
// ) // TODO: rewrite to dinamyc public!!!
// logger.Log('Backup made')
// // writing data
// utils.WriteFile(JSON.stringify(newData), dataFile)
// logger.Log('New data file written')
// // reloading data file
// data = [...newData]
// data = newData
logger.Log('Data set to newData')
res.json({
status: respStatuses.ok,
user: user.name,
})
logger.Log('Data updating done!', logger.GetColor('bluebg'))
} catch (error) {
logger.Log(`Data upload error! `, logger.GetColor('redbg'))
console.error(error)
res.json({ status: respStatuses.error, msg: error.message })
}
})
function getNewQdb(location, maxIndex) { function getNewQdb(location, maxIndex) {
logger.Log( logger.Log(
@ -1069,6 +960,10 @@ function GetApp(): ModuleType {
function saveQuestion(questions, subj, testUrl, userid) { function saveQuestion(questions, subj, testUrl, userid) {
// TODO: clear folder every now and then, check if saved questions exist // TODO: clear folder every now and then, check if saved questions exist
if (!subj) {
logger.Log('No subj name to save test question')
return
}
const questionsToSave = { const questionsToSave = {
questions: questions, questions: questions,
subj: subj, subj: subj,
@ -1077,7 +972,7 @@ function GetApp(): ModuleType {
date: new Date(), date: new Date(),
} }
const fname = `${utils.GetDateString()}_${userid}_${testUrl}.json` const fname = `${utils.GetDateString()}_${userid}_${testUrl}.json`
const subject = getSubjNameWithoutYear(subj) const subject = getSubjNameWithoutYear(subj).replace(/\//g, '-')
const subjPath = `${savedQuestionsDir}/${subject}` const subjPath = `${savedQuestionsDir}/${subject}`
const savedSubjQuestionsFilePath = `${subjPath}/${savedQuestionsFileName}` const savedSubjQuestionsFilePath = `${subjPath}/${savedQuestionsFileName}`
@ -1451,6 +1346,354 @@ function GetApp(): ModuleType {
res.json({ msg: 'done' }) res.json({ msg: 'done' })
}) })
app.get('/possibleAnswers', (req: Request, res: any) => {
logger.LogReq(req)
const files = utils.ReadDir(savedQuestionsDir)
files.sort(function(a, b) {
return (
fs.statSync(savedQuestionsDir + '/' + b).mtime.getTime() -
fs.statSync(savedQuestionsDir + '/' + a).mtime.getTime()
)
})
res.json({
savedQuestionsFileName: savedQuestionsFileName,
subjects: files.map((subj) => {
return {
name: subj,
path: `savedQuestions/${subj}/`,
}
}),
})
})
app.post('/rmPossibleAnswer', (req: Request, res: any) => {
logger.LogReq(req)
const user: User = req.session.user
const subj = req.body.subj
const file = req.body.file
const savedQuestionsPath = `${savedQuestionsDir}/${subj}/${savedQuestionsFileName}`
const savedQuestions = utils.ReadJSON(savedQuestionsPath)
let path = `${savedQuestionsDir}/${subj}/${file}`
// to prevent deleting ../../../ ... /etc/shadow
while (path.includes('..')) {
path = path.replace(/\.\./g, '.')
}
if (utils.FileExists(path)) {
utils.deleteFile(path)
utils.WriteFile(
JSON.stringify(
savedQuestions.filter((sq) => {
return sq.fname !== file
})
),
savedQuestionsPath
)
logger.Log(
`User #${user.id} deleted '${file}' from subject '${subj}'`,
logger.GetColor('cyanbg')
)
res.json({
res: 'ok',
})
} else {
logger.Log(
`User #${user.id} tried to delete '${file}' from subject '${subj}', but failed`,
logger.GetColor('red')
)
res.json({
res: 'fail',
})
}
})
app.post('/updateQuestion', (req: Request, res) => {
logger.LogReq(req)
const user: User = req.session.user
const date = utils.GetDateString()
let saveDb = false
const editType = req.body.type
const selectedDb = req.body.selectedDb
if (!editType || !selectedDb) {
res.json({
status: 'fail',
msg: 'No .editType or .selectedDb !',
})
return
}
const dbIndex = questionDbs.findIndex((qdb) => {
return qdb.name === selectedDb.name
})
const currDb = questionDbs[dbIndex]
if (dbIndex === -1) {
res.json({
status: 'fail',
msg: `No question db named like ${selectedDb.name}!`,
})
return
}
// {
// "index": 0,
// "subjName": "VHDL programozás",
// "type": "delete",
// "selectedDb": {
// "path": "questionDbs/elearning.uni-obuda.hu.json",
// "name": "elearning.uni-obuda.hu"
// }
// }
if (editType === 'delete') {
const { index, subjName } = req.body
let deletedQuestion = {}
if (isNaN(index) || !subjName) {
res.json({
status: 'fail',
msg: 'No .index or .subjName !',
})
return
}
questionDbs[dbIndex].data = currDb.data.map((subj) => {
if (subj.Name !== subjName) {
return subj
} else {
return {
...subj,
Questions: subj.Questions.filter((question, i) => {
if (index === i) {
deletedQuestion = question
return false
} else {
return true
}
}),
}
}
})
logger.Log(
`User #${user.id} deleted a question from '${subjName}'`,
logger.GetColor('cyanbg')
)
utils.AppendToFile(
`${date}: User ${user.id} deleted a question from '${subjName}' (index: ${index})`,
dataEditsLog
)
utils.AppendToFile(JSON.stringify(deletedQuestion, null, 2), dataEditsLog)
saveDb = true
}
// {
// "index": 0,
// "subjName": "Elektronika",
// "type": "edit",
// "newVal": {
// "Q": "Analóg műszer esetén az érzékenység az a legkisebb mennyiség, amely a műszer kijelzőjén meghatározott mértékű változást okoz.",
// "A": "Igaz",
// "data": {
// "type": "simple",
// "possibleAnswers": [
// "Igaz"
// ]
// },
// "possibleAnswers": [
// "Igaz"
// ]
// },
// "selectedDb": {
// "path": "questionDbs/elearning.uni-obuda.hu.json",
// "name": "elearning.uni-obuda.hu"
// }
// }
if (editType === 'edit') {
const { index, subjName, newVal } = req.body
let oldVal = {}
if (isNaN(index) || !subjName) {
res.json({
status: 'fail',
msg: 'No .index or .subjName !',
})
return
}
if (!isQuestionValid(newVal)) {
res.json({
status: 'fail',
msg: 'edited question is not valid',
question: newVal,
})
return
}
questionDbs[dbIndex].data = currDb.data.map((subj) => {
if (subj.Name !== subjName) {
return subj
} else {
return {
...subj,
Questions: subj.Questions.map((question, i) => {
if (index === i) {
oldVal = question
return newVal
} else {
return question
}
}),
}
}
})
logger.Log(
`User #${user.id} edited a question in '${subjName}'`,
logger.GetColor('cyanbg')
)
utils.AppendToFile(
`${date}: User ${user.id} edited a question in '${subjName}' (index: ${index})`,
dataEditsLog
)
utils.AppendToFile(
JSON.stringify(
{
newVal: newVal,
oldVal: oldVal,
},
null,
2
),
dataEditsLog
)
saveDb = true
}
// {
// "subjName": "Elektronika",
// "changedQuestions": [
// {
// "index": 1,
// "value": {
// "Q": "A műszer pontosságát a hibájával fejezzük ki, melyet az osztályjel (osztálypontosság ) mutat meg.",
// "A": "Hamis",
// "data": {
// "type": "simple",
// "possibleAnswers": [
// "Igaz",
// "Hamis"
// ]
// }
// }
// }
// ],
// "deletedQuestions": [
// 0
// ],
// "type": "subjEdit",
// "selectedDb": {
// "path": "questionDbs/elearning.uni-obuda.hu.json",
// "name": "elearning.uni-obuda.hu"
// }
// }
if (editType === 'subjEdit') {
const { subjName, changedQuestions, deletedQuestions } = req.body
const deletedQuestionsToWrite = []
const changedQuestionsToWrite = []
if (
!Array.isArray(changedQuestions) ||
!Array.isArray(deletedQuestions)
) {
res.json({
status: 'fail',
msg: 'no changedQuestions or deletedQuestions!',
})
return
}
// processing changed questions
questionDbs[dbIndex].data = currDb.data.map((subj) => {
if (subj.Name !== subjName) {
return subj
} else {
return {
...subj,
Questions: subj.Questions.map((question, i) => {
const changedTo = changedQuestions.find((cq) => {
return cq.index === i
})
if (changedTo) {
changedQuestionsToWrite.push({
oldVal: question,
newVal: changedTo.value,
})
return changedTo.value
} else {
return question
}
}),
}
}
})
// processing deletedQuestions
questionDbs[dbIndex].data = currDb.data.map((subj) => {
if (subj.Name !== subjName) {
return subj
} else {
return {
...subj,
Questions: subj.Questions.filter((question, i) => {
const isDeleted = deletedQuestions.includes(i)
if (isDeleted) {
deletedQuestionsToWrite.push(question)
return false
} else {
return true
}
}),
}
}
})
logger.Log(
`User #${user.id} modified '${subjName}'. Edited: ${deletedQuestionsToWrite.length}, deleted: ${deletedQuestionsToWrite.length}`,
logger.GetColor('cyanbg')
)
utils.AppendToFile(
`${date} User #${user.id} modified '${subjName}'. Edited: ${deletedQuestionsToWrite.length}, deleted: ${deletedQuestionsToWrite.length}`,
dataEditsLog
)
utils.AppendToFile(
JSON.stringify(
{
deletedQuestions: deletedQuestionsToWrite,
changedQuestions: changedQuestionsToWrite,
},
null,
2
),
dataEditsLog
)
saveDb = true
}
if (saveDb) {
utils.WriteFile(JSON.stringify(currDb.data), currDb.path)
msgAllWorker({
qdbs: questionDbs,
type: 'update',
})
}
res.json({
status: 'OK',
})
})
// ------------------------------------------------------------------------------------------- // -------------------------------------------------------------------------------------------
app.get('*', function(req: Request, res: any) { app.get('*', function(req: Request, res: any) {

View file

@ -161,7 +161,7 @@ const excludeFromStats = utils.ReadJSON(statExcludeFile)
app.use( app.use(
reqlogger({ reqlogger({
loggableKeywords: ['news.json'], loggableKeywords: ['news.json'],
loggableModules: ['dataeditor'], loggableModules: [],
exceptions: ['_next/static'], exceptions: ['_next/static'],
excludeFromStats: excludeFromStats, excludeFromStats: excludeFromStats,
}) })

View file

@ -268,7 +268,7 @@ function processIncomingRequestUsingDb(
}) })
} }
function isQuestionValid(question: Question) { export function isQuestionValid(question: Question): Boolean {
if (!question.Q) { if (!question.Q) {
return false return false
} }

View file

@ -619,6 +619,8 @@ if (!isMainThread) {
// ) // )
} else if (msg.type === 'update') { } else if (msg.type === 'update') {
qdbs = msg.qdbs qdbs = msg.qdbs
logger.DebugLog(`Worker update ${workerIndex}`, 'worker update', 1)
// console.log(`[THREAD #${workerIndex}]: update`) // console.log(`[THREAD #${workerIndex}]: update`)
} else if (msg.type === 'newdb') { } else if (msg.type === 'newdb') {
qdbs.push(msg.newdb) qdbs.push(msg.newdb)

View file

@ -375,6 +375,9 @@ function C(color?: string): string {
if (color === 'bluebg') { if (color === 'bluebg') {
return '\x1b[44m' return '\x1b[44m'
} }
if (color === 'cyanbg') {
return '\x1b[46m'
}
if (color === 'green') { if (color === 'green') {
return '\x1b[32m' return '\x1b[32m'
} }

View file

@ -11,6 +11,7 @@ export default {
CopyFile, CopyFile,
GetDateString, GetDateString,
formatUrl, formatUrl,
deleteFile: deleteFile,
} }
import fs from 'fs' import fs from 'fs'
@ -162,3 +163,11 @@ function AppendToFile(data: string, file: string): void {
console.error(err) console.error(err)
} }
} }
function deleteFile(fname: string): Boolean {
if (FileExists(fname)) {
fs.unlinkSync(fname)
return true
}
return false
}

@ -1 +1 @@
Subproject commit 2d6211a80af3ccd6fe78d6e30c98f340f0579ac8 Subproject commit 2ae8d7ffb21e8bcc42731e8a4a61fe819f850478

@ -1 +1 @@
Subproject commit e9e9f94130e452e9d88eb0c1949b8f80e98c2f13 Subproject commit aae943336ab052e61f61e20d2c3dd0b45a263183