UNPKG

@grouparoo/core

Version:
231 lines (230 loc) 9.46 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.RunOps = void 0; const actionhero_1 = require("actionhero"); const Run_1 = require("../../models/Run"); const GrouparooRecord_1 = require("../../models/GrouparooRecord"); const Group_1 = require("../../models/Group"); const Schedule_1 = require("../../models/Schedule"); const GroupMember_1 = require("../../models/GroupMember"); const GrouparooModel_1 = require("../../models/GrouparooModel"); const Property_1 = require("../../models/Property"); const Import_1 = require("../../models/Import"); const sequelize_1 = require("sequelize"); const runMode_1 = require("../runMode"); var RunOps; (function (RunOps) { /** * Create a Run for this Group or Model */ async function run(creator, destinationId) { if ((0, runMode_1.getGrouparooRunMode)() === "cli:validate") return; if ((0, runMode_1.getGrouparooRunMode)() === "cli:config") return; await stopPreviousRuns(creator); if (creator instanceof Group_1.Group) { if (creator.state !== "deleted") { await creator.update({ state: "updating" }); } } const run = await Run_1.Run.create({ creatorId: creator.id, creatorType: getCreatorTypeString(creator), state: "running", destinationId, }); return run; } RunOps.run = run; /** * Stop previous Runs for this Group */ async function stopPreviousRuns(creator) { const previousRuns = await Run_1.Run.findAll({ where: { creatorId: creator.id, creatorType: getCreatorTypeString(creator), state: "running", }, }); for (const run of previousRuns) await run.stop(); } RunOps.stopPreviousRuns = stopPreviousRuns; function getCreatorTypeString(creator) { if (creator instanceof Schedule_1.Schedule) return "schedule"; if (creator instanceof Property_1.Property) return "property"; if (creator instanceof Group_1.Group) return "group"; if (creator instanceof GrouparooModel_1.GrouparooModel) return "grouparooModel"; throw new Error(`cannot determine creator type of ${creator}`); } RunOps.getCreatorTypeString = getCreatorTypeString; /** * Return counts of the states of each import in N chunks over the lifetime of the run * Great for drawing charts! */ async function quantizedTimeline(run, steps = 25) { var _a, _b; const data = []; const start = run.createdAt.getTime(); const lastImportedImport = await Import_1.Import.findOne({ where: { creatorId: run.id }, order: [["importedAt", "desc"]], limit: 1, }); const end = ((_a = lastImportedImport === null || lastImportedImport === void 0 ? void 0 : lastImportedImport.importedAt) === null || _a === void 0 ? void 0 : _a.getTime()) || ((_b = run === null || run === void 0 ? void 0 : run.completedAt) === null || _b === void 0 ? void 0 : _b.getTime()) || new Date().getTime(); const stepSize = Math.floor((end - start) / steps); const boundaries = [start - stepSize * 2]; let i = 1; while (i <= steps + 4) { const lastBoundary = boundaries[i - 1]; const nextBoundary = lastBoundary + stepSize; boundaries.push(nextBoundary); const timeData = { lastBoundary, nextBoundary, steps: { associate: await run.$count("imports", { where: { recordAssociatedAt: { [sequelize_1.Op.gte]: lastBoundary, [sequelize_1.Op.lt]: nextBoundary, }, }, }), imported: await run.$count("imports", { where: { importedAt: { [sequelize_1.Op.gte]: lastBoundary, [sequelize_1.Op.lt]: nextBoundary, }, }, }), }, }; data.push(timeData); i++; } return data; } RunOps.quantizedTimeline = quantizedTimeline; /** * Count up the totals from imports for `importsCreated`, `recordsCreated` and `recordsImported` */ async function updateTotals(run) { const importsCreated = await Import_1.Import.count({ where: { creatorId: run.id }, }); const recordsCreated = await Import_1.Import.count({ where: { creatorId: run.id, createdRecord: true }, }); const recordsImported = await Import_1.Import.count({ where: { creatorId: run.id, importedAt: { [sequelize_1.Op.ne]: null } }, distinct: true, col: "recordId", }); const percentComplete = await run.determinePercentComplete(false, false); await run.update({ importsCreated, recordsCreated, recordsImported, percentComplete, }); } RunOps.updateTotals = updateTotals; /** * Make a guess to what percent complete this Run is */ async function determinePercentComplete(run) { await run.reload(); // the loaded instance's counts may have changed after it was loaded if (run.state === "complete") return 100; if (run.state === "stopped") return 100; if (run.creatorType === "group") { if (run.method === "complete") return 99; if (!run.method) return 0; // we are exporting the group to CSV or not yet set const group = await Group_1.Group.findById(run.creatorId); let totalGroupMembers = await group.countPotentialMembers(); const membersAlreadyUpdated = await GroupMember_1.GroupMember.count({ where: { groupId: group.id, updatedAt: { [sequelize_1.Op.gte]: run.createdAt }, }, }); if (totalGroupMembers === 0 && membersAlreadyUpdated === 0) return 0; if (group.state === "deleted") return 99; // there are 3 phases to group runs, but only 2 really could have work, so we attribute 1/2 to each phase let percentComplete = Math.floor(100 * (membersAlreadyUpdated / (totalGroupMembers > 0 ? totalGroupMembers : 1))); if (!run.method.match(/remove/i)) { percentComplete = Math.floor(percentComplete / 2); } else { percentComplete = 49 + Math.floor(percentComplete / 2); } return percentComplete; } else if (run.creatorType === "schedule") { const schedule = await Schedule_1.Schedule.findById(run.creatorId); try { return schedule.runPercentComplete(run); } catch (error) { (0, actionhero_1.log)(`Error calculating the percent complete for run ${run.id} (${schedule.name}): ${error}`, "error"); return 0; } } else { // for properties and for other types of internal run, we can assume we have to check every record in the system const totalRecords = await GrouparooRecord_1.GrouparooRecord.count(); return Math.floor(100 * (run.importsCreated / (totalRecords > 0 ? totalRecords : 1))); } } RunOps.determinePercentComplete = determinePercentComplete; /** * Is there already a task enqueued to process this run? */ async function isRunEnqueued(taskName, runId) { let found = []; // normal queues const queues = await actionhero_1.api.resque.queue.queues(); for (const i in queues) { const q = queues[i]; const length = await actionhero_1.api.resque.queue.length(q); const batchFound = await actionhero_1.task.queued(q, 0, length + 1); const matches = batchFound.filter((t) => t.class === taskName); found = found.concat(matches); } // delayed queues const allDelayed = await actionhero_1.api.resque.queue.allDelayed(); for (const timestamp in allDelayed) { const matches = allDelayed[timestamp].filter((t) => t.class === taskName); found = found.concat(matches); } // working tasks const workingOn = await actionhero_1.task.allWorkingOn(); for (const worker in workingOn) { if (typeof workingOn[worker] === "string") continue; const payload = workingOn[worker].payload; if (payload.class === taskName) found = found.concat(payload); } return (found.filter((t) => t.args[0].runId === runId) .length > 0); } RunOps.isRunEnqueued = isRunEnqueued; })(RunOps = exports.RunOps || (exports.RunOps = {}));