feat: add qualification thesis example

This commit is contained in:
Kristofers Solo 2025-08-22 21:29:11 +03:00
parent e26f01c1d5
commit 0986808ab3
Signed by: kristoferssolo
GPG Key ID: 8687F2D3EEE6F0ED
8 changed files with 531 additions and 88 deletions

View File

@ -0,0 +1,26 @@
typst:
type: Web
title: Typst
author:
- Mädje
- Laurenz
- Haug
- Martin
- Typst Projekta Izstrādātāji
url: {value: "https://typst.app/", date: 2025-01-01}
lvs_68:
type: Book
title: Programmatūras prasību specifikācijas ceļvedis
author: Institūcija SIA "Latvijas standarts"
issue: 68
date: 1996-03-27
organization: Latvijas Nacionālais standartizācijas un metroloģijas centrs
page-total: 22
lvs_72:
type: Book
title: Ieteicamā prakse programmatūras projektējuma aprakstīšanai
author: Institūcija SIA "Latvijas standarts"
issue: 72
date: 1996-03-27
organization: Latvijas Nacionālais standartizācijas un metroloģijas centrs
page-total: 13

View File

@ -0,0 +1,286 @@
#import "@preview/fletcher:0.5.8" as fletcher: diagram, edge, node
#import fletcher.shapes: cylinder, ellipse
#import "@preview/solo-lu-df:0.0.1": ludf
#import "utils/tables.typ": function-table
#import "utils/diagrams.typ": data-store, dpd-database, dpd-edge, process
#show: ludf.with(
title: "Darba Nosaukums",
thesis-type: "Kvalifikācijas Darbs",
authors: (
(
name: "Jānis Bērziņš",
code: "jb12345",
location: [Riga, Latvia],
email: "jb12345@edu.lu.lv",
),
(
name: "Zane Kalniņa",
code: "zk67890",
location: [Riga, Latvia],
email: "zk67890@edu.lu.lv",
),
),
date: (
year: 2025,
),
place: "Rīga",
bibliography: bibliography("bibliography.yml"),
abstract: (
primary: (
text: [
#lorem(50)
#lorem(30)
#lorem(20)
],
keywords: (
"Foo",
"Bar",
"Baz",
),
),
secondary: (
text: [
#lorem(20)
#lorem(30)
#lorem(50)
],
keywords: (
"foo",
"bar",
"baz",
),
),
),
)
#set heading(numbering: none)
= Apzīmējumu saraksts
/ Docs: Typst dokumentācija.#footnote[https://typst.com/docs/]
/ Universe: Typst kopienas paketes un šabloni.#footnote[https://typst.app/universe/]
= Ievads
== Nolūks
#lorem(100)
== Darbības sfēra
== Saistība ar citiem dokumentiem
PPS ir izstrādāta, ievērojot LVS 68:1996 "Programmatūras prasību specifikācijas
ceļvedis" @lvs_68 un LVS 72:1996 "Ieteicamā prakse programmatūras projektējuma
aprakstīšanai" standarta prasības @lvs_72.
== Pārskats
#set heading(numbering: "1.1.")
= Vispārējais apraksts
== Esošā stāvokļa apraksts
== Pasūtītājs
== Produkta perspektīva
== Darījumprasības
== Sistēmas lietotāji
Skatīt @dpd-0
#figure(
caption: [\0. līmeņa DPD],
diagram(
data-store((0, 0), [Lietotājs]),
dpd-edge("rr,ddd,ll", align(center)[Ievades ierīces\ dati]),
process((0, 3), [Sistēma], inset: 20pt),
dpd-edge(
"lll,uuu,rrr",
align(center)[Vizuālās\ izvades dati],
),
dpd-edge(
"l,uuu,r",
align(center)[Audio\ izvades dati],
),
),
) <dpd-0>
/// Or use an image
//
// #figure(
// caption: "0. līmeņa DPD",
// image("path/to/image"),
// ) <dpd-0>
== Vispārējie ierobežojumi
= Programmatūras prasību specifikācija
== Konceptuālais datu bāzes apraksts
== Funkcionālās prasības
#figure(
caption: [\1. līmeņa DPD],
diagram({
dpd-database((0, 0), [Datubāze], snap: -1)
dpd-edge("ldd", align(center)[Neapstrādāti\ ārējie dati])
dpd-edge("rrr,d", align(center)[Neapstrādāti\ dati])
process(
(-1, 2),
[Ārējs apstrādātājs],
inset: 20pt,
stroke: (thickness: 1pt, dash: "dashed"),
)
dpd-edge("r,uu", align(center)[Apstrādāti\ ārējie dati])
data-store((0, -2), [Lietotājs])
dpd-edge("dd", align(center)[Neapstrādāti\ lietotāja dati])
process((3, 1), [A modulis], inset: 20pt)
dpd-edge("lllu", align(center)[Apstrādāti dati], shift: 20pt)
}),
) <dpd-1>
=== Funkciju sadalījums moduļos
Funkciju sadalījums moduļos ir aprakstīts tabulā (@function-modules[tab]).
#figure(
caption: "Funkciju sadalījums pa moduļiem",
table(
columns: (auto, 1fr, auto),
align: left,
table.header([Modulis], [Funkcija], [Identifikators]),
table.cell(rowspan: 1)[A modulis],
[A saskarne], [#link(<AF01>)[AF01]],
table.cell(rowspan: 2)[B modulis],
[B saskarne], [#link(<BF01>)[BF01]],
[B apstrāde], [#link(<BF02>)[BF02]],
),
) <function-modules>
=== A Modulis
#function-table(
"A Saskarne",
"AF01",
[#lorem(15)],
[
+ #lorem(4)
+ #lorem(5)
+ #lorem(6)
],
[
+ #lorem(4)
+ #lorem(5)
+ #lorem(6)
+ #lorem(7)
+ #lorem(8)
- #lorem(2)
- #lorem(1)
- #lorem(3)
- #lorem(1)
],
[
+ #lorem(10)
+ #lorem(10)
],
) <AF01>
=== B Modulis
#function-table(
"B Saskarne",
"BF01",
[#lorem(15)],
[
+ #lorem(4)
+ #lorem(5)
+ #lorem(6)
],
[
+ #lorem(4)
+ #lorem(5)
+ #lorem(6)
+ #lorem(7)
+ #lorem(8)
- #lorem(2)
- #lorem(1)
- #lorem(3)
- #lorem(1)
],
[
+ #lorem(10)
+ #lorem(10)
],
[
+ #lorem(10)
+ #lorem(10)
],
) <BF01>
#function-table(
"B Apstrāde",
"BF02",
[#lorem(15)],
[
+ #lorem(4)
+ #lorem(5)
+ #lorem(6)
],
[
+ #lorem(4)
+ #lorem(5)
+ #lorem(6)
+ #lorem(7)
+ #lorem(8)
- #lorem(2)
- #lorem(1)
- #lorem(3)
- #lorem(1)
],
[
+ #lorem(10)
+ #lorem(10)
],
) <BF02>
== Nefunkcionālās prasības
=== Veiktspējas prasības
= Programmatūras projektējuma apraksts
== Datu bāzes projektējums
=== Datu bāzes loģiskais ER modelis
Skatīt @logical-erd
#figure(
caption: "Datu bāzes loģiskais ER modelis",
[\<image>],
// image("path/to/image")
) <logical-erd>
=== Datu bāzes fiziskais ER modelis
Skatīt @physical-erd[attēlu].
#figure(
caption: "Datu bāzes loģiskais ER modelis",
[\<image>],
// image("path/to/image")
) <physical-erd>
=== Datu bāzes tabulu apraksts
== Daļējs funkciju projektējums
== Daļējs lietotāju saskarņu projektējums
=== Navigācija
sk. @view-flow-diagram
#figure(
caption: "Ekrānskatu plūsmas diagramma",
[\<image>],
// image("path/to/image")
) <view-flow-diagram>
=== Ekrānskati

