This commit is contained in:
2025-02-08 23:26:49 +02:00
parent 1a06e70082
commit 7c7ac9257d
7 changed files with 818 additions and 868 deletions

1
.gitignore vendored
View File

@ -0,0 +1 @@
*.pdf

View File

@ -1,4 +1,4 @@
# Typst template for NURE works.
> [!WARNING]
> Extremely WIP!
> Still WIP, but you can use it.

80
bibl.yml Normal file
View File

@ -0,0 +1,80 @@
go:
type: Web
title: The Go Programming Language
author: The Go Programming Language
url:
value: https://go.dev/
date: 2024-12-10
htmx:
type: Web
title: Htmx - high power tools for html
author: Htmx - high power tools for html
url:
value: https://htmx.org/
date: 2024-12-10
mysql:
type: Book
title: MySQL Language Reference
author: Ab M.
publisher: MySQL Press
date: 2004
page-total: 600
neovim:
type: Web
title: About neovim
author: Neovim
url:
value: https://neovim.io/charter/
date: 2024-12-10
echo:
type: Web
title: High performance, extensible, minimalist Go web framework | Echo
author: High performance, extensible, minimalist Go web framework | Echo
url:
value: https://echo.labstack.com/
date: 2024-12-10
sqlx:
type: Web
title: Illustrated Guide to SQLX
author: GitHub Pages
url:
value: http://jmoiron.github.io/sqlx/
date: 2024-12-10
human:
type: Web
title: Про нас | HUMAN
author: HUMAN
url:
value: https://human.ua/
date: 2024-12-11
docker:
type: Web
title: "Docker: accelerated container application development"
author: Docker
url:
value: https://www.docker.com/
date: 2024-12-15
docker_compose:
type: Web
title: Docker compose
author: Docker Documentation
url:
value: https://docs.docker.com/compose/
date: 2024-12-15
hateoas:
type: Web
title: HATEOAS and Why It's Needed in RESTful API?
author: GeeksforGeeks
url:
value: https://www.geeksforgeeks.org/hateoas-and-why-its-needed-in-restful-api/
date: 2024-12-15

View File

@ -1,267 +0,0 @@
#import "@preview/indenta:0.0.3": fix-indent
#let subjects = (
БД: (
name: "Бази даних",
mentor: (
name: "Черепанова Ю. Ю.",
gender: "f",
degree: "ст. викл. каф. ПІ",
),
),
)
/// DSTU 3008:2015 Template for NURE
/// -> content
/// - doc (content): Content to apply the template to.
/// - title (string): Title of the document.
/// - authors: List of Author dicts.
/// - mentor: Mentor dict.
/// - include_toc: Include table of contents?
/// - doctype ("lb" | "pz" | "ku"): Document type.
/// - worknumber: Number of the work, can be omitted.
/// - subject: Subject name.
#let conf(
doc,
title: [Work Title],
authors: ((name: "Author Name", variant: 1, group: "Group Name", gender: "m"),),
include_toc: false,
doctype: "ЛБ",
worknumber: 1,
subject_shorthand: "NONE",
) = {
// numless header
let numless(it) = {
set heading(numbering: none)
it
}
// LaTeX-like vfill shorthand
let vfill = () => v(1fr)
set document(title: title, author: authors.at(0).name)
set page(
paper: "a4",
margin: (top: 20mm, right: 10mm, bottom: 20mm, left: 25mm),
number-align: top + right,
numbering: (..numbers) => {
if numbers.pos().at(0) != 1 {
numbering("1", numbers.pos().at(0))
}
},
)
set text(font: "Times New Roman", size: 14pt, hyphenate: false, lang: "ua")
set par(justify: true, first-line-indent: 1.25cm)
// set 1.5 line spacing
let spacing = 0.95em
set block(spacing: spacing)
set par(spacing: spacing)
set par(leading: spacing)
// enums and lists
let ua_alph(pattern: "а)") = {
// INFO: This alphabet is not full, maybe it should be extended or maybe not.
// I cant remember nor find proper formatting rules.
// "абвгґдеєжзиіїйклмнопрстуфхцчшщьюя" (full alphabet)
let alphabet = "абвгдежиклмнпрстуфхцшщюя".split("")
i => {
let letter = alphabet.at(i)
let str = ""
for char in pattern {
if char == "а" {
str += letter
} else if char == "А" {
str += upper(letter)
} else {
str += char
}
}
str
}
}
set enum(numbering: ua_alph(pattern: "а)"), indent: 1.25cm, body-indent: 0.5cm)
show enum: it => {
set enum(indent: 0em, numbering: "1)")
it
}
set list(indent: 1.35cm, body-indent: 0.5cm, marker: [--])
// figures
set figure.caption(separator: [ -- ])
let fig = counter("figure")
let tab = counter("table")
show figure.where(kind: image): set figure(
numbering: (..) => {
fig.step()
context str(counter(heading).get().at(0) + worknumber - 1) + "." + context fig.display()
},
)
show figure.where(kind: table): set figure(
numbering: (..) => {
tab.step()
context str(counter(heading).get().at(0) + worknumber - 1) + "." + context tab.display()
},
)
// TODO: Maybe this will be better. Must be investigated.
//
// set math.equation(numbering: (..num) =>
// numbering("(1.1)", counter(heading).get().first(), num.pos().first())
// )
// set figure(numbering: (..num) =>
// numbering("1.1", counter(heading).get().first(), num.pos().first())
// )
show figure: it => {
v(spacing * 2, weak: true)
it
v(spacing * 2, weak: true)
}
// headings
// set heading(numbering: "1.1")
// HACK: I can't set initial value for headers counter, so change is only visual. If this style is changed do not forget to fix images and tables numbering as well.
set heading(numbering: (n1, ..x) => numbering("1.1", n1 - 1 + worknumber, ..x))
show heading.where(level: 1): it => {
set align(center)
set text(size: 14pt, weight: "semibold")
pagebreak(weak: true)
block(width: 100%, spacing: 0em)[ #upper(it) ]
}
show heading.where(level: 2): it => {
set text(size: 14pt, weight: "regular")
block(width: 100%, spacing: 0em)[
#h(1.25cm)
#counter(heading).display(it.numbering)
#it.body
]
}
let fix-spacings() = doc => {
if not doc.has("children") { return doc }
let elems = doc.children.filter(x => x != [ ] and x.func() != parbreak)
let is_heading = e => e != none and e.func() == heading
for (i, elem) in elems.enumerate() {
if not is_heading(elem) {
elem
continue
}
let prev_elem = elems.at(i - 1, default: none)
let next_elem = elems.at(i + 1, default: none)
if elem.depth == 2 {
v(spacing * (if not is_heading(prev_elem) { 2 } else { 1 }), weak: true)
}
elem
v(spacing * (if not is_heading(next_elem) { 2 } else { 1 }), weak: true)
}
}
show: fix-spacings()
show: fix-indent()
if doctype in ("ЛБ", "ПЗ") {
let subject = subjects.at(subject_shorthand, default: "NONE")
let mentor = subject.mentor
align(
center,
[
МІНІСТЕРСТВО ОСВІТИ І НАУКИ УКРАЇНИ
ХАРКІВСЬКИЙ НАЦІОНАЛЬНИЙ УНІВЕРСИТЕТ РАДІОЕЛЕКТРОНІКИ
#linebreak()
Кафедра Програмної інженерії
#linebreak()
#linebreak()
#linebreak()
Звіт
з
#if doctype == "ЛБ" { [лабораторної роботи] } else { [практичної роботи] }
#if worknumber != none { [ #worknumber] }
з дисципліни: "#subject.name"
з теми: "#title"
#linebreak()
#linebreak()
#linebreak()
#columns(2)[
#set align(left)
#if authors.len() == 1 {
let author = authors.at(0)
if author.gender == "m" { [Виконав:\ ] } else { [Виконала:\ ] }
[
ст. гр. #author.group\
#author.name\
#if author.variant != none { [Варіант: #author.variant] }
]
} else {
[
Виконали:\
ст. гр. #authors.at(0).group\
#authors.map(a => [ #a.name\ ])
]
}
#colbreak()
#set align(right)
#if mentor.gender == "m" { [Перевірив:\ ] } else { [Перевірила:\ ] }
#mentor.degree #if mentor.degree.len() >= 15 {
[\
]
}
#mentor.name\
]
#vfill()
Харків -- #datetime.today().display("[year]")
],
)
} else if doctype == "КУ" { }
if include_toc {
outline(
title: [
ЗМІСТ
#v(spacing * 2)
],
depth: 2,
indent: auto,
)
}
doc
}

