Skip to content

Commit 9d7d45b

Browse files
dqna64Allynixtor
andauthored
Patch course revision system (#194)
* added mockup question structure for COMP1511 revision session * changed mockup file type to mdx * remove extra bracket * add letter_pairs problem * nested dynamic routes for course revision sessions * course revision sessions page * exercises sidebar * problem difficulties * show course revision content on home and collection pages * fix lint errs * update 2521-22T2 to 22T3 * fix more lint errs * opendev info: deployment wk9 * filter dynamically genreated exercises paths to match with correct course Co-authored-by: Allynixtor <bunchymovie123@gmail.com>
1 parent 6ec0418 commit 9d7d45b

File tree

15 files changed

+126
-57
lines changed

15 files changed

+126
-57
lines changed

README.md

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,3 +66,27 @@ We're CSESoc Education! Development is currently led by:
6666
- [Jeffrey Yao](https://github.com/jeffreydyao)
6767
- [Gordon Huang](https://github.com/dqna64)
6868
- [Jasper Di Francesco](https://github.com/jasperdifran)
69+
70+
## Putting Course Revision Resources onto the Learning Platform
71+
72+
The Learning Platform has a standard format for publishing course revision materials. It assumes that a course revision campaign consists of a set of exercises that can be written in markdown.
73+
74+
75+
In this *course-revision* folder observe that there are folders likes *1511-22T3* with mdx files inside. There is also a corresponding *1511-22T3.mdx* file next to the folder of the same name.
76+
77+
When you create a new course revision campaign, you **must** provide the following:
78+
79+
- An mdx file *course-revision/<course-offering>.mdx* which contains some info about this course revision campaign and how to get set up for the exercises (e.g. downloading some starter code). The <course-offering> should be replaced with the name of the course offering that this revision session is made for e.g. *1511-22T3* The fields you must provide in the yaml are:
80+
- title: Title of the course revision campaign (e.g. CSESoc COMP1511 Revision Practice Problems)
81+
- desc: Brief descriptoin of this course revision campaign (e.g. Practical coding exercises to help you prepare for your COMP1511 22T3 final exam)
82+
- course: (e.g. COMP1511) (Currently not used in the code)
83+
- offering: (e.g. 22T3) (Currently not used in the code)
84+
- A folder *course-revision/<course-offering>* which contains mdx files for each exercise. The fields you must provide in the yaml are:
85+
- title: Title of the exercise (e.g. BST Difference)
86+
- desc: Brief description of exercise (e.g. Find the mininum distance between any two numbers in a binary search tree)
87+
- class: Which course this exercise is for (eg. COMP2521)
88+
- difficulty: Difficult rating (1=easy, 2=medium, 3=hard) (e.g. 2)
89+
90+
Note: Both the <course-offering>.mdx and <course-offering> folder **MUST** share the exact same name in order to work (e.g. file `1511-22T3.mdx` and folder `1511-22T3`)
91+
92+
Once you have uploaded these files you can navigate to them at the url */course-revision* (e.g. *learn.csesoc.org.au/course-revision* or *localhost:3000/course-revision*)
File renamed without changes.

pages/course-revision/[course_offering]/[exercise]/index.tsx

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import Avatar from 'boring-avatars'
22
import { Flex } from 'components/Flex'
33
import { Text } from 'components/Text'
4-
import { allCourseRevisionOfferings, allCourseRevisionExercises } from 'contentlayer/generated'
4+
import { allCourseRevisionOfferings, allCourseRevisionExercises, CourseRevisionOffering, CourseRevisionExercise } from 'contentlayer/generated'
55
import { format, parseISO } from 'date-fns'
66
import { useMDXComponent } from 'next-contentlayer/hooks'
77
import Head from 'next/head'
@@ -35,11 +35,17 @@ const defaultComponents = {
3535
// See https://github.com/tsriram/with-mdx-bundler for details.
3636
const components = { ...defaultComponents }
3737

38+
type PropTypes = {
39+
courseOfferingContent: CourseRevisionOffering, exercisesContent: CourseRevisionExercise[], exerciseIdx: number
40+
}
41+
3842
export async function getStaticPaths() {
3943
const paths = []
4044
allCourseRevisionOfferings.forEach((o) => {
4145
allCourseRevisionExercises.forEach((e) => {
42-
paths.push({ params: { course_offering: o.slug, exercise: e.slug } })
46+
if (e._raw.sourceFileDir.endsWith(o.slug)) {
47+
paths.push({ params: { course_offering: o.slug, exercise: e.slug } })
48+
}
4349
})
4450
})
4551
console.log("paths: ", paths);
@@ -62,7 +68,7 @@ export async function getStaticProps({ params }) {
6268
}
6369
}
6470

65-
const ExercisePage = ({ courseOfferingContent, exercisesContent, exerciseIdx }) => {
71+
const ExercisePage = ({ courseOfferingContent, exercisesContent, exerciseIdx }: PropTypes) => {
6672
const MDXContent = useMDXComponent(exercisesContent[exerciseIdx].body.code)
6773

6874
return (

pages/opendev/index.tsx

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -169,7 +169,7 @@ const OpenDevTwo: NextPage = () => {
169169
<td>19 Oct 2022</td>
170170
<td>2-4pm, CLB 6</td>
171171
</tr>
172-
<tr style={{ backgroundColor: 'rgb(233, 240, 251)' }}>
172+
<tr style={{ backgroundColor: 'rgb(233, 240, 251)', color: '#b8bcc0' }}>
173173
<td>Week 7</td>
174174
<td><span style={{ fontWeight: "800" }} >MongoDB &amp; ExpressJS Workshop</span>
175175
<p style={{ margin: "0.2rem 0 0", fontSize: "0.7rem", lineHeight: "0.9rem" }}>
@@ -179,12 +179,12 @@ const OpenDevTwo: NextPage = () => {
179179
<td>26 Oct 2022</td>
180180
<td>2-4pm, Ainsworth 202</td>
181181
</tr>
182-
<tr style={{ backgroundColor: 'rgb(224, 235, 253)' }}>
182+
<tr style={{ backgroundColor: 'rgb(224, 235, 253)', color: '#b8bcc0' }}>
183183
<td>Week 8</td>
184184
<td>
185185
<span style={{ fontWeight: "800" }} >MongoDB &amp; ExpressJS Dev Session</span>
186186
<p style={{ margin: "0.2rem 0 0", fontSize: "0.7rem", lineHeight: "0.9rem" }}>
187-
Work on ReactJS Part 2 exercises and get help 🥰<br />
187+
Work on MongoDB &amp; ExpressJS exercises and get help 🥰<br />
188188
Chill, chat, study. Snacks provided 🧁
189189
</p>
190190
</td>
@@ -193,10 +193,9 @@ const OpenDevTwo: NextPage = () => {
193193
</tr>
194194
<tr style={{ backgroundColor: 'rgb(233, 240, 251)' }}>
195195
<td>Week 9</td>
196-
<td><span style={{ fontWeight: "800" }} >Project Dev Session</span>
196+
<td><span style={{ fontWeight: "800" }} >Deploying Web Apps (as a broke uni student)</span>
197197
<p style={{ margin: "0.2rem 0 0", fontSize: "0.7rem", lineHeight: "0.9rem" }}>
198-
Put your new skills to the test and build a project!<br />
199-
Chill, chat, study. Snacks provided 🧁
198+
Learn how to configure, build and deploy our web apps so everyone can see it online ⚡️
200199
</p></td>
201200
<td>9 Nov 2022</td>
202201
<td>2-4pm, Ainsworth 202</td>

public/~partytown/debug/partytown-atomics.js

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
/* Partytown 0.6.4 - MIT builder.io */
1+
/* Partytown 0.7.1 - MIT builder.io */
22
(window => {
33
const isPromise = v => "object" == typeof v && v && v.then;
44
const noop = () => {};
@@ -130,7 +130,7 @@
130130
added.add(obj);
131131
for (propName in obj) {
132132
if (isValidMemberName(propName)) {
133-
propValue = obj[propName];
133+
propValue = "path" === propName && obj instanceof Event ? obj.composedPath() : obj[propName];
134134
(includeFunctions || "function" != typeof propValue) && (includeEmptyStrings || "" !== propValue) && (serializedObj[propName] = serializeForWorker(winId, propValue, added));
135135
}
136136
}
@@ -541,14 +541,14 @@
541541
};
542542
})(((accessReq, responseCallback) => mainAccessHandler(worker, accessReq).then(responseCallback))).then((onMessageHandler => {
543543
if (onMessageHandler) {
544-
worker = new Worker(libPath + "partytown-ww-atomics.js?v=0.6.4", {
544+
worker = new Worker(libPath + "partytown-ww-atomics.js?v=0.7.1", {
545545
name: "Partytown 🎉"
546546
});
547547
worker.onmessage = ev => {
548548
const msg = ev.data;
549549
12 === msg[0] ? mainAccessHandler(worker, msg[1]) : onMessageHandler(worker, msg);
550550
};
551-
logMain("Created Partytown web worker (0.6.4)");
551+
logMain("Created Partytown web worker (0.7.1)");
552552
worker.onerror = ev => console.error("Web Worker Error", ev);
553553
mainWindow.addEventListener("pt1", (ev => registerWindow(worker, getAndSetInstanceId(ev.detail.frameElement), ev.detail)));
554554
}

public/~partytown/debug/partytown-media.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
/* Partytown 0.6.4 - MIT builder.io */
1+
/* Partytown 0.7.1 - MIT builder.io */
22
(self => {
33
const [getter, setter, callMethod, constructGlobal, definePrototypePropertyDescriptor, randomId, WinIdKey, InstanceIdKey, ApplyPathKey] = self.$bridgeToMedia$;
44
delete self.$bridgeToMedia$;

public/~partytown/debug/partytown-sandbox-sw.js

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
/* Partytown 0.6.4 - MIT builder.io */
1+
/* Partytown 0.7.1 - MIT builder.io */
22
(window => {
33
const isPromise = v => "object" == typeof v && v && v.then;
44
const noop = () => {};
@@ -130,7 +130,7 @@
130130
added.add(obj);
131131
for (propName in obj) {
132132
if (isValidMemberName(propName)) {
133-
propValue = obj[propName];
133+
propValue = "path" === propName && obj instanceof Event ? obj.composedPath() : obj[propName];
134134
(includeFunctions || "function" != typeof propValue) && (includeEmptyStrings || "" !== propValue) && (serializedObj[propName] = serializeForWorker(winId, propValue, added));
135135
}
136136
}
@@ -528,14 +528,14 @@
528528
}));
529529
})(((accessReq, responseCallback) => mainAccessHandler(worker, accessReq).then(responseCallback))).then((onMessageHandler => {
530530
if (onMessageHandler) {
531-
worker = new Worker(libPath + "partytown-ww-sw.js?v=0.6.4", {
531+
worker = new Worker(libPath + "partytown-ww-sw.js?v=0.7.1", {
532532
name: "Partytown 🎉"
533533
});
534534
worker.onmessage = ev => {
535535
const msg = ev.data;
536536
12 === msg[0] ? mainAccessHandler(worker, msg[1]) : onMessageHandler(worker, msg);
537537
};
538-
logMain("Created Partytown web worker (0.6.4)");
538+
logMain("Created Partytown web worker (0.7.1)");
539539
worker.onerror = ev => console.error("Web Worker Error", ev);
540540
mainWindow.addEventListener("pt1", (ev => registerWindow(worker, getAndSetInstanceId(ev.detail.frameElement), ev.detail)));
541541
}

public/~partytown/debug/partytown-sw.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
/* Partytown 0.6.4 - MIT builder.io */
1+
/* Partytown 0.7.1 - MIT builder.io */
22
const resolves = new Map;
33

44
const swMessageError = (accessReq, $error$) => ({
@@ -52,7 +52,7 @@ self.onfetch = ev => {
5252
const url = new URL(req.url);
5353
const pathname = url.pathname;
5454
if (pathname.endsWith("sw.html")) {
55-
ev.respondWith(response('<!DOCTYPE html><html><head><meta charset="utf-8"><script src="./partytown-sandbox-sw.js?v=0.6.4"><\/script></head></html>'));
55+
ev.respondWith(response('<!DOCTYPE html><html><head><meta charset="utf-8"><script src="./partytown-sandbox-sw.js?v=0.7.1"><\/script></head></html>'));
5656
} else {
5757
pathname.endsWith("proxytown") && ev.respondWith(httpRequestFromWebWorker(req));
5858
}

public/~partytown/debug/partytown-ww-atomics.js

Lines changed: 34 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
/* Partytown 0.6.4 - MIT builder.io */
1+
/* Partytown 0.7.1 - MIT builder.io */
22
(self => {
33
const WinIdKey = Symbol();
44
const InstanceIdKey = Symbol();
@@ -224,7 +224,7 @@
224224
this.stack = errorObject.stack;
225225
}
226226
}
227-
const NodeList = class {
227+
class NodeList {
228228
constructor(nodes) {
229229
(this._ = nodes).map(((node, index) => this[index] = node));
230230
}
@@ -249,7 +249,7 @@
249249
[Symbol.iterator]() {
250250
return this._[Symbol.iterator]();
251251
}
252-
};
252+
}
253253
const Attr = class {
254254
constructor(serializedAttr) {
255255
this.name = serializedAttr[0];
@@ -692,7 +692,7 @@
692692
};
693693
const run = (env, scriptContent, scriptUrl) => {
694694
env.$runWindowLoadEvent$ = 1;
695-
scriptContent = `with(this){${(webWorkerCtx.$config$.globalFns || []).filter((globalFnName => /[a-zA-Z_$][0-9a-zA-Z_$]*/.test(globalFnName))).map((g => `(typeof ${g}=='function'&&(window.${g}=${g}))`)).join(";") + scriptContent.replace(/\bthis\b/g, "(thi$(this)?window:this)").replace(/\/\/# so/g, "//Xso")}\n;function thi$(t){return t===this}}` + (scriptUrl ? "\n//# sourceURL=" + scriptUrl : "");
695+
scriptContent = `with(this){${scriptContent.replace(/\bthis\b/g, "(thi$(this)?window:this)").replace(/\/\/# so/g, "//Xso")}\n;function thi$(t){return t===this}};${(webWorkerCtx.$config$.globalFns || []).filter((globalFnName => /[a-zA-Z_$][0-9a-zA-Z_$]*/.test(globalFnName))).map((g => `(typeof ${g}=='function'&&(this.${g}=${g}))`)).join(";")};` + (scriptUrl ? "\n//# sourceURL=" + scriptUrl : "");
696696
env.$isSameOrigin$ || (scriptContent = scriptContent.replace(/.postMessage\(/g, `.postMessage('${env.$winId$}',`));
697697
new Function(scriptContent).call(env.$window$);
698698
env.$runWindowLoadEvent$ = 0;
@@ -722,12 +722,13 @@
722722
return resolvedUrl;
723723
};
724724
const resolveUrl = (env, url, type) => resolveToUrl(env, url, type) + "";
725-
const getPartytownScript = () => `<script src="${partytownLibUrl("partytown.js?v=0.6.4")}"><\/script>`;
725+
const getPartytownScript = () => `<script src="${partytownLibUrl("partytown.js?v=0.7.1")}"><\/script>`;
726726
const createImageConstructor = env => class HTMLImageElement {
727727
constructor() {
728728
this.s = "";
729729
this.l = [];
730730
this.e = [];
731+
this.style = {};
731732
}
732733
get src() {
733734
return this.s;
@@ -860,13 +861,15 @@
860861
};
861862
const isScriptJsType = scriptType => !scriptType || "text/javascript" === scriptType;
862863
const createNodeCstr = (win, env, WorkerBase) => {
864+
const config = webWorkerCtx.$config$;
863865
const WorkerNode = defineConstructorName(class extends WorkerBase {
864866
appendChild(node) {
865867
return this.insertBefore(node, null);
866868
}
867869
get href() {}
868870
set href(_) {}
869871
insertBefore(newNode, referenceNode) {
872+
var _a, _b;
870873
const winId = newNode[WinIdKey] = this[WinIdKey];
871874
const instanceId = newNode[InstanceIdKey];
872875
const nodeName = newNode[InstanceDataKey];
@@ -877,11 +880,17 @@
877880
const scriptType = getInstanceStateValue(newNode, 5);
878881
if (scriptContent) {
879882
if (isScriptJsType(scriptType)) {
880-
const errorMsg = runScriptContent(env, instanceId, scriptContent, winId, "");
881-
const datasetType = errorMsg ? "pterror" : "ptid";
882-
const datasetValue = errorMsg || instanceId;
883-
setter(newNode, [ "type" ], "text/partytown-x");
884-
setter(newNode, [ "dataset", datasetType ], datasetValue);
883+
const scriptId = newNode.id;
884+
const loadOnMainThread = scriptId && (null === (_b = null === (_a = config.loadScriptsOnMainThread) || void 0 === _a ? void 0 : _a.includes) || void 0 === _b ? void 0 : _b.call(_a, scriptId));
885+
if (loadOnMainThread) {
886+
setter(newNode, [ "type" ], "text/javascript");
887+
} else {
888+
const errorMsg = runScriptContent(env, instanceId, scriptContent, winId, "");
889+
const datasetType = errorMsg ? "pterror" : "ptid";
890+
const datasetValue = errorMsg || instanceId;
891+
setter(newNode, [ "type" ], "text/partytown-x");
892+
setter(newNode, [ "dataset", datasetType ], datasetValue);
893+
}
885894
}
886895
setter(newNode, [ "innerHTML" ], scriptContent);
887896
}
@@ -1288,7 +1297,7 @@
12881297
(() => {
12891298
if (!webWorkerCtx.$initWindowMedia$) {
12901299
self.$bridgeToMedia$ = [ getter, setter, callMethod, constructGlobal, definePrototypePropertyDescriptor, randomId, WinIdKey, InstanceIdKey, ApplyPathKey ];
1291-
webWorkerCtx.$importScripts$(partytownLibUrl("partytown-media.js?v=0.6.4"));
1300+
webWorkerCtx.$importScripts$(partytownLibUrl("partytown-media.js?v=0.7.1"));
12921301
webWorkerCtx.$initWindowMedia$ = self.$bridgeFromMedia$;
12931302
delete self.$bridgeFromMedia$;
12941303
}
@@ -1309,6 +1318,9 @@
13091318
win.Window = WorkerWindow;
13101319
win.name = name + `${normalizedWinId($winId$)} (${$winId$})`;
13111320
createNodeCstr(win, env, WorkerBase);
1321+
(win => {
1322+
win.NodeList = defineConstructorName(NodeList, "NodeList");
1323+
})(win);
13121324
createCSSStyleDeclarationCstr(win, WorkerBase, "CSSStyleDeclaration");
13131325
((win, WorkerBase, cstrName) => {
13141326
win[cstrName] = defineConstructorName(class extends WorkerBase {
@@ -1661,16 +1673,24 @@
16611673
let errorMsg = "";
16621674
let env = environments[winId];
16631675
let rsp;
1676+
let javascriptContentTypes = [ "text/jscript", "text/javascript", "text/x-javascript", "application/javascript", "application/x-javascript", "text/ecmascript", "text/x-ecmascript", "application/ecmascript" ];
16641677
if (scriptSrc) {
16651678
try {
16661679
scriptSrc = resolveToUrl(env, scriptSrc, "script") + "";
16671680
setInstanceStateValue(instance, 4, scriptSrc);
16681681
webWorkerCtx.$config$.logScriptExecution && logWorker(`Execute script src: ${scriptOrgSrc}`, winId);
16691682
rsp = await fetch(scriptSrc);
16701683
if (rsp.ok) {
1671-
scriptContent = await rsp.text();
1672-
env.$currentScriptId$ = instanceId;
1673-
run(env, scriptContent, scriptOrgSrc || scriptSrc);
1684+
let responseContentType = rsp.headers.get("content-type");
1685+
let shouldExecute = javascriptContentTypes.some((ct => {
1686+
var _a, _b, _c;
1687+
return null === (_c = null === (_a = null == responseContentType ? void 0 : responseContentType.toLowerCase) || void 0 === _a ? void 0 : (_b = _a.call(responseContentType)).includes) || void 0 === _c ? void 0 : _c.call(_b, ct);
1688+
}));
1689+
if (shouldExecute) {
1690+
scriptContent = await rsp.text();
1691+
env.$currentScriptId$ = instanceId;
1692+
run(env, scriptContent, scriptOrgSrc || scriptSrc);
1693+
}
16741694
runStateLoadHandlers(instance, "load");
16751695
} else {
16761696
errorMsg = rsp.statusText;

0 commit comments

Comments
 (0)