View File

@ -0,0 +1,48 @@
#import "@preview/fletcher:0.5.8" as fletcher: diagram, edge, node
#import fletcher.shapes: cylinder, diamond, ellipse
#let default-node-stroke = 1pt
#let default-edge-stroke = 1pt
/// Read https://github.com/typst/packages/raw/main/packages/preview/fletcher/0.5.8/docs/manual.pdf for more information
#let data-store(pos, text) = {
node(
pos,
text,
inset: 20pt,
stroke: default-node-stroke,
)
}
#let process(..args) = {
node(
inset: 10pt,
shape: ellipse,
stroke: default-node-stroke,
..args,
)
}
#let dpd-edge(..args) = {
edge(
label-pos: 0.5,
stroke: default-edge-stroke,
label-anchor: "center",
label-fill: white,
corner-radius: 4pt,
label-size: 10pt,
..args,
"-|>",
)
}
#let dpd-database(..args) = {
node(
shape: cylinder,
height: 6em,
width: 10em,
stroke: default-node-stroke,
..args,
)
}

View File

@ -0,0 +1,56 @@
/// Creates a two-column "function" table with a caption.
///
/// Intended for "Funkciju sadalījums moduļos" section
///
/// Parameters:
/// - caption: optional caption; defaults to the first positional item.
/// - title: tuple of column/section titles (defaults provided).
/// - items: positional cells; first two items populate the top row,
/// remaining items are paired with the titles ["Apraksts", "Ievade", ...]
///
/// Behavior:
/// - Renders the first two titles as table headers and the first two
/// positional items beneath them.
/// - For titles from index 2 onward, each title is rendered as a full-width
/// (colspan 2) bold row followed by a full-width row containing the
/// corresponding positional item. Missing items become empty strings.
/// - Safe for fewer/more items: missing values are coerced to "", extra
/// items beyond the paired section are ignored.
///
/// Default caption to the first positional item if none provided.
#let function-table(
caption: "",
titles: (
"Funkcijas nosaukums",
"Funkcijas identifikators",
"Apraksts",
"Ievade",
"Apstrāde",
"Izvade",
"Paziņojumi",
),
..items,
) = {
if caption == "" {
caption = items.pos().first()
}
let cells = titles
.slice(2) // start from "Apraksts"
.zip(items.pos().slice(2))
.map(pair => (
table.cell(colspan: 2, strong(pair.at(0))),
table.cell(colspan: 2, pair.at(1)),
))
.flatten()
figure(
caption: caption,
table(
columns: (1fr, 1fr),
strong(titles.at(0)), strong(titles.at(1)),
items.pos().at(0), items.pos().at(1),
..cells,
),
)
}

