store_magic.js

/**
 * 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);
            });
        },
    },
});