Open technical graphics as four years of AutoCAD.

Each lesson has a method, a dimensioned assignment and a quiz, from the drawing sheet through the exam model.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
pepaskopec-blip 2026-10-11 13:44:38 +02:00
commit 6662eb185e
84 changed files with 3355 additions and 6 deletions

View file

@ -30,6 +30,8 @@ import org.maturita.maturita.ui.HwQuizScreen
import org.maturita.maturita.ui.HwYearsScreen
import org.maturita.maturita.ui.LessonListScreen
import org.maturita.maturita.ui.FyzQuizScreen
import org.maturita.maturita.ui.TgQuizScreen
import org.maturita.maturita.ui.TgYearsScreen
import org.maturita.maturita.ui.MathPracticeScreen
import org.maturita.maturita.ui.MatYearsScreen
import org.maturita.maturita.ui.FyzYearsScreen
@ -525,6 +527,63 @@ private fun MaturitaAppBody(vm: AppViewModel) {
}
}
is Route.Fyz4Ex -> FyzQuizScreen(vm, 4, r.id)
Route.TgYears -> TgYearsScreen(vm)
Route.TgMap -> LessonListScreen(
vm, vm.tr("tg_year1"), vm.tr("tg_sub"),
vm.content.tg.map { l ->
val id = l.int("id")
LessonRow(vm.tr(l.str("titleKey")), vm.progress.tgDone(id), Route.TgLesson(id))
},
)
is Route.TgLesson -> {
val l = vm.content.tgLesson(r.id)
if (l != null) SlidesScreen(vm, vm.tr(l.str("titleKey")), vm.tr(l.str("subKey")), l.arr("slides")) {
vm.replace(Route.TgEx(r.id))
}
}
is Route.TgEx -> TgQuizScreen(vm, 1, r.id)
Route.Tg2Map -> LessonListScreen(
vm, vm.tr("tg_year2"), vm.tr("tg2_sub"),
vm.content.tg2.map { l ->
val id = l.int("id")
LessonRow(vm.tr(l.str("titleKey")), vm.progress.tg2Done(id), Route.Tg2Lesson(id))
},
)
is Route.Tg2Lesson -> {
val l = vm.content.tg2Lesson(r.id)
if (l != null) SlidesScreen(vm, vm.tr(l.str("titleKey")), vm.tr(l.str("subKey")), l.arr("slides")) {
vm.replace(Route.Tg2Ex(r.id))
}
}
is Route.Tg2Ex -> TgQuizScreen(vm, 2, r.id)
Route.Tg3Map -> LessonListScreen(
vm, vm.tr("tg_year3"), vm.tr("tg3_sub"),
vm.content.tg3.map { l ->
val id = l.int("id")
LessonRow(vm.tr(l.str("titleKey")), vm.progress.tg3Done(id), Route.Tg3Lesson(id))
},
)
is Route.Tg3Lesson -> {
val l = vm.content.tg3Lesson(r.id)
if (l != null) SlidesScreen(vm, vm.tr(l.str("titleKey")), vm.tr(l.str("subKey")), l.arr("slides")) {
vm.replace(Route.Tg3Ex(r.id))
}
}
is Route.Tg3Ex -> TgQuizScreen(vm, 3, r.id)
Route.Tg4Map -> LessonListScreen(
vm, vm.tr("tg_year4"), vm.tr("tg4_sub"),
vm.content.tg4.map { l ->
val id = l.int("id")
LessonRow(vm.tr(l.str("titleKey")), vm.progress.tg4Done(id), Route.Tg4Lesson(id))
},
)
is Route.Tg4Lesson -> {
val l = vm.content.tg4Lesson(r.id)
if (l != null) SlidesScreen(vm, vm.tr(l.str("titleKey")), vm.tr(l.str("subKey")), l.arr("slides")) {
vm.replace(Route.Tg4Ex(r.id))
}
}
is Route.Tg4Ex -> TgQuizScreen(vm, 4, r.id)
Route.SciMap -> SciMapScreen(vm)
Route.ChemMap -> LessonListScreen(
vm, vm.tr("Chemie"), vm.tr("chem_sub"),

View file

@ -242,6 +242,26 @@ class AppViewModel(val content: Content, val platform: Platform) {
refresh()
}
fun markTg(id: Int) {
progress.markTg(id)
refresh()
}
fun markTg2(id: Int) {
progress.markTg2(id)
refresh()
}
fun markTg3(id: Int) {
progress.markTg3(id)
refresh()
}
fun markTg4(id: Int) {
progress.markTg4(id)
refresh()
}
fun markMluv(n: Int) {
progress.markMluv(n)
refresh()

View file

@ -105,6 +105,19 @@ sealed class Route(val page: String) {
data object Fyz4Map : Route("fyz4map")
data class Fyz4Lesson(val id: Int) : Route("fyz4unit$id")
data class Fyz4Ex(val id: Int) : Route("fyz4ex$id")
data object TgYears : Route("tgyears")
data object TgMap : Route("tgmap")
data class TgLesson(val id: Int) : Route("tgunit$id")
data class TgEx(val id: Int) : Route("tgex$id")
data object Tg2Map : Route("tg2map")
data class Tg2Lesson(val id: Int) : Route("tg2unit$id")
data class Tg2Ex(val id: Int) : Route("tg2ex$id")
data object Tg3Map : Route("tg3map")
data class Tg3Lesson(val id: Int) : Route("tg3unit$id")
data class Tg3Ex(val id: Int) : Route("tg3ex$id")
data object Tg4Map : Route("tg4map")
data class Tg4Lesson(val id: Int) : Route("tg4unit$id")
data class Tg4Ex(val id: Int) : Route("tg4ex$id")
data object CzechMap : Route("czechmap")
data object Mluvnice : Route("mluvnice")
data class MluvEx(val n: Int) : Route("mluve$n")
@ -237,6 +250,19 @@ sealed class Route(val page: String) {
Fyz4Map -> listOf(Subjects, FyzYears, Fyz4Map)
is Fyz4Lesson -> listOf(Subjects, FyzYears, Fyz4Map, this)
is Fyz4Ex -> listOf(Subjects, FyzYears, Fyz4Map, Fyz4Lesson(id), this)
TgYears -> listOf(Subjects, TgYears)
TgMap -> listOf(Subjects, TgYears, TgMap)
is TgLesson -> listOf(Subjects, TgYears, TgMap, this)
is TgEx -> listOf(Subjects, TgYears, TgMap, TgLesson(id), this)
Tg2Map -> listOf(Subjects, TgYears, Tg2Map)
is Tg2Lesson -> listOf(Subjects, TgYears, Tg2Map, this)
is Tg2Ex -> listOf(Subjects, TgYears, Tg2Map, Tg2Lesson(id), this)
Tg3Map -> listOf(Subjects, TgYears, Tg3Map)
is Tg3Lesson -> listOf(Subjects, TgYears, Tg3Map, this)
is Tg3Ex -> listOf(Subjects, TgYears, Tg3Map, Tg3Lesson(id), this)
Tg4Map -> listOf(Subjects, TgYears, Tg4Map)
is Tg4Lesson -> listOf(Subjects, TgYears, Tg4Map, this)
is Tg4Ex -> listOf(Subjects, TgYears, Tg4Map, Tg4Lesson(id), this)
CzechMap -> listOf(Subjects, CzechMap)
Mluvnice -> listOf(Subjects, CzechMap, Mluvnice)
is MluvEx -> listOf(Subjects, CzechMap, Mluvnice, this)
@ -289,6 +315,11 @@ fun routeFromPage(page: String): Route? = when (page) {
"fyz2map" -> Route.Fyz2Map
"fyz3map" -> Route.Fyz3Map
"fyz4map" -> Route.Fyz4Map
"tgyears" -> Route.TgYears
"tgmap" -> Route.TgMap
"tg2map" -> Route.Tg2Map
"tg3map" -> Route.Tg3Map
"tg4map" -> Route.Tg4Map
"czechmap" -> Route.CzechMap
"mluvnice" -> Route.Mluvnice
"readinglist" -> Route.ReadingList
@ -386,6 +417,30 @@ fun routeFromPage(page: String): Route? = when (page) {
Regex("""fyzex(\d+)""").matchEntire(page)?.let {
return Route.FyzEx(it.groupValues[1].toInt())
}
Regex("""tg4unit(\d+)""").matchEntire(page)?.let {
return Route.Tg4Lesson(it.groupValues[1].toInt())
}
Regex("""tg4ex(\d+)""").matchEntire(page)?.let {
return Route.Tg4Ex(it.groupValues[1].toInt())
}
Regex("""tg3unit(\d+)""").matchEntire(page)?.let {
return Route.Tg3Lesson(it.groupValues[1].toInt())
}
Regex("""tg3ex(\d+)""").matchEntire(page)?.let {
return Route.Tg3Ex(it.groupValues[1].toInt())
}
Regex("""tg2unit(\d+)""").matchEntire(page)?.let {
return Route.Tg2Lesson(it.groupValues[1].toInt())
}
Regex("""tg2ex(\d+)""").matchEntire(page)?.let {
return Route.Tg2Ex(it.groupValues[1].toInt())
}
Regex("""tgunit(\d+)""").matchEntire(page)?.let {
return Route.TgLesson(it.groupValues[1].toInt())
}
Regex("""tgex(\d+)""").matchEntire(page)?.let {
return Route.TgEx(it.groupValues[1].toInt())
}
Regex("""lit4unit(\d+)""").matchEntire(page)?.let {
return Route.Lit4Lesson(it.groupValues[1].toInt())
}

View file

@ -56,6 +56,10 @@ class Content(root: JSONObject) {
val fyz2 = raw.arr("fyz2")
val fyz3 = raw.arr("fyz3")
val fyz4 = raw.arr("fyz4")
val tg = raw.arr("tg")
val tg2 = raw.arr("tg2")
val tg3 = raw.arr("tg3")
val tg4 = raw.arr("tg4")
val mat0 = raw.arr("mat0")
val mat = raw.arr("mat")
val mat2 = raw.arr("mat2")
@ -108,6 +112,10 @@ class Content(root: JSONObject) {
fun fyz2Lesson(id: Int) = fyz2.firstOrNull { it.int("id") == id }
fun fyz3Lesson(id: Int) = fyz3.firstOrNull { it.int("id") == id }
fun fyz4Lesson(id: Int) = fyz4.firstOrNull { it.int("id") == id }
fun tgLesson(id: Int) = tg.firstOrNull { it.int("id") == id }
fun tg2Lesson(id: Int) = tg2.firstOrNull { it.int("id") == id }
fun tg3Lesson(id: Int) = tg3.firstOrNull { it.int("id") == id }
fun tg4Lesson(id: Int) = tg4.firstOrNull { it.int("id") == id }
fun mat0Lesson(id: Int) = mat0.firstOrNull { it.int("id") == id }
fun matLesson(id: Int) = mat.firstOrNull { it.int("id") == id }
fun mat2Lesson(id: Int) = mat2.firstOrNull { it.int("id") == id }

View file

@ -91,6 +91,14 @@ class ProgressStore(private val prefs: Prefs, content: Content) {
fun markFyz3(id: Int) { mark("fyz3.$id") }
fun fyz4Done(id: Int) = done("fyz4.$id")
fun markFyz4(id: Int) { mark("fyz4.$id") }
fun tgDone(id: Int) = done("tg.$id")
fun markTg(id: Int) { mark("tg.$id") }
fun tg2Done(id: Int) = done("tg2.$id")
fun markTg2(id: Int) { mark("tg2.$id") }
fun tg3Done(id: Int) = done("tg3.$id")
fun markTg3(id: Int) { mark("tg3.$id") }
fun tg4Done(id: Int) = done("tg4.$id")
fun markTg4(id: Int) { mark("tg4.$id") }
fun mat0Done(id: Int) = done("mat0.$id")
fun markMat0(id: Int) { mark("mat0.$id") }
fun matDone(id: Int) = done("mat.$id")
@ -280,6 +288,38 @@ fun summarize(content: Content, p: ProgressStore): ProgressSum {
sum.doneUnits += 1
}
}
content.tg.forEach { l ->
sum.totalEx += 1
sum.openUnits += 1
if (p.tgDone(l.int("id"))) {
sum.doneEx += 1
sum.doneUnits += 1
}
}
content.tg2.forEach { l ->
sum.totalEx += 1
sum.openUnits += 1
if (p.tg2Done(l.int("id"))) {
sum.doneEx += 1
sum.doneUnits += 1
}
}
content.tg3.forEach { l ->
sum.totalEx += 1
sum.openUnits += 1
if (p.tg3Done(l.int("id"))) {
sum.doneEx += 1
sum.doneUnits += 1
}
}
content.tg4.forEach { l ->
sum.totalEx += 1
sum.openUnits += 1
if (p.tg4Done(l.int("id"))) {
sum.doneEx += 1
sum.doneUnits += 1
}
}
content.mat0.forEach { l ->
sum.totalEx += 1
sum.openUnits += 1

View file

@ -1082,6 +1082,30 @@ fun ChoiceLessonScreen(vm: AppViewModel, lesson: J?, onDone: () -> Unit) {
DispatchExercise(vm, spec, vm.tr(lesson.str("exTitleKey")), onDone)
}
@Composable
fun TgQuizScreen(vm: AppViewModel, year: Int, id: Int) {
val lesson = when (year) {
2 -> vm.content.tg2Lesson(id)
3 -> vm.content.tg3Lesson(id)
4 -> vm.content.tg4Lesson(id)
else -> vm.content.tgLesson(id)
} ?: return
val spec = J(org.json.JSONObject().apply {
put("type", "choice")
put("title", vm.tr(lesson.str("exTitleKey")))
put("sub", lesson.str("quizHeadKey"))
put("questions", lesson.o.optJSONArray("quiz"))
})
DispatchExercise(vm, spec, vm.tr(lesson.str("exTitleKey"))) {
when (year) {
2 -> vm.markTg2(id)
3 -> vm.markTg3(id)
4 -> vm.markTg4(id)
else -> vm.markTg(id)
}
}
}
@Composable
fun FyzQuizScreen(vm: AppViewModel, year: Int, id: Int) {
val lesson = when (year) {

View file

@ -25,6 +25,7 @@ import androidx.compose.foundation.verticalScroll
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.automirrored.filled.ArrowBack
import androidx.compose.material.icons.automirrored.filled.MenuBook
import androidx.compose.material.icons.filled.Architecture
import androidx.compose.material.icons.filled.Bolt
import androidx.compose.material.icons.filled.Functions
import androidx.compose.material.icons.filled.Check
@ -225,6 +226,7 @@ private fun SubjectIcon(icon: String, open: Boolean) {
"flask" -> Icon(Icons.Filled.Science, null, tint = tint, modifier = Modifier.size(26.dp))
"bolt" -> Icon(Icons.Filled.Bolt, null, tint = tint, modifier = Modifier.size(26.dp))
"function" -> Icon(Icons.Filled.Functions, null, tint = tint, modifier = Modifier.size(26.dp))
"ruler" -> Icon(Icons.Filled.Architecture, null, tint = tint, modifier = Modifier.size(26.dp))
else -> Icon(Icons.Filled.Lock, null, tint = tint)
}
}
@ -348,6 +350,13 @@ private fun subjectSum(vm: AppViewModel, s: J): ProgressSum {
vm.content.fyz3.count { vm.progress.fyz3Done(it.int("id")) } +
vm.content.fyz4.count { vm.progress.fyz4Done(it.int("id")) }
}
"tgyears", "tgmap", "tg2map", "tg3map", "tg4map" -> {
sum.totalEx = vm.content.tg.size + vm.content.tg2.size + vm.content.tg3.size + vm.content.tg4.size
sum.doneEx = vm.content.tg.count { vm.progress.tgDone(it.int("id")) } +
vm.content.tg2.count { vm.progress.tg2Done(it.int("id")) } +
vm.content.tg3.count { vm.progress.tg3Done(it.int("id")) } +
vm.content.tg4.count { vm.progress.tg4Done(it.int("id")) }
}
"czechmap" -> {
val litDone = vm.content.lit.count { vm.progress.litDone(it.int("id")) } +
vm.content.lit2.count { vm.progress.lit2Done(it.int("id")) } +
@ -649,6 +658,27 @@ private fun buildSearch(vm: AppViewModel): List<Hit> {
add(vm.tr(l.str("titleKey")), vm.tr("search_lesson"), "fyz4unit${l.int("id")}", extra = "fyzika")
add(vm.tr(l.str("titleKey")), vm.tr("search_exercise"), "fyz4ex${l.int("id")}")
}
add(vm.tr("Technická grafika"), vm.tr("search_lesson"), "tgyears", extra = "autocad grafika")
add(vm.tr("tg_year1"), vm.tr("search_lesson"), "tgmap", extra = "rocnik")
add(vm.tr("tg_year2"), vm.tr("search_lesson"), "tg2map", extra = "rocnik")
add(vm.tr("tg_year3"), vm.tr("search_lesson"), "tg3map", extra = "rocnik")
add(vm.tr("tg_year4"), vm.tr("search_lesson"), "tg4map", extra = "rocnik")
vm.content.tg.forEach { l ->
add(vm.tr(l.str("titleKey")), vm.tr("search_lesson"), "tgunit${l.int("id")}", extra = "autocad")
add(vm.tr(l.str("titleKey")), vm.tr("search_exercise"), "tgex${l.int("id")}")
}
vm.content.tg2.forEach { l ->
add(vm.tr(l.str("titleKey")), vm.tr("search_lesson"), "tg2unit${l.int("id")}", extra = "autocad")
add(vm.tr(l.str("titleKey")), vm.tr("search_exercise"), "tg2ex${l.int("id")}")
}
vm.content.tg3.forEach { l ->
add(vm.tr(l.str("titleKey")), vm.tr("search_lesson"), "tg3unit${l.int("id")}", extra = "autocad")
add(vm.tr(l.str("titleKey")), vm.tr("search_exercise"), "tg3ex${l.int("id")}")
}
vm.content.tg4.forEach { l ->
add(vm.tr(l.str("titleKey")), vm.tr("search_lesson"), "tg4unit${l.int("id")}", extra = "autocad")
add(vm.tr(l.str("titleKey")), vm.tr("search_exercise"), "tg4ex${l.int("id")}")
}
add(vm.tr("Chemie"), vm.tr("search_lesson"), "chemmap", extra = "chemie")
add(vm.tr("Biologie"), vm.tr("search_lesson"), "biomap", extra = "biologie")
vm.content.chem.forEach { l ->
@ -848,6 +878,20 @@ fun MatYearsScreen(vm: AppViewModel) {
}
}
@Composable
fun TgYearsScreen(vm: AppViewModel) {
DetailScaffold(vm, vm.tr("Technická grafika"), vm.tr("tg_years_sub")) { inner ->
LazyColumn(contentPadding = PaddingValues(top = inner.calculateTopPadding(), bottom = inner.calculateBottomPadding() + 16.dp)) {
item {
NavRow(title = vm.tr("tg_year1"), subtitle = vm.tr("tg_sub"), leading = { Text("1.", style = MaterialTheme.typography.titleMedium) }) { vm.go(Route.TgMap) }
NavRow(title = vm.tr("tg_year2"), subtitle = vm.tr("tg2_sub"), leading = { Text("2.", style = MaterialTheme.typography.titleMedium) }) { vm.go(Route.Tg2Map) }
NavRow(title = vm.tr("tg_year3"), subtitle = vm.tr("tg3_sub"), leading = { Text("3.", style = MaterialTheme.typography.titleMedium) }) { vm.go(Route.Tg3Map) }
NavRow(title = vm.tr("tg_year4"), subtitle = vm.tr("tg4_sub"), leading = { Text("4.", style = MaterialTheme.typography.titleMedium) }) { vm.go(Route.Tg4Map) }
}
}
}
}
@Composable
fun FyzYearsScreen(vm: AppViewModel) {
DetailScaffold(vm, vm.tr("Fyzika"), vm.tr("fyz_years_sub")) { inner ->