View File

@ -1,5 +1,5 @@
#import "@preview/headcount:0.1.0": *
#import "utils.typ": render-abstract
#import "utils.typ": make-abstract, make-documentary-page, make-title
#let indent = 1cm
@ -217,44 +217,17 @@
)
// Display the paper's title and authors at the top of the page,
// spanning all columns (hence floating at the scope of the
// columns' parent, which is the page).
// The page can contain a logo if you pass one with `logo: "logo.png"`.
align(center, upper(text(size: 16pt, [
#university\
#faculty
])))
v(1fr)
align(center, upper(text(20pt, weight: "bold", title)))
v(0.2fr)
align(center, upper(text(size: 16pt, thesis-type)))
v(1fr)
// Author information
context [
#set par(first-line-indent: 0pt)
#if authors.len() > 1 { "Autori:" } else { "Autors:" }
#authors.map(author => strong(author.name)).join(", ")
#if authors.len() > 1 { "Studentu" } else { "Studenta" }
apliecības Nr.: #authors.map(author => author.code).join(", ")
#if advisors.len() > 0 [
Darba #if advisors.len() > 1 { "vadītāji:" } else { "vadītājs:" }
#advisors.map(advisor => [#advisor.title #advisor.name]).join("\n")
]
]
v(0.5fr)
align(center, upper([#place #date.year]))
make-title(
title,
authors,
advisors,
university,
faculty,
thesis-type,
date,
place,
logo,
)
// Start page numbering
set page(numbering: "1", number-align: center)
@ -262,8 +235,8 @@
// Display abstract and keywords.
if abstract != none {
render-abstract("primary", abstract.primary)
render-abstract("secondary", abstract.secondary)
make-abstract("primary", abstract.primary)
make-abstract("secondary", abstract.secondary)
}
// Table of contents.
@ -279,4 +252,6 @@
// Display bibliography.
bibliography
make-documentary-page()
}

98
src/utils.typ Normal file
View File

@ -0,0 +1,98 @@
#let merge(a, b) = {
let result = a
for (k, v) in b { result.at(k) = v }
result
}
#let make-abstract(role, abstract) = {
// Define role-based defaults
let defaults = if role == "primary" {
(
lang: "lv",
title: "Anotācija",
keyword-title: "Atslēgvārdi",
text: [],
keywords: [],
)
} else {
(
lang: "en",
title: "Abstract",
keyword-title: "Keywords",
text: [],
keywords: [],
)
}
// Merge defaults with overrides
let abs = merge(defaults, abstract)
context [
#set text(lang: abs.lang)
#heading(
level: 1,
outlined: false,
numbering: none,
abs.title,
)
// Abstract body text
#abs.text
// Keywords
#par(first-line-indent: 0cm)[ *#abs.keyword-title*: ]
#abs.keywords.join(", ").
]
}
// Display the paper's title and authors at the top of the page,
// spanning all columns (hence floating at the scope of the
// columns' parent, which is the page).
// The page can contain a logo if you pass one with `logo: "logo.png"`.
#let make-title(
title,
authors,
advisors,
university,
faculty,
thesis-type,
date,
place,
logo,
) = {
align(center, upper(text(size: 16pt, [
#university\
#faculty
])))
v(1fr)
align(center, upper(text(20pt, weight: "bold", title)))
v(0.2fr)
align(center, upper(text(size: 16pt, thesis-type)))
v(1fr)
// Author information
context [
#set par(first-line-indent: 0pt)
#if authors.len() > 1 { "Autori:" } else { "Autors:" }
#authors.map(author => strong(author.name)).join(", ")
#if authors.len() > 1 { "Studentu" } else { "Studenta" }
apliecības Nr.: #authors.map(author => author.code).join(", ")
#if advisors.len() > 0 [
Darba #if advisors.len() > 1 { "vadītāji:" } else { "vadītājs:" }
#advisors.map(advisor => [#advisor.title #advisor.name]).join("\n")
]
]
v(0.5fr)
align(center, upper([#place #date.year]))
}
#let make-documentary-page() = {}

View File

@ -2,7 +2,7 @@
name = "solo-lu-df"
version = "0.0.1"
compiler = "0.13.0"
entrypoint = "lib.typ"
entrypoint = "src/lib.typ"
repository = "https://github.com/kristoferssolo/LU-DF-Typst-Template"
authors = ["Kristofers Solo <dev@kristofers.xyz>"]
license = "MIT"

View File

@ -1,46 +0,0 @@
#let merge(a, b) = {
let result = a
for (k, v) in b { result.at(k) = v }
result
}
#let render-abstract(role, abstract) = {
// Define role-based defaults
let defaults = if role == "primary" {
(
lang: "lv",
title: "Anotācija",
keyword-title: "Atslēgvārdi",
text: [],
keywords: [],
)
} else {
(
lang: "en",
title: "Abstract",
keyword-title: "Keywords",
text: [],
keywords: [],
)
}
// Merge defaults with overrides
let abs = merge(defaults, abstract)
context [
#set text(lang: abs.lang)
#heading(
level: 1,
outlined: false,
numbering: none,
abs.title,
)
// Abstract body text
#abs.text
// Keywords
#par(first-line-indent: 0cm)[ *#abs.keyword-title*: ]
#abs.keywords.join(", ").
]
}