Skip to content

Commit

Permalink
Fixing some UI bugs and display wrong regexpes nicer
Browse files Browse the repository at this point in the history
  • Loading branch information
ineiti committed May 23, 2024
1 parent ecd5f4c commit 46481d2
Show file tree
Hide file tree
Showing 7 changed files with 137 additions and 34 deletions.
10 changes: 7 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -170,18 +170,22 @@ This software is licensed under AGPL-3.0 or later, at your convenience.
- stats
- when all values are 0, it doesn't show nicely
- features
- add other user to owner of course
- show stats for 7 / 14 / 31 days
- get all quizzes from a github repo
- enter the name of a github / gitlab
- compatibility with Delibay - probably difficult
- add "delete" button to clean students
- modes
- exam mode: only once the admin switch is flicked will the students see if they answered correctly
- live mode: after every question, students see if they answered correctly
- updatable: students can update their questions
- groups of students for race

# CHANGELOG

2024-05-23:
- regroup wrong answers in regexps
- when changing quiz for dojo, got the previous quiz in the dojo multiple times
- twice quiz name in corrections

2024-05-19:
- add statistics
- show statistics
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/app/components/quiz/quiz.component.html
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ <h3 [innerHTML]="answer.question.title"></h3>
<mat-form-field style="width: 100%;">
<mat-label>Answer</mat-label>
<input matInput [(ngModel)]="answer.choice.regexp" (input)="updateRegexp()" [class]="resultClasses[0]"
[disabled]="corrections">
[disabled]="corrections" id="quizInput">
</mat-form-field>
<div *ngIf="corrections">
@for (match of answer.question.options.regexp!.match; track $index ) {
Expand Down
10 changes: 5 additions & 5 deletions frontend/src/app/course/corrections/corrections.component.html
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
<div *ngIf="course.state.isCorrections() && quiz && results">
<h2>{{quiz.title}}</h2>
<div *ngIf="results.chosen.length === 0">
<h3>No results available</h3>
</div>
Expand Down Expand Up @@ -68,12 +67,13 @@ <h3 [innerHTML]="question.title"></h3>
style="margin: auto 0 auto 1em; font-size: 120%;">{{results.chosen[sorted[qIndex][0]][$index]}}</span>
</div>
}
@for (wrong of results.texts[sorted[qIndex][0]]; track $index) {
@for (wrong of wrongRegexps; track $index) {
<div style="display: flex;">
<mat-list-option togglePosition="before" class="resultWrong" style="width: 50%">
<span class="option" [innerHTML]="wrong"></span>
<mat-list-option togglePosition="before" class="resultWrong"
[style.width]="resultWidth[$index + question.options.regexp.match.length]">
<span class="option" [innerHTML]="wrong[0]"></span>
</mat-list-option>
<span class="option" style="margin: auto 0 auto 1em; font-size: 120%;">1</span>
<span class="option" style="margin: auto 0 auto 1em; font-size: 120%;">{{ wrong[1] }}</span>
</div>
}
</mat-selection-list>
Expand Down
12 changes: 11 additions & 1 deletion frontend/src/app/course/corrections/corrections.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ export class CorrectionsComponent {
results!: ResultsSummary;
qIndex = 0;
sorted: number[][] = [];
wrongRegexps: [string, number][] = [];

constructor(private livequiz: LivequizStorageService, private storage: StorageService,
private bcs: BreadcrumbService, private stats: StatsService) {
Expand Down Expand Up @@ -92,22 +93,31 @@ export class CorrectionsComponent {
updateClasses() {
for (let question = 0; question < this.quiz.questions.length; question++) {
const score = Math.round(this.sorted[question][1] * 8);
console.log(question, score);
this.tileClasses[question] = "questionTile" +
(question % 2 === 1 ? " questionTileOdd" : "") +
` questionTileColor${score}`;
}
this.tileClasses[this.qIndex] += " questionTileChosen";

this.wrongRegexps = [];
const question = this.quiz.questions[this.sorted[this.qIndex][0]];
if (question.options.multi !== undefined) {
this.resultClasses = question.options.multi.correct.map((_) => 'resultCorrect')
.concat(question.options.multi!.wrong.map((_) => 'resultWrong'));
} else {
this.resultClasses = question.options.regexp!.match.map((_) => 'resultCorrect')
this.resultClasses = question.options.regexp!.match.map((_) => 'resultCorrect');
const wrongAnswers = this.results.texts[this.sorted[this.qIndex][0]];
[...new Set(wrongAnswers)].map((wrong, i) =>
this.wrongRegexps[i] = [wrong, wrongAnswers.filter((ans) => ans === wrong).length]);
}

if (this.results.chosen.length > 0) {
this.resultWidth = this.results.chosen[this.sorted[this.qIndex][0]]
.map((s) => `${Math.round(s / this.users.length * 50) + 50}%`);
if (this.wrongRegexps.length > 0) {
this.resultWidth = this.resultWidth.concat(this.wrongRegexps.map(([_, n]) => `${Math.round(n / this.users.length * 50) + 50}%`));
}
}
}

Expand Down
30 changes: 20 additions & 10 deletions frontend/src/app/course/course-manage/course-manage.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,14 +33,7 @@ export class CourseManageComponent {
private stats: StatsService) { }

async ngOnChanges() {
this.quizzes = [];
for (const id of this.course.quizIds) {
this.quizzes.push(await this.storage.getNomad(id, new Quiz()));
}
if (this.course.state.state !== CourseStateEnum.Idle) {
const dojo = await this.livequiz.getDojo(this.course.state.getDojoID());
this.quiz = await this.livequiz.getQuiz(dojo.quizId);
}
await this.updateQuizzes();
if (!this.isStudent()) {
this.user.courses.set(this.course.id.toHex(), this.course.name);
this.stats.add(StatsService.course_join);
Expand All @@ -49,6 +42,21 @@ export class CourseManageComponent {
this.user.addCourse(this.course);
}
this.user.update();
await this.updateDojo();
}

async updateDojo(){
if (this.course.state.state !== CourseStateEnum.Idle) {
const dojo = await this.livequiz.getDojo(this.course.state.getDojoID());
this.quiz = await this.livequiz.getQuiz(dojo.quizId);
}
}

async updateQuizzes(){
this.quizzes = [];
for (const id of this.course.quizIds) {
this.quizzes.push(await this.storage.getNomad(id, new Quiz()));
}
}

isAdmin(): boolean {
Expand All @@ -62,6 +70,7 @@ export class CourseManageComponent {
async dojoQuiz(id: QuizID) {
this.stats.add(StatsService.dojo_start);
await this.livequiz.setDojoQuiz(this.course.id, id);
await this.updateDojo();
}

async dojoCorrections() {
Expand All @@ -71,6 +80,7 @@ export class CourseManageComponent {
async dojoIdle() {
this.stats.add(StatsService.dojo_stop);
await this.livequiz.setDojoIdle(this.course.id);
await this.updateDojo();
}

async deleteQuiz(id: QuizID) {
Expand All @@ -83,7 +93,7 @@ export class CourseManageComponent {
this.course.state.state = CourseStateEnum.Idle;
}
this.stats.add(StatsService.quiz_delete);
this.ngOnChanges();
await this.updateDojo();
}
}

Expand All @@ -103,7 +113,7 @@ export class CourseManageComponent {
this.course.quizIds.push(q.id);
this.stats.add(StatsService.quiz_create_upload);
}
this.ngOnChanges();
this.updateQuizzes();
} catch (e) {
await ModalModule.openOKCancel(this.dialog, 'Error',
`While reading quiz: ${e}`
Expand Down
14 changes: 10 additions & 4 deletions frontend/src/test/livequiz.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,12 +38,18 @@ export class Livequiz {
return this.by(By.xpath(xp));
}

text(t: string): WEP {
return this.xpath(`//*[contains(text(), "${t}")]`);
text(t: string, occurence = 1): WEP {
return this.xpath(`(//*[contains(text(), "${t}")])[${occurence}]`);
}

async click(t: string) {
await this.text(t).click();
async click(...texts: string[]) {
for (const text of texts) {
await this.text(text).click();
}
}

async find(t: string){
await this.text(t).find();
}
}
export class WEP {
Expand Down
93 changes: 83 additions & 10 deletions frontend/src/test/newUser.spec.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { Livequiz } from './livequiz';
import { readFileSync } from 'fs';

describe("Logging in", () => {
describe("E2E tests", () => {
it("Correctly identifies 2 users", async () => {
const admin = await Livequiz.reset();
await admin.id('cname').sendKeys('Testing');
Expand All @@ -22,6 +22,7 @@ describe("Logging in", () => {
await user1.id("userName").sendKeys("user1");

let user2 = await Livequiz.init(courseUrl);
await Livequiz.wait(200);
await user2.id("userName").clear();
await user2.id("userName").sendKeys("user2");
await user2.click("LiveQuiz");
Expand Down Expand Up @@ -51,22 +52,94 @@ describe("Logging in", () => {

await Livequiz.wait(100);
admin.browser.quit();
});
}, 10000);

it("Stores stats", async () => {
const admin = await Livequiz.reset();

await Livequiz.wait(200);
let user1 = await Livequiz.init();
await user1.id('cname').sendKeys('Testing');
await user1.css('button').click();
await user1.click('Testing');
await user1.click('Create Quiz');
await user1.click('Save');
await user1.click('Start Quiz');
await user1.click('Enter Dojo');
await user1.click('Add a course', 'Testing', 'Create Quiz', 'Save', 'Start Quiz', 'Enter Dojo');

await Livequiz.wait(200);
await admin.click("Stats");
})
})

user1.browser.quit();
admin.browser.quit();
});

it("Corrections", async () => {
// Verify that the fully-wrong answer comes before the half-correct, which comes
// before the fully-correct one.
const admin = await Livequiz.reset();
await admin.id('cname').sendKeys('Testing');
await admin.click('Add a course', 'Testing');
await admin.id('fileInput').sendKeys(`${__dirname}/quiz1.md`);
await admin.click('Start Quiz', 'Enter Dojo', 'wrong', 'Next', 'correct', 'Next');
await admin.id('quizInput').sendKeys('one');
await admin.click('Testing', 'Start Corrections');

await admin.find("Question 1");
await admin.click('Next');
await admin.find("Question 2");
await admin.click('Next');
await admin.find("Question 3");
admin.browser.quit();
});

it("Change dojo", async () => {
// Create two quizzes and change the quiz in the dojo
const admin = await Livequiz.reset();
await admin.id('cname').sendKeys('Testing');
await admin.click('Add a course', 'Testing');
await admin.id('fileInput').sendKeys(`${__dirname}/quiz1.md`);
await admin.click('Create Quiz', 'Save');
await admin.click('Start Quiz');
await admin.find('Quiz in Dojo: Test Quiz');
await admin.text('Start Quiz', 2).click();
await admin.find('Quiz in Dojo: Title of Quiz');
admin.browser.quit();
});

it("Sorts wrong regexps", async () => {
// Create two quizzes and change the quiz in the dojo
const admin = await Livequiz.reset();
await admin.id('cname').sendKeys('Testing');
await admin.click('Add a course', 'Testing');
await admin.id('fileInput').sendKeys(`${__dirname}/quiz1.md`);
await admin.click('Start Quiz');
const courseUrl = await admin.browser.getCurrentUrl() + "/dojo";

const user1 = await Livequiz.init(courseUrl);
const user2 = await Livequiz.init(courseUrl);
const user3 = await Livequiz.init(courseUrl);
await Livequiz.wait(200);
await user1.click('Next', 'Next');
await user1.id('quizInput').sendKeys(' three!!');
await user2.click('Next', 'Next');
await user2.id('quizInput').sendKeys('four');
await user3.click('Next', 'Next');
await user3.id('quizInput').sendKeys('three!!');

await Livequiz.wait(300);
await admin.click("Corrections", "Next", "Next");
await admin.text('three').find();
await throwError(() => admin.text('three!!', 2).find());
await admin.text('four').find();

await admin.browser.quit();
await user1.browser.quit();
await user2.browser.quit();
await user3.browser.quit();
}, 10000)
});

async function throwError(f: () => Promise<any>): Promise<void> {
try {
await f();
} catch (_) {
return;
}
return Promise.reject("Didn't throw error");
}

0 comments on commit 46481d2

Please sign in to comment.