/**
* This store mnages AI/ML tasks and requests, as well as other "smart" or api-drieven features
* @file store/magic.ts
* @namespace .magic
* @memberOf store
*/
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
// setup Pinia store
import { _status } from "@/common";
import { defineStore } from "pinia";
const API_URL = `https://${process.env.VUE_APP_BRAND_DOMAIN__API}/`;
export const useMagic = defineStore({
id: "magic",
/**
* @namespace .magic.state
* @memberOf .magic
*/
state: () => ({
/**
* @memberOf .magic.state
* @property {Object} last Keeps track of when each endpoint was last called and with what hash data in order to rate limit
*/
last: {},
/**
* @memberOf .magic.state
* @property {Array} types List of task types and their short names, full names, list names, and keyboard shortcuts
*/
types: [
{ key: "note", name: "Note", list_as: "Add a Note", shortcuts: ["n"] },
{
key: "task",
name: "Assignment",
list_as: "Schedule an Assignment",
shortcuts: ["a"],
},
// socratic: "Socratic Seminar",
{
key: "test",
name: "Test",
list_as: "Schedule a Test",
shortcuts: ["t"],
},
// summative: "Summative Assignment",
// midterm: "Midterm",
{
key: "project",
name: "Project",
list_as: "Schedule a Project",
shortcuts: ["p"],
},
{
key: "quiz",
name: "Quiz",
list_as: "Schedule a Quiz",
shortcuts: ["q"],
},
{
key: "exam",
name: "Exam",
list_as: "Schedule an Exam",
shortcuts: ["e"],
},
],
/**
* @memberOf .magic.state
* @property {Object} prefixes List of prefixes for each task type
*/
prefixes: {
note: "",
task: "Complete ",
// socratic: "",
test: "Study for ",
// summative: "",
// midterm: "",
project: "Prepare ",
quiz: "Study for ",
exam: "Study for ",
},
/**
* @memberOf .magic.state
* @property {Object} done_prefixes List of prefixes for making a task finished/unfinished
*/
done_prefixes: {
note: "",
task: "",
// socratic: "",
test: "Studying for ",
// summative: "",
// midterm: "",
project: "Preparation for ",
quiz: "Studying for ",
exam: "Studying for ",
},
}),
/**
* @namespace .magic.getters
* @memberOf .magic
*/
getters: {},
/**
* @namespace .magic.actions
* @memberOf .magic
*/
actions: {
/**
* @memberOf .magic.actions
* @function prefix
* @description get the prefix for a given task
* @param {Object} task - the task object
* @returns {String} text - the prefix for the task
* @see prefixes
* @see types
*/
prefix(task) {
return this.prefixes[task.type];
},
/**
* @memberOf .magic.actions
* @function done_prefix
* @description get the done prefix for a given task
* @param {Object} task - the task object
* @returns {String} text - the done prefix for the task
* @see done_prefixes
* @see types
*/
done_prefix(task) {
if (!(task === null || task === void 0 ? void 0 : task.type) || !task.name)
return "Task";
return `${this.done_prefixes[task.type]
? this.done_prefixes[task.type] + task.type
: this.type_full(task.type)} "${task.name}"`;
},
/**
* @memberOf .magic.actions
* @function type_full
* @description get the full type name from the short type
* @param {String} short_type - the short type of the task
* @returns {String} text - the full type name
* @see types
*/
type_full(short_type) {
var _a;
return ((_a = this.types.find((t) => t.key === short_type)) === null || _a === void 0 ? void 0 : _a.name) || short_type;
},
/**
* @memberOf .magic.actions
* @function path
* @description get the smart text associated with a given url
* @param {String} path
* @returns {String} text - the smart text associated with the url
*/
text(path) {
return __awaiter(this, void 0, void 0, function* () {
if (!path)
return;
return yield this.rated_api_get("get/magic/link-text", { path });
});
},
/**
* @memberOf .magic.actions
* @function type
* @description get the smart type associated with a given description
* @param {String} text
*/
type(task) {
return __awaiter(this, void 0, void 0, function* () {
if (!task)
return;
return yield this.rated_api_get("get/magic/task-type", { task });
});
},
/**
* @memberOf .magic.actions
* @function api_get
* @description do get request to url: API_URL + endpoint with request query of data
* @param {String} endpoint api endpoint
* @param {Object} data query params
* @returns {Promise<Object>} json
*/
api_get(endpoint, data) {
return __awaiter(this, void 0, void 0, function* () {
data = data || {};
if (!endpoint)
return;
// get url with query
const url = new URL(API_URL + endpoint);
Object.keys(data).forEach((key) => url.searchParams.append(key, data[key]));
const run_hash = Math.random().toString(36).substring(7);
_status.log(`🛜 Running API fetch | <${run_hash}>`);
// do request
this.last[endpoint] = { time: Date.now(), hash: run_hash };
try {
const response = yield fetch(url.href, {
method: "GET",
headers: {
"Content-Type": "application/json",
},
});
// response
if (!response.ok)
throw "Non-valid response";
const json = yield response.json();
_status.log(`🛜 API fetch success | <${run_hash}>`);
return json.data;
}
catch (err) {
_status.error(`🛜 API fetch failed | <${run_hash}>`, err);
return null;
}
});
},
/**
* @memberOf .magic.actions
* @function rated_api_get
* @description do api_get but abide by rate limit (only eval once every 15 seconds)
*/
rated_api_get(type, data) {
return __awaiter(this, void 0, void 0, function* () {
var _a, _b, _c, _d;
_status.log("🛜 API fetch requested");
const run_hash = Math.random().toString(36).substring(7);
const prev_time = (_a = this.last[type]) === null || _a === void 0 ? void 0 : _a.time;
const prev_hash = (_b = this.last[type]) === null || _b === void 0 ? void 0 : _b.hash;
if (Date.now() - prev_time < 2000) {
_status.warn("🛜 API fetch rate limited");
// wait 15 seconds and see if it is still the most recent request
yield new Promise((resolve) => setTimeout(resolve, 15000));
if (((_c = this.last[type]) === null || _c === void 0 ? void 0 : _c.time) !== prev_time || ((_d = this.last[type]) === null || _d === void 0 ? void 0 : _d.hash) !== prev_hash) {
_status.warn("🛜 Outdated API fetch skipped");
return;
}
else {
_status.log("🛜 API fetch rate limit lifted");
}
}
return yield this.api_get(type, data);
});
},
},
});