@grouparoo/core
Version:
The Grouparoo Core
231 lines (230 loc) • 9.46 kB
JavaScript
"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 = {}));