Skip to content

Commit

Permalink
more analysis
Browse files Browse the repository at this point in the history
  • Loading branch information
Entkenntnis committed Jan 25, 2023
1 parent f87ac55 commit 2bae0ac
Show file tree
Hide file tree
Showing 7 changed files with 224 additions and 38 deletions.
96 changes: 70 additions & 26 deletions components/Overview.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import { createRef, useEffect } from 'react'
import {
editCodeAndResetProgress,
forceRerender,
setPersist,
setShowImpressum,
setShowPrivacy,
} from '../lib/commands/mode'
Expand Down Expand Up @@ -84,9 +85,6 @@ export function Overview() {
<FaIcon icon={faPenToSquare} className="mr-1" />
Aufgaben-Editor
</a>
<span className="ml-8 cursor-pointer hover:underline font-bold">
Anmelden
</span>
</div>
{core.ws.ui.isAnalyze && (
<div className="bg-white px-16 pb-8 mt-4">
Expand All @@ -112,7 +110,8 @@ export function Overview() {
{core.ws.analyze.showEditor} mal Editor angezeigt,{' '}
{core.ws.analyze.showPlayground} mal Spielwiese,{' '}
{core.ws.analyze.showDemo} mal Demo,{' '}
{core.ws.analyze.showStructogram} mal Struktogramm
{core.ws.analyze.showStructogram} mal Struktogramm,{' '}
{core.ws.analyze.usePersist} mal Fortschrit gespeichert
</p>
<h2 className="mt-6 mb-4 text-lg">Bearbeitungen</h2>
{core.ws.analyze.customQuests.map((entry, i) => (
Expand Down Expand Up @@ -143,6 +142,16 @@ export function Overview() {
- {entry[1].count} mal gestartet
</p>
))}
<h2 className="mt-6 mb-4 text-lg">Zeiten (in Minuten)</h2>
<p className="mb-2">
Median: {median(core.ws.analyze.times)} Minuten
</p>
<p>{core.ws.analyze.times.join(', ')}</p>
<h2 className="mt-6 mb-4 text-lg">Anzahl gelöste Aufgaben</h2>
<p className="mb-2">
Median: {median(core.ws.analyze.solvedCount)}
</p>
<p>{core.ws.analyze.solvedCount.join(', ')}</p>
</div>
)}
<div className="mx-12 lg:mx-16 xl:mx-24 flex-auto overflow-hidden -mt-8">
Expand All @@ -163,29 +172,38 @@ export function Overview() {
)
)}
</div>
{isQuestDone(1) &&
!sessionStorage.getItem('robot_karol_online_hide_save_message') && (
<div className="text-sm text-right mr-4 mt-1 mb-3 text-gray-600">
Beim Schließen des Tabs wird dein Fortschritt zurückgesetzt. Eine
Speicherfunktion ist in Arbeit.{' '}
<button
onClick={() => {
sessionStorage.setItem(
'robot_karol_online_hide_save_message',
'1'
)
{!core.ws.ui.isAnalyze && (
<div className="text-sm text-right mr-4 mt-48 text-gray-600">
<label>
<input
type="checkbox"
checked={!!localStorage.getItem('karol_quest_beta_persist')}
onChange={(e) => {
setPersist(core, e.target.checked)
forceRerender(core)
}}
>
<FaIcon
icon={faTimes}
className="inline-block px-1 bg-white hover:bg-gray-100"
/>
</button>
</div>
)}
<div className="text-center mb-2 mt-10">
Version: Januar 2023 |{' '}
/>{' '}
Fortschritt dauerhaft speichern
</label>{' '}
|{' '}
<button
className="hover:underline"
onClick={() => {
const res = confirm('Fortschritt jetzt zurücksetzen?')
if (res) {
setPersist(core, false)
sessionStorage.clear()
forceRerender(core)
}
}}
>
zurücksetzen
</button>
</div>
)}

<div className="text-center mb-2">
Version: Februar 2023 |{' '}
<button
className="hover:underline"
onClick={() => {
Expand Down Expand Up @@ -252,6 +270,8 @@ export function Overview() {

const task = questData[index].tasks[0]

const times = quartiles(core.ws.analyze.questTimes[index] ?? [0])

return (
<div
className={clsx(
Expand Down Expand Up @@ -297,7 +317,7 @@ export function Overview() {
{core.ws.ui.isAnalyze &&
(() => {
const entry = core.ws.analyze.quests[index]
if (index == 1) {
if (index == 1 && entry) {
return <span>{entry.complete} Spieler*innen</span>
}
if (entry) {
Expand All @@ -313,6 +333,12 @@ export function Overview() {
return null
}
})()}
{core.ws.analyze.questTimes[index] && (
<div className="mt-2">
Zeiten: {times.max} / {times.q3} / <strong>{times.q2}</strong> /{' '}
{times.q1} / {times.min}
</div>
)}
<div className="overflow-hidden -mt-8">
<View
world={questDone ? task.target! : task.start}
Expand All @@ -334,3 +360,21 @@ export function Overview() {
)
}
}

function median(arr: number[]) {
const middle = Math.floor(arr.length / 2)
if (arr.length % 2 === 0) {
return (arr[middle - 1] + arr[middle]) / 2
} else {
return arr[middle]
}
}

function quartiles(arr: number[]) {
var max = arr[0]
var min = arr[arr.length - 1]
var q3 = arr[Math.floor((arr.length - 1) / 4)]
var q2 = arr[Math.floor((arr.length - 1) / 2)]
var q1 = arr[Math.floor(((arr.length - 1) * 3) / 4)]
return { min: min, q1: q1, q2: q2, q3: q3, max: max }
}
117 changes: 108 additions & 9 deletions lib/commands/init.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,8 @@ export async function initClient(core: Core) {

if (hash == '#ANALYZE' && window.location.hostname == 'localhost') {
try {
const password = prompt('Zugangspasswort:') ?? ''
const storedPW = sessionStorage.getItem('karol_stored_pw')
const password = storedPW ?? prompt('Zugangspasswort:') ?? ''
const response = await fetch(backend.analyzeEndpoint, {
method: 'POST',
body: new URLSearchParams({ password }),
Expand All @@ -44,15 +45,26 @@ export async function initClient(core: Core) {
event: string
createdAt: string
}[]
const cutoff = new Date(2023, 0, 21)
if (data.length > 0) {
sessionStorage.setItem('karol_stored_pw', password)
}
const cutoff = new Date(2023, 0, 26)
let count = 0
let showEditor = 0
let showPlayground = 0
let showDemo = 0
let showStructogram = 0
let usePersist = 0
let legacy: { [key: string]: { count: number } } = {}
let users: { [key: string]: { solved: string[] } } = {}
let users: {
[key: string]: { solved: string[]; firstDate: number; lastDate: number }
} = {}
const dedup: { [key: string]: boolean } = {}

const questEvents: {
[key: string]: { questId: string; isStart: boolean; ts: number }[]
} = {}

core.mutateWs((ws) => {
ws.ui.isAnalyze = true
ws.analyze.cutoff = cutoff.toLocaleString()
Expand All @@ -64,6 +76,48 @@ export async function initClient(core: Core) {
} = {}
for (const entry of data) {
if (isAfter(new Date(entry.createdAt), cutoff)) {
const ts = new Date(entry.createdAt).getTime()
if (!users[entry.userId]) {
users[entry.userId] = {
solved: [],
firstDate: ts,
lastDate: ts,
}
}
if (ts < users[entry.userId].firstDate) {
users[entry.userId].firstDate = ts
}
if (ts > users[entry.userId].lastDate) {
users[entry.userId].lastDate = ts
}
const startQuest = /start_quest_(.+)/.exec(entry.event)

if (startQuest) {
const id = startQuest[1]
if (!questEvents[entry.userId]) {
questEvents[entry.userId] = []
}
questEvents[entry.userId].push({
questId: id,
isStart: true,
ts,
})
}

const completeQuest = /^quest_complete_(.+)/.exec(entry.event)

if (completeQuest) {
const id = completeQuest[1]
if (!questEvents[entry.userId]) {
questEvents[entry.userId] = []
}
questEvents[entry.userId].push({
questId: id,
isStart: false,
ts,
})
}

const key = entry.userId + entry.event
if (dedup[key]) {
continue
Expand All @@ -87,6 +141,10 @@ export async function initClient(core: Core) {
showStructogram++
continue
}
if (entry.event == 'persist_progress') {
usePersist++
continue
}
const publish = /publish_custom_quest_(.+)/.exec(entry.event)
if (publish) {
ws.analyze.published.push({
Expand Down Expand Up @@ -115,25 +173,22 @@ export async function initClient(core: Core) {
customStuff[id].complete++
continue
}
const startQuest = /start_quest_(.+)/.exec(entry.event)
if (startQuest) {
const id = startQuest[1]
if (!quests[id]) {
quests[id] = { start: 0, complete: 0 }
}
quests[id].start++
const key = entry.userId + id

continue
}
const completeQuest = /quest_complete_(.+)/.exec(entry.event)
if (completeQuest) {
const id = completeQuest[1]
if (!quests[id]) {
quests[id] = { start: 0, complete: 0 }
}
quests[id].complete++
if (!users[entry.userId]) {
users[entry.userId] = { solved: [] }
}
users[entry.userId].solved.push(id)
continue
}
Expand Down Expand Up @@ -162,9 +217,26 @@ export async function initClient(core: Core) {
ws.analyze.showPlayground = showPlayground
ws.analyze.showDemo = showDemo
ws.analyze.showStructogram = showStructogram
ws.analyze.usePersist = usePersist
ws.analyze.legacy = legacy
ws.analyze.users = users

const times = []
const solvedCount = []

for (const id in users) {
if (users[id].solved.length > 0) {
const span = users[id].lastDate - users[id].firstDate
times.push(Math.round(span / 1000 / 60))
solvedCount.push(users[id].solved.length)
}
}
times.sort((a, b) => b - a)
solvedCount.sort((a, b) => b - a)

ws.analyze.times = times
ws.analyze.solvedCount = solvedCount

for (const index in questData) {
let reachableCount = 0

Expand All @@ -180,7 +252,34 @@ export async function initClient(core: Core) {
ws.analyze.reachable[index] = reachableCount
}

console.log(users)
// console.log(questEvents)
const questTimes: { [key: string]: number[] } = {}

for (const userId in questEvents) {
const solved: string[] = []
const lastStartedTime: { [key: string]: number } = {}
for (const event of questEvents[userId]) {
if (event.isStart) {
lastStartedTime[event.questId] = event.ts
} else {
if (solved.includes(event.questId)) continue
if (!questTimes[event.questId]) {
questTimes[event.questId] = []
}
questTimes[event.questId].push(
Math.round(
(event.ts - lastStartedTime[event.questId]) / 1000 / 60
)
)
solved.push(event.questId)
}
}
}
for (const questId in questTimes) {
questTimes[questId].sort((a, b) => b - a)
}
ws.analyze.questTimes = questTimes
// console.log(questTimes)
})
} catch (e) {}
}
Expand Down
25 changes: 25 additions & 0 deletions lib/commands/mode.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
import { uiPosition } from 'blockly'
import { questData } from '../data/quests'
import { getQuestSessionData } from '../helper/session'
import { sliderToDelay } from '../helper/speedSlider'
import { submit_event } from '../helper/submit'
import { Core } from '../state/core'

export function setMode(core: Core, mode: Core['ws']['settings']['mode']) {
Expand Down Expand Up @@ -125,3 +128,25 @@ export function forceRerender(core: Core) {
ui.renderCounter++
})
}

export function setPersist(core: Core, val: boolean) {
if (val) {
submit_event('persist_progress', core)
}
if (val) {
localStorage.setItem('karol_quest_beta_persist', '1')
} else {
localStorage.removeItem('karol_quest_beta_persist')
}
for (const id in questData) {
const qd = getQuestSessionData(parseInt(id))
if (qd) {
if (val) {
localStorage.setItem(`karol_quest_beta_${id}`, JSON.stringify(qd))
} else {
localStorage.removeItem(`karol_quest_beta_${id}`)
sessionStorage.setItem(`karol_quest_beta_${id}`, JSON.stringify(qd))
}
}
}
}
6 changes: 6 additions & 0 deletions lib/commands/quest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,12 @@ export function storeQuestToSession(core: Core) {
completed:
core.ws.ui.controlBarShowFinishQuest || core.ws.ui.isAlreadyCompleted,
}
if (!!localStorage.getItem('karol_quest_beta_persist')) {
localStorage.setItem(
`karol_quest_beta_${core.ws.quest.id}`,
JSON.stringify(data)
)
}
sessionStorage.setItem(
`karol_quest_beta_${core.ws.quest.id}`,
JSON.stringify(data)
Expand Down
Loading

0 comments on commit 2bae0ac

Please sign in to comment.