Browse Source

Display task status in TaskDisplay

mj-deploy
Standa Lukeš 4 years ago
parent
commit
af57e530f8
  1. 15
      frontend/src/Graph.svelte
  2. 16
      frontend/src/GraphNode.svelte
  3. 34
      frontend/src/TaskDisplay.svelte
  4. 2
      frontend/src/TasksLoader.svelte
  5. 12
      frontend/src/ksp-task-grabber.ts
  6. 29
      frontend/src/task-status-cache.ts

15
frontend/src/Graph.svelte

@ -6,6 +6,7 @@
import type { TasksFile, TaskDescriptor } from "./task-loader"; import type { TasksFile, TaskDescriptor } from "./task-loader";
import { createNodesAndEdges } from "./graph-types"; import { createNodesAndEdges } from "./graph-types";
import { taskForce } from "./task-force"; import { taskForce } from "./task-force";
import { taskStatuses } from './task-status-cache'
import { grabTaskStates, isLoggedIn } from "./ksp-task-grabber"; import { grabTaskStates, isLoggedIn } from "./ksp-task-grabber";
import type { TaskStatus } from "./ksp-task-grabber" import type { TaskStatus } from "./ksp-task-grabber"
@ -20,7 +21,6 @@
let clientHeight: number; let clientHeight: number;
let clientWidth: number; let clientWidth: number;
let svgElement: SVGElement; let svgElement: SVGElement;
let taskStatuses = new Map<string, TaskStatus>();
// this prevents svelte from updating nodes and edges // this prevents svelte from updating nodes and edges
// when we update nodes and edges // when we update nodes and edges
@ -94,17 +94,6 @@
} }
if (isLoggedIn()) {
const cachedTaskStatuses = localStorage.getItem("taskStatuses-cache")
if (cachedTaskStatuses) {
try { taskStatuses = new Map(JSON.parse(cachedTaskStatuses)) } catch(e) { console.warn(e) }
}
grabTaskStates(tasks.tasks.map(t => t.id)).then(t => {
taskStatuses = t
localStorage.setItem("taskStatuses-cache", JSON.stringify(Array.from(t.entries())))
})
}
// start simulation and center view on create // start simulation and center view on create
onMount(() => { onMount(() => {
// set center of the SVG at (0,0) // set center of the SVG at (0,0)
@ -155,7 +144,7 @@
on:click={nodeClick(task.task)} on:click={nodeClick(task.task)}
on:hoveringChange={nodeHover(task.task)} on:hoveringChange={nodeHover(task.task)}
on:positionChange={() => { tasks = tasks; }} on:positionChange={() => { tasks = tasks; }}
status={taskStatuses.get(task.id)} status={$taskStatuses.get(task.id)}
draggingEnabled={nodeDraggingEnabled} draggingEnabled={nodeDraggingEnabled}
on:dblclick={nodeDoubleClick(task.task)} /> on:dblclick={nodeDoubleClick(task.task)} />
{/each} {/each}

16
frontend/src/GraphNode.svelte

@ -83,11 +83,23 @@
fill: #69b3a2 fill: #69b3a2
} }
.submitted ellipse { .submitted ellipse {
fill: red fill: red; /* TODO */
}
.solved ellipse {
fill: green; /* TODO */
} }
</style> </style>
<g on:mouseenter={enter} on:mouseleave={leave} on:click={click} on:mousedown={dragStart} on:mouseup={dragStop} on:mousemove={drag} class={status && status.submitted ? "submitted" : ""} on:dblclick={dblclick}> <g on:mouseenter={enter}
on:mouseleave={leave}
on:click={click}
on:mousedown={dragStart}
on:mouseup={dragStop}
on:mousemove={drag}
on:dblclick={dblclick}
class={status == null ? "" :
status.solved ? "solved" :
status.submitted ? "submitted" : ""}>
<ellipse rx={ellipse_rx} ry={20} {cx} {cy} /> <ellipse rx={ellipse_rx} ry={20} {cx} {cy} />
<text <text
bind:this={text_element} bind:this={text_element}

34
frontend/src/TaskDisplay.svelte

@ -1,7 +1,9 @@
<script type="ts"> <script type="ts">
import { grabAssignment, grabSolution } from "./ksp-task-grabber"; import { grabAssignment, grabSolution } from "./ksp-task-grabber";
import type { TaskStatus } from "./ksp-task-grabber";
import { nonNull } from './helpers' import { nonNull } from './helpers'
import App from "./App.svelte"; import App from "./App.svelte";
import { taskStatuses } from "./task-status-cache";
export let taskId: string | null | undefined export let taskId: string | null | undefined
export let showSolution: boolean = false export let showSolution: boolean = false
@ -9,11 +11,26 @@ import App from "./App.svelte";
taskId taskId
showSolution = false showSolution = false
} }
let status: TaskStatus | undefined
$: if (taskId) status = $taskStatuses.get(taskId)
</script> </script>
<style> <style>
div { div {
text-align: justify; text-align: justify;
} }
.header {
display: flex;
width: 100%;
flex-direction: row;
}
.header div {
flex-grow: 1;
}
.header .status {
text-align: right;
font-style: italic;
}
</style> </style>
<div> <div>
@ -21,7 +38,22 @@ import App from "./App.svelte";
{#await grabAssignment(nonNull(taskId))} {#await grabAssignment(nonNull(taskId))}
Načítám úlohu Načítám úlohu
{:then task} {:then task}
{@html task.titleHtml} <div class="header">
<div class="title"><h3>{task.name}</h3></div>
<div class="status">
<p>
{task.id} | {task.points} bodů
{#if status && status.submitted}
{#if nonNull(status).solved}
| Vyřešeno 🥳
{:else}
| odevzdáno za {nonNull(status).points} bod{ "ů yyy"[nonNull(status).points] ?? "ů" }
{/if}
{/if}
</p>
</div>
</div>
{@html task.description} {@html task.description}
<div class="clearfloat" /> <div class="clearfloat" />

2
frontend/src/TasksLoader.svelte

@ -1,5 +1,6 @@
<script lang="ts"> <script lang="ts">
import type { TasksFile } from "./task-loader"; import type { TasksFile } from "./task-loader";
import { refresh } from './task-status-cache'
export let promise: Promise<TasksFile>; export let promise: Promise<TasksFile>;
@ -8,6 +9,7 @@
let err: any | null = null; let err: any | null = null;
promise.then( promise.then(
(d) => { (d) => {
refresh(d.tasks.map(t => t.id))
data = d; data = d;
}, },
(e) => { (e) => {

12
frontend/src/ksp-task-grabber.ts

@ -19,6 +19,7 @@ export type TaskStatus = {
id: string id: string
name: string name: string
submitted: boolean submitted: boolean
solved: boolean
points: number points: number
maxPoints: number maxPoints: number
type: string type: string
@ -80,15 +81,17 @@ function parseTask(startElementId: string, doc: HTMLDocument): TaskAssignmentDat
let e = titleElement let e = titleElement
const titleMatch = /(\d-Z?\d+-\d+) (.*?)( \((\d+) bod.*\))?/.exec(e.innerText.trim()) const titleMatch = /^(\d+-Z?\d+-\d+) (.*?)( \((\d+) bod.*\))?$/.exec(e.innerText.trim())
if (!titleMatch) { if (!titleMatch) {
var [_, id, name, __, points] = ["", startElementId, "Neznámé jméno úlohy", "", ""] var [_, id, name, __, points] = ["", startElementId, "Neznámé jméno úlohy", "", ""]
} else { } else {
var [_, id, name, __, points] = titleMatch var [_, id, name, __, points] = titleMatch
} }
e = e.nextElementSibling as HTMLElement
while (e.nextElementSibling && while (e.nextElementSibling &&
e.nextElementSibling?.tagName.toLowerCase() == "hr") e.tagName.toLowerCase() == "hr")
e = e.nextElementSibling as HTMLElement e = e.nextElementSibling as HTMLElement
while (!e.classList.contains("story") && while (!e.classList.contains("story") &&
@ -124,11 +127,12 @@ function parseTaskStatuses(doc: HTMLDocument): TaskStatus[] {
const type = r.cells[1].innerText.trim() const type = r.cells[1].innerText.trim()
const name = r.cells[2].innerText.trim() const name = r.cells[2].innerText.trim()
const pointsStr = r.cells[4].innerText.trim() const pointsStr = r.cells[4].innerText.trim()
const pointsMatch = /(–|\d+) *\/ *(\d+)/.exec(pointsStr) const pointsMatch = /((–|\.|\d)+) *\/ *(\d+)/.exec(pointsStr)
if (!pointsMatch) throw new Error() if (!pointsMatch) throw new Error()
const points = +pointsMatch[1] const points = +pointsMatch[1]
const maxPoints = +pointsMatch[2] const maxPoints = +pointsMatch[2]
return { id, name, submitted, type, points, maxPoints } const solved = r.classList.contains("zs-submitted")
return { id, name, submitted, type, points, maxPoints, solved }
}) })
} }

29
frontend/src/task-status-cache.ts

@ -0,0 +1,29 @@
import { grabTaskStates, isLoggedIn} from "./ksp-task-grabber"
import type { TaskStatus } from "./ksp-task-grabber"
import { readable } from 'svelte/store';
let writeFn: (value: Map<string, TaskStatus>) => void = null!;
let lastVal = new Map<string, TaskStatus>()
if (isLoggedIn()) {
const cachedTaskStatuses = localStorage.getItem("taskStatuses-cache")
if (cachedTaskStatuses) {
lastVal = new Map(JSON.parse(cachedTaskStatuses))
}
}
export const taskStatuses = readable(lastVal, write => {
writeFn = v => { lastVal = v; write(v); }
})
console.log(isLoggedIn())
export function refresh(ids: string[]) {
if (!isLoggedIn()) return;
grabTaskStates(ids).then(t => {
const tt = Array.from(t.entries())
writeFn(new Map(Array.from(lastVal.entries()).concat(tt)))
localStorage.setItem("taskStatuses-cache", JSON.stringify(tt))
})
}