a
This commit is contained in:
parent
9d7e8e4511
commit
92b571b0b2
16 changed files with 142 additions and 55 deletions
|
@ -5,3 +5,4 @@ indent_style = space
|
|||
|
||||
[*.{md,svelte,ts,json,rst}]
|
||||
indent_size = 2
|
||||
|
||||
|
|
|
@ -1,23 +0,0 @@
|
|||
import { PrimaryKey, Sequelize, Column, Table, Model } from "sequelize-typescript";
|
||||
|
||||
@Table
|
||||
export class Page extends Model {
|
||||
@PrimaryKey
|
||||
@Column
|
||||
public slug: string;
|
||||
|
||||
@Column
|
||||
public title: string;
|
||||
}
|
||||
|
||||
@Table
|
||||
export class Exercise extends Model {
|
||||
}
|
||||
|
||||
export async function init(path: string): Promise<Sequelize> {
|
||||
let sequelize = new Sequelize(`sqlite:${path}`, {
|
||||
models: [Page, Exercise],
|
||||
});
|
||||
await sequelize.sync({ force: true });
|
||||
return sequelize;
|
||||
}
|
54
compile-database/db/page.ts
Normal file
54
compile-database/db/page.ts
Normal file
|
@ -0,0 +1,54 @@
|
|||
import { PrimaryKey, Sequelize, Column, Table, Model, DataType } from "sequelize-typescript";
|
||||
|
||||
@Table
|
||||
export class Page extends Model {
|
||||
@PrimaryKey
|
||||
@Column
|
||||
public slug: string;
|
||||
|
||||
@Column
|
||||
public title: string;
|
||||
|
||||
@Column
|
||||
public content: string;
|
||||
}
|
||||
|
||||
@Table
|
||||
export class Exercise extends Model {
|
||||
@PrimaryKey
|
||||
@Column
|
||||
public page_slug: string;
|
||||
|
||||
@PrimaryKey
|
||||
@Column
|
||||
public name: string;
|
||||
}
|
||||
|
||||
@Table
|
||||
export class Grader extends Model {
|
||||
@PrimaryKey
|
||||
@Column
|
||||
public page_slug: string;
|
||||
|
||||
@PrimaryKey
|
||||
@Column
|
||||
public exercise_name: string;
|
||||
|
||||
@PrimaryKey
|
||||
@Column
|
||||
public language: string;
|
||||
|
||||
@Column
|
||||
public style: string;
|
||||
|
||||
@Column(DataType.JSON)
|
||||
public props: any;
|
||||
}
|
||||
|
||||
export async function init(path: string): Promise<Sequelize> {
|
||||
let sequelize = new Sequelize(`sqlite:${path}`, {
|
||||
models: [Page, Exercise, Grader],
|
||||
});
|
||||
await sequelize.sync({ force: true });
|
||||
return sequelize;
|
||||
}
|
|
@ -4,8 +4,8 @@ import * as yaml from "js-yaml";
|
|||
import { plainToClass } from "class-transformer";
|
||||
import { validate } from "class-validator";
|
||||
|
||||
import { init, Page } from "./db";
|
||||
import { Page as PageConfig, Exercise as ExerciseConfig } from "./page";
|
||||
import { init, Page, Exercise, Grader } from "./db/page";
|
||||
import { Page as PageConfig, Exercise as ExerciseConfig, Grader as GraderConfig } from "./page";
|
||||
|
||||
// TODO: configure this thru cmdline or something later
|
||||
let materials_dir = "../material";
|
||||
|
@ -17,7 +17,6 @@ let SLUG_RE = /([A-Za-z\-\_]+)/;
|
|||
async function loadPageIntoDb(name: string): Promise<void> {
|
||||
let slug_match = name.match(SLUG_RE);
|
||||
let slug = slug_match[0];
|
||||
console.log("SLUG", slug);
|
||||
|
||||
// if this slug has already been loaded into the database, don't do anything
|
||||
let hasPage = await Page.count({ where: { slug }}) > 0;
|
||||
|
@ -33,18 +32,36 @@ async function loadPageIntoDb(name: string): Promise<void> {
|
|||
let page = new Page({
|
||||
slug,
|
||||
title: page_cfg.title,
|
||||
content: page_cfg.content,
|
||||
});
|
||||
await page.save();
|
||||
|
||||
// save exercises
|
||||
async function loadExerciseIntoDb(ex_cfg: ExerciseConfig): Promise<void> {
|
||||
console.log("pog", ex_cfg);
|
||||
let exercise = new Exercise({
|
||||
page_slug: slug,
|
||||
name: ex_cfg.name,
|
||||
});
|
||||
await exercise.save();
|
||||
|
||||
async function loadGraderIntoDb([language, grader_cfg]: [string, GraderConfig]): Promise<void> {
|
||||
let grader = new Grader({
|
||||
page_slug: slug,
|
||||
exercise_name: ex_cfg.name,
|
||||
language,
|
||||
style: grader_cfg.style,
|
||||
props: grader_cfg.props,
|
||||
});
|
||||
await grader.save();
|
||||
}
|
||||
|
||||
let graders = Object.entries(ex_cfg.graders);
|
||||
await Promise.all(graders.map(loadGraderIntoDb));
|
||||
}
|
||||
|
||||
let exercises = page_cfg.exercises;
|
||||
if (exercises != null) {
|
||||
await Promise.all(exercises
|
||||
.map(loadExerciseIntoDb));
|
||||
await Promise.all(exercises.map(loadExerciseIntoDb));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -3,14 +3,12 @@ export class Page {
|
|||
public type: string;
|
||||
|
||||
public summary?: string;
|
||||
|
||||
public content?: string;
|
||||
|
||||
public content: string;
|
||||
public exercises?: Exercise[];
|
||||
};
|
||||
|
||||
export class Exercise {
|
||||
public slug: string;
|
||||
public name: string;
|
||||
public description: string;
|
||||
public graders: Grader[];
|
||||
};
|
||||
|
|
|
@ -1,4 +1,6 @@
|
|||
title: Functional Programming Basics
|
||||
type: listing
|
||||
content: |
|
||||
This listing contains some of the basics of functional programming.
|
||||
|
||||
- [Functions](page://fp-function)
|
||||
|
|
|
@ -7,12 +7,13 @@ content: |
|
|||
output, but in functional programming, we can usually get around this either
|
||||
by using [tuples][1] or by [currying][2].
|
||||
|
||||
[1]: page://tuples
|
||||
[2]: page://currying
|
||||
[1]: page://fp-tuples
|
||||
[2]: page://fp-currying
|
||||
|
||||
exercises:
|
||||
|
||||
- name: doubleIt
|
||||
style: gradedProgram
|
||||
description: |
|
||||
Write a function called `doubleIt` that takes an integer and doubles it.
|
||||
|
||||
|
@ -28,3 +29,13 @@ exercises:
|
|||
(fun x -> assert ((doubleIt x) = (x * 2)))
|
||||
(List.init 100 (fun x -> x + 1));
|
||||
|
||||
- name: whichIsFunction
|
||||
style: multipleChoice
|
||||
description: |
|
||||
Which of the following can be described as a _function_?
|
||||
|
||||
graders:
|
||||
ocaml:
|
||||
style: multipleChoice
|
||||
props:
|
||||
foo: bar
|
||||
|
|
11
notes.txt
11
notes.txt
|
@ -34,3 +34,14 @@ ocaml student.cmo driver.ml
|
|||
|
||||
probably should have like $OCAMLCFLAGS in there to be able to customize each
|
||||
step as well
|
||||
|
||||
---
|
||||
|
||||
what are some good classes to start out with?
|
||||
|
||||
- functional programming
|
||||
- ctfs?
|
||||
- possibly a logic class for math
|
||||
- proof class
|
||||
- ML???? look into running it
|
||||
- can we do reading????????????
|
||||
|
|
|
@ -1,4 +1,9 @@
|
|||
{
|
||||
"scripts": {
|
||||
"compiledb": "cd compile-database && npm start",
|
||||
"webdev": "cd web && npm run dev"
|
||||
},
|
||||
|
||||
"devDependencies": {
|
||||
"compile-database": "file:compile-database",
|
||||
"web": "file:web"
|
||||
|
|
|
@ -26,5 +26,8 @@
|
|||
"tslib": "^2.0.0",
|
||||
"typescript": "^4.0.0"
|
||||
},
|
||||
"type": "module"
|
||||
"type": "module",
|
||||
"dependencies": {
|
||||
"sequelize-typescript": "^2.1.0"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,6 +4,11 @@
|
|||
<meta charset="utf-8" />
|
||||
<link rel="icon" href="/favicon.png" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Heebo:wght@300;400;700&display=swap" integrity="sha384-TIFtbbKQ3b73InoKF2MDgqFhPYGLQ1h2cnNtGUFVvvd/eRw94RvudHY4y+MMmbw4" crossorigin="anonymous">
|
||||
|
||||
%svelte.head%
|
||||
<style>html, body { margin: 0; paddding: 0; }</style>
|
||||
</head>
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
<script lang="ts">
|
||||
import QuizBox from "$lib/QuizBox.svelte";
|
||||
import MultipleChoice from "$lib/activity/MultipleChoice.svelte";
|
||||
</script>
|
||||
|
||||
<QuizBox />
|
||||
<MultipleChoice />
|
||||
|
|
|
@ -1,10 +0,0 @@
|
|||
export default class Question {
|
||||
public description: string;
|
||||
public choices: Choice[];
|
||||
public concepts: string[];
|
||||
};
|
||||
|
||||
export class Choice {
|
||||
public text: string;
|
||||
public correct: boolean;
|
||||
};
|
|
@ -1,7 +1,5 @@
|
|||
<script lang="ts">
|
||||
import type Question from "$lib/Question";
|
||||
|
||||
export let question: Question = {
|
||||
export let question = {
|
||||
description: `
|
||||
what is 1 + 1?
|
||||
`,
|
||||
|
@ -13,12 +11,15 @@
|
|||
],
|
||||
};
|
||||
|
||||
// State related variables
|
||||
let state = "ask";
|
||||
let currentChoice;
|
||||
let wasCorrect = false;
|
||||
|
||||
let choose = (index: number) => {
|
||||
state = "answer";
|
||||
wasCorrect = question.choices[index].correct;
|
||||
currentChoice = index;
|
||||
// state = "answer";
|
||||
// wasCorrect = question.choices[index].correct;
|
||||
};
|
||||
</script>
|
||||
|
||||
|
@ -33,6 +34,11 @@
|
|||
</li>
|
||||
{/each}
|
||||
</ul>
|
||||
|
||||
<div class="answer-buttons">
|
||||
<button>Not sure</button>
|
||||
<button>Sure</button>
|
||||
</div>
|
||||
{:else}
|
||||
<small>A:</small>
|
||||
{#if wasCorrect }
|
||||
|
@ -47,6 +53,7 @@
|
|||
.quiz-box {
|
||||
border: 1px solid gray;
|
||||
border-radius: 8px;
|
||||
padding: 8px;
|
||||
|
||||
.choices {
|
||||
display: flex;
|
||||
|
@ -63,5 +70,10 @@
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
.answer-buttons {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
}
|
||||
</style>
|
1
web/src/lib/db/index.ts
Normal file
1
web/src/lib/db/index.ts
Normal file
|
@ -0,0 +1 @@
|
|||
export {};
|
|
@ -22,7 +22,7 @@
|
|||
<div class="footer-block">
|
||||
<div class="footer-block-title">Eduproj</div>
|
||||
<ul class="list-reset">
|
||||
<li>Hellosus</li>
|
||||
<li><a href="https://git.mzhang.io/michael/eduproj" target="_blank">Source Code</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="footer-block">
|
||||
|
@ -53,7 +53,7 @@
|
|||
$footer-height: 180px;
|
||||
|
||||
#app {
|
||||
font-family: sans-serif;
|
||||
font-family: Heebo, sans-serif;
|
||||
position: relative;
|
||||
min-height: 100vh;
|
||||
|
||||
|
|
Loading…
Reference in a new issue