Skip to content
Open
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
64 changes: 64 additions & 0 deletions .github/workflows/update-algolia.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
name: Script - upserts course information to Algolia
on:
workflow_dispatch:
inputs:
environment:
description: Application environment to execute in.
required: true
term:
description: Academic term to execute the script with. ie 202009, 202001
required: true
jobs:
lint:
name: Lint
runs-on: ubuntu-latest
defaults:
run:
working-directory: ./functions
steps:
- uses: actions/checkout@v2
- name: Setup Node.js
uses: actions/setup-node@v1
with:
node-version: 14
- name: Cache npm dependencies
uses: actions/cache@v2
with:
key: npm-${{ hashFiles('package-lock.json') }}
path: ~/.npm
restore-keys: |
npm-
- name: Install dependencies
run: npm ci --ignore-scripts --no-audit --no-progress
- name: Lint
run: npm run lint
Comment thread
szeckirjr marked this conversation as resolved.
Outdated
update-algolia:
name: Update Algolia index
needs: [lint]
runs-on: ubuntu-latest
environment: ${{ github.event.inputs.environment }}
defaults:
run:
working-directory: ./functions
steps:
- uses: actions/checkout@v2
- name: Setup Node.js
uses: actions/setup-node@v1
with:
node-version: 14
- name: Cache npm dependencies
uses: actions/cache@v2
with:
key: npm-${{ hashFiles('package-lock.json') }}
path: ~/.npm
restore-keys: |
npm-
- name: Install dependencies
run: npm ci --ignore-scripts --no-audit --no-progress
- name: Set up Cloud SDK
uses: google-github-actions/setup-gcloud@v0
with:
service_account_key: ${{ secrets.FIREBASE_SERVICE_ACCOUNT }}
export_default_credentials: true
Comment thread
szeckirjr marked this conversation as resolved.
Outdated
- name: Run Update Algolia Script
run: npx ts-node ./scripts/update-algolia-index.ts ${{ github.event.inputs.term }}
96 changes: 46 additions & 50 deletions functions/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions functions/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@
"@types/ioredis": "^4.27.1",
"@types/jest": "^26.0.24",
"@types/node": "^14.18.3",
"@types/node-fetch": "^2.6.4",
Comment thread
szeckirjr marked this conversation as resolved.
"@types/object-hash": "^2.1.1",
"@types/supertest": "^2.0.11",
"@types/swagger-ui-express": "^4.1.3",
Expand Down
78 changes: 78 additions & 0 deletions functions/scripts/update-algolia-index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
import fetch from 'node-fetch';
import algoliasearch from 'algoliasearch';

if (process.argv.length != 3) throw Error('Term argument not found.');

const term: string = process.argv[2];

if (!/20\d{2}0[1,5,9]/.test(term.trim()))
throw Error('Invalid term argument format');

const BASE = 'https://courseup.vikelabs.ca/api';

const client = algoliasearch('ALGOLIA_APP_ID', 'ALGOLIA_ADMIN_KEY');
const index = client.initIndex('ALGOLIA_INDEX_NAME');
Comment thread
szeckirjr marked this conversation as resolved.
Outdated

interface Course {
subject: string;
code: string;
pid: string;
description: string;
title: string;
}

const getCourses = async (term: string): Promise<Course[]> => {
const res = await fetch(`${BASE}/courses/${term}`);
const data: Course[] = await res.json();
return data;
};

const getCourse = async (term: string, course: Course): Promise<Course> => {
const res = await fetch(
`${BASE}/courses/${term}/${course.subject}/${course.code}`
);
const data: Course = await res.json();
return data;
};

const main = async () => {
const courses: Course[] = await getCourses(term);

console.log(`Found ${courses.length} courses for ${term}`);

const values = courses.values();

const vals: Course[] = [];

const workers = Array(50)
.fill(values)
.map(async (iterator) => {
for (const course of iterator) {
const courseDetails = await getCourse(term, course);
console.log(course.subject, course.code);
vals.push({ ...courseDetails });
}
});

await Promise.allSettled(workers);

const coursesForIndex = vals.map((course) => ({
objectID: `${course.subject}${course.code}`,
subject: course.subject,
code: course.code,
Comment on lines +69 to +70

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Having a field that's just the concatenation like objectID helps with cases where people enter CSC100 without a space.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is objectID not taken into consideration for the search?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

and re: synonyms for courses, are you thinking a sort of look up table with pre defined synonyms for the most common stuff?

pid: course.pid,
description: course.description,
title: course.title,
}));

index
.saveObjects(coursesForIndex)
.then(({ objectIDs }) => {
console.log(objectIDs);
})
.catch((err) => {
console.log(err);
});
Comment on lines +76 to +83

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are you going to update a single index by clearing it and then adding new data or doing this? iirc if you say run this twice in succession you'll have duplicate records in the index. I think the objectID should make it so it doesn't but just want to check in.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is the plan for stale courses? ie. courses that don't exist anymore cause this will make it so some will linger.

@szeckirjr szeckirjr Jul 18, 2023

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yep you're right! I ran this twice and I don't get duplicate records because of the objectID field. What it does is replace all other fields in the object aside from the ID (which in our case will be the subject+code)

but hmmm that's a good point 🤔 wiping it clear and then populating might be good, and switching to replaceAllObjects seems easy enough
but then it worried me about anything going wrong with the API (e.g. running the script with 202401 currently will return 0 courses to be added since API is not working, so it would simply wipe the index instead of not touching it at all). What do you think?

};

main();
2 changes: 1 addition & 1 deletion functions/tsconfig.dev.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
"outDir": "lib",
"sourceMap": true,
"strict": true,
"target": "ES2019"
"target": "es2020"
},
"include": ["scripts", "src", ".eslintrc.js"]
}
2 changes: 1 addition & 1 deletion functions/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
"outDir": "lib",
"sourceMap": true,
"strict": true,
"target": "ES2019"
"target": "es2020"
},
"compileOnSave": true,
"include": ["src"],
Expand Down