127
example.typ Normal file
View File

@ -0,0 +1,127 @@
#import "template.typ": *
#let authors = (
(
name: "Ситник Є. С.",
full_name_gen: "Ситника Єгора Сергійовича",
variant: 13,
group: "ПЗПІ-23-2",
gender: "m",
),
)
#let mentors = (
(
name: "Черепанова Ю. Ю.",
gender: "f",
degree: "Ст. викл. каф. ПІ",
),
(
name: "Русакова Н. Є.",
gender: "f",
degree: "Доц. каф. ПІ",
),
(
name: "Широкопетлєва М. С.",
gender: "f",
degree: "Ст. викл. каф. ПІ",
),
)
#let task_list = (
done_date: datetime(year: 2024, month: 12, day: 27),
initial_data: datetime(year: 2024, month: 9, day: 15),
source: "методичні вказівки до виконання курсової роботи, вимоги до інформаційної системи, предметна область, що пов’язана з управлінням класом та класним керівництвом.",
content: "вступ, аналіз предметної області; постановка задачі; проектування бази даних; опис програми; висновки; перелік джерел посилання.",
graphics: "загальна діаграма класів, ER-діаграма, UML-діаграми, DFD-діаграма, схема БД в 1НФ, 2НФ, 3НФ, копії екранів (“скриншоти”) прикладної програми, приклади звітів прикладної програми.",
)
#let calendar_plan = (
plan_table: [],
approval_date: datetime(year: 2024, month: 12, day: 27),
)
#let abstract = (
keywords: (
"БАЗА ДАНИХ",
"АВТОМАТИЗАЦІЯ",
"КЛАСНИЙ КЕРІВНИК",
"КЛАС",
"ШКОЛА",
"GO",
"HTMX",
"MYSQL",
"SQL",
),
text: [
Мета даної роботи -- проєктування та розробка інформаційної системи «Помічник класного керівника. Керування класом», яка спрямована на автоматизацію процесів управління класом, облік даних про учнів, планування та аналіз навчального процесу. Основна задача інформаційної системи спростити роботу класного керівника, забезпечити ефективну організацію документації та взаємодію з учасниками освітнього процесу.
Для реалізації системи було використано сучасний стек технологій, а саме: Go -- як основна мова програмування для створення серверної логіки, HTMX -- для динамічного оновлення інтерфейсу без використання складних фреймворків, MySQL -- як СУБД для зберігання даних про учнів, їх оцінки та розклад, Neovim -- як середовище для швидкої та ефективної розробки коду, Go Echo -- веб-фреймворк для створення REST API, Go SQLx -- бібліотека для роботи з базою даних, що забезпечує зручність і гнучкість.
Результат роботи веб-додаток, який дозволяє обліковувати особисті дані учнів та їхніх опікунів, включаючи інформацію про успішність, відвідуваність та інші показники; планувати розклад занять; генерувати звіти про успішність учнів та переглядати різну статистику. Інтерфейс, створений з використанням HTMX, легко адаптується під потреби користувача.
],
)
#let appendices = (
(
title: "Приклад звіту 1",
content: [test],
),
(
title: "Приклад звіту 2",
content: [test],
),
(
title: "Приклад звіту 3",
content: [test],
),
)
#show: conf.with(
doctype: "КП",
title: "Інформаційна система «Помічник класного керівника». Керування класом",
subject_shorthand: "БД",
department_gen: "Програмної інженерії",
edu_program: "ПЗПІ",
authors: authors,
mentors: mentors,
task_list: task_list,
calendar_plan: calendar_plan,
abstract: abstract,
bib_path: "bibl.yml",
appendices: appendices,
)
#heading(depth: 1, numbering: none)[Вступ]
У сучасному освітньому середовищі інформаційні системи відіграють
ключову роль у забезпеченні ефективного управління та автоматизації процесів.
Особливо це важливо для шкіл, де необхідно систематизувати облік даних про
учнів, планувати навчальний процес і створювати звіти. Відсутність зручних
інструментів ускладнює роботу класного керівника, змушуючи його витрачати
багато часу на тяганину з документами, зокрема на складання звітів, ведення
обліку даних про учнів і підготовку розкладу, що потребує сучасних рішень для
організації взаємодії між учасниками освітнього процесу.
Метою цієї курсової роботи є розробка інформаційної системи «Помічник
класного керівника. Керування класом», яка спрощує процес управління класом,
дозволяючи автоматизувати складання звітів, зберігати та обробляти дані про
учнів, а також планувати навчальний процес у зручному форматі, забезпечує
зручний облік даних про учнів і сприяє ефективному плануванню навчального
процесу. У процесі роботи над системою було проведено аналіз потреб
користувачів, спроєктовано реляційну базу даних і розроблено веб-додаток, який
дозволяє обліковувати особисті дані учнів та їхніх опікунів, відстежувати
успішність і відвідуваність, планувати розклад занять, формувати звіти та
переглядати статистичні дані.
Основну серверну логіку написано мовою програмування Go@go, а для
забезпечення роботи інтерфейсу застосовано HTMX@htmx. Дані зберігаються у базі
даних MySQL@mysql, взаємодія з якою здійснюється через бібліотеку Go SQLx@sqlx.
Для створення REST API використано веб-фреймворк Go Echo@echo.
Розробка виконувалася у середовищі Neovim@neovim.
= Аналіз та концептуальне моделювання предметної області
#v(-1em)
== test
test
== test

