From 85562484192e0caa8a21c4706f74bd0737d096b6 Mon Sep 17 00:00:00 2001 From: John Schiltz Date: Mon, 9 Jan 2023 02:20:17 -0600 Subject: [PATCH 01/22] add typedoc --- .gitignore | 8 ++- package-lock.json | 165 +++++++++++++++++++++++++++++++++++++++++++++- package.json | 10 +-- tsconfig.json | 12 ++++ 4 files changed, 189 insertions(+), 6 deletions(-) diff --git a/.gitignore b/.gitignore index 6ca9203..b88772c 100644 --- a/.gitignore +++ b/.gitignore @@ -3,4 +3,10 @@ *.env node_modules unused-commands -d.py/ \ No newline at end of file +d.py/ + +# Ignore specific dev commands not allowed in production +commands/owner/deleteChannels.ts +commands/owner/deleteRoles.ts + +docs/ diff --git a/package-lock.json b/package-lock.json index d8e1b07..241c100 100644 --- a/package-lock.json +++ b/package-lock.json @@ -22,7 +22,8 @@ "devDependencies": { "@jest/globals": "^29.3.1", "jest": "^29.3.1", - "ts-jest": "^29.0.3" + "ts-jest": "^29.0.3", + "typedoc": "^0.23.24" }, "engines": { "node": "17.x" @@ -3010,6 +3011,12 @@ "node": ">=6" } }, + "node_modules/jsonc-parser": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.2.0.tgz", + "integrity": "sha512-gfFQZrcTc8CnKXp6Y4/CBT3fTc0OVuDofpre4aEeEpSBPV5X5v4+Vmx+8snU7RLPrNHPKSgLxGo9YuQzz20o+w==", + "dev": true + }, "node_modules/kareem": { "version": "2.4.1", "resolved": "https://registry.npmjs.org/kareem/-/kareem-2.4.1.tgz", @@ -3071,6 +3078,12 @@ "yallist": "^3.0.2" } }, + "node_modules/lunr": { + "version": "2.3.9", + "resolved": "https://registry.npmjs.org/lunr/-/lunr-2.3.9.tgz", + "integrity": "sha512-zTU3DaZaF3Rt9rhN3uBMGQD3dD2/vFQqnvZCDv4dl5iOzq2IZQqTxu90r4E5J+nP70J3ilqVCrbho2eWaeW8Ow==", + "dev": true + }, "node_modules/make-dir": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", @@ -3100,6 +3113,18 @@ "tmpl": "1.0.5" } }, + "node_modules/marked": { + "version": "4.2.5", + "resolved": "https://registry.npmjs.org/marked/-/marked-4.2.5.tgz", + "integrity": "sha512-jPueVhumq7idETHkb203WDD4fMA3yV9emQ5vLwop58lu8bTclMghBWcYAavlDqIEMaisADinV1TooIFCfqOsYQ==", + "dev": true, + "bin": { + "marked": "bin/marked.js" + }, + "engines": { + "node": ">= 12" + } + }, "node_modules/memory-pager": { "version": "1.5.0", "resolved": "https://registry.npmjs.org/memory-pager/-/memory-pager-1.5.0.tgz", @@ -3660,6 +3685,17 @@ "node": ">=8" } }, + "node_modules/shiki": { + "version": "0.12.1", + "resolved": "https://registry.npmjs.org/shiki/-/shiki-0.12.1.tgz", + "integrity": "sha512-aieaV1m349rZINEBkjxh2QbBvFFQOlgqYTNtCal82hHj4dDZ76oMlQIX+C7ryerBTDiga3e5NfH6smjdJ02BbQ==", + "dev": true, + "dependencies": { + "jsonc-parser": "^3.2.0", + "vscode-oniguruma": "^1.7.0", + "vscode-textmate": "^8.0.0" + } + }, "node_modules/sift": { "version": "16.0.0", "resolved": "https://registry.npmjs.org/sift/-/sift-16.0.0.tgz", @@ -4047,6 +4083,48 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/typedoc": { + "version": "0.23.24", + "resolved": "https://registry.npmjs.org/typedoc/-/typedoc-0.23.24.tgz", + "integrity": "sha512-bfmy8lNQh+WrPYcJbtjQ6JEEsVl/ce1ZIXyXhyW+a1vFrjO39t6J8sL/d6FfAGrJTc7McCXgk9AanYBSNvLdIA==", + "dev": true, + "dependencies": { + "lunr": "^2.3.9", + "marked": "^4.2.5", + "minimatch": "^5.1.2", + "shiki": "^0.12.1" + }, + "bin": { + "typedoc": "bin/typedoc" + }, + "engines": { + "node": ">= 14.14" + }, + "peerDependencies": { + "typescript": "4.6.x || 4.7.x || 4.8.x || 4.9.x" + } + }, + "node_modules/typedoc/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/typedoc/node_modules/minimatch": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.2.tgz", + "integrity": "sha512-bNH9mmM9qsJ2X4r2Nat1B//1dJVcn3+iBLa3IgqJ7EbGaDNepL9QSHOxN4ng33s52VMMhhIfgCYDk3C4ZmlDAg==", + "dev": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/typescript": { "version": "4.8.3", "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.8.3.tgz", @@ -4128,6 +4206,18 @@ "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==", "dev": true }, + "node_modules/vscode-oniguruma": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/vscode-oniguruma/-/vscode-oniguruma-1.7.0.tgz", + "integrity": "sha512-L9WMGRfrjOhgHSdOYgCt/yRMsXzLDJSL7BPrOZt73gU0iWO4mpqzqQzOz5srxqTvMBaR0XZTSrVWo4j55Rc6cA==", + "dev": true + }, + "node_modules/vscode-textmate": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/vscode-textmate/-/vscode-textmate-8.0.0.tgz", + "integrity": "sha512-AFbieoL7a5LMqcnOF04ji+rpXadgOXnZsxQr//r83kLPr7biP7am3g9zbaZIaBGwBRWeSvoMD4mgPdX3e4NWBg==", + "dev": true + }, "node_modules/walker": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/walker/-/walker-1.0.8.tgz", @@ -6571,6 +6661,12 @@ "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", "dev": true }, + "jsonc-parser": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.2.0.tgz", + "integrity": "sha512-gfFQZrcTc8CnKXp6Y4/CBT3fTc0OVuDofpre4aEeEpSBPV5X5v4+Vmx+8snU7RLPrNHPKSgLxGo9YuQzz20o+w==", + "dev": true + }, "kareem": { "version": "2.4.1", "resolved": "https://registry.npmjs.org/kareem/-/kareem-2.4.1.tgz", @@ -6623,6 +6719,12 @@ "yallist": "^3.0.2" } }, + "lunr": { + "version": "2.3.9", + "resolved": "https://registry.npmjs.org/lunr/-/lunr-2.3.9.tgz", + "integrity": "sha512-zTU3DaZaF3Rt9rhN3uBMGQD3dD2/vFQqnvZCDv4dl5iOzq2IZQqTxu90r4E5J+nP70J3ilqVCrbho2eWaeW8Ow==", + "dev": true + }, "make-dir": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", @@ -6646,6 +6748,12 @@ "tmpl": "1.0.5" } }, + "marked": { + "version": "4.2.5", + "resolved": "https://registry.npmjs.org/marked/-/marked-4.2.5.tgz", + "integrity": "sha512-jPueVhumq7idETHkb203WDD4fMA3yV9emQ5vLwop58lu8bTclMghBWcYAavlDqIEMaisADinV1TooIFCfqOsYQ==", + "dev": true + }, "memory-pager": { "version": "1.5.0", "resolved": "https://registry.npmjs.org/memory-pager/-/memory-pager-1.5.0.tgz", @@ -7054,6 +7162,17 @@ "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", "dev": true }, + "shiki": { + "version": "0.12.1", + "resolved": "https://registry.npmjs.org/shiki/-/shiki-0.12.1.tgz", + "integrity": "sha512-aieaV1m349rZINEBkjxh2QbBvFFQOlgqYTNtCal82hHj4dDZ76oMlQIX+C7ryerBTDiga3e5NfH6smjdJ02BbQ==", + "dev": true, + "requires": { + "jsonc-parser": "^3.2.0", + "vscode-oniguruma": "^1.7.0", + "vscode-textmate": "^8.0.0" + } + }, "sift": { "version": "16.0.0", "resolved": "https://registry.npmjs.org/sift/-/sift-16.0.0.tgz", @@ -7317,6 +7436,38 @@ "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", "dev": true }, + "typedoc": { + "version": "0.23.24", + "resolved": "https://registry.npmjs.org/typedoc/-/typedoc-0.23.24.tgz", + "integrity": "sha512-bfmy8lNQh+WrPYcJbtjQ6JEEsVl/ce1ZIXyXhyW+a1vFrjO39t6J8sL/d6FfAGrJTc7McCXgk9AanYBSNvLdIA==", + "dev": true, + "requires": { + "lunr": "^2.3.9", + "marked": "^4.2.5", + "minimatch": "^5.1.2", + "shiki": "^0.12.1" + }, + "dependencies": { + "brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "requires": { + "balanced-match": "^1.0.0" + } + }, + "minimatch": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.2.tgz", + "integrity": "sha512-bNH9mmM9qsJ2X4r2Nat1B//1dJVcn3+iBLa3IgqJ7EbGaDNepL9QSHOxN4ng33s52VMMhhIfgCYDk3C4ZmlDAg==", + "dev": true, + "requires": { + "brace-expansion": "^2.0.1" + } + } + } + }, "typescript": { "version": "4.8.3", "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.8.3.tgz", @@ -7374,6 +7525,18 @@ } } }, + "vscode-oniguruma": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/vscode-oniguruma/-/vscode-oniguruma-1.7.0.tgz", + "integrity": "sha512-L9WMGRfrjOhgHSdOYgCt/yRMsXzLDJSL7BPrOZt73gU0iWO4mpqzqQzOz5srxqTvMBaR0XZTSrVWo4j55Rc6cA==", + "dev": true + }, + "vscode-textmate": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/vscode-textmate/-/vscode-textmate-8.0.0.tgz", + "integrity": "sha512-AFbieoL7a5LMqcnOF04ji+rpXadgOXnZsxQr//r83kLPr7biP7am3g9zbaZIaBGwBRWeSvoMD4mgPdX3e4NWBg==", + "dev": true + }, "walker": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/walker/-/walker-1.0.8.tgz", diff --git a/package.json b/package.json index 34d7017..c89c90c 100644 --- a/package.json +++ b/package.json @@ -7,9 +7,10 @@ "description": "", "main": "index.ts", "scripts": { - "run": "ts-node index.ts", + "run": "ts-node ./src/index.ts", "format": "prettier --write .", - "test": "jest" + "test": "jest", + "docs": "typedoc" }, "repository": { "type": "git", @@ -35,6 +36,7 @@ "devDependencies": { "@jest/globals": "^29.3.1", "jest": "^29.3.1", - "ts-jest": "^29.0.3" + "ts-jest": "^29.0.3", + "typedoc": "^0.23.24" } -} \ No newline at end of file +} diff --git a/tsconfig.json b/tsconfig.json index 6fc73cc..4f22e4c 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -96,5 +96,17 @@ /* Completeness */ // "skipDefaultLibCheck": true, /* Skip type checking .d.ts files that are included with TypeScript. */ "skipLibCheck": true /* Skip type checking all .d.ts files. */ + }, + "typedocOptions":{ + // Only setting the entry point to src does not find the other files + // "entryPoints":["src"], + + // Setting the entry poits and the strategy to expand does finds all files, but does not format the page nicely + "entryPoints": ["src/**"], + "entryPointStrategy": "expand", + + "out": "docs", + "media": "demo", + "exclude":["__tests__", ".github", "docs", "node_modules"] } } \ No newline at end of file From 224ad99ceb23688774ddbfd08f743fe90f2119d1 Mon Sep 17 00:00:00 2001 From: John Schiltz Date: Mon, 9 Jan 2023 02:21:42 -0600 Subject: [PATCH 02/22] example --- utils/sleep.ts | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/utils/sleep.ts b/utils/sleep.ts index 6d492e6..65e75ce 100644 --- a/utils/sleep.ts +++ b/utils/sleep.ts @@ -1,3 +1,10 @@ +/** + * @description Creates a promise that resolves itself after the given number of milliseconds + * @author John Schiltz + * @export + * @param ms + * @return {*} + */ export function sleep(ms: number) { // Create new promise that resolves itself after a delay of return new Promise((resolve: (args: void) => void) => From c9e186e7878bd405f6075094253945058491bc9a Mon Sep 17 00:00:00 2001 From: Nate Goldsborough Date: Sat, 14 Jan 2023 22:24:11 -0600 Subject: [PATCH 03/22] start work on updateDB command --- src/commands/owner/updateDB.ts | 138 +++++++++++++++++++++++++++++++++ 1 file changed, 138 insertions(+) create mode 100644 src/commands/owner/updateDB.ts diff --git a/src/commands/owner/updateDB.ts b/src/commands/owner/updateDB.ts new file mode 100644 index 0000000..a64ec4c --- /dev/null +++ b/src/commands/owner/updateDB.ts @@ -0,0 +1,138 @@ +import { ICommand } from "wokcommands"; +import DiscordJS from "discord.js"; +import chalk from "chalk"; +import axios from "axios"; +import { classModel } from "../../models/classModel"; + +interface course{ + code: string; + title: string; + description: string; +} + +async function getCourses(srcdb: string, subject: string) { + const body = encodeURIComponent(`{"other":{"srcdb":"${srcdb}"},"criteria":[{"field":"subject","value":"${subject}"}]}`); + const config = { + method: "post", + url: `https://catalog.uwm.edu/course-search/api/?page=fose&route=search&subject=${subject}`, + headers: { + "Content-Type": "text/plain", + }, + data: body, + }; + const response = await axios(config); + return response.data.results; +} + +async function getDescription(course: any, srcdb: string){ + const code = course.code; + const crn = course.crn; + const body = encodeURIComponent(`{"group":"code:${code}","key":"crn:${crn}","srcdb":"${srcdb}","matched":"crn:${crn}"}`); + const config = { + method: "post", + url: `https://catalog.uwm.edu/course-search/api/?page=fose&route=details`, + headers: { + "Content-Type": "text/plain", + }, + data: body, + }; + const result = await axios(config); + return result.data.description; +} + +async function getCourseInfo(srcdb: string, subject: string){ + const courses = await getCourses(srcdb, subject); + let info: course[] = []; + courses.forEach(async (course: any) => { + const description = await getDescription(course, srcdb); + let entry: course = { + code: course.code, + title: course.title, + description: description, + }; + info.push(entry); + + + }); + return info; +} +export default { + name: "updateDB", + category: "owner", + description: "Update the database with new classes", + slash: true, + expectedArgs: "", + minArgs: 1, + permissions: ["MANAGE_MESSAGES", "MANAGE_GUILD"], + guildOnly: true, + ownerOnly: true, + options: [ + { + name: "semester", + description: "Which Semester to query", + type: DiscordJS.Constants.ApplicationCommandOptionTypes.STRING, + required: true, + choices: [ + { + name: "Spring 2023", + value: "2232", + }, + { + name: "Summer 2023", + value: "2228", + }, + ], + }, + { + name: "subject", + description: "Which Subject to query", + type: DiscordJS.Constants.ApplicationCommandOptionTypes.STRING, + required: true, + choices: [ + { + name: "Computer Science", + value: "COMPSCI", + }, + ], + }, + ], + + callback: async ({ interaction }) => { + const srcdb = interaction.options.getString("semester"); + const subject = interaction.options.getString("subject"); + let currentCourses = await classModel.find({}); + let info = await getCourseInfo(srcdb!, subject!) + + currentCourses.forEach(async (course: any) => { + course.ACTIVE = false; + await course.save(); + }); + + info.forEach(async (course: any) => { + let currentCourse = await classModel.findOne({ CODE: course.code }); + if (currentCourse) { + currentCourse.ACTIVE = true; + currentCourse.NAME = course.code; + currentCourse.TITLE = course.title; + currentCourse.INFO = course.description; + await currentCourse.save(); + } else { + let newCourse = new classModel({ + NAME: course.code, + TITLE: course.title, + INFO: course.description, + ACTIVE: true, + DUPE: false, + }); + await newCourse.save(); + } + }); + + + + + + // Log the command usage + console.log(chalk.blue(`${chalk.green(`[COMMAND]`)} ${chalk.yellow(interaction.user.tag)} used the ${chalk.green(`/updateDB`)} command in ${chalk.yellow(interaction.guild?.name)}`)); + }, +} as ICommand; From 0e4c2f591e468b5e21d8ce8fe24e9534c5dcddc5 Mon Sep 17 00:00:00 2001 From: Nate Goldsborough Date: Wed, 18 Jan 2023 15:06:55 -0600 Subject: [PATCH 04/22] progress --- src/commands/owner/updateDB.ts | 122 ++++++++++++++------------------- 1 file changed, 51 insertions(+), 71 deletions(-) diff --git a/src/commands/owner/updateDB.ts b/src/commands/owner/updateDB.ts index a64ec4c..0a45248 100644 --- a/src/commands/owner/updateDB.ts +++ b/src/commands/owner/updateDB.ts @@ -4,60 +4,58 @@ import chalk from "chalk"; import axios from "axios"; import { classModel } from "../../models/classModel"; -interface course{ +interface course { code: string; title: string; description: string; } -async function getCourses(srcdb: string, subject: string) { - const body = encodeURIComponent(`{"other":{"srcdb":"${srcdb}"},"criteria":[{"field":"subject","value":"${subject}"}]}`); +async function getCourses(srcdb: string) { + const body = encodeURIComponent(`{"other":{"srcdb":"${srcdb}"},"criteria":[{"field":"subject","value":"COMPSCI"}]}`); const config = { method: "post", - url: `https://catalog.uwm.edu/course-search/api/?page=fose&route=search&subject=${subject}`, + url: `https://catalog.uwm.edu/course-search/api/?page=fose&route=search&subject=COMPSCI`, headers: { "Content-Type": "text/plain", }, data: body, }; - const response = await axios(config); - return response.data.results; + const response = await axios(config); + return response.data.results; } -async function getDescription(course: any, srcdb: string){ - const code = course.code; - const crn = course.crn; - const body = encodeURIComponent(`{"group":"code:${code}","key":"crn:${crn}","srcdb":"${srcdb}","matched":"crn:${crn}"}`); - const config = { - method: "post", - url: `https://catalog.uwm.edu/course-search/api/?page=fose&route=details`, - headers: { - "Content-Type": "text/plain", - }, - data: body, - }; - const result = await axios(config); - return result.data.description; +async function getDescription(course: any, srcdb: string) { + const code = course.code; + const crn = course.crn; + const body = encodeURIComponent(`{"group":"code:${code}","key":"crn:${crn}","srcdb":"${srcdb}","matched":"crn:${crn}"}`); + const config = { + method: "get", + url: `https://catalog.uwm.edu/course-search/api/?page=fose&route=details`, + headers: { + "Content-Type": "text/plain", + }, + data: body, + }; + const result = await axios(config); + return result.data.description; } -async function getCourseInfo(srcdb: string, subject: string){ - const courses = await getCourses(srcdb, subject); - let info: course[] = []; - courses.forEach(async (course: any) => { - const description = await getDescription(course, srcdb); - let entry: course = { - code: course.code, - title: course.title, - description: description, - }; - info.push(entry); - - - }); - return info; +async function getCourseInfo(srcdb: string) { + const courses = await getCourses(srcdb); + let info: course[] = []; + for (const course of courses) { + const description = await getDescription(course, srcdb); + let entry: course = { + code: course.code, + title: course.title, + description: description.replace(/<\/?[^>]+(>|$)/g, "").replace(".", ". "), // cleans all html tags and adds spaces after periods + }; + info.push(entry); + } + return await info; } export default { - name: "updateDB", + name: "getCsClasses", category: "owner", description: "Update the database with new classes", slash: true, @@ -69,10 +67,10 @@ export default { options: [ { name: "semester", - description: "Which Semester to query", + description: "Which semester to query", type: DiscordJS.Constants.ApplicationCommandOptionTypes.STRING, required: true, - choices: [ + choices: [ //TODO: Make this dynamic by querying the catalog api for all available semesters when the bot starts and then updating this list when the bot is restarted { name: "Spring 2023", value: "2232", @@ -83,54 +81,36 @@ export default { }, ], }, - { - name: "subject", - description: "Which Subject to query", - type: DiscordJS.Constants.ApplicationCommandOptionTypes.STRING, - required: true, - choices: [ - { - name: "Computer Science", - value: "COMPSCI", - }, - ], - }, ], callback: async ({ interaction }) => { + interaction.deferReply(); // Defer the reply until all entries have been created or updated const srcdb = interaction.options.getString("semester"); - const subject = interaction.options.getString("subject"); let currentCourses = await classModel.find({}); - let info = await getCourseInfo(srcdb!, subject!) + let info = await getCourseInfo(srcdb!); - currentCourses.forEach(async (course: any) => { + // Set all current entries to inactive so when we update the db we can know the ones that are no longer active. they will get moved to the PAST CLASSES category + for (const course of currentCourses) { course.ACTIVE = false; await course.save(); - }); + } - info.forEach(async (course: any) => { - let currentCourse = await classModel.findOne({ CODE: course.code }); + // Loop through all the new classes info and update current entries or create new ones + for (const entry of info) { + const name = entry.code.toLowerCase().replace("compsci ", "cs").trim(); + let currentCourse = await classModel.findOne({ NAME: name }); if (currentCourse) { currentCourse.ACTIVE = true; - currentCourse.NAME = course.code; - currentCourse.TITLE = course.title; - currentCourse.INFO = course.description; + currentCourse.NAME = name; + currentCourse.TITLE = entry.title; + currentCourse.INFO = entry.description.replace(/<\/?[^>]+(>|$)/g, "").replace(".", ". "); // cleans all html tags and adds spaces after periods await currentCourse.save(); } else { - let newCourse = new classModel({ - NAME: course.code, - TITLE: course.title, - INFO: course.description, - ACTIVE: true, - DUPE: false, - }); - await newCourse.save(); + //TODO: Create new class db entry. Need to create channel and role and get name/id's to save to db } - }); - - - + } + //TODO: Reply to interaction when all classes are updated // Log the command usage console.log(chalk.blue(`${chalk.green(`[COMMAND]`)} ${chalk.yellow(interaction.user.tag)} used the ${chalk.green(`/updateDB`)} command in ${chalk.yellow(interaction.guild?.name)}`)); From fafac956ee9b47b95fba127e74e6a64b34650c0c Mon Sep 17 00:00:00 2001 From: nathen418 Date: Wed, 18 Jan 2023 21:19:42 +0000 Subject: [PATCH 05/22] Apply auto formatting changes --- src/commands/owner/updateDB.ts | 205 ++++++++++++++++++--------------- 1 file changed, 111 insertions(+), 94 deletions(-) diff --git a/src/commands/owner/updateDB.ts b/src/commands/owner/updateDB.ts index 0a45248..8c0959b 100644 --- a/src/commands/owner/updateDB.ts +++ b/src/commands/owner/updateDB.ts @@ -5,114 +5,131 @@ import axios from "axios"; import { classModel } from "../../models/classModel"; interface course { - code: string; - title: string; - description: string; + code: string; + title: string; + description: string; } async function getCourses(srcdb: string) { - const body = encodeURIComponent(`{"other":{"srcdb":"${srcdb}"},"criteria":[{"field":"subject","value":"COMPSCI"}]}`); - const config = { - method: "post", - url: `https://catalog.uwm.edu/course-search/api/?page=fose&route=search&subject=COMPSCI`, - headers: { - "Content-Type": "text/plain", - }, - data: body, - }; - const response = await axios(config); - return response.data.results; + const body = encodeURIComponent( + `{"other":{"srcdb":"${srcdb}"},"criteria":[{"field":"subject","value":"COMPSCI"}]}` + ); + const config = { + method: "post", + url: `https://catalog.uwm.edu/course-search/api/?page=fose&route=search&subject=COMPSCI`, + headers: { + "Content-Type": "text/plain", + }, + data: body, + }; + const response = await axios(config); + return response.data.results; } async function getDescription(course: any, srcdb: string) { - const code = course.code; - const crn = course.crn; - const body = encodeURIComponent(`{"group":"code:${code}","key":"crn:${crn}","srcdb":"${srcdb}","matched":"crn:${crn}"}`); - const config = { - method: "get", - url: `https://catalog.uwm.edu/course-search/api/?page=fose&route=details`, - headers: { - "Content-Type": "text/plain", - }, - data: body, - }; - const result = await axios(config); - return result.data.description; + const code = course.code; + const crn = course.crn; + const body = encodeURIComponent( + `{"group":"code:${code}","key":"crn:${crn}","srcdb":"${srcdb}","matched":"crn:${crn}"}` + ); + const config = { + method: "get", + url: `https://catalog.uwm.edu/course-search/api/?page=fose&route=details`, + headers: { + "Content-Type": "text/plain", + }, + data: body, + }; + const result = await axios(config); + return result.data.description; } async function getCourseInfo(srcdb: string) { - const courses = await getCourses(srcdb); - let info: course[] = []; - for (const course of courses) { - const description = await getDescription(course, srcdb); - let entry: course = { - code: course.code, - title: course.title, - description: description.replace(/<\/?[^>]+(>|$)/g, "").replace(".", ". "), // cleans all html tags and adds spaces after periods - }; - info.push(entry); - } - return await info; + const courses = await getCourses(srcdb); + let info: course[] = []; + for (const course of courses) { + const description = await getDescription(course, srcdb); + let entry: course = { + code: course.code, + title: course.title, + description: description + .replace(/<\/?[^>]+(>|$)/g, "") + .replace(".", ". "), // cleans all html tags and adds spaces after periods + }; + info.push(entry); + } + return await info; } export default { - name: "getCsClasses", - category: "owner", - description: "Update the database with new classes", - slash: true, - expectedArgs: "", - minArgs: 1, - permissions: ["MANAGE_MESSAGES", "MANAGE_GUILD"], - guildOnly: true, - ownerOnly: true, - options: [ - { - name: "semester", - description: "Which semester to query", - type: DiscordJS.Constants.ApplicationCommandOptionTypes.STRING, - required: true, - choices: [ //TODO: Make this dynamic by querying the catalog api for all available semesters when the bot starts and then updating this list when the bot is restarted - { - name: "Spring 2023", - value: "2232", - }, - { - name: "Summer 2023", - value: "2228", - }, - ], - }, - ], + name: "getCsClasses", + category: "owner", + description: "Update the database with new classes", + slash: true, + expectedArgs: "", + minArgs: 1, + permissions: ["MANAGE_MESSAGES", "MANAGE_GUILD"], + guildOnly: true, + ownerOnly: true, + options: [ + { + name: "semester", + description: "Which semester to query", + type: DiscordJS.Constants.ApplicationCommandOptionTypes.STRING, + required: true, + choices: [ + //TODO: Make this dynamic by querying the catalog api for all available semesters when the bot starts and then updating this list when the bot is restarted + { + name: "Spring 2023", + value: "2232", + }, + { + name: "Summer 2023", + value: "2228", + }, + ], + }, + ], - callback: async ({ interaction }) => { - interaction.deferReply(); // Defer the reply until all entries have been created or updated - const srcdb = interaction.options.getString("semester"); - let currentCourses = await classModel.find({}); - let info = await getCourseInfo(srcdb!); + callback: async ({ interaction }) => { + interaction.deferReply(); // Defer the reply until all entries have been created or updated + const srcdb = interaction.options.getString("semester"); + let currentCourses = await classModel.find({}); + let info = await getCourseInfo(srcdb!); - // Set all current entries to inactive so when we update the db we can know the ones that are no longer active. they will get moved to the PAST CLASSES category - for (const course of currentCourses) { - course.ACTIVE = false; - await course.save(); - } + // Set all current entries to inactive so when we update the db we can know the ones that are no longer active. they will get moved to the PAST CLASSES category + for (const course of currentCourses) { + course.ACTIVE = false; + await course.save(); + } - // Loop through all the new classes info and update current entries or create new ones - for (const entry of info) { - const name = entry.code.toLowerCase().replace("compsci ", "cs").trim(); - let currentCourse = await classModel.findOne({ NAME: name }); - if (currentCourse) { - currentCourse.ACTIVE = true; - currentCourse.NAME = name; - currentCourse.TITLE = entry.title; - currentCourse.INFO = entry.description.replace(/<\/?[^>]+(>|$)/g, "").replace(".", ". "); // cleans all html tags and adds spaces after periods - await currentCourse.save(); - } else { - //TODO: Create new class db entry. Need to create channel and role and get name/id's to save to db - } - } + // Loop through all the new classes info and update current entries or create new ones + for (const entry of info) { + const name = entry.code.toLowerCase().replace("compsci ", "cs").trim(); + let currentCourse = await classModel.findOne({ NAME: name }); + if (currentCourse) { + currentCourse.ACTIVE = true; + currentCourse.NAME = name; + currentCourse.TITLE = entry.title; + currentCourse.INFO = entry.description + .replace(/<\/?[^>]+(>|$)/g, "") + .replace(".", ". "); // cleans all html tags and adds spaces after periods + await currentCourse.save(); + } else { + //TODO: Create new class db entry. Need to create channel and role and get name/id's to save to db + } + } - //TODO: Reply to interaction when all classes are updated + //TODO: Reply to interaction when all classes are updated - // Log the command usage - console.log(chalk.blue(`${chalk.green(`[COMMAND]`)} ${chalk.yellow(interaction.user.tag)} used the ${chalk.green(`/updateDB`)} command in ${chalk.yellow(interaction.guild?.name)}`)); - }, + // Log the command usage + console.log( + chalk.blue( + `${chalk.green(`[COMMAND]`)} ${chalk.yellow( + interaction.user.tag + )} used the ${chalk.green(`/updateDB`)} command in ${chalk.yellow( + interaction.guild?.name + )}` + ) + ); + }, } as ICommand; From ef5b84ebd9761cf5d5b7893cbb67e1fff6b17408 Mon Sep 17 00:00:00 2001 From: Nate Goldsborough Date: Fri, 20 Jan 2023 08:15:44 -0600 Subject: [PATCH 06/22] documentation --- src/commands/owner/updateDB.ts | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/src/commands/owner/updateDB.ts b/src/commands/owner/updateDB.ts index 0a45248..ffe0cc2 100644 --- a/src/commands/owner/updateDB.ts +++ b/src/commands/owner/updateDB.ts @@ -4,12 +4,23 @@ import chalk from "chalk"; import axios from "axios"; import { classModel } from "../../models/classModel"; +/** + * @description + * @author Nathen Goldsborough + * @interface course + */ interface course { code: string; title: string; description: string; } +/** + * @description - Gets all courses from the course catalog api + * @author Nathen Goldsborough + * @param srcdb + * @return - the response data (results field) + */ async function getCourses(srcdb: string) { const body = encodeURIComponent(`{"other":{"srcdb":"${srcdb}"},"criteria":[{"field":"subject","value":"COMPSCI"}]}`); const config = { @@ -24,6 +35,13 @@ async function getCourses(srcdb: string) { return response.data.results; } +/** + * @description - Gets the course description from the course catalog api + * @author Nathen Goldsborough + * @param course - the course to get the description for + * @param srcdb - the semester id to get the description for + * @return - the course description returned from the api call + */ async function getDescription(course: any, srcdb: string) { const code = course.code; const crn = course.crn; @@ -40,6 +58,12 @@ async function getDescription(course: any, srcdb: string) { return result.data.description; } +/** + * @description - Gets all course data from the course catalog api + * @author Nathen Goldsborough + * @param srcdb - the semester id to get the courses for + * @return - Array of course objects + */ async function getCourseInfo(srcdb: string) { const courses = await getCourses(srcdb); let info: course[] = []; From 5a9cdcdadafcdb262c1ab4261e2880c2d5e2df26 Mon Sep 17 00:00:00 2001 From: Nate Goldsborough Date: Fri, 20 Jan 2023 08:50:35 -0600 Subject: [PATCH 07/22] fix command name --- src/commands/owner/updateDB.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/commands/owner/updateDB.ts b/src/commands/owner/updateDB.ts index 3aa2a39..38a1f2d 100644 --- a/src/commands/owner/updateDB.ts +++ b/src/commands/owner/updateDB.ts @@ -85,7 +85,7 @@ async function getCourseInfo(srcdb: string) { return await info; } export default { - name: "getCsClasses", + name: "updatedb", category: "owner", description: "Update the database with new classes", slash: true, From bac4b340059f5b286c8fef64be97c673757bcce8 Mon Sep 17 00:00:00 2001 From: Nate Goldsborough Date: Fri, 20 Jan 2023 13:53:10 -0600 Subject: [PATCH 08/22] progress, edit required class fields --- src/commands/owner/updateDB.ts | 10 +++++++++- src/models/classModel.ts | 36 +++++++++++++++++----------------- 2 files changed, 27 insertions(+), 19 deletions(-) diff --git a/src/commands/owner/updateDB.ts b/src/commands/owner/updateDB.ts index 38a1f2d..929815a 100644 --- a/src/commands/owner/updateDB.ts +++ b/src/commands/owner/updateDB.ts @@ -139,7 +139,15 @@ export default { .replace(".", ". "); // cleans all html tags and adds spaces after periods await currentCourse.save(); } else { - //TODO: Create new class db entry. Need to create channel and role and get name/id's to save to db + const newClass = new classModel({ + NAME: name, + TITLE: entry.title, + INFO: entry.description + .replace(/<\/?[^>]+(>|$)/g, "") + .replace(".", ". "), // cleans all html tags and adds spaces after periods + ACTIVE: true, + }); + await newClass.save(); // ROLE_NAME, ROLE_ID, CHANNEL_ID, DUPE will be added later by using createRoles, csCreateChannels, and migrateDB } } diff --git a/src/models/classModel.ts b/src/models/classModel.ts index 3163a5f..55c57aa 100644 --- a/src/models/classModel.ts +++ b/src/models/classModel.ts @@ -2,27 +2,27 @@ import mongoose, { Schema } from "mongoose"; import { IRole } from "../utils/roles"; export interface IClass extends IRole { - id: string; - NAME: string; - TITLE: string; - INFO: string; - DUPE: boolean; - ACTIVE: boolean; - ROLE_NAME: string; - ROLE_ID: string; - CHANNEL_ID: string; + id: string; + NAME: string; + TITLE: string; + INFO: string; + DUPE: boolean; + ACTIVE: boolean; + ROLE_NAME: string; + ROLE_ID: string; + CHANNEL_ID: string; } const ClassSchema = new Schema({ - id: { type: String }, - NAME: { type: String, required: true }, - TITLE: { type: String, required: true }, - INFO: { type: String, required: true }, - DUPE: { type: Boolean, required: true }, - ACTIVE: { type: Boolean, required: true }, - ROLE_NAME: { type: String, required: true }, - ROLE_ID: { type: String, required: true }, - CHANNEL_ID: { type: String }, + id: { type: String }, + NAME: { type: String, required: true }, + TITLE: { type: String, required: true }, + INFO: { type: String, required: true }, + DUPE: { type: Boolean, required: false }, + ACTIVE: { type: Boolean, required: true }, + ROLE_NAME: { type: String, required: false }, + ROLE_ID: { type: String, required: false }, + CHANNEL_ID: { type: String, required: false }, }); const name = "class"; From b80eff6edfae3a211db29fc0de62b43c821067ec Mon Sep 17 00:00:00 2001 From: Nate Goldsborough Date: Fri, 20 Jan 2023 13:55:02 -0600 Subject: [PATCH 09/22] Remove typedoc file parts --- .gitignore | 1 - package.json | 8 ++------ tsconfig.json | 12 ------------ 3 files changed, 2 insertions(+), 19 deletions(-) diff --git a/.gitignore b/.gitignore index 6d31101..f740b4e 100644 --- a/.gitignore +++ b/.gitignore @@ -8,4 +8,3 @@ d.py/ # Ignore specific dev commands not allowed in production commands/owner/deleteChannels.ts commands/owner/deleteRoles.ts -docs/ diff --git a/package.json b/package.json index 52cfffa..3a3b539 100644 --- a/package.json +++ b/package.json @@ -8,9 +8,7 @@ "main": "index.ts", "scripts": { "run": "ts-node src/index.ts", - "format": "prettier --write .", - "test": "jest", - "docs": "typedoc" + "format": "prettier --write ." }, "repository": { "type": "git", @@ -36,8 +34,6 @@ }, "devDependencies": { "@jest/globals": "^29.3.1", - "jest": "^29.3.1", - "ts-jest": "^29.0.3", - "typedoc": "^0.23.24" + "jest": "^29.3.1" } } diff --git a/tsconfig.json b/tsconfig.json index 4f22e4c..6fc73cc 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -96,17 +96,5 @@ /* Completeness */ // "skipDefaultLibCheck": true, /* Skip type checking .d.ts files that are included with TypeScript. */ "skipLibCheck": true /* Skip type checking all .d.ts files. */ - }, - "typedocOptions":{ - // Only setting the entry point to src does not find the other files - // "entryPoints":["src"], - - // Setting the entry poits and the strategy to expand does finds all files, but does not format the page nicely - "entryPoints": ["src/**"], - "entryPointStrategy": "expand", - - "out": "docs", - "media": "demo", - "exclude":["__tests__", ".github", "docs", "node_modules"] } } \ No newline at end of file From 24adda7035daa518ce2a19f536a3c7e6138a3fd8 Mon Sep 17 00:00:00 2001 From: nathen418 Date: Fri, 20 Jan 2023 19:55:39 +0000 Subject: [PATCH 10/22] Apply auto formatting changes --- src/models/classModel.ts | 36 ++++++++++++++++++------------------ 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/src/models/classModel.ts b/src/models/classModel.ts index 55c57aa..30d6afb 100644 --- a/src/models/classModel.ts +++ b/src/models/classModel.ts @@ -2,27 +2,27 @@ import mongoose, { Schema } from "mongoose"; import { IRole } from "../utils/roles"; export interface IClass extends IRole { - id: string; - NAME: string; - TITLE: string; - INFO: string; - DUPE: boolean; - ACTIVE: boolean; - ROLE_NAME: string; - ROLE_ID: string; - CHANNEL_ID: string; + id: string; + NAME: string; + TITLE: string; + INFO: string; + DUPE: boolean; + ACTIVE: boolean; + ROLE_NAME: string; + ROLE_ID: string; + CHANNEL_ID: string; } const ClassSchema = new Schema({ - id: { type: String }, - NAME: { type: String, required: true }, - TITLE: { type: String, required: true }, - INFO: { type: String, required: true }, - DUPE: { type: Boolean, required: false }, - ACTIVE: { type: Boolean, required: true }, - ROLE_NAME: { type: String, required: false }, - ROLE_ID: { type: String, required: false }, - CHANNEL_ID: { type: String, required: false }, + id: { type: String }, + NAME: { type: String, required: true }, + TITLE: { type: String, required: true }, + INFO: { type: String, required: true }, + DUPE: { type: Boolean, required: false }, + ACTIVE: { type: Boolean, required: true }, + ROLE_NAME: { type: String, required: false }, + ROLE_ID: { type: String, required: false }, + CHANNEL_ID: { type: String, required: false }, }); const name = "class"; From 0dad2afab16d3e792dc1a20d310800c0b8577f80 Mon Sep 17 00:00:00 2001 From: John Schiltz Date: Fri, 20 Jan 2023 16:17:19 -0600 Subject: [PATCH 11/22] update db --- src/commands/owner/updateDB.ts | 76 ++++++++++++++++++++-------------- src/utils/course_cleaning.ts | 12 ++++++ 2 files changed, 58 insertions(+), 30 deletions(-) create mode 100644 src/utils/course_cleaning.ts diff --git a/src/commands/owner/updateDB.ts b/src/commands/owner/updateDB.ts index 929815a..8d3f16c 100644 --- a/src/commands/owner/updateDB.ts +++ b/src/commands/owner/updateDB.ts @@ -2,7 +2,9 @@ import { ICommand } from "wokcommands"; import DiscordJS from "discord.js"; import chalk from "chalk"; import axios from "axios"; -import { classModel } from "../../models/classModel"; +import { classModel, IClass } from "../../models/classModel"; +import { Types, Document } from "mongoose"; +import { cleanCourseCode, removeHTML } from "../../utils/course_cleaning"; /** * @description @@ -68,21 +70,19 @@ async function getDescription(course: any, srcdb: string) { * @param srcdb - the semester id to get the courses for * @return - Array of course objects */ -async function getCourseInfo(srcdb: string) { +async function getCoursesWithDescription(srcdb: string) { const courses = await getCourses(srcdb); let info: course[] = []; for (const course of courses) { const description = await getDescription(course, srcdb); let entry: course = { - code: course.code, + code: cleanCourseCode(course.code), title: course.title, - description: description - .replace(/<\/?[^>]+(>|$)/g, "") - .replace(".", ". "), // cleans all html tags and adds spaces after periods + description: removeHTML(description), }; info.push(entry); } - return await info; + return info; } export default { name: "updatedb", @@ -117,40 +117,56 @@ export default { callback: async ({ interaction }) => { interaction.deferReply(); // Defer the reply until all entries have been created or updated const srcdb = interaction.options.getString("semester"); - let currentCourses = await classModel.find({}); - let info = await getCourseInfo(srcdb!); + let current_courses = await classModel.find({}); + let new_courses = await getCoursesWithDescription(srcdb!); // Set all current entries to inactive so when we update the db we can know the ones that are no longer active. they will get moved to the PAST CLASSES category - for (const course of currentCourses) { - course.ACTIVE = false; - await course.save(); + for (const current_course of current_courses) { + current_course.ACTIVE = false; + await current_course.save(); } + let courses_to_save: (Document & + IClass & { _id: Types.ObjectId })[] = []; // Loop through all the new classes info and update current entries or create new ones - for (const entry of info) { - const name = entry.code.toLowerCase().replace("compsci ", "cs").trim(); - let currentCourse = await classModel.findOne({ NAME: name }); - if (currentCourse) { - currentCourse.ACTIVE = true; - currentCourse.NAME = name; - currentCourse.TITLE = entry.title; - currentCourse.INFO = entry.description - .replace(/<\/?[^>]+(>|$)/g, "") - .replace(".", ". "); // cleans all html tags and adds spaces after periods - await currentCourse.save(); - } else { + for (const new_course of new_courses) { + let found_courses = await classModel.find({ NAME: new_course.code }); + if (found_courses === undefined || found_courses.length === 0) { const newClass = new classModel({ - NAME: name, - TITLE: entry.title, - INFO: entry.description - .replace(/<\/?[^>]+(>|$)/g, "") - .replace(".", ". "), // cleans all html tags and adds spaces after periods + NAME: new_course.code, + TITLE: new_course.title, + INFO: new_course.description, ACTIVE: true, + DUPE: false, }); - await newClass.save(); // ROLE_NAME, ROLE_ID, CHANNEL_ID, DUPE will be added later by using createRoles, csCreateChannels, and migrateDB + courses_to_save.push(newClass); + } else { + let found_dupe = false; + const multiple_results = found_courses.length > 1 ? true : false; + for (const found_course of found_courses) { + found_course.DUPE = multiple_results; + if (found_course.TITLE === new_course.title) { + found_course.ACTIVE = true; + found_course.NAME = new_course.code; + found_course.INFO = new_course.description; + found_dupe = true; + } + courses_to_save.push(found_course); + } + if (found_dupe === false) { + const newClass = new classModel({ + NAME: new_course.code, + TITLE: new_course.title, + INFO: new_course.description, + ACTIVE: true, + DUPE: multiple_results, + }); + courses_to_save.push(newClass); + } } } + await classModel.bulkSave(courses_to_save); //TODO: Reply to interaction when all classes are updated // Log the command usage diff --git a/src/utils/course_cleaning.ts b/src/utils/course_cleaning.ts new file mode 100644 index 0000000..0e36328 --- /dev/null +++ b/src/utils/course_cleaning.ts @@ -0,0 +1,12 @@ +const shorteningMap = new Map([["computer science", "cs"]]); +export function cleanCourseCode(s: string) { + let ret_str = s.toLowerCase(); + shorteningMap.forEach((value, key) => { + ret_str = ret_str.replace(key, value); + }); + return ret_str.trim(); +} + +export function removeHTML(s: string): string { + return s.replace(/<\/?[^>]+(>|$)/g, "").replace(".", ". "); // cleans all html tags and adds spaces after periods +} From 989f3e4dff531d901701c856562e8de190f1fce1 Mon Sep 17 00:00:00 2001 From: John Schiltz Date: Fri, 20 Jan 2023 16:20:50 -0600 Subject: [PATCH 12/22] clean up course_cleaning --- src/utils/course_cleaning.ts | 27 +++++++++++++++++++++------ 1 file changed, 21 insertions(+), 6 deletions(-) diff --git a/src/utils/course_cleaning.ts b/src/utils/course_cleaning.ts index 0e36328..1b24018 100644 --- a/src/utils/course_cleaning.ts +++ b/src/utils/course_cleaning.ts @@ -1,12 +1,27 @@ -const shorteningMap = new Map([["computer science", "cs"]]); -export function cleanCourseCode(s: string) { - let ret_str = s.toLowerCase(); +const shorteningMap = new Map([["compsci", "cs"]]); + +/** + * @description - Cleans a course code by removing spaces and replacing matches in the shorteningMap + * @author John Schiltz + * @export + * @param course_code - the course code to clean + * @return {*} - the cleaned course code + */ +export function cleanCourseCode(course_code: string) { + let clean_course_code = course_code.toLowerCase(); shorteningMap.forEach((value, key) => { - ret_str = ret_str.replace(key, value); + clean_course_code = clean_course_code.replace(key, value); }); - return ret_str.trim(); + return clean_course_code.trim(); } +/** + * @description - Removes all html tags from a string and adds spaces after periods + * @author Nathen Goldsborough + * @export + * @param s + * @return {*} + */ export function removeHTML(s: string): string { - return s.replace(/<\/?[^>]+(>|$)/g, "").replace(".", ". "); // cleans all html tags and adds spaces after periods + return s.replace(/<\/?[^>]+(>|$)/g, "").replace(".", ". "); } From 049302f268660f534f964a63f63b7847aa58b43c Mon Sep 17 00:00:00 2001 From: John Schiltz Date: Fri, 20 Jan 2023 16:22:27 -0600 Subject: [PATCH 13/22] add return type --- src/utils/course_cleaning.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/utils/course_cleaning.ts b/src/utils/course_cleaning.ts index 1b24018..4870546 100644 --- a/src/utils/course_cleaning.ts +++ b/src/utils/course_cleaning.ts @@ -7,7 +7,7 @@ const shorteningMap = new Map([["compsci", "cs"]]); * @param course_code - the course code to clean * @return {*} - the cleaned course code */ -export function cleanCourseCode(course_code: string) { +export function cleanCourseCode(course_code: string): string { let clean_course_code = course_code.toLowerCase(); shorteningMap.forEach((value, key) => { clean_course_code = clean_course_code.replace(key, value); From d1167e89b06f7d60459a62b311a539f39438a026 Mon Sep 17 00:00:00 2001 From: John Schiltz Date: Fri, 20 Jan 2023 16:23:02 -0600 Subject: [PATCH 14/22] add comment --- src/commands/owner/updateDB.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/commands/owner/updateDB.ts b/src/commands/owner/updateDB.ts index 8d3f16c..88c4eb1 100644 --- a/src/commands/owner/updateDB.ts +++ b/src/commands/owner/updateDB.ts @@ -142,7 +142,7 @@ export default { courses_to_save.push(newClass); } else { let found_dupe = false; - const multiple_results = found_courses.length > 1 ? true : false; + const multiple_results = found_courses.length > 1 ? true : false; // If there are multiple results for a class then it is a duplicate for (const found_course of found_courses) { found_course.DUPE = multiple_results; if (found_course.TITLE === new_course.title) { From 4a65cc42021a58d38ed2f3eddc3c5c9e064abb61 Mon Sep 17 00:00:00 2001 From: John Schiltz Date: Fri, 20 Jan 2023 16:26:52 -0600 Subject: [PATCH 15/22] reduce number of database edits --- src/commands/owner/updateDB.ts | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/commands/owner/updateDB.ts b/src/commands/owner/updateDB.ts index 88c4eb1..4bdb613 100644 --- a/src/commands/owner/updateDB.ts +++ b/src/commands/owner/updateDB.ts @@ -121,10 +121,11 @@ export default { let new_courses = await getCoursesWithDescription(srcdb!); // Set all current entries to inactive so when we update the db we can know the ones that are no longer active. they will get moved to the PAST CLASSES category - for (const current_course of current_courses) { - current_course.ACTIVE = false; - await current_course.save(); - } + current_courses.forEach((course) => { + course.ACTIVE = false; + }); + await classModel.bulkSave(current_courses); + let courses_to_save: (Document & IClass & { _id: Types.ObjectId })[] = []; From 3a4313d557a6d1d50932cf26505afd13fe7d11f5 Mon Sep 17 00:00:00 2001 From: John Schiltz Date: Fri, 20 Jan 2023 16:28:24 -0600 Subject: [PATCH 16/22] reduce database saves again --- src/commands/owner/updateDB.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/commands/owner/updateDB.ts b/src/commands/owner/updateDB.ts index 4bdb613..47f7823 100644 --- a/src/commands/owner/updateDB.ts +++ b/src/commands/owner/updateDB.ts @@ -124,7 +124,6 @@ export default { current_courses.forEach((course) => { course.ACTIVE = false; }); - await classModel.bulkSave(current_courses); let courses_to_save: (Document & IClass & { _id: Types.ObjectId })[] = []; @@ -152,7 +151,6 @@ export default { found_course.INFO = new_course.description; found_dupe = true; } - courses_to_save.push(found_course); } if (found_dupe === false) { const newClass = new classModel({ @@ -168,6 +166,7 @@ export default { } await classModel.bulkSave(courses_to_save); + await classModel.bulkSave(current_courses); //TODO: Reply to interaction when all classes are updated // Log the command usage From 60e0ecdbf5be899aaa9fa5e0106d949e1d55871a Mon Sep 17 00:00:00 2001 From: nathen418 Date: Fri, 20 Jan 2023 22:34:38 +0000 Subject: [PATCH 17/22] Apply auto formatting changes --- src/utils/course_cleaning.ts | 54 ++++++++++++++++++------------------ 1 file changed, 27 insertions(+), 27 deletions(-) diff --git a/src/utils/course_cleaning.ts b/src/utils/course_cleaning.ts index 4870546..d56115b 100644 --- a/src/utils/course_cleaning.ts +++ b/src/utils/course_cleaning.ts @@ -1,27 +1,27 @@ -const shorteningMap = new Map([["compsci", "cs"]]); - -/** - * @description - Cleans a course code by removing spaces and replacing matches in the shorteningMap - * @author John Schiltz - * @export - * @param course_code - the course code to clean - * @return {*} - the cleaned course code - */ -export function cleanCourseCode(course_code: string): string { - let clean_course_code = course_code.toLowerCase(); - shorteningMap.forEach((value, key) => { - clean_course_code = clean_course_code.replace(key, value); - }); - return clean_course_code.trim(); -} - -/** - * @description - Removes all html tags from a string and adds spaces after periods - * @author Nathen Goldsborough - * @export - * @param s - * @return {*} - */ -export function removeHTML(s: string): string { - return s.replace(/<\/?[^>]+(>|$)/g, "").replace(".", ". "); -} +const shorteningMap = new Map([["compsci", "cs"]]); + +/** + * @description - Cleans a course code by removing spaces and replacing matches in the shorteningMap + * @author John Schiltz + * @export + * @param course_code - the course code to clean + * @return {*} - the cleaned course code + */ +export function cleanCourseCode(course_code: string): string { + let clean_course_code = course_code.toLowerCase(); + shorteningMap.forEach((value, key) => { + clean_course_code = clean_course_code.replace(key, value); + }); + return clean_course_code.trim(); +} + +/** + * @description - Removes all html tags from a string and adds spaces after periods + * @author Nathen Goldsborough + * @export + * @param s + * @return {*} + */ +export function removeHTML(s: string): string { + return s.replace(/<\/?[^>]+(>|$)/g, "").replace(".", ". "); +} From 6553f6d5aa11099e87c89c3b82de4dade03f7765 Mon Sep 17 00:00:00 2001 From: Nate Goldsborough Date: Fri, 20 Jan 2023 17:21:12 -0600 Subject: [PATCH 18/22] ds and add an interaction reply --- src/commands/owner/updateDB.ts | 29 +++++++++++++++++------------ 1 file changed, 17 insertions(+), 12 deletions(-) diff --git a/src/commands/owner/updateDB.ts b/src/commands/owner/updateDB.ts index 47f7823..bef152f 100644 --- a/src/commands/owner/updateDB.ts +++ b/src/commands/owner/updateDB.ts @@ -46,7 +46,7 @@ async function getCourses(srcdb: string) { * @param srcdb - the semester id to get the description for * @return - the course description returned from the api call */ -async function getDescription(course: any, srcdb: string) { +async function getDescription(course: any, srcdb: string) { // skipcq: JS-0323 const code = course.code; const crn = course.crn; const body = encodeURIComponent( @@ -70,12 +70,12 @@ async function getDescription(course: any, srcdb: string) { * @param srcdb - the semester id to get the courses for * @return - Array of course objects */ -async function getCoursesWithDescription(srcdb: string) { +async function getCoursesWithDescription(srcdb: string) { const courses = await getCourses(srcdb); - let info: course[] = []; + const info: course[] = []; for (const course of courses) { const description = await getDescription(course, srcdb); - let entry: course = { + const entry: course = { code: cleanCourseCode(course.code), title: course.title, description: removeHTML(description), @@ -91,7 +91,7 @@ export default { slash: true, expectedArgs: "", minArgs: 1, - permissions: ["MANAGE_MESSAGES", "MANAGE_GUILD"], + permissions: ["MANAGE_GUILD", "MANAGE_CHANNELS","MANAGE_ROLES"], guildOnly: true, ownerOnly: true, options: [ @@ -117,20 +117,20 @@ export default { callback: async ({ interaction }) => { interaction.deferReply(); // Defer the reply until all entries have been created or updated const srcdb = interaction.options.getString("semester"); - let current_courses = await classModel.find({}); - let new_courses = await getCoursesWithDescription(srcdb!); + const current_courses = await classModel.find({}); + const new_courses = await getCoursesWithDescription(srcdb!); // skipcq: JS-0339 // Set all current entries to inactive so when we update the db we can know the ones that are no longer active. they will get moved to the PAST CLASSES category current_courses.forEach((course) => { course.ACTIVE = false; }); - let courses_to_save: (Document & - IClass & { _id: Types.ObjectId })[] = []; + const courses_to_save: (Document & // skipcq: JS-0323 + IClass & { _id: Types.ObjectId })[] = []; // Loop through all the new classes info and update current entries or create new ones for (const new_course of new_courses) { - let found_courses = await classModel.find({ NAME: new_course.code }); + const found_courses = await classModel.find({ NAME: new_course.code }); if (found_courses === undefined || found_courses.length === 0) { const newClass = new classModel({ NAME: new_course.code, @@ -165,8 +165,13 @@ export default { } } - await classModel.bulkSave(courses_to_save); - await classModel.bulkSave(current_courses); + await classModel.bulkSave(courses_to_save).then(async () => { + await classModel.bulkSave(current_courses).then(() => { + interaction.editReply({ content: "Database updated!" }); // Can't be ephemeral because the interaction is deferred and deferred edits can't be ephemeral + }); + }); + + //TODO: Reply to interaction when all classes are updated // Log the command usage From 70fb5bdc60a56fc6702b0878263ca51942ebffcf Mon Sep 17 00:00:00 2001 From: nathen418 Date: Fri, 20 Jan 2023 23:21:51 +0000 Subject: [PATCH 19/22] Apply auto formatting changes --- src/commands/owner/updateDB.ts | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/commands/owner/updateDB.ts b/src/commands/owner/updateDB.ts index bef152f..43086ae 100644 --- a/src/commands/owner/updateDB.ts +++ b/src/commands/owner/updateDB.ts @@ -46,7 +46,8 @@ async function getCourses(srcdb: string) { * @param srcdb - the semester id to get the description for * @return - the course description returned from the api call */ -async function getDescription(course: any, srcdb: string) { // skipcq: JS-0323 +async function getDescription(course: any, srcdb: string) { + // skipcq: JS-0323 const code = course.code; const crn = course.crn; const body = encodeURIComponent( @@ -70,7 +71,7 @@ async function getDescription(course: any, srcdb: string) { // skipcq: JS-0323 * @param srcdb - the semester id to get the courses for * @return - Array of course objects */ -async function getCoursesWithDescription(srcdb: string) { +async function getCoursesWithDescription(srcdb: string) { const courses = await getCourses(srcdb); const info: course[] = []; for (const course of courses) { @@ -91,7 +92,7 @@ export default { slash: true, expectedArgs: "", minArgs: 1, - permissions: ["MANAGE_GUILD", "MANAGE_CHANNELS","MANAGE_ROLES"], + permissions: ["MANAGE_GUILD", "MANAGE_CHANNELS", "MANAGE_ROLES"], guildOnly: true, ownerOnly: true, options: [ @@ -126,7 +127,7 @@ export default { }); const courses_to_save: (Document & // skipcq: JS-0323 - IClass & { _id: Types.ObjectId })[] = []; + IClass & { _id: Types.ObjectId })[] = []; // Loop through all the new classes info and update current entries or create new ones for (const new_course of new_courses) { @@ -170,8 +171,7 @@ export default { interaction.editReply({ content: "Database updated!" }); // Can't be ephemeral because the interaction is deferred and deferred edits can't be ephemeral }); }); - - + //TODO: Reply to interaction when all classes are updated // Log the command usage From 134c975f575cae9b1dee74fbe64877ce3da186f1 Mon Sep 17 00:00:00 2001 From: Nate Goldsborough Date: Fri, 20 Jan 2023 17:56:24 -0600 Subject: [PATCH 20/22] move getCourseName to course_cleaning --- __tests__/utils/channels.tests.ts | 2 +- src/commands/owner/csClassPoll.ts | 2 +- src/commands/owner/csCreateChannels.ts | 3 ++- src/utils/channels.ts | 11 +---------- src/utils/course_cleaning.ts | 24 ++++++++++++++++++++++++ 5 files changed, 29 insertions(+), 13 deletions(-) diff --git a/__tests__/utils/channels.tests.ts b/__tests__/utils/channels.tests.ts index 5cf18d1..2e0f43c 100644 --- a/__tests__/utils/channels.tests.ts +++ b/__tests__/utils/channels.tests.ts @@ -2,12 +2,12 @@ import { cleanChannelString, concatCategoryName, moveChannel, - getCourseName, getTopic, } from "../../src/utils/channels"; import { expect, describe, it, beforeEach } from "@jest/globals"; import { Guild, GuildChannel } from "discord.js"; import { IClass } from "../../src/models/classModel"; +import { getCourseName } from "../../src/utils/course_cleaning"; //cleanChannelString describe("cleanChannelString", () => { diff --git a/src/commands/owner/csClassPoll.ts b/src/commands/owner/csClassPoll.ts index ff3acb0..c193b95 100644 --- a/src/commands/owner/csClassPoll.ts +++ b/src/commands/owner/csClassPoll.ts @@ -9,7 +9,7 @@ import { ICommand } from "wokcommands"; import { classModel, IClass } from "../../models/classModel"; import { checkForRoles, cleanRoleString } from "../../utils/roles"; import { sleep } from "../../utils/sleep"; -import { getCourseName } from "../../utils/channels"; +import { getCourseName } from "../../utils/course_cleaning"; // Splits any size list into lists of at most `max_list_len` /** diff --git a/src/commands/owner/csCreateChannels.ts b/src/commands/owner/csCreateChannels.ts index de9f7e3..40d2813 100644 --- a/src/commands/owner/csCreateChannels.ts +++ b/src/commands/owner/csCreateChannels.ts @@ -8,10 +8,11 @@ import { getCategory, moveChannel, concatCategoryName, + getTopic } from "../../utils/channels"; import { create_default_embed } from "../../utils/embeds"; import { cleanRoleString } from "../../utils/roles"; -import { getCourseName, getTopic } from "../../utils/channels"; +import { getCourseName } from "../../utils/course_cleaning"; export default { name: "csCreateChannels", diff --git a/src/utils/channels.ts b/src/utils/channels.ts index 9bd0cf5..8440c9a 100644 --- a/src/utils/channels.ts +++ b/src/utils/channels.ts @@ -28,16 +28,7 @@ export function cleanChannelString(name: string): string { export function getTopic(course: IClass): string { return `${course.TITLE} | ${course.INFO}`.slice(0, 1024); } -/** - * @description - Gets the course name, if it is a duplicate, it adds the title to the end of the name - * @author John Schiltz - * @export - * @param course - * @return - The course name - */ -export function getCourseName(course: IClass) { - return course.DUPE === true ? `${course.NAME}-${course.TITLE}` : course.NAME; -} + /** * @description - Checks if a channel exists in the guild first by id, then by name diff --git a/src/utils/course_cleaning.ts b/src/utils/course_cleaning.ts index d56115b..5ac62dd 100644 --- a/src/utils/course_cleaning.ts +++ b/src/utils/course_cleaning.ts @@ -1,3 +1,4 @@ +import { IClass } from "../models/classModel"; const shorteningMap = new Map([["compsci", "cs"]]); /** @@ -25,3 +26,26 @@ export function cleanCourseCode(course_code: string): string { export function removeHTML(s: string): string { return s.replace(/<\/?[^>]+(>|$)/g, "").replace(".", ". "); } + +/** + * @description - Gets the course name, if it is a duplicate, it adds the title to the end of the name + * @author John Schiltz + * @export + * @param course + * @return - The course name + */ +export function getCourseName(course: IClass) { + return course.DUPE === true ? `${course.NAME}-${course.TITLE}` : course.NAME; +} + +/** + * @description - Cleans the course title string for computer science courses by removing the "advanced topics in computer science:" part of the title + * @author John Schiltz + * @export + * @param course + * @return - The cleaned string + */ +export function cleanCompSciTitle(s: string): string { + return s.replace(/(?:advanced )?topics in computer science:/gim, "").trim(); +} + From abf3fb96725d395b5a0442121cd0f4a5eb555ebd Mon Sep 17 00:00:00 2001 From: Nate Goldsborough Date: Fri, 20 Jan 2023 17:57:30 -0600 Subject: [PATCH 21/22] remove cleanCompSciString, move CleanCompSciTitle --- src/commands/owner/migrateDB.ts | 10 ++-------- src/commands/owner/updateDB.ts | 4 +++- 2 files changed, 5 insertions(+), 9 deletions(-) diff --git a/src/commands/owner/migrateDB.ts b/src/commands/owner/migrateDB.ts index d4a9884..fc0217a 100644 --- a/src/commands/owner/migrateDB.ts +++ b/src/commands/owner/migrateDB.ts @@ -4,19 +4,13 @@ import { classModel, IClass } from "../../models/classModel"; import { create_default_embed } from "../../utils/embeds"; import { Schema, Types, Document } from "mongoose"; import { - getCourseName, cleanChannelString, getTopic, } from "../../utils/channels"; import { cleanRoleString } from "../../utils/roles"; +import { getCourseName, cleanCompSciTitle, cleanCourseCode } from "../../utils/course_cleaning"; import Bottleneck from "bottleneck"; -function cleanCompSciString(s: string): string { - return s.toLowerCase().replace("compsci ", "cs").trim(); -} -function cleanCompSciTitle(s: string): string { - return s.replace(/(?:advanced )?topics in computer science:/gim, "").trim(); -} function isDupe(name: string): number { return name.search(/\(\d\)/); } @@ -76,7 +70,7 @@ export default { // Remove the number new_name = new_name.slice(0, is_dupe); } - new_name = cleanCompSciString(new_name); + new_name = cleanCourseCode(new_name); course.set("NAME", new_name); // Remove UUID console.log(chalk.yellow(`${code}\t${new_name}`)); diff --git a/src/commands/owner/updateDB.ts b/src/commands/owner/updateDB.ts index bef152f..145ccdb 100644 --- a/src/commands/owner/updateDB.ts +++ b/src/commands/owner/updateDB.ts @@ -4,7 +4,8 @@ import chalk from "chalk"; import axios from "axios"; import { classModel, IClass } from "../../models/classModel"; import { Types, Document } from "mongoose"; -import { cleanCourseCode, removeHTML } from "../../utils/course_cleaning"; +import { cleanCourseCode, getCourseName, removeHTML } from "../../utils/course_cleaning"; +import { cleanRoleString } from "../../utils/roles"; /** * @description @@ -139,6 +140,7 @@ export default { ACTIVE: true, DUPE: false, }); + newClass.ROLE_NAME = cleanRoleString(getCourseName(newClass)) courses_to_save.push(newClass); } else { let found_dupe = false; From 5f3c242cd934490a837f746f3b5354fdd4d21a05 Mon Sep 17 00:00:00 2001 From: nathen418 Date: Fri, 20 Jan 2023 23:58:16 +0000 Subject: [PATCH 22/22] Apply auto formatting changes --- src/commands/owner/csCreateChannels.ts | 2 +- src/commands/owner/migrateDB.ts | 11 ++++++----- src/commands/owner/updateDB.ts | 8 ++++++-- src/utils/channels.ts | 1 - src/utils/course_cleaning.ts | 1 - 5 files changed, 13 insertions(+), 10 deletions(-) diff --git a/src/commands/owner/csCreateChannels.ts b/src/commands/owner/csCreateChannels.ts index 40d2813..f00ddec 100644 --- a/src/commands/owner/csCreateChannels.ts +++ b/src/commands/owner/csCreateChannels.ts @@ -8,7 +8,7 @@ import { getCategory, moveChannel, concatCategoryName, - getTopic + getTopic, } from "../../utils/channels"; import { create_default_embed } from "../../utils/embeds"; import { cleanRoleString } from "../../utils/roles"; diff --git a/src/commands/owner/migrateDB.ts b/src/commands/owner/migrateDB.ts index fc0217a..68f6e2b 100644 --- a/src/commands/owner/migrateDB.ts +++ b/src/commands/owner/migrateDB.ts @@ -3,12 +3,13 @@ import { ICommand } from "wokcommands"; import { classModel, IClass } from "../../models/classModel"; import { create_default_embed } from "../../utils/embeds"; import { Schema, Types, Document } from "mongoose"; -import { - cleanChannelString, - getTopic, -} from "../../utils/channels"; +import { cleanChannelString, getTopic } from "../../utils/channels"; import { cleanRoleString } from "../../utils/roles"; -import { getCourseName, cleanCompSciTitle, cleanCourseCode } from "../../utils/course_cleaning"; +import { + getCourseName, + cleanCompSciTitle, + cleanCourseCode, +} from "../../utils/course_cleaning"; import Bottleneck from "bottleneck"; function isDupe(name: string): number { diff --git a/src/commands/owner/updateDB.ts b/src/commands/owner/updateDB.ts index cf5fb3a..214e260 100644 --- a/src/commands/owner/updateDB.ts +++ b/src/commands/owner/updateDB.ts @@ -4,7 +4,11 @@ import chalk from "chalk"; import axios from "axios"; import { classModel, IClass } from "../../models/classModel"; import { Types, Document } from "mongoose"; -import { cleanCourseCode, getCourseName, removeHTML } from "../../utils/course_cleaning"; +import { + cleanCourseCode, + getCourseName, + removeHTML, +} from "../../utils/course_cleaning"; import { cleanRoleString } from "../../utils/roles"; /** @@ -141,7 +145,7 @@ export default { ACTIVE: true, DUPE: false, }); - newClass.ROLE_NAME = cleanRoleString(getCourseName(newClass)) + newClass.ROLE_NAME = cleanRoleString(getCourseName(newClass)); courses_to_save.push(newClass); } else { let found_dupe = false; diff --git a/src/utils/channels.ts b/src/utils/channels.ts index 8440c9a..6449b6f 100644 --- a/src/utils/channels.ts +++ b/src/utils/channels.ts @@ -29,7 +29,6 @@ export function getTopic(course: IClass): string { return `${course.TITLE} | ${course.INFO}`.slice(0, 1024); } - /** * @description - Checks if a channel exists in the guild first by id, then by name * @author John Schiltz diff --git a/src/utils/course_cleaning.ts b/src/utils/course_cleaning.ts index 5ac62dd..98b3640 100644 --- a/src/utils/course_cleaning.ts +++ b/src/utils/course_cleaning.ts @@ -48,4 +48,3 @@ export function getCourseName(course: IClass) { export function cleanCompSciTitle(s: string): string { return s.replace(/(?:advanced )?topics in computer science:/gim, "").trim(); } -