mirror of
https://github.com/jorenchik/mdemory.git
synced 2026-03-22 00:26:21 +00:00
2035 lines
61 KiB
Plaintext
2035 lines
61 KiB
Plaintext
#import "layout.typst": project, indent-par
|
||
#import "@preview/i-figured:0.1.0"
|
||
#import "@preview/tablex:0.0.6": tablex, rowspanx, colspanx, cellx
|
||
#import "utils.typst": *
|
||
|
||
#show: project.with(
|
||
university: "Latvijas Universitāte",
|
||
faculty: "Eksakto zinātņu un tehnoloģiju fakultāte",
|
||
title: [Atmiņas kartīšu lietotne ar domēna specifiskas valodas izmantošanu\ Kvalifikācijas darbs],
|
||
authors: ( "Jorens Štekeļs, js21283", ),
|
||
advisor: "prof. Dr. sc. comp. Laila Niedrīte",
|
||
date: "Rīga 2025",
|
||
)
|
||
|
||
#set heading(numbering: none)
|
||
|
||
#pagebreak(weak: true)
|
||
= Apzīmējumu saraksts
|
||
|
||
#par(
|
||
first-line-indent: 0cm,
|
||
[
|
||
*Iezīmēšanas valoda (angl. markup)* -- dokumentu formatēšanas sistēma, kas
|
||
izmanto birkas un citus simbolus, lai noteiktu dokumenta struktūru un
|
||
izkārtojumu. To izmanto, lai organizētu informāciju un kontrolētu dokumenta
|
||
izskatu;
|
||
|
||
*Markdown* -- vienkārša un populāra iezīmēšanas valoda, kas galvenokārt
|
||
paredzēta teksta dokumentu (īpaši tīmekļa lapu) formatēšanai;
|
||
|
||
*Transpilēšana* -- teksta pārvēršana programmas datu struktūrās vai
|
||
objektos, kas paredzēta kā starpposms starp dažādiem programmēšanas valodas
|
||
līmeņiem;
|
||
|
||
*Detranspilēšana* -- programmas objektu atgriešana atpakaļ noteiktā teksta
|
||
formātā vai valodā, saglabājot objekta struktūru un īpašības;
|
||
|
||
*Norāde* -- mainīgais vai datu struktūra, kas satur citu objektu vai
|
||
vērtību adresi, ļaujot piekļūt šiem objektiem netieši;
|
||
|
||
*Nulles objekts* -- objekts, kas norāda uz tukšu vai neinicializētu
|
||
stāvokli, piemēram, lai apzīmētu, ka objekts nesatur derīgu vērtību vai ir
|
||
tukšs;
|
||
|
||
*Vienkāršais teksts* -- teksta dati, kas attēlo simbolus bez grafiskām vai
|
||
citām struktūrām, piemēram, attēliem vai formatētiem elementiem, un ir
|
||
viegli lasāms jebkurā teksta redaktorā;
|
||
|
||
*Metadati* -- dati, kas apraksta citus datus, sniedzot informāciju par to
|
||
īpašībām, piemēram, saturu, izcelsmi un struktūru;
|
||
|
||
*CSV* -- formāts, kas satur vērtības, kas ir atdalīti ar komatiem;
|
||
|
||
*Parsēšana* -- TODO;
|
||
|
||
*Buferis* -- TODO;
|
||
|
||
],
|
||
)
|
||
|
||
#pagebreak(weak: true)
|
||
|
||
= Ievads
|
||
|
||
== Nolūks
|
||
|
||
#indent-par([
|
||
Šī darba nolūks ir specificēt lietotnes programmatūras prasības un
|
||
aprakstīt tās projektējumu, kas tiek aprakstīta un izstrādāta
|
||
kvalifikācijas darba ietvaros.
|
||
])
|
||
|
||
== Darbības sfēra
|
||
|
||
#indent-par([
|
||
// Kas ir atmiņas kartes
|
||
Atmiņas kartītes (angl. flashcards) ir izplatīts veids, kā skolēni,
|
||
studenti un citi cilvēki, kas mācās vai studē, iegaumē materiālu. Šādu
|
||
kartīšu pamatā parasti ir priekša un aizmugure. Priekšā atrodas jautājums
|
||
vai uzdevums vai cita informācija, kas uzstāda mentālu vai praktisku
|
||
uzdevumu, iespējams iekļaujot arī mājieni. Aizmugurē atrodas atbildes
|
||
teksts, kas ar sevi atbild vai parāda pareizu atbildi uz priekšā uzstādītu
|
||
jautājumu vai uzdevumu.
|
||
])
|
||
|
||
// Mācīšanās process un spaced-repetition
|
||
Mācīšanās nolūkiem tiek veidotas vairākas atmiņas kartītes uz līdzīgu tēmu,
|
||
veidojas kartīšu kaudzes. Mācīšanas seansā, tiek vērtēts, cik grūti vai viegli
|
||
jautājumi vai uzdevumi ir attiecīgi atbildami vai atrisināmi. Balstoties uz
|
||
grūtību vai iespēju atbildēt, kartītes tiek grupētas noteiktās grupās, kas,
|
||
savukārt, nosaka intervālu, pēc kura kartīte figurēs nākamā mācīšanās seansā.
|
||
Piemēram, grupas - ļoti viegli, vidēji, grūti, neizdevās -, kam atbilst 5
|
||
dienas, 3 dienas, 1 diena, 0 dienas attiecīgi. Balstoties uz subjektīvu
|
||
piepūli, atbildot uz jautājumu vai risinot uzdevumu, tiek izvēlēta atbilsoša
|
||
intervālu grupa, kas nosaka laiku pirms nākamās reizes, kad šī kartīte būs
|
||
izmantota. Šī mācīšanās metode ir atkārtošana ar intervāliem. Mācīšanās metode
|
||
vai līdzīgas metodes var uzlabot atcerēšanās līmeni pēc mācīšanās
|
||
(#link(<spaced-repetition-1>)[Alison Voice and Arran Stirton, 2020]).
|
||
|
||
// Stuff mdemory does
|
||
Veidojot atmiņas kartītes manuāli tās var pazaudēt, var kļūdīties, sagatavojot
|
||
to tekstu. Tāpēc ir iespējams kartītes veidot un uzturēt, izmantojot tīmekļa
|
||
vai lokālo lietotni. Aprakstāmās programmatūras mērķis ir vienkāršot atmiņas
|
||
kartīšu veidošanu ar iespēju veidot, mainīt un glabāt atmiņas kartītes
|
||
vienkāršā tekstā un mācījies izmantojot atkārtošanu ar intervāliem. Atmiņas
|
||
kartītes ir glabātas teksta failos un glabājas direktorijā, kas pilnībā
|
||
paliek lietotāja pārvaldē. Vienkārša teksta kartīšu glabāšana paredz, ka
|
||
lietotājs var brīvi pārvietot un mainīt kartīšu saturu, turpinot izmantot
|
||
programmatūras mācīšanās funkcionalitāti.
|
||
|
||
Uzsvars ir uz pilnīgu atmiņas kartīšu glabāšanas pilnīgu caurspīdīgumu -
|
||
kartītes glabājas bez slēptas informācijas. Vienkārša teksta izmantošana bez
|
||
slēptas informācijas atbrīvo lietotāju no atkarības no šīs programmatūras ar
|
||
iespēju pārslēgties uz citu risinājumu ar dažām teksta izmaiņām.
|
||
|
||
// @Improve: add some text?;
|
||
|
||
// The name
|
||
Programmatūras produkta nosaukums ir "Mdemory", kas ir darināts nosaukums no
|
||
"MD" (iezīmēšanas valodas Markdown abreviatūra) un "memory" (angl. atmiņa), kas
|
||
parāda produkta saistību ar atmiņu.
|
||
|
||
== Saistība ar citiem dokumentiem
|
||
|
||
#indent-par([
|
||
Programmatūras prasībus specifikācijas ir izstrādāta, ievērojot LVS 68:1996
|
||
"Programmatūras prasību specifikācijas ceļvedis" standarta un LVS 72:1996
|
||
"Ieteicamā prakse programmatūras projektējuma aprakstīšanai" standarta
|
||
prasības.
|
||
])
|
||
|
||
== Pārskats
|
||
|
||
// apraksts: Ievads
|
||
|
||
#indent-par([
|
||
Ievadā tiek definēts nolūks darbības sfēra, aprakstīta galvenā programmatūras
|
||
produkta būtība un mērķi, saistītie dokumenti.
|
||
])
|
||
|
||
Vispārējā apraksta sadaļā tiek aprakstīts esošais stāvoklis, pasūtītājs, produkta perspektīva,
|
||
galvenās darījumprasības, pieņēmumi un atkarības.
|
||
|
||
Programmatūras prasību specifikācijas sadaļā tiek aprakstīts risinājumā
|
||
izmantots karītes formāts un ar to saistītas datu struktūras, risinājuma datu
|
||
plūsmas, funkcijas un risinājuma nefunkcionālās prasības.
|
||
|
||
// @Complete: apraksts -- Programmatūras projektējuma apraksts
|
||
// @Complete: other things
|
||
|
||
#pagebreak(weak: true)
|
||
#set heading(numbering: "1.1.")
|
||
= Vispārējais apraksts
|
||
|
||
== Esošā stāvokļa apraksts
|
||
|
||
// @TODO: go over the thing
|
||
|
||
#indent-par([
|
||
"Mdemory" lietotne piedāvā vairāku specializēto atmiņas kartīšu veidu
|
||
funkcionalitāti un pilnīgu vienkārša teksta atmiņas kartīšu definēšanu un
|
||
pārvāldību. Vairāki esošie risinājumi piedāvā daļu no minētām iespējām, bet
|
||
gan nepiedāvā abas iespējas.
|
||
])
|
||
|
||
// Quizlet
|
||
Daži risinājumi iekļauj vairākus specializētus jautājumu veidus, kā grupēšanas
|
||
jautājumi, vairāku atbilžu jautājumi un secības jautājumi. Tādu risinājumu
|
||
piemērs ir #link(<quizlet>)["Quizlet"] lietotne. Quizlet importa
|
||
funkcionalitāte realizē vienkāršu importu, kas visbiežāk ir izmantots ar mērķi
|
||
pārnest atmiņas kartītes vienas lietotnes ietvaros, nevis veidot kartītes šajā
|
||
formātā. Quizlet vienkārša teksta imports ir realizēts, izmantojot CSV formātu.
|
||
|
||
// org-drill
|
||
Diezgan tuvs pēc funckionalitātes mērķiem ir "Emacs" teksta redaktora
|
||
papildinājums "org-drill". Tas piedāvā vienkārša teksta atmiņas kartīšu
|
||
veidošanu un glabāšanu ar vienkāršu tekstu. Lietotojot "ord-drill", ir
|
||
nepieciešams izmantot "Emacs" teksta redaktoru. Programmas funkcionalitāte ir
|
||
domāta pieredzējošākiem lietotājiem. Risinājums liek uzsvaru uz vienkāršām
|
||
kartītēm, kas neiekļauj specializētus kartīšu jautājumu veidus
|
||
(#link(<org-drill>)[Paul Sexton]).
|
||
// @Improve: should I add date here?
|
||
|
||
// # Org-mode functions
|
||
// Simple: Basic question and answer.
|
||
// Cloze Deletion: Creates fill-in-the-blank cards where specific terms are
|
||
// masked and must be recalled.
|
||
// Multicloze: Allows multiple cloze deletions within the same card, which is
|
||
// ideal for complex information.
|
||
|
||
Visu jautājumu importa funkcija parasti nav pieejama esošos risinājumos.
|
||
Pašlaik neviens no esošiem risinājumiem neatbalsta specializēto atmiņas kartīšu
|
||
veidu importu. "Org-mode" dod iespēju glabāt kartītes vienkāršā teksta failos,
|
||
taču tas fokusējas uz vienkāršumu ar klasisku kartīšu veidu bez specializētiem
|
||
jautājumu veidiem. Lielākā daļa risinājumu glabā kartītes savā, pielāgotā,
|
||
slēptā formātā, kas apgrūtina kartīšu pārvaldību un pārnešanu.
|
||
|
||
== Pasūtītājs
|
||
|
||
#indent-par([
|
||
Programmatūras produkts tiek izstrādāts pēc darba autora iniciatīvas
|
||
kvalifikācijas darba ietvaros.
|
||
])
|
||
|
||
== Produkta perspektīva
|
||
|
||
#indent-par([
|
||
Risinājums ir patstāvīga darbvirsmas lietotne. Lietotnes izstrādē ir izmantotas
|
||
lietotnes saskarnes bibliotēkas un programmēšanas valodas standarta bibliotēka.
|
||
Risinājums lasīs un izmainīs teksta failus no lietotāja norādītās direktorijas.
|
||
])
|
||
|
||
== Risinājuma lietotāji
|
||
|
||
// TODO Spelling
|
||
#indent-par([
|
||
Risinājumam ir viena lietotāju grupa, kam ir pieejamas visas risinājuma
|
||
iespējas un funkcijas.
|
||
Risinājuma augsta līmeņa datu plūsmas ir attēlota 0. līmeņa datu plūsmas
|
||
diagrammā (DPD; skat. @fig:dpd0 attēls). Lietotāju galvenā datu apmaiņa ir
|
||
atmiņas kartīšu dati un dati par saskarni ar kartītēm. DPD ir iekļautas trīs
|
||
datu glabātuves: failu glabātuve, pagaidu kartīšu glabātuve un konfigurācijas
|
||
glabātuve.
|
||
])
|
||
|
||
Failu glabātuve ir vieta, kur tiek glabātas kartītes vienkārša teskta formātā.
|
||
Tas ir direktorijs ar noteikta formāta failiem (skat. attēlu @fig:conceptual-er
|
||
un tabulas 2.1-2.6). Šo glabātuvi pārvalda lietotājs -- brīvi pievieno,
|
||
modificē un pārstrukturē direktorijā esošos failus un apakšdirektorijus.
|
||
|
||
Pagaidu kartīšu glabātuve glabā apskatāmās kartītes un visus izmainītos atmiņas
|
||
failiem atbilstošus kartīšu sarakstus. Glabātuve ir spēkā risinājuma programmas
|
||
darba laikā. Sākot darbu ar risinājumu, kartītes tiek ielādētas no failu
|
||
glabātuves pagaidu glabātuvē. Darbu beidzot vai pēc lietotāja izvēles, kartītes
|
||
tiek saglabātas failos, no kuriem katītes tika ielādētas. Izmaiņas pagaidu
|
||
glabātuves datos notiek ar risinājuma saskarni.
|
||
|
||
Konfigurācijas glabātuve ir fails(/-i), kuros tiek glabātas konfigurācijas
|
||
vērtības, ko uzstāda un saglabā lietotājs. Konfigurācijas glabātuvi pārvalda
|
||
lietotājs -- ir iespējams mainīt konfigurācijas faila saturu ar nosacījumu, ka
|
||
faila formāts paliek korekts un ievadītās vertības ir korektas un ir pieejamā
|
||
vērtību diapazonā.
|
||
|
||
#figure(
|
||
caption: "0.līmeņa datu plūsmas diagramma",
|
||
placement: auto,
|
||
image("img/DPD0.svg"),
|
||
) <dpd0>
|
||
|
||
== Darījumprasības
|
||
|
||
+ Kartīšu failu apstrāde, izmantojot definēto domēna specifikas valodu;
|
||
+ Kartīšu saraksta veidošana;
|
||
+ Definēto jautājumu veidu tekstuāla atspoguļošana;
|
||
+ Definēto jautājumu veidu interaktīva mācīšanas funkcija;
|
||
+ Mācīšanās ar atkārtošanu ar intervāliem;
|
||
+ Kartīšu pievienošana, modifikācija un dzēšana sarakstā;
|
||
+ Vairāku kartīšu failu apstrāde direktorijā;
|
||
+ Kartīšu saglabāšana no saraksta.
|
||
|
||
== Vispārējie ierobežojumi
|
||
|
||
+ Risinājums nepieslēdzas internetam;
|
||
+ Risinājums ir lokāla darbvirsmas programma;
|
||
+ Atmiņas kartītes un kofigurācija ir glabāta vienkārša teksta failos, ko
|
||
lietotājs var modificēt pilnībā;
|
||
|
||
== Pieņēmumi un atkarības
|
||
|
||
+ Lietotnes var tikt uzbūvēta darbam uz Windows vai Linux sistēmām, uz kurām ir
|
||
pieejamas risinājumā izmantotas saskarnes bibliotēkas;
|
||
+ Lietotājs atbild par atmiņas kartīšu failu glabāšanu;
|
||
|
||
#pagebreak(weak: true)
|
||
= Programmatūras prasību specifikācija
|
||
|
||
// Šajā sadaļā tiks aprakstīts TODO??
|
||
|
||
== Konceptuālais entitāšu-relāciju modelis
|
||
|
||
// TODO spelling
|
||
Risinājuma glabātie dati iekļauj atmiņas kartīšu un konfigurācijas datus, kas
|
||
tiek glabāti vienkāršos failos. Risinājums neizmantos datubāzi datu glabāšanai.
|
||
Risinājumā entitātes ir attēlotas konceptuālā entitāšu-relāciju modelī (skat. attēlu
|
||
@fig:conceptual-er).
|
||
|
||
Atmiņas kartītes tiek organizētas noteikta paplašinājuma failos, kas satur
|
||
kartītes noteiktā formātā. Kartītes ir dažādu veidu jautājumi, kas ietver
|
||
sākuma -- jautājuma tekstu un vairākus saistītus jautājuma atbildes elementus.
|
||
Ir atbildes jautājumi, kas satur vairākus vienlīdzīgus elementus, un ir grupas
|
||
jautājumi, kas satur vairākas grupas ar vairākiem tai piederošiem elementiem.
|
||
Atbildes elementiem piemīt veids, katrs atšķiras ar to, kā elements sākas, ko
|
||
nosaka sākuma simboli. Grupas elementi sastāv no teksta un pieder kādai grupai,
|
||
kam ir nosaukums.
|
||
|
||
Modelī ir iekļauti metadati, kā pēdējās mācīšanās laiks, jautājuma pārtraukuma
|
||
laiks un faila nosaukums. Pēdējās mācīšanās laiks tiek izmantots intervālu
|
||
metodē, lai reģistrētu laiku un pielietot intervālu metodi turpmākās mācību
|
||
reizēs. Jautājuma pārtraukuma laiks nosaka, cik ilgam laikam jāpaiet, lai
|
||
jautājums atkal būtu iekļauts intervālu metodes mācīšanās.
|
||
Faila nosaukums tiek izmantots kā kartīšu saraksta nosaukums -- to var izmanot,
|
||
lai nosauktu failā esošās kartītes, piemēram, to tēmu vai nolūku.
|
||
|
||
|
||
Papildus datetime TODO
|
||
|
||
#figure(
|
||
caption: "Konceptuālais entitāšu-relāciju modelis",
|
||
placement: auto,
|
||
image("img/conceptualER.svg"),
|
||
) <conceptual-er>
|
||
|
||
== Funkcionālās prasības
|
||
|
||
#let ref_df01 = [(skat. tabulu 2.1)]
|
||
#let ref_df02 = [(skat. tabulu 2.2)]
|
||
#let ref_df03 = [(skat. tabulu 2.3)]
|
||
#let ref_df04 = [(skat. tabulu 2.4)]
|
||
#let ref_df05 = [(skat. tabulu 2.5)]
|
||
#let ref_df06 = [(skat. tabulu 2.6)]
|
||
#let ref_ds01 = [(skat. tabulu 2.7)]
|
||
#let ref_ds02 = [(skat. tabulu 2.8)]
|
||
#let ref_ds03 = [(skat. tabulu 2.9)]
|
||
#let ref_ds04 = [(skat. tabulu 2.10)]
|
||
|
||
#let ref_pr01 = [(skat. tabulu 2.12)]
|
||
#let ref_pr02 = [(skat. tabulu 2.13)]
|
||
|
||
#let ref_ab01 = [(skat. tabulu 2.14)]
|
||
#let ref_ab02 = [(skat. tabulu 2.15)]
|
||
#let ref_ab03 = [(skat. tabulu 2.16)]
|
||
#let ref_ab04 = [(skat. tabulu 2.17)]
|
||
#let ref_ab05 = [(skat. tabulu 2.18)]
|
||
#let ref_ab06 = [(skat. tabulu 2.19)]
|
||
#let ref_ab07 = [(skat. tabulu 2.20)]
|
||
#let ref_ab08 = [(skat. tabulu 2.21)]
|
||
|
||
#let ref_ma01 = [(skat. tabulu 2.22)]
|
||
#let ref_ma02 = [(skat. tabulu 2.23)]
|
||
#let ref_ma03 = [(skat. tabulu 2.24)]
|
||
#let ref_ma04 = [(skat. tabulu 2.25)]
|
||
#let ref_ma05 = [(skat. tabulu 2.26)]
|
||
|
||
#let ref_kf01 = [(skat. tabulu 2.37)]
|
||
#let ref_kf02 = [(skat. tabulu 2.38)]
|
||
#let ref_kf03 = [(skat. tabulu 2.39)]
|
||
|
||
#let questionSource = [
|
||
simbolu virkne, kas satur jautājumus formātā, kas atbilst datu formātiem
|
||
#link(<DF02>)[DF02],
|
||
#link(<DF03>)[DF03],
|
||
#link(<DF04>)[DF04],
|
||
#link(<DF05>)[DF05] (skat. tabulas 2.2.-2.5.)
|
||
];
|
||
#let questionList = [saraksts ar objektiem, kas atbilst datu struktūrai #link(<DS01>)[DS01] #ref_ds01];
|
||
#let question = [objekts, kas atbilst datu struktūrai #link(<DS01>)[DS01] #ref_ds01];
|
||
#let pageList = [saraksts ar objektiem, kas atbilst datu struktūrai #link(<DS02>)[DS02] #ref_ds02];
|
||
#let datetime = [simbola virkne formātā, kas atbilst datu struktūrai #link(<DS03>)[DS03] #ref_ds03];
|
||
|
||
#let uint = [vesels pozitīvs skaitlis];
|
||
#let mdemFilePath = [simbolu virkne, kas atbilst failam ar noteitu paplašinājumu];
|
||
#let listWithCorrectFlags = [Izvēlēto atbilžu simbolu virkņu saraksts ar pareizuma karodziņu vērtībām];
|
||
#let configValue = [viens no: skaitlis, simbolu virkne, karodziņš];
|
||
#let changeIndicator = [Saskarnē parādās indikators, ka atmiņas kartīšu fails tika izmainīts];
|
||
|
||
|
||
=== Augsta līmeņa kartīšu valodas specifikācija
|
||
|
||
#indent-par([
|
||
Šajā apakšsadaļā tiks aprakstīta domēna specifiska valodas formāta
|
||
vienības, kas ir sastāvdaļas no valodas, kas ir izmantota risinājumā,
|
||
definējot, glabājot un rediģējot atmiņas kartītes. Formāti ir aprakstīti
|
||
tabulās 2.1.-2.6.
|
||
])
|
||
|
||
==== Kartīšu veidi
|
||
|
||
#format-table(
|
||
"Jautājuma sākums",
|
||
"DF01",
|
||
[
|
||
Kartīšu veidošanai vienkāršā tekstā tiek izmantota vienkārša domēna specifiska
|
||
valoda. Ir 4 jautājumu veidi, kuru struktūra ir līdzīga ar dažām atšķirībām.
|
||
Katram jautājumam ir sākums, kam var būt skaitlis, kas apzīmē intervālu
|
||
stundās, pēc kura tas būs atkārtoti izmantots. Priekša ir sākas ar "-" un
|
||
beidzas ar ">".
|
||
],
|
||
[
|
||
```
|
||
- Ka sauc Latvijas galvaspilsētu? >
|
||
- [12.5] Ka sauc Igaunijas galvaspilsētu? >
|
||
```
|
||
]
|
||
)<DF01>
|
||
|
||
|
||
#format-table(
|
||
"Vienkāršs atbilžu jautājums",
|
||
"DF02",
|
||
[
|
||
Vienkāršs atbilžu jautājums ir jautājums, kam ir viena vai
|
||
vairākas pareizas atbildes, katra atbilde sākas ar "-".
|
||
],
|
||
[
|
||
```markdown
|
||
- Ka sauc Lietuvas galvas pilsētu? >
|
||
- Viļņa
|
||
- Nosauciet Baltijas valstis >
|
||
- Latvija
|
||
- Igaunija
|
||
- Lietuva
|
||
```
|
||
]
|
||
)<DF02>
|
||
|
||
#format-table(
|
||
"Izvēles jautājums",
|
||
"DF03",
|
||
[
|
||
Izvēles jautājums ir jautājums, kam ir viens vai vairākas pareizas
|
||
atbildes, pareizās atbildes sākas ar "+", nepareizās -- ar "-".
|
||
],
|
||
[
|
||
```markdown
|
||
- Kuras no valstīm ir Baltijas valstis >
|
||
+ Latvija
|
||
- Rumānija
|
||
+ Lietuva
|
||
- Spānija
|
||
```
|
||
]
|
||
)<DF03>
|
||
|
||
#format-table(
|
||
"Secības jautājums",
|
||
"DF04",
|
||
[
|
||
Secības jautājums -- jautājums ar vairākām vērtībām, kam ir noteikta secība,
|
||
vērtības sākas ar "-^".
|
||
],
|
||
[
|
||
```
|
||
- Sakārtojiet zinātniskās metodes soļus pareizā secībā >
|
||
-^ Uzdod jautājumu
|
||
-^ Izvirza hipotēzi
|
||
-^ Veic eksperimentu
|
||
-^ Analizē datus
|
||
```
|
||
]
|
||
)<DF04>
|
||
|
||
#format-table(
|
||
"Grupēšanas jautājums",
|
||
"DF05",
|
||
[
|
||
Grupēšanas jautājums -- jautājums, kur ir vairākas grupas, kurām pieder nulle
|
||
vai vairāki elementi. Grupas sākas ar "-" un beidzas ar ":". Grupas elementi
|
||
sākas ar "-".
|
||
],
|
||
[
|
||
```
|
||
- Savienojiet planētas ar to īpašībām >
|
||
- Zeme:
|
||
- Ir dzīve
|
||
- Jupiters:
|
||
- Lielākā planēta
|
||
- Ir gredzeni
|
||
- Venēra:
|
||
- Merkurs:
|
||
- Mazākā planēta
|
||
- Saturns:
|
||
- Ir gredzeni
|
||
- Neptūns:
|
||
- Tālākā no Saules
|
||
- Ir gredzeni
|
||
```
|
||
]
|
||
)<DF05>
|
||
|
||
// @TODO: replace every jautājums -> kartīte except where its really needed.
|
||
|
||
#format-table(
|
||
"Kartītes faila pirmteksts",
|
||
"DF06",
|
||
[
|
||
Kartītes pirmteksta failā var tikt iekļauti vairāki dažādu veidu jautājumi.
|
||
Sākumā var būt datuma un laika simbolu virkne, kas atbilst datu struktūrai #link(<DS02>)[DS02] #ref_ds02.
|
||
],
|
||
[
|
||
```
|
||
21.10.2024 12:54
|
||
- Savienojiet dzīvniekus ar to īpašībām >
|
||
- Kaķis:
|
||
- Murrā
|
||
- Suns:
|
||
- Ir uzticīgs
|
||
- Draudzīgs
|
||
- Zivs:
|
||
- Varde:
|
||
- Dzīvo gan ūdenī, gan uz sauszemes
|
||
- Zirgs:
|
||
- Ir ātrs
|
||
- Putns:
|
||
- Spēj lidot
|
||
- Dzīvo kokā
|
||
- Sakārtojiet cilvēka dzīves posmus pareizā secībā >
|
||
-^ Bērnība
|
||
-^ Pusaudža gadi
|
||
-^ Pieaugšana
|
||
-^ Vecumdienas
|
||
```
|
||
]
|
||
)<DF06>
|
||
|
||
=== Formātu un datu struktūru specifikācija
|
||
|
||
#indent-par([
|
||
Šajā apakšsadaļā tiks apskatīti ievades un izvades vērtību
|
||
formāti, kas tiks izmantoti funkciju aprakstos.
|
||
Formāti ir aprakstīti tabulās 2.7.-2.9.
|
||
])
|
||
|
||
#io-table(
|
||
"Kartīšu datu struktūra",
|
||
"DS01",
|
||
[
|
||
Jautājumu datu struktūra satur informāciju par atmiņas kartīti ar papildus
|
||
informāciju, kas atbils noteiktam jautājuma veidam.
|
||
|
||
Pamatā jautājumam ir:
|
||
|
||
- Teksts -- simbola virkne;
|
||
- Ilgums, cik kartīte nebūs izmantota atkal intervālu metodē -- pozitīvs daļu skaitlis;
|
||
|
||
Papildus informācija iedalās vairākos veidos.
|
||
|
||
+ Vairāku elementu veids:
|
||
- Veids -- uzskaitījums, kurš no - vairāku variantu, secības, vienkāršs -
|
||
jautājums ir;
|
||
- Saraksts ar objektiem, kas sastāv no simbolu
|
||
virknes un korektuma karodziņu;
|
||
+ Grupas veids:
|
||
- Saraksts ar objektiem, kas sastāv no:
|
||
- Nosaukuma -- simbolu virkne;
|
||
- Elementiem -- saraksts ar simbolu virknēm.
|
||
|
||
],
|
||
) <DS01>
|
||
|
||
#io-table(
|
||
"Lappuses datu struktūra",
|
||
"DS02",
|
||
[
|
||
Lappuse ir ir saraksts ar referencēm uz vairākiem jautājuma objektiem, kam ir
|
||
noteikts lappuses skaitlis.
|
||
|
||
// TODO: elaborate more
|
||
],
|
||
)<DS02>
|
||
|
||
#io-table(
|
||
"Datuma un laika simbolu virkne",
|
||
"DS03",
|
||
[
|
||
Datuma un laika simbolu virknē tiek izmantots sekojošs formāts -
|
||
[dienas_numurs].[mēneša_numurs].[gads] [stunda]:[minūte] -, piemēram,
|
||
"13.05.2024 12:02".
|
||
],
|
||
)<DS03>
|
||
|
||
#io-table(
|
||
"Transpilēšanas kļūdas datu struktūra",
|
||
"DS04",
|
||
[
|
||
Datu struktūra satur kļūdas aprakstu, kas notika transpilēšanas
|
||
laikā. Papildus ir pievienots rindas numurs un kolonnas numurs.
|
||
],
|
||
)<DS04>
|
||
|
||
=== Funkciju sadalījums moduļos
|
||
|
||
#indent-par([
|
||
Risinājuma moduļi ar to saistītām datu plūsmām un datu glabātuvēm ir attēlots
|
||
1\. līmeņa DPD (skat. @fig:dpd1 attēls). Funkciju sadalījums moduļos ir
|
||
apraksīts sadalījuma tabulā (skat. @tbl:function_table tabula).
|
||
])
|
||
|
||
#figure(
|
||
caption: "1. līmeņa datu plūsmas diagramma",
|
||
placement: auto,
|
||
image("img/DPD1.svg"),
|
||
) <dpd1>
|
||
|
||
#pagebreak(weak: true)
|
||
#figure(
|
||
caption: "Funkciju sadalījums pa moduļiem",
|
||
kind: table,
|
||
tablex(
|
||
columns: 3,
|
||
/* --- header --- */
|
||
[*Modulis*], [*Funkcija*], [*Identifikators*],
|
||
/* -------------- */
|
||
|
||
rowspanx(8)[Atmiņas bāzes modulis],
|
||
[Kartīšu faila ielāde], [#link(<AB01>)[AB01]],
|
||
[Kartīšu saraksta lappušu izveidošana], [#link(<AB02>)[AB02]],
|
||
[Atmiņas bāzes lappuses iegūšana], [#link(<AB03>)[AB03]],
|
||
[Kartīšu pievienošana], [#link(<AB04>)[AB04]],
|
||
[Kartītes pirmteksta iegūšana], [#link(<AB05>)[AB05]],
|
||
// @Fix: rediģēšana everywhere
|
||
[Kartītes rediģēšana], [#link(<AB06>)[AB06]],
|
||
[Kartītes dzēšana], [#link(<AB07>)[AB07]],
|
||
[Kartītes faila saglabāšana], [#link(<AB08>)[AB08]],
|
||
|
||
rowspanx(5)[Mācīšanās modulis],
|
||
[Nākamās kartītes noteikšana], [#link(<MA01>)[MA01]],
|
||
[Izvēles kartītes pārbaude], [#link(<MA02>)[MA02]],
|
||
[Kārtošanas kartītes pārbaude], [#link(<MA03>)[MA03]],
|
||
[Grupēšanas kartītes pārbaude], [#link(<MA04>)[MA04]],
|
||
[Intervāla grupas atbildes sniegšana], [#link(<MA05>)[MA05]],
|
||
// un meta datu saglabāšana
|
||
|
||
rowspanx(3)[Konfigurācijas modulis],
|
||
[Konfigurācijas vērtības iegūšana], [#link(<KF01>)[KF01]],
|
||
[Konfigurācijas vērtību iegūšana], [#link(<KF02>)[KF02]],
|
||
[Konfigurācijas vērtības izmaiņa], [#link(<KF03>)[KF03]],
|
||
|
||
),
|
||
) <function_table>
|
||
|
||
=== Kopīgas procedūras
|
||
|
||
#indent-par([
|
||
Vairākas prasībās definētās funkcijas izmanto kopīgas procedūras, kas tiek
|
||
definētas tabulās 2.12. un 2.13.
|
||
])
|
||
|
||
#procedure-table(
|
||
"Teksta jautājumu transpilēšana",
|
||
"PR01",
|
||
[
|
||
Procedūra apstrādā simbolu virkni, iegūstot jautājumu datu struktūru
|
||
objektus. Ir sagaidāms, ka simbolu virkne atbilst specificētai valodai. Ja
|
||
simbolu virkne valodai neatbilst, tiek izvadīta kļūda. Simbolu virkne tiek
|
||
apstrādāta vairākos posmos.
|
||
],
|
||
[
|
||
+ Jautājumu pirmteksts -- #questionSource\;
|
||
],
|
||
[
|
||
+ Tiek nolasīti simboli, sagrupējot to tekstvienībās;
|
||
+ Tiek veikta virspusēja gramatikas pārbaude;
|
||
+ Ja tiek sastapta tekstvienība, kas nav sagaidāma kādā kontekstā, beidz
|
||
apstrādi, izvadē pievienojot kļūdu ar tās pozīciju;
|
||
+ Tiek nolasīti jautājumi, nosakot to veidu pēc to struktūras:
|
||
+ Jautājuma sākums;
|
||
+ Veidojot jautājumus tiek pielasīts arī intervāla laiks, ja tāds ir;
|
||
+ Pēc jautājuma sākuma ir viens vai vairākas atbildes jeb apakšpunkti;
|
||
+ Vairāki apakšpunkti ar "-" -- atbilžu jautājums;
|
||
+ Vairāki apakšpunkti ar "+" -- izvēles jautājums;
|
||
+ Vairāki apakšpunkti ar "-^" -- secības jautājums;
|
||
+ Vairāki apakšpunkti, kas beidzas ar ":",
|
||
kam seko apakšpunkti ar "-" (iespējams tos nenorādīt);
|
||
+ Jautājumu veidi ir nosakāmi bez tiešas vieda norādīšanas;
|
||
+ Ja nolasīšanas laikā parādās nesagaidīta tekstvienība, beidz apstrādi,
|
||
izvadē pievienojot kļūdu ar tās pozīciju;
|
||
],
|
||
[
|
||
+ Saraksts ar jautājumiem -- #questionList\;
|
||
+ Kļūda ar tekstu un tās pozīciju saturā -- paziņojuma objekts, kas atbilst
|
||
datu struktūrai #link(<DS04>)[DS04] #ref_ds04\;
|
||
],
|
||
) <PR01>
|
||
|
||
#procedure-table(
|
||
"Teksta jautājumu detranspilēšana",
|
||
"PR02",
|
||
[
|
||
Procedūra apstrādā jautājumu sarakstu un izveido tam atbilstošu
|
||
simbolu virkni, kas atbilst specificētai valodai.
|
||
Papildus sākumā tiek pievienots datums un laiks, ja tas tiek padots,
|
||
kas ir daļa no metadatiem.
|
||
],
|
||
[
|
||
+ Jautājumu saraksts -- #questionList\;
|
||
+ Datums un laiks -- #datetime\;
|
||
],
|
||
[
|
||
+ Iegūst konfigurācijas vērtības, kas nosaka cik garš ir tabulācijas simbols
|
||
un kāds ir maksimāls rindiņas platums pirms tiek veidots jaunās rindas simbols;
|
||
+ Ja datums un laiks ir padots, pievieno datumu un laiku atbilstoši formātam;
|
||
+ Katram jautājumam veic sekojošo:
|
||
+ Pievieno jautājuma sākumu ar intervāla skaitli, ja tāds ir;
|
||
+ Pievieno apakšpunktus atbilstoši tā veidam;
|
||
+ Vairāki apakšpunkti ar "-" -- atbilžu jautājums;
|
||
+ Vairāki apakšpunkti ar "+" -- izvēles jautājums;
|
||
+ Vairāki apakšpunkti ar "-^" -- izvēles jautājums;
|
||
+ Vairāki apakšpunkti, kas beidzas ar ":", kam seko apakšpunkti ar "-";
|
||
+ Veidojot jautājumus ievēro iegūtās konfigurācijas vērtības;
|
||
],
|
||
[
|
||
+ Jautājumu pirmeksts -- #questionSource\;
|
||
],
|
||
) <PR02>
|
||
|
||
|
||
=== Atmiņas bāzes modulis
|
||
|
||
#indent-par([
|
||
Atmiņas bāze ir direktorijs ar atmiņas kartīšu failiem. Modulis atbild par
|
||
mijiedarbību ar atmiņas šiem failiem un atmiņas kartīšu prezentēšanu. Šī moduļa
|
||
funkcijas ir izmantotas atmiņu kartīšu saraksta funkcionalitātei. Funkcijas
|
||
mijiedarbojas ar pagaidu glabātuvē esošām atmiņas kartītēm un failu glabātuves
|
||
failiem, kas satur atmiņu kartīšu informāciju. Ar funkcijām saistītas datu plūsmas
|
||
ir attēlotas moduļa 2. līmeņa DPD (skat. @fig:abdpd2 attēlu).
|
||
Moduļa funkcijas tiek definētas tabulās 2.14.-2.21.
|
||
])
|
||
|
||
#figure(
|
||
caption: "Atmiņas bāzes moduļa 2.līmeņa datu plūsmas diagramma",
|
||
placement: auto,
|
||
image("img/ABDPD2.svg"),
|
||
) <abdpd2>
|
||
|
||
#pagebreak(weak:true)
|
||
#function-table(
|
||
"Kartīšu faila ielāde",
|
||
"AB01",
|
||
[
|
||
Funkcija apstrādā faila saturu. Iegūstot objektus ar jautājumu dati
|
||
un ar to saistītiem metadatiem. Ieejas failam jāatbilst valodas
|
||
specifikācijai.
|
||
],
|
||
[
|
||
+ Faila atrašanās ceļš -- #mdemFilePath\;
|
||
],
|
||
[
|
||
+ Faila saturs tiek ielādēts atmiņā:
|
||
+ Ja faila ielāde ir neveiksmīga, parāda 1. paziņojumu.
|
||
+ Tiek nolasīts datums un laiks no faila sākuma, ja tāds eksistē:
|
||
+ Ja datuma un laika formāts nav korekts, parāda 2. paziņojumu
|
||
ar atbilstošu iemeslu un kļūdas pazīmi, beidz apstrādi.
|
||
+ Tiek iegūti jautājuma objekti, izmantojot procedūru #link(<PR01>)[PR01] #ref_pr01\;
|
||
+ Ja radās kļūda, parāda 2. paziņojumu ar kļūdas informāciju, beidz
|
||
apstrādi.
|
||
+ Ja kādā no apstrādes posmiem radās kļūda, parāda 2. paziņojumu
|
||
ar atbilstošu iemeslu un kļūdas pozīciju, beidz apstrādi.
|
||
],
|
||
[
|
||
+ Saraksts ar jautājumiem -- #questionList\;
|
||
+ Faila reģistrēts datums un laiks -- #datetime\;
|
||
],
|
||
[
|
||
+ Ielādes kļūda: neizdevās atvērt failu;
|
||
+ Ielādes kļūda: [kļūdas iemesls] [kļūdas rinda]:[kļūdas kolonna];
|
||
],
|
||
) <AB01>
|
||
|
||
#pagebreak(weak:true)
|
||
#function-table(
|
||
"Kartīšu saraksta lappušu izveidošana",
|
||
"AB02",
|
||
[
|
||
Funkcija izveido vairākas lappuses, sadalot atmiņā esošus jautājumu
|
||
objektus intervālos. Lappušu izmērs ir noteikts ar konfigurācijas
|
||
parametrus.
|
||
],
|
||
[
|
||
+ Jautājumu saraksts -- #questionList\;
|
||
],
|
||
[
|
||
+ No konfigurācijas tiek iegūts jautājumu skaits lappusē, un rādāmo lappušu
|
||
pogu skaits;
|
||
+ Jautājumi tiek sadalīti lappusēs, veidojot lappušu objektus, katrā tiek
|
||
ievietotas references uz jautājumiem. Lappušu pogu skaits atbilst
|
||
konfigurācijas vērtībai;
|
||
],
|
||
[
|
||
+ Lappušu saraksts -- #pageList\;
|
||
],
|
||
[
|
||
Funkcijai nav paziņojumu.
|
||
],
|
||
) <AB02>
|
||
|
||
#function-table(
|
||
"Atmiņas bāzes lappuses iegūšana",
|
||
"AB03",
|
||
[
|
||
Funkcija iegūst jautājumu objektu intervālu
|
||
no pagaidu glabātuves.
|
||
],
|
||
[
|
||
+ Lappuses numurs -- #uint\;
|
||
],
|
||
[
|
||
+ No pagaidu atmiņas kartīšu glabātuves tiek iegūtas atmiņas kartīšu lappuses;
|
||
+ Tiek meklēts lappušu objekts ar norādīto numuru;
|
||
+ Ja lappuse tiek atrasta, saistītie atmiņas kartīšu objekti tiek atgriezti;
|
||
+ Ja tāda lappuse netiek atrasta, parāda 1. paziņojumu;
|
||
],
|
||
[
|
||
+ Jautājumu saraksts -- #questionList\;
|
||
+ Datums un laiks -- #datetime\;
|
||
// @Maybe: Add 'metadata' into DPD (datums un laiks);
|
||
],
|
||
[
|
||
+ Pieprasītā lappuse neeksistē;
|
||
],
|
||
) <AB03>
|
||
|
||
|
||
// @TODO: for all things that save someth somewhere, say that in the Izvade
|
||
|
||
#function-table(
|
||
"Kartīšu pievienošana",
|
||
"AB04",
|
||
[
|
||
Funkcija pieņem lietotāja ievadīto tekstu un pārveido to jautājumu
|
||
objektos, kas pievieno tos jau esošiem jautājumiem.
|
||
],
|
||
[
|
||
+ Kartītes pirmteksts -- #questionSource\;
|
||
],
|
||
[
|
||
+ Ja ievadīts teksts, parāda 1. paziņojumu, beidz apstrādi;
|
||
+ Apstrādā simbolu virkni, izmantojot procedūru #link(<PR01>)[PR01] #ref_pr01\;
|
||
+ Ja tiek izvadīta kļūda, parāda 2. paziņojumu ar kļūdas informāciju,
|
||
beidz apstrādi;
|
||
],
|
||
[
|
||
Izvades jautājuma objekts tiek atjaunots atbilstošam ierakstu jautājumu pagaidu
|
||
glabātuvē. #changeIndicator\.
|
||
|
||
+ Jautājumu saraksts -- #questionList\;
|
||
],
|
||
[
|
||
+ Nav ievadīts neviens jautājums;
|
||
+ Ielādes kļūda: [kļūdas iemesls] [kļūdas rinda]:[kļūdas kolonna];
|
||
],
|
||
) <AB04>
|
||
|
||
#function-table(
|
||
"Kartītes pirmteksta iegūšana",
|
||
"AB05",
|
||
[
|
||
Funkcija iegūst kartītes pirmtekstu, kas atbilst valodas specifikācijai.
|
||
Funkcija ir specializēts procedūras izmantošanas gadījums, kurā tiek
|
||
detranspilēts tikai viens jautājums.
|
||
|
||
Funkcija ir izmantota rediģēšanas iespējai.
|
||
],
|
||
[
|
||
+ Jautājums -- #question\;
|
||
],
|
||
[
|
||
+ Izmantojot procedūru #link(<PR01>)[PR01] #ref_pr01, jautājums tiek detranspilēts
|
||
pirmtekstā\;
|
||
],
|
||
[
|
||
+ Kartītes pirmteksts -- #questionSource\;
|
||
],
|
||
[
|
||
Funkcijai nav paziņojumu.
|
||
Saskarnē parādās indikators, ka atmiņas kartīšu fails tika izmainīts.
|
||
],
|
||
) <AB05>
|
||
|
||
#function-table(
|
||
"Kartītes rediģēšana",
|
||
"AB06",
|
||
[
|
||
Funkcija izmaina esošo jautājumu, pēc to pirmteksta, kas atbilst
|
||
valodas specifikācijai.
|
||
],
|
||
[
|
||
+ Norāde uz jautājumu -- #question\;
|
||
+ Jautājuma pirmteksts -- #questionSource\;
|
||
],
|
||
[
|
||
+ Ja ievades teksts ir tukšs, parāda 1. paziņojumu, beidz apstrādi;
|
||
+ Izmantojot procedūru #link(<PR01>)[PR01] #ref_pr01, tiek iegūts jautājuma objekts no sniegtā
|
||
pirmteksta;
|
||
+ Ja tiek izvadīta kļūda, parāda 1. paziņojumu ar atbilstošu kļūdas informāciju,
|
||
beidz apstrādi;
|
||
+ Padotais jautājuma objekts tiek atjaunots ar iegūta objekta datiem;
|
||
],
|
||
[
|
||
Izvades jautājuma objekts tiek atjaunots atbilstošam ierakstu jautājumu
|
||
pagaidu glabātuvē. #changeIndicator\.
|
||
|
||
+ Izmainīts jautājums -- #question\;
|
||
],
|
||
[
|
||
+ Nav ievadīts neviens jautājums;
|
||
+ Rediģēšanas kļūda: [kļūdas iemesls] [kļūdas rinda]:[kļūdas kolonna];
|
||
],
|
||
) <AB06>
|
||
|
||
#function-table(
|
||
"Kartītes dzēšana",
|
||
"AB07",
|
||
[
|
||
Funkcija izdēš kartīti no pagaidu glabātuves.
|
||
],
|
||
[
|
||
+ Norāde uz jautājumu -- #question\;
|
||
],
|
||
[
|
||
+ Jautājums tiek sameklēts un izdzēsts;
|
||
],
|
||
[
|
||
Izvades jautājuma objekts tiek atjaunots atbilstošam ierakstu jautājumu
|
||
pagaidu glabātuvē. #changeIndicator\.
|
||
],
|
||
[
|
||
Funkcijai nav paziņojumu.
|
||
],
|
||
) <AB07>
|
||
|
||
#function-table(
|
||
"Kartītes faila saglabāšana",
|
||
"AB08",
|
||
[
|
||
Funkcija saglabā kartīšu jautājumus un metadatus tam atbilstošā
|
||
failā, pārveidojot tos valodai atbilstošā formātā.
|
||
],
|
||
[
|
||
+ Faila atrašanās ceļš -- #mdemFilePath\;
|
||
+ Jautājumu saraksts -- #questionList\;
|
||
+ Datums un laiks -- #datetime\;
|
||
],
|
||
[
|
||
+ Iegūst pirmtekstu padotajiem objektiem, izmantojot procedūru #link(<PR02>)[PR02] #ref_pr02\;
|
||
+ Ja tiek izvadīta kļūda, parāda 2. paziņojumu ar kļūdas informāciju, beidz apstrādi;
|
||
+ Izveido vai atver failu padotā ceļā;
|
||
+ Ja atvēršana vai izveidošana neizdevās, parāda 1. paziņojumu;
|
||
+ Pievienot iegūto saturu failam un saglabā to;
|
||
],
|
||
[
|
||
Fails tiks atjaunots failu sistēmā.
|
||
Saskarnē pazūd indikators, ka atmiņas kartīšu fails tika izmainīts.
|
||
|
||
+ Fails ar kartītes informāciju, kas atbilst datu formātam #link(<DF06>)[DF06] #ref_df06\;
|
||
],
|
||
[
|
||
+ Neizdevās atvērt vai izveidot atmiņas kartīšu failu;
|
||
+ Rediģēšanas kļūda: [kļūdas iemesls] [kļūdas rinda]:[kļūdas kolonna];
|
||
],
|
||
) <AB08>
|
||
|
||
|
||
=== Mācīšanās modulis
|
||
|
||
#indent-par([
|
||
Mācīšanās risinājuma kontekstā ir kartīšu prezentēšana ar interaktīvu atbildes
|
||
funkcionalitāti, atbilstoši atmiņas kartītes tipam. Šī moduļa funkcijas ir
|
||
izmantotas mācīšanās loga funkcionalitātei. Funkcijas ietver darbības ar pagaidu
|
||
glabātuvē esošām atmiņas kartītēm. Ar funkcijām saistītas datu plūsmas ir
|
||
attēlotas moduļa 2. līmeņa DPD (skat. @fig:madpd2 attēlu).
|
||
Moduļa funkcijas tiek definētas tabulās 2.22.-2.26.
|
||
])
|
||
|
||
#figure(
|
||
caption: "Mācīšanās moduļa 2.līmeņa datu plūsmas diagramma",
|
||
placement: auto,
|
||
image("img/MADPD2.svg"),
|
||
) <madpd2>
|
||
|
||
#pagebreak(weak: true)
|
||
#function-table(
|
||
"Nākamās kartītes noteikšana",
|
||
"MA01",
|
||
[
|
||
Funkcija nosaka nākamo kartīti, kas tiks parādīta lietotājam
|
||
mācīšanas procesā. Kartīšu secību nosaka mācīšanās algoritms.
|
||
],
|
||
[
|
||
+ Norāde uz pašreizējo jautājums -- #question\;
|
||
+ Algoritms -- viens no vērtībām: "fizisks", "nejaušs", "intervālu"\;
|
||
],
|
||
[
|
||
+ Ja pagaidu kartīšu glabātuvē nav nevienas kartītes, beidz apstrādi, neatgriežot
|
||
kartīti;
|
||
+ Ja padotais algoritms ir "fizisks";
|
||
+ Ja pašreizējā kartīte ir padota, izvēlās nākamo kartīti starp pagaidu kartīšu glabātuves;
|
||
+ Citādi izvēlas pirmo kartīti no pagaidu kartīšu glabātuves;
|
||
+ Ja padotais algoritms ir "nejaušs";
|
||
+ Izvēlas nākamo kartīti nejaušā kārtībā starp pagaidu kartīšu
|
||
glabātuves, izņemot pašžreizējo jautājumu;
|
||
+ Ja padotais algoritms ir "intervālu";
|
||
+ No pagaidu kartīšu glabātuves izvēlas tikai to, kur nav norādīts intervāls
|
||
vai intervāls ir 0, izņemot pašžreizējo jautājumu;
|
||
+ No šī saraksta izvēlas nākamo kartīti nejaušā kārtībā;
|
||
],
|
||
[
|
||
// Add null explanation
|
||
+ Nākamās kartītes objekts (iespējams nulles objekts) -- #question\;
|
||
],
|
||
[
|
||
Funkcijai nav paziņojumu.
|
||
],
|
||
) <MA01>
|
||
|
||
#function-table(
|
||
"Izvēles kartītes pārbaude",
|
||
"MA02",
|
||
[
|
||
Veic jautājuma atbildes pārbaudi izvēles kartītes jautājuma veidam.
|
||
],
|
||
[
|
||
+ Izvēlēto atbilžu simbolu virkņu saraksts ar pazīmi, vai atbilde ir izvēlēta;
|
||
+ Norāde uz izvēles kartītes objektu -- #question;
|
||
],
|
||
[
|
||
+ Izskata abus sarakstus paralēli;
|
||
+ Ja atbildes un jautājuma objektu izvēles elementa pazīmes sakrīt
|
||
(piemēram, "pareizs" un "pareizs");
|
||
+ Atbilžu elementam uzstāda pareizuma pazīmi kā "pareizs";
|
||
+ Citādi uzstāda atbildes pareizuma atbildi kā "nepareizs";
|
||
],
|
||
[
|
||
+ #listWithCorrectFlags\;
|
||
],
|
||
[
|
||
Funkcijai nav paziņojumu.
|
||
],
|
||
) <MA02>
|
||
|
||
#function-table(
|
||
"Kārtošanas kartītes pārbaude",
|
||
"MA03",
|
||
[
|
||
Veic jautājuma atbildes pārbaudi kārtošanas kartītes jautājuma veidam.
|
||
],
|
||
[
|
||
+ Atbilžu simbolu virkņu saraksts;
|
||
+ Norāde uz kārtošanas kartītes objektu -- #question;
|
||
],
|
||
[
|
||
+ Izskata abus sarakstus paralēli;
|
||
+ Ja atbildes un jautājuma objektu izvēles elementa sakrīt, atbilžu
|
||
elementam uzstāda pareizuma pazīmi kā "pareizs";
|
||
+ Citādi uzstāda atbildes pareizuma atbildi kā "nepareizs";
|
||
],
|
||
[
|
||
+ #listWithCorrectFlags\;
|
||
],
|
||
[
|
||
Funkcijai nav paziņojumu.
|
||
],
|
||
) <MA03>
|
||
|
||
#function-table(
|
||
"Grupēšanas kartītes pārbaude",
|
||
"MA04",
|
||
[
|
||
Veic jautājuma atbildes pārbaudi grupēšanas kartītes jautājuma veidam.
|
||
],
|
||
[
|
||
+ Negrupēto elementu simbolu virkņu saraksts;
|
||
+ Grupētu simbolu virkņu sarakstu saraksts;
|
||
+ Norāde uz grupēšanas kartītes objektu -- #question;
|
||
],
|
||
[
|
||
+ Katram nesagrupētam elementam uzstāda pareizuma pazīmi uz "nepareizs";
|
||
+ Katru grupai atbilžu un jautājuma objekta saraksta grupu izskata paralēli;
|
||
+ Elementiem, kas sakrīt abos sarakstos, atbilžu sarakstā uzstāda pareizuma
|
||
pazīmi uz "pareizs";
|
||
+ Elementiem, kas nav jautājuma atbildes grupā pareizuma pazīmi uz "nepareizs";
|
||
],
|
||
[
|
||
+ Negrupēto elementu simbolu virkņu saraksts ar pareizuma karodziņu vērtībām;
|
||
+ Grupētu simbolu virkņu sarakstu saraksts ar pareizuma karodziņu vērtībām;
|
||
],
|
||
[
|
||
Funkcijai nav paziņojumu.
|
||
],
|
||
) <MA04>
|
||
|
||
#function-table(
|
||
"Intervāla grupas atbildes sniegšana",
|
||
"MA05",
|
||
[
|
||
Izmaina jautājuma objektu atbilstoši izvēlētam grūtības vai izpildījuma
|
||
vērtējumam veicot kartītes atbildi. Atbilstošie intervāli tiek iegūti no
|
||
konfigurācijas.
|
||
],
|
||
[
|
||
+ Intervāla atbilde -- viens no: "nav atbildēts", "grūti", "vidēji",
|
||
"viegli", "ļoti viegli";
|
||
+ Jautājums -- #question\;
|
||
],
|
||
[
|
||
+ No konfigurācijas glabātuves tiek iegūts intervāls, kas atbilst sniegtai
|
||
atbildei;
|
||
+ Jautājumam tiek uzstādīts intervāls;
|
||
],
|
||
[
|
||
+ Izmainīts jautājums -- #question\;
|
||
],
|
||
[
|
||
Funkcijai nav paziņojumu.
|
||
],
|
||
) <MA05>
|
||
|
||
=== Konfigurācijas modulis
|
||
|
||
#indent-par([
|
||
Konfigurācijas modulis nodrošina parametru apstrādi, ko var personalizēt
|
||
saistībā ar mācīšanās procesu, prezentāciju uc. Šī moduļa funkcijas ir
|
||
izmantotas konfigurējot vērtības, kas tiek glabātas konfigurācijas failā un
|
||
tiek izmantotas risinājuma funkcionalitātei piemīt konfigurējamas
|
||
vērtības. Ar funkcijām saistītas datu plūsmas ir attēlotas moduļa 2. līmeņa
|
||
DPD (skat. @fig:kfdpd2 attēlu). Moduļa funkcijas tiek definētas tabulās
|
||
2.27.-2.29.
|
||
])
|
||
|
||
#figure(
|
||
caption: "Konfigurācijas moduļa 2.līmeņa datu plūsmas diagramma",
|
||
placement: auto,
|
||
image("img/KFDPD2.svg"),
|
||
) <kfdpd2>
|
||
|
||
|
||
#function-table(
|
||
"Konfigurācijas vērtības iegūšana",
|
||
"KF01",
|
||
[
|
||
Funkcijas iegūst vienu definēto konfigurācijas vērtību.
|
||
],
|
||
[
|
||
+ Vērtības atslēga -- simbolu virkne;
|
||
],
|
||
[
|
||
+ Ja konfigurācija vēl nebija ielādēta, tiek ielādēts konfigurācijas fails;
|
||
+ Ielādētā konfigurācijā tiek sameklēts konfigurācijas vērības ieraksts;
|
||
],
|
||
[
|
||
+ Konfigurācijas vērtība -- #configValue\;
|
||
],
|
||
[
|
||
+ Konfigurācijas fails nav korekts, nevar nolasīt konfigurāciju;
|
||
],
|
||
) <KF01>
|
||
|
||
#function-table(
|
||
"Konfigurācijas vērtību iegūšana",
|
||
"KF02",
|
||
[
|
||
Funkcija iegūst visas definētās konfigurācijas vērtības.
|
||
],
|
||
[
|
||
+ Vērtības atslēga -- simbolu virkne;
|
||
],
|
||
[
|
||
+ Ja konfigurācija vēl nebija ielādēta, tiek ielādēts konfigurācijas fails;
|
||
+ Ielādētā konfigurācijā tiek iegūtas vērtības;
|
||
],
|
||
[
|
||
+ Konfigurācijas vērtību vārdnīca -- konfigurācijas vērtības atslēga uz #configValue\;
|
||
],
|
||
[
|
||
+ Konfigurācijas fails nav korekts, nevar nolasīt konfigurāciju;
|
||
],
|
||
) <KF02>
|
||
|
||
#function-table(
|
||
"Konfigurācijas vērtības izmaiņa",
|
||
"KF03",
|
||
[
|
||
Funkcija izmaina vērtību no definētām konfigurācijas vērtībām.
|
||
],
|
||
[
|
||
+ Vērtības atslēga -- simbolu virkne;
|
||
+ Konfigurācijas vērtība -- #configValue\;
|
||
],
|
||
[
|
||
+ Ja konfigurācija vēl nebija ielādēta, tiek ielādēts konfigurācijas fails;
|
||
+ Konfigurācijas vērtība ar ievadīto atslēgu tiek izmainītu uz ievadīto konfigurācijas vērtību;
|
||
],
|
||
[
|
||
+ Izmainīta konfigurācijas faila saturs;
|
||
],
|
||
[
|
||
+ Konfigurācijas fails nav korekts, nevar rediģēt konfigurāciju;
|
||
],
|
||
) <KF03>
|
||
|
||
== Nefunkcionālās prasības
|
||
|
||
Šajā sadaļā tiks apskatītas risinājuma nefunkcionālās prasības.
|
||
|
||
=== Veikspējas prasības
|
||
|
||
Uz dinamisko veikspēju ir sekojošas prasības:
|
||
|
||
+ Failu transpilēšanas ātrums ir vismaz 50000 rindas sekundē, ja teksta vērtību
|
||
garumi nepārsniedz 15 simbolus;
|
||
+ Failu transpilēšanas ātrums ir vismaz 25000 rindas sekundē, ja teksta vērtību
|
||
garumi nepārsniedz 40 simbolus;
|
||
+ Failu detranspilēšanas (saglabāšanas) ātrums ir vismaz 25000 rindas sekundē,
|
||
ja teksta vērtību garumi nepārsniedz 40 simbolus;
|
||
|
||
=== Atribūti
|
||
|
||
==== Lietojamība
|
||
|
||
Uz risinājuma lietojamību ir sekojošas prasības:
|
||
|
||
+ Visiem datiem jābūt maināmiem un redzamiem no vienkārša teksta failiem;
|
||
+ Bieži izmantojamām darbībām jābūt atbilstošiem tastatūras taustiņu īsceļiem;
|
||
|
||
// @Complete: add some additional points
|
||
|
||
==== Pirmkoda īpašības un uzturamība
|
||
|
||
Pirmkodam jābūt izstrādātam ar sekojošiem nosacījumiem:
|
||
|
||
+ Risinājuma pirmkods ir realizēts galvenokārt procedurālā stilā;
|
||
+ Transpilatora funkcionalitātes testu pārklājums ir 90%;
|
||
|
||
==== Pārnesamība
|
||
|
||
#indent-par([
|
||
Uz pārnesamību risinājumam ir sekojoša prasība: kartīšu failiem ir jābūt pārnesamiem
|
||
pārkopējot visu direktoriju uz citu mašīnu. Nosacījums saglabā atgriezenisko savietojamību
|
||
vienas galvenās versijas ietvaros.
|
||
])
|
||
|
||
==== Drošība
|
||
|
||
Uz drošību risinājumam ir sekojošas prasības:
|
||
|
||
+ Risinājuma pirmkods ir iekļauts kopā ar izpildāmo failu;
|
||
+ Risinājums nemodificē un nelasa failus, izņemot izmantotās bibliotēkas, failu
|
||
glabātuvi, standarta konfigurācijas vietu;
|
||
|
||
#pagebreak(weak: true)
|
||
= Programmatūras projektējuma apraksts
|
||
|
||
== Daļējs funkciju projektējums
|
||
|
||
#figure(
|
||
caption: "Leksiskās analīzes aktivitātes diagramma",
|
||
placement: none,
|
||
image("img/lexer_activity.svg"),
|
||
) <lexing-activity>
|
||
|
||
#figure(
|
||
caption: "Parsēšanas aktivitātes diagramma",
|
||
placement: none,
|
||
image("img/parsing_activity.svg"),
|
||
) <parsing-acticity>
|
||
|
||
#figure(
|
||
caption: "Atbildes parsēšanas aktivitātes diagramma",
|
||
placement: none,
|
||
image("img/parsing_answer_activity.svg"),
|
||
) <parsing-answer-activity>
|
||
|
||
== Saskarnes projektējums
|
||
|
||
#figure(
|
||
caption: "Kartīšu saraksta skats",
|
||
placement: none,
|
||
image("img/screens/mdem_list.png"),
|
||
) <mdem-list-view>
|
||
|
||
#figure(
|
||
caption: "Atbildes jautājuma skats",
|
||
placement: auto,
|
||
image("img/screens/answer_question.png"),
|
||
) <answer-question-view>
|
||
|
||
#figure(
|
||
caption: "Izvēles jautājuma skats",
|
||
placement: auto,
|
||
image("img/screens/multiple_answer_question.png"),
|
||
) <choice-question-view>
|
||
|
||
#figure(
|
||
caption: "Secības jautājuma skats",
|
||
placement: auto,
|
||
image("img/screens/order_question.png"),
|
||
) <order-question-view>
|
||
|
||
#figure(
|
||
caption: "Grupēšanas jautājuma skats",
|
||
placement: auto,
|
||
image("img/screens/group_question.png"),
|
||
) <group-question-view>
|
||
|
||
#figure(
|
||
caption: "Konfigurācijas skats",
|
||
placement: none,
|
||
image("img/screens/config.png"),
|
||
) <config-view>
|
||
|
||
#figure(
|
||
caption: "Apmācības skats",
|
||
placement: none,
|
||
image("img/screens/help.png"),
|
||
) <help-view>
|
||
|
||
#pagebreak(weak: true)
|
||
= Testēšanas dokumentācija
|
||
|
||
#pagebreak(weak: true)
|
||
= Projekta organizācija
|
||
|
||
#pagebreak(weak: true)
|
||
= Kvalitātes nodrošināšana
|
||
|
||
#pagebreak(weak: true)
|
||
= Konfigurācijas pārvaldība
|
||
|
||
#pagebreak(weak: true)
|
||
= Darbietiplības novērtējums
|
||
|
||
#pagebreak(weak: true)
|
||
= Secinājumi
|
||
|
||
#pagebreak(weak: true)
|
||
#heading(numbering: none, "Izmantotā literatūra un avoti")
|
||
|
||
+ #hyperlink-source(
|
||
"Alison Voice and Arran Stirton",
|
||
[Spaced Repetition: towards more effective learning in STEM (2020)],
|
||
"https://web.archive.org/web/20171019211402/https://www.loc.gov/standards/datetime/ISO_DIS%208601-1.pdf",
|
||
std.datetime(
|
||
year: 2024,
|
||
month: 10,
|
||
day: 12,
|
||
),
|
||
)<spaced-repetition-1>
|
||
|
||
+ #hyperlink-source(
|
||
"Institūcija \"Latvijas standarts\"",
|
||
[LVS 68:1996 "Programmatūras prasību specifikācijas ceļvedis". 1996. marts],
|
||
"",
|
||
none
|
||
)<lvs-standard-1>
|
||
|
||
+ #hyperlink-source(
|
||
"Institūcija \"Latvijas standarts\"",
|
||
[LVS 72:1996 "Ieteicamā prakse programmatūras projektējuma aprakstīšanai". 1996, marts],
|
||
"",
|
||
none
|
||
)<lvs-standard-1>
|
||
|
||
|
||
+ #hyperlink-source(
|
||
"Paul Sexton",
|
||
[Org-drill.el – flashcards and spaced repetition for org-mode],
|
||
"https://orgmode.org/worg/org-contrib/org-drill.html",
|
||
std.datetime(
|
||
year: 2024,
|
||
month: 10,
|
||
day: 21,
|
||
),
|
||
)<org-drill>
|
||
|
||
+ #hyperlink-source(
|
||
"",
|
||
[What are flashcard sets?],
|
||
"https://help.quizlet.com/hc/en-us/articles/360032006352-What-are-flashcard-sets",
|
||
std.datetime(
|
||
year: 2024,
|
||
month: 10,
|
||
day: 21,
|
||
),
|
||
)<quizlet>
|
||
|
||
|
||
+ TODO UML 2.5. spec
|
||
|
||
#pagebreak(weak: true)
|
||
#heading(numbering: none, "Pielikumi")
|
||
|
||
// TODO caption
|
||
|
||
```cpp
|
||
std::vector<Token> tokens;
|
||
std::vector<char> buffer;
|
||
int32_t row;
|
||
int32_t column;
|
||
int32_t previousRow;
|
||
int32_t previousColumn;
|
||
bool textStarted = false;
|
||
bool identifierStarted = false;
|
||
bool sof;
|
||
|
||
/*
|
||
* TODO
|
||
*/
|
||
void trimString(std::string &str, std::string trimChars) {
|
||
|
||
// Noņem kreisās puses simbolus.
|
||
int padSize = 0;
|
||
bool pad = false;
|
||
for (size_t i = 0; i < str.size(); ++i) {
|
||
for (size_t k = 0; k < trimChars.size(); ++k) {
|
||
if (str[i] == trimChars[k]) {
|
||
padSize++;
|
||
pad = true;
|
||
break;
|
||
}
|
||
}
|
||
if (!pad) {
|
||
break;
|
||
}
|
||
pad = false;
|
||
}
|
||
if (padSize > 0) {
|
||
str.erase(0, padSize);
|
||
}
|
||
|
||
// Noņem labās puses simbolus.
|
||
padSize = 0;
|
||
pad = false;
|
||
for (size_t i = str.size(); i-- > 0;) {
|
||
for (size_t k = 0; k < trimChars.size(); ++k) {
|
||
if (str[i] == trimChars[k]) {
|
||
padSize++;
|
||
pad = true;
|
||
break;
|
||
}
|
||
}
|
||
if (!pad) {
|
||
break;
|
||
}
|
||
pad = false;
|
||
}
|
||
if (padSize > 0) {
|
||
str.erase(str.end() - padSize, str.end());
|
||
}
|
||
}
|
||
|
||
/*
|
||
* Izveido tekstvienību, iegūstot to no bufera beigām.
|
||
* Ja buferī ir teksta vienība pirms tekstvienības, pievieno to pirms beigu
|
||
* tekstvienības.
|
||
*/
|
||
void tokenWithBuffer(
|
||
TokenType ttype,
|
||
size_t tokenLen,
|
||
TokenType textType
|
||
) {
|
||
std::string token(buffer.end() - tokenLen, buffer.end());
|
||
if (buffer.size() > tokenLen) {
|
||
std::string prevFragment(buffer.begin(), buffer.end() - tokenLen);
|
||
trimString(prevFragment, " \n\t");
|
||
if (prevFragment.length() > 0) {
|
||
tokens.push_back(Token{
|
||
textType,
|
||
prevFragment,
|
||
previousRow,
|
||
previousColumn
|
||
});
|
||
}
|
||
}
|
||
buffer.clear();
|
||
|
||
tokens.push_back(Token{
|
||
ttype,
|
||
token,
|
||
row,
|
||
column
|
||
});
|
||
|
||
previousRow = row;
|
||
previousColumn = column;
|
||
buffer.clear();
|
||
}
|
||
|
||
/*
|
||
* Pārveido simbolu virkni tekstvienību sarakstā.
|
||
* */
|
||
Result<std::vector<Token>> tokenizeMdem(const std::string& content) {
|
||
row = 1;
|
||
column = 1;
|
||
previousRow = 1;
|
||
previousColumn = 1;
|
||
textStarted = false;
|
||
tokens.clear();
|
||
buffer.clear();
|
||
|
||
// Beidz, ja satur tikai tukšumus vai neko.
|
||
if (content.find_first_not_of(" \n\t") == std::string::npos) {
|
||
return {tokens, ""};
|
||
}
|
||
|
||
for (size_t i = 0; i < content.size(); ++i) {
|
||
char c = content[i];
|
||
|
||
// Apstrādā īpašos simbolus un tekstu.
|
||
if (c == '\n') {
|
||
row += 1;
|
||
column = 0;
|
||
}
|
||
if (c == '\\') {
|
||
i += 1;
|
||
if (i < content.size()) {
|
||
buffer.push_back(content[i]);
|
||
}
|
||
continue;
|
||
} else {
|
||
buffer.push_back(c);
|
||
}
|
||
if (!textStarted) {
|
||
if (c == '\n') {
|
||
previousRow += 1;
|
||
previousColumn = 1;
|
||
} else if (c == ' ') {
|
||
previousColumn += 1;
|
||
} else if (c == '\t') {
|
||
previousColumn += 4;
|
||
} else {
|
||
textStarted = true;
|
||
}
|
||
}
|
||
|
||
// Emitē tekstvienības.
|
||
switch (c) {
|
||
case '[': {
|
||
tokenWithBuffer(
|
||
TokenType::CooldownStart,
|
||
1,
|
||
TokenType::TextFragment
|
||
);
|
||
previousRow = row;
|
||
previousColumn = column;
|
||
textStarted = false;
|
||
identifierStarted = true;
|
||
} break;
|
||
case ']': {
|
||
if (!identifierStarted) {
|
||
return {
|
||
tokens,
|
||
"Cannot end identifier if it is not started",
|
||
tokens[i].row,
|
||
tokens[i].column
|
||
};
|
||
}
|
||
tokenWithBuffer(
|
||
TokenType::CooldownEnd,
|
||
1,
|
||
TokenType::Cooldown
|
||
);
|
||
previousRow = row;
|
||
previousColumn = column;
|
||
textStarted = false;
|
||
identifierStarted = false;
|
||
} break;
|
||
case '-': {
|
||
tokenWithBuffer(
|
||
TokenType::ElementDashStart,
|
||
1,
|
||
TokenType::TextFragment
|
||
);
|
||
previousRow = row;
|
||
previousColumn = column;
|
||
textStarted = false;
|
||
} break;
|
||
case '^': {
|
||
tokenWithBuffer(
|
||
TokenType::ElementOrderModifier,
|
||
1,
|
||
TokenType::TextFragment
|
||
);
|
||
previousRow = row;
|
||
previousColumn = column;
|
||
textStarted = false;
|
||
} break;
|
||
case ':': {
|
||
tokenWithBuffer(
|
||
TokenType::MatchGroupEnd,
|
||
1,
|
||
TokenType::TextFragment
|
||
);
|
||
previousRow = row;
|
||
previousColumn = column;
|
||
textStarted = false;
|
||
} break;
|
||
case '>': {
|
||
tokenWithBuffer(
|
||
TokenType::QuestionEnd,
|
||
1,
|
||
TokenType::TextFragment
|
||
);
|
||
previousRow = row;
|
||
previousColumn = column;
|
||
textStarted = false;
|
||
} break;
|
||
case '+': {
|
||
tokenWithBuffer(
|
||
TokenType::ElementPlusStart,
|
||
1,
|
||
TokenType::TextFragment
|
||
);
|
||
previousRow = row;
|
||
previousColumn = column;
|
||
textStarted = false;
|
||
} break;
|
||
}
|
||
|
||
column += 1;
|
||
}
|
||
|
||
// Pievieno beigu simbolu, lai atvieglotu parsēšanu.
|
||
tokenWithBuffer(
|
||
TokenType::EndOfFile,
|
||
0,
|
||
TokenType::TextFragment
|
||
);
|
||
|
||
if (debug) {
|
||
std::cout << "SECTION: Lexer output:\n";
|
||
std::cout << std::format("Token count: {}", tokens.size()) << std::endl;
|
||
for (const Token& token : tokens) {
|
||
std::cout << token.toString();
|
||
}
|
||
std::cout << "SECTION END: Lexer output\n";
|
||
}
|
||
|
||
return {tokens, ""};
|
||
}
|
||
|
||
std::string Token::toString(const TokenType* ttype) {
|
||
switch (*ttype) {
|
||
case TokenType::TextFragment: return "text fragment";
|
||
case TokenType::QuestionEnd: return "question end symbol";
|
||
case TokenType::MatchGroupEnd: return "match group end";
|
||
case TokenType::ElementDashStart: return "dash element start";
|
||
case TokenType::ElementOrderModifier: return "order element modifier";
|
||
case TokenType::ElementPlusStart: return "plus element start";
|
||
case TokenType::Cooldown: return "cooldown";
|
||
case TokenType::CooldownStart: return "start of cooldown";
|
||
case TokenType::CooldownEnd: return "end of cooldown";
|
||
case TokenType::StartOfFile: return "start of the file";
|
||
case TokenType::EndOfFile: return "end of file";
|
||
default: return "unrecognized token";
|
||
}
|
||
}
|
||
```
|
||
|
||
// TODO caption
|
||
|
||
```cpp
|
||
typedef std::map<TokenType, std::vector<TokenType>> TokenAutomata;
|
||
|
||
TokenAutomata *automata = nullptr;
|
||
/*
|
||
* Tekstvienību secības pārejas, kas nosaka, kādā secībā tekstvienības var būt.
|
||
* */
|
||
void initParserAutomata() {
|
||
automata = new TokenAutomata;
|
||
(*automata)[TokenType::TextFragment] = {
|
||
TokenType::QuestionEnd,
|
||
TokenType::ElementDashStart,
|
||
TokenType::ElementPlusStart,
|
||
TokenType::MatchGroupEnd,
|
||
TokenType::EndOfFile,
|
||
};
|
||
(*automata)[TokenType::MatchGroupEnd] = {
|
||
TokenType::ElementDashStart
|
||
};
|
||
(*automata)[TokenType::QuestionEnd] = {
|
||
TokenType::ElementDashStart,
|
||
TokenType::ElementPlusStart
|
||
};
|
||
(*automata)[TokenType::ElementDashStart] = {
|
||
TokenType::CooldownStart,
|
||
TokenType::TextFragment,
|
||
TokenType::ElementOrderModifier
|
||
};
|
||
(*automata)[TokenType::ElementOrderModifier] = {
|
||
TokenType::TextFragment
|
||
};
|
||
(*automata)[TokenType::ElementPlusStart] = {
|
||
TokenType::TextFragment
|
||
};
|
||
(*automata)[TokenType::Cooldown] = {
|
||
TokenType::CooldownEnd,
|
||
};
|
||
(*automata)[TokenType::CooldownStart] = {
|
||
TokenType::Cooldown
|
||
};
|
||
(*automata)[TokenType::CooldownEnd] = {
|
||
TokenType::TextFragment
|
||
};
|
||
(*automata)[TokenType::StartOfFile] = {
|
||
TokenType::TextFragment,
|
||
TokenType::ElementDashStart,
|
||
TokenType::EndOfFile
|
||
};
|
||
(*automata)[TokenType::EndOfFile] = {};
|
||
}
|
||
|
||
/*
|
||
* Pārbauda, vai vai tekstvienību sarakstu akceptē atbilst atbilst valodas
|
||
* automāts.
|
||
* */
|
||
Result<NoneType> ValidateGrammar(const std::vector<Token>& tokens) {
|
||
if (!automata) {
|
||
initParserAutomata();
|
||
}
|
||
for (size_t i = 0; i < tokens.size() - 1; ++i) {
|
||
Token token = tokens[i];
|
||
Token nextToken = tokens[i + 1];
|
||
if (
|
||
std::find(
|
||
(*automata)[token.tokenType].begin(),
|
||
(*automata)[token.tokenType].end(),
|
||
nextToken.tokenType
|
||
) == (*automata)[token.tokenType].end()) {
|
||
|
||
auto capitalize = [](const std::string& str) {
|
||
if (str.empty()) return str;
|
||
std::string result = str;
|
||
result[0] = std::towupper(result[0]);
|
||
return result;
|
||
};
|
||
return {
|
||
.error=std::format(
|
||
"Invalid token sequence: {} cannot precede {}",
|
||
std::string(capitalize(Token::toString(&token.tokenType))),
|
||
std::string(capitalize(Token::toString(&nextToken.tokenType)))
|
||
),
|
||
.row=token.row,
|
||
.column=token.column
|
||
};
|
||
}
|
||
}
|
||
return {};
|
||
}
|
||
|
||
// @Fix: Prevent duplicate group names and questions in ordered question (to
|
||
// simplify checking in practice).
|
||
Result<ParseInfo> parseQuestions(const std::vector<Token>& tokens) {
|
||
auto questions = std::vector<Question*>();
|
||
time_t time = 0;
|
||
|
||
auto makeResult = [&questions, &time](std::string error, Token token) -> Result<ParseInfo> {
|
||
return {
|
||
{ questions, time },
|
||
error,
|
||
token.row,
|
||
token.column
|
||
};
|
||
};
|
||
|
||
if (tokens.size() == 0) {
|
||
return makeResult("", Token());
|
||
}
|
||
|
||
auto result = ValidateGrammar(tokens);
|
||
if (result.error.length() > 0) {
|
||
return makeResult(
|
||
result.error,
|
||
Token{.row=result.row, .column=result.column}
|
||
);
|
||
}
|
||
|
||
std::string section;
|
||
size_t i = 0;
|
||
|
||
if (debug) {
|
||
std::cout << "SECTION: Parser output:\n";
|
||
}
|
||
|
||
auto isInBounds = [tokens](size_t i) {
|
||
return i < tokens.size() && tokens[i].tokenType != TokenType::EndOfFile;
|
||
};
|
||
|
||
if (isInBounds(i) && tokens[i].tokenType == TokenType::TextFragment) {
|
||
try {
|
||
auto parseToUTCTime = [](const std::string datetime, std::string format) {
|
||
std::tm tm = {};
|
||
std::istringstream ss(datetime);
|
||
ss >> std::get_time(&tm, format.c_str());
|
||
if (ss.fail()) {
|
||
throw std::runtime_error("Failed to parse datetime string");
|
||
}
|
||
std::time_t time = timegm(&tm);
|
||
return time;
|
||
};
|
||
|
||
time = parseToUTCTime(tokens[i].content.c_str(), "%d.%m.%Y %H:%M");
|
||
} catch (std::exception e) {
|
||
return makeResult(
|
||
std::format("cannot parse the time - {}", e.what()),
|
||
tokens[i]
|
||
);
|
||
}
|
||
i++;
|
||
}
|
||
|
||
while (i < tokens.size()) {
|
||
if (tokens[i].tokenType == TokenType::ElementDashStart) {
|
||
std::string questionText;
|
||
std::vector<QuestionElement> questionElements;
|
||
double cooldown;
|
||
bool isOrderQuestion = false;
|
||
bool isGroupQuestion = false;
|
||
bool isPlusQuestion = false;
|
||
|
||
// Start element parsing & add to the offset.
|
||
if (isInBounds(i + 1) && tokens[i + 1].tokenType == TokenType::ElementOrderModifier) {
|
||
return makeResult(
|
||
"cannot have order modifier ('^') in the question definition",
|
||
tokens[i + 1]
|
||
);
|
||
}
|
||
if (isInBounds(i + 1) && tokens[i + 1].tokenType == TokenType::CooldownStart) {
|
||
try {
|
||
cooldown = std::stod(tokens[i + 2].content);
|
||
} catch (std::exception e) {
|
||
return makeResult(
|
||
"error parsing cooldown",
|
||
tokens[i + 1]
|
||
);
|
||
}
|
||
questionText = tokens[i + 4].content;
|
||
i += 6;
|
||
} else {
|
||
cooldown = 0;
|
||
questionText = tokens[i + 1].content;
|
||
i += 3;
|
||
}
|
||
|
||
// Parse elements of a question.
|
||
while (isInBounds(i)) {
|
||
|
||
// Check question end.
|
||
if (isInBounds(i + 3) && tokens[i].tokenType == TokenType::ElementDashStart) {
|
||
// Distance to the possible question end.
|
||
size_t offset;
|
||
if (tokens[i + 1].tokenType == TokenType::ElementOrderModifier) {
|
||
offset = tokens[i + 2].tokenType == TokenType::CooldownStart ? 6 : 3;
|
||
} else {
|
||
offset = tokens[i + 1].tokenType == TokenType::CooldownStart ? 5 : 2;
|
||
}
|
||
if (isInBounds(i + offset) && tokens[i + offset].tokenType == TokenType::QuestionEnd) {
|
||
break;
|
||
}
|
||
if (offset == 5 && tokens[i + 5].tokenType != TokenType::QuestionEnd) {
|
||
// Cannot place the identifier on the ordinary element.
|
||
return makeResult(
|
||
"Invalid identifier placement",
|
||
tokens[i]
|
||
);
|
||
}
|
||
}
|
||
|
||
// Determine element type.
|
||
bool isDash;
|
||
bool isGroup = false;
|
||
bool isOrder = false;
|
||
if (tokens[i].tokenType == TokenType::ElementDashStart) {
|
||
isDash = true;
|
||
} else {
|
||
isDash = false;
|
||
isPlusQuestion = true;
|
||
}
|
||
if (isInBounds(i+1) && tokens[i + 1].tokenType == TokenType::ElementOrderModifier) {
|
||
isOrder = true;
|
||
isOrderQuestion = true;
|
||
if (!isDash) {
|
||
return makeResult(
|
||
"order questions can only be used with dashes ('-')",
|
||
tokens[i]
|
||
);
|
||
}
|
||
if (isGroupQuestion) {
|
||
return makeResult(
|
||
"question with groups cannot be ordered ('-^' and ':')",
|
||
tokens[i]
|
||
);
|
||
}
|
||
if (isInBounds(i + 3) && tokens[i + 3].tokenType == TokenType::MatchGroupEnd) {
|
||
return makeResult(
|
||
"cannot have groups in order question('-^' and ':')",
|
||
tokens[i]
|
||
);
|
||
}
|
||
}
|
||
if (isInBounds(i + 2) && tokens[i + 2].tokenType == TokenType::MatchGroupEnd) {
|
||
isGroup = true;
|
||
isGroupQuestion = true;
|
||
if (!isDash) {
|
||
return makeResult(
|
||
"group questions can only be used with dashes ('-')",
|
||
tokens[i]
|
||
);
|
||
}
|
||
}
|
||
|
||
QuestionElement questionElement;
|
||
questionElement.isDash = isDash;
|
||
questionElement.isGroup = isGroup;
|
||
if (isOrder) {
|
||
questionElement.content = tokens[i + 2].content;
|
||
} else {
|
||
questionElement.content = tokens[i + 1].content;
|
||
}
|
||
questionElements.push_back(questionElement);
|
||
|
||
size_t offset = 2;
|
||
if (isOrder) {
|
||
offset += 1;
|
||
}
|
||
if (isGroup) {
|
||
offset += 1;
|
||
}
|
||
|
||
i += offset;
|
||
}
|
||
|
||
if (questionElements.size() > 0) {
|
||
if (isGroupQuestion) {
|
||
auto *question = new GroupQuestion();
|
||
question->cooldown = cooldown;
|
||
question->questionText = questionText;
|
||
question->section = section;
|
||
int32_t k = -1;
|
||
for (size_t i = 0; i < questionElements.size(); ++i) {
|
||
auto questionElement = questionElements[i];
|
||
if (questionElement.isGroup) {
|
||
++k;
|
||
auto group = Group();
|
||
group.name = cleanContent(questionElement.content);
|
||
question->groups.push_back(group);
|
||
} else {
|
||
if (k >= 0) {
|
||
question->groups[k].elements.push_back(
|
||
cleanContent(
|
||
questionElement.content
|
||
)
|
||
);
|
||
}
|
||
}
|
||
}
|
||
questions.push_back(question);
|
||
if (debug) {
|
||
std::cout << question->toString() << "\n";
|
||
}
|
||
} else {
|
||
auto *question = new MultiElementQuestion();
|
||
question->cooldown = cooldown;
|
||
question->questionText = cleanContent(questionText);
|
||
question->section = section;
|
||
for (const auto& elem : questionElements) {
|
||
Choice choice;
|
||
choice.answer = cleanContent(elem.content);
|
||
choice.isCorrect = !elem.isDash;
|
||
question->choices.push_back(choice);
|
||
}
|
||
questions.push_back(question);
|
||
if (isPlusQuestion) {
|
||
question->type = MultiElementType::MultiChoice;
|
||
} else if (isOrderQuestion) {
|
||
question->type = MultiElementType::Order;
|
||
} else {
|
||
question->type = MultiElementType::Regular;
|
||
}
|
||
if (debug) {
|
||
std::cout << question->toString() << "\n";
|
||
}
|
||
}
|
||
}
|
||
} else if (tokens[i].tokenType == TokenType::EndOfFile) {
|
||
if (debug) {
|
||
std::cout << "File terminated: EndOfFile\n";
|
||
}
|
||
break;
|
||
} else {
|
||
return makeResult(
|
||
"Unexpected token encountered",
|
||
tokens[i]
|
||
);
|
||
}
|
||
}
|
||
|
||
if (debug) {
|
||
std::cout << "SECTION END: Parser output:\n";
|
||
}
|
||
return makeResult(
|
||
"",
|
||
Token()
|
||
);
|
||
}
|
||
|
||
std::string MultiElementQuestion::toString() const {
|
||
std::stringstream ss;
|
||
for (const auto& choice : choices) {
|
||
char opener;
|
||
if (type == MultiElementType::Order) {
|
||
opener = '^';
|
||
} else if (choice.isCorrect) {
|
||
opener = '+';
|
||
} else {
|
||
opener = '-';
|
||
}
|
||
ss << opener << " " << choice.answer << "; ";
|
||
}
|
||
return std::format(
|
||
"<Multiple element>\nsection:{}\nid:{}\n{}\n{}",
|
||
section,
|
||
cooldown,
|
||
questionText,
|
||
ss.str()
|
||
);
|
||
}
|
||
|
||
std::string GroupQuestion::toString() const {
|
||
std::stringstream ss;
|
||
for (auto group: groups) {
|
||
ss << group.name << ": ";
|
||
for (auto el: group.elements) {
|
||
ss << el << ", ";
|
||
}
|
||
ss << "; ";
|
||
}
|
||
return std::format(
|
||
"<GroupQuestion>\nsection:{}\nid:{}\n{}\n{}",
|
||
section,
|
||
cooldown,
|
||
questionText,
|
||
ss.str()
|
||
);
|
||
}
|
||
```
|