import {query,UUID2hex,HEX2uuid} from '@commtool/sql-query'
import { parseTimestampToSeconds } from '../../utils/parseTimestamp.js'
import {errorLoggerUpdate} from '../../utils/requestLogger.js'
import {renderObject} from '../../utils/renderTemplates.js'
import {Templates} from '../../utils/compileTemplates.js'
import {match as matchAchievement} from '../../utils/objectfilter/filters/achievement.js'
import './../../types.js';
/**
* Checks if a person is qualified for a job based on their achievements.
*
* @param {Buffer} UIDperson - The unique identifier of the person to check qualifications for.
* @param {Array|Object} qualifications - The qualifications/achievements required for the job.
* @param {number} [timestamp] - Optional Unix timestamp to check qualifications at a specific point in time.
* @returns {Promise<number>} - Returns 1 if the person is qualified, 0 if not qualified, and undefined if an error occurs.
* @throws {Error} - Logs error to errorLoggerUpdate if query fails.
*/
export const jobQualified=async (UIDperson,qualifications,timestamp)=>
{
try {
const asOf=timestamp ? `FOR SYSTEM_TIME AS OF FROM_UNIXTIME(${timestamp})` : ''
// check if person is qualified for this job
const fullfilled = await query(`SELECT ObjectBase.UIDBelongsTo, Links.UIDTarget FROM ObjectBase ${asOf}
INNER JOIN Links ${asOf} ON (Links.UID=ObjectBase.UID)
WHERE ObjectBase.UIDBelongsTo=? AND Links.Type IN ('member','memberA','achievement') AND ObjectBase.Type='achievement'
GROUP BY Links.UIDTarget `,[UIDperson])
const qualies=fullfilled.map(el=>(HEX2uuid(el.UIDTarget)))
if(qualifications && matchAchievement(qualies,qualifications))
{
return 1
}
return 0
}
catch(e)
{
errorLoggerUpdate(e)
}
}
/**
* Recreates job objects in the database for the provieded job UIDs (array)
*
* @async
* @function recreateJobs
* @param {Object} req - The request object containing session information.
* @param {Array} jobs - Array of job objects to be recreated.
* @param {number} timestamp - The timestamp to use for updating objects.
* @param {boolean} [requalify=false] - Whether to requalify the jobs.
* @throws {Error} Logs error if job recreation fails.
*
* @description
* This function iterates through a list of jobs and updates their corresponding
* entries in the ObjectBase database table. It processes job qualification status,
* renders objects from templates, and updates various fields including Title,
* qualification index, hierarchie, stage, gender */
export const recreateJobs=async (req,jobs,timestamp,requalify=false)=>
{
try {
const root=req.session.root
const template= Templates[root].job
for (const job of jobs)
{
let qualified= job.qualified ? 1 : 0
if(requalify)
{
const functionData=JSON.parse(job.FunctionData)
qualified=await jobQualified(job.UIDperson,functionData.qualification ?? {},timestamp)
}
let jobData={}
try
{
jobData=JSON.parse(job.JobData)
}
catch(e)
{
console.error('json parse error recreateJobs in jobs.js',e,job.JobData)
errorLoggerUpdate(e)
}
const Data={
...jobData,
UID:HEX2uuid(job.UID),
qualified:qualified,
function:{...JSON.parse(job.FunctionData),functionUID:HEX2uuid(job.UIDfunction)},
}
const object=await renderObject(template,{...Data,member:JSON.parse(job.MemberData)},req)
// get old object dqta and update, if there is a difference
const oldObject=await query(`SELECT ObjectBase.Title,ObjectBase.dindex, ObjectBase.hierarchie, ObjectBase.stage, ObjectBase.gender
FROM ObjectBase WHERE UID=?`,[job.UID])
if(oldObject.length===0)
{
throw new Error('job not found')
}
const objectBase=oldObject[0]
if(objectBase.Title!==object.Title || objectBase.dindex!==qualified || objectBase.hierarchie!==object.hierarchie || objectBase.stage!==object.stage
|| objectBase.gender!==object.gender)
{
await query( `UPDATE ObjectBase SET
Title=?,dindex=?,hierarchie=?,stage=?,gender=?,Data=?
WHERE UID=?`,
[object.Title,qualified,
object.hierarchie,object.stage,object.gender,JSON.stringify(Data),job.UID],
{backDate:Math.max(timestamp,job.ValidFrom)})
}
}
}
catch(e)
{
errorLoggerUpdate(e)
}
}
/**
* Recreates all jobs for an organization based on templates.
*
* This function queries the database for all job objects belonging to the organization,
* optionally as of a specific timestamp, and then recreates them. It retrieves related
* data such as person, function, group, and member information needed for job recreation.
*
* @param {Object} req - Express request object
* @param {Object} req.query - Query parameters
* @param {string} [req.query.timestamp] - Optional timestamp in milliseconds for point-in-time retrieval
* @param {Object} req.session - Session data
* @param {string} req.session.root - Root UID of the organization
* @param {Object} res - Express response object
* @returns {Promise<void>} - JSON response indicating success or failure
* @throws {Error} - Logs errors to the error logger
*/
export const recreateJobsOrga= async (req,res)=>
{
// recreate all objects of the organisation via the template
let timestamp = parseTimestampToSeconds(req.query.timestamp ? String(req.query.timestamp) : undefined)
const asOf=timestamp ? `FOR SYSTEM_TIME AS OF FROM_UNIXTIME(${timestamp})` : ''
const requalify=req.query?.['requalify'] ? req.query['requalify'] === 'true' : true
try{
if(!timestamp)
timestamp=Date.now()/1000
const jobs=await query(`SELECT ObjectBase.UID,ObjectBase.Data AS JobData,ObjectBase.dindex as qualified, PGroup.Data AS GroupData,
Person.UID AS UIDperson,FunctionT.Data AS FunctionData, FunctionT.UID AS UIDfunction,
Member.Data AS MemberData ,UNIX_TIMESTAMP(latest.ValidFrom) AS ValidFrom
FROM ObjectBase ${asOf}
INNER JOIN ObjectBase AS latest ON (ObjectBase.UID=latest.UID)
INNER JOIN Links ${asOf} ON (Links.UID=ObjectBase.UID AND Links.Type IN ('member','memberA'))
INNER JOIN Links ${asOf} AS GLink ON (GLink.UID=ObjectBase.UID AND GLink.Type='memberA')
INNER JOIN ObjectBase ${asOf} AS PGroup ON (PGroup.UID=GLink.UIDTarget)
INNER JOIN ObjectBase ${asOf} AS Person ON (ObjectBase.UIDBelongsTo=Person.UID AND Person.Type IN ('person','extern'))
INNER JOIN Links ${asOf} AS FLink ON (FLink.UIDTarget=ObjectBase.UID AND FLink.Type='function')
INNER JOIN ObjectBase ${asOf} AS FunctionT ON (FunctionT.UID=FLink.UID)
INNER JOIN Member ON (Member.UID=ObjectBase.UIDBelongsTo)
WHERE ObjectBase.Type='job' AND Links.UIDTarget=?
GROUP BY ObjectBase.UID`,
[UUID2hex(req.session.root)])
recreateJobs(req,jobs,timestamp, requalify)
res.json({success:true})
}
catch(e)
{
errorLoggerUpdate(e)
}
}
/**
* Requalifies all jobs based on the given function template.
*
* This function fetches all jobs related to the specified function template and updates their qualification status.
* It also recreates the jobs with the updated qualification status.
* This function should be called, when a function template including its achievement rules is updated.
*
* @async
* @param {Object} req - The request object.
* @param {Buffer} functionT - The UID of the function template.
* @param {number} [timestamp] - Unix timestamp to get data as of a specific point in time. If not provided, current timestamp is used.
* @returns {Promise<Object>} An object with a success property indicating if the operation was successful.
* @throws {Error} Logs any errors that occur during the process via errorLoggerUpdate.
*/
export const requalify=async (req,functionT,timestamp=null)=>
{
// requalify all jobs based on this function template
try{
const asOf=timestamp ? `FOR SYSTEM_TIME AS OF FROM_UNIXTIME(${timestamp})` : ''
if(!timestamp)
timestamp=Date.now()/1000
const jobs=await query(`SELECT ObjectBase.UID, ObjectBase.UIDBelongsTo AS UIDperson,
FunctionT.Data AS FunctionData, FunctionT.UID AS UIDfunction, ObjectBase.Data AS JobData,
Member.Data AS MemberData, UNIX_TIMESTAMP(latest.ValidFrom) AS ValidFrom
FROM ObjectBase ${asOf}
INNER JOIN ObjectBase AS latest ON (ObjectBase.UID=latest.UID)
INNER JOIN Links ${asOf} AS FLink ON (FLink.UIDTarget=ObjectBase.UID AND FLink.Type='function')
INNER JOIN ObjectBase ${asOf} AS FunctionT ON (FunctionT.UID=FLink.UID)
INNER JOIN Member ON (Member.UID=ObjectBase.UIDBelongsTo)
WHERE ObjectBase.Type='job' AND FunctionT.UID=?`,[functionT])
for(const job of jobs)
{
const functionData=JSON.parse(job.FunctionData)
if(functionData.qualification)
{
const qualified=await jobQualified(job.UIDperson,functionData.qualification,timestamp)
const jobData=JSON.parse(job.JobData)
jobData.qualified=qualified
query(`UPDATE ObjectBase SET Data=?,dindex=? WHERE UID=?`,
[JSON.stringify(jobData),qualified,job.UID]),
{backDate:Math.max(timestamp,job.ValidFrom)}
}
}
await recreateJobs(req,jobs,timestamp,true)
return {success:true}
}
catch(e)
{
errorLoggerUpdate(e)
}
}
/**
* Recreates jobs based on a specified function template.
*
* This function retrieves all jobs associated with a given function template
* and recreates them based on the template using the recreateJobs function.
* it should be called, when a function template is updated without changed to the achievement rules.
* @param {Object} req - The request object from the client.
* @param {string} functionT - The UID of the function template to use for recreating jobs.
* @param {number|null} [timestamp=null] - Optional UNIX timestamp to retrieve jobs as they were at that time.
* If null, current time is used.
* @returns {Promise<Object>} A promise that resolves to an object with a success property.
* @throws {Error} If the database query fails, the error is logged but not returned.
*/
export const recreateJobsFunction=async (req,functionT,timestamp=null)=>
{
// recreate all jobs based on this function template of the organisation via the template
try{
const asOf=timestamp ? `FOR SYSTEM_TIME AS OF FROM_UNIXTIME(${timestamp})` : ''
if(!timestamp)
timestamp=Date.now()/1000
const jobs=await query(`SELECT ObjectBase.UID,ObjectBase.Data AS JobData,ObjectBase.dindex as qualified, PGroup.Data AS GroupData,PGroup.UID AS UIDGroup,
Person.UID AS UIDperson, Person.dindex AS dindexPerson,FunctionT.Data AS FunctionData, FunctionT.UID AS UIDfunction,
Member.Data AS MemberData, UNIX_TIMESTAMP(latest.ValidFrom) AS ValidFrom
FROM ObjectBase ${asOf}
INNER JOIN ObjectBase AS latest ON (ObjectBase.UID=latest.UID)
INNER JOIN Links ${asOf} ON (Links.UID=ObjectBase.UID AND Links.Type IN ('member','memberA'))
INNER JOIN Links ${asOf} AS GLink ON (GLink.UID=ObjectBase.UID AND GLink.Type='memberA')
INNER JOIN ObjectBase ${asOf} AS PGroup ON (PGroup.UID=GLink.UIDTarget)
INNER JOIN ObjectBase ${asOf} AS Person ON (ObjectBase.UIDBelongsTo=Person.UID AND Person.Type IN('person',extern'))
INNER JOIN Links ${asOf} AS FLink ON (FLink.UIDTarget=ObjectBase.UID AND FLink.Type='function')
INNER JOIN ObjectBase ${asOf} AS FunctionT ON (FunctionT.UID=FLink.UID)
INNER JOIN Member ON (Member.UID=ObjectBase.UIDBelongsTo)
WHERE ObjectBase.Type='job' AND FunctionT.UID=?
GROUP BY ObjectBase.UID`,
[functionT],
{log:true})
recreateJobs(req,jobs,timestamp,true)
return {success:true}
}
catch(e)
{
errorLoggerUpdate(e)
}
}
/**
* Recreates jobs for a specific person based on their templates.
*
* @async
* @function recreateJobsPerson
* @param {Object} req - Express request object
* @param {Object} req.params - Request parameters
* @param {string} req.params.UIDperson - UID of the person
* @param {Object} req.query - Request query parameters
* @param {number} [req.query.timestamp] - Optional timestamp for point-in-time query
* @param {Object} res - Express response object
* @param {boolean} [requalify=true] - Whether to recalculate job qualification status based on achievements
* @returns {Promise<void>} - Sends JSON response indicating success or handles error
* @throws {Error} - Logs error to error logger if query fails
* @description Retrieves job data for a person at a specific point in time (if timestamp provided),
* then recreates the jobs using the recreateJobs function. When requalify is true,
* it recalculates job qualification status based on achievements if requalify is true (default).
* then recreates the jobs using the recreateJobs function.
*/
/**
* Recreates jobs for a specific person based on their UID.
*
* This function fetches job data from the database for a person, potentially as of a specific timestamp,
* and then recreates these jobs. It can also requalify the jobs as part of the process.
*
* @async
* @function recreateJobsPerson
* @param {Object} req - The request object containing query parameters.
* @param {string|Buffer} UIDperson - The UID (hex string or Buffer) of the person whose jobs need to be recreated.
* @param {boolean} [requalify=true] - Whether to requalify the jobs during recreation.
* @returns {Promise<Object>} - A promise that resolves when the jobs have been recreated.
* @throws {Error} - Any errors that occur during the job recreation process are logged.
*/
export const recreateJobsPerson = async (req, UIDperson, requalify=true)=>
{
let timestamp = parseTimestampToSeconds(req.query.timestamp ? String(req.query.timestamp) : undefined)
const asOf=timestamp ? `FOR SYSTEM_TIME AS OF FROM_UNIXTIME(${timestamp})` : ''
const person=UUID2hex(UIDperson)
try{
if(!timestamp)
timestamp=Date.now()/1000
const jobs=await query(`SELECT ObjectBase.UID,ObjectBase.Data AS JobData,ObjectBase.dindex as qualified, PGroup.Data AS GroupData,
Person.UID AS UIDperson,FunctionT.Data AS FunctionData, FunctionT.UID AS UIDfunction ,
Member.Data AS MemberData,UNIX_TIMESTAMP(latest.ValidFrom) AS ValidFrom
FROM ObjectBase ${asOf}
INNER JOIN ObjectBase AS latest ON (ObjectBase.UID=latest.UID)
INNER JOIN Links ${asOf} ON (Links.UID=ObjectBase.UID AND Links.Type IN ('member','memberA'))
INNER JOIN Links ${asOf} AS GLink ON (GLink.UID=ObjectBase.UID AND GLink.Type='memberA')
INNER JOIN ObjectBase ${asOf} AS PGroup ON (PGroup.UID=GLink.UIDTarget)
INNER JOIN ObjectBase ${asOf} AS Person ON (ObjectBase.UIDBelongsTo=Person.UID AND Person.Type IN('person','extern'))
INNER JOIN Links ${asOf} AS FLink ON (FLink.UIDTarget=ObjectBase.UID AND FLink.Type='function')
INNER JOIN ObjectBase ${asOf} AS FunctionT ON (FunctionT.UID=FLink.UID)
INNER JOIN Member ON (Member.UID=ObjectBase.UIDBelongsTo)
WHERE ObjectBase.Type='job' AND ObjectBase.UIDBelongsTo=?
GROUP BY ObjectBase.UID`,[person],
{log:true}
)
recreateJobs(req,jobs,timestamp,requalify)
return {success:true}
}
catch(e)
{
errorLoggerUpdate(e)
}
}
/**
* Recreates all jobs within a specified group using templates.
*
* This function fetches all job objects associated with a given group,
* optionally at a specific point in time (using system versioning),
* and recreates them based on their templates using the recreateJobs function.
* this function should be called, when a group is updated.
*
* @param {Object} req - The request object
* @param {Buffer} group - The UID of the group whose jobs will be recreated
* @param {number|null} timestamp - Optional Unix timestamp for point-in-time recreation (null for current time)
* @returns {Promise<Object>} A response object with success status and optional error message
* @throws {Error} May throw errors which are caught and logged via errorLoggerUpdate
*/
export const recreateJobsGroup=async (req,group,timestamp=null)=>
{
try {
// recreate all objects of the group via the template
const asOf=timestamp ? `FOR SYSTEM_TIME AS OF FROM_UNIXTIME(${timestamp})` : ''
try{
if(!timestamp)
timestamp=Date.now()/1000
const jobs=await query(`SELECT ObjectBase.UID,ObjectBase.Data AS JobData,ObjectBase.dindex as qualified, PGroup.Data AS GroupData,PGroup.UID AS UIDGroup,
Person.UID AS UIDperson,Person.dindex AS dindexPerson, FunctionT.Data AS FunctionData, FunctionT.UID AS UIDfunction,
Member.Data AS MemberData, UNIX_TIMESTAMP(latest.ValidFrom) AS ValidFrom
FROM ObjectBase ${asOf}
INNER JOIN ObjectBase AS latest ON (ObjectBase.UID=latest.UID)
INNER JOIN Links ${asOf} ON (Links.UID=ObjectBase.UID AND Links.Type IN ('member','memberA'))
INNER JOIN Links ${asOf} AS GLink ON (GLink.UID=ObjectBase.UID AND GLink.Type='memberA')
INNER JOIN ObjectBase ${asOf} AS PGroup ON (PGroup.UID=GLink.UIDTarget)
INNER JOIN ObjectBase ${asOf} AS Person ON (ObjectBase.UIDBelongsTo=Person.UID AND Person.Type IN('person','extern'))
INNER JOIN Links ${asOf} AS FLink ON (FLink.UIDTarget=ObjectBase.UID AND FLink.Type='function')
INNER JOIN ObjectBase ${asOf} AS FunctionT ON (FunctionT.UID=FLink.UID)
INNER JOIN Member ON (Member.UID=ObjectBase.UIDBelongsTo)
WHERE ObjectBase.Type='job' AND Links.UIDTarget=?
GROUP BY ObjectBase.UID `,[group])
recreateJobs(req,jobs,timestamp,true)
return {success:true}
}
catch(e)
{
return {succes:false,message:e}
}
}
catch(e)
{
errorLoggerUpdate(e)
}
}