Skip to content

Commit

Permalink
fix(frontend): Task to Markdown Conversion (#442)
Browse files Browse the repository at this point in the history
* fix(frontend): Correctly convert tasks to markdown

Closes: #441

* fix(frontend): Don't collapse tasks by default

* test(frontend): Add test for parsing task lists
  • Loading branch information
Clashsoft authored Dec 10, 2024
1 parent 20aad56 commit f848dd2
Show file tree
Hide file tree
Showing 2 changed files with 59 additions and 7 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import {TestBed} from '@angular/core/testing';
import {TaskMarkdownService} from './task-markdown.service';
import Task from '../../model/task';
import {TaskService} from '../../services/task.service';

describe(TaskMarkdownService.name, () => {
let md: TaskMarkdownService;

beforeEach(() => {
TestBed.configureTestingModule({
providers: [TaskService, TaskMarkdownService],
});
md = TestBed.inject(TaskMarkdownService);
});

const tasks: Task[] = [
{
_id: 'A', description: 'A', points: 1, children: [
{_id: 'A.1', description: 'A.1', points: 2, children: []},
],
},
// See https://github.com/fujaba/fulib.org/issues/441.
// This must output as a ## headline.
{_id: 'B', description: 'B', points: -1, children: []},
// And to avoid C becoming a child of B, it must also output as a ## headline.
{_id: 'C', description: 'C', points: 3, children: []},
];
const markdown = `\
## A (1P)<!--{"_id":"A"}-->
- A.1 (2P)<!--{"_id":"A.1"}-->
## B (-1P)<!--{"_id":"B"}-->
## C (3P)<!--{"_id":"C"}-->
`;

it('should render markdown', () => {
const actual = md.renderTasks(tasks);
expect(actual).toBe(markdown);
});

it('should parse markdown', () => {
const actual = md.parseTasks(markdown);
expect(actual).toEqual(tasks);
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@ export class TaskMarkdownService {
}

parseTasks(markdown: string): Task[] {
// # Assignment 1 (xP/100P)
// ## Task 1 (xP/30P)
// # Assignment 1 (100P)
// ## Task 1 (30P)
// - Something wrong (-1P)
const taskStack: Task[][] = [[]];
for (const line of markdown.split('\n')) {
Expand All @@ -28,7 +28,6 @@ export class TaskMarkdownService {
_id: _id || this.taskService.generateID(),
points: +points,
children: [],
collapsed: true,
};
switch (prefix) {
case '-':
Expand All @@ -47,20 +46,29 @@ export class TaskMarkdownService {
}

renderTasks(tasks: Task[], depth = 0) {
return tasks.map(t => this.renderTask(t, depth)).join('');
let result = '';
let lastTaskWasHeadline = false;
for (const task of tasks) {
const line = this.renderTask(task, depth, lastTaskWasHeadline);
result += line;
lastTaskWasHeadline = line.startsWith('#');
}
return result;
}

private renderTask(task: Task, depth: number) {
private renderTask(task: Task, depth: number, asHeadline: boolean) {
const {children, deleted, description, points, ...rest} = task;
if (deleted) {
return '';
}
const extra = JSON.stringify(rest);
if (points < 0 || !children || children.length === 0) {
// If the previous task had children, we need to write this as a new headline.
// Otherwise, it would end up as a subtask of the previous task without a way to distinguish it.
if (!asHeadline && (points < 0 || !children || children.length === 0)) {
return `- ${description} (${points}P)<!--${extra}-->\n`;
}
const childrenMd = this.renderTasks(children, depth + 1);
const headlinePrefix = '#'.repeat(depth + 2);
return `${headlinePrefix} ${description} (x/${points}P)<!--${extra}-->\n${childrenMd}`;
return `${headlinePrefix} ${description} (${points}P)<!--${extra}-->\n${childrenMd}`;
}
}

0 comments on commit f848dd2

Please sign in to comment.