Skip to content

Commit 5ac4eb6

Browse files
Responsive filtering doing its things (#138)
* applied filters into the search function using flags in the model, will have to refine more in the filters, but this is great for the demo * wrote departments filter option * added check to make sure to not recommend courses the client has taken before * initial state good * departments go to model * trying to add filtering out courses with NULL fields as an implementable filter option, merging with main start as per teams request * merging into brain? * some ?. properties check, because skill issue * more course?. things * finished the noNull feature, essentially fixed typos * null course checkbox * null check linked * Null field toggle works * nice and beutiful * removed the explicit null string checks, hopefully doesnt break, also wrote logic for Period filtering * period filter * a bit polishing, touching on the period presenter since i thought its broken, but it was the database dropping all course.period data >:( * fixed some filters logic (level filter, period filter), added descriptions to most filters tooltips, fixed About us button, touched Tab title to current working title, fixed some other things * visually persistent filters * now fixed * once again * unlocking departments and locations * wip * filters enabled by clicking on them * My side merge with Dean's side * departments half functional * rebase to main * rebase to main * Refactoring transcript scraper (#126) * Reworked UploadTranscriptPresenter into a source file, now SidebarPresenter calls it, allows us to upgrade it so that the transcript scraper function has access to a flattened out model (course list with only id, and name fields needed) * reworked LocalStorage so that it stores object array instead of strings, object structure is {id (str),name(str),is_in_DB(bool)} * fixed couple errors relating to the new LocaleStorage finished courses, also touched on the departments so that its trucated by tailwind instead of just 25 characters * fixed a lot of typos (deparmen != department) * trying to add hover over tooltips such that it displayes name of course * inbetween * removing the console.logs because theyre dumb and we should remove them before committing to main especially, added some comments, will add more * department percistency done only SU and Education Office broken * even more documentation, added descriptions and cleared up toolTips on the sidebar * departments should be working * nothing really * work on upload bahaviour --------- Co-authored-by: kexana <deotsts@gmail.com>
1 parent 0adf391 commit 5ac4eb6

14 files changed

+455
-337
lines changed

my-app/firebase.js

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,6 @@ export function connectToFirebase(model) {
3535
const options = JSON.parse(localStorage.getItem("filterOptions"));
3636
if (options) {
3737
model.setFilterOptions(options);
38-
console.log("Restore options from local storage");
3938
}
4039

4140
reaction(
@@ -203,7 +202,6 @@ export async function uploadDepartmentsAndLocations(departments, locations) {
203202
const departmentsRef = ref(db, "departments");
204203
try {
205204
await set(departmentsRef, departments);
206-
console.log("Uploaded Departments");
207205
} catch (error) {
208206
console.error("Failed to upload departments:", error);
209207
return false;
@@ -213,7 +211,6 @@ export async function uploadDepartmentsAndLocations(departments, locations) {
213211
const locationsRef = ref(db, "locations");
214212
try {
215213
await set(locationsRef, locations);
216-
console.log("Uploaded Locations");
217214
} catch (error) {
218215
console.error("Failed to upload locations:", error);
219216
return false;
@@ -275,7 +272,6 @@ async function loadCoursesFromCacheOrFirebase(model) {
275272
});
276273

277274
if (cachedTimestamp === firebaseTimestamp) {
278-
console.log("Using cached courses from IndexedDB...");
279275
const courseTx = db.transaction("courses", "readonly");
280276
const courseStore = courseTx.objectStore("courses");
281277
const getAllReq = courseStore.getAll();
@@ -291,7 +287,6 @@ async function loadCoursesFromCacheOrFirebase(model) {
291287
}
292288

293289
// fallback: fetch from Firebase
294-
console.log("Fetching courses from Firebase...");
295290
const courses = await fetchAllCourses();
296291
model.setCourses(courses);
297292
saveCoursesToCache(courses, firebaseTimestamp);
@@ -304,7 +299,6 @@ export async function addReviewForCourse(courseCode, review) {
304299
const updateCourseAvgRating = httpsCallable(functions, 'updateCourseAvgRating');
305300
const result = await updateCourseAvgRating({ courseCode });
306301

307-
console.log('Average rating updated:', result.data.avgRating);
308302
} catch (error) {
309303
console.error("Error when adding a course to firebase or updating the average:", error);
310304
}

my-app/src/model.js

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -222,7 +222,6 @@ export const model = {
222222

223223
updateLevelFilter(level) {
224224
this.filterOptions.level = level;
225-
console.log(level);
226225
},
227226

228227
updateDepartmentFilter(department) {
@@ -274,7 +273,7 @@ export const model = {
274273
},
275274
//for better display we would like the departments in a structured format based on school
276275
formatDepartments() {
277-
const grouped = this.departments?.reduce((acc, item) => {
276+
const grouped = this?.departments.reduce((acc, item) => {
278277
const [school, department] = item.split("/");
279278
if (!acc[school]) {
280279
acc[school] = [];

my-app/src/presenters/FilterPresenter.jsx

Lines changed: 33 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -6,16 +6,24 @@ import { SearchbarPresenter } from './SearchbarPresenter.jsx';
66
/* FilterPresenter is responsible for applying the logic necessary to filter out the courses from the overall list */
77
const FilterPresenter = observer(({ model }) => {
88
/* global variable for the scope of this presenter, all the smaller functions depend on it instead of passing it back and forth as params */
9-
var localFilteredCourses = []; //might need to declare out of scope. idk js
9+
var localFilteredCourses = [];
1010

11+
/* functions declared here are generally things the main function of this observer takes and runs if the given filters are enabled,
12+
* this is determined through model.filterOptions.apply*Insert filter name* flags.
13+
* This presenter should be changed such that it uses side-effects instead model.filtersChange flag, since
14+
*/
1115

16+
/* functions */
1217
function applyTranscriptEligibility() {
1318
if (localFilteredCourses.length == 0)
1419
return;
1520

16-
/* */
21+
/* this should be either weak/moderate/strong */
1722
const eligibilitytype = model.filterOptions.eligibility;
1823

24+
/* I am doing this trick in a multitude of filters, essentially the best fitting courses should appear first in the
25+
* list view on the right side, so we just filter for those and at the very end merge them back together into a single array
26+
*/
1927
let strongcourses = [];
2028
let zerocourses = [];
2129
let moderatecourses = [];
@@ -24,7 +32,7 @@ const FilterPresenter = observer(({ model }) => {
2432
let storedFinishedCourses = [];
2533

2634
if (localStorage.getItem("completedCourses"))
27-
storedFinishedCourses = JSON.parse(localStorage.getItem("completedCourses"));
35+
storedFinishedCourses = JSON.parse(localStorage.getItem("completedCourses")).map(obj => String(obj.id));
2836

2937

3038
localFilteredCourses.forEach(course => {
@@ -55,6 +63,7 @@ const FilterPresenter = observer(({ model }) => {
5563

5664
});
5765

66+
/* If user selects strong matching he should get all courses which might be strongly fitting (so strong courses and zero/missing prereq courses) */
5867
switch (eligibilitytype) {
5968
case "strong":
6069
{
@@ -73,7 +82,7 @@ const FilterPresenter = observer(({ model }) => {
7382
}
7483
default:
7584
{
76-
console.log("Error: somehow we got into a state where model.eligibility is no \"strong\"/\"moderate\"/\"weak\".");
85+
console.log("Error: somehow we got into a state where model.eligibility is not either \"strong\"/\"moderate\"/\"weak\".");
7786
localFilteredCourses = [];
7887
break;
7988
}
@@ -182,8 +191,6 @@ const FilterPresenter = observer(({ model }) => {
182191
//course?.language.english (true/false/undefined)
183192
//course?.language.swedish (true/false/undefined)
184193

185-
//console.log(data);
186-
187194
switch (languages) {
188195
case "none":
189196
{
@@ -196,8 +203,7 @@ const FilterPresenter = observer(({ model }) => {
196203
try {
197204
return (course?.language?.english === true);
198205
} catch (error) {
199-
console.log(course);
200-
console.log("BIG ERROR", error);
206+
console.log("BIG ERROR", error, course);
201207
return false;
202208
}
203209

@@ -207,8 +213,7 @@ const FilterPresenter = observer(({ model }) => {
207213
try {
208214
return (course?.language === undefined);
209215
} catch (error) {
210-
console.log(course);
211-
console.log("BIG ERROR");
216+
console.log("BIG ERROR", error, course);
212217
return false;
213218
}
214219

@@ -221,8 +226,7 @@ const FilterPresenter = observer(({ model }) => {
221226
try {
222227
return (course?.language?.swedish === true);
223228
} catch (error) {
224-
console.log(course);
225-
console.log("BIG ERROR");
229+
console.log("BIG ERROR", error, course);
226230
return false;
227231
}
228232

@@ -232,8 +236,7 @@ const FilterPresenter = observer(({ model }) => {
232236
try {
233237
return (course?.language === undefined);
234238
} catch (error) {
235-
console.log(course);
236-
console.log("BIG ERROR");
239+
console.log("BIG ERROR", error, course);
237240
return false;
238241
}
239242

@@ -246,8 +249,7 @@ const FilterPresenter = observer(({ model }) => {
246249
try {
247250
return ((course?.language?.english === true) && (course?.language?.swedish === true));
248251
} catch (error) {
249-
console.log(course);
250-
console.log("BIG ERROR");
252+
console.log("BIG ERROR", error, course);
251253
return false;
252254
}
253255

@@ -258,8 +260,7 @@ const FilterPresenter = observer(({ model }) => {
258260
return (((course?.language?.english === true) && (course?.language?.swedish === false))
259261
|| ((course?.language?.english === false) && (course?.language?.swedish === true)));
260262
} catch (error) {
261-
console.log(course);
262-
console.log("BIG ERROR");
263+
console.log("BIG ERROR", error, course);
263264
return false;
264265
}
265266

@@ -269,8 +270,7 @@ const FilterPresenter = observer(({ model }) => {
269270
try {
270271
return (course?.language === undefined);
271272
} catch (error) {
272-
console.log(course);
273-
console.log("BIG ERROR");
273+
console.log("BIG ERROR", error, course);
274274
return false;
275275
}
276276

@@ -293,31 +293,16 @@ const FilterPresenter = observer(({ model }) => {
293293

294294
localFilteredCourses = localFilteredCourses.filter(course => levels.includes(course?.academicLevel));
295295

296-
/*
297-
let levels = model.filterOptions.level;
298-
let stayingCourses = [];
299-
300-
for(let i=0; i<localFilteredCourses.length; i++){
301-
let stay = false;
302-
for(let j=0; j<levels.length; j++){
303-
if(localFilteredCourses[i].academicLevel === levels[j]){
304-
stay = true;
305-
break;
306-
}
307-
}
308-
if(stay) stayingCourses.push(localFilteredCourses[i]);
309-
}
310-
localFilteredCourses = [...stayingCourses];*/
311296
}
312297

313298
function updateDepartments() {
314-
const deparments = model.filterOptions.deparment;
299+
const departments = model.filterOptions.department;
315300
let bestCourses = [];
316301
let worstCourses = [];
317302

318303
bestCourses = localFilteredCourses.filter(function (course) {
319304
try {
320-
return (deparments.includes(course?.deparment));
305+
return (departments.includes(course?.department));
321306
} catch (error) {
322307
console.log("for some reason course?.department is: ", course?.department, error);
323308
return false;
@@ -328,7 +313,7 @@ const FilterPresenter = observer(({ model }) => {
328313
try {
329314
return (course?.department === undefined);
330315
} catch (error) {
331-
console.log("BIG ERROR", error);
316+
console.log("BIG ERROR", error, course);
332317
return false;
333318
}
334319

@@ -337,6 +322,10 @@ const FilterPresenter = observer(({ model }) => {
337322
localFilteredCourses = [...bestCourses, ...worstCourses];
338323
}
339324

325+
/* Function that deals with removing the courses that have no properties or have null properties in the categories the user
326+
* using for filtering. The "null" check is a remainder from a version where we didn't use the ?. property accessing yet,
327+
* should be able to be removed without problem in the future.
328+
*/
340329
function updateNoNullcourses(){
341330
let local = [...localFilteredCourses];
342331

@@ -380,6 +369,7 @@ const FilterPresenter = observer(({ model }) => {
380369
localFilteredCourses = [...local];
381370
}
382371

372+
/* function that should run every single time the model changes (see note below) */
383373
async function run() {
384374
if (model.courses.length == 0) {
385375
return;
@@ -408,17 +398,20 @@ const FilterPresenter = observer(({ model }) => {
408398
if (model.filterOptions.applyTranscriptFilter) {
409399
applyTranscriptEligibility();
410400
}
411-
if (model.filterOptions.applyDepartments) {
401+
if (model.filterOptions.applyDepartmentFilter) {
412402
updateDepartments();
413403
}
414404

415405
model.filteredCourses = [...localFilteredCourses];
416406
model.filtersChange = false;
417407
model.setFiltersCalculated();
418-
//console.log("filtered objects number of elements: ", model.filteredCourses.length);
419408
}
420409
}
421410

411+
/* the problem is that unless using sideeffects, the run() not being async and/or it setting the filterschange = false very early can mean
412+
* that 0 courses will get put into the model.filtered courses (which is the list of courses getting passed to search, and then listview)
413+
* therefore TODO: rework it to stop using this dumb flags we started before learning anything about react,observers,js
414+
*/
422415
run();
423416

424417
});

my-app/src/presenters/PrerequisitePresenter.jsx

Lines changed: 0 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,6 @@ export const PrerequisitePresenter = observer((props) => {
5252
const nodeHeight = 36;
5353

5454
loadTree();
55-
//console.log(initialNodes);
5655

5756
const getLayoutedElements = (nodes, edges, direction = 'LR') => {
5857
const isHorizontal = direction === 'LR';
@@ -237,8 +236,6 @@ export const PrerequisitePresenter = observer((props) => {
237236

238237
if (!Array.isArray(current_object)) { // Is object
239238
let key = Object.keys(current_object)[0];
240-
//console.log("Len: " + current_object[key].length);
241-
//console.log("Type: " + typeof current_object[key]);
242239
if (current_object[key] != null && current_object[key].length == 1 && (typeof current_object[key][0] == "string"
243240
|| (current_object[key][0].length == 1 && typeof current_object[key][0][0] == "string"))) {
244241
prereq_convert(courses_taken, current_object[key], key, previous_node_id);
@@ -281,13 +278,10 @@ export const PrerequisitePresenter = observer((props) => {
281278
if (!started_compressing) {
282279
if (i < current_object.length - 2 && typeof current_object[i + 1] == "string" && typeof current_object[i + 2] == "string") {
283280
let next = current_object[i + 1]; let next_next = current_object[i + 2];
284-
//console.log(next)
285-
//console.log(course_number, next.slice(2), next_next.slice(2))
286281
if (next.slice(0, 2) === course_letters
287282
&& next_next.slice(0, 2) === course_letters && !isNaN(next.slice(2))
288283
&& !isNaN(next_next.slice(2)) && parseInt(next.slice(2)) == course_number + 1
289284
&& parseInt(next_next.slice(2)) == course_number + 2) {
290-
//console.log(course_number, next.slice(2), next_next.slice(2))
291285
current_compresion.push([i, course_letters, course_number]);
292286
current_compresion.push([i + 1, course_letters, course_number + 1]);
293287
current_compresion.push([i + 2, course_letters, course_number + 2]);
@@ -312,7 +306,6 @@ export const PrerequisitePresenter = observer((props) => {
312306
} else {
313307
started_compressing = false;
314308
//refined_course_array.push([i + 1, current_object[i + 1].slice(0, 2), current_object[i + 1].slice(2)]);
315-
//console.log(current_compresion);
316309
refined_course_array.push([-1, "!", current_compresion]);
317310
current_compresion = [];
318311
}
@@ -331,8 +324,6 @@ export const PrerequisitePresenter = observer((props) => {
331324
}
332325

333326
}
334-
//console.log("HERERERERERE!!!")
335-
//console.log(refined_course_array);
336327

337328

338329
for (let i = 0; i < refined_course_array.length; i++) {
@@ -359,8 +350,6 @@ export const PrerequisitePresenter = observer((props) => {
359350
} else if (previous_key == "and" && compressed_done_count == compressed_length) {
360351
course_done = true;
361352
}
362-
//console.log("Compressed:");
363-
//console.log(refined_course_array[i][2]);
364353
course_code = refined_course_array[i][2][0][1] + refined_course_array[i][2][0][2] +
365354
"-" + refined_course_array[i][2][compressed_length - 1][1] + refined_course_array[i][2][compressed_length - 1][2];
366355
input_text = course_code;
@@ -394,9 +383,6 @@ export const PrerequisitePresenter = observer((props) => {
394383
if (typeof current_object == "object" && !Array.isArray(current_object)) {
395384
let key = Object.keys(current_object)[0];
396385
let object_array = current_object[key];
397-
//console.log("DEBUGGING ")
398-
//console.log(current_node)
399-
//console.log(object_array)
400386
let num_of_matches = 0;
401387
for (let i = 0; i < object_array.length; i++) {
402388
if (Array.isArray(object_array[i])) {
@@ -430,15 +416,12 @@ export const PrerequisitePresenter = observer((props) => {
430416
} else if(object_array[i] === true) {num_of_matches++}
431417
}
432418
if (key == "or" && num_of_matches > 0) {
433-
//console.log(current_node)
434419
current_object[key] = true;
435420
if (current_node != null) {
436421
current_node["style"]["backgroundColor"] = "lightgreen";
437422
}
438423
}
439424
else if (key == "and" && num_of_matches == object_array.length) {
440-
//console.log("DEBUGGING 2");
441-
//console.log(num_of_matches, object_array.length)
442425
current_object[key] = true;
443426
if (current_node != null) {
444427
current_node["style"]["backgroundColor"] = "lightgreen";
@@ -453,7 +436,6 @@ export const PrerequisitePresenter = observer((props) => {
453436

454437
function generateTree(courses_taken, prereqs) {
455438
prereq_convert(courses_taken, prereqs, null, props.selectedCourse.code);
456-
//console.log(JSON.stringify(prereqs, null, 4));
457439
let key = Object.keys(prereqs);
458440
if (prereqs[key] === true) {
459441
return true;
@@ -467,7 +449,6 @@ export const PrerequisitePresenter = observer((props) => {
467449

468450
function loadTree() {
469451

470-
//console.log(JSON.stringify(props.selectedCourse.prerequisites, null, 4));
471452
if (!props.selectedCourse?.prerequisites || props.selectedCourse.prerequisites.length == 0) {
472453
let display_node = createNode("No Prerequisites", "No Prerequisites", "default");
473454
display_node.style["pointerEvents"] = "none";

0 commit comments

Comments
 (0)