Skip to content

Commit fbb2a44

Browse files
kevin-lannthomasyzy7dawangk
authored
Kl/scrum 41 ai timetable generate (#75)
Co-authored-by: dawangk <[email protected]> Co-authored-by: dawangk <[email protected]> Co-authored-by: kevin-lann <[email protected]> Co-authored-by: dawangk <[email protected]>
1 parent 9c358fc commit fbb2a44

File tree

20 files changed

+1354
-228
lines changed

20 files changed

+1354
-228
lines changed
Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
import { analyzeQuery } from "../src/utils/analyzeQuery";
2+
import { describe, test, expect, jest } from "@jest/globals";
3+
import {
4+
NAMESPACE_KEYWORDS,
5+
ASSISTANT_TERMS,
6+
DEPARTMENT_CODES,
7+
} from "../src/constants/promptKeywords";
8+
9+
// Mock the constants if needed
10+
jest.mock("../src/constants/promptKeywords", () => ({
11+
NAMESPACE_KEYWORDS: {
12+
courses_v3: ["course", "class", "description"],
13+
offerings: ["offering", "schedule", "timetable"],
14+
prerequisites: ["prerequisite", "prereq"],
15+
corequisites: ["corequisite", "coreq"],
16+
departments: ["department", "faculty"],
17+
programs: ["program", "major", "minor"],
18+
},
19+
ASSISTANT_TERMS: ["you", "your", "morpheus", "assistant"],
20+
DEPARTMENT_CODES: ["cs", "math", "eng"],
21+
GENERAL_ACADEMIC_TERMS: ["academic", "study", "education"],
22+
}));
23+
24+
describe("analyzeQuery", () => {
25+
test("should return no search required for assistant-related queries", () => {
26+
const result = analyzeQuery("Can you help me with something?");
27+
expect(result).toEqual({
28+
requiresSearch: false,
29+
relevantNamespaces: [],
30+
});
31+
});
32+
33+
test("should detect course-related keywords and return appropriate namespaces", () => {
34+
const result = analyzeQuery("Tell me about this course");
35+
expect(result.requiresSearch).toBe(true);
36+
expect(result.relevantNamespaces).toContain("courses_v3");
37+
});
38+
39+
test("should detect course codes and include relevant namespaces", () => {
40+
const result = analyzeQuery("What is CSC108 about?");
41+
expect(result.requiresSearch).toBe(true);
42+
expect(result.relevantNamespaces).toContain("courses_v3");
43+
expect(result.relevantNamespaces).toContain("offerings");
44+
expect(result.relevantNamespaces).toContain("prerequisites");
45+
});
46+
47+
test("should detect department codes and include relevant namespaces", () => {
48+
const result = analyzeQuery("What math courses are available?");
49+
expect(result.requiresSearch).toBe(true);
50+
expect(result.relevantNamespaces).toContain("departments");
51+
expect(result.relevantNamespaces).toContain("courses_v3");
52+
});
53+
54+
test("should detect offering-related keywords", () => {
55+
const result = analyzeQuery("What is the schedule for winter semester?");
56+
expect(result.requiresSearch).toBe(true);
57+
expect(result.relevantNamespaces).toContain("offerings");
58+
});
59+
60+
test("should detect prerequisite-related keywords", () => {
61+
const result = analyzeQuery("What are the prerequisites for this class?");
62+
expect(result.requiresSearch).toBe(true);
63+
expect(result.relevantNamespaces).toContain("prerequisites");
64+
});
65+
66+
test("should detect corequisite-related keywords", () => {
67+
const result = analyzeQuery("Are there any corequisites for this course?");
68+
expect(result.requiresSearch).toBe(true);
69+
expect(result.relevantNamespaces).toContain("corequisites");
70+
});
71+
72+
test("should return all namespaces when search is required but no specific namespaces identified", () => {
73+
// Assuming GENERAL_ACADEMIC_TERMS includes 'academic'
74+
const result = analyzeQuery("I need academic information");
75+
expect(result.requiresSearch).toBe(true);
76+
expect(result.relevantNamespaces).toEqual([
77+
"courses_v3",
78+
"offerings",
79+
"prerequisites",
80+
"corequisites",
81+
"departments",
82+
"programs",
83+
]);
84+
});
85+
86+
test("should be case insensitive", () => {
87+
const result = analyzeQuery("TELL ME ABOUT THIS COURSE");
88+
expect(result.requiresSearch).toBe(true);
89+
expect(result.relevantNamespaces).toContain("courses_v3");
90+
});
91+
92+
test("should detect multiple namespaces in a single query", () => {
93+
const result = analyzeQuery(
94+
"What are the prerequisites and schedule for CSC108?",
95+
);
96+
expect(result.requiresSearch).toBe(true);
97+
expect(result.relevantNamespaces).toContain("prerequisites");
98+
expect(result.relevantNamespaces).toContain("offerings");
99+
expect(result.relevantNamespaces).toContain("courses_v3");
100+
});
101+
102+
test("should correctly identify course codes with different formats", () => {
103+
const formats = [
104+
"CSC108", // Standard format
105+
"CSC108H", // With suffix
106+
"CSCA08", // Four letters
107+
"MAT224", // Different department
108+
"ECO100Y", // Another format
109+
];
110+
111+
formats.forEach((code) => {
112+
const result = analyzeQuery(`Tell me about ${code}`);
113+
expect(result.requiresSearch).toBe(true);
114+
expect(result.relevantNamespaces).toContain("courses_v3");
115+
expect(result.relevantNamespaces).toContain("offerings");
116+
expect(result.relevantNamespaces).toContain("prerequisites");
117+
});
118+
});
119+
});
Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
1+
import {
2+
BREADTH_REQUIREMENT_KEYWORDS,
3+
YEAR_LEVEL_KEYWORDS,
4+
} from "../src/constants/promptKeywords";
5+
import { includeFilters } from "../src/utils/includeFilters";
6+
import { describe, test, expect, jest, beforeEach } from "@jest/globals";
7+
import * as ModuleType from "../src/utils/convert-breadth-requirement";
8+
import * as ModuleType0 from "../src/utils/convert-year-level";
9+
10+
// Create mock functions
11+
const mockConvertBreadthRequirement = jest.fn(
12+
(namespace) => `converted_${namespace}`,
13+
);
14+
const mockConvertYearLevel = jest.fn((namespace) => `converted_${namespace}`);
15+
16+
// Mock the modules
17+
jest.mock("../src/utils/convert-breadth-requirement", () => ({
18+
convertBreadthRequirement: (namespace: string) =>
19+
mockConvertBreadthRequirement(namespace),
20+
}));
21+
22+
jest.mock("../src/utils/convert-year-level", () => ({
23+
convertYearLevel: (namespace: string) => mockConvertYearLevel(namespace),
24+
}));
25+
26+
describe("includeFilters", () => {
27+
beforeEach(() => {
28+
// Clear mock data before each test
29+
mockConvertBreadthRequirement.mockClear();
30+
mockConvertYearLevel.mockClear();
31+
});
32+
33+
test("should return empty object when no filters match", () => {
34+
const query = "something random";
35+
const result = includeFilters(query);
36+
expect(result).toEqual({});
37+
});
38+
39+
test("should match breadth requirement keywords case-insensitively", () => {
40+
const query = "I want to study ART Literature";
41+
const result = includeFilters(query);
42+
expect(result).toEqual({
43+
$or: [{ breadth_requirement: { $eq: "converted_ART_LIT_LANG" } }],
44+
});
45+
});
46+
47+
test("should match year level keywords case-insensitively", () => {
48+
const query = "Looking for A-level courses";
49+
const result = includeFilters(query);
50+
expect(result).toEqual({
51+
$or: [{ year_level: { $eq: "converted_first_year" } }],
52+
});
53+
});
54+
55+
test("should combine both breadth and year level filters with $and when both are present", () => {
56+
const query = "Natural Science First-Year courses";
57+
const result = includeFilters(query);
58+
expect(result).toEqual({
59+
$and: [
60+
{
61+
$or: [{ breadth_requirement: { $eq: "converted_NAT_SCI" } }],
62+
},
63+
{
64+
$or: [{ year_level: { $eq: "converted_first_year" } }],
65+
},
66+
],
67+
});
68+
});
69+
70+
test("should handle multiple breadth requirements", () => {
71+
const query = "social science or quantitative reasoning";
72+
const result = includeFilters(query);
73+
expect(result).toEqual({
74+
$or: [
75+
{ breadth_requirement: { $eq: "converted_SOCIAL_SCI" } },
76+
{ breadth_requirement: { $eq: "converted_QUANT" } },
77+
],
78+
});
79+
});
80+
81+
test("should handle multiple year levels", () => {
82+
const query = "third year or fourth-year courses";
83+
const result = includeFilters(query);
84+
expect(result).toEqual({
85+
$or: [
86+
{ year_level: { $eq: "converted_third_year" } },
87+
{ year_level: { $eq: "converted_fourth_year" } },
88+
],
89+
});
90+
});
91+
92+
test("should handle multiple breadth requirements and year levels", () => {
93+
const query = "history philosophy B-level or C-level";
94+
const result = includeFilters(query);
95+
console.log(result);
96+
expect(result).toEqual({
97+
$and: [
98+
{
99+
$or: [{ breadth_requirement: { $eq: "converted_HIS_PHIL_CUL" } }],
100+
},
101+
{
102+
$or: [
103+
{ year_level: { $eq: "converted_second_year" } },
104+
{ year_level: { $eq: "converted_third_year" } },
105+
],
106+
},
107+
],
108+
});
109+
});
110+
111+
test("should ignore partial keyword matches", () => {
112+
// "art" alone shouldn't match "art literature language"
113+
const query = "art courses";
114+
const result = includeFilters(query);
115+
// This should not match any specific filter since "art" alone isn't in the keywords
116+
expect(result).toEqual({});
117+
});
118+
119+
test("should handle edge case with empty query", () => {
120+
const query = "";
121+
const result = includeFilters(query);
122+
expect(result).toEqual({});
123+
});
124+
});

course-matrix/backend/package-lock.json

Lines changed: 3 additions & 3 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)