mirror of
https://github.com/pepaskopec-blip/Graduately.git
synced 2026-10-27 07:04:35 +00:00
Add a C# course with a side-by-side code runner.
Each lesson explains a topic and runs the edited program next to the text through the local .NET SDK. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
parent
6662eb185e
commit
333085ee53
86 changed files with 2482 additions and 5 deletions
|
|
@ -73,6 +73,8 @@ Intel Mac, jiná architektura nebo úpravy kódu → [sestavení ze zdroje](#ses
|
||||||
ročníky 2–4 po 8 lekcích (monitory, sestavení, RAID, servery, údržba)
|
ročníky 2–4 po 8 lekcích (monitory, sestavení, RAID, servery, údržba)
|
||||||
- **Technická grafika** — 4 ročníky AutoCADu, v každém 8 lekcí:
|
- **Technická grafika** — 4 ročníky AutoCADu, v každém 8 lekcí:
|
||||||
výkres a souřadnice, úpravy a kóty, pohledy a tisk, 3D a maturitní zadání
|
výkres a souřadnice, úpravy a kóty, pohledy a tisk, 3D a maturitní zadání
|
||||||
|
- **Programování** — 4 ročníky C# po vzoru W3Schools. U každé lekce se kód
|
||||||
|
píše a spouští vedle výkladu, od výpisu po třídy, LINQ a async
|
||||||
- **Český jazyk a literatura** — literatura (4 ročníky, 32 lekcí podle osnov
|
- **Český jazyk a literatura** — literatura (4 ročníky, 32 lekcí podle osnov
|
||||||
středních škol), mluvnice (20 cvičení) a maturitní četba
|
středních škol), mluvnice (20 cvičení) a maturitní četba
|
||||||
(82 knih: zápisky, kvíz, sestavení děje)
|
(82 knih: zápisky, kvíz, sestavení děje)
|
||||||
|
|
@ -92,7 +94,7 @@ Intel Mac, jiná architektura nebo úpravy kódu → [sestavení ze zdroje](#ses
|
||||||
|
|
||||||
### Použití
|
### Použití
|
||||||
|
|
||||||
1. **Úvod** ukáže postup a mřížku předmětů. Otevřený předmět spustí cestu. Zamčené dlaždice zatím nic neotevřou: prezentační grafika, programování a programové vybavení.
|
1. **Úvod** ukáže postup a mřížku předmětů. Otevřený předmět spustí cestu. Zamčené dlaždice zatím nic neotevřou: prezentační grafika a programové vybavení.
|
||||||
2. U němčiny je učebnice (Neue Freunde, Aus aller Welt, Bei uns zu Hause) a čtyři ročníky. Ostatní otevřené předměty mají ročníky, lekce nebo knihy.
|
2. U němčiny je učebnice (Neue Freunde, Aus aller Welt, Bei uns zu Hause) a čtyři ročníky. Ostatní otevřené předměty mají ročníky, lekce nebo knihy.
|
||||||
3. Z cesty se otevře výklad, poslech, čtení, psaní nebo kvíz.
|
3. Z cesty se otevře výklad, poslech, čtení, psaní nebo kvíz.
|
||||||
4. **Statistiky**, **Nastavení** a **Hledat** jsou v postranním panelu na Macu. Na telefonu a v desktopové aplikaci jsou na záložkách, hledání navíc ikonou lupy. Zkratka je `Cmd/Ctrl+K`.
|
4. **Statistiky**, **Nastavení** a **Hledat** jsou v postranním panelu na Macu. Na telefonu a v desktopové aplikaci jsou na záložkách, hledání navíc ikonou lupy. Zkratka je `Cmd/Ctrl+K`.
|
||||||
|
|
@ -263,6 +265,8 @@ Intel Mac, another architecture, or hacking on the code →
|
||||||
years 2–4 have 8 lessons each (displays, assembly, RAID, servers, maintenance)
|
years 2–4 have 8 lessons each (displays, assembly, RAID, servers, maintenance)
|
||||||
- **Technical graphics** — 4 years of AutoCAD, 8 lessons each:
|
- **Technical graphics** — 4 years of AutoCAD, 8 lessons each:
|
||||||
the sheet and coordinates, editing and dimensions, views and plotting, 3D and the exam drawing
|
the sheet and coordinates, editing and dimensions, views and plotting, 3D and the exam drawing
|
||||||
|
- **Programming** — 4 years of C# in the style of W3Schools. Each lesson has
|
||||||
|
code you edit and run beside the explanation, from output to classes, LINQ and async
|
||||||
- **Czech language** — literature (4 years, 32 lessons from the secondary-school
|
- **Czech language** — literature (4 years, 32 lessons from the secondary-school
|
||||||
syllabus), grammar (20 exercises) and required reading
|
syllabus), grammar (20 exercises) and required reading
|
||||||
(82 books: notes, quiz, plot ordering)
|
(82 books: notes, quiz, plot ordering)
|
||||||
|
|
@ -283,7 +287,7 @@ Intel Mac, another architecture, or hacking on the code →
|
||||||
|
|
||||||
### Usage
|
### Usage
|
||||||
|
|
||||||
1. **Welcome** shows progress and the subject grid. An open subject starts its path. Locked tiles do nothing yet: presentation graphics, programming and software.
|
1. **Welcome** shows progress and the subject grid. An open subject starts its path. Locked tiles do nothing yet: presentation graphics and software.
|
||||||
2. German has the textbook (Neue Freunde, Aus aller Welt, Bei uns zu Hause) and four years. The other open subjects have years, lessons or books.
|
2. German has the textbook (Neue Freunde, Aus aller Welt, Bei uns zu Hause) and four years. The other open subjects have years, lessons or books.
|
||||||
3. A path opens an overview, listening, reading, writing or a quiz.
|
3. A path opens an overview, listening, reading, writing or a quiz.
|
||||||
4. **Statistics**, **Settings** and **Search** sit in the sidebar on Mac. On the phone and the desktop app they are tabs, with search also on the magnifier. The shortcut is `Cmd/Ctrl+K`.
|
4. **Statistics**, **Settings** and **Search** sit in the sidebar on Mac. On the phone and the desktop app they are tabs, with search also on the magnifier. The shortcut is `Cmd/Ctrl+K`.
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,128 @@
|
||||||
|
package org.maturita.maturita.desktop
|
||||||
|
|
||||||
|
import org.maturita.maturita.platform.CSharpRun
|
||||||
|
import java.io.ByteArrayOutputStream
|
||||||
|
import java.io.File
|
||||||
|
import java.nio.charset.StandardCharsets
|
||||||
|
import java.util.concurrent.TimeUnit
|
||||||
|
|
||||||
|
/** One cached console project. The student's program replaces Program.cs and `dotnet run` executes it. */
|
||||||
|
object CSharpLab {
|
||||||
|
private val gate = Object()
|
||||||
|
|
||||||
|
fun run(source: String, stdin: String): CSharpRun {
|
||||||
|
if (source.length > 100_000) return CSharpRun("fail", "Program je příliš dlouhý.")
|
||||||
|
val dotnet = findDotnet() ?: return CSharpRun("unavailable", "")
|
||||||
|
synchronized(gate) {
|
||||||
|
return try {
|
||||||
|
execute(dotnet, source, stdin)
|
||||||
|
} catch (e: Exception) {
|
||||||
|
CSharpRun("fail", e.message ?: "Spuštění selhalo.")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun execute(dotnet: String, source: String, stdin: String): CSharpRun {
|
||||||
|
val dir = File(System.getProperty("user.home"), ".graduately/csharp-lab")
|
||||||
|
dir.mkdirs()
|
||||||
|
val csproj = File(dir, "Lab.csproj")
|
||||||
|
if (!csproj.isFile) {
|
||||||
|
csproj.writeText(projectFile(targetFramework(dotnet)), StandardCharsets.UTF_8)
|
||||||
|
val restored = command(dotnet, listOf("restore", dir.absolutePath), dir, "", 90)
|
||||||
|
if (restored.status != "ok") return restored
|
||||||
|
}
|
||||||
|
File(dir, "Program.cs").writeText(source, StandardCharsets.UTF_8)
|
||||||
|
val args = mutableListOf("run", "--project", dir.absolutePath, "--verbosity", "quiet")
|
||||||
|
if (File(dir, "obj").isDirectory) args += "--no-restore"
|
||||||
|
return command(dotnet, args, dir, stdin, 20)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun command(dotnet: String, args: List<String>, dir: File, stdin: String, seconds: Long): CSharpRun {
|
||||||
|
val process = ProcessBuilder(listOf(dotnet) + args)
|
||||||
|
.directory(dir)
|
||||||
|
.redirectErrorStream(true)
|
||||||
|
.apply {
|
||||||
|
environment().clear()
|
||||||
|
environment().putAll(environmentFor(dotnet))
|
||||||
|
}
|
||||||
|
.start()
|
||||||
|
process.outputStream.use { it.write(stdin.toByteArray(StandardCharsets.UTF_8)) }
|
||||||
|
val output = ByteArrayOutputStream()
|
||||||
|
val reader = Thread {
|
||||||
|
process.inputStream.use { it.copyTo(output) }
|
||||||
|
}
|
||||||
|
reader.isDaemon = true
|
||||||
|
reader.start()
|
||||||
|
val finished = process.waitFor(seconds, TimeUnit.SECONDS)
|
||||||
|
if (!finished) {
|
||||||
|
process.descendants().forEach { it.destroyForcibly() }
|
||||||
|
process.destroyForcibly()
|
||||||
|
process.waitFor(2, TimeUnit.SECONDS)
|
||||||
|
reader.join(1000)
|
||||||
|
return CSharpRun("timeout", clip(output.toString(StandardCharsets.UTF_8)))
|
||||||
|
}
|
||||||
|
reader.join(2000)
|
||||||
|
val text = clip(output.toString(StandardCharsets.UTF_8)).trim()
|
||||||
|
return if (process.exitValue() == 0) CSharpRun("ok", text) else CSharpRun("fail", text)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun clip(text: String): String =
|
||||||
|
if (text.length <= 16_000) text else text.take(16_000) + "\n…"
|
||||||
|
|
||||||
|
private fun environmentFor(dotnet: String): Map<String, String> {
|
||||||
|
val home = System.getProperty("user.home")
|
||||||
|
val root = File(dotnet).parent
|
||||||
|
val path = listOf(root, "/opt/homebrew/bin", "/usr/local/bin", "/usr/bin", "/bin")
|
||||||
|
.joinToString(File.pathSeparator)
|
||||||
|
val env = mutableMapOf(
|
||||||
|
"PATH" to path,
|
||||||
|
"HOME" to home,
|
||||||
|
"USERPROFILE" to home,
|
||||||
|
"DOTNET_ROOT" to root,
|
||||||
|
"DOTNET_CLI_TELEMETRY_OPTOUT" to "1",
|
||||||
|
"DOTNET_NOLOGO" to "1",
|
||||||
|
"DOTNET_SKIP_FIRST_TIME_EXPERIENCE" to "1",
|
||||||
|
"TEMP" to System.getProperty("java.io.tmpdir"),
|
||||||
|
"TMP" to System.getProperty("java.io.tmpdir"),
|
||||||
|
)
|
||||||
|
System.getenv("SystemRoot")?.let { env["SystemRoot"] = it }
|
||||||
|
System.getenv("ComSpec")?.let { env["ComSpec"] = it }
|
||||||
|
return env
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun targetFramework(dotnet: String): String {
|
||||||
|
val version = command(dotnet, listOf("--version"), File(System.getProperty("java.io.tmpdir")), "", 20)
|
||||||
|
val major = version.output.trim().substringBefore('.').filter { it.isDigit() }
|
||||||
|
return if (major.isEmpty()) "net8.0" else "net$major.0"
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun projectFile(framework: String) = """
|
||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
<PropertyGroup>
|
||||||
|
<OutputType>Exe</OutputType>
|
||||||
|
<TargetFramework>$framework</TargetFramework>
|
||||||
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
|
<Nullable>disable</Nullable>
|
||||||
|
<LangVersion>latest</LangVersion>
|
||||||
|
</PropertyGroup>
|
||||||
|
</Project>
|
||||||
|
""".trimIndent()
|
||||||
|
|
||||||
|
private fun findDotnet(): String? {
|
||||||
|
val home = System.getProperty("user.home")
|
||||||
|
val programFiles = System.getenv("ProgramFiles") ?: "C:\\Program Files"
|
||||||
|
val names = listOf(
|
||||||
|
"/usr/local/share/dotnet/dotnet",
|
||||||
|
"/usr/share/dotnet/dotnet",
|
||||||
|
"/opt/homebrew/share/dotnet/dotnet",
|
||||||
|
"$home/.dotnet/dotnet",
|
||||||
|
"$programFiles\\dotnet\\dotnet.exe",
|
||||||
|
)
|
||||||
|
names.firstOrNull { File(it).canExecute() }?.let { return it }
|
||||||
|
val path = System.getenv("PATH") ?: return null
|
||||||
|
return path.split(File.pathSeparator)
|
||||||
|
.map { File(it, if (System.getProperty("os.name").startsWith("Windows")) "dotnet.exe" else "dotnet") }
|
||||||
|
.firstOrNull { it.canExecute() }
|
||||||
|
?.absolutePath
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,8 +1,10 @@
|
||||||
package org.maturita.maturita.desktop
|
package org.maturita.maturita.desktop
|
||||||
|
|
||||||
|
import org.maturita.maturita.platform.CSharpRun
|
||||||
import org.maturita.maturita.platform.Platform
|
import org.maturita.maturita.platform.Platform
|
||||||
import org.maturita.maturita.platform.Prefs
|
import org.maturita.maturita.platform.Prefs
|
||||||
import org.maturita.maturita.platform.Speaker
|
import org.maturita.maturita.platform.Speaker
|
||||||
|
import javax.swing.SwingUtilities
|
||||||
|
|
||||||
class DesktopPlatform(
|
class DesktopPlatform(
|
||||||
override val commit: String,
|
override val commit: String,
|
||||||
|
|
@ -14,4 +16,11 @@ class DesktopPlatform(
|
||||||
override val updater = DesktopUpdater(commit, install)
|
override val updater = DesktopUpdater(commit, install)
|
||||||
|
|
||||||
override fun speaker(): Speaker? = DesktopSpeaker.create()
|
override fun speaker(): Speaker? = DesktopSpeaker.create()
|
||||||
|
|
||||||
|
override fun runCSharp(source: String, stdin: String, cb: (CSharpRun) -> Unit) {
|
||||||
|
Thread {
|
||||||
|
val result = CSharpLab.run(source, stdin)
|
||||||
|
SwingUtilities.invokeLater { cb(result) }
|
||||||
|
}.apply { isDaemon = true }.start()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -23,6 +23,8 @@ import org.maturita.maturita.data.ColorMode
|
||||||
import org.maturita.maturita.data.Palette
|
import org.maturita.maturita.data.Palette
|
||||||
import org.maturita.maturita.ui.BookListScreen
|
import org.maturita.maturita.ui.BookListScreen
|
||||||
import org.maturita.maturita.ui.BookScreen
|
import org.maturita.maturita.ui.BookScreen
|
||||||
|
import org.maturita.maturita.ui.CodeLessonScreen
|
||||||
|
import org.maturita.maturita.ui.CsYearsScreen
|
||||||
import org.maturita.maturita.ui.CzechMapScreen
|
import org.maturita.maturita.ui.CzechMapScreen
|
||||||
import org.maturita.maturita.ui.GermanExercise
|
import org.maturita.maturita.ui.GermanExercise
|
||||||
import org.maturita.maturita.ui.HomeScreen
|
import org.maturita.maturita.ui.HomeScreen
|
||||||
|
|
@ -584,6 +586,51 @@ private fun MaturitaAppBody(vm: AppViewModel) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
is Route.Tg4Ex -> TgQuizScreen(vm, 4, r.id)
|
is Route.Tg4Ex -> TgQuizScreen(vm, 4, r.id)
|
||||||
|
Route.CsYears -> CsYearsScreen(vm)
|
||||||
|
Route.CsMap -> LessonListScreen(
|
||||||
|
vm, vm.tr("cs_year1"), vm.tr("cs_sub"),
|
||||||
|
vm.content.cs.map { l ->
|
||||||
|
val id = l.int("id")
|
||||||
|
LessonRow(vm.tr(l.str("titleKey")), vm.progress.csDone(id), Route.CsLesson(id))
|
||||||
|
},
|
||||||
|
)
|
||||||
|
is Route.CsLesson -> {
|
||||||
|
val l = vm.content.csLesson(r.id)
|
||||||
|
if (l != null) CodeLessonScreen(vm, l) { vm.markCs(r.id) }
|
||||||
|
}
|
||||||
|
Route.Cs2Map -> LessonListScreen(
|
||||||
|
vm, vm.tr("cs_year2"), vm.tr("cs2_sub"),
|
||||||
|
vm.content.cs2.map { l ->
|
||||||
|
val id = l.int("id")
|
||||||
|
LessonRow(vm.tr(l.str("titleKey")), vm.progress.cs2Done(id), Route.Cs2Lesson(id))
|
||||||
|
},
|
||||||
|
)
|
||||||
|
is Route.Cs2Lesson -> {
|
||||||
|
val l = vm.content.cs2Lesson(r.id)
|
||||||
|
if (l != null) CodeLessonScreen(vm, l) { vm.markCs2(r.id) }
|
||||||
|
}
|
||||||
|
Route.Cs3Map -> LessonListScreen(
|
||||||
|
vm, vm.tr("cs_year3"), vm.tr("cs3_sub"),
|
||||||
|
vm.content.cs3.map { l ->
|
||||||
|
val id = l.int("id")
|
||||||
|
LessonRow(vm.tr(l.str("titleKey")), vm.progress.cs3Done(id), Route.Cs3Lesson(id))
|
||||||
|
},
|
||||||
|
)
|
||||||
|
is Route.Cs3Lesson -> {
|
||||||
|
val l = vm.content.cs3Lesson(r.id)
|
||||||
|
if (l != null) CodeLessonScreen(vm, l) { vm.markCs3(r.id) }
|
||||||
|
}
|
||||||
|
Route.Cs4Map -> LessonListScreen(
|
||||||
|
vm, vm.tr("cs_year4"), vm.tr("cs4_sub"),
|
||||||
|
vm.content.cs4.map { l ->
|
||||||
|
val id = l.int("id")
|
||||||
|
LessonRow(vm.tr(l.str("titleKey")), vm.progress.cs4Done(id), Route.Cs4Lesson(id))
|
||||||
|
},
|
||||||
|
)
|
||||||
|
is Route.Cs4Lesson -> {
|
||||||
|
val l = vm.content.cs4Lesson(r.id)
|
||||||
|
if (l != null) CodeLessonScreen(vm, l) { vm.markCs4(r.id) }
|
||||||
|
}
|
||||||
Route.SciMap -> SciMapScreen(vm)
|
Route.SciMap -> SciMapScreen(vm)
|
||||||
Route.ChemMap -> LessonListScreen(
|
Route.ChemMap -> LessonListScreen(
|
||||||
vm, vm.tr("Chemie"), vm.tr("chem_sub"),
|
vm, vm.tr("Chemie"), vm.tr("chem_sub"),
|
||||||
|
|
|
||||||
|
|
@ -262,6 +262,26 @@ class AppViewModel(val content: Content, val platform: Platform) {
|
||||||
refresh()
|
refresh()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun markCs(id: Int) {
|
||||||
|
progress.markCs(id)
|
||||||
|
refresh()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun markCs2(id: Int) {
|
||||||
|
progress.markCs2(id)
|
||||||
|
refresh()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun markCs3(id: Int) {
|
||||||
|
progress.markCs3(id)
|
||||||
|
refresh()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun markCs4(id: Int) {
|
||||||
|
progress.markCs4(id)
|
||||||
|
refresh()
|
||||||
|
}
|
||||||
|
|
||||||
fun markMluv(n: Int) {
|
fun markMluv(n: Int) {
|
||||||
progress.markMluv(n)
|
progress.markMluv(n)
|
||||||
refresh()
|
refresh()
|
||||||
|
|
|
||||||
|
|
@ -118,6 +118,15 @@ sealed class Route(val page: String) {
|
||||||
data object Tg4Map : Route("tg4map")
|
data object Tg4Map : Route("tg4map")
|
||||||
data class Tg4Lesson(val id: Int) : Route("tg4unit$id")
|
data class Tg4Lesson(val id: Int) : Route("tg4unit$id")
|
||||||
data class Tg4Ex(val id: Int) : Route("tg4ex$id")
|
data class Tg4Ex(val id: Int) : Route("tg4ex$id")
|
||||||
|
data object CsYears : Route("csyears")
|
||||||
|
data object CsMap : Route("csmap")
|
||||||
|
data class CsLesson(val id: Int) : Route("csunit$id")
|
||||||
|
data object Cs2Map : Route("cs2map")
|
||||||
|
data class Cs2Lesson(val id: Int) : Route("cs2unit$id")
|
||||||
|
data object Cs3Map : Route("cs3map")
|
||||||
|
data class Cs3Lesson(val id: Int) : Route("cs3unit$id")
|
||||||
|
data object Cs4Map : Route("cs4map")
|
||||||
|
data class Cs4Lesson(val id: Int) : Route("cs4unit$id")
|
||||||
data object CzechMap : Route("czechmap")
|
data object CzechMap : Route("czechmap")
|
||||||
data object Mluvnice : Route("mluvnice")
|
data object Mluvnice : Route("mluvnice")
|
||||||
data class MluvEx(val n: Int) : Route("mluve$n")
|
data class MluvEx(val n: Int) : Route("mluve$n")
|
||||||
|
|
@ -263,6 +272,15 @@ sealed class Route(val page: String) {
|
||||||
Tg4Map -> listOf(Subjects, TgYears, Tg4Map)
|
Tg4Map -> listOf(Subjects, TgYears, Tg4Map)
|
||||||
is Tg4Lesson -> listOf(Subjects, TgYears, Tg4Map, this)
|
is Tg4Lesson -> listOf(Subjects, TgYears, Tg4Map, this)
|
||||||
is Tg4Ex -> listOf(Subjects, TgYears, Tg4Map, Tg4Lesson(id), this)
|
is Tg4Ex -> listOf(Subjects, TgYears, Tg4Map, Tg4Lesson(id), this)
|
||||||
|
CsYears -> listOf(Subjects, CsYears)
|
||||||
|
CsMap -> listOf(Subjects, CsYears, CsMap)
|
||||||
|
is CsLesson -> listOf(Subjects, CsYears, CsMap, this)
|
||||||
|
Cs2Map -> listOf(Subjects, CsYears, Cs2Map)
|
||||||
|
is Cs2Lesson -> listOf(Subjects, CsYears, Cs2Map, this)
|
||||||
|
Cs3Map -> listOf(Subjects, CsYears, Cs3Map)
|
||||||
|
is Cs3Lesson -> listOf(Subjects, CsYears, Cs3Map, this)
|
||||||
|
Cs4Map -> listOf(Subjects, CsYears, Cs4Map)
|
||||||
|
is Cs4Lesson -> listOf(Subjects, CsYears, Cs4Map, this)
|
||||||
CzechMap -> listOf(Subjects, CzechMap)
|
CzechMap -> listOf(Subjects, CzechMap)
|
||||||
Mluvnice -> listOf(Subjects, CzechMap, Mluvnice)
|
Mluvnice -> listOf(Subjects, CzechMap, Mluvnice)
|
||||||
is MluvEx -> listOf(Subjects, CzechMap, Mluvnice, this)
|
is MluvEx -> listOf(Subjects, CzechMap, Mluvnice, this)
|
||||||
|
|
@ -320,6 +338,11 @@ fun routeFromPage(page: String): Route? = when (page) {
|
||||||
"tg2map" -> Route.Tg2Map
|
"tg2map" -> Route.Tg2Map
|
||||||
"tg3map" -> Route.Tg3Map
|
"tg3map" -> Route.Tg3Map
|
||||||
"tg4map" -> Route.Tg4Map
|
"tg4map" -> Route.Tg4Map
|
||||||
|
"csyears" -> Route.CsYears
|
||||||
|
"csmap" -> Route.CsMap
|
||||||
|
"cs2map" -> Route.Cs2Map
|
||||||
|
"cs3map" -> Route.Cs3Map
|
||||||
|
"cs4map" -> Route.Cs4Map
|
||||||
"czechmap" -> Route.CzechMap
|
"czechmap" -> Route.CzechMap
|
||||||
"mluvnice" -> Route.Mluvnice
|
"mluvnice" -> Route.Mluvnice
|
||||||
"readinglist" -> Route.ReadingList
|
"readinglist" -> Route.ReadingList
|
||||||
|
|
@ -441,6 +464,18 @@ fun routeFromPage(page: String): Route? = when (page) {
|
||||||
Regex("""tgex(\d+)""").matchEntire(page)?.let {
|
Regex("""tgex(\d+)""").matchEntire(page)?.let {
|
||||||
return Route.TgEx(it.groupValues[1].toInt())
|
return Route.TgEx(it.groupValues[1].toInt())
|
||||||
}
|
}
|
||||||
|
Regex("""cs4unit(\d+)""").matchEntire(page)?.let {
|
||||||
|
return Route.Cs4Lesson(it.groupValues[1].toInt())
|
||||||
|
}
|
||||||
|
Regex("""cs3unit(\d+)""").matchEntire(page)?.let {
|
||||||
|
return Route.Cs3Lesson(it.groupValues[1].toInt())
|
||||||
|
}
|
||||||
|
Regex("""cs2unit(\d+)""").matchEntire(page)?.let {
|
||||||
|
return Route.Cs2Lesson(it.groupValues[1].toInt())
|
||||||
|
}
|
||||||
|
Regex("""csunit(\d+)""").matchEntire(page)?.let {
|
||||||
|
return Route.CsLesson(it.groupValues[1].toInt())
|
||||||
|
}
|
||||||
Regex("""lit4unit(\d+)""").matchEntire(page)?.let {
|
Regex("""lit4unit(\d+)""").matchEntire(page)?.let {
|
||||||
return Route.Lit4Lesson(it.groupValues[1].toInt())
|
return Route.Lit4Lesson(it.groupValues[1].toInt())
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -60,6 +60,10 @@ class Content(root: JSONObject) {
|
||||||
val tg2 = raw.arr("tg2")
|
val tg2 = raw.arr("tg2")
|
||||||
val tg3 = raw.arr("tg3")
|
val tg3 = raw.arr("tg3")
|
||||||
val tg4 = raw.arr("tg4")
|
val tg4 = raw.arr("tg4")
|
||||||
|
val cs = raw.arr("cs")
|
||||||
|
val cs2 = raw.arr("cs2")
|
||||||
|
val cs3 = raw.arr("cs3")
|
||||||
|
val cs4 = raw.arr("cs4")
|
||||||
val mat0 = raw.arr("mat0")
|
val mat0 = raw.arr("mat0")
|
||||||
val mat = raw.arr("mat")
|
val mat = raw.arr("mat")
|
||||||
val mat2 = raw.arr("mat2")
|
val mat2 = raw.arr("mat2")
|
||||||
|
|
@ -116,6 +120,10 @@ class Content(root: JSONObject) {
|
||||||
fun tg2Lesson(id: Int) = tg2.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 tg3Lesson(id: Int) = tg3.firstOrNull { it.int("id") == id }
|
||||||
fun tg4Lesson(id: Int) = tg4.firstOrNull { it.int("id") == id }
|
fun tg4Lesson(id: Int) = tg4.firstOrNull { it.int("id") == id }
|
||||||
|
fun csLesson(id: Int) = cs.firstOrNull { it.int("id") == id }
|
||||||
|
fun cs2Lesson(id: Int) = cs2.firstOrNull { it.int("id") == id }
|
||||||
|
fun cs3Lesson(id: Int) = cs3.firstOrNull { it.int("id") == id }
|
||||||
|
fun cs4Lesson(id: Int) = cs4.firstOrNull { it.int("id") == id }
|
||||||
fun mat0Lesson(id: Int) = mat0.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 matLesson(id: Int) = mat.firstOrNull { it.int("id") == id }
|
||||||
fun mat2Lesson(id: Int) = mat2.firstOrNull { it.int("id") == id }
|
fun mat2Lesson(id: Int) = mat2.firstOrNull { it.int("id") == id }
|
||||||
|
|
|
||||||
|
|
@ -99,6 +99,14 @@ class ProgressStore(private val prefs: Prefs, content: Content) {
|
||||||
fun markTg3(id: Int) { mark("tg3.$id") }
|
fun markTg3(id: Int) { mark("tg3.$id") }
|
||||||
fun tg4Done(id: Int) = done("tg4.$id")
|
fun tg4Done(id: Int) = done("tg4.$id")
|
||||||
fun markTg4(id: Int) { mark("tg4.$id") }
|
fun markTg4(id: Int) { mark("tg4.$id") }
|
||||||
|
fun csDone(id: Int) = done("cs.$id")
|
||||||
|
fun markCs(id: Int) { mark("cs.$id") }
|
||||||
|
fun cs2Done(id: Int) = done("cs2.$id")
|
||||||
|
fun markCs2(id: Int) { mark("cs2.$id") }
|
||||||
|
fun cs3Done(id: Int) = done("cs3.$id")
|
||||||
|
fun markCs3(id: Int) { mark("cs3.$id") }
|
||||||
|
fun cs4Done(id: Int) = done("cs4.$id")
|
||||||
|
fun markCs4(id: Int) { mark("cs4.$id") }
|
||||||
fun mat0Done(id: Int) = done("mat0.$id")
|
fun mat0Done(id: Int) = done("mat0.$id")
|
||||||
fun markMat0(id: Int) { mark("mat0.$id") }
|
fun markMat0(id: Int) { mark("mat0.$id") }
|
||||||
fun matDone(id: Int) = done("mat.$id")
|
fun matDone(id: Int) = done("mat.$id")
|
||||||
|
|
@ -320,6 +328,38 @@ fun summarize(content: Content, p: ProgressStore): ProgressSum {
|
||||||
sum.doneUnits += 1
|
sum.doneUnits += 1
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
content.cs.forEach { l ->
|
||||||
|
sum.totalEx += 1
|
||||||
|
sum.openUnits += 1
|
||||||
|
if (p.csDone(l.int("id"))) {
|
||||||
|
sum.doneEx += 1
|
||||||
|
sum.doneUnits += 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
content.cs2.forEach { l ->
|
||||||
|
sum.totalEx += 1
|
||||||
|
sum.openUnits += 1
|
||||||
|
if (p.cs2Done(l.int("id"))) {
|
||||||
|
sum.doneEx += 1
|
||||||
|
sum.doneUnits += 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
content.cs3.forEach { l ->
|
||||||
|
sum.totalEx += 1
|
||||||
|
sum.openUnits += 1
|
||||||
|
if (p.cs3Done(l.int("id"))) {
|
||||||
|
sum.doneEx += 1
|
||||||
|
sum.doneUnits += 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
content.cs4.forEach { l ->
|
||||||
|
sum.totalEx += 1
|
||||||
|
sum.openUnits += 1
|
||||||
|
if (p.cs4Done(l.int("id"))) {
|
||||||
|
sum.doneEx += 1
|
||||||
|
sum.doneUnits += 1
|
||||||
|
}
|
||||||
|
}
|
||||||
content.mat0.forEach { l ->
|
content.mat0.forEach { l ->
|
||||||
sum.totalEx += 1
|
sum.totalEx += 1
|
||||||
sum.openUnits += 1
|
sum.openUnits += 1
|
||||||
|
|
|
||||||
|
|
@ -18,8 +18,18 @@ interface Platform {
|
||||||
|
|
||||||
/** A speech engine for listening exercises, or null when there is none. */
|
/** A speech engine for listening exercises, or null when there is none. */
|
||||||
fun speaker(): Speaker?
|
fun speaker(): Speaker?
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Compiles and runs a C# program. [cb] runs on the UI thread.
|
||||||
|
* Status is ok, fail, timeout or unavailable.
|
||||||
|
*/
|
||||||
|
fun runCSharp(source: String, stdin: String, cb: (CSharpRun) -> Unit) {
|
||||||
|
cb(CSharpRun("unavailable", ""))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
data class CSharpRun(val status: String, val output: String)
|
||||||
|
|
||||||
/** The subset of Android's SharedPreferences the app uses. */
|
/** The subset of Android's SharedPreferences the app uses. */
|
||||||
interface Prefs {
|
interface Prefs {
|
||||||
fun getBoolean(key: String, fallback: Boolean): Boolean
|
fun getBoolean(key: String, fallback: Boolean): Boolean
|
||||||
|
|
|
||||||
158
android/shared/src/org/maturita/maturita/ui/CodeLesson.kt
Normal file
158
android/shared/src/org/maturita/maturita/ui/CodeLesson.kt
Normal file
|
|
@ -0,0 +1,158 @@
|
||||||
|
package org.maturita.maturita.ui
|
||||||
|
|
||||||
|
import androidx.compose.foundation.background
|
||||||
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
|
import androidx.compose.foundation.layout.BoxWithConstraints
|
||||||
|
import androidx.compose.foundation.layout.Column
|
||||||
|
import androidx.compose.foundation.layout.Row
|
||||||
|
import androidx.compose.foundation.layout.fillMaxHeight
|
||||||
|
import androidx.compose.foundation.layout.fillMaxSize
|
||||||
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
|
import androidx.compose.foundation.layout.height
|
||||||
|
import androidx.compose.foundation.layout.padding
|
||||||
|
import androidx.compose.foundation.rememberScrollState
|
||||||
|
import androidx.compose.foundation.text.BasicTextField
|
||||||
|
import androidx.compose.foundation.verticalScroll
|
||||||
|
import androidx.compose.material3.FilledTonalButton
|
||||||
|
import androidx.compose.material3.MaterialTheme
|
||||||
|
import androidx.compose.material3.OutlinedButton
|
||||||
|
import androidx.compose.material3.Text
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.getValue
|
||||||
|
import androidx.compose.runtime.mutableStateOf
|
||||||
|
import androidx.compose.runtime.remember
|
||||||
|
import androidx.compose.runtime.setValue
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.graphics.SolidColor
|
||||||
|
import androidx.compose.ui.text.TextStyle
|
||||||
|
import androidx.compose.ui.text.font.FontFamily
|
||||||
|
import androidx.compose.ui.unit.dp
|
||||||
|
import androidx.compose.ui.unit.sp
|
||||||
|
import org.maturita.maturita.AppViewModel
|
||||||
|
import org.maturita.maturita.data.J
|
||||||
|
|
||||||
|
/** Explanation on one side, the program and its output on the other. */
|
||||||
|
@Composable
|
||||||
|
fun CodeLessonScreen(vm: AppViewModel, lesson: J, onSuccess: () -> Unit) {
|
||||||
|
val starter = lesson.str("code")
|
||||||
|
var code by remember(lesson.str("key")) { mutableStateOf(starter) }
|
||||||
|
var stdin by remember(lesson.str("key")) { mutableStateOf("") }
|
||||||
|
var output by remember(lesson.str("key")) { mutableStateOf("") }
|
||||||
|
var running by remember(lesson.str("key")) { mutableStateOf(false) }
|
||||||
|
|
||||||
|
fun run() {
|
||||||
|
if (running) return
|
||||||
|
running = true
|
||||||
|
output = vm.tr("cs_running")
|
||||||
|
vm.platform.runCSharp(code, stdin) { result ->
|
||||||
|
running = false
|
||||||
|
output = when (result.status) {
|
||||||
|
"unavailable" -> vm.tr("cs_unavailable")
|
||||||
|
"timeout" -> (result.output + "\n\n" + vm.tr("cs_timeout")).trim()
|
||||||
|
else -> result.output.ifEmpty { vm.tr("cs_empty") }
|
||||||
|
}
|
||||||
|
if (result.status == "ok") onSuccess()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
DetailScaffold(vm, vm.tr(lesson.str("titleKey")), vm.tr(lesson.str("subKey"))) { inner ->
|
||||||
|
BoxWithConstraints(Modifier.fillMaxSize().padding(inner).padding(12.dp)) {
|
||||||
|
if (maxWidth >= 840.dp) {
|
||||||
|
Row(Modifier.fillMaxSize(), horizontalArrangement = Arrangement.spacedBy(12.dp)) {
|
||||||
|
Explanation(vm, lesson, Modifier.weight(0.9f).fillMaxHeight())
|
||||||
|
EditorColumn(vm, code, { code = it }, stdin, { stdin = it }, expand = true, Modifier.weight(1.15f).fillMaxHeight())
|
||||||
|
OutputColumn(vm, output, running, { run() }, { code = starter }, expand = true, Modifier.weight(1f).fillMaxHeight())
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Column(Modifier.fillMaxSize().verticalScroll(rememberScrollState()), verticalArrangement = Arrangement.spacedBy(12.dp)) {
|
||||||
|
Explanation(vm, lesson, Modifier.fillMaxWidth())
|
||||||
|
EditorColumn(vm, code, { code = it }, stdin, { stdin = it }, expand = false, Modifier.fillMaxWidth())
|
||||||
|
OutputColumn(vm, output, running, { run() }, { code = starter }, expand = false, Modifier.fillMaxWidth())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
private fun Explanation(vm: AppViewModel, lesson: J, modifier: Modifier) {
|
||||||
|
Column(modifier.verticalScroll(rememberScrollState()), verticalArrangement = Arrangement.spacedBy(14.dp)) {
|
||||||
|
lesson.arr("slides").forEach { slide ->
|
||||||
|
Text(slide.str("kicker"), color = MaterialTheme.colorScheme.primary, style = MaterialTheme.typography.labelLarge)
|
||||||
|
Text(slide.str("title"), style = MaterialTheme.typography.titleLarge)
|
||||||
|
slide.strs("lines").forEach { line ->
|
||||||
|
Text("• $line", style = MaterialTheme.typography.bodyLarge)
|
||||||
|
}
|
||||||
|
slide.strOrNull("tip")?.let { tip ->
|
||||||
|
Text(tip, color = MaterialTheme.colorScheme.onSurfaceVariant, style = MaterialTheme.typography.bodyMedium)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
private fun EditorColumn(
|
||||||
|
vm: AppViewModel,
|
||||||
|
code: String,
|
||||||
|
onCode: (String) -> Unit,
|
||||||
|
stdin: String,
|
||||||
|
onStdin: (String) -> Unit,
|
||||||
|
expand: Boolean,
|
||||||
|
modifier: Modifier,
|
||||||
|
) {
|
||||||
|
Column(modifier, verticalArrangement = Arrangement.spacedBy(8.dp)) {
|
||||||
|
Text(vm.tr("cs_code"), style = MaterialTheme.typography.titleMedium)
|
||||||
|
BasicTextField(
|
||||||
|
value = code,
|
||||||
|
onValueChange = onCode,
|
||||||
|
textStyle = codeStyle().copy(color = MaterialTheme.colorScheme.onSurface),
|
||||||
|
cursorBrush = SolidColor(MaterialTheme.colorScheme.primary),
|
||||||
|
modifier = Modifier
|
||||||
|
.then(if (expand) Modifier.weight(1f) else Modifier.height(240.dp))
|
||||||
|
.fillMaxWidth()
|
||||||
|
.background(MaterialTheme.colorScheme.surfaceContainer)
|
||||||
|
.padding(12.dp)
|
||||||
|
.verticalScroll(rememberScrollState()),
|
||||||
|
)
|
||||||
|
Text(vm.tr("cs_stdin"), style = MaterialTheme.typography.labelLarge, color = MaterialTheme.colorScheme.onSurfaceVariant)
|
||||||
|
BasicTextField(
|
||||||
|
value = stdin,
|
||||||
|
onValueChange = onStdin,
|
||||||
|
textStyle = codeStyle().copy(color = MaterialTheme.colorScheme.onSurface),
|
||||||
|
cursorBrush = SolidColor(MaterialTheme.colorScheme.primary),
|
||||||
|
modifier = Modifier.fillMaxWidth().background(MaterialTheme.colorScheme.surfaceContainer).padding(12.dp),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
private fun OutputColumn(
|
||||||
|
vm: AppViewModel,
|
||||||
|
output: String,
|
||||||
|
running: Boolean,
|
||||||
|
onRun: () -> Unit,
|
||||||
|
onReset: () -> Unit,
|
||||||
|
expand: Boolean,
|
||||||
|
modifier: Modifier,
|
||||||
|
) {
|
||||||
|
Column(modifier, verticalArrangement = Arrangement.spacedBy(8.dp)) {
|
||||||
|
Row(horizontalArrangement = Arrangement.spacedBy(8.dp)) {
|
||||||
|
FilledTonalButton(onClick = onRun, enabled = !running) { Text(vm.tr("cs_run")) }
|
||||||
|
OutlinedButton(onClick = onReset, enabled = !running) { Text(vm.tr("cs_reset")) }
|
||||||
|
}
|
||||||
|
Text(vm.tr("cs_output"), style = MaterialTheme.typography.titleMedium)
|
||||||
|
Text(
|
||||||
|
output.ifEmpty { vm.tr("cs_empty") },
|
||||||
|
style = codeStyle(),
|
||||||
|
color = MaterialTheme.colorScheme.onSurface,
|
||||||
|
modifier = Modifier
|
||||||
|
.then(if (expand) Modifier.weight(1f) else Modifier.height(180.dp))
|
||||||
|
.fillMaxWidth()
|
||||||
|
.background(MaterialTheme.colorScheme.surfaceContainer)
|
||||||
|
.padding(12.dp)
|
||||||
|
.verticalScroll(rememberScrollState()),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun codeStyle() = TextStyle(fontFamily = FontFamily.Monospace, fontSize = 14.sp, lineHeight = 20.sp)
|
||||||
|
|
@ -36,6 +36,7 @@ import androidx.compose.material.icons.filled.Groups
|
||||||
import androidx.compose.material.icons.filled.Lock
|
import androidx.compose.material.icons.filled.Lock
|
||||||
import androidx.compose.material.icons.filled.Quiz
|
import androidx.compose.material.icons.filled.Quiz
|
||||||
import androidx.compose.material.icons.filled.Science
|
import androidx.compose.material.icons.filled.Science
|
||||||
|
import androidx.compose.material.icons.filled.Terminal
|
||||||
import androidx.compose.material.icons.filled.Search
|
import androidx.compose.material.icons.filled.Search
|
||||||
import androidx.compose.material.icons.outlined.Lightbulb
|
import androidx.compose.material.icons.outlined.Lightbulb
|
||||||
import androidx.compose.material3.Card
|
import androidx.compose.material3.Card
|
||||||
|
|
@ -227,6 +228,7 @@ private fun SubjectIcon(icon: String, open: Boolean) {
|
||||||
"bolt" -> Icon(Icons.Filled.Bolt, 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))
|
"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))
|
"ruler" -> Icon(Icons.Filled.Architecture, null, tint = tint, modifier = Modifier.size(26.dp))
|
||||||
|
"code" -> Icon(Icons.Filled.Terminal, null, tint = tint, modifier = Modifier.size(26.dp))
|
||||||
else -> Icon(Icons.Filled.Lock, null, tint = tint)
|
else -> Icon(Icons.Filled.Lock, null, tint = tint)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -350,6 +352,13 @@ private fun subjectSum(vm: AppViewModel, s: J): ProgressSum {
|
||||||
vm.content.fyz3.count { vm.progress.fyz3Done(it.int("id")) } +
|
vm.content.fyz3.count { vm.progress.fyz3Done(it.int("id")) } +
|
||||||
vm.content.fyz4.count { vm.progress.fyz4Done(it.int("id")) }
|
vm.content.fyz4.count { vm.progress.fyz4Done(it.int("id")) }
|
||||||
}
|
}
|
||||||
|
"csyears", "csmap", "cs2map", "cs3map", "cs4map" -> {
|
||||||
|
sum.totalEx = vm.content.cs.size + vm.content.cs2.size + vm.content.cs3.size + vm.content.cs4.size
|
||||||
|
sum.doneEx = vm.content.cs.count { vm.progress.csDone(it.int("id")) } +
|
||||||
|
vm.content.cs2.count { vm.progress.cs2Done(it.int("id")) } +
|
||||||
|
vm.content.cs3.count { vm.progress.cs3Done(it.int("id")) } +
|
||||||
|
vm.content.cs4.count { vm.progress.cs4Done(it.int("id")) }
|
||||||
|
}
|
||||||
"tgyears", "tgmap", "tg2map", "tg3map", "tg4map" -> {
|
"tgyears", "tgmap", "tg2map", "tg3map", "tg4map" -> {
|
||||||
sum.totalEx = vm.content.tg.size + vm.content.tg2.size + vm.content.tg3.size + vm.content.tg4.size
|
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")) } +
|
sum.doneEx = vm.content.tg.count { vm.progress.tgDone(it.int("id")) } +
|
||||||
|
|
@ -679,6 +688,23 @@ private fun buildSearch(vm: AppViewModel): List<Hit> {
|
||||||
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_lesson"), "tg4unit${l.int("id")}", extra = "autocad")
|
||||||
add(vm.tr(l.str("titleKey")), vm.tr("search_exercise"), "tg4ex${l.int("id")}")
|
add(vm.tr(l.str("titleKey")), vm.tr("search_exercise"), "tg4ex${l.int("id")}")
|
||||||
}
|
}
|
||||||
|
add(vm.tr("Programování"), vm.tr("search_lesson"), "csyears", extra = "csharp c#")
|
||||||
|
add(vm.tr("cs_year1"), vm.tr("search_lesson"), "csmap", extra = "rocnik")
|
||||||
|
add(vm.tr("cs_year2"), vm.tr("search_lesson"), "cs2map", extra = "rocnik")
|
||||||
|
add(vm.tr("cs_year3"), vm.tr("search_lesson"), "cs3map", extra = "rocnik")
|
||||||
|
add(vm.tr("cs_year4"), vm.tr("search_lesson"), "cs4map", extra = "rocnik")
|
||||||
|
vm.content.cs.forEach { l ->
|
||||||
|
add(vm.tr(l.str("titleKey")), vm.tr("search_lesson"), "csunit${l.int("id")}", extra = "csharp")
|
||||||
|
}
|
||||||
|
vm.content.cs2.forEach { l ->
|
||||||
|
add(vm.tr(l.str("titleKey")), vm.tr("search_lesson"), "cs2unit${l.int("id")}", extra = "csharp")
|
||||||
|
}
|
||||||
|
vm.content.cs3.forEach { l ->
|
||||||
|
add(vm.tr(l.str("titleKey")), vm.tr("search_lesson"), "cs3unit${l.int("id")}", extra = "csharp")
|
||||||
|
}
|
||||||
|
vm.content.cs4.forEach { l ->
|
||||||
|
add(vm.tr(l.str("titleKey")), vm.tr("search_lesson"), "cs4unit${l.int("id")}", extra = "csharp")
|
||||||
|
}
|
||||||
add(vm.tr("Chemie"), vm.tr("search_lesson"), "chemmap", extra = "chemie")
|
add(vm.tr("Chemie"), vm.tr("search_lesson"), "chemmap", extra = "chemie")
|
||||||
add(vm.tr("Biologie"), vm.tr("search_lesson"), "biomap", extra = "biologie")
|
add(vm.tr("Biologie"), vm.tr("search_lesson"), "biomap", extra = "biologie")
|
||||||
vm.content.chem.forEach { l ->
|
vm.content.chem.forEach { l ->
|
||||||
|
|
@ -878,6 +904,20 @@ fun MatYearsScreen(vm: AppViewModel) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun CsYearsScreen(vm: AppViewModel) {
|
||||||
|
DetailScaffold(vm, vm.tr("Programování"), vm.tr("cs_years_sub")) { inner ->
|
||||||
|
LazyColumn(contentPadding = PaddingValues(top = inner.calculateTopPadding(), bottom = inner.calculateBottomPadding() + 16.dp)) {
|
||||||
|
item {
|
||||||
|
NavRow(title = vm.tr("cs_year1"), subtitle = vm.tr("cs_sub"), leading = { Text("1.", style = MaterialTheme.typography.titleMedium) }) { vm.go(Route.CsMap) }
|
||||||
|
NavRow(title = vm.tr("cs_year2"), subtitle = vm.tr("cs2_sub"), leading = { Text("2.", style = MaterialTheme.typography.titleMedium) }) { vm.go(Route.Cs2Map) }
|
||||||
|
NavRow(title = vm.tr("cs_year3"), subtitle = vm.tr("cs3_sub"), leading = { Text("3.", style = MaterialTheme.typography.titleMedium) }) { vm.go(Route.Cs3Map) }
|
||||||
|
NavRow(title = vm.tr("cs_year4"), subtitle = vm.tr("cs4_sub"), leading = { Text("4.", style = MaterialTheme.typography.titleMedium) }) { vm.go(Route.Cs4Map) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun TgYearsScreen(vm: AppViewModel) {
|
fun TgYearsScreen(vm: AppViewModel) {
|
||||||
DetailScaffold(vm, vm.tr("Technická grafika"), vm.tr("tg_years_sub")) { inner ->
|
DetailScaffold(vm, vm.tr("Technická grafika"), vm.tr("tg_years_sub")) { inner ->
|
||||||
|
|
|
||||||
|
|
@ -18,7 +18,7 @@ Skript vypíše počet lekcí, nebo chyby s cestou a číslem řádku
|
||||||
|
|
||||||
| Složka | Obsah |
|
| Složka | Obsah |
|
||||||
|---|---|
|
|---|---|
|
||||||
| `pocitacove-site/`, `technicke-vybaveni/`, `technicka-grafika/`, `obcanska-nauka/`, `fyzika/` | `N-rocnik/`, v nich lekce |
|
| `pocitacove-site/`, `technicke-vybaveni/`, `technicka-grafika/`, `programovani/`, `obcanska-nauka/`, `fyzika/` | `N-rocnik/`, v nich lekce |
|
||||||
| `prirodni-vedy/chemie`, `prirodni-vedy/biologie` | lekce |
|
| `prirodni-vedy/chemie`, `prirodni-vedy/biologie` | lekce |
|
||||||
| `cestina/literatura/N-rocnik` | lekce literatury |
|
| `cestina/literatura/N-rocnik` | lekce literatury |
|
||||||
| `cestina/mluvnice` | cvičení z mluvnice |
|
| `cestina/mluvnice` | cvičení z mluvnice |
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,12 @@
|
||||||
# cs: bullet
|
# cs: bullet
|
||||||
# en: bullet
|
# en: bullet
|
||||||
|
|
||||||
|
--- 2026-10-11
|
||||||
|
cs: Programování: čtyři ročníky C#, od výpisu po LINQ a async
|
||||||
|
cs: Kód se píše v lekci a hned vedle se spustí
|
||||||
|
en: Programming: four years of C#, from output to LINQ and async
|
||||||
|
en: You write the code in the lesson and run it right beside the text
|
||||||
|
|
||||||
--- 2026-10-11
|
--- 2026-10-11
|
||||||
cs: Technická grafika: čtyři ročníky AutoCADu, od výkresu po 3D
|
cs: Technická grafika: čtyři ročníky AutoCADu, od výkresu po 3D
|
||||||
cs: V každé lekci je postup, zadání s rozměry a kvíz
|
cs: V každé lekci je postup, zadání s rozměry a kvíz
|
||||||
|
|
|
||||||
|
|
@ -13,5 +13,5 @@
|
||||||
| Základy přírodních věd | Základy přírodních věd | Fundamentals of Natural Sciences | flask | scimap | ano |
|
| Základy přírodních věd | Základy přírodních věd | Fundamentals of Natural Sciences | flask | scimap | ano |
|
||||||
| Technická grafika | Technická grafika | Technical Graphics | ruler | tgyears | ano |
|
| Technická grafika | Technická grafika | Technical Graphics | ruler | tgyears | ano |
|
||||||
| Prezentační grafika | Prezentační grafika | Presentation Graphics | lock | | ne |
|
| Prezentační grafika | Prezentační grafika | Presentation Graphics | lock | | ne |
|
||||||
| Programování | Programování | Programming | lock | | ne |
|
| Programování | Programování | Programming | code | csyears | ano |
|
||||||
| Programové vybavení | Programové vybavení | Software | lock | | ne |
|
| Programové vybavení | Programové vybavení | Software | lock | | ne |
|
||||||
|
|
|
||||||
|
|
@ -243,6 +243,29 @@
|
||||||
| tg3_sub | Pohledy, řezy, bloky a tisk. | Views, sections, blocks and plotting. |
|
| tg3_sub | Pohledy, řezy, bloky a tisk. | Views, sections, blocks and plotting. |
|
||||||
| tg4_sub | 3D model a výkres k maturitě. | A 3D model and the exam drawing. |
|
| tg4_sub | 3D model a výkres k maturitě. | A 3D model and the exam drawing. |
|
||||||
|
|
||||||
|
# Programování
|
||||||
|
|
||||||
|
| Klíč | Česky | Anglicky |
|
||||||
|
|---|---|---|
|
||||||
|
| cs_years_sub | C# od prvního výpisu po pokročilejší nástroje. Kód se píše a spouští vedle výkladu. | C# from the first line of output to the later tools. You write the code and run it beside the lesson. |
|
||||||
|
| cs_year1 | 1. ročník | Year 1 |
|
||||||
|
| cs_year2 | 2. ročník | Year 2 |
|
||||||
|
| cs_year3 | 3. ročník | Year 3 |
|
||||||
|
| cs_year4 | 4. ročník | Year 4 |
|
||||||
|
| cs_sub | Výpis, proměnné, typy, operátory a řetězce. | Output, variables, types, operators and strings. |
|
||||||
|
| cs2_sub | Podmínky, cykly, pole a metody. | Conditions, loops, arrays and methods. |
|
||||||
|
| cs3_sub | Třídy, dědičnost, rozhraní a výčty. | Classes, inheritance, interfaces and enums. |
|
||||||
|
| cs4_sub | Výjimky, kolekce, LINQ, soubory a async. | Exceptions, collections, LINQ, files and async. |
|
||||||
|
| cs_code | Kód | Code |
|
||||||
|
| cs_output | Výstup | Output |
|
||||||
|
| cs_stdin | Vstup pro Console.ReadLine | Input for Console.ReadLine |
|
||||||
|
| cs_run | Spustit | Run |
|
||||||
|
| cs_reset | Obnovit ukázku | Reset example |
|
||||||
|
| cs_running | Překládám… | Compiling… |
|
||||||
|
| cs_empty | Zatím nic. Upravte kód a spusťte ho. | Nothing yet. Edit the code and run it. |
|
||||||
|
| cs_timeout | Program běžel příliš dlouho a byl zastaven. | The program ran too long and was stopped. |
|
||||||
|
| cs_unavailable | Tady C# nespustíte. Na počítači s nainstalovaným .NET se výstup objeví vedle kódu. | C# cannot run here. On a computer with .NET installed the output appears beside the code. |
|
||||||
|
|
||||||
# Angličtina a němčina
|
# Angličtina a němčina
|
||||||
|
|
||||||
| Klíč | Česky | Anglicky |
|
| Klíč | Česky | Anglicky |
|
||||||
|
|
|
||||||
4
content/programovani/1-rocnik/01-ahoj-svete.en.md
Normal file
4
content/programovani/1-rocnik/01-ahoj-svete.en.md
Normal file
|
|
@ -0,0 +1,4 @@
|
||||||
|
---
|
||||||
|
nazev: Hello, world
|
||||||
|
popis: The first program: writing to the console.
|
||||||
|
---
|
||||||
42
content/programovani/1-rocnik/01-ahoj-svete.md
Normal file
42
content/programovani/1-rocnik/01-ahoj-svete.md
Normal file
|
|
@ -0,0 +1,42 @@
|
||||||
|
---
|
||||||
|
id: cs-1-ahoj-svete
|
||||||
|
nazev: Ahoj světe
|
||||||
|
popis: První program: výpis na konzoli.
|
||||||
|
---
|
||||||
|
|
||||||
|
# Výklad
|
||||||
|
|
||||||
|
## Výpis
|
||||||
|
*WriteLine*
|
||||||
|
|
||||||
|
- Console.WriteLine vypíše text a odřádkuje.
|
||||||
|
- Console.Write vypíše text a zůstane na stejném řádku.
|
||||||
|
- Text se píše do uvozovek. Středník ukončuje příkaz.
|
||||||
|
|
||||||
|
> Tip: Bez středníku se program nepřeloží.
|
||||||
|
|
||||||
|
## Krátký a dlouhý zápis
|
||||||
|
*Main*
|
||||||
|
|
||||||
|
- Starší učebnice mají třídu Program a metodu Main.
|
||||||
|
- Nový projekt stejný program zapíše několika řádky bez třídy.
|
||||||
|
- Obojí spustí stejný výpis. V lekcích používáme krátký zápis.
|
||||||
|
|
||||||
|
> Tip: Krátký zápis je pořád C#. Není to jiný jazyk.
|
||||||
|
|
||||||
|
## Vyzkoušejte
|
||||||
|
*Editor*
|
||||||
|
|
||||||
|
- Vpravo je kód. Upravte ho a stiskněte Spustit.
|
||||||
|
- Výstup se objeví hned vedle.
|
||||||
|
- Obnovit ukázku vrátí původní program.
|
||||||
|
|
||||||
|
> Tip: Změňte text v uvozovkách a spusťte znovu.
|
||||||
|
|
||||||
|
# Kód
|
||||||
|
|
||||||
|
```
|
||||||
|
Console.WriteLine("Ahoj světe");
|
||||||
|
Console.Write("Toto je ");
|
||||||
|
Console.WriteLine("C#");
|
||||||
|
```
|
||||||
4
content/programovani/1-rocnik/02-komentare.en.md
Normal file
4
content/programovani/1-rocnik/02-komentare.en.md
Normal file
|
|
@ -0,0 +1,4 @@
|
||||||
|
---
|
||||||
|
nazev: Comments
|
||||||
|
popis: Notes the program does not run.
|
||||||
|
---
|
||||||
35
content/programovani/1-rocnik/02-komentare.md
Normal file
35
content/programovani/1-rocnik/02-komentare.md
Normal file
|
|
@ -0,0 +1,35 @@
|
||||||
|
---
|
||||||
|
id: cs-1-komentare
|
||||||
|
nazev: Komentáře
|
||||||
|
popis: Poznámky, které se nespouštějí.
|
||||||
|
---
|
||||||
|
|
||||||
|
# Výklad
|
||||||
|
|
||||||
|
## Jednořádkový
|
||||||
|
*//*
|
||||||
|
|
||||||
|
- Vše za // na stejném řádku počítač přeskočí.
|
||||||
|
- Komentář vysvětluje, proč tam řádek je, ne co je vidět.
|
||||||
|
- Kód bez komentáře pořád běží stejně.
|
||||||
|
|
||||||
|
> Tip: Komentář není výpis. Na konzoli se neobjeví.
|
||||||
|
|
||||||
|
## Více řádků
|
||||||
|
*/* */*
|
||||||
|
|
||||||
|
- Blok začíná /* a končí */.
|
||||||
|
- Hodí se, když chcete dočasně vypnout několik řádků.
|
||||||
|
- Zapomenuté */ rozbije zbytek souboru.
|
||||||
|
|
||||||
|
> Tip: Nevnořujte blokové komentáře do sebe.
|
||||||
|
|
||||||
|
# Kód
|
||||||
|
|
||||||
|
```
|
||||||
|
// Tohle se nespustí.
|
||||||
|
Console.WriteLine("Tento řádek ano");
|
||||||
|
/*
|
||||||
|
Console.WriteLine("A tento ne");
|
||||||
|
*/
|
||||||
|
```
|
||||||
4
content/programovani/1-rocnik/03-promenne.en.md
Normal file
4
content/programovani/1-rocnik/03-promenne.en.md
Normal file
|
|
@ -0,0 +1,4 @@
|
||||||
|
---
|
||||||
|
nazev: Variables
|
||||||
|
popis: A named value you can change.
|
||||||
|
---
|
||||||
36
content/programovani/1-rocnik/03-promenne.md
Normal file
36
content/programovani/1-rocnik/03-promenne.md
Normal file
|
|
@ -0,0 +1,36 @@
|
||||||
|
---
|
||||||
|
id: cs-1-promenne
|
||||||
|
nazev: Proměnné
|
||||||
|
popis: Pojmenovaná hodnota, kterou lze změnit.
|
||||||
|
---
|
||||||
|
|
||||||
|
# Výklad
|
||||||
|
|
||||||
|
## Deklarace
|
||||||
|
*typ jméno*
|
||||||
|
|
||||||
|
- Nejdřív typ, pak jméno, pak hodnota: int vek = 17;
|
||||||
|
- Jméno začíná písmenem. Mezery v něm být nesmí.
|
||||||
|
- Do stejné proměnné lze později uložit novou hodnotu stejného typu.
|
||||||
|
|
||||||
|
> Tip: int drží celé číslo. string drží text.
|
||||||
|
|
||||||
|
## Čtení
|
||||||
|
*jméno*
|
||||||
|
|
||||||
|
- Jméno bez uvozovek je proměnná. Uvozovky by z něj udělaly text.
|
||||||
|
- WriteLine umí vypsat proměnnou i text spojený plusem.
|
||||||
|
- Hodnota se dosadí až při běhu, ne při psaní.
|
||||||
|
|
||||||
|
> Tip: Změňte vek na 18 a spusťte znovu.
|
||||||
|
|
||||||
|
# Kód
|
||||||
|
|
||||||
|
```
|
||||||
|
string jmeno = "Anna";
|
||||||
|
int vek = 17;
|
||||||
|
jmeno = "Eva";
|
||||||
|
Console.WriteLine(jmeno);
|
||||||
|
Console.WriteLine(vek);
|
||||||
|
Console.WriteLine(jmeno + " má " + vek + " let");
|
||||||
|
```
|
||||||
4
content/programovani/1-rocnik/04-typy.en.md
Normal file
4
content/programovani/1-rocnik/04-typy.en.md
Normal file
|
|
@ -0,0 +1,4 @@
|
||||||
|
---
|
||||||
|
nazev: Data types
|
||||||
|
popis: int, double, bool, char and string.
|
||||||
|
---
|
||||||
40
content/programovani/1-rocnik/04-typy.md
Normal file
40
content/programovani/1-rocnik/04-typy.md
Normal file
|
|
@ -0,0 +1,40 @@
|
||||||
|
---
|
||||||
|
id: cs-1-typy
|
||||||
|
nazev: Datové typy
|
||||||
|
popis: int, double, bool, char a string.
|
||||||
|
---
|
||||||
|
|
||||||
|
# Výklad
|
||||||
|
|
||||||
|
## Čísla
|
||||||
|
*int a double*
|
||||||
|
|
||||||
|
- int je celé číslo: 42.
|
||||||
|
- double je číslo s desetinnou tečkou: 3.14.
|
||||||
|
- V kódu je desetinná tečka, ne čárka.
|
||||||
|
|
||||||
|
> Tip: 3,14 by C# přečetl jako dva údaje oddělené čárkou.
|
||||||
|
|
||||||
|
## Text a ano/ne
|
||||||
|
*string, char, bool*
|
||||||
|
|
||||||
|
- string je text v uvozovkách.
|
||||||
|
- char je jeden znak v apostrofech: 'A'.
|
||||||
|
- bool je jen true nebo false.
|
||||||
|
|
||||||
|
> Tip: Apostrof a uvozovky nejsou totéž. 'A' je znak, "A" je text.
|
||||||
|
|
||||||
|
# Kód
|
||||||
|
|
||||||
|
```
|
||||||
|
int cele = 42;
|
||||||
|
double desetinne = 3.14;
|
||||||
|
bool prsi = false;
|
||||||
|
char pismeno = 'A';
|
||||||
|
string text = "ahoj";
|
||||||
|
Console.WriteLine(cele);
|
||||||
|
Console.WriteLine(desetinne);
|
||||||
|
Console.WriteLine(prsi);
|
||||||
|
Console.WriteLine(pismeno);
|
||||||
|
Console.WriteLine(text);
|
||||||
|
```
|
||||||
4
content/programovani/1-rocnik/05-prevody.en.md
Normal file
4
content/programovani/1-rocnik/05-prevody.en.md
Normal file
|
|
@ -0,0 +1,4 @@
|
||||||
|
---
|
||||||
|
nazev: Type conversion
|
||||||
|
popis: From double to int and from text to a number.
|
||||||
|
---
|
||||||
36
content/programovani/1-rocnik/05-prevody.md
Normal file
36
content/programovani/1-rocnik/05-prevody.md
Normal file
|
|
@ -0,0 +1,36 @@
|
||||||
|
---
|
||||||
|
id: cs-1-prevody
|
||||||
|
nazev: Převody typů
|
||||||
|
popis: Z double na int a z textu na číslo.
|
||||||
|
---
|
||||||
|
|
||||||
|
# Výklad
|
||||||
|
|
||||||
|
## Ořez desetin
|
||||||
|
*(int)*
|
||||||
|
|
||||||
|
- (int)3.9 je 3. Desetinná část se odřízne, nezaokrouhlí.
|
||||||
|
- Opačně int do double ztrátu nemá: 3 se stane 3.0.
|
||||||
|
- Převod napište před hodnotu do závorky.
|
||||||
|
|
||||||
|
> Tip: Když potřebujete zaokrouhlit, (int) na to není.
|
||||||
|
|
||||||
|
## Text na číslo
|
||||||
|
*Parse*
|
||||||
|
|
||||||
|
- int.Parse("20") udělá z textu číslo 20.
|
||||||
|
- Když text číslo není, program spadne. To později chytíme výjimkou.
|
||||||
|
- Číslo na text udělá ToString nebo zápis do řetězce.
|
||||||
|
|
||||||
|
> Tip: Vstup z konzole je vždy text, i když uživatel napíše číslici.
|
||||||
|
|
||||||
|
# Kód
|
||||||
|
|
||||||
|
```
|
||||||
|
double cena = 19.9;
|
||||||
|
int cele = (int)cena;
|
||||||
|
Console.WriteLine(cele);
|
||||||
|
string vstup = "20";
|
||||||
|
int cislo = int.Parse(vstup);
|
||||||
|
Console.WriteLine(cislo + 1);
|
||||||
|
```
|
||||||
4
content/programovani/1-rocnik/06-operatory.en.md
Normal file
4
content/programovani/1-rocnik/06-operatory.en.md
Normal file
|
|
@ -0,0 +1,4 @@
|
||||||
|
---
|
||||||
|
nazev: Operators
|
||||||
|
popis: Arithmetic, remainder, comparison and logic.
|
||||||
|
---
|
||||||
37
content/programovani/1-rocnik/06-operatory.md
Normal file
37
content/programovani/1-rocnik/06-operatory.md
Normal file
|
|
@ -0,0 +1,37 @@
|
||||||
|
---
|
||||||
|
id: cs-1-operatory
|
||||||
|
nazev: Operátory
|
||||||
|
popis: Počítání, zbytek, porovnání a a zároveň.
|
||||||
|
---
|
||||||
|
|
||||||
|
# Výklad
|
||||||
|
|
||||||
|
## Počet
|
||||||
|
*+ - * / %*
|
||||||
|
|
||||||
|
- Sčítání, odčítání a násobení fungují, jak čekáte.
|
||||||
|
- Dělení dvou int je celočíselné: 17 / 5 je 3.
|
||||||
|
- % je zbytek po dělení: 17 % 5 je 2.
|
||||||
|
|
||||||
|
> Tip: Když chcete desetinný podíl, jedno číslo udělejte double: 17 / 5.0.
|
||||||
|
|
||||||
|
## Porovnání
|
||||||
|
*true a false*
|
||||||
|
|
||||||
|
- == je rovnost. = je uložení. Nejsou totéž.
|
||||||
|
- && znamená a zároveň. || znamená nebo.
|
||||||
|
- ! otočí bool: !true je false.
|
||||||
|
|
||||||
|
> Tip: Jedno = v podmínce je častá chyba. Podmínka chce ==.
|
||||||
|
|
||||||
|
# Kód
|
||||||
|
|
||||||
|
```
|
||||||
|
int a = 17;
|
||||||
|
int b = 5;
|
||||||
|
Console.WriteLine(a + b);
|
||||||
|
Console.WriteLine(a / b);
|
||||||
|
Console.WriteLine(a % b);
|
||||||
|
Console.WriteLine(a > b);
|
||||||
|
Console.WriteLine(a > 10 && b < 10);
|
||||||
|
```
|
||||||
4
content/programovani/1-rocnik/07-retezce.en.md
Normal file
4
content/programovani/1-rocnik/07-retezce.en.md
Normal file
|
|
@ -0,0 +1,4 @@
|
||||||
|
---
|
||||||
|
nazev: Strings
|
||||||
|
popis: Joining text, length and a slice.
|
||||||
|
---
|
||||||
36
content/programovani/1-rocnik/07-retezce.md
Normal file
36
content/programovani/1-rocnik/07-retezce.md
Normal file
|
|
@ -0,0 +1,36 @@
|
||||||
|
---
|
||||||
|
id: cs-1-retezce
|
||||||
|
nazev: Řetězce
|
||||||
|
popis: Skládání textu, délka a výřez.
|
||||||
|
---
|
||||||
|
|
||||||
|
# Výklad
|
||||||
|
|
||||||
|
## Interpolace
|
||||||
|
*$*
|
||||||
|
|
||||||
|
- $"Ahoj, {jmeno}" dosadí hodnotu do složených závorek.
|
||||||
|
- Je přehlednější než spojování plusem.
|
||||||
|
- Do závorek může i výpočet: {vek + 1}.
|
||||||
|
|
||||||
|
> Tip: Bez dolaru před uvozovkami se {jmeno} vypíše doslova.
|
||||||
|
|
||||||
|
## Metody textu
|
||||||
|
*Length*
|
||||||
|
|
||||||
|
- Length je počet znaků.
|
||||||
|
- ToUpper vrátí velká písmena a původní text nezmění.
|
||||||
|
- Substring(0, 2) vezme dva znaky od začátku.
|
||||||
|
|
||||||
|
> Tip: První znak má index 0, ne 1.
|
||||||
|
|
||||||
|
# Kód
|
||||||
|
|
||||||
|
```
|
||||||
|
string jmeno = "Anna";
|
||||||
|
int vek = 17;
|
||||||
|
Console.WriteLine($"Ahoj, {jmeno}. Za rok ti bude {vek + 1}.");
|
||||||
|
Console.WriteLine(jmeno.Length);
|
||||||
|
Console.WriteLine(jmeno.ToUpper());
|
||||||
|
Console.WriteLine(jmeno.Substring(0, 2));
|
||||||
|
```
|
||||||
4
content/programovani/1-rocnik/08-vstup.en.md
Normal file
4
content/programovani/1-rocnik/08-vstup.en.md
Normal file
|
|
@ -0,0 +1,4 @@
|
||||||
|
---
|
||||||
|
nazev: Console input
|
||||||
|
popis: Console.ReadLine reads a line.
|
||||||
|
---
|
||||||
34
content/programovani/1-rocnik/08-vstup.md
Normal file
34
content/programovani/1-rocnik/08-vstup.md
Normal file
|
|
@ -0,0 +1,34 @@
|
||||||
|
---
|
||||||
|
id: cs-1-vstup
|
||||||
|
nazev: Vstup z konzole
|
||||||
|
popis: Console.ReadLine přečte řádek.
|
||||||
|
---
|
||||||
|
|
||||||
|
# Výklad
|
||||||
|
|
||||||
|
## ReadLine
|
||||||
|
*text*
|
||||||
|
|
||||||
|
- ReadLine počká na řádek a vrátí ho jako string.
|
||||||
|
- V této lekci řádek napište do pole Vstup a pak Spustit.
|
||||||
|
- Každé další ReadLine vezme další řádek vstupu.
|
||||||
|
|
||||||
|
> Tip: Když vstup necháte prázdný, ReadLine dostane konec a program může spadnout.
|
||||||
|
|
||||||
|
## Číslo ze vstupu
|
||||||
|
*Parse*
|
||||||
|
|
||||||
|
- ReadLine nikdy nevrací int. Vrací text.
|
||||||
|
- int.Parse z něj číslo udělá.
|
||||||
|
- Napište do vstupu třeba 17.
|
||||||
|
|
||||||
|
> Tip: Zkuste ve vstupu Anna a uvidíte chybu. Parse čeká číslo.
|
||||||
|
|
||||||
|
# Kód
|
||||||
|
|
||||||
|
```
|
||||||
|
Console.WriteLine("Kolik ti je?");
|
||||||
|
string radek = Console.ReadLine();
|
||||||
|
int vek = int.Parse(radek);
|
||||||
|
Console.WriteLine($"Za rok ti bude {vek + 1}.");
|
||||||
|
```
|
||||||
4
content/programovani/2-rocnik/01-if.en.md
Normal file
4
content/programovani/2-rocnik/01-if.en.md
Normal file
|
|
@ -0,0 +1,4 @@
|
||||||
|
---
|
||||||
|
nazev: If and else
|
||||||
|
popis: A branch that runs only sometimes.
|
||||||
|
---
|
||||||
43
content/programovani/2-rocnik/01-if.md
Normal file
43
content/programovani/2-rocnik/01-if.md
Normal file
|
|
@ -0,0 +1,43 @@
|
||||||
|
---
|
||||||
|
id: cs-2-if
|
||||||
|
nazev: If a else
|
||||||
|
popis: Větev, která se spustí jen někdy.
|
||||||
|
---
|
||||||
|
|
||||||
|
# Výklad
|
||||||
|
|
||||||
|
## Podmínka
|
||||||
|
*if*
|
||||||
|
|
||||||
|
- if (vek >= 18) spustí blok ve složených závorkách, jen když je podmínka true.
|
||||||
|
- else je větev pro opačný případ.
|
||||||
|
- else if přidá další podmínku mezi ně.
|
||||||
|
|
||||||
|
> Tip: Za if není středník. Středník by podmínku oddělil od bloku.
|
||||||
|
|
||||||
|
## Porovnání
|
||||||
|
*==*
|
||||||
|
|
||||||
|
- Čísla porovnávejte >, <, >=, <=, == a !=.
|
||||||
|
- Text se porovnává ==. "Anna" == "anna" je false.
|
||||||
|
- Do podmínky patří bool, ne přiřazení.
|
||||||
|
|
||||||
|
> Tip: Změňte vek a sledujte, která věta se vypíše.
|
||||||
|
|
||||||
|
# Kód
|
||||||
|
|
||||||
|
```
|
||||||
|
int vek = 17;
|
||||||
|
if (vek >= 18)
|
||||||
|
{
|
||||||
|
Console.WriteLine("plnoletý");
|
||||||
|
}
|
||||||
|
else if (vek >= 15)
|
||||||
|
{
|
||||||
|
Console.WriteLine("téměř");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Console.WriteLine("ještě ne");
|
||||||
|
}
|
||||||
|
```
|
||||||
4
content/programovani/2-rocnik/02-switch.en.md
Normal file
4
content/programovani/2-rocnik/02-switch.en.md
Normal file
|
|
@ -0,0 +1,4 @@
|
||||||
|
---
|
||||||
|
nazev: Switch
|
||||||
|
popis: Several branches for one value.
|
||||||
|
---
|
||||||
43
content/programovani/2-rocnik/02-switch.md
Normal file
43
content/programovani/2-rocnik/02-switch.md
Normal file
|
|
@ -0,0 +1,43 @@
|
||||||
|
---
|
||||||
|
id: cs-2-switch
|
||||||
|
nazev: Switch
|
||||||
|
popis: Víc větví podle jedné hodnoty.
|
||||||
|
---
|
||||||
|
|
||||||
|
# Výklad
|
||||||
|
|
||||||
|
## Větve
|
||||||
|
*case*
|
||||||
|
|
||||||
|
- switch vybere case, který se rovná hodnotě.
|
||||||
|
- break ukončí větev. Bez něj se překlad v klasickém zápisu zastaví.
|
||||||
|
- default je větev, když nesedí žádný case.
|
||||||
|
|
||||||
|
> Tip: Na známku 1 až 5 se switch hodí víc než řada else if.
|
||||||
|
|
||||||
|
## Nový zápis
|
||||||
|
*šipka*
|
||||||
|
|
||||||
|
- case 1 => "výborně" je krátká větev bez break.
|
||||||
|
- Výraz switch umí hodnotu rovnou vrátit.
|
||||||
|
- V lekci je klasický zápis, ať je vidět break.
|
||||||
|
|
||||||
|
> Tip: Přidejte case 5 a spusťte se znamkou 5.
|
||||||
|
|
||||||
|
# Kód
|
||||||
|
|
||||||
|
```
|
||||||
|
int znamka = 2;
|
||||||
|
switch (znamka)
|
||||||
|
{
|
||||||
|
case 1:
|
||||||
|
Console.WriteLine("výborně");
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
Console.WriteLine("chvalitebně");
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
Console.WriteLine("jiná známka");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
```
|
||||||
4
content/programovani/2-rocnik/03-while.en.md
Normal file
4
content/programovani/2-rocnik/03-while.en.md
Normal file
|
|
@ -0,0 +1,4 @@
|
||||||
|
---
|
||||||
|
nazev: While
|
||||||
|
popis: Repeat while a condition holds.
|
||||||
|
---
|
||||||
38
content/programovani/2-rocnik/03-while.md
Normal file
38
content/programovani/2-rocnik/03-while.md
Normal file
|
|
@ -0,0 +1,38 @@
|
||||||
|
---
|
||||||
|
id: cs-2-while
|
||||||
|
nazev: While
|
||||||
|
popis: Opakování, dokud podmínka platí.
|
||||||
|
---
|
||||||
|
|
||||||
|
# Výklad
|
||||||
|
|
||||||
|
## Smyčka
|
||||||
|
*while*
|
||||||
|
|
||||||
|
- Tělo se opakuje, dokud je podmínka true.
|
||||||
|
- Když podmínku nikdy nezměníte, smyčka neskončí.
|
||||||
|
- Aplikace dlouhý běh po chvíli zastaví.
|
||||||
|
|
||||||
|
> Tip: i = i + 1 nebo i++ posune čítač. Bez toho se i nemění.
|
||||||
|
|
||||||
|
## Součet
|
||||||
|
*1 až 4*
|
||||||
|
|
||||||
|
- Začněte součtem 0 a přičítejte i.
|
||||||
|
- 1+2+3+4 je 10.
|
||||||
|
- Podmínka i <= 4 pustí i číslo 4 dovnitř.
|
||||||
|
|
||||||
|
> Tip: Změňte 4 na 10. Součet 1 až 10 je 55.
|
||||||
|
|
||||||
|
# Kód
|
||||||
|
|
||||||
|
```
|
||||||
|
int i = 1;
|
||||||
|
int soucet = 0;
|
||||||
|
while (i <= 4)
|
||||||
|
{
|
||||||
|
soucet = soucet + i;
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
Console.WriteLine(soucet);
|
||||||
|
```
|
||||||
4
content/programovani/2-rocnik/04-for.en.md
Normal file
4
content/programovani/2-rocnik/04-for.en.md
Normal file
|
|
@ -0,0 +1,4 @@
|
||||||
|
---
|
||||||
|
nazev: For
|
||||||
|
popis: A loop with its counter in one place.
|
||||||
|
---
|
||||||
34
content/programovani/2-rocnik/04-for.md
Normal file
34
content/programovani/2-rocnik/04-for.md
Normal file
|
|
@ -0,0 +1,34 @@
|
||||||
|
---
|
||||||
|
id: cs-2-for
|
||||||
|
nazev: For
|
||||||
|
popis: Smyčka s čítačem na jednom místě.
|
||||||
|
---
|
||||||
|
|
||||||
|
# Výklad
|
||||||
|
|
||||||
|
## Tři části
|
||||||
|
*start; podmínka; krok*
|
||||||
|
|
||||||
|
- for (int i = 0; i < 3; i++) má začátek, podmínku a krok.
|
||||||
|
- i < 3 pustí 0, 1 a 2. Trojku už ne.
|
||||||
|
- Krok i++ se provede až po těle.
|
||||||
|
|
||||||
|
> Tip: Když potřebujete přesný počet opakování, for je přehlednější než while.
|
||||||
|
|
||||||
|
## Násobilka
|
||||||
|
*výpis*
|
||||||
|
|
||||||
|
- V těle můžete počítat i * 2.
|
||||||
|
- WriteLine v těle vypíše jeden řádek na každé opakování.
|
||||||
|
- Čítač nemusí začínat nulou.
|
||||||
|
|
||||||
|
> Tip: Přepište podmínku na i <= 5 a sledujte další řádek.
|
||||||
|
|
||||||
|
# Kód
|
||||||
|
|
||||||
|
```
|
||||||
|
for (int i = 1; i <= 3; i++)
|
||||||
|
{
|
||||||
|
Console.WriteLine($"{i} krát 2 je {i * 2}");
|
||||||
|
}
|
||||||
|
```
|
||||||
4
content/programovani/2-rocnik/05-pole.en.md
Normal file
4
content/programovani/2-rocnik/05-pole.en.md
Normal file
|
|
@ -0,0 +1,4 @@
|
||||||
|
---
|
||||||
|
nazev: Arrays
|
||||||
|
popis: Several values of one type under one name.
|
||||||
|
---
|
||||||
35
content/programovani/2-rocnik/05-pole.md
Normal file
35
content/programovani/2-rocnik/05-pole.md
Normal file
|
|
@ -0,0 +1,35 @@
|
||||||
|
---
|
||||||
|
id: cs-2-pole
|
||||||
|
nazev: Pole
|
||||||
|
popis: Víc hodnot stejného typu pod jedním jménem.
|
||||||
|
---
|
||||||
|
|
||||||
|
# Výklad
|
||||||
|
|
||||||
|
## Index
|
||||||
|
*od nuly*
|
||||||
|
|
||||||
|
- new int[] { 2, 4, 6 } je pole tří čísel.
|
||||||
|
- První prvek je znamky[0], poslední znamky[2].
|
||||||
|
- Length je počet prvků, ne index posledního.
|
||||||
|
|
||||||
|
> Tip: znamky[3] u pole délky 3 spadne. Poslední index je 2.
|
||||||
|
|
||||||
|
## Zápis
|
||||||
|
*prvku*
|
||||||
|
|
||||||
|
- Do prvku se dá uložit nová hodnota: znamky[1] = 5.
|
||||||
|
- Ostatní prvky zůstanou.
|
||||||
|
- Pole má pevnou délku. Přidat čtvrtý prvek nejde, na to je později List.
|
||||||
|
|
||||||
|
> Tip: Vyměňte první známku a spusťte znovu.
|
||||||
|
|
||||||
|
# Kód
|
||||||
|
|
||||||
|
```
|
||||||
|
int[] znamky = { 1, 3, 2 };
|
||||||
|
Console.WriteLine(znamky[0]);
|
||||||
|
Console.WriteLine(znamky.Length);
|
||||||
|
znamky[1] = 5;
|
||||||
|
Console.WriteLine(znamky[1]);
|
||||||
|
```
|
||||||
4
content/programovani/2-rocnik/06-foreach.en.md
Normal file
4
content/programovani/2-rocnik/06-foreach.en.md
Normal file
|
|
@ -0,0 +1,4 @@
|
||||||
|
---
|
||||||
|
nazev: Foreach
|
||||||
|
popis: Visit every item without an index.
|
||||||
|
---
|
||||||
35
content/programovani/2-rocnik/06-foreach.md
Normal file
35
content/programovani/2-rocnik/06-foreach.md
Normal file
|
|
@ -0,0 +1,35 @@
|
||||||
|
---
|
||||||
|
id: cs-2-foreach
|
||||||
|
nazev: Foreach
|
||||||
|
popis: Projít každý prvek bez indexu.
|
||||||
|
---
|
||||||
|
|
||||||
|
# Výklad
|
||||||
|
|
||||||
|
## Projítí
|
||||||
|
*foreach*
|
||||||
|
|
||||||
|
- foreach (int z in znamky) vezme prvky od prvního po poslední.
|
||||||
|
- z je kopie hodnoty. U int se zápisem do z pole nezmění.
|
||||||
|
- Když index nepotřebujete, foreach je kratší než for.
|
||||||
|
|
||||||
|
> Tip: Součet ve smyčce přičítá aktuální z.
|
||||||
|
|
||||||
|
## Texty
|
||||||
|
*string[]*
|
||||||
|
|
||||||
|
- Pole může být i ze stringů.
|
||||||
|
- Stejný foreach funguje pro oba typy, jen se liší typ prvku.
|
||||||
|
- Pořadí je pořadí v závorce při vytvoření.
|
||||||
|
|
||||||
|
> Tip: Přidejte do pole třetí jméno.
|
||||||
|
|
||||||
|
# Kód
|
||||||
|
|
||||||
|
```
|
||||||
|
string[] jmena = { "Anna", "Eva", "Jan" };
|
||||||
|
foreach (string jmeno in jmena)
|
||||||
|
{
|
||||||
|
Console.WriteLine(jmeno);
|
||||||
|
}
|
||||||
|
```
|
||||||
4
content/programovani/2-rocnik/07-metody.en.md
Normal file
4
content/programovani/2-rocnik/07-metody.en.md
Normal file
|
|
@ -0,0 +1,4 @@
|
||||||
|
---
|
||||||
|
nazev: Methods
|
||||||
|
popis: A named piece of the program you can call.
|
||||||
|
---
|
||||||
37
content/programovani/2-rocnik/07-metody.md
Normal file
37
content/programovani/2-rocnik/07-metody.md
Normal file
|
|
@ -0,0 +1,37 @@
|
||||||
|
---
|
||||||
|
id: cs-2-metody
|
||||||
|
nazev: Metody
|
||||||
|
popis: Pojmenovaný kus programu, který lze zavolat.
|
||||||
|
---
|
||||||
|
|
||||||
|
# Výklad
|
||||||
|
|
||||||
|
## Volání
|
||||||
|
*jméno()*
|
||||||
|
|
||||||
|
- static void Pozdrav() je metoda bez výsledku.
|
||||||
|
- Zavolá se Pozdrav(); včetně závorek.
|
||||||
|
- Bez závorek by to bylo jen jméno, ne volání.
|
||||||
|
|
||||||
|
> Tip: Metodu lze zavolat víckrát a pokaždé udělá totéž.
|
||||||
|
|
||||||
|
## Proč
|
||||||
|
*jednou*
|
||||||
|
|
||||||
|
- Kód, který se opakuje, patří do metody.
|
||||||
|
- Jméno metody říká, co dělá: Pozdrav, ne Metoda1.
|
||||||
|
- void znamená, že nevrací hodnotu.
|
||||||
|
|
||||||
|
> Tip: Přidejte druhé volání Pozdrav().
|
||||||
|
|
||||||
|
# Kód
|
||||||
|
|
||||||
|
```
|
||||||
|
Pozdrav();
|
||||||
|
Pozdrav();
|
||||||
|
|
||||||
|
static void Pozdrav()
|
||||||
|
{
|
||||||
|
Console.WriteLine("Ahoj");
|
||||||
|
}
|
||||||
|
```
|
||||||
4
content/programovani/2-rocnik/08-navrat.en.md
Normal file
4
content/programovani/2-rocnik/08-navrat.en.md
Normal file
|
|
@ -0,0 +1,4 @@
|
||||||
|
---
|
||||||
|
nazev: Parameters and return
|
||||||
|
popis: A method receives values and can return one.
|
||||||
|
---
|
||||||
38
content/programovani/2-rocnik/08-navrat.md
Normal file
38
content/programovani/2-rocnik/08-navrat.md
Normal file
|
|
@ -0,0 +1,38 @@
|
||||||
|
---
|
||||||
|
id: cs-2-navrat
|
||||||
|
nazev: Parametry a návrat
|
||||||
|
popis: Metoda dostane hodnoty a může jednu vrátit.
|
||||||
|
---
|
||||||
|
|
||||||
|
# Výklad
|
||||||
|
|
||||||
|
## Parametr
|
||||||
|
*vstup*
|
||||||
|
|
||||||
|
- int a v závorce je parametr. Volající dodá argument.
|
||||||
|
- Součet(2, 5) uloží 2 do a a 5 do b.
|
||||||
|
- Jména parametrů platí jen uvnitř metody.
|
||||||
|
|
||||||
|
> Tip: Změňte argumenty a výsledek se změní, metoda ne.
|
||||||
|
|
||||||
|
## return
|
||||||
|
*výstup*
|
||||||
|
|
||||||
|
- int před jménem je typ výsledku.
|
||||||
|
- return hodnotu vrátí a metodu ukončí.
|
||||||
|
- Volající si výsledek může uložit: int s = Soucet(2, 5);
|
||||||
|
|
||||||
|
> Tip: return 0 omylem na začátku by zbytek metody nespustil.
|
||||||
|
|
||||||
|
# Kód
|
||||||
|
|
||||||
|
```
|
||||||
|
int s = Soucet(2, 5);
|
||||||
|
Console.WriteLine(s);
|
||||||
|
Console.WriteLine(Soucet(10, 1));
|
||||||
|
|
||||||
|
static int Soucet(int a, int b)
|
||||||
|
{
|
||||||
|
return a + b;
|
||||||
|
}
|
||||||
|
```
|
||||||
4
content/programovani/3-rocnik/01-trida.en.md
Normal file
4
content/programovani/3-rocnik/01-trida.en.md
Normal file
|
|
@ -0,0 +1,4 @@
|
||||||
|
---
|
||||||
|
nazev: Class and object
|
||||||
|
popis: Your own type and one concrete piece of it.
|
||||||
|
---
|
||||||
40
content/programovani/3-rocnik/01-trida.md
Normal file
40
content/programovani/3-rocnik/01-trida.md
Normal file
|
|
@ -0,0 +1,40 @@
|
||||||
|
---
|
||||||
|
id: cs-3-trida
|
||||||
|
nazev: Třída a objekt
|
||||||
|
popis: Vlastní typ a jeho konkrétní kus.
|
||||||
|
---
|
||||||
|
|
||||||
|
# Výklad
|
||||||
|
|
||||||
|
## Třída
|
||||||
|
*návod*
|
||||||
|
|
||||||
|
- class Zak popisuje, co žák má: jméno a věk.
|
||||||
|
- new Zak() vytvoří objekt, jeden konkrétní kus.
|
||||||
|
- Dva new jsou dva objekty. Změna jednoho druhý nemění.
|
||||||
|
|
||||||
|
> Tip: Třída sama o sobě není žák. Je to popis.
|
||||||
|
|
||||||
|
## Pole objektu
|
||||||
|
*tečka*
|
||||||
|
|
||||||
|
- z.Jmeno se čte a zapisuje přes tečku.
|
||||||
|
- Na začátek dáme veřejná pole, ať je vidět, komu patří.
|
||||||
|
- Později je schováme za vlastnost.
|
||||||
|
|
||||||
|
> Tip: Vytvořte druhého žáka a vypište obě jména.
|
||||||
|
|
||||||
|
# Kód
|
||||||
|
|
||||||
|
```
|
||||||
|
Zak z = new Zak();
|
||||||
|
z.Jmeno = "Anna";
|
||||||
|
z.Vek = 17;
|
||||||
|
Console.WriteLine($"{z.Jmeno}, {z.Vek}");
|
||||||
|
|
||||||
|
class Zak
|
||||||
|
{
|
||||||
|
public string Jmeno;
|
||||||
|
public int Vek;
|
||||||
|
}
|
||||||
|
```
|
||||||
4
content/programovani/3-rocnik/02-konstruktor.en.md
Normal file
4
content/programovani/3-rocnik/02-konstruktor.en.md
Normal file
|
|
@ -0,0 +1,4 @@
|
||||||
|
---
|
||||||
|
nazev: Constructor
|
||||||
|
popis: A method that fills the object immediately.
|
||||||
|
---
|
||||||
44
content/programovani/3-rocnik/02-konstruktor.md
Normal file
44
content/programovani/3-rocnik/02-konstruktor.md
Normal file
|
|
@ -0,0 +1,44 @@
|
||||||
|
---
|
||||||
|
id: cs-3-konstruktor
|
||||||
|
nazev: Konstruktor
|
||||||
|
popis: Metoda, která objekt hned naplní.
|
||||||
|
---
|
||||||
|
|
||||||
|
# Výklad
|
||||||
|
|
||||||
|
## Jméno třídy
|
||||||
|
*new Zak*
|
||||||
|
|
||||||
|
- Konstruktor se jmenuje jako třída a nemá návratový typ.
|
||||||
|
- new Zak("Anna", 17) ho zavolá.
|
||||||
|
- Prázdný new Zak() přestane fungovat, jakmile napíšete vlastní konstruktor.
|
||||||
|
|
||||||
|
> Tip: Hodnoty patří do new, ne až na další řádky.
|
||||||
|
|
||||||
|
## this
|
||||||
|
*tento objekt*
|
||||||
|
|
||||||
|
- this.Jmeno je pole objektu. Jmeno bez this by v konstruktoru byl parametr.
|
||||||
|
- Když se jmenují stejně, this je nutný.
|
||||||
|
- Mimo konstruktor this často není potřeba.
|
||||||
|
|
||||||
|
> Tip: Přidejte do výpisu věk z konstruktoru.
|
||||||
|
|
||||||
|
# Kód
|
||||||
|
|
||||||
|
```
|
||||||
|
Zak z = new Zak("Anna", 17);
|
||||||
|
Console.WriteLine($"{z.Jmeno}, {z.Vek}");
|
||||||
|
|
||||||
|
class Zak
|
||||||
|
{
|
||||||
|
public string Jmeno;
|
||||||
|
public int Vek;
|
||||||
|
|
||||||
|
public Zak(string jmeno, int vek)
|
||||||
|
{
|
||||||
|
Jmeno = jmeno;
|
||||||
|
Vek = vek;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
4
content/programovani/3-rocnik/03-vlastnosti.en.md
Normal file
4
content/programovani/3-rocnik/03-vlastnosti.en.md
Normal file
|
|
@ -0,0 +1,4 @@
|
||||||
|
---
|
||||||
|
nazev: Properties
|
||||||
|
popis: get and set instead of a bare field.
|
||||||
|
---
|
||||||
50
content/programovani/3-rocnik/03-vlastnosti.md
Normal file
50
content/programovani/3-rocnik/03-vlastnosti.md
Normal file
|
|
@ -0,0 +1,50 @@
|
||||||
|
---
|
||||||
|
id: cs-3-vlastnosti
|
||||||
|
nazev: Vlastnosti
|
||||||
|
popis: get a set místo holého pole.
|
||||||
|
---
|
||||||
|
|
||||||
|
# Výklad
|
||||||
|
|
||||||
|
## get a set
|
||||||
|
*tečka zůstává*
|
||||||
|
|
||||||
|
- public string Jmeno { get; set; } se zvenku používá jako pole.
|
||||||
|
- Uvnitř může set odmítnout špatnou hodnotu.
|
||||||
|
- get hodnotu vrátí, set ji uloží.
|
||||||
|
|
||||||
|
> Tip: Zápis z.Vek = -1 může set zarazit, holé pole ne.
|
||||||
|
|
||||||
|
## Kontrola
|
||||||
|
*set*
|
||||||
|
|
||||||
|
- V příkladu set u věku záporné číslo neuloží.
|
||||||
|
- get pořád vrátí poslední platnou hodnotu.
|
||||||
|
- Tak se objekt nepokazí zvenku.
|
||||||
|
|
||||||
|
> Tip: Zkuste z.Vek = -3 a vypište věk. Zůstane 17.
|
||||||
|
|
||||||
|
# Kód
|
||||||
|
|
||||||
|
```
|
||||||
|
Zak z = new Zak("Anna", 17);
|
||||||
|
z.Vek = -3;
|
||||||
|
Console.WriteLine(z.Vek);
|
||||||
|
|
||||||
|
class Zak
|
||||||
|
{
|
||||||
|
public string Jmeno { get; set; }
|
||||||
|
private int vek;
|
||||||
|
public int Vek
|
||||||
|
{
|
||||||
|
get { return vek; }
|
||||||
|
set { if (value >= 0) vek = value; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public Zak(string jmeno, int vek)
|
||||||
|
{
|
||||||
|
Jmeno = jmeno;
|
||||||
|
Vek = vek;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
4
content/programovani/3-rocnik/04-zapouzdreni.en.md
Normal file
4
content/programovani/3-rocnik/04-zapouzdreni.en.md
Normal file
|
|
@ -0,0 +1,4 @@
|
||||||
|
---
|
||||||
|
nazev: Encapsulation
|
||||||
|
popis: Private data and public methods.
|
||||||
|
---
|
||||||
49
content/programovani/3-rocnik/04-zapouzdreni.md
Normal file
49
content/programovani/3-rocnik/04-zapouzdreni.md
Normal file
|
|
@ -0,0 +1,49 @@
|
||||||
|
---
|
||||||
|
id: cs-3-zapouzdreni
|
||||||
|
nazev: Zapouzdření
|
||||||
|
popis: Soukromá data a veřejné metody.
|
||||||
|
---
|
||||||
|
|
||||||
|
# Výklad
|
||||||
|
|
||||||
|
## private
|
||||||
|
*schované*
|
||||||
|
|
||||||
|
- private pole vidí jen kód té třídy.
|
||||||
|
- public metoda je dveře, kterými se s objektem mluví.
|
||||||
|
- Zvenku z.penize nenapíšete. Zavoláte Pridej.
|
||||||
|
|
||||||
|
> Tip: Když je vše public, kdokoli rozbije pravidla objektu.
|
||||||
|
|
||||||
|
## Metoda
|
||||||
|
*Pridej*
|
||||||
|
|
||||||
|
- Pridej přičte a vrátí nový stav.
|
||||||
|
- Záporný vklad lze v metodě odmítnout.
|
||||||
|
- Výpis stavu jde přes Kolik, ne přes pole.
|
||||||
|
|
||||||
|
> Tip: Zkuste Pridej(-10). Stav se nemá zmenšit.
|
||||||
|
|
||||||
|
# Kód
|
||||||
|
|
||||||
|
```
|
||||||
|
Pokladna p = new Pokladna();
|
||||||
|
p.Pridej(50);
|
||||||
|
p.Pridej(-10);
|
||||||
|
Console.WriteLine(p.Kolik());
|
||||||
|
|
||||||
|
class Pokladna
|
||||||
|
{
|
||||||
|
private int penize;
|
||||||
|
|
||||||
|
public void Pridej(int castka)
|
||||||
|
{
|
||||||
|
if (castka > 0) penize = penize + castka;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int Kolik()
|
||||||
|
{
|
||||||
|
return penize;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
4
content/programovani/3-rocnik/05-dedicnost.en.md
Normal file
4
content/programovani/3-rocnik/05-dedicnost.en.md
Normal file
|
|
@ -0,0 +1,4 @@
|
||||||
|
---
|
||||||
|
nazev: Inheritance
|
||||||
|
popis: A new class takes over a finished one.
|
||||||
|
---
|
||||||
52
content/programovani/3-rocnik/05-dedicnost.md
Normal file
52
content/programovani/3-rocnik/05-dedicnost.md
Normal file
|
|
@ -0,0 +1,52 @@
|
||||||
|
---
|
||||||
|
id: cs-3-dedicnost
|
||||||
|
nazev: Dědičnost
|
||||||
|
popis: Nová třída převezme hotovou.
|
||||||
|
---
|
||||||
|
|
||||||
|
# Výklad
|
||||||
|
|
||||||
|
## base
|
||||||
|
*: Zak*
|
||||||
|
|
||||||
|
- class Student : Zak znamená, že student je žák a má něco navíc.
|
||||||
|
- Dostane Jmeno a Vek, aniž by se psaly znovu.
|
||||||
|
- base(...) zavolá konstruktor předka.
|
||||||
|
|
||||||
|
> Tip: Student umí to, co Zak, a ještě obor.
|
||||||
|
|
||||||
|
## Je
|
||||||
|
*typ*
|
||||||
|
|
||||||
|
- Student lze uložit do proměnné Zak. Pořád je to student.
|
||||||
|
- Opačně žák bez oboru studentem není.
|
||||||
|
- Na specialitu sáhnete, až když máte původní typ.
|
||||||
|
|
||||||
|
> Tip: Vypište i obor. Patří studentovi, ne obecnému žákovi.
|
||||||
|
|
||||||
|
# Kód
|
||||||
|
|
||||||
|
```
|
||||||
|
Student s = new Student("Eva", 18, "informatika");
|
||||||
|
Console.WriteLine($"{s.Jmeno}, {s.Obor}");
|
||||||
|
|
||||||
|
class Zak
|
||||||
|
{
|
||||||
|
public string Jmeno { get; set; }
|
||||||
|
public int Vek { get; set; }
|
||||||
|
public Zak(string jmeno, int vek)
|
||||||
|
{
|
||||||
|
Jmeno = jmeno;
|
||||||
|
Vek = vek;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class Student : Zak
|
||||||
|
{
|
||||||
|
public string Obor { get; set; }
|
||||||
|
public Student(string jmeno, int vek, string obor) : base(jmeno, vek)
|
||||||
|
{
|
||||||
|
Obor = obor;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
4
content/programovani/3-rocnik/06-polymorfismus.en.md
Normal file
4
content/programovani/3-rocnik/06-polymorfismus.en.md
Normal file
|
|
@ -0,0 +1,4 @@
|
||||||
|
---
|
||||||
|
nazev: Polymorphism
|
||||||
|
popis: The same call, a different answer from the child.
|
||||||
|
---
|
||||||
50
content/programovani/3-rocnik/06-polymorfismus.md
Normal file
50
content/programovani/3-rocnik/06-polymorfismus.md
Normal file
|
|
@ -0,0 +1,50 @@
|
||||||
|
---
|
||||||
|
id: cs-3-polymorfismus
|
||||||
|
nazev: Polymorfismus
|
||||||
|
popis: Stejné volání, jiná odpověď potomka.
|
||||||
|
---
|
||||||
|
|
||||||
|
# Výklad
|
||||||
|
|
||||||
|
## virtual a override
|
||||||
|
*přepsání*
|
||||||
|
|
||||||
|
- virtual u předka dovolí potomkovi metodu vyměnit.
|
||||||
|
- override u potomka řekne, že jde o tu samou metodu.
|
||||||
|
- Bez virtual by potomek jen schoval jméno a přes typ předka by se volala stará verze.
|
||||||
|
|
||||||
|
> Tip: Proměnná je Zvire, objekt je Pes. Zavolá se štěk, ne obecné zvíře.
|
||||||
|
|
||||||
|
## Seznam
|
||||||
|
*víc tvarů*
|
||||||
|
|
||||||
|
- Do pole Zvire lze dát psa i kočku.
|
||||||
|
- foreach volá Zvuk a každý objekt odpoví po svém.
|
||||||
|
- Nové zvíře přidáte třídou, ne úpravou smyčky.
|
||||||
|
|
||||||
|
> Tip: Přidejte do pole kočku a třetí řádek výstupu bude mňau.
|
||||||
|
|
||||||
|
# Kód
|
||||||
|
|
||||||
|
```
|
||||||
|
Zvire[] zvirata = { new Pes(), new Kocka() };
|
||||||
|
foreach (Zvire z in zvirata)
|
||||||
|
{
|
||||||
|
Console.WriteLine(z.Zvuk());
|
||||||
|
}
|
||||||
|
|
||||||
|
class Zvire
|
||||||
|
{
|
||||||
|
public virtual string Zvuk() { return "..."; }
|
||||||
|
}
|
||||||
|
|
||||||
|
class Pes : Zvire
|
||||||
|
{
|
||||||
|
public override string Zvuk() { return "haf"; }
|
||||||
|
}
|
||||||
|
|
||||||
|
class Kocka : Zvire
|
||||||
|
{
|
||||||
|
public override string Zvuk() { return "mňau"; }
|
||||||
|
}
|
||||||
|
```
|
||||||
4
content/programovani/3-rocnik/07-rozhrani.en.md
Normal file
4
content/programovani/3-rocnik/07-rozhrani.en.md
Normal file
|
|
@ -0,0 +1,4 @@
|
||||||
|
---
|
||||||
|
nazev: Interface
|
||||||
|
popis: A contract: which methods a type must have.
|
||||||
|
---
|
||||||
50
content/programovani/3-rocnik/07-rozhrani.md
Normal file
50
content/programovani/3-rocnik/07-rozhrani.md
Normal file
|
|
@ -0,0 +1,50 @@
|
||||||
|
---
|
||||||
|
id: cs-3-rozhrani
|
||||||
|
nazev: Rozhraní
|
||||||
|
popis: Smlouva: které metody typ musí mít.
|
||||||
|
---
|
||||||
|
|
||||||
|
# Výklad
|
||||||
|
|
||||||
|
## interface
|
||||||
|
*I*
|
||||||
|
|
||||||
|
- interface nemá hotový kód, jen hlavičky metod.
|
||||||
|
- class Pes : IMluvici slíbí, že Mluv napíše.
|
||||||
|
- Jedna třída může splnit víc rozhraní. Dědit může jen jednu třídu.
|
||||||
|
|
||||||
|
> Tip: Jméno rozhraní se často píše s I na začátku: IMluvici.
|
||||||
|
|
||||||
|
## Společný typ
|
||||||
|
*seznam*
|
||||||
|
|
||||||
|
- Proměnná typu rozhraní udrží kohokoli, kdo smlouvu splnil.
|
||||||
|
- Pes a Radio nemají společného předka, ale oba umí Mluv.
|
||||||
|
- Smyčka jim rozumí přes smlouvu, ne přes původ.
|
||||||
|
|
||||||
|
> Tip: Rozhraní neříká, jak metoda funguje. To napíše až třída.
|
||||||
|
|
||||||
|
# Kód
|
||||||
|
|
||||||
|
```
|
||||||
|
IMluvici[] veci = { new Pes(), new Radio() };
|
||||||
|
foreach (IMluvici v in veci)
|
||||||
|
{
|
||||||
|
Console.WriteLine(v.Mluv());
|
||||||
|
}
|
||||||
|
|
||||||
|
interface IMluvici
|
||||||
|
{
|
||||||
|
string Mluv();
|
||||||
|
}
|
||||||
|
|
||||||
|
class Pes : IMluvici
|
||||||
|
{
|
||||||
|
public string Mluv() { return "haf"; }
|
||||||
|
}
|
||||||
|
|
||||||
|
class Radio : IMluvici
|
||||||
|
{
|
||||||
|
public string Mluv() { return "hudba"; }
|
||||||
|
}
|
||||||
|
```
|
||||||
4
content/programovani/3-rocnik/08-enum.en.md
Normal file
4
content/programovani/3-rocnik/08-enum.en.md
Normal file
|
|
@ -0,0 +1,4 @@
|
||||||
|
---
|
||||||
|
nazev: Enum
|
||||||
|
popis: A fixed list of named values.
|
||||||
|
---
|
||||||
50
content/programovani/3-rocnik/08-enum.md
Normal file
50
content/programovani/3-rocnik/08-enum.md
Normal file
|
|
@ -0,0 +1,50 @@
|
||||||
|
---
|
||||||
|
id: cs-3-enum
|
||||||
|
nazev: Výčet
|
||||||
|
popis: Pevný seznam pojmenovaných hodnot.
|
||||||
|
---
|
||||||
|
|
||||||
|
# Výklad
|
||||||
|
|
||||||
|
## enum
|
||||||
|
*Den*
|
||||||
|
|
||||||
|
- enum Den { Po, Ut, St } jsou tři pojmenované hodnoty.
|
||||||
|
- Místo magického čísla 1 píšete Den.Po.
|
||||||
|
- Překladač nepustí hodnotu, která ve výčtu není.
|
||||||
|
|
||||||
|
> Tip: Čísla 0, 1, 2 by za měsíc nikdo nepřečetl. Jména ano.
|
||||||
|
|
||||||
|
## switch
|
||||||
|
*přes výčet*
|
||||||
|
|
||||||
|
- switch na enum má case pro každou hodnotu.
|
||||||
|
- Když přidáte čtvrtek a zapomenete case, překlad na to v novém zápisu upozorní.
|
||||||
|
- Výčet je typ. Není to string.
|
||||||
|
|
||||||
|
> Tip: Změňte dnes na Den.St.
|
||||||
|
|
||||||
|
# Kód
|
||||||
|
|
||||||
|
```
|
||||||
|
Den dnes = Den.Po;
|
||||||
|
switch (dnes)
|
||||||
|
{
|
||||||
|
case Den.Po:
|
||||||
|
Console.WriteLine("pondělí");
|
||||||
|
break;
|
||||||
|
case Den.Ut:
|
||||||
|
Console.WriteLine("úterý");
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
Console.WriteLine("jiný den");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
enum Den
|
||||||
|
{
|
||||||
|
Po,
|
||||||
|
Ut,
|
||||||
|
St
|
||||||
|
}
|
||||||
|
```
|
||||||
4
content/programovani/4-rocnik/01-vyjimky.en.md
Normal file
4
content/programovani/4-rocnik/01-vyjimky.en.md
Normal file
|
|
@ -0,0 +1,4 @@
|
||||||
|
---
|
||||||
|
nazev: Exceptions
|
||||||
|
popis: An error you can catch and explain.
|
||||||
|
---
|
||||||
43
content/programovani/4-rocnik/01-vyjimky.md
Normal file
43
content/programovani/4-rocnik/01-vyjimky.md
Normal file
|
|
@ -0,0 +1,43 @@
|
||||||
|
---
|
||||||
|
id: cs-4-vyjimky
|
||||||
|
nazev: Výjimky
|
||||||
|
popis: Chyba, kterou lze chytit a vysvětlit.
|
||||||
|
---
|
||||||
|
|
||||||
|
# Výklad
|
||||||
|
|
||||||
|
## try
|
||||||
|
*pokus*
|
||||||
|
|
||||||
|
- try je kus, který může spadnout.
|
||||||
|
- catch (Exception e) ho chytí, místo aby spadl celý program.
|
||||||
|
- e.Message je text chyby.
|
||||||
|
|
||||||
|
> Tip: Bez try by Parse na textu abc program ukončil.
|
||||||
|
|
||||||
|
## finally
|
||||||
|
*vždy*
|
||||||
|
|
||||||
|
- finally se spustí, ať try prošel, nebo ne.
|
||||||
|
- Hodí se na úklid. V příkladu jen vypíše, že končíme.
|
||||||
|
- catch může být víc, pro různé typy chyb.
|
||||||
|
|
||||||
|
> Tip: Změňte abc na 5. catch se přeskočí, finally ne.
|
||||||
|
|
||||||
|
# Kód
|
||||||
|
|
||||||
|
```
|
||||||
|
try
|
||||||
|
{
|
||||||
|
int n = int.Parse("abc");
|
||||||
|
Console.WriteLine(n);
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
Console.WriteLine("Chyba: " + e.Message);
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
Console.WriteLine("konec");
|
||||||
|
}
|
||||||
|
```
|
||||||
4
content/programovani/4-rocnik/02-list.en.md
Normal file
4
content/programovani/4-rocnik/02-list.en.md
Normal file
|
|
@ -0,0 +1,4 @@
|
||||||
|
---
|
||||||
|
nazev: List
|
||||||
|
popis: A list that can grow.
|
||||||
|
---
|
||||||
39
content/programovani/4-rocnik/02-list.md
Normal file
39
content/programovani/4-rocnik/02-list.md
Normal file
|
|
@ -0,0 +1,39 @@
|
||||||
|
---
|
||||||
|
id: cs-4-list
|
||||||
|
nazev: List
|
||||||
|
popis: Seznam, který umí růst.
|
||||||
|
---
|
||||||
|
|
||||||
|
# Výklad
|
||||||
|
|
||||||
|
## Přidávání
|
||||||
|
*Add*
|
||||||
|
|
||||||
|
- List<string> začne prázdný. Add přidá na konec.
|
||||||
|
- Count je počet. Indexy jsou zase od nuly.
|
||||||
|
- Na rozdíl od pole délku dopředu neříkáte.
|
||||||
|
|
||||||
|
> Tip: List<string> potřebuje špičaté závorky s typem prvku.
|
||||||
|
|
||||||
|
## Odebrání
|
||||||
|
*Remove*
|
||||||
|
|
||||||
|
- Remove("Eva") odebere první Evu.
|
||||||
|
- RemoveAt(0) odebere podle indexu.
|
||||||
|
- foreach pak projde, co zůstalo.
|
||||||
|
|
||||||
|
> Tip: Přidejte třetí jméno před odebráním.
|
||||||
|
|
||||||
|
# Kód
|
||||||
|
|
||||||
|
```
|
||||||
|
List<string> jmena = new List<string>();
|
||||||
|
jmena.Add("Anna");
|
||||||
|
jmena.Add("Eva");
|
||||||
|
jmena.Remove("Eva");
|
||||||
|
foreach (string j in jmena)
|
||||||
|
{
|
||||||
|
Console.WriteLine(j);
|
||||||
|
}
|
||||||
|
Console.WriteLine(jmena.Count);
|
||||||
|
```
|
||||||
4
content/programovani/4-rocnik/03-slovnik.en.md
Normal file
4
content/programovani/4-rocnik/03-slovnik.en.md
Normal file
|
|
@ -0,0 +1,4 @@
|
||||||
|
---
|
||||||
|
nazev: Dictionary
|
||||||
|
popis: A value under a key.
|
||||||
|
---
|
||||||
39
content/programovani/4-rocnik/03-slovnik.md
Normal file
39
content/programovani/4-rocnik/03-slovnik.md
Normal file
|
|
@ -0,0 +1,39 @@
|
||||||
|
---
|
||||||
|
id: cs-4-slovnik
|
||||||
|
nazev: Slovník
|
||||||
|
popis: Hodnota pod klíčem.
|
||||||
|
---
|
||||||
|
|
||||||
|
# Výklad
|
||||||
|
|
||||||
|
## Klíč
|
||||||
|
*jednou*
|
||||||
|
|
||||||
|
- Dictionary<string, int> má klíč text a hodnotu číslo.
|
||||||
|
- znamky["Anna"] = 1 uloží nebo přepíše.
|
||||||
|
- Stejný klíč je v slovníku jen jednou.
|
||||||
|
|
||||||
|
> Tip: Pole je podle čísla. Slovník je podle jména, které si zvolíte.
|
||||||
|
|
||||||
|
## Čtení
|
||||||
|
*ContainsKey*
|
||||||
|
|
||||||
|
- Čtení chybějícího klíče spadne.
|
||||||
|
- ContainsKey napřed zjistí, jestli klíč je.
|
||||||
|
- foreach projde dvojice Key a Value.
|
||||||
|
|
||||||
|
> Tip: Zeptejte se na jméno, které v slovníku není, až po ContainsKey.
|
||||||
|
|
||||||
|
# Kód
|
||||||
|
|
||||||
|
```
|
||||||
|
Dictionary<string, int> znamky = new Dictionary<string, int>();
|
||||||
|
znamky["Anna"] = 1;
|
||||||
|
znamky["Eva"] = 2;
|
||||||
|
znamky["Anna"] = 3;
|
||||||
|
if (znamky.ContainsKey("Eva"))
|
||||||
|
{
|
||||||
|
Console.WriteLine(znamky["Eva"]);
|
||||||
|
}
|
||||||
|
Console.WriteLine(znamky["Anna"]);
|
||||||
|
```
|
||||||
4
content/programovani/4-rocnik/04-linq.en.md
Normal file
4
content/programovani/4-rocnik/04-linq.en.md
Normal file
|
|
@ -0,0 +1,4 @@
|
||||||
|
---
|
||||||
|
nazev: LINQ
|
||||||
|
popis: Queries over a list.
|
||||||
|
---
|
||||||
36
content/programovani/4-rocnik/04-linq.md
Normal file
36
content/programovani/4-rocnik/04-linq.md
Normal file
|
|
@ -0,0 +1,36 @@
|
||||||
|
---
|
||||||
|
id: cs-4-linq
|
||||||
|
nazev: LINQ
|
||||||
|
popis: Dotazy nad seznamem.
|
||||||
|
---
|
||||||
|
|
||||||
|
# Výklad
|
||||||
|
|
||||||
|
## Where
|
||||||
|
*filtr*
|
||||||
|
|
||||||
|
- Where nechá jen prvky, které splní podmínku.
|
||||||
|
- n => n >= 10 je krátká funkce: vezmi n a vrať, jestli je aspoň 10.
|
||||||
|
- Samotné Where ještě nic neprojde. ToList nebo foreach výsledek vezme.
|
||||||
|
|
||||||
|
> Tip: Šipka => není porovnání. Je to zápis malé funkce.
|
||||||
|
|
||||||
|
## Select
|
||||||
|
*převod*
|
||||||
|
|
||||||
|
- Select z každého prvku udělá jiný.
|
||||||
|
- OrderBy seřadí.
|
||||||
|
- Dotaz lze řetězit: Where a pak Select.
|
||||||
|
|
||||||
|
> Tip: Změňte 10 na 12 a dvojka z výpisu zmizí.
|
||||||
|
|
||||||
|
# Kód
|
||||||
|
|
||||||
|
```
|
||||||
|
int[] cisla = { 4, 11, 7, 20 };
|
||||||
|
var velka = cisla.Where(n => n >= 10).OrderBy(n => n);
|
||||||
|
foreach (int n in velka)
|
||||||
|
{
|
||||||
|
Console.WriteLine(n);
|
||||||
|
}
|
||||||
|
```
|
||||||
4
content/programovani/4-rocnik/05-soubory.en.md
Normal file
4
content/programovani/4-rocnik/05-soubory.en.md
Normal file
|
|
@ -0,0 +1,4 @@
|
||||||
|
---
|
||||||
|
nazev: Files
|
||||||
|
popis: Writing and reading text.
|
||||||
|
---
|
||||||
33
content/programovani/4-rocnik/05-soubory.md
Normal file
33
content/programovani/4-rocnik/05-soubory.md
Normal file
|
|
@ -0,0 +1,33 @@
|
||||||
|
---
|
||||||
|
id: cs-4-soubory
|
||||||
|
nazev: Soubory
|
||||||
|
popis: Zápis a čtení textu.
|
||||||
|
---
|
||||||
|
|
||||||
|
# Výklad
|
||||||
|
|
||||||
|
## WriteAllText
|
||||||
|
*jednou*
|
||||||
|
|
||||||
|
- File.WriteAllText zapíše celý soubor. Když existuje, přepíše ho.
|
||||||
|
- Cesta pozdrav.txt je vedle programu, v jeho složce.
|
||||||
|
- ReadAllText celý text vrátí jako string.
|
||||||
|
|
||||||
|
> Tip: Na telefonu se soubor nespustí. Na počítači s .NET ano.
|
||||||
|
|
||||||
|
## Řádky
|
||||||
|
*Append*
|
||||||
|
|
||||||
|
- AppendAllText přidá na konec a soubor nesmaže.
|
||||||
|
- ReadAllLines vrátí pole řádků.
|
||||||
|
- Po skončení programu soubor ve složce laboratoře zůstane.
|
||||||
|
|
||||||
|
> Tip: Spusťte dvakrát. Druhý zápis WriteAllText starý text nahradí.
|
||||||
|
|
||||||
|
# Kód
|
||||||
|
|
||||||
|
```
|
||||||
|
File.WriteAllText("pozdrav.txt", "Ahoj ze souboru");
|
||||||
|
string zpet = File.ReadAllText("pozdrav.txt");
|
||||||
|
Console.WriteLine(zpet);
|
||||||
|
```
|
||||||
4
content/programovani/4-rocnik/06-generika.en.md
Normal file
4
content/programovani/4-rocnik/06-generika.en.md
Normal file
|
|
@ -0,0 +1,4 @@
|
||||||
|
---
|
||||||
|
nazev: Generics
|
||||||
|
popis: One method for several types.
|
||||||
|
---
|
||||||
37
content/programovani/4-rocnik/06-generika.md
Normal file
37
content/programovani/4-rocnik/06-generika.md
Normal file
|
|
@ -0,0 +1,37 @@
|
||||||
|
---
|
||||||
|
id: cs-4-generika
|
||||||
|
nazev: Generika
|
||||||
|
popis: Jedna metoda pro víc typů.
|
||||||
|
---
|
||||||
|
|
||||||
|
# Výklad
|
||||||
|
|
||||||
|
## T
|
||||||
|
*typový parametr*
|
||||||
|
|
||||||
|
- static void Vypis<T>(T hodnota) přijme int i string.
|
||||||
|
- T se doplní při volání podle argumentu.
|
||||||
|
- Jedna metoda tak nenutí kopírovat kód pro každý typ.
|
||||||
|
|
||||||
|
> Tip: Když metoda s T má umět jen porovnávat, T se omezí. Tady stačí výpis.
|
||||||
|
|
||||||
|
## List
|
||||||
|
*už ho znáte*
|
||||||
|
|
||||||
|
- List<string> je generická třída. string je dosazený T.
|
||||||
|
- Stejný List<int> drží čísla a metody jsou stejné.
|
||||||
|
- Generika hlídá typ. Do List<int> string nedáte.
|
||||||
|
|
||||||
|
> Tip: Zavolejte Vypis s číslem i s textem.
|
||||||
|
|
||||||
|
# Kód
|
||||||
|
|
||||||
|
```
|
||||||
|
Vypis(7);
|
||||||
|
Vypis("ahoj");
|
||||||
|
|
||||||
|
static void Vypis<T>(T hodnota)
|
||||||
|
{
|
||||||
|
Console.WriteLine(hodnota);
|
||||||
|
}
|
||||||
|
```
|
||||||
4
content/programovani/4-rocnik/07-delegaty.en.md
Normal file
4
content/programovani/4-rocnik/07-delegaty.en.md
Normal file
|
|
@ -0,0 +1,4 @@
|
||||||
|
---
|
||||||
|
nazev: Delegates
|
||||||
|
popis: A variable that holds a method.
|
||||||
|
---
|
||||||
34
content/programovani/4-rocnik/07-delegaty.md
Normal file
34
content/programovani/4-rocnik/07-delegaty.md
Normal file
|
|
@ -0,0 +1,34 @@
|
||||||
|
---
|
||||||
|
id: cs-4-delegaty
|
||||||
|
nazev: Delegáty
|
||||||
|
popis: Proměnná, do které uložíte metodu.
|
||||||
|
---
|
||||||
|
|
||||||
|
# Výklad
|
||||||
|
|
||||||
|
## Func
|
||||||
|
*funkce*
|
||||||
|
|
||||||
|
- Func<int, int> je metoda, která dostane int a vrátí int.
|
||||||
|
- x => x * x je taková metoda bez jména.
|
||||||
|
- Do proměnné ji uložíte a zavoláte později.
|
||||||
|
|
||||||
|
> Tip: Závorky pocet(4) metodu spustí. Bez nich byste ji jen předali dál.
|
||||||
|
|
||||||
|
## Action
|
||||||
|
*bez výsledku*
|
||||||
|
|
||||||
|
- Action nic nevrací. Hodí se na kroky.
|
||||||
|
- Metodu můžete předat jiné metodě jako argument.
|
||||||
|
- Tak se píše i Where z minulé lekce.
|
||||||
|
|
||||||
|
> Tip: Vyměňte x * x za x + 1.
|
||||||
|
|
||||||
|
# Kód
|
||||||
|
|
||||||
|
```
|
||||||
|
Func<int, int> pocet = x => x * x;
|
||||||
|
Console.WriteLine(pocet(4));
|
||||||
|
Action pozdrav = () => Console.WriteLine("ahoj");
|
||||||
|
pozdrav();
|
||||||
|
```
|
||||||
4
content/programovani/4-rocnik/08-async.en.md
Normal file
4
content/programovani/4-rocnik/08-async.en.md
Normal file
|
|
@ -0,0 +1,4 @@
|
||||||
|
---
|
||||||
|
nazev: Async and await
|
||||||
|
popis: Work that waits without freezing the way you write it.
|
||||||
|
---
|
||||||
33
content/programovani/4-rocnik/08-async.md
Normal file
33
content/programovani/4-rocnik/08-async.md
Normal file
|
|
@ -0,0 +1,33 @@
|
||||||
|
---
|
||||||
|
id: cs-4-async
|
||||||
|
nazev: Async a await
|
||||||
|
popis: Práce, která počká, aniž by zamkla zbytek zápisu.
|
||||||
|
---
|
||||||
|
|
||||||
|
# Výklad
|
||||||
|
|
||||||
|
## await
|
||||||
|
*počkej*
|
||||||
|
|
||||||
|
- await Task.Delay čeká, ale zápis zůstane řádek za řádkem.
|
||||||
|
- Metoda s await má v hlavičce async a vrací Task.
|
||||||
|
- V krátkém programu smí await stát i na nejvyšší úrovni.
|
||||||
|
|
||||||
|
> Tip: Delay(300) čeká 300 milisekund. Výpis hotovo přijde až po něm.
|
||||||
|
|
||||||
|
## Task
|
||||||
|
*slib*
|
||||||
|
|
||||||
|
- Task je slib, že práce doběhne.
|
||||||
|
- await slib rozbalí. Bez await byste měli slib, ne výsledek.
|
||||||
|
- Na síť a soubory se await hodí. Na 2 + 2 ne.
|
||||||
|
|
||||||
|
> Tip: Dejte za první výpis ještě jeden await a druhý řádek.
|
||||||
|
|
||||||
|
# Kód
|
||||||
|
|
||||||
|
```
|
||||||
|
Console.WriteLine("začátek");
|
||||||
|
await Task.Delay(300);
|
||||||
|
Console.WriteLine("hotovo");
|
||||||
|
```
|
||||||
|
|
@ -254,6 +254,26 @@ final class AppModel: ObservableObject {
|
||||||
refresh()
|
refresh()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func markCs(_ id: Int) {
|
||||||
|
progress.markCs(id)
|
||||||
|
refresh()
|
||||||
|
}
|
||||||
|
|
||||||
|
func markCs2(_ id: Int) {
|
||||||
|
progress.markCs2(id)
|
||||||
|
refresh()
|
||||||
|
}
|
||||||
|
|
||||||
|
func markCs3(_ id: Int) {
|
||||||
|
progress.markCs3(id)
|
||||||
|
refresh()
|
||||||
|
}
|
||||||
|
|
||||||
|
func markCs4(_ id: Int) {
|
||||||
|
progress.markCs4(id)
|
||||||
|
refresh()
|
||||||
|
}
|
||||||
|
|
||||||
func markMat0(_ id: Int) {
|
func markMat0(_ id: Int) {
|
||||||
progress.markMat0(id)
|
progress.markMat0(id)
|
||||||
refresh()
|
refresh()
|
||||||
|
|
|
||||||
|
|
@ -63,6 +63,10 @@ final class Content {
|
||||||
let tg2: [J]
|
let tg2: [J]
|
||||||
let tg3: [J]
|
let tg3: [J]
|
||||||
let tg4: [J]
|
let tg4: [J]
|
||||||
|
let cs: [J]
|
||||||
|
let cs2: [J]
|
||||||
|
let cs3: [J]
|
||||||
|
let cs4: [J]
|
||||||
let mat0: [J]
|
let mat0: [J]
|
||||||
let mat: [J]
|
let mat: [J]
|
||||||
let mat2: [J]
|
let mat2: [J]
|
||||||
|
|
@ -133,6 +137,10 @@ final class Content {
|
||||||
tg2 = raw.arr("tg2")
|
tg2 = raw.arr("tg2")
|
||||||
tg3 = raw.arr("tg3")
|
tg3 = raw.arr("tg3")
|
||||||
tg4 = raw.arr("tg4")
|
tg4 = raw.arr("tg4")
|
||||||
|
cs = raw.arr("cs")
|
||||||
|
cs2 = raw.arr("cs2")
|
||||||
|
cs3 = raw.arr("cs3")
|
||||||
|
cs4 = raw.arr("cs4")
|
||||||
mat0 = raw.arr("mat0")
|
mat0 = raw.arr("mat0")
|
||||||
mat = raw.arr("mat")
|
mat = raw.arr("mat")
|
||||||
mat2 = raw.arr("mat2")
|
mat2 = raw.arr("mat2")
|
||||||
|
|
@ -199,6 +207,10 @@ final class Content {
|
||||||
func tg2Lesson(_ id: Int) -> J? { tg2.first { $0.int("id") == id } }
|
func tg2Lesson(_ id: Int) -> J? { tg2.first { $0.int("id") == id } }
|
||||||
func tg3Lesson(_ id: Int) -> J? { tg3.first { $0.int("id") == id } }
|
func tg3Lesson(_ id: Int) -> J? { tg3.first { $0.int("id") == id } }
|
||||||
func tg4Lesson(_ id: Int) -> J? { tg4.first { $0.int("id") == id } }
|
func tg4Lesson(_ id: Int) -> J? { tg4.first { $0.int("id") == id } }
|
||||||
|
func csLesson(_ id: Int) -> J? { cs.first { $0.int("id") == id } }
|
||||||
|
func cs2Lesson(_ id: Int) -> J? { cs2.first { $0.int("id") == id } }
|
||||||
|
func cs3Lesson(_ id: Int) -> J? { cs3.first { $0.int("id") == id } }
|
||||||
|
func cs4Lesson(_ id: Int) -> J? { cs4.first { $0.int("id") == id } }
|
||||||
func mat0Lesson(_ id: Int) -> J? { mat0.first { $0.int("id") == id } }
|
func mat0Lesson(_ id: Int) -> J? { mat0.first { $0.int("id") == id } }
|
||||||
func matLesson(_ id: Int) -> J? { mat.first { $0.int("id") == id } }
|
func matLesson(_ id: Int) -> J? { mat.first { $0.int("id") == id } }
|
||||||
func mat2Lesson(_ id: Int) -> J? { mat2.first { $0.int("id") == id } }
|
func mat2Lesson(_ id: Int) -> J? { mat2.first { $0.int("id") == id } }
|
||||||
|
|
|
||||||
|
|
@ -102,6 +102,14 @@ final class ProgressStore {
|
||||||
func markTg3(_ id: Int) { mark("tg3.\(id)") }
|
func markTg3(_ id: Int) { mark("tg3.\(id)") }
|
||||||
func tg4Done(_ id: Int) -> Bool { done("tg4.\(id)") }
|
func tg4Done(_ id: Int) -> Bool { done("tg4.\(id)") }
|
||||||
func markTg4(_ id: Int) { mark("tg4.\(id)") }
|
func markTg4(_ id: Int) { mark("tg4.\(id)") }
|
||||||
|
func csDone(_ id: Int) -> Bool { done("cs.\(id)") }
|
||||||
|
func markCs(_ id: Int) { mark("cs.\(id)") }
|
||||||
|
func cs2Done(_ id: Int) -> Bool { done("cs2.\(id)") }
|
||||||
|
func markCs2(_ id: Int) { mark("cs2.\(id)") }
|
||||||
|
func cs3Done(_ id: Int) -> Bool { done("cs3.\(id)") }
|
||||||
|
func markCs3(_ id: Int) { mark("cs3.\(id)") }
|
||||||
|
func cs4Done(_ id: Int) -> Bool { done("cs4.\(id)") }
|
||||||
|
func markCs4(_ id: Int) { mark("cs4.\(id)") }
|
||||||
func mat0Done(_ id: Int) -> Bool { done("mat0.\(id)") }
|
func mat0Done(_ id: Int) -> Bool { done("mat0.\(id)") }
|
||||||
func markMat0(_ id: Int) { mark("mat0.\(id)") }
|
func markMat0(_ id: Int) { mark("mat0.\(id)") }
|
||||||
func matDone(_ id: Int) -> Bool { done("mat.\(id)") }
|
func matDone(_ id: Int) -> Bool { done("mat.\(id)") }
|
||||||
|
|
@ -328,6 +336,38 @@ func summarize(_ content: Content, _ p: ProgressStore) -> ProgressSum {
|
||||||
sum.doneUnits += 1
|
sum.doneUnits += 1
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
for l in content.cs {
|
||||||
|
sum.totalEx += 1
|
||||||
|
sum.openUnits += 1
|
||||||
|
if p.csDone(l.int("id")) {
|
||||||
|
sum.doneEx += 1
|
||||||
|
sum.doneUnits += 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for l in content.cs2 {
|
||||||
|
sum.totalEx += 1
|
||||||
|
sum.openUnits += 1
|
||||||
|
if p.cs2Done(l.int("id")) {
|
||||||
|
sum.doneEx += 1
|
||||||
|
sum.doneUnits += 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for l in content.cs3 {
|
||||||
|
sum.totalEx += 1
|
||||||
|
sum.openUnits += 1
|
||||||
|
if p.cs3Done(l.int("id")) {
|
||||||
|
sum.doneEx += 1
|
||||||
|
sum.doneUnits += 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for l in content.cs4 {
|
||||||
|
sum.totalEx += 1
|
||||||
|
sum.openUnits += 1
|
||||||
|
if p.cs4Done(l.int("id")) {
|
||||||
|
sum.doneEx += 1
|
||||||
|
sum.doneUnits += 1
|
||||||
|
}
|
||||||
|
}
|
||||||
for l in content.mat0 {
|
for l in content.mat0 {
|
||||||
sum.totalEx += 1
|
sum.totalEx += 1
|
||||||
sum.openUnits += 1
|
sum.openUnits += 1
|
||||||
|
|
|
||||||
|
|
@ -124,6 +124,15 @@ enum Route: Hashable {
|
||||||
case tg4Map
|
case tg4Map
|
||||||
case tg4Lesson(Int)
|
case tg4Lesson(Int)
|
||||||
case tg4Ex(Int)
|
case tg4Ex(Int)
|
||||||
|
case csYears
|
||||||
|
case csMap
|
||||||
|
case csLesson(Int)
|
||||||
|
case cs2Map
|
||||||
|
case cs2Lesson(Int)
|
||||||
|
case cs3Map
|
||||||
|
case cs3Lesson(Int)
|
||||||
|
case cs4Map
|
||||||
|
case cs4Lesson(Int)
|
||||||
case czechMap
|
case czechMap
|
||||||
case mluvnice
|
case mluvnice
|
||||||
case mluvEx(Int)
|
case mluvEx(Int)
|
||||||
|
|
@ -250,6 +259,15 @@ enum Route: Hashable {
|
||||||
case .tg4Map: return "tg4map"
|
case .tg4Map: return "tg4map"
|
||||||
case .tg4Lesson(let id): return "tg4unit\(id)"
|
case .tg4Lesson(let id): return "tg4unit\(id)"
|
||||||
case .tg4Ex(let id): return "tg4ex\(id)"
|
case .tg4Ex(let id): return "tg4ex\(id)"
|
||||||
|
case .csYears: return "csyears"
|
||||||
|
case .csMap: return "csmap"
|
||||||
|
case .csLesson(let id): return "csunit\(id)"
|
||||||
|
case .cs2Map: return "cs2map"
|
||||||
|
case .cs2Lesson(let id): return "cs2unit\(id)"
|
||||||
|
case .cs3Map: return "cs3map"
|
||||||
|
case .cs3Lesson(let id): return "cs3unit\(id)"
|
||||||
|
case .cs4Map: return "cs4map"
|
||||||
|
case .cs4Lesson(let id): return "cs4unit\(id)"
|
||||||
case .czechMap: return "czechmap"
|
case .czechMap: return "czechmap"
|
||||||
case .mluvnice: return "mluvnice"
|
case .mluvnice: return "mluvnice"
|
||||||
case .mluvEx(let n): return "mluve\(n)"
|
case .mluvEx(let n): return "mluve\(n)"
|
||||||
|
|
@ -500,6 +518,24 @@ enum Route: Hashable {
|
||||||
return [.tgYears, .tg4Map, .tg4Lesson(id)]
|
return [.tgYears, .tg4Map, .tg4Lesson(id)]
|
||||||
case .tg4Ex(let id):
|
case .tg4Ex(let id):
|
||||||
return [.tgYears, .tg4Map, .tg4Lesson(id), .tg4Ex(id)]
|
return [.tgYears, .tg4Map, .tg4Lesson(id), .tg4Ex(id)]
|
||||||
|
case .csYears:
|
||||||
|
return [.csYears]
|
||||||
|
case .csMap:
|
||||||
|
return [.csYears, .csMap]
|
||||||
|
case .csLesson(let id):
|
||||||
|
return [.csYears, .csMap, .csLesson(id)]
|
||||||
|
case .cs2Map:
|
||||||
|
return [.csYears, .cs2Map]
|
||||||
|
case .cs2Lesson(let id):
|
||||||
|
return [.csYears, .cs2Map, .cs2Lesson(id)]
|
||||||
|
case .cs3Map:
|
||||||
|
return [.csYears, .cs3Map]
|
||||||
|
case .cs3Lesson(let id):
|
||||||
|
return [.csYears, .cs3Map, .cs3Lesson(id)]
|
||||||
|
case .cs4Map:
|
||||||
|
return [.csYears, .cs4Map]
|
||||||
|
case .cs4Lesson(let id):
|
||||||
|
return [.csYears, .cs4Map, .cs4Lesson(id)]
|
||||||
case .czechMap:
|
case .czechMap:
|
||||||
return [.czechMap]
|
return [.czechMap]
|
||||||
case .mluvnice:
|
case .mluvnice:
|
||||||
|
|
@ -565,6 +601,11 @@ func routeFromPage(_ page: String) -> Route? {
|
||||||
case "tg2map": return .tg2Map
|
case "tg2map": return .tg2Map
|
||||||
case "tg3map": return .tg3Map
|
case "tg3map": return .tg3Map
|
||||||
case "tg4map": return .tg4Map
|
case "tg4map": return .tg4Map
|
||||||
|
case "csyears": return .csYears
|
||||||
|
case "csmap": return .csMap
|
||||||
|
case "cs2map": return .cs2Map
|
||||||
|
case "cs3map": return .cs3Map
|
||||||
|
case "cs4map": return .cs4Map
|
||||||
case "czechmap": return .czechMap
|
case "czechmap": return .czechMap
|
||||||
case "mluvnice": return .mluvnice
|
case "mluvnice": return .mluvnice
|
||||||
case "readinglist": return .readingList
|
case "readinglist": return .readingList
|
||||||
|
|
@ -698,6 +739,18 @@ func routeFromPage(_ page: String) -> Route? {
|
||||||
if let m = page.wholeMatch(of: /tgex(\d+)/), let n = Int(m.1) {
|
if let m = page.wholeMatch(of: /tgex(\d+)/), let n = Int(m.1) {
|
||||||
return .tgEx(n)
|
return .tgEx(n)
|
||||||
}
|
}
|
||||||
|
if let m = page.wholeMatch(of: /cs4unit(\d+)/), let n = Int(m.1) {
|
||||||
|
return .cs4Lesson(n)
|
||||||
|
}
|
||||||
|
if let m = page.wholeMatch(of: /cs3unit(\d+)/), let n = Int(m.1) {
|
||||||
|
return .cs3Lesson(n)
|
||||||
|
}
|
||||||
|
if let m = page.wholeMatch(of: /cs2unit(\d+)/), let n = Int(m.1) {
|
||||||
|
return .cs2Lesson(n)
|
||||||
|
}
|
||||||
|
if let m = page.wholeMatch(of: /csunit(\d+)/), let n = Int(m.1) {
|
||||||
|
return .csLesson(n)
|
||||||
|
}
|
||||||
if let m = page.wholeMatch(of: /lit4unit(\d+)/), let n = Int(m.1) {
|
if let m = page.wholeMatch(of: /lit4unit(\d+)/), let n = Int(m.1) {
|
||||||
return .lit4Lesson(n)
|
return .lit4Lesson(n)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -680,6 +680,7 @@ func subjectSymbol(_ icon: String) -> String {
|
||||||
case "bolt": return "bolt.fill"
|
case "bolt": return "bolt.fill"
|
||||||
case "function": return "function"
|
case "function": return "function"
|
||||||
case "ruler": return "ruler.fill"
|
case "ruler": return "ruler.fill"
|
||||||
|
case "code": return "chevron.left.forwardslash.chevron.right"
|
||||||
default: return "lock.fill"
|
default: return "lock.fill"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -713,6 +713,12 @@ private func subjectSum(_ vm: AppModel, _ s: J) -> ProgressSum {
|
||||||
+ vm.content.mat2.filter { vm.progress.mat2Done($0.int("id")) }.count
|
+ vm.content.mat2.filter { vm.progress.mat2Done($0.int("id")) }.count
|
||||||
+ vm.content.mat3.filter { vm.progress.mat3Done($0.int("id")) }.count
|
+ vm.content.mat3.filter { vm.progress.mat3Done($0.int("id")) }.count
|
||||||
+ vm.content.mat4.filter { vm.progress.mat4Done($0.int("id")) }.count
|
+ vm.content.mat4.filter { vm.progress.mat4Done($0.int("id")) }.count
|
||||||
|
case "csyears", "csmap", "cs2map", "cs3map", "cs4map":
|
||||||
|
sum.totalEx = vm.content.cs.count + vm.content.cs2.count + vm.content.cs3.count + vm.content.cs4.count
|
||||||
|
sum.doneEx = vm.content.cs.filter { vm.progress.csDone($0.int("id")) }.count
|
||||||
|
+ vm.content.cs2.filter { vm.progress.cs2Done($0.int("id")) }.count
|
||||||
|
+ vm.content.cs3.filter { vm.progress.cs3Done($0.int("id")) }.count
|
||||||
|
+ vm.content.cs4.filter { vm.progress.cs4Done($0.int("id")) }.count
|
||||||
case "tgyears", "tgmap", "tg2map", "tg3map", "tg4map":
|
case "tgyears", "tgmap", "tg2map", "tg3map", "tg4map":
|
||||||
sum.totalEx = vm.content.tg.count + vm.content.tg2.count + vm.content.tg3.count + vm.content.tg4.count
|
sum.totalEx = vm.content.tg.count + vm.content.tg2.count + vm.content.tg3.count + vm.content.tg4.count
|
||||||
sum.doneEx = vm.content.tg.filter { vm.progress.tgDone($0.int("id")) }.count
|
sum.doneEx = vm.content.tg.filter { vm.progress.tgDone($0.int("id")) }.count
|
||||||
|
|
@ -1042,6 +1048,23 @@ private func buildSearch(_ vm: AppModel) -> [Hit] {
|
||||||
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_lesson"), "tg4unit\(l.int("id"))", extra: "autocad")
|
||||||
add(vm.tr(l.str("titleKey")), vm.tr("search_exercise"), "tg4ex\(l.int("id"))")
|
add(vm.tr(l.str("titleKey")), vm.tr("search_exercise"), "tg4ex\(l.int("id"))")
|
||||||
}
|
}
|
||||||
|
add(vm.tr("Programování"), vm.tr("search_lesson"), "csyears", extra: "csharp c#")
|
||||||
|
add(vm.tr("cs_year1"), vm.tr("search_lesson"), "csmap", extra: "rocnik")
|
||||||
|
add(vm.tr("cs_year2"), vm.tr("search_lesson"), "cs2map", extra: "rocnik")
|
||||||
|
add(vm.tr("cs_year3"), vm.tr("search_lesson"), "cs3map", extra: "rocnik")
|
||||||
|
add(vm.tr("cs_year4"), vm.tr("search_lesson"), "cs4map", extra: "rocnik")
|
||||||
|
for l in vm.content.cs {
|
||||||
|
add(vm.tr(l.str("titleKey")), vm.tr("search_lesson"), "csunit\(l.int("id"))", extra: "csharp")
|
||||||
|
}
|
||||||
|
for l in vm.content.cs2 {
|
||||||
|
add(vm.tr(l.str("titleKey")), vm.tr("search_lesson"), "cs2unit\(l.int("id"))", extra: "csharp")
|
||||||
|
}
|
||||||
|
for l in vm.content.cs3 {
|
||||||
|
add(vm.tr(l.str("titleKey")), vm.tr("search_lesson"), "cs3unit\(l.int("id"))", extra: "csharp")
|
||||||
|
}
|
||||||
|
for l in vm.content.cs4 {
|
||||||
|
add(vm.tr(l.str("titleKey")), vm.tr("search_lesson"), "cs4unit\(l.int("id"))", extra: "csharp")
|
||||||
|
}
|
||||||
add(vm.tr("Chemie"), vm.tr("search_lesson"), "chemmap", extra: "chemie")
|
add(vm.tr("Chemie"), vm.tr("search_lesson"), "chemmap", extra: "chemie")
|
||||||
add(vm.tr("Biologie"), vm.tr("search_lesson"), "biomap", extra: "biologie")
|
add(vm.tr("Biologie"), vm.tr("search_lesson"), "biomap", extra: "biologie")
|
||||||
for l in vm.content.chem {
|
for l in vm.content.chem {
|
||||||
|
|
@ -1776,6 +1799,47 @@ struct MatYearsScreen: View {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct CsYearsScreen: View {
|
||||||
|
@ObservedObject var vm: AppModel
|
||||||
|
|
||||||
|
var body: some View {
|
||||||
|
#if os(macOS)
|
||||||
|
let years: [(String, String, Route)] = [
|
||||||
|
("cs_year1", "cs_sub", .csMap),
|
||||||
|
("cs_year2", "cs2_sub", .cs2Map),
|
||||||
|
("cs_year3", "cs3_sub", .cs3Map),
|
||||||
|
("cs_year4", "cs4_sub", .cs4Map),
|
||||||
|
]
|
||||||
|
MacFillGrid(count: years.count, minWidth: 260, minHeight: 180) { i in
|
||||||
|
NavigationLink(value: years[i].2) {
|
||||||
|
MacLinkCard(title: vm.tr(years[i].0), subtitle: vm.tr(years[i].1), badge: "\(i + 1).")
|
||||||
|
}
|
||||||
|
.buttonStyle(PressScaleStyle())
|
||||||
|
}
|
||||||
|
.navigationTitle(vm.tr("Programování"))
|
||||||
|
.navigationSubtitle(vm.tr("cs_years_sub"))
|
||||||
|
#else
|
||||||
|
List {
|
||||||
|
NavigationLink(value: Route.csMap) {
|
||||||
|
ListRowLabel(title: vm.tr("cs_year1"), subtitle: vm.tr("cs_sub"))
|
||||||
|
}
|
||||||
|
NavigationLink(value: Route.cs2Map) {
|
||||||
|
ListRowLabel(title: vm.tr("cs_year2"), subtitle: vm.tr("cs2_sub"))
|
||||||
|
}
|
||||||
|
NavigationLink(value: Route.cs3Map) {
|
||||||
|
ListRowLabel(title: vm.tr("cs_year3"), subtitle: vm.tr("cs3_sub"))
|
||||||
|
}
|
||||||
|
NavigationLink(value: Route.cs4Map) {
|
||||||
|
ListRowLabel(title: vm.tr("cs_year4"), subtitle: vm.tr("cs4_sub"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.navigationTitle(vm.tr("Programování"))
|
||||||
|
.navigationSubtitle(vm.tr("cs_years_sub"))
|
||||||
|
.appListStyle()
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
struct TgYearsScreen: View {
|
struct TgYearsScreen: View {
|
||||||
@ObservedObject var vm: AppModel
|
@ObservedObject var vm: AppModel
|
||||||
|
|
||||||
|
|
@ -2876,6 +2940,64 @@ struct RouteDestination: View {
|
||||||
}
|
}
|
||||||
case .fyz4Ex(let id):
|
case .fyz4Ex(let id):
|
||||||
FyzQuizScreen(vm: vm, year: 4, id: id)
|
FyzQuizScreen(vm: vm, year: 4, id: id)
|
||||||
|
case .csYears:
|
||||||
|
CsYearsScreen(vm: vm)
|
||||||
|
case .csMap:
|
||||||
|
LessonListScreen(
|
||||||
|
vm: vm,
|
||||||
|
title: vm.tr("cs_year1"),
|
||||||
|
subtitle: vm.tr("cs_sub"),
|
||||||
|
rows: vm.content.cs.map { l in
|
||||||
|
let id = l.int("id")
|
||||||
|
return LessonRow(id: "c\(id)", title: vm.tr(l.str("titleKey")), done: vm.progress.csDone(id), destination: .csLesson(id))
|
||||||
|
}
|
||||||
|
)
|
||||||
|
case .csLesson(let id):
|
||||||
|
if let l = vm.content.csLesson(id) {
|
||||||
|
CodeLessonScreen(vm: vm, lesson: l, onSuccess: { vm.markCs(id) })
|
||||||
|
}
|
||||||
|
case .cs2Map:
|
||||||
|
LessonListScreen(
|
||||||
|
vm: vm,
|
||||||
|
title: vm.tr("cs_year2"),
|
||||||
|
subtitle: vm.tr("cs2_sub"),
|
||||||
|
rows: vm.content.cs2.map { l in
|
||||||
|
let id = l.int("id")
|
||||||
|
return LessonRow(id: "c2\(id)", title: vm.tr(l.str("titleKey")), done: vm.progress.cs2Done(id), destination: .cs2Lesson(id))
|
||||||
|
}
|
||||||
|
)
|
||||||
|
case .cs2Lesson(let id):
|
||||||
|
if let l = vm.content.cs2Lesson(id) {
|
||||||
|
CodeLessonScreen(vm: vm, lesson: l, onSuccess: { vm.markCs2(id) })
|
||||||
|
}
|
||||||
|
case .cs3Map:
|
||||||
|
LessonListScreen(
|
||||||
|
vm: vm,
|
||||||
|
title: vm.tr("cs_year3"),
|
||||||
|
subtitle: vm.tr("cs3_sub"),
|
||||||
|
rows: vm.content.cs3.map { l in
|
||||||
|
let id = l.int("id")
|
||||||
|
return LessonRow(id: "c3\(id)", title: vm.tr(l.str("titleKey")), done: vm.progress.cs3Done(id), destination: .cs3Lesson(id))
|
||||||
|
}
|
||||||
|
)
|
||||||
|
case .cs3Lesson(let id):
|
||||||
|
if let l = vm.content.cs3Lesson(id) {
|
||||||
|
CodeLessonScreen(vm: vm, lesson: l, onSuccess: { vm.markCs3(id) })
|
||||||
|
}
|
||||||
|
case .cs4Map:
|
||||||
|
LessonListScreen(
|
||||||
|
vm: vm,
|
||||||
|
title: vm.tr("cs_year4"),
|
||||||
|
subtitle: vm.tr("cs4_sub"),
|
||||||
|
rows: vm.content.cs4.map { l in
|
||||||
|
let id = l.int("id")
|
||||||
|
return LessonRow(id: "c4\(id)", title: vm.tr(l.str("titleKey")), done: vm.progress.cs4Done(id), destination: .cs4Lesson(id))
|
||||||
|
}
|
||||||
|
)
|
||||||
|
case .cs4Lesson(let id):
|
||||||
|
if let l = vm.content.cs4Lesson(id) {
|
||||||
|
CodeLessonScreen(vm: vm, lesson: l, onSuccess: { vm.markCs4(id) })
|
||||||
|
}
|
||||||
case .tgYears:
|
case .tgYears:
|
||||||
TgYearsScreen(vm: vm)
|
TgYearsScreen(vm: vm)
|
||||||
case .tgMap:
|
case .tgMap:
|
||||||
|
|
@ -2970,3 +3092,251 @@ struct RouteDestination: View {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct CodeLessonScreen: View {
|
||||||
|
@ObservedObject var vm: AppModel
|
||||||
|
let lesson: J
|
||||||
|
let onSuccess: () -> Void
|
||||||
|
@State private var code = ""
|
||||||
|
@State private var stdin = ""
|
||||||
|
@State private var output = ""
|
||||||
|
@State private var running = false
|
||||||
|
|
||||||
|
var body: some View {
|
||||||
|
GeometryReader { geo in
|
||||||
|
if geo.size.width >= 840 {
|
||||||
|
HStack(alignment: .top, spacing: 16) {
|
||||||
|
explanation.frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .topLeading)
|
||||||
|
editor.frame(maxWidth: .infinity, maxHeight: .infinity)
|
||||||
|
outputPane.frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .topLeading)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
ScrollView {
|
||||||
|
VStack(alignment: .leading, spacing: 16) {
|
||||||
|
explanation
|
||||||
|
editor.frame(minHeight: 240)
|
||||||
|
outputPane.frame(minHeight: 160)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.padding(16)
|
||||||
|
.detailScreen(vm.tr(lesson.str("titleKey")), subtitle: vm.tr(lesson.str("subKey")))
|
||||||
|
.onAppear {
|
||||||
|
if code.isEmpty { code = lesson.str("code") }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private var explanation: some View {
|
||||||
|
ScrollView {
|
||||||
|
VStack(alignment: .leading, spacing: 16) {
|
||||||
|
ForEach(Array(lesson.arr("slides").enumerated()), id: \.offset) { _, slide in
|
||||||
|
VStack(alignment: .leading, spacing: 8) {
|
||||||
|
Text(slide.str("kicker"))
|
||||||
|
.font(.subheadline.weight(.semibold))
|
||||||
|
.foregroundStyle(Color.accentColor)
|
||||||
|
Text(slide.str("title"))
|
||||||
|
.font(.title3.bold())
|
||||||
|
ForEach(slide.strs("lines"), id: \.self) { line in
|
||||||
|
Text("• \(line)")
|
||||||
|
.frame(maxWidth: .infinity, alignment: .leading)
|
||||||
|
}
|
||||||
|
if let tip = slide.strOrNull("tip") {
|
||||||
|
Text(tip)
|
||||||
|
.foregroundStyle(.secondary)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private var editor: some View {
|
||||||
|
VStack(alignment: .leading, spacing: 8) {
|
||||||
|
Text(vm.tr("cs_code")).font(.headline)
|
||||||
|
TextEditor(text: $code)
|
||||||
|
.font(.system(.body, design: .monospaced))
|
||||||
|
.scrollContentBackground(.hidden)
|
||||||
|
.padding(8)
|
||||||
|
.frame(maxWidth: .infinity, maxHeight: .infinity)
|
||||||
|
.background(codeFill, in: RoundedRectangle(cornerRadius: 12))
|
||||||
|
Text(vm.tr("cs_stdin"))
|
||||||
|
.font(.caption)
|
||||||
|
.foregroundStyle(.secondary)
|
||||||
|
TextField("", text: $stdin, axis: .vertical)
|
||||||
|
.font(.system(.body, design: .monospaced))
|
||||||
|
.lineLimit(2...4)
|
||||||
|
.textFieldStyle(.roundedBorder)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private var outputPane: some View {
|
||||||
|
VStack(alignment: .leading, spacing: 8) {
|
||||||
|
HStack {
|
||||||
|
Button(vm.tr("cs_run")) { run() }
|
||||||
|
.buttonStyle(.borderedProminent)
|
||||||
|
.disabled(running)
|
||||||
|
Button(vm.tr("cs_reset")) { code = lesson.str("code") }
|
||||||
|
.disabled(running)
|
||||||
|
}
|
||||||
|
Text(vm.tr("cs_output")).font(.headline)
|
||||||
|
ScrollView {
|
||||||
|
Text(output.isEmpty ? vm.tr("cs_empty") : output)
|
||||||
|
.font(.system(.body, design: .monospaced))
|
||||||
|
.frame(maxWidth: .infinity, alignment: .topLeading)
|
||||||
|
.textSelection(.enabled)
|
||||||
|
}
|
||||||
|
.padding(12)
|
||||||
|
.frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .topLeading)
|
||||||
|
.background(codeFill, in: RoundedRectangle(cornerRadius: 12))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private var codeFill: Color {
|
||||||
|
#if os(macOS)
|
||||||
|
Color(.textBackgroundColor).opacity(0.55)
|
||||||
|
#else
|
||||||
|
Color(.secondarySystemBackground)
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
private func run() {
|
||||||
|
running = true
|
||||||
|
output = vm.tr("cs_running")
|
||||||
|
CSharpRunner.run(code, stdin: stdin) { status, text in
|
||||||
|
running = false
|
||||||
|
switch status {
|
||||||
|
case "unavailable":
|
||||||
|
output = vm.tr("cs_unavailable")
|
||||||
|
case "timeout":
|
||||||
|
output = (text + "\n\n" + vm.tr("cs_timeout")).trimmingCharacters(in: .whitespacesAndNewlines)
|
||||||
|
default:
|
||||||
|
output = text.isEmpty ? vm.tr("cs_empty") : text
|
||||||
|
}
|
||||||
|
if status == "ok" { onSuccess() }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
enum CSharpRunner {
|
||||||
|
static func run(_ source: String, stdin: String, done: @escaping (String, String) -> Void) {
|
||||||
|
#if os(macOS)
|
||||||
|
DispatchQueue.global(qos: .userInitiated).async {
|
||||||
|
let result = runMac(source, stdin: stdin)
|
||||||
|
DispatchQueue.main.async { done(result.0, result.1) }
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
done("unavailable", "")
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
#if os(macOS)
|
||||||
|
private static func runMac(_ source: String, stdin: String) -> (String, String) {
|
||||||
|
guard source.count <= 100_000 else { return ("fail", "Program je příliš dlouhý.") }
|
||||||
|
guard let dotnet = findDotnet() else { return ("unavailable", "") }
|
||||||
|
let dir = URL(fileURLWithPath: NSHomeDirectory()).appendingPathComponent(".graduately/csharp-lab")
|
||||||
|
let csproj = dir.appendingPathComponent("Lab.csproj")
|
||||||
|
if !FileManager.default.fileExists(atPath: csproj.path) {
|
||||||
|
try? FileManager.default.createDirectory(at: dir, withIntermediateDirectories: true)
|
||||||
|
let framework = targetFramework(dotnet)
|
||||||
|
let xml = """
|
||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
<PropertyGroup>
|
||||||
|
<OutputType>Exe</OutputType>
|
||||||
|
<TargetFramework>\(framework)</TargetFramework>
|
||||||
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
|
<Nullable>disable</Nullable>
|
||||||
|
<LangVersion>latest</LangVersion>
|
||||||
|
</PropertyGroup>
|
||||||
|
</Project>
|
||||||
|
"""
|
||||||
|
try? xml.write(to: csproj, atomically: true, encoding: .utf8)
|
||||||
|
let restored = command(dotnet, ["restore", dir.path], dir, "", 90)
|
||||||
|
if restored.0 != "ok" { return restored }
|
||||||
|
}
|
||||||
|
do {
|
||||||
|
try source.write(to: dir.appendingPathComponent("Program.cs"), atomically: true, encoding: .utf8)
|
||||||
|
} catch {
|
||||||
|
return ("fail", error.localizedDescription)
|
||||||
|
}
|
||||||
|
var args = ["run", "--project", dir.path, "--verbosity", "quiet"]
|
||||||
|
if FileManager.default.fileExists(atPath: dir.appendingPathComponent("obj").path) {
|
||||||
|
args.append("--no-restore")
|
||||||
|
}
|
||||||
|
return command(dotnet, args, dir, stdin, 20)
|
||||||
|
}
|
||||||
|
|
||||||
|
private static func command(_ dotnet: String, _ args: [String], _ dir: URL, _ stdin: String, _ seconds: Double) -> (String, String) {
|
||||||
|
let proc = Process()
|
||||||
|
proc.executableURL = URL(fileURLWithPath: dotnet)
|
||||||
|
proc.arguments = args
|
||||||
|
proc.currentDirectoryURL = dir
|
||||||
|
var env = ProcessInfo.processInfo.environment
|
||||||
|
let root = (dotnet as NSString).deletingLastPathComponent
|
||||||
|
env["PATH"] = [root, "/opt/homebrew/bin", "/usr/local/bin", "/usr/bin", "/bin"].joined(separator: ":")
|
||||||
|
env["DOTNET_ROOT"] = root
|
||||||
|
env["DOTNET_CLI_TELEMETRY_OPTOUT"] = "1"
|
||||||
|
env["DOTNET_NOLOGO"] = "1"
|
||||||
|
env["DOTNET_SKIP_FIRST_TIME_EXPERIENCE"] = "1"
|
||||||
|
proc.environment = env
|
||||||
|
let out = Pipe()
|
||||||
|
let input = Pipe()
|
||||||
|
proc.standardOutput = out
|
||||||
|
proc.standardError = out
|
||||||
|
proc.standardInput = input
|
||||||
|
var collected = Data()
|
||||||
|
let lock = NSLock()
|
||||||
|
out.fileHandleForReading.readabilityHandler = { handle in
|
||||||
|
let chunk = handle.availableData
|
||||||
|
guard !chunk.isEmpty else { return }
|
||||||
|
lock.lock()
|
||||||
|
collected.append(chunk)
|
||||||
|
lock.unlock()
|
||||||
|
}
|
||||||
|
do { try proc.run() } catch { return ("fail", error.localizedDescription) }
|
||||||
|
if let data = stdin.data(using: .utf8) { input.fileHandleForWriting.write(data) }
|
||||||
|
try? input.fileHandleForWriting.close()
|
||||||
|
let group = DispatchGroup()
|
||||||
|
group.enter()
|
||||||
|
proc.terminationHandler = { _ in group.leave() }
|
||||||
|
if group.wait(timeout: .now() + seconds) == .timedOut {
|
||||||
|
proc.terminate()
|
||||||
|
kill(proc.processIdentifier, SIGKILL)
|
||||||
|
out.fileHandleForReading.readabilityHandler = nil
|
||||||
|
return ("timeout", clip(collected, lock))
|
||||||
|
}
|
||||||
|
out.fileHandleForReading.readabilityHandler = nil
|
||||||
|
let rest = out.fileHandleForReading.readDataToEndOfFile()
|
||||||
|
lock.lock()
|
||||||
|
collected.append(rest)
|
||||||
|
lock.unlock()
|
||||||
|
let text = clip(collected, lock)
|
||||||
|
return proc.terminationStatus == 0 ? ("ok", text) : ("fail", text)
|
||||||
|
}
|
||||||
|
|
||||||
|
private static func clip(_ data: Data, _ lock: NSLock) -> String {
|
||||||
|
lock.lock()
|
||||||
|
let copy = data
|
||||||
|
lock.unlock()
|
||||||
|
let text = String(data: copy, encoding: .utf8) ?? ""
|
||||||
|
let trimmed = text.trimmingCharacters(in: .whitespacesAndNewlines)
|
||||||
|
return trimmed.count <= 16_000 ? trimmed : String(trimmed.prefix(16_000)) + "\n…"
|
||||||
|
}
|
||||||
|
|
||||||
|
private static func targetFramework(_ dotnet: String) -> String {
|
||||||
|
let version = command(dotnet, ["--version"], URL(fileURLWithPath: NSTemporaryDirectory()), "", 20)
|
||||||
|
let major = version.1.split(separator: ".").first.map(String.init) ?? ""
|
||||||
|
return major.allSatisfy(\.isNumber) && !major.isEmpty ? "net\(major).0" : "net8.0"
|
||||||
|
}
|
||||||
|
|
||||||
|
private static func findDotnet() -> String? {
|
||||||
|
let names = [
|
||||||
|
"/usr/local/share/dotnet/dotnet",
|
||||||
|
"/opt/homebrew/share/dotnet/dotnet",
|
||||||
|
"/usr/local/bin/dotnet",
|
||||||
|
NSHomeDirectory() + "/.dotnet/dotnet",
|
||||||
|
]
|
||||||
|
return names.first { FileManager.default.isExecutableFile(atPath: $0) }
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -116,9 +116,20 @@ def parse_doc(path: Path) -> Doc:
|
||||||
para: Node | None = None
|
para: Node | None = None
|
||||||
table: Node | None = None
|
table: Node | None = None
|
||||||
in_comment = False
|
in_comment = False
|
||||||
for n in range(i, len(lines)):
|
indexes = iter(range(i, len(lines)))
|
||||||
|
for n in indexes:
|
||||||
ln = lines[n].rstrip()
|
ln = lines[n].rstrip()
|
||||||
no = n + 1
|
no = n + 1
|
||||||
|
if ln.startswith("```"):
|
||||||
|
para = table = None
|
||||||
|
body: list[str] = []
|
||||||
|
for n2 in indexes:
|
||||||
|
raw_line = lines[n2]
|
||||||
|
if raw_line.rstrip().startswith("```"):
|
||||||
|
break
|
||||||
|
body.append(raw_line)
|
||||||
|
nodes.append(Node("code", "\n".join(body).strip("\n"), no))
|
||||||
|
continue
|
||||||
if in_comment:
|
if in_comment:
|
||||||
if "-->" in ln:
|
if "-->" in ln:
|
||||||
in_comment = False
|
in_comment = False
|
||||||
|
|
@ -539,6 +550,36 @@ def lesson_files(directory: Path) -> list[Path]:
|
||||||
return files
|
return files
|
||||||
|
|
||||||
|
|
||||||
|
def code_of(p: Part | None) -> str:
|
||||||
|
if p is None:
|
||||||
|
return ""
|
||||||
|
for nd in p.nodes:
|
||||||
|
if nd.kind == "code":
|
||||||
|
return nd.text
|
||||||
|
return ""
|
||||||
|
|
||||||
|
|
||||||
|
def code_lessons(directory: Path, prefix: str) -> list[dict]:
|
||||||
|
"""Lessons with an explanation and a C# program the student can edit and run."""
|
||||||
|
out = []
|
||||||
|
for i, path in enumerate(lesson_files(directory), start=1):
|
||||||
|
doc, en = parse_doc(path), en_doc(path)
|
||||||
|
load_translations(en)
|
||||||
|
lid = lesson_id(doc, f"{prefix}.{i}")
|
||||||
|
code = code_of(part(doc, "Kód", "Code"))
|
||||||
|
if not code.strip():
|
||||||
|
fail(path, None, "lekce nemá ukázkový kód v sekci # Kód")
|
||||||
|
out.append({
|
||||||
|
"id": i,
|
||||||
|
"key": lid,
|
||||||
|
"titleKey": keyed(f"{prefix}_unit{i}", doc, en, "nazev"),
|
||||||
|
"subKey": keyed(f"{prefix}_unit{i}_sub", doc, en, "popis"),
|
||||||
|
"slides": read_slides(doc, part(doc, "Výklad")),
|
||||||
|
"code": code,
|
||||||
|
})
|
||||||
|
return out
|
||||||
|
|
||||||
|
|
||||||
def std_lessons(directory: Path, prefix: str) -> list[dict]:
|
def std_lessons(directory: Path, prefix: str) -> list[dict]:
|
||||||
"""Lessons with slides and a choice quiz (IT, civics, literature, sciences, physics)."""
|
"""Lessons with slides and a choice quiz (IT, civics, literature, sciences, physics)."""
|
||||||
out = []
|
out = []
|
||||||
|
|
@ -1068,6 +1109,10 @@ STD = [
|
||||||
("technicka-grafika/1-rocnik", "tg"), ("technicka-grafika/2-rocnik", "tg2"),
|
("technicka-grafika/1-rocnik", "tg"), ("technicka-grafika/2-rocnik", "tg2"),
|
||||||
("technicka-grafika/3-rocnik", "tg3"), ("technicka-grafika/4-rocnik", "tg4"),
|
("technicka-grafika/3-rocnik", "tg3"), ("technicka-grafika/4-rocnik", "tg4"),
|
||||||
]
|
]
|
||||||
|
CODE = [
|
||||||
|
("programovani/1-rocnik", "cs"), ("programovani/2-rocnik", "cs2"),
|
||||||
|
("programovani/3-rocnik", "cs3"), ("programovani/4-rocnik", "cs4"),
|
||||||
|
]
|
||||||
MATH = [
|
MATH = [
|
||||||
("matematika/0-opakovani", "mat0"), ("matematika/1-rocnik", "mat"),
|
("matematika/0-opakovani", "mat0"), ("matematika/1-rocnik", "mat"),
|
||||||
("matematika/2-rocnik", "mat2"), ("matematika/3-rocnik", "mat3"),
|
("matematika/2-rocnik", "mat2"), ("matematika/3-rocnik", "mat3"),
|
||||||
|
|
@ -1091,6 +1136,8 @@ def build(content: Path = CONTENT) -> dict:
|
||||||
payload[key] = std_lessons(content / d, key)
|
payload[key] = std_lessons(content / d, key)
|
||||||
for d, key in MATH:
|
for d, key in MATH:
|
||||||
payload[key] = math_lessons(content / d, key)
|
payload[key] = math_lessons(content / d, key)
|
||||||
|
for d, key in CODE:
|
||||||
|
payload[key] = code_lessons(content / d, key)
|
||||||
for d, tag in SKILLS:
|
for d, tag in SKILLS:
|
||||||
for year in range(1, 5):
|
for year in range(1, 5):
|
||||||
payload[tag if year == 1 else f"{tag}{year}"] = skill_lessons(
|
payload[tag if year == 1 else f"{tag}{year}"] = skill_lessons(
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue