2 files added
5 files modified
| | |
| | | } |
| | | }) |
| | | |
| | | // Kurs Admins - über Rolle |
| | | .get("/api/kurs/rolle/admin", async function (req, res) { |
| | | try { |
| | | const data = await db.getCourseAdminRoles() |
| | | return res.send(data) |
| | | } catch (err) { |
| | | console.error(err) |
| | | return res.code(500).send({status: "error", error: err.toString()}) |
| | | } |
| | | }) |
| | | |
| | | // Kurs Admins - über Rolle - FEHLENDE Zuweisung eines tatsächlichen Users |
| | | .get("/api/kurs/rolle/noadmin", async function (req, res) { |
| | | try { |
| | | const data = await db.getCourseWithoutAdminRoles() |
| | | return res.send(data) |
| | | } catch (err) { |
| | | console.error(err) |
| | | return res.code(500).send({status: "error", error: err.toString()}) |
| | | } |
| | | }) |
| | | |
| | | |
| | | |
| | | /////// STATIC / SPA //////////////////////////////////////////////////////////////// |
| | | |
| New file |
| | |
| | | const yargs = require("yargs") |
| | | const csv = require("json-2-csv") |
| | | |
| | | const db = require("../lib/db") |
| | | const {exitDelayed} = require("../lib/tools"); |
| | | |
| | | ///////////////////////////////////////////////////////////////////////// |
| | | |
| | | const argv = yargs |
| | | .command({ |
| | | command: "list", |
| | | describe: "list course admin roles", |
| | | builder: { |
| | | format: { |
| | | alias: "f", |
| | | demandOption: false, |
| | | describe: "output format: table(default) | json | csv", |
| | | type: "string", |
| | | default: "table" |
| | | } |
| | | }, |
| | | handler: async function ({format}) { |
| | | const data = await db.getCourseAdminRoles() |
| | | await print(data, format) |
| | | exitDelayed() |
| | | } |
| | | }) |
| | | .command({ |
| | | command: "listn", |
| | | describe: "list courses without user assigned admin role", |
| | | builder: { |
| | | format: { |
| | | alias: "f", |
| | | demandOption: false, |
| | | describe: "output format: table(default) | json | csv", |
| | | type: "string", |
| | | default: "table" |
| | | } |
| | | }, |
| | | handler: async function ({format}) { |
| | | const data = await db.getCourseWithoutAdminRoles() |
| | | await print(data, format) |
| | | exitDelayed() |
| | | } |
| | | }) |
| | | // .command({ |
| | | // command: "get <item> [_id]", |
| | | // describe: "get db items - if _id is given, get only one document identified by _id", |
| | | // builder: { |
| | | // skip: { |
| | | // demandOption: false, |
| | | // describe: "skip this number of items", |
| | | // type: "number", |
| | | // default: 0, |
| | | // }, |
| | | // limit: { |
| | | // demandOption: false, |
| | | // describe: "limit to this number of items", |
| | | // type: "number", |
| | | // default: 5, |
| | | // }, |
| | | // }, |
| | | // handler: async function ({item, _id, skip, limit}) { |
| | | // const db = require("../data/db/db") |
| | | // if (!_id) { |
| | | // const res = await db[item].find().skip(skip).limit(limit).lean() |
| | | // console.log(JSON.stringify(res, null, " ")) |
| | | // } else { |
| | | // const res = await db[item].findOne({_id}).lean() |
| | | // console.log(JSON.stringify(res, null, " ")) |
| | | // } |
| | | // exitDelayed() |
| | | // } |
| | | // }) |
| | | .alias("h", "help") |
| | | .demandCommand() |
| | | .version(false) |
| | | // .wrap(yargs.terminalWidth()) |
| | | // .wrap(100) |
| | | .strict() |
| | | .argv |
| | | |
| | | |
| | | ///////////////////////////////////////////////////////////////////////// |
| | | |
| | | async function print(data, format) { |
| | | switch (format) { |
| | | case "table": |
| | | console.table(data) |
| | | break; |
| | | case "json": |
| | | console.log(JSON.stringify(data, null, " ")) |
| | | break; |
| | | case "csv": |
| | | const res = await csv.json2csv(data) |
| | | console.log(res) |
| | | break; |
| | | default: |
| | | throw new Error(`unknown format "${format}"`) |
| | | } |
| | | } |
| | |
| | | |
| | | setStatus, |
| | | |
| | | getCourseAdmins, |
| | | getCoursesWithNoAdmins, |
| | | getCourseAdminRoles, |
| | | getCourseWithoutAdminRoles, |
| | | } |
| | | |
| | | ///////////////////////////////////////////////////////////////////////// |
| | |
| | | |
| | | /////// ADMINS //////////////////////////////////////////////////////////////// |
| | | |
| | | async function getCourseAdmins() { |
| | | |
| | | async function getCourseAdminRoles() { |
| | | const pool = await poolP |
| | | const q = `SELECT om.obj_id as kurs_obj_id, |
| | | t.ref_id as kurs_ref_id, |
| | | om.usr_id, |
| | | ud.login, |
| | | om.admin, |
| | | ud.firstname, |
| | | ud.lastname, |
| | | od2.title |
| | | FROM ${database}.obj_members om |
| | | INNER JOIN ${database}.usr_data ud ON ud.usr_id = om.usr_id |
| | | INNER JOIN ${database}.object_data od2 ON od2.obj_id = om.obj_id |
| | | INNER JOIN ${database}.object_reference t ON t.obj_id = om.obj_id |
| | | WHERE om.admin = 1 |
| | | const q = ` |
| | | SELECT od.obj_id as crs_obj_id, t.ref_id as crs_ref_id, od.title as crs_title, rf.rol_id, od2.title as role |
| | | FROM ${database}.object_data od |
| | | INNER JOIN ${database}.object_reference t ON t.obj_id = od.obj_id |
| | | INNER JOIN ${database}.rbac_fa rf ON rf.parent = t.ref_id |
| | | INNER JOIN ${database}.object_data od2 ON od2.obj_id = rf.rol_id |
| | | WHERE od.\`type\` = "crs" |
| | | AND od2.title LIKE "%admin%" |
| | | ` |
| | | const [results] = await pool.query(q) |
| | | return results |
| | | } |
| | | |
| | | /** |
| | | * Liefert die Kurse ohne Admins |
| | | * Admins hier definiert als Einträge in obj_members wo admin==1 |
| | | * Darüber hinaus gibt es offenbar noch einen anderen Mechanismus über die Rolle. |
| | | * Denn ein Kurs ohne Admin (z.B. lokal Fliesenratgeber ref_id=88) hat in ILIAS |
| | | * trotzdem einen Admin im Screen "Members". |
| | | * Dort wird wohl über die Rolle zugeordnet. |
| | | * |
| | | * Die Frage ist wo der Fehler GS-2333 auftritt. |
| | | * Bei obj_members oder bei fehlender Rolle. |
| | | * @return {Promise<*>} |
| | | */ |
| | | async function getCoursesWithNoAdmins() { |
| | | async function getCourseWithoutAdminRoles() { |
| | | const pool = await poolP |
| | | const q = ` |
| | | SELECT asdf.obj_id, t.ref_id, asdf.numTn, asdf.title |
| | | FROM (SELECT om.obj_id, COUNT(*) as numTn, od.title, MAX(om.admin) as maxAdmin |
| | | FROM ${database}.obj_members om |
| | | INNER JOIN ${database}.object_data od ON od.obj_id = om.obj_id |
| | | GROUP by om.obj_id |
| | | ORDER BY numTn DESC) asdf |
| | | INNER JOIN ${database}.object_reference t ON t.obj_id = asdf.obj_id |
| | | WHERE asdf.maxAdmin = 0 |
| | | SELECT adminRoles.crs_obj_id, adminRoles.crs_ref_id, adminRoles.crs_title, adminRoles.rol_id, adminRoles.role |
| | | FROM (SELECT od.obj_id as crs_obj_id, t.ref_id as crs_ref_id, od.title as crs_title, rf.rol_id, od2.title as role |
| | | FROM ${database}.object_data od |
| | | INNER JOIN ${database}.object_reference t ON t.obj_id = od.obj_id |
| | | INNER JOIN ${database}.rbac_fa rf ON rf.parent = t.ref_id |
| | | INNER JOIN ${database}.object_data od2 ON od2.obj_id = rf.rol_id |
| | | WHERE od.\`type\` = "crs" |
| | | AND od2.title LIKE "%admin%") adminRoles |
| | | WHERE adminRoles.rol_id NOT IN (SELECT ru2.rol_id |
| | | FROM ${database}.rbac_ua ru2) |
| | | ` |
| | | const [results] = await pool.query(q) |
| | | return results |
| | | } |
| | | |
| | | // !! wird nicht gebraucht - Admin Erkennung in ILIAS läuft anders ab - über die Rolle |
| | | // async function getCourseAdmins() { |
| | | // const pool = await poolP |
| | | // const q = `SELECT om.obj_id as kurs_obj_id, |
| | | // t.ref_id as kurs_ref_id, |
| | | // om.usr_id, |
| | | // ud.login, |
| | | // om.admin, |
| | | // ud.firstname, |
| | | // ud.lastname, |
| | | // od2.title |
| | | // FROM ${database}.obj_members om |
| | | // INNER JOIN ${database}.usr_data ud ON ud.usr_id = om.usr_id |
| | | // INNER JOIN ${database}.object_data od2 ON od2.obj_id = om.obj_id |
| | | // INNER JOIN ${database}.object_reference t ON t.obj_id = om.obj_id |
| | | // WHERE om.admin = 1 |
| | | // ` |
| | | // const [results] = await pool.query(q) |
| | | // return results |
| | | // } |
| | | // |
| | | // /** |
| | | // * Liefert die Kurse ohne Admins |
| | | // * Admins hier definiert als Einträge in obj_members wo admin==1 |
| | | // * Darüber hinaus gibt es offenbar noch einen anderen Mechanismus über die Rolle. |
| | | // * Denn ein Kurs ohne Admin (z.B. lokal Fliesenratgeber ref_id=88) hat in ILIAS |
| | | // * trotzdem einen Admin im Screen "Members". |
| | | // * Dort wird wohl über die Rolle zugeordnet. |
| | | // * |
| | | // * Die Frage ist wo der Fehler GS-2333 auftritt. |
| | | // * Bei obj_members oder bei fehlender Rolle. |
| | | // * @return {Promise<*>} |
| | | // */ |
| | | // async function getCoursesWithNoAdmins() { |
| | | // const pool = await poolP |
| | | // const q = ` |
| | | // SELECT asdf.obj_id, t.ref_id, asdf.numTn, asdf.title |
| | | // FROM (SELECT om.obj_id, COUNT(*) as numTn, od.title, MAX(om.admin) as maxAdmin |
| | | // FROM ${database}.obj_members om |
| | | // INNER JOIN ${database}.object_data od ON od.obj_id = om.obj_id |
| | | // GROUP by om.obj_id |
| | | // ORDER BY numTn DESC) asdf |
| | | // INNER JOIN ${database}.object_reference t ON t.obj_id = asdf.obj_id |
| | | // WHERE asdf.maxAdmin = 0 |
| | | // ` |
| | | // const [results] = await pool.query(q) |
| | | // return results |
| | | // } |
| New file |
| | |
| | | module.exports = { |
| | | exitDelayed, |
| | | } |
| | | |
| | | ///////////////////////////////////////////////////////////////////////// |
| | | |
| | | function exitDelayed (delay = 750) { |
| | | setTimeout(function () { |
| | | process.exit() |
| | | }, delay) |
| | | } |
| | |
| | | "enquirer": "^2.4.1", |
| | | "fastify": "^5.3.3", |
| | | "flexsearch": "^0.8.205", |
| | | "json-2-csv": "^5.5.10", |
| | | "lodash": "^4.17.21", |
| | | "mocha": "^11.6.0", |
| | | "mysql2": "^3.14.1", |
| | |
| | | "node": ">=0.10.0" |
| | | } |
| | | }, |
| | | "node_modules/deeks": { |
| | | "version": "3.1.0", |
| | | "resolved": "https://registry.npmjs.org/deeks/-/deeks-3.1.0.tgz", |
| | | "integrity": "sha512-e7oWH1LzIdv/prMQ7pmlDlaVoL64glqzvNgkgQNgyec9ORPHrT2jaOqMtRyqJuwWjtfb6v+2rk9pmaHj+F137A==", |
| | | "license": "MIT", |
| | | "engines": { |
| | | "node": ">= 16" |
| | | } |
| | | }, |
| | | "node_modules/deep-eql": { |
| | | "version": "5.0.2", |
| | | "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-5.0.2.tgz", |
| | |
| | | "license": "BSD-3-Clause", |
| | | "engines": { |
| | | "node": ">=0.3.1" |
| | | } |
| | | }, |
| | | "node_modules/doc-path": { |
| | | "version": "4.1.1", |
| | | "resolved": "https://registry.npmjs.org/doc-path/-/doc-path-4.1.1.tgz", |
| | | "integrity": "sha512-h1ErTglQAVv2gCnOpD3sFS6uolDbOKHDU1BZq+Kl3npPqroU3dYL42lUgMfd5UimlwtRgp7C9dLGwqQ5D2HYgQ==", |
| | | "license": "MIT", |
| | | "engines": { |
| | | "node": ">=16" |
| | | } |
| | | }, |
| | | "node_modules/dotenv": { |
| | |
| | | "node": ">=6" |
| | | } |
| | | }, |
| | | "node_modules/json-2-csv": { |
| | | "version": "5.5.10", |
| | | "resolved": "https://registry.npmjs.org/json-2-csv/-/json-2-csv-5.5.10.tgz", |
| | | "integrity": "sha512-Dep8wO3Fr5wNjQevO2Z8Y7yeee/nYSGRsi7q6zJDKEVHxXkXT+v21vxHmDX923UzmCXXkSo62HaTz6eTWzFLaw==", |
| | | "license": "MIT", |
| | | "dependencies": { |
| | | "deeks": "3.1.0", |
| | | "doc-path": "4.1.1" |
| | | }, |
| | | "engines": { |
| | | "node": ">= 16" |
| | | } |
| | | }, |
| | | "node_modules/json-schema-ref-resolver": { |
| | | "version": "2.0.1", |
| | | "resolved": "https://registry.npmjs.org/json-schema-ref-resolver/-/json-schema-ref-resolver-2.0.1.tgz", |
| | |
| | | "enquirer": "^2.4.1", |
| | | "fastify": "^5.3.3", |
| | | "flexsearch": "^0.8.205", |
| | | "json-2-csv": "^5.5.10", |
| | | "lodash": "^4.17.21", |
| | | "mocha": "^11.6.0", |
| | | "mysql2": "^3.14.1", |
| | |
| | | afterEach(async function () { |
| | | }) |
| | | |
| | | describe("the function getCourseAdmins()", function () { |
| | | it("should get the Admins of all courses", async function () { |
| | | const res = await db.getCourseAdmins() |
| | | console.table(res) |
| | | |
| | | describe("the function getCourseAdminRoles()", function () { |
| | | it("should return all admin roles for all courses", async function () { |
| | | const res = await db.getCourseAdminRoles() |
| | | // console.table(res) |
| | | // console.log(res) |
| | | expect(res).to.be.a("array") |
| | | for (const item of res) { |
| | | expect(item).to.have.property("kurs_obj_id").and.to.be.a("number") |
| | | expect(item).to.have.property("kurs_ref_id").and.to.be.a("number") |
| | | expect(item).to.have.property("usr_id").and.to.be.a("number") |
| | | expect(item).to.have.property("login").and.to.be.a("string") |
| | | expect(item).to.have.property("firstname").and.to.be.a("string") |
| | | expect(item).to.have.property("lastname").and.to.be.a("string") |
| | | expect(item).to.have.property("title").and.to.be.a("string") |
| | | expect(item).to.have.property("crs_obj_id").and.to.be.a("number") |
| | | expect(item).to.have.property("crs_ref_id").and.to.be.a("number") |
| | | expect(item).to.have.property("crs_title").and.to.be.a("string") |
| | | expect(item).to.have.property("rol_id").and.to.be.a("number") |
| | | expect(item).to.have.property("role").and.to.be.a("string") |
| | | } |
| | | }) |
| | | }) |
| | | |
| | | describe("the function getCoursesWithNoAdmins()", function () { |
| | | it("should return all courses without a single admin member", async function () { |
| | | const res = await db.getCoursesWithNoAdmins() |
| | | console.table(res) |
| | | describe("the function getCoursesWithoutAdminRoles()", function () { |
| | | it("should return all admin roles for all courses", async function () { |
| | | const res = await db.getCourseWithoutAdminRoles() |
| | | // console.table(res) |
| | | console.log(res) |
| | | expect(res).to.be.a("array") |
| | | for (const item of res) { |
| | | expect(item).to.have.property("obj_id").and.to.be.a("number") |
| | | expect(item).to.have.property("ref_id").and.to.be.a("number") |
| | | expect(item).to.have.property("numTn").and.to.be.a("number") |
| | | expect(item).to.have.property("title").and.to.be.a("string") |
| | | expect(item).to.have.property("crs_obj_id").and.to.be.a("number") |
| | | expect(item).to.have.property("crs_ref_id").and.to.be.a("number") |
| | | expect(item).to.have.property("crs_title").and.to.be.a("string") |
| | | expect(item).to.have.property("rol_id").and.to.be.a("number") |
| | | expect(item).to.have.property("role").and.to.be.a("string") |
| | | } |
| | | }) |
| | | }) |
| | | |
| | | // wird nicht gebraucht - Admin Erkennung in ILIAS läuft anders ab |
| | | // describe("the function getCourseAdmins()", function () { |
| | | // it("should get the Admins of all courses", async function () { |
| | | // const res = await db.getCourseAdmins() |
| | | // console.table(res) |
| | | // expect(res).to.be.a("array") |
| | | // for (const item of res) { |
| | | // expect(item).to.have.property("kurs_obj_id").and.to.be.a("number") |
| | | // expect(item).to.have.property("kurs_ref_id").and.to.be.a("number") |
| | | // expect(item).to.have.property("usr_id").and.to.be.a("number") |
| | | // expect(item).to.have.property("login").and.to.be.a("string") |
| | | // expect(item).to.have.property("firstname").and.to.be.a("string") |
| | | // expect(item).to.have.property("lastname").and.to.be.a("string") |
| | | // expect(item).to.have.property("title").and.to.be.a("string") |
| | | // } |
| | | // }) |
| | | // }) |
| | | // |
| | | // describe("the function getCoursesWithNoAdmins()", function () { |
| | | // it("should return all courses without a single admin member", async function () { |
| | | // const res = await db.getCoursesWithNoAdmins() |
| | | // console.table(res) |
| | | // expect(res).to.be.a("array") |
| | | // for (const item of res) { |
| | | // expect(item).to.have.property("obj_id").and.to.be.a("number") |
| | | // expect(item).to.have.property("ref_id").and.to.be.a("number") |
| | | // expect(item).to.have.property("numTn").and.to.be.a("number") |
| | | // expect(item).to.have.property("title").and.to.be.a("string") |
| | | // } |
| | | // }) |
| | | // }) |
| | | |
| | | }) |
| | | |
| | | ///////////////////////////////////////////////////////////////////////// |