609
template.typ Normal file
View File

@ -0,0 +1,609 @@
#import "@preview/indenta:0.0.3": fix-indent
#let hfill(width) = box(width: width, repeat("")) // NOTE: This is a HAIR SPACE (U+200A ), not a regular space
#let uline(align: center, content) = underline([
#if align != left { hfill(1fr) }
#content
#if align != right { hfill(1fr) }
])
#let bold(content) = text(weight: "bold")[#content]
#let fig(path, caption) = [
#figure(
image(path),
caption: caption,
)
#label(path.split("/").last().split(".").first())
]
#let subjects = (
"БД": "Бази даних",
)
#let edu_programs = (
"ПЗПІ": (
name: "Інженерія програмного забезпечення",
number: 121,
),
)
#let month_gen(month) = (
"січня",
"лютого",
"березня",
"квітня",
"травня",
"червня",
"липня",
"серпня",
"вересня",
"жовтня",
"листопада",
"грудня",
).at(month - 1)
#let spacing = 0.95em
/// DSTU 3008:2015 Template for NURE
/// -> content
/// - doc (content): Content to apply the template to.
/// - doctype ("ЛБ" | "ПЗ" | "КП"): Document type.
/// - title (str): Title of the document.
/// - subject_shorthand (str): Subject name.
/// - department_gen (str): Department name in genitive form.
/// - edu_program_number (int): Education program number.
/// - worknumber (int): Number of the work, can be omitted.
/// - authors ((name: str, full_name_gen: str, variant: int, group: str, gender: str),): List of Authors dicts.
/// - task_list (done_date: datetime, initial_data: datetime, source: str, content: str, graphics: str): Task list.
/// - calendar_plan (plan_table: list, approval_date: datetime): Calendar plan.
/// - abstract (keywords: (str,), text: [str,]): Abstract.
#let conf(
doc,
title: "NONE",
doctype: "NONE",
subject_shorthand: "NONE",
department_gen: "Програмної інженерії",
worknumber: 1,
authors: (),
mentors: (),
edu_program: "NONE",
task_list: (),
calendar_plan: (),
abstract: (),
bib_path: "bibl.yml",
appendices: (),
) = {
set document(title: title, author: authors.at(0).name)
set page(
paper: "a4",
margin: (top: 20mm, right: 10mm, bottom: 20mm, left: 25mm),
number-align: top + right,
numbering: (..numbers) => {
if numbers.pos().at(0) != 1 {
numbering("1", numbers.pos().at(0))
}
},
)
set text(font: "Liberation Serif", size: 14pt, hyphenate: false, lang: "ua")
set par(justify: true, first-line-indent: 1.25cm)
set underline(evade: false)
// set 1.5 line spacing
set block(spacing: spacing)
set par(spacing: spacing)
set par(leading: spacing)
// enums and lists
let ua_alph_numbering() = {
// INFO: This alphabet is not full, maybe it should be extended or maybe not.
// I cant remember nor find proper formatting rules.
// "абвгґдеєжзиіїйклмнопрстуфхцчшщьюя" (full alphabet)
let alphabet = "абвгдежиклмнпрстуфхцшщюя".split("")
i => { alphabet.at(i) + ")" }
}
set enum(numbering: ua_alph_numbering(), indent: 1.25cm, body-indent: 0.5cm)
show enum: it => {
set enum(indent: 0em, numbering: "1)")
it
}
set list(indent: 1.35cm, body-indent: 0.5cm, marker: [--])
// citations
let bib-count = state("citation-counter", ())
show cite: it => {
it
bib-count.update(((..c)) => (..c, it.key))
}
let cit = context bib-count.final().dedup().len()
// figures
set figure.caption(separator: [ -- ])
let img = counter("image")
let tab = counter("table")
show figure.where(kind: image): set figure(
numbering: (..) => {
img.step()
context str(counter(heading).get().at(0) + worknumber - 1) + "." + context img.display()
},
)
show figure.where(kind: table): set figure(
numbering: (..) => {
tab.step()
context str(counter(heading).get().at(0) + worknumber - 1) + "." + context tab.display()
},
)
// TODO: Maybe this will be better. Must be investigated.
//
// set math.equation(numbering: (..num) =>
// numbering("(1.1)", counter(heading).get().first(), num.pos().first())
// )
// set figure(numbering: (..num) =>
// numbering("1.1", counter(heading).get().first(), num.pos().first())
// )
show figure: it => {
v(spacing * 2, weak: true)
it
v(spacing * 2, weak: true)
}
// headings
// HACK: I can't set initial value for headers counter, so change is only visual. If this style is changed do not forget to fix images and tables numbering as well.
set heading(numbering: (n1, ..x) => numbering("1.1", n1 - 1 + worknumber, ..x))
show heading.where(level: 1): it => {
set align(center)
set text(size: 14pt, weight: "semibold")
pagebreak(weak: true)
upper(it)
v(spacing * 2, weak: true)
}
show heading.where(level: 2): it => {
set text(size: 14pt, weight: "regular")
v(spacing * 2, weak: true)
block(width: 100%, spacing: 0em)[
#h(1.25cm)
#counter(heading).display(it.numbering)
#it.body
]
v(spacing * 2, weak: true)
}
show: fix-indent()
let subject = subjects.at(subject_shorthand, default: "NONE")
if doctype in ("ЛБ", "ПЗ") {
let mentor = subject.mentors.at(0)
align(center)
[
МІНІСТЕРСТВО ОСВІТИ І НАУКИ УКРАЇНИ
ХАРКІВСЬКИЙ НАЦІОНАЛЬНИЙ УНІВЕРСИТЕТ РАДІОЕЛЕКТРОНІКИ
#linebreak()
Кафедра Програмної інженерії
#linebreak()
#linebreak()
#linebreak()
Звіт
з
#if doctype == "ЛБ" [лабораторної роботи] else [практичної роботи]
#if worknumber != none [ #worknumber]
з дисципліни: "#subject.name"
з теми: "#title"
#linebreak()
#linebreak()
#linebreak()
#columns(2)[
#set align(left)
#if authors.len() == 1 [
#let author = authors.at(0)
#if author.gender == "m" { [Виконав:\ ] } else { [Виконала:\ ] }
ст. гр. #author.group\
#author.name\
#if author.variant != none { [Варіант: #author.variant] }
] else [
Виконали:\
ст. гр. #authors.at(0).group\
#authors.map(a => [ #a.name\ ])
]
#colbreak()
#set align(right)
#if mentor.gender == "m" { [Перевірив:\ ] } else { [Перевірила:\ ] }
#mentor.degree #if mentor.degree.len() >= 15 [\ ]
#mentor.name\
]
#v(1fr)
Харків -- #datetime.today().display("[year]")
]
} else if doctype == "КП" {
let head_mentor = mentors.at(0)
let author = authors.at(0)
let edu_program = edu_programs.at(edu_program)
// page 1
align(center)[
МІНІСТЕРСТВО ОСВІТИ І НАУКИ УКРАЇНИ
ХАРКІВСЬКИЙ НАЦІОНАЛЬНИЙ УНІВЕРСИТЕТ РАДІОЕЛЕКТРОНІКИ
#linebreak()
Кафедра Програмної інженерії
#linebreak()
ПОЯСНЮВАЛЬНА ЗАПИСКА
ДО КУРСОВОЇ РОБОТИ
з дисципліни: "#subject"
Тема роботи: "#title"
#linebreak()
#linebreak()
#linebreak()
#columns(2, gutter: 4cm)[
#set align(left)
#if authors.len() == 1 [
#if author.gender == "m" { [Виконав\ ] } else { [Виконала\ ] } ст. гр. #author.group
]
#linebreak()
Керівник:\
#head_mentor.degree
#linebreak()
Робота захищена на оцінку
#linebreak()
Комісія:\
#for mentor in mentors {
[#mentor.degree\
]
}
#colbreak()
#set align(left)
#linebreak()
#author.name
#linebreak()
#linebreak()
#head_mentor.name
#linebreak()
#underline(" " * 35)
#linebreak()
#linebreak()
#for mentor in mentors {
[#mentor.name\
]
}
]
#v(1fr)
Харків -- #datetime.today().display("[year]")
#pagebreak()
]
//
// page 2
{
uline([Харківський національний університет радіоелектроніки])
linebreak()
linebreak()
grid(
columns: (100pt, 1fr),
bold([
Кафедра
Дисципліна
Спеціальність
]),
{
uline(align: left, department_gen)
linebreak()
uline(align: left, subject)
linebreak()
uline(align: left, [#edu_program.number #edu_program.name])
},
)
grid(
columns: (1fr, 1fr, 1fr),
gutter: 0.3fr,
[#bold("Курс") #uline(2)], [#bold("Група") #uline(author.group)], [#bold("Семестр") #uline(3)],
)
linebreak()
linebreak()
linebreak()
align(center)[#bold([
ЗАВДАННЯ
на курсову роботу студента
])]
linebreak()
uline(align: left)[#emph(author.full_name_gen)]
linebreak()
linebreak()
bold([ #"1. "Тема роботи: ])
uline([#title.])
linebreak()
{
bold([ #"2. "Строк здачі закінченої роботи: ])
uline(task_list.done_date.display("[day].[month].[year]"))
hfill(10fr)
}
linebreak()
bold([ #"3. "Вихідні дані для роботи: ])
uline(task_list.source)
linebreak()
bold([ #"4. "Зміст розрахунково-пояснювальної записки: ])
uline(task_list.content)
linebreak()
bold([ #"5. "Перелік графічного матеріалу: ])
uline(task_list.graphics)
linebreak()
{
bold([ #"6. "Строк здачі закінченої роботи: ])
uline(task_list.done_date.display("[day].[month].[year]"))
hfill(10fr)
}
pagebreak()
}
// page 3
{
align(center)[#bold([КАЛЕНДАРНИЙ ПЛАН])]
linebreak()
calendar_plan.plan_table
linebreak()
grid(
columns: (6fr, 5fr),
grid(
columns: (1fr, 3fr, 1fr),
gutter: 0.2fr,
[
Студент
#linebreak()
Керівник
#linebreak()
#align(center, ["#calendar_plan.approval_date.day()"])
],
[
#uline(align: center, [])
#linebreak()
#uline(align: center, [])
#linebreak()
#uline(align: center, month_gen(calendar_plan.approval_date.month()))
],
{
linebreak()
linebreak()
[#calendar_plan.approval_date.year() р.]
},
),
[
#author.name,
#linebreak()
#head_mentor.degree
#head_mentor.name.
],
)
pagebreak()
}
// page 4 {{{
[
#align(center)[#bold([РЕФЕРАТ])]
#linebreak()
#context [
#let pages = counter(page).final().at(0)
#let tables = counter("table").final().at(0)
#let images = counter("image").final().at(0)
#let bibs = bib-count.final().dedup().len()
#let counters = ()
#if pages != 0 { counters.push([#pages с.]) }
#if tables != 0 { counters.push([#tables табл.]) }
#if images != 0 { counters.push([#images рис.]) }
#if bibs != 0 { counters.push([#bibs джерел]) }
Пояснювальна записка до курсової роботи: #counters.join(", ").
]
#linebreak()
#{
let keywords = abstract.keywords
let is_cyrillic = word => word
.split("")
.any(char => ("А" <= char and char <= "я") or char == "Ё" or char == "ё")
let n = keywords.len()
for i in range(n) {
for j in range(0, n - i - 1) {
if (
(not is_cyrillic(keywords.at(j)) and is_cyrillic(keywords.at(j + 1)))
or (
is_cyrillic(keywords.at(j)) == is_cyrillic(keywords.at(j + 1)) and keywords.at(j) > keywords.at(j + 1)
)
) {
(keywords.at(j), keywords.at(j + 1)) = (keywords.at(j + 1), keywords.at(j))
}
}
}
keywords.join(", ")
}
#linebreak()
#abstract.text
]
// }}}
// page 5 {{{
show outline.entry: it => {
show linebreak: none
it
}
outline(
title: [
ЗМІСТ
#v(spacing * 2)
],
depth: 2,
indent: auto,
)
// }}}
}
doc
// bibliography {{{
show bibliography: it => {
set text(size: 0pt)
it
}
heading(depth: 1, numbering: none)[Перелік джерел посилання]
bibliography(
bib_path,
style: "ieee",
full: true,
title: none,
)
let bib_data = yaml(bib_path)
let format-entry(citation) = {
if (citation.type == "Web") {
let date_array = citation.url.date.split("-")
let date = datetime(
year: int(date_array.at(0)),
month: int(date_array.at(1)),
day: int(date_array.at(2)),
)
[
#citation.title.
_#{citation.author}_.
URL: #citation.url.value (дата звернення: #date.display("[day].[month].[year]")).
]
} else if citation.type == "Book" [
#citation.author
#citation.title.
#citation.publisher,
#citation.date.
#citation.page-total c.
] else [
UNSUPPORTED BIBLIOGRAPHY ENTRY TYPE, PLEASE OPEN THE ISSUE
]
}
show enum.item: it => {
box(width: 1.25cm)
box(width: 1em + 0.5cm)[#it.number.]
it.body
linebreak()
}
context {
let citations = query(ref.where(element: none)).map(r => str(r.target)).dedup()
for (i, citation) in citations.enumerate() {
enum.item(
i + 1,
format-entry(bib_data.at(citation)),
)
}
}
// }}}
// appendices {{{
counter(heading).update(0)
for (i, appendix) in appendices.enumerate() [
#set heading(
numbering: i => [
Додаток #"АБВГДЕЖИКЛМНПРСТУФХЦШЩЮЯ".split("").at(i)
],
)
#show heading: it => {
set align(center)
set text(size: 14pt, weight: "regular")
pagebreak(weak: true)
bold(upper(counter(heading).display(it.numbering)))
linebreak()
it.body
v(spacing * 2, weak: true)
}
= #appendix.title
#appendix.content
]
// }}}
}

600
test.typ
View File

@ -1,600 +0,0 @@
#import "@preview/indenta:0.0.3": fix-indent
#let subjects = (
"БД": (
name: "Бази даних",
mentors: (
(
name: "Черепанова Ю. Ю.",
gender: "f",
degree: "Ст. викл. каф. ПІ",
),
(
name: "Русакова Н. Є.",
gender: "f",
degree: "Доц. каф. ПІ",
),
(
name: "Широкопетлєва М. С.",
gender: "f",
degree: "Ст. викл. каф. ПІ",
),
),
),
)
/// DSTU 3008:2015 Template for NURE
/// -> content
/// - doc (content): Content to apply the template to.
/// - title (string): Title of the document.
/// - authors: List of Author dicts.
/// - mentor: Mentor dict.
/// - include_toc: Include table of contents?
/// - doctype ("lb" | "pz" | "ku"): Document type.
/// - worknumber: Number of the work, can be omitted.
/// - subject: Subject name.
#let conf(
doc,
authors: (
(
name: "Ситник Є. С.",
full_name_gen: "Ситника Єгора Сергійовича",
variant: 14,
group: "ПЗПІ-23-2",
)
),
title: "Інформаційна система «Помічник класного керівника». Керування класом",
worknumber: 1,
doctype: "ЛБ",
subject_shorthand: "БД",
department: "Програмної інженерії",
edu_program: (
name: "Інженерія програмного забезпечення",
number: 121,
),
task_list: (
done_date: datetime(year: 2024, month: 12, day: 27),
initial_data: datetime(year: 2024, month: 9, day: 15),
source: "методичні вказівки до виконання курсової роботи, вимоги до інформаційної системи, предметна область, що пов’язана з управлінням класом та класним керівництвом.",
content: "вступ, аналіз предметної області; постановка задачі; проектування бази даних; опис програми; висновки; перелік джерел посилання.",
graphics: "загальна діаграма класів, ER-діаграма, UML-діаграми, DFD-діаграма, схема БД в 1НФ, 2НФ, 3НФ, копії екранів (“скриншоти”) прикладної програми, приклади звітів прикладної програми.",
),
calendar_plan: (
plan_table: [],
approval_date: datetime(year: 2024, month: 12, day: 27),
),
abstract: (
keywords: (
"БАЗА ДАНИХ",
"АВТОМАТИЗАЦІЯ",
"КЛАСНИЙ КЕРІВНИК",
"КЛАС",
"ШКОЛА",
"GO",
"HTMX",
"MYSQL",
"SQL",
),
text: [
Мета даної роботи проєктування та розробка інформаційної системи «Помічник класного керівника. Керування класом», яка спрямована на автоматизацію процесів управління класом, облік даних про учнів, планування та аналіз навчального процесу. Основна задача інформаційної системи спростити роботу класного керівника, забезпечити ефективну організацію документації та взаємодію з учасниками освітнього процесу.
Для реалізації системи було використано сучасний стек технологій, а саме: Go як основна мова програмування для створення серверної логіки, HTMX для динамічного оновлення інтерфейсу без використання складних фреймворків, MySQL як СУБД для зберігання даних про учнів, їх оцінки та розклад, Neovim як середовище для швидкої та ефективної розробки коду, Go Echo веб-фреймворк для створення REST API, Go SQLx бібліотека для роботи з базою даних, що забезпечує зручність і гнучкість.
Результат роботи веб-додаток, який дозволяє обліковувати особисті дані учнів та їхніх опікунів, включаючи інформацію про успішність, відвідуваність та інші показники; планувати розклад занять; генерувати звіти про успішність учнів та переглядати різну статистику. Інтерфейс, створений з використанням HTMX, легко адаптується під потреби користувача.
],
),
include_toc: true,
) = {
// LaTeX-like vfill/hfill shorthands
let vfill = () => v(1fr)
let hfill(width) = box(width: width, repeat("")) // NOTE: This is a HAIR SPACE (U+200A ), not a regular space
set underline(evade: false)
let uline(align: center, content) = {
underline([
#if align != left { hfill(1fr) }
#content
#if align != right { hfill(1fr) }
])
}
// other shorthands
let bold(it) = text(weight: "bold")[#it]
let month_gen(month) = (
"січня",
"лютого",
"березня",
"квітня",
"травня",
"червня",
"липня",
"серпня",
"вересня",
"жовтня",
"листопада",
"грудня",
).at(month - 1)
set document(title: title, author: authors.at(0).name)
set page(
paper: "a4",
margin: (top: 20mm, right: 10mm, bottom: 20mm, left: 25mm),
number-align: top + right,
numbering: (..numbers) => {
if numbers.pos().at(0) != 1 {
numbering("1", numbers.pos().at(0))
}
},
)
set text(font: "Times New Roman", size: 14pt, hyphenate: false, lang: "ua")
set par(justify: true, first-line-indent: 1.25cm)
// set 1.5 line spacing
let spacing = 0.95em
set block(spacing: spacing)
set par(spacing: spacing)
set par(leading: spacing)
// enums and lists
let ua_alph(pattern: "а)") = {
// INFO: This alphabet is not full, maybe it should be extended or maybe not.
// I cant remember nor find proper formatting rules.
// "абвгґдеєжзиіїйклмнопрстуфхцчшщьюя" (full alphabet)
let alphabet = "абвгдежиклмнпрстуфхцшщюя".split("")
i => {
let letter = alphabet.at(i)
let str = ""
for char in pattern {
if char == "а" {
str += letter
} else if char == "А" {
str += upper(letter)
} else {
str += char
}
}
str
}
}
set enum(numbering: ua_alph(pattern: "а)"), indent: 1.25cm, body-indent: 0.5cm)
show enum: it => {
set enum(indent: 0em, numbering: "1)")
it
}
set list(indent: 1.35cm, body-indent: 0.5cm, marker: [--])
// figures
set figure.caption(separator: [ -- ])
let img = counter("image")
let tab = counter("table")
show figure.where(kind: image): set figure(
numbering: (..) => {
img.step()
context str(counter(heading).get().at(0) + worknumber - 1) + "." + context img.display()
},
)
show figure.where(kind: table): set figure(
numbering: (..) => {
tab.step()
context str(counter(heading).get().at(0) + worknumber - 1) + "." + context tab.display()
},
)
// TODO: Maybe this will be better. Must be investigated.
//
// set math.equation(numbering: (..num) =>
// numbering("(1.1)", counter(heading).get().first(), num.pos().first())
// )
// set figure(numbering: (..num) =>
// numbering("1.1", counter(heading).get().first(), num.pos().first())
// )
show figure: it => {
v(spacing * 2, weak: true)
it
v(spacing * 2, weak: true)
}
// headings
// set heading(numbering: "1.1")
// HACK: I can't set initial value for headers counter, so change is only visual. If this style is changed do not forget to fix images and tables numbering as well.
set heading(numbering: (n1, ..x) => numbering("1.1", n1 - 1 + worknumber, ..x))
show heading.where(level: 1): it => {
set align(center)
set text(size: 14pt, weight: "semibold")
pagebreak(weak: true)
block(width: 100%, spacing: 0em)[ #upper(it) ]
}
show heading.where(level: 2): it => {
set text(size: 14pt, weight: "regular")
block(width: 100%, spacing: 0em)[
#h(1.25cm)
#counter(heading).display(it.numbering)
#it.body
]
}
let fix-spacings() = doc => {
if not doc.has("children") { return doc }
let elems = doc.children.filter(x => x != [ ])
let is_heading = e => e != none and e.func() == heading
for (i, elem) in elems.enumerate() {
if not is_heading(elem) {
elem
continue
}
let prev_elem = elems.at(i - 1, default: none)
let next_elem = elems.at(i + 1, default: none)
if elem.depth == 2 {
v(spacing * (if not is_heading(prev_elem) { 2 } else { 1 }), weak: true)
}
elem
v(spacing * (if not is_heading(next_elem) { 2 } else { 1 }), weak: true)
}
}
show: fix-spacings()
show: fix-indent()
let subject = subjects.at(subject_shorthand, default: "NONE")
if doctype in ("ЛБ", "ПЗ") {
let mentor = subject.mentors.at(0)
align(
center,
[
МІНІСТЕРСТВО ОСВІТИ І НАУКИ УКРАЇНИ
ХАРКІВСЬКИЙ НАЦІОНАЛЬНИЙ УНІВЕРСИТЕТ РАДІОЕЛЕКТРОНІКИ
#linebreak()
Кафедра Програмної інженерії
#linebreak()
#linebreak()
#linebreak()
Звіт
з
#if doctype == "ЛБ" { [лабораторної роботи] } else { [практичної роботи] }
#if worknumber != none { [ #worknumber] }
з дисципліни: "#subject.name"
з теми: "#title"
#linebreak()
#linebreak()
#linebreak()
#columns(2)[
#set align(left)
#if authors.len() == 1 {
let author = authors.at(0)
if author.gender == "m" { [Виконав:\ ] } else { [Виконала:\ ] }
[
ст. гр. #author.group\
#author.name\
#if author.variant != none { [Варіант: #author.variant] }
]
} else {
[
Виконали:\
ст. гр. #authors.at(0).group\
#authors.map(a => [ #a.name\ ])
]
}
#colbreak()
#set align(right)
#if mentor.gender == "m" { [Перевірив:\ ] } else { [Перевірила:\ ] }
#mentor.degree #if mentor.degree.len() >= 15 {
[\
]
}
#mentor.name\
]
#vfill()
Харків -- #datetime.today().display("[year]")
],
)
} else if doctype == "КУ" {
let head_mentor = subject.mentors.at(0)
let author = authors.at(0)
// page 1 {{{
align(center)[
МІНІСТЕРСТВО ОСВІТИ І НАУКИ УКРАЇНИ
ХАРКІВСЬКИЙ НАЦІОНАЛЬНИЙ УНІВЕРСИТЕТ РАДІОЕЛЕКТРОНІКИ
#linebreak()
Кафедра Програмної інженерії
#linebreak()
ПОЯСНЮВАЛЬНА ЗАПИСКА
ДО КУРСОВОЇ РОБОТИ
з дисципліни: "#subject.name"
Тема роботи: "#title"
#linebreak()
#linebreak()
#linebreak()
#columns(2, gutter: 4cm)[
#set align(left)
#if authors.len() == 1 [
#if author.gender == "m" { [Виконав\ ] } else { [Виконала\ ] } ст. гр. #author.group
]
#linebreak()
Керівник:\
#head_mentor.degree
#linebreak()
Робота захищена на оцінку
#linebreak()
Комісія:\
#for mentor in subject.mentors {
[#mentor.degree\
]
}
#colbreak()
#set align(left)
#linebreak()
#author.name
#linebreak()
#linebreak()
#head_mentor.name
#linebreak()
#underline(" " * 35)
#linebreak()
#linebreak()
#for mentor in subject.mentors {
[#mentor.name\
]
}
]
#vfill()
Харків -- #datetime.today().display("[year]")
#pagebreak()
]
// }}}
// page 2 {{{
uline([Харківський національний університет радіоелектроніки])
linebreak()
linebreak()
grid(
columns: (100pt, 1fr),
bold([
Кафедра
Дисципліна
Спеціальність
]),
{
uline(align: left, department)
linebreak()
uline(align: left, subject.name)
linebreak()
uline(align: left, [#edu_program.number #edu_program.name])
},
)
grid(
columns: (1fr, 1fr, 1fr),
gutter: 0.3fr,
[#bold("Курс") #uline(2)], [#bold("Група") #uline(author.group)], [#bold("Семестр") #uline(3)],
)
linebreak()
linebreak()
linebreak()
align(center)[#bold([
ЗАВДАННЯ
на курсову роботу студента
])]
linebreak()
uline(align: left)[#emph(author.full_name_gen)]
linebreak()
linebreak()
bold([ #"1. "Тема роботи: ])
uline([#title.])
linebreak()
{
bold([ #"2. "Строк здачі закінченої роботи: ])
uline(task_list.done_date.display("[day].[month].[year]"))
hfill(10fr)
}
linebreak()
bold([ #"3. "Вихідні дані для роботи: ])
uline(task_list.source)
linebreak()
bold([ #"4. "Зміст розрахунково-пояснювальної записки: ])
uline(task_list.content)
linebreak()
bold([ #"5. "Перелік графічного матеріалу: ])
uline(task_list.graphics)
linebreak()
{
bold([ #"6. "Строк здачі закінченої роботи: ])
uline(task_list.done_date.display("[day].[month].[year]"))
hfill(10fr)
}
pagebreak()
// }}}
// page 3 {{{
heading(numbering: none, depth: 1, [Календарний план])
calendar_plan.plan_table
linebreak()
grid(
columns: (6fr, 5fr),
grid(
columns: (1fr, 3fr, 1fr),
gutter: 0.2fr,
[
Студент
#linebreak()
Керівник
#linebreak()
#align(center, ["#calendar_plan.approval_date.day()"])
],
[
#uline(align: center, [])
#linebreak()
#uline(align: center, [])
#linebreak()
#uline(align: center, month_gen(calendar_plan.approval_date.month()))
],
{
linebreak()
linebreak()
[#calendar_plan.approval_date.year() р.]
},
),
[
#author.name,
#linebreak()
#head_mentor.degree
#head_mentor.name.
],
)
pagebreak()
// }}}
// page 4 {{{
[
#heading(numbering: none, depth: 1, [Реферат])
Пояснювальна записка до курсової роботи:
#context [ #counter(page).final().at(0) ] с.,
#context [ #counter("table").final().at(0) ] табл.,
#context [ #counter("image").final().at(0) ] рис.,
#context [ #counter(bibliography).final().at(0) ] джерел.
#linebreak()
#{
let keywords = abstract.keywords
let is_cyrillic = word => word
.split("")
.any(char => ("А" <= char and char <= "я") or char == "Ё" or char == "ё")
let n = keywords.len()
for i in range(n) {
for j in range(0, n - i - 1) {
if (
(not is_cyrillic(keywords.at(j)) and is_cyrillic(keywords.at(j + 1)))
or (
is_cyrillic(keywords.at(j)) == is_cyrillic(keywords.at(j + 1)) and keywords.at(j) > keywords.at(j + 1)
)
) {
(keywords.at(j), keywords.at(j + 1)) = (keywords.at(j + 1), keywords.at(j))
}
}
}
keywords.join(", ")
}
#linebreak()
#abstract.text
]
// }}}
}
if include_toc {
outline(
title: [
ЗМІСТ
#v(spacing * 2)
],
depth: 2,
indent: auto,
)
}
doc
}
#show: conf.with(
authors: (
(name: "Ситник Є. С.", full_name_gen: "Ситника Єгора Сергійовича", variant: 15, group: "ПЗПІ-23-2", gender: "m"),
),
title: "Інформаційна система «Помічник класного керівника». Керування класом",
include_toc: true,
doctype: "КУ",
subject_shorthand: "БД",
)