From a380a465ce02059a630ef678fecd6666cbdf8f30 Mon Sep 17 00:00:00 2001
From: alex <alex@alexloehr.net>
Date: Fri, 28 Nov 2025 10:21:00 +0000
Subject: [PATCH] GS-2333

---
 lib/db.js |  685 +++++++++++++++++++++++++++++++++++++++++++++++++++-----
 1 files changed, 622 insertions(+), 63 deletions(-)

diff --git a/lib/db.js b/lib/db.js
index d492507..dd4dc76 100644
--- a/lib/db.js
+++ b/lib/db.js
@@ -1,12 +1,16 @@
 const mysql = require("mysql2/promise")
+const dayjs = require("dayjs")
+const _ = require("lodash")
 
+const log = require("../logger")
+const searchLib = require("./search")
 const {host, port, user, database, password} = require("./../settings").db
 
 /////////////////////////////////////////////////////////////////////////
 
 let poolP = initPool()
 
-async function initPool () {
+async function initPool() {
    return mysql.createPool({
       host,
       port,
@@ -25,81 +29,139 @@
    getUserByUserId,
    getUserDefinedFields,
    getUserDefinedField,
+   getUserTeilnahmen,
 
    getObjIdFromRefId,
    getRefIdFromObjId,
 
    getKurse,
-   getKursByObjId,
-   getKursByRefId,
-   getTeilnehmer,
+   getKurs,
+   getKursItems,
+   getKursItems2,
+   // getKursByObjId,
+   // getKursByRefId,
+   getKursTeilnehmer,
+   getSingleKursTeilnehmer,
+   getKursTeilnehmerCount,
+
+   getKursLp,
+   getKursUnterobjektLp,
+
+   getKursOffline,
+   setKursOffline,
+
+   getUdf,
+
+   getKursTeilnehmerRolle,
+   getKursTeilnehmerByRole,
+   getKursRoles,
+
+   setStatus,
+
+   getCourseAdminRoles,
+   getCourseWithoutAdminRoles,
 }
 
 /////////////////////////////////////////////////////////////////////////
 
-async function getUsers (offset = 0, limit = 10) {
+async function getUserSearchQuery(offset, limit, search) {
    const sel = `usr_id, login, firstname, lastname, gender, email, institution, street, city, zipcode, country, department, active`
-   // TODO user defined fields
-   // TODO check args for SQL Injection
-
-   const pool = await poolP
-   const [results, fields] = await pool.query(
-      `SELECT ${sel}
-       FROM ${database}.usr_data AS ud
-       WHERE login REGEXP '^[0-9]+$'
-       LIMIT ${limit}
-       OFFSET ${offset}
+   if (!search || search === '') {
+      return `SELECT ${sel}
+              FROM ${database}.usr_data AS ud
+              WHERE login REGEXP '^[0-9]+$'
+              LIMIT ${limit}
+              OFFSET ${offset}
       `
-   )
-   const count = await getUserCount()
-   return {
-      total: count,
-      offset, limit,
-      data: results,
+   } else {
+      const ids = await searchLib.search(search)
+      if (!ids.length) {
+         throw "nothing found"
+         return await getUserSearchQuery(offset, limit)
+      } // nothing found
+      return `SELECT ${sel}
+              FROM ${database}.usr_data AS ud
+              WHERE login REGEXP '^[0-9]+$'
+              AND usr_id IN (${ids.join(",")})
+                  LIMIT ${limit}
+              OFFSET ${offset}
+      `
    }
 }
 
-async function getUserCount () {
+async function getUsers(offset = 0, limit = 10, search = null) {
+   // log.info("++++++++++ get users", offset, limit, search)
+   limit = Number(limit) || 10
+   offset = Number(offset) || 0
+   // TODO check args for SQL Injection
+
    const pool = await poolP
-   const [results, fields] = await pool.query(
-      `SELECT COUNT(*)
-       FROM ${database}.usr_data AS ud
-       WHERE login REGEXP '^[0-9]+$'`
-   )
-   return results[0]["COUNT(*)"]
+
+   try {
+      let userSearchQuery = await getUserSearchQuery(offset, limit, search)
+      // log.info(userSearchQuery)
+      const [results, fields] = await pool.query(userSearchQuery)
+      // console.log(results, fields)
+      const count = await getUserCount(offset, limit, search)
+      return {
+         total: count,
+         offset, limit,
+         data: results,
+      }
+   } catch (ex) {
+      return {
+         total: 0,
+         offset: 0,
+         limit: 0,
+         data: [],
+      }
+   }
 }
 
-async function getUserByLogin (login) {
+async function getUserCount(offset, limit, search) {
+   const pool = await poolP
+   // const q = getUserSearchQuery(offset, limit, search)
+   const q = await getUserSearchQuery(0, 1000000, search) // hier darf kein Limit sein, offset=0
+   const q2 = `SELECT COUNT(*) AS count
+               FROM (${q}) AS X`
+   const [results, fields] = await pool.query(q2)
+   return results[0].count
+
+   // const [results, fields] = await pool.query(
+   //    `SELECT COUNT(*)
+   //     FROM ${database}.usr_data AS ud
+   //     WHERE login REGEXP '^[0-9]+$'`
+   // )
+   // return results[0]["COUNT(*)"]
+}
+
+async function getUserByLogin(login) {
    const sel = `usr_id, login, firstname, lastname, gender, email, institution, street, city, zipcode, country, department, active`
-   // TODO user defined fields
    // TODO check args for SQL Injection
 
    const pool = await poolP
    const [results, fields] = await pool.query(
       `SELECT ${sel}
        FROM ${database}.usr_data AS ud
-       WHERE login = '${login}'
-         AND login REGEXP '^[0-9]+$'`
+       WHERE login = '${login}'`
    )
    return joinUDF(results[0])
 }
 
-async function getUserByUserId (userId) {
+async function getUserByUserId(userId) {
    const sel = `usr_id, login, firstname, lastname, gender, email, institution, street, city, zipcode, country, department, active`
-   // TODO user defined fields
    // TODO check args for SQL Injection
 
    const pool = await poolP
    const [results, fields] = await pool.query(
       `SELECT ${sel}
        FROM ${database}.usr_data AS ud
-       WHERE usr_id = '${userId}'
-         AND login REGEXP '^[0-9]+$'`
+       WHERE usr_id = '${userId}'`
    )
    return joinUDF(results[0])
 }
 
-async function getUserDefinedFields () {
+async function getUserDefinedFields() {
    const pool = await poolP
    const [results] = await pool.query(
       `SELECT ut.usr_id, ud.field_name, ut.value
@@ -109,7 +171,7 @@
    return results
 }
 
-async function getUserDefinedField (usr_id) {
+async function getUserDefinedField(usr_id) {
    const pool = await poolP
    const [results] = await pool.query(
       `SELECT ut.usr_id, ud.field_name, ut.value
@@ -123,7 +185,11 @@
 
 /////// obj_id / ref_id ////////////////////////////////////////////////////////////////
 
-async function getObjIdFromRefId (refId) {
+/**
+ * @param refId
+ * @returns {Promise<{ref_id,obj_id}|undefined>}
+ */
+async function getObjIdFromRefId(refId) {
    const pool = await poolP
    const [results] = await pool.query(
       `SELECT ref_id, obj_id
@@ -134,7 +200,7 @@
    return results.length ? results[0] : undefined
 }
 
-async function getRefIdFromObjId (objId) {
+async function getRefIdFromObjId(objId) {
    const pool = await poolP
    const [results] = await pool.query(
       `SELECT ref_id, obj_id
@@ -147,11 +213,12 @@
 
 /////////////////////////////////////////////////////////////////////////
 
-async function promiseDelay (ms) {
+async function promiseDelay(ms) {
    return new Promise(resolve => setTimeout(resolve, ms))
 }
 
-async function joinUDF (user) {
+async function joinUDF(user) {
+   if (!user) return user
    const fields = await getUserDefinedField(user.usr_id)
    for (const field of fields) {
       user[field.field_name] = field.value
@@ -161,45 +228,537 @@
 
 /////// Kurs ////////////////////////////////////////////////////////////////
 
-async function getKurse () {
+async function getKurse() {
    const pool = await poolP
-   const q = `SELECT or2.ref_id, or2.obj_id, od.title, od.description, od.type
+   const q = `SELECT or2.ref_id, or2.obj_id, od.title, od.description, od.type, od.offline
               FROM ${database}.object_reference or2
-              INNER JOIN ${database}.object_data od ON od.obj_id = or2.obj_id
-              WHERE od.type = 'crs' AND or2.deleted IS NULL
+                       INNER JOIN ${database}.object_data od ON od.obj_id = or2.obj_id
+              WHERE od.type = 'crs'
+                AND or2.deleted IS NULL
    `
    const [results] = await pool.query(q)
    return results
 }
 
-async function getKursByRefId (refId) {
-   const {ref_id, obj_id} = await getObjIdFromRefId(refId)
-   return getKursByObjId(obj_id)
-}
+// async function getKursByRefId (refId) {
+//    const {ref_id, obj_id} = await getObjIdFromRefId(refId)
+//    return getKursByObjId(obj_id)
+// }
 
-async function getKursByObjId (obj_id) {
+async function getKurs(ref_id) {
    const pool = await poolP
-   const q = `SELECT or2.ref_id, or2.obj_id, od.title, od.description, od.type
+   const q = `SELECT or2.ref_id, or2.obj_id, od.title, od.description, od.type, od.create_date, od.offline
               FROM ${database}.object_reference or2
-              INNER JOIN ${database}.object_data od ON od.obj_id = or2.obj_id
-              WHERE or2.obj_id = '${obj_id}'
+                       INNER JOIN ${database}.object_data od ON od.obj_id = or2.obj_id
+              WHERE or2.ref_id = '${ref_id}'
+                AND or2.deleted IS NULL
    `
-   const [results] = await pool.query(q)
-   return results.length ? results[0] : undefined
+   let [results] = await pool.query(q)
+   results = results.length ? results[0] : undefined
+   return results
 }
 
-async function getTeilnehmer (obj_id) {
+async function getKursItems(ref_id) {
    const pool = await poolP
-   const q = `SELECT ci.parent_id, or2.ref_id, or2.obj_id, od.title, od.\`type\`, om.usr_id, ulm.status
+   // const q = `SELECT or2.ref_id, or2.obj_id, od.title, od.description, od.type
+   //            FROM ${database}.object_reference or2
+   //            INNER JOIN ${database}.object_data od ON od.obj_id = or2.obj_id
+   //            WHERE or2.obj_id = '${obj_id}'
+   // `
+   const q = `SELECT ci.parent_id, or2.ref_id, or2.obj_id, od.title, od.type
               FROM ${database}.crs_items ci
                        INNER JOIN ${database}.object_reference or2 ON or2.ref_id = ci.obj_id
                        INNER JOIN ${database}.object_data od ON od.obj_id = or2.obj_id
-                       INNER JOIN ${database}.obj_members om ON om.obj_id = or2.obj_id
-                       INNER JOIN ${database}.ut_lp_marks ulm
-                                  ON ulm.obj_id = or2.obj_id AND ulm.usr_id = om.usr_id
-              WHERE (or2.ref_id = ${obj_id}
-                  OR parent_id = ${obj_id}) # Fliesenratgeber #AND om.usr_id = 6573 # Holger Börner / nicht-bestanden -
+              WHERE or2.ref_id = ${ref_id}
+                 OR parent_id = ${ref_id}
+                  AND or2.deleted is NULL
    `
    const [results] = await pool.query(q)
    return results
 }
+
+async function getKursItems2(ref_id) {
+   const pool = await poolP
+   const q = `
+       WITH RECURSIVE tree (parent_id, obj_id, ref_id, title, type) AS
+                          (SELECT ci.parent_id,
+                                  or2.obj_id,
+                                  ci.obj_id as ref_id,
+                                  od.title,
+                                  od.type
+                           FROM ${database}.crs_items ci
+                                    INNER JOIN ${database}.object_reference or2 ON or2.ref_id = ci.obj_id
+                                    INNER JOIN ${database}.object_data od ON od.obj_id = or2.obj_id
+                           WHERE ci.obj_id = ${ref_id} -- Kurs ref_id
+                             AND or2.deleted is NULL
+
+                           UNION ALL
+
+                           SELECT child.parent_id,
+                                  or2.obj_id,
+                                  child.obj_id as ref_id,
+                                  od.title,
+                                  od.type
+                           FROM ${database}.crs_items child
+                                    INNER JOIN ${database}.object_reference or2 ON or2.ref_id = child.obj_id
+                                    INNER JOIN ${database}.object_data od ON od.obj_id = or2.obj_id
+                                    JOIN tree ON child.parent_id = tree.ref_id
+                           WHERE or2.deleted is NULL)
+       SELECT *
+       FROM tree
+       ORDER BY tree.ref_id
+   `
+   const [results] = await pool.query(q)
+   return results
+}
+
+async function getKursTeilnehmer(ref_id) {
+   const pool = await poolP
+   const q = `SELECT ci.parent_id,
+                     or2.ref_id,
+                     or2.obj_id,
+                     od.title,
+                     od.type,
+                     om.usr_id,
+                     ud.login,
+                     ud.firstname,
+                     ud.lastname,
+                     ud.active,
+                     om.passed,
+                     ulm.status,
+                     ulm.status_changed
+              FROM ${database}.crs_items ci
+                       INNER JOIN ${database}.object_reference or2 ON or2.ref_id = ci.obj_id
+                       INNER JOIN ${database}.object_data od ON od.obj_id = or2.obj_id
+                       INNER JOIN ${database}.obj_members om ON om.obj_id = or2.obj_id AND om.member = 1
+                       INNER JOIN ${database}.usr_data ud ON ud.usr_id = om.usr_id
+                       LEFT JOIN ${database}.ut_lp_marks ulm ON ulm.obj_id = or2.obj_id AND ud.usr_id = ulm.usr_id
+              WHERE (or2.ref_id = ${ref_id} OR parent_id = ${ref_id})
+                AND or2.deleted IS NULL
+              ORDER BY usr_id
+   `
+   // log.info(q)
+   const [results] = await pool.query(q)
+   return results
+}
+
+async function getSingleKursTeilnehmer(ref_id, usr_id) {
+   const pool = await poolP
+   const q = `SELECT ci.parent_id,
+                     or2.ref_id,
+                     or2.obj_id,
+                     od.title,
+                     od.type,
+                     om.usr_id,
+                     ud.login,
+                     ud.firstname,
+                     ud.lastname,
+                     ud.active,
+                     om.passed,
+                     ulm.status,
+                     ulm.status_changed
+              FROM ${database}.crs_items ci
+                       INNER JOIN ${database}.object_reference or2 ON or2.ref_id = ci.obj_id
+                       INNER JOIN ${database}.object_data od ON od.obj_id = or2.obj_id
+                       INNER JOIN ${database}.obj_members om ON om.obj_id = or2.obj_id AND om.member = 1
+                       INNER JOIN ${database}.usr_data ud ON ud.usr_id = om.usr_id
+                       LEFT JOIN ${database}.ut_lp_marks ulm ON ulm.obj_id = or2.obj_id AND ud.usr_id = ulm.usr_id
+              WHERE (or2.ref_id = ${ref_id}
+                  OR parent_id = ${ref_id})
+                AND om.usr_id = ${usr_id}
+              ORDER BY usr_id
+   `
+   // log.info(q)
+   const [results] = await pool.query(q)
+   return results[0]
+}
+
+async function getKursTeilnehmerCount(ref_id) {
+   const pool = await poolP
+   const q = `SELECT COUNT(*) as count
+              FROM ${database}.crs_items ci
+                  INNER JOIN ${database}.object_reference or2
+              ON or2.ref_id = ci.obj_id
+                  INNER JOIN ${database}.object_data od ON od.obj_id = or2.obj_id
+                  INNER JOIN ${database}.obj_members om ON om.obj_id = or2.obj_id AND om.member = 1
+                  INNER JOIN ${database}.usr_data ud ON ud.usr_id = om.usr_id
+                  LEFT JOIN ${database}.ut_lp_marks ulm ON ulm.obj_id = or2.obj_id AND ud.usr_id = ulm.usr_id
+              WHERE (or2.ref_id = ${ref_id}
+                 OR parent_id = ${ref_id})
+              ORDER BY usr_id
+   `
+   let [results] = await pool.query(q)
+   results = results.length ? results[0] : undefined
+   return {ref_id, count: results.count}
+}
+
+async function getUserTeilnahmen(usr_id) {
+   const pool = await poolP
+   const q = `SELECT om.obj_id, or2.ref_id, om.usr_id, od.title, ulm.status, om.passed, ulm.status_changed
+              FROM ${database}.obj_members om
+                       INNER JOIN ${database}.object_reference or2 ON or2.obj_id = om.obj_id
+                       INNER JOIN ${database}.usr_data ud ON ud.usr_id = om.usr_id
+                       INNER JOIN ${database}.object_data od ON od.obj_id = om.obj_id
+                       LEFT JOIN ${database}.ut_lp_marks ulm
+                                 ON ulm.usr_id = om.usr_id AND ulm.obj_id = om.obj_id
+              WHERE om.usr_id = ${usr_id}
+                AND om.member = 1
+   `
+   // log.info(q).catch(console.error)
+   const [results] = await pool.query(q)
+   return results
+}
+
+async function getKursLp(obj_id, raw = false) {
+   const {ref_id} = await getRefIdFromObjId(obj_id)
+   const tnUnter = await getKursUnterobjektLp(obj_id)
+
+   if (raw) {
+      return tnUnter
+   } else { // LP aller Unterobjekte zusammenfassen
+      const libLp = require("../lib/libLp")
+      const data = libLp.alleAuswerten(tnUnter)
+      return data
+   }
+
+   // const teilnehmer = await getKursTeilnehmer(ref_id)
+   // const tnUnter = await getKursUnterobjektLp(obj_id)
+   // console.table(teilnehmer)
+   // console.table(tnUnter)
+   //
+   // /**
+   //  * Die beiden Datensätze mergen
+   //  * NEIN - es reicht der
+   //  *
+   //  */
+   //
+   // const idx = _.groupBy(tnUnter, "usr_id")
+   // for (const tn of teilnehmer) {
+   //     delete tn.parent_id
+   //     delete tn.type
+   //     delete tn.active
+   //
+   //     /** @type Array */
+   //     const unter = idx[tn.usr_id]
+   //     if (!unter) continue
+   //
+   //     /**
+   //      * wenn unter.status_changed neuer, wird unter tn.status_changed vorgezogen
+   //      * wenn unter.status neuer, überscheibt es tn.status
+   //      * tn.passed muss zurückgesetzt werden wenn tn.status überschrieben wird
+   //      */
+   //     tn.status_overwrite = false
+   //
+   //     // max unter status_changed finden
+   //     const unterStatusChanged = _.max(unter.map(u => u.status_changed))
+   //
+   //     // es muss nur überschrieben werden wenn das Unterdatum größer ist
+   //     if (unterStatusChanged > tn.status_changed) {
+   //         // unter Status auswerten
+   //         // 0 = noch nicht bearbeitet
+   //         // 1 = in Bearbeitung
+   //         // 2 = bestanden
+   //         // 3 = nicht bestanden
+   //
+   //         /** @type Array */
+   //         const unterStatusse = unter.map(u => u.status)
+   //         let newStatus = tn.status
+   //         const allSame = function () {
+   //             if (!unterStatusse.length) return false
+   //             const first = unterStatusse[0]
+   //             return unterStatusse.every(it => it === first)
+   //         }()
+   //         console.log({unterStatusse})
+   //         // Fall 1: keine Unterstatussse vorhanden -> status vom Kurs
+   //         if (!unterStatusse.length) {
+   //             newStatus = tn.status
+   //         }
+   //         // Fall 2: eines nicht bestanden -> nicht bestanden // 0,1,2,3 -> 3
+   //         if (unterStatusse.some(u => u === 3)) {
+   //             newStatus = 3
+   //         }
+   //         // Fall 3: alle statusse gleich -> status // 0,0,0 1,1,1 2,2,2 3,3,3
+   //         else if (allSame) {
+   //             newStatus = unterStatusse[0]
+   //         }
+   //         // Fall 4: wenn eines in Bearbeitung -> in Bearbeitung // 0,1,0 2,1,2
+   //         else if (unterStatusse.some(u => u === 1)) {
+   //             newStatus = 1
+   //         }
+   //         // Fall 5: sonst in Bearbeitung
+   //         else {
+   //             // newStatus = Math.max.apply(this, unterStatusse)
+   //             newStatus = 1
+   //         }
+   //         tn.status_changed = unterStatusChanged
+   //         tn.status = newStatus
+   //         tn.status_overwrite = true
+   //     }
+   // }
+   // return teilnehmer
+}
+
+async function getKursUnterobjektLp(obj_id) {
+   const pool = await poolP
+   const q = `SELECT ud.usr_id,
+                     ud.login,
+                     ud.firstname,
+                     ud.lastname,
+                     ulc.obj_id,
+                     ulc.item_id,
+                     ulc.lpmode,
+                     t.obj_id as item_obj_id,
+                     od.type,
+                     ulm.status,
+                     ulm.status_changed,
+                     ulm.percentage,
+                     ulm.completed
+              FROM ${database}.ut_lp_collections ulc
+                       INNER JOIN ${database}.object_reference t ON t.ref_id = ulc.item_id
+                       INNER JOIN ${database}.ut_lp_marks ulm ON ulm.obj_id = t.obj_id
+                       INNER JOIN ${database}.usr_data ud ON ud.usr_id = ulm.usr_id
+                       INNER JOIN ${database}.object_data od ON od.obj_id = t.obj_id
+              WHERE ulc.obj_id = ${obj_id} # obj_id Kurs
+                    AND ulc.active = 1
+                    AND ulc.lpmode = 5  # nur mode 5
+              ORDER BY ud.usr_id, ulc.item_id
+   `
+   const [results] = await pool.query(q)
+   return results
+}
+
+
+async function getKursOffline(obj_id) {
+   const pool = await poolP
+   const q = `SELECT offline
+              FROM ${database}.object_data
+              WHERE obj_id = ${obj_id}
+   `
+   const [results] = await pool.query(q)
+   return results[0]
+}
+
+async function setKursOffline(isOffline, obj_id) {
+   const pool = await poolP
+   const q = `UPDATE ${database}.object_data
+              SET offline = ${isOffline}
+              WHERE obj_id = ${obj_id}
+   `
+   const [results] = await pool.query(q)
+   // return results
+   return {offline: isOffline}
+}
+
+/////// UDF ////////////////////////////////////////////////////////////////
+
+async function getUdf() {
+   const pool = await poolP
+   const q = `SELECT field_id, field_name, field_type
+              from ${database}.udf_definition;`
+   const [results] = await pool.query(q)
+   return results
+}
+
+/////// ROLLEN ////////////////////////////////////////////////////////////////
+
+async function getKursTeilnehmerRolle(obj_id) {
+   const pool = await poolP
+   const q = `SELECT obj_id, title, description
+              from object_data od
+              WHERE type = "role" #AND title LIKE 'il_crs_member_157'
+                AND od.description LIKE 'Member%${obj_id}'
+   `
+   const [results] = await pool.query(q)
+   // darf nur einen Treffer ergeben
+   if (results.length) {
+      const {obj_id, title, description} = results[0]
+      return obj_id
+   } else {
+      return null
+   }
+}
+
+async function getKursTeilnehmerByRole(obj_id) {
+   const pool = await poolP
+   const q = `
+       SELECT obj_id as role_id, ru.usr_id, ud.firstname, ud.lastname
+       FROM ${database}.object_data od
+                INNER JOIN ${database}.rbac_ua ru ON ru.rol_id = od.obj_id
+                INNER JOIN ${database}.usr_data ud ON ud.usr_id = ru.usr_id
+       WHERE type = "role" #AND title LIKE 'il_crs_member_157'
+                AND od.description LIKE 'Member%${obj_id}'
+   `
+   const [results] = await pool.query(q)
+   return results
+}
+
+async function getKursRoles(ref_id) {
+   const pool = await poolP
+   const q = `
+       SELECT pa.rol_id, or2.ref_id, or2.obj_id, od2.type, od2.title, od2.description
+       FROM ${database}.rbac_pa pa
+                INNER JOIN ${database}.object_reference or2 ON or2.ref_id = pa.ref_id
+                INNER JOIN ${database}.object_data od ON od.obj_id = or2.obj_id
+                INNER JOIN ${database}.object_data od2 ON od2.obj_id = pa.rol_id
+       WHERE pa.ref_id = ${ref_id}
+   `
+   const [results] = await pool.query(q)
+   return results
+}
+
+
+/////// STATUS ////////////////////////////////////////////////////////////////
+
+/**
+ * Zwei Möglichkeiten:
+ * 1. es gibt schon einen Eintrag in ut_lp_marks, dann muss man diesen updaten
+ * 2. es gibt noch keinen Eintrag in ut_lp_marks, dann muss erst einer erstellt werden (NEIN! Für neuen TN sind bereits beide Einträge vorhanden, kann also wegfallen)
+ *
+ * Weiterhin haben wir es mit zwei Tabellen zu tun:
+ *    - obj_members (passed)
+ *    - ut_lp_marks (status)
+ *
+ * UPDATE: Wir nehmen an, dass beide Einträge schon vorhanden sind!
+ *
+ * @param courseId
+ * @param userId
+ * @param {Number} passed
+ * @param {Number} status
+ * @returns {Promise<{status: string}>}
+ */
+async function setStatus(courseId, userId, passed = null, status = null) {
+   const pool = await poolP
+   log.info(`------------setStatus----- courseId=${courseId}  userId=${userId}  passed=${passed}  status=${status}`)
+   passed = isNaN(Number(passed)) ? passed : Number(passed)
+   status = isNaN(Number(status)) ? status : Number(status)
+
+   // ACHTUNG Transactions funktioneren so nicht, erst mal ohne machen...
+   const q = `
+START TRANSACTION;
+
+UPDATE ${database}.ut_lp_marks ulm  
+SET status = ${status}
+WHERE ulm.usr_id = 31793 AND ulm.obj_id = 32212;
+
+UPDATE ${database}.obj_members om 
+SET passed = ${passed}
+WHERE om.usr_id = ${userId} AND om.obj_id = ${courseId};
+
+COMMIT;
+`
+
+   let date = dayjs().format('YYYY-MM-DD HH:mm:ss')
+   const q1 = `
+       UPDATE ${database}.ut_lp_marks ulm
+       SET status         = ${status},
+           status_changed = "${date}"
+       WHERE ulm.usr_id = ${userId}
+         AND ulm.obj_id = ${courseId};
+   `
+   const q2 = `
+       UPDATE ${database}.obj_members om
+       SET passed = ${passed}
+       WHERE om.usr_id = ${userId}
+         AND om.obj_id = ${courseId};
+   `
+
+   const [results1] = await pool.query(q1)
+   const [results2] = await pool.query(q2)
+   const {affectedRows: affectedRows1} = results1
+   const {affectedRows: affectedRows2} = results2
+
+   if (affectedRows1 && affectedRows2) {
+      return {status: "ok"}
+   } else {
+      throw {
+         status: "error",
+         reason: {affectedRows1, affectedRows2}
+      }
+   }
+}
+
+
+/////// ADMINS ////////////////////////////////////////////////////////////////
+
+
+async function getCourseAdminRoles() {
+   const pool = await poolP
+   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
+}
+
+async function getCourseWithoutAdminRoles() {
+   const pool = await poolP
+   const q = `
+       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
+// }

--
Gitblit v1.8.0