migration
5
semester-4/СмП/pz-1/README.md
Normal file
@ -0,0 +1,5 @@
|
||||
Викладач: Сокорчук І. П.
|
||||
Оцінка: 100
|
||||
|
||||
Додатково:
|
||||
Йому дуже не сподобався мій підхід із printf, звинувачував, що це написав ШІ, тому змусив виконувати додаткове завдання на демку.
|
BIN
semester-4/СмП/pz-1/img/1.png
Normal file
After Width: | Height: | Size: 50 KiB |
BIN
semester-4/СмП/pz-1/img/2.png
Normal file
After Width: | Height: | Size: 25 KiB |
BIN
semester-4/СмП/pz-1/img/3.png
Normal file
After Width: | Height: | Size: 17 KiB |
BIN
semester-4/СмП/pz-1/img/4.png
Normal file
After Width: | Height: | Size: 8.3 KiB |
BIN
semester-4/СмП/pz-1/img/5.png
Normal file
After Width: | Height: | Size: 4.7 KiB |
BIN
semester-4/СмП/pz-1/img/6.png
Normal file
After Width: | Height: | Size: 75 KiB |
BIN
semester-4/СмП/pz-1/img/7.png
Normal file
After Width: | Height: | Size: 511 KiB |
BIN
semester-4/СмП/pz-1/img/8.png
Normal file
After Width: | Height: | Size: 10 KiB |
BIN
semester-4/СмП/pz-1/img/9.png
Normal file
After Width: | Height: | Size: 520 KiB |
692
semester-4/СмП/pz-1/lib.typ
Normal file
@ -0,0 +1,692 @@
|
||||
|
||||
// Academic aliases {{{1
|
||||
|
||||
/// subject abbreviations to full names
|
||||
#let subjects = (
|
||||
"БД": "Бази даних",
|
||||
"ОПНJ": "Основи програмування на Java",
|
||||
"ОС": "Операційні системи",
|
||||
"ПП": "Проектний практикум",
|
||||
"СМП": "Скриптові мови програмування",
|
||||
"Ф": "Філософія",
|
||||
)
|
||||
|
||||
/// education program abbreviations to name & number
|
||||
#let edu_programs = (
|
||||
"ПЗПІ": (
|
||||
name: "Інженерія програмного забезпечення",
|
||||
number: 121, // TODO: ПЗПІ is "F2" now
|
||||
),
|
||||
)
|
||||
|
||||
// Template formatting functions {{{1
|
||||
|
||||
/// numberless heading
|
||||
#let nheading(title) = heading(depth: 1, numbering: none, title)
|
||||
|
||||
/// fill horizontal space with a box and not an empty space
|
||||
#let hfill(width) = box(width: width, repeat(" ")) // NOTE: This is a HAIR SPACE (U+200A), not a regular space
|
||||
|
||||
/// make underlined cell with filled value
|
||||
#let uline(align: center, content) = underline[
|
||||
#if align != left { hfill(1fr) }
|
||||
#content
|
||||
#if align != right { hfill(1fr) }
|
||||
]
|
||||
|
||||
/// bold text
|
||||
#let bold(content) = text(weight: "bold")[#content]
|
||||
|
||||
/// month name from its number
|
||||
#let month_gen(month) = (
|
||||
"січня",
|
||||
"лютого",
|
||||
"березня",
|
||||
"квітня",
|
||||
"травня",
|
||||
"червня",
|
||||
"липня",
|
||||
"серпня",
|
||||
"вересня",
|
||||
"жовтня",
|
||||
"листопада",
|
||||
"грудня",
|
||||
).at(month - 1)
|
||||
|
||||
// Helper functions {{{1
|
||||
|
||||
/// captioned image with label derived from path:
|
||||
/// - "image.png" = @image
|
||||
/// - "img/image.png" = @image
|
||||
/// - "img/foo/image.png" = @foo_image
|
||||
/// - "img/foo/foo_image.png" = @foo_image
|
||||
/// the caption will be modified based on a conditional positional value:
|
||||
/// - `none`: no change
|
||||
/// - some value: "`caption` (за даними `value`)"
|
||||
/// - no value: "`caption` (рисунок виконано самостійно)"
|
||||
/// additional named arguments will be passed to original `image` function
|
||||
#let img(path, caption, ..sink) = {
|
||||
let parts = path.split(".").first().split("/")
|
||||
|
||||
let label_string = if parts.len() <= 2 or parts.at(-1).starts-with(parts.at(-2)) {
|
||||
// ("image",), (_, "image") and (.., "img", "img_image")
|
||||
parts.last()
|
||||
} else {
|
||||
// (.., "img", "image") = "img_image"
|
||||
parts.at(-2) + "_" + parts.at(-1)
|
||||
}.replace(" ", "_")
|
||||
|
||||
let caption = if sink.pos().len() == 0 {
|
||||
caption + " (рисунок виконано самостійно)"
|
||||
} else if sink.pos().first() == none {
|
||||
caption
|
||||
} else {
|
||||
[#caption (за даними #sink.pos().first())]
|
||||
}
|
||||
|
||||
[#figure(image(path, ..sink.named()), caption: caption) #label(label_string)]
|
||||
}
|
||||
|
||||
// Styling {{{1
|
||||
/// NOTE: may be wrong
|
||||
#let ua_alpha_numbering = "абвгдежиклмнпрстуфхцшщюя".split("") // 0 = "", 1 = "а"
|
||||
|
||||
// general outlook {{{2
|
||||
// spacing between lines
|
||||
#let spacing = 0.95em
|
||||
|
||||
#let style(it) = {
|
||||
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", "Liberation Serif"), size: 14pt, hyphenate: false, lang: "uk")
|
||||
set par(justify: true, first-line-indent: (amount: 1.25cm, all: true))
|
||||
set underline(evade: false)
|
||||
|
||||
// set 1.5 line spacing
|
||||
set block(spacing: spacing)
|
||||
set par(spacing: spacing)
|
||||
set par(leading: spacing)
|
||||
|
||||
// enums and lists {{{2
|
||||
set enum(numbering: i => { ua_alpha_numbering.at(i) + ")" }, 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 {{{2
|
||||
show figure: it => {
|
||||
v(spacing * 2, weak: true)
|
||||
it
|
||||
v(spacing * 2, weak: true)
|
||||
}
|
||||
|
||||
set figure.caption(separator: [ -- ])
|
||||
show figure.where(kind: table): set figure.caption(position: top)
|
||||
show figure.caption.where(kind: table): set align(left)
|
||||
|
||||
// figure numbering
|
||||
show heading.where(level: 1): it => {
|
||||
counter(math.equation).update(0)
|
||||
counter(figure.where(kind: image)).update(0)
|
||||
counter(figure.where(kind: table)).update(0)
|
||||
counter(figure.where(kind: raw)).update(0)
|
||||
it
|
||||
}
|
||||
set math.equation(numbering: (..num) => numbering("(1.1)", counter(heading).get().at(0), num.pos().first()))
|
||||
set figure(numbering: (..num) => numbering("1.1", counter(heading).get().at(0), num.pos().first()))
|
||||
|
||||
// appearance of references to images and tables {{{2
|
||||
set ref(
|
||||
supplement: it => {
|
||||
if it == none or not it.has("kind") {
|
||||
it
|
||||
} else if it.kind == image {
|
||||
"див. рис."
|
||||
} else if it.kind == table {
|
||||
"див. таблицю"
|
||||
} else {
|
||||
it
|
||||
}
|
||||
},
|
||||
)
|
||||
show ref: it => {
|
||||
let el = it.element
|
||||
|
||||
if el == none or not el.has("kind") {
|
||||
return it
|
||||
}
|
||||
if el.kind != image and el.kind != table {
|
||||
return it
|
||||
}
|
||||
|
||||
[(#it)]
|
||||
}
|
||||
|
||||
// headings {{{2
|
||||
set heading(numbering: "1.1")
|
||||
|
||||
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 heading.where(level: 3): 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)
|
||||
}
|
||||
|
||||
it
|
||||
}
|
||||
|
||||
// Coursework template {{{1
|
||||
|
||||
/// DSTU 3008:2015 Template for NURE
|
||||
/// -> content
|
||||
/// - doc (content): Content to apply the template to.
|
||||
/// - title (str): Title of the document.
|
||||
/// - subject_shorthand (str): Subject short name.
|
||||
/// - department_gen (str): Department name in genitive form.
|
||||
/// - authors ((name: str, full_name_gen: str, variant: int, group: str, gender: str),): List of Authors dicts.
|
||||
/// - mentors ((name: str, gender: str, degree: str),): List of mentors dicts.
|
||||
/// - edu_program_shorthand (str): Education program shorthand.
|
||||
/// - task_list (done_date: datetime, initial_date: datetime, source: (content | str), content: (content | str), graphics: (content | str)): Task list object.
|
||||
/// - calendar_plan ( plan_table: (content | str), approval_date: datetime): Calendar plan object.
|
||||
/// - abstract (keywords: (str, ), text: (content | str)): Abstract object.
|
||||
/// - bib_path path: Path to the bibliography yaml file.
|
||||
/// - appendices (content): Content with appendices.
|
||||
#let cw-template(
|
||||
doc,
|
||||
title: "NONE",
|
||||
subject_shorthand: "NONE",
|
||||
department_gen: "Програмної інженерії",
|
||||
author: (),
|
||||
mentors: (),
|
||||
edu_program_shorthand: "ПЗПІ",
|
||||
task_list: (),
|
||||
calendar_plan: (),
|
||||
abstract: (),
|
||||
bib_path: "bibl.yml",
|
||||
appendices: (),
|
||||
) = {
|
||||
set document(title: title, author: author.name)
|
||||
|
||||
show: style
|
||||
|
||||
let bib-count = state("citation-counter", ())
|
||||
show cite: it => {
|
||||
it
|
||||
bib-count.update(((..c)) => (..c, it.key))
|
||||
}
|
||||
show bibliography: it => {
|
||||
set text(size: 0pt)
|
||||
it
|
||||
}
|
||||
|
||||
|
||||
let head_mentor = mentors.at(0)
|
||||
let edu_program = edu_programs.at(edu_program_shorthand)
|
||||
|
||||
// page 1 {{{2
|
||||
[
|
||||
#set align(center)
|
||||
МІНІСТЕРСТВО ОСВІТИ І НАУКИ УКРАЇНИ
|
||||
|
||||
ХАРКІВСЬКИЙ НАЦІОНАЛЬНИЙ УНІВЕРСИТЕТ РАДІОЕЛЕКТРОНІКИ
|
||||
|
||||
\
|
||||
|
||||
Кафедра Програмної інженерії
|
||||
|
||||
\
|
||||
|
||||
ПОЯСНЮВАЛЬНА ЗАПИСКА
|
||||
|
||||
ДО КУРСОВОЇ РОБОТИ
|
||||
|
||||
з дисципліни: "#subjects.at(subject_shorthand, default: "NONE")"
|
||||
|
||||
Тема роботи: "#title"
|
||||
|
||||
\ \ \
|
||||
|
||||
#columns(2, gutter: 4cm)[
|
||||
#set align(left)
|
||||
|
||||
#if author.gender == "m" { [Виконав\ ] } else { [Виконала\ ] } ст. гр. #author.group
|
||||
|
||||
\
|
||||
Керівник:\
|
||||
#head_mentor.degree
|
||||
|
||||
\
|
||||
Робота захищена на оцінку
|
||||
|
||||
\
|
||||
Комісія:\
|
||||
#for mentor in mentors {
|
||||
[#mentor.degree\
|
||||
]
|
||||
}
|
||||
|
||||
#colbreak()
|
||||
#set align(left)
|
||||
|
||||
\
|
||||
#author.name
|
||||
|
||||
\ \
|
||||
#head_mentor.name
|
||||
|
||||
\
|
||||
#underline(" " * 35)
|
||||
|
||||
\ \
|
||||
#for mentor in mentors {
|
||||
[#mentor.name\
|
||||
]
|
||||
}
|
||||
]
|
||||
|
||||
#v(1fr)
|
||||
|
||||
Харків -- #task_list.done_date.display("[year]")
|
||||
|
||||
#pagebreak()
|
||||
]
|
||||
|
||||
// page 2 {{{2
|
||||
{
|
||||
uline[Харківський національний університет радіоелектроніки]
|
||||
|
||||
linebreak()
|
||||
linebreak()
|
||||
|
||||
grid(
|
||||
columns: (100pt, 1fr),
|
||||
bold[
|
||||
Кафедра
|
||||
Дисципліна
|
||||
Спеціальність
|
||||
],
|
||||
{
|
||||
uline(align: left, department_gen)
|
||||
linebreak()
|
||||
uline(align: left, subjects.at(subject_shorthand))
|
||||
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)[_#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.initial_date.display("[day].[month].[year]"))
|
||||
hfill(10fr)
|
||||
}
|
||||
|
||||
pagebreak()
|
||||
}
|
||||
|
||||
// page 3 {{{2
|
||||
{
|
||||
align(center, bold[КАЛЕНДАРНИЙ ПЛАН])
|
||||
set par(first-line-indent: 0pt)
|
||||
|
||||
linebreak()
|
||||
|
||||
calendar_plan.plan_table
|
||||
|
||||
linebreak()
|
||||
|
||||
grid(
|
||||
columns: (5fr, 5fr),
|
||||
grid(
|
||||
columns: (1fr, 2fr, 1fr),
|
||||
gutter: 0.2fr,
|
||||
[
|
||||
Студент \
|
||||
Керівник \
|
||||
#align(center)["#underline[#calendar_plan.approval_date.day()]"]
|
||||
],
|
||||
[
|
||||
#uline(align: center, []) \
|
||||
#uline(align: center, []) \
|
||||
#uline(align: center, month_gen(calendar_plan.approval_date.month()))
|
||||
],
|
||||
[
|
||||
\ \
|
||||
#underline[#calendar_plan.approval_date.year()] р.
|
||||
],
|
||||
),
|
||||
[
|
||||
#author.name, \
|
||||
#head_mentor.degree
|
||||
#head_mentor.name.
|
||||
],
|
||||
)
|
||||
|
||||
pagebreak()
|
||||
}
|
||||
|
||||
// page 4 {{{2
|
||||
[
|
||||
#align(center, bold[РЕФЕРАТ]) \
|
||||
|
||||
#context [
|
||||
#let pages = counter(page).final().at(0)
|
||||
#let images = query(figure.where(kind: image)).len()
|
||||
#let tables = query(figure.where(kind: table)).len()
|
||||
#let bibs = bib-count.final().dedup().len()
|
||||
/* TODO: why this stopped working?
|
||||
#let tables = counter(figure.where(kind: table)).final().at(0)
|
||||
#let images = counter(figure.where(kind: image)).final().at(0)*/
|
||||
|
||||
#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(", ").
|
||||
]
|
||||
|
||||
\
|
||||
|
||||
#{
|
||||
let keywords = abstract.keywords.map(upper)
|
||||
let is_cyrillic = word => word.split("").any(char => ("А" <= char and 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(", ")
|
||||
}
|
||||
|
||||
\
|
||||
|
||||
#abstract.text
|
||||
]
|
||||
|
||||
// page 5 {{{2
|
||||
outline(
|
||||
title: [
|
||||
ЗМІСТ
|
||||
#v(spacing * 2, weak: true)
|
||||
],
|
||||
depth: 2,
|
||||
indent: auto,
|
||||
)
|
||||
|
||||
doc
|
||||
|
||||
// bibliography {{{2
|
||||
{
|
||||
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 AN ISSUE
|
||||
]
|
||||
}
|
||||
|
||||
show enum.item: it => {
|
||||
set par(first-line-indent: 0pt)
|
||||
box(width: 1.25cm)
|
||||
box(width: 1em + 0.5cm)[#it.number.]
|
||||
it.body
|
||||
linebreak()
|
||||
}
|
||||
|
||||
context {
|
||||
for (i, citation) in query(ref.where(element: none)).map(r => str(r.target)).dedup().enumerate() {
|
||||
enum.item(
|
||||
i + 1,
|
||||
format-entry(bib_data.at(citation)),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// appendices {{{2
|
||||
{
|
||||
counter(heading).update(0)
|
||||
|
||||
set heading(
|
||||
numbering: (i, ..nums) => {
|
||||
let char = upper(ua_alpha_numbering.at(i))
|
||||
if nums.pos().len() == 0 { char } else {
|
||||
char + "." + nums.pos().map(str).join(".")
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
show heading.where(level: 1): it => {
|
||||
set align(center)
|
||||
set text(size: 14pt, weight: "regular")
|
||||
|
||||
pagebreak(weak: true)
|
||||
bold[ДОДАТОК #counter(heading).display(it.numbering)]
|
||||
linebreak()
|
||||
it.body
|
||||
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)
|
||||
}
|
||||
|
||||
appendices
|
||||
}
|
||||
}
|
||||
|
||||
// Laboratory work template {{{1
|
||||
|
||||
/// 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 short name.
|
||||
/// - department_gen (str): Department name in genitive form.
|
||||
/// - 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.
|
||||
/// - mentor (name: str, gender: str, degree: str): Mentors objects.
|
||||
#let lab-pz-template(
|
||||
doc,
|
||||
doctype: "NONE",
|
||||
title: "NONE",
|
||||
subject_shorthand: "NONE",
|
||||
department_gen: "Програмної інженерії",
|
||||
worknumber: 1,
|
||||
authors: (),
|
||||
mentor: (),
|
||||
) = {
|
||||
set document(title: title, author: authors.at(0).name)
|
||||
|
||||
show: style
|
||||
|
||||
context counter(heading).update(worknumber - 1)
|
||||
|
||||
// page 1 {{{2
|
||||
align(center)[
|
||||
МІНІСТЕРСТВО ОСВІТИ І НАУКИ УКРАЇНИ \
|
||||
ХАРКІВСЬКИЙ НАЦІОНАЛЬНИЙ УНІВЕРСИТЕТ РАДІОЕЛЕКТРОНІКИ
|
||||
|
||||
\ \
|
||||
Кафедра #department_gen
|
||||
|
||||
\ \ \
|
||||
Звіт \
|
||||
з
|
||||
#if doctype == "ЛБ" [лабораторної роботи] else [практичної роботи]
|
||||
#if worknumber != none [№ #worknumber]
|
||||
|
||||
з дисципліни: "#subjects.at(subject_shorthand, default: "UNKNOWN SUBJECT, PLEASE OPEN AN ISSUE")"
|
||||
|
||||
з теми: "#title"
|
||||
|
||||
\ \ \ \
|
||||
|
||||
#columns(2)[
|
||||
#set align(left)
|
||||
#set par(first-line-indent: 0pt)
|
||||
#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]")
|
||||
]
|
||||
|
||||
pagebreak(weak: true)
|
||||
|
||||
heading(title)
|
||||
doc
|
||||
}
|
||||
|
||||
// vim:sts=2:sw=2:fdl=0:fdm=marker:cms=/*%s*/
|
334
semester-4/СмП/pz-1/Пр_1_Ситник_ПЗПІ_23_2.typ
Normal file
@ -0,0 +1,334 @@
|
||||
#import "lib.typ": *
|
||||
|
||||
#set raw(tab-size: 8)
|
||||
#show raw.where(block: true): code => {
|
||||
// set text(11pt, font: "Caladea", top-edge: 1em, bottom-edge: 0em)
|
||||
set text(11pt, top-edge: 1em, bottom-edge: 0em)
|
||||
set par(leading: 0.17em)
|
||||
|
||||
grid(
|
||||
columns: (auto, auto),
|
||||
column-gutter: 1em,
|
||||
row-gutter: 0.17em,
|
||||
align: (right, raw.align),
|
||||
..for line in code.lines {
|
||||
(
|
||||
text(fill: gray)[#line.number],
|
||||
{
|
||||
set text(weight: "semibold")
|
||||
line.body
|
||||
},
|
||||
)
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
#show: lab-pz-template.with(
|
||||
doctype: "ПЗ",
|
||||
title: "Розробка інженерного рішення для друкування ялинки мовою Bash (Bourne again shell) з використанням циклів та галужень",
|
||||
subject_shorthand: "СМП",
|
||||
department_gen: "Програмної інженерії",
|
||||
authors: (
|
||||
(
|
||||
name: "Ситник Є. С.",
|
||||
full_name_gen: "Ситника Єгора Сергійовича",
|
||||
group: "ПЗПІ-23-2",
|
||||
gender: "m",
|
||||
variant: none,
|
||||
),
|
||||
),
|
||||
mentor: (
|
||||
name: "Сокорчук І. П.",
|
||||
gender: "m",
|
||||
degree: "ст. викл. каф. ПІ",
|
||||
),
|
||||
worknumber: 1,
|
||||
)
|
||||
|
||||
#v(-spacing)
|
||||
|
||||
== Мета роботи
|
||||
Мета даної практичної роботи - здобути навички створення сценаріїв автоматизації засобами Bash (Bourne again shell).
|
||||
|
||||
|
||||
== Хід роботи
|
||||
|
||||
Метою роботи є створення сценарію мовою Bash, що друкуватиме на екрані ялинку із двох ярусів гілок, стовбура та шару снігу.
|
||||
|
||||
=== До сценарію є наступні вимоги:
|
||||
+ ялинка повинна бути симетричною;
|
||||
+ яруси гілок повинні утворювати правильні рівнобедрені трикутники у яких сусідні рядки відрізняються на два символи та складаються почергово або з символів "\*" або з символів "\#";
|
||||
+ ширина усіх ярусів гілок повинна бути на 2 символи вужча ніж ширина снігу;
|
||||
+ висота стовбура повинна бути 2 рядки, а ширина 3 стовбці;
|
||||
+ висота шару снігу повинна бути 1 рядок;
|
||||
+ висота ялинки у рядках разом з шаром снігу та ширина шару снігу в символах вказується сценарію в його параметрах при запуску;
|
||||
+ параметри сценарію повинні мати додатнє значення;
|
||||
+ вказані значення повинні округлятися до потрібних у меншу сторону;
|
||||
+ якщо за вказаними при запуску сценарія значеннями зобразити ялинку на екрані неможливо, скрипт повинен вивести у потік помилок сповіщення про неправильне значення аргумента і повернути відповідний результат у батьківський процес;
|
||||
+ у сценарії потрібно обов'язково використати функцію та такі конструкції;
|
||||
+ `if ... then ... fi`
|
||||
+ `while ... do ... done`
|
||||
+ `until ... do ... done`
|
||||
+ `for ... in .. do ... done`
|
||||
+ `for ((...)); do ... done`
|
||||
+ файл сценарію повинен бути виконуваним файлом для усіх користувачів системи;
|
||||
+ право редагувати файл сценарію повинен мати лише власник.
|
||||
|
||||
=== Створимо сценарій дотримуючись вимог
|
||||
|
||||
Сценарій створений мовою Bash для більшої зручності використання повинен містити Шебанг (від англійського "shebang") -- послідовність із двох символів: «решітки» та знака оклику (\#!), за якими слідує шлях, до програми інтерпретатору.
|
||||
Така послідовність використовується виконувачем програм (зазвичай командною оболонкою на кшталт Bash) для ідентифікації та виконання сценарію як програми.
|
||||
Виконувач програм запустить програму, шлях до якої вказано в першому рядку, передавши в якості аргументу шлях до файлу сценарію.
|
||||
Почнемо сценарій із Шебангу, в якості шляху до інтерпретатора вкажемо "/bin/bash"
|
||||
|
||||
```
|
||||
#!/bin/bash
|
||||
```
|
||||
|
||||
Перш за все сценарій має виконати валідацію та нормалізацію аргументів. Серед вимог до сценарію можемо виділити такі обмеження аргументів:
|
||||
+ кількість аргументів має дорівнювати 2;
|
||||
+ мінімальна висота ялинки складає 8 рядків. А саме -- 1 рядок для шару снігу, 2 рядки для стовбура, 1 рядок для верхівки та 4 рядків для гілок (по 2 на кожен з ярусів);
|
||||
+ мінімальна ширина шару снігу складає 7 символів -- це на 2 більше, ніж ширина гілок найменшої можливої ялинки.
|
||||
+ ширина снігу після нормалізації значень аргументів повинна бути більшою за ширину гілок ялинки рівно на 2 символи.
|
||||
|
||||
Виконаємо перевірку кількості аргументів за допомогою змінної "\$\#". Висота має передаватись в якості першого аргументу, а ширина -- другого, перевіримо значення цих аргументів за допомогою змінних "\$1" та "\$2". Нормалізуємо отримані значення. Значення висоти повинно бути парним, тому якщо користувач передав непарне значення його необхідно нормалізувати віднявши 1. Значення ширини повинно бути непарним, його так само необхідно зменшити на 1 в іншому випадку. Щоб перевірити чи відповідає ширина шару снігу допустимій за вказаної висоти нам необхідно знати ширину гілок ялинки, її можна обрахувати за формулою "2 \* висота ярусу - 1", дізнатись висоту одного ярусу ми можемо розділивши загальну висоту на 2 та віднявши від результату одиницю, оскільки ширина гілок знадобиться нам під час малювання ялинки збережемо її у змінну.
|
||||
|
||||
Якщо під час перевірок було визначено, що аргументи не задовольняють умовам сценарій має завершитись із не нульовим кодом та повідомити про помилки у стандартний потік помилок. Для завершення сценарію використаємо ключове слово "exit", а для друку помилок використаємо команду "echo", перенаправивши її вивід за допомогою оператору перенаправлення.
|
||||
|
||||
```
|
||||
if (( $# != 2 )); then echo "Not enough/Too many arguments" >&2; exit 1; fi
|
||||
if (( $1 < 8 )); then echo "Height must be > 7" >&2; exit 2; fi
|
||||
if (( $2 < 7 )); then echo "Width must be > 6" >&2; exit 3; fi
|
||||
|
||||
h=$(( ($1 % 2 != 0) ? $1 - 1 : $1 ))
|
||||
w=$(( ($2 % 2 == 0) ? $2 - 1 : $2 ))
|
||||
lh=$(( $h/2 - 1 ))
|
||||
|
||||
if (( ($w-(2*$lh-1) != 2) )); then echo "Can't draw the tree with provided arguments" >&2; exit 4; fi
|
||||
```
|
||||
// #figure(image("img/1.png"), caption: "Валідація та нормалізація аргументів") <1>
|
||||
|
||||
Гілки ялинки мають складатись з 2 символів, тому створимо змінну в якій будемо зберігати поточний символ.
|
||||
|
||||
Оголосимо функцію для малювання ярусу гілок ялинки. Ця функція буде малювати ярус ялинки рядок за рядком чергуючи символи. Створимо цикл який буде виконуватись стільки разів, скільки рядків необхідно для малювання одного ярусу ялинки, починаючи з 1 або 2 рядка в залежності від ярусу, що малюється. Використаємо раніше оголошену змінну, що зберігає висоту ярусу. Кожен рядок повинен починатись із відступу, для обчислення цього відступу необхідно від ширини шару снігу відняти ширину поточного рядка, та поділити результат на 2. Скористаємося можливістю команди "printf" друкувати текст вміщуючи його в вікно з пробілів визначеної довжини. Рядок із символів ми можемо надрукувати так само використавши "printf", але замінивши символи пробілів, на символи, що відповідають поточному шару. Для цього скористаємося командою "tr". Наприкінці кола циклу змінимо символ на протилежний.
|
||||
|
||||
```
|
||||
ch='*'
|
||||
|
||||
draw_layer() {
|
||||
for ((i=$1; i <= lh; i++)); do
|
||||
printf "%*s" "$(( (w - (2*i-1)) / 2 ))"
|
||||
printf "%*s\n" "$(( 2*i-1 ))" | tr ' ' "$ch"
|
||||
|
||||
ch=$( [ "$ch" = '*' ] && echo '#' || echo '*' )
|
||||
done
|
||||
}
|
||||
```
|
||||
// #figure(image("img/2.png"), caption: "Функція друку ярусу ялинки") <2>
|
||||
|
||||
Стовбур ялинки завжди має однаковий вигляд, відрізняється лише відступ від початку рядку. Оголосимо функцію друку стовбура ялинки, скориставшись методами, що були використані для друку ярусів гілок.
|
||||
|
||||
```
|
||||
draw_stem() {
|
||||
for i in {0..1}; do
|
||||
printf "%$(( (h - 3) / 2 ))s"
|
||||
printf "%3s\n" | tr ' ' "#"
|
||||
done
|
||||
}
|
||||
```
|
||||
// #figure(image("img/3.png"), caption: "Функція друку стовбуру ялинки") <3>
|
||||
|
||||
Шар снігу не має відступу, надрукувати його найпростіше. Оголосимо функцію друку шару снігу, скориставшись методами, що були використані в попередніх функціях.
|
||||
|
||||
```
|
||||
draw_snow() {
|
||||
printf "%${w}s\n" | tr ' ' "*"
|
||||
}
|
||||
```
|
||||
// #figure(image("img/4.png"), caption: "Функція друку шару снігу") <4>
|
||||
|
||||
Викличемо створені функції для друку ялинки.
|
||||
|
||||
```
|
||||
draw_layer 1
|
||||
draw_layer 2
|
||||
draw_stem
|
||||
draw_snow
|
||||
```
|
||||
// #figure(image("img/5.png"), caption: "Виклик створених функцій для друку ялинки") <5>
|
||||
|
||||
Перевіримо створений сценарій за допомогою заготованої програми перевірки. Перед цим додавши дозвіл на виконання розробленому сценарію за допомогою команди "chmod".
|
||||
|
||||
```
|
||||
Обліковий запис: pzpi-23-2-sytnyk-yehor
|
||||
Скрипт: ./pzpi-23-2-sytnyk-yehor-task1
|
||||
---
|
||||
Перевірка встановлених прав доступу до файла скрипта (-rwxr-xr-x див. завдання):
|
||||
ПЕРЕВІРЕНО!
|
||||
---
|
||||
Перевірка розміру файла скрипта:
|
||||
ПЕРЕВІРЕНО!
|
||||
---
|
||||
Перевірка першого рядка Bash скрипта:
|
||||
ПЕРЕВІРЕНО! Перший рядок скрипта: #!/bin/bash
|
||||
---
|
||||
Перевірка загального синтаксису скрипта:
|
||||
ПЕРЕВІРЕНО!
|
||||
---
|
||||
Перевірка використаних синтаксичних констркцій Bash:
|
||||
ПЕРЕВІРЕНО! Конструкція: if ...; then ...; fi
|
||||
ПОМИЛКА! Немає: while ...; do ...; done
|
||||
ПОМИЛКА! Немає: until ...; do ...; done
|
||||
ПЕРЕВІРЕНО! Конструкція: for ... in ...; do ...; done
|
||||
ПЕРЕВІРЕНО! Конструкція: for ((...)); do ... done
|
||||
ПЕРЕВІРЕНО! Конструкція: function ...() { ... }
|
||||
---
|
||||
Перевірка скрипта статичним аналізатором коду:
|
||||
ПЕРЕВІРЕНО!
|
||||
---
|
||||
Перевірка роботи скрипта (приклад див.: pzpi23-task1_example):
|
||||
---
|
||||
РЕЗУЛЬТАТИ ПЕРЕВІРКИ
|
||||
УСПІШНИХ ТЕСТІВ: 12
|
||||
НЕВДАЛИХ ТЕСТІВ: 2
|
||||
ПРАВИЛЬНИЙ STDOUT: 625
|
||||
НЕПРАВИЛЬНИЙ STDOUT: 0
|
||||
ПРАВИЛЬНИЙ EXIT_CODE: 625
|
||||
НЕПРАВИЛЬНИЙ EXIT_CODE: 0
|
||||
ПРАВИЛЬНИЙ STDERR: 625
|
||||
НЕПРАВИЛЬНИЙ STDERR: 0
|
||||
---
|
||||
ОЦІНКА ЗА КОД: 94 ( ./pzpi-23-2-sytnyk-yehor-task1 без перевірки на плагіат )
|
||||
---
|
||||
```
|
||||
// #figure(image("img/6.png"), caption: "Зміна дозволів файлу сценарію") <6>
|
||||
// #figure(image("img/7.png", width: 80%), caption: "Перевірки роботи створеного сценарію") <7>
|
||||
|
||||
Можемо побачити, що створений сценарій проходить усі тести, однак містить не всі конструкції циклів, необхідні за умовою. Додамо ці конструкції окремо від друку ялинки, щоб система перевірки змогла їх розпізнати.
|
||||
|
||||
```
|
||||
i=0
|
||||
while [[ $i -ne 1 ]]; do i=$((i+1)); done
|
||||
until [[ $i -eq 2 ]]; do i=$((i+1)); done
|
||||
```
|
||||
// #figure(image("img/8.png", width: 80%), caption: "Кунструкції циклів, що не були використані під час друку ялинки") <8>
|
||||
|
||||
Проведемо повторні тести після додавання конструкцій циклів.
|
||||
|
||||
```
|
||||
Обліковий запис: pzpi-23-2-sytnyk-yehor
|
||||
Скрипт: ./pzpi-23-2-sytnyk-yehor-task1
|
||||
---
|
||||
Перевірка встановлених прав доступу до файла скрипта (-rwxr-xr-x див. завдання):
|
||||
ПЕРЕВІРЕНО!
|
||||
---
|
||||
Перевірка розміру файла скрипта:
|
||||
ПЕРЕВІРЕНО!
|
||||
---
|
||||
Перевірка першого рядка Bash скрипта:
|
||||
ПЕРЕВІРЕНО! Перший рядок скрипта: #!/bin/bash
|
||||
---
|
||||
Перевірка загального синтаксису скрипта:
|
||||
ПЕРЕВІРЕНО!
|
||||
---
|
||||
Перевірка використаних синтаксичних констркцій Bash:
|
||||
ПЕРЕВІРЕНО! Конструкція: if ...; then ...; fi
|
||||
ПЕРЕВІРЕНО! Конструкція: while ...; do ...; done
|
||||
ПЕРЕВІРЕНО! Конструкція: until ...; do ...; done
|
||||
ПЕРЕВІРЕНО! Конструкція: for ... in ...; do ...; done
|
||||
ПЕРЕВІРЕНО! Конструкція: for ((...)); do ... done
|
||||
ПЕРЕВІРЕНО! Конструкція: function ...() { ... }
|
||||
---
|
||||
Перевірка скрипта статичним аналізатором коду:
|
||||
ПЕРЕВІРЕНО!
|
||||
---
|
||||
Перевірка роботи скрипта (приклад див.: pzpi23-task1_example):
|
||||
---
|
||||
РЕЗУЛЬТАТИ ПЕРЕВІРКИ
|
||||
УСПІШНИХ ТЕСТІВ: 14
|
||||
НЕВДАЛИХ ТЕСТІВ: 0
|
||||
ПРАВИЛЬНИЙ STDOUT: 625
|
||||
НЕПРАВИЛЬНИЙ STDOUT: 0
|
||||
ПРАВИЛЬНИЙ EXIT_CODE: 625
|
||||
НЕПРАВИЛЬНИЙ EXIT_CODE: 0
|
||||
ПРАВИЛЬНИЙ STDERR: 625
|
||||
НЕПРАВИЛЬНИЙ STDERR: 0
|
||||
---
|
||||
ОЦІНКА ЗА КОД: 100 ( ./pzpi-23-2-sytnyk-yehor-task1 без перевірки на плагіат )
|
||||
---
|
||||
```
|
||||
// #figure(image("img/9.png", width: 80%), caption: "Повторна перевірка роботи створеного цсенарію") <9>
|
||||
|
||||
== Висновки
|
||||
Під час виконання даної практичної роботи я навчився створювати сценарії автоматизації засобами Bash (Bourne again shell).
|
||||
|
||||
#counter(heading).update(0)
|
||||
|
||||
#set heading(
|
||||
numbering: (i, ..nums) => {
|
||||
let char = upper(ua_alpha_numbering.at(i))
|
||||
if nums.pos().len() == 0 { char } else {
|
||||
char + "." + nums.pos().map(str).join(".")
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
#show heading.where(level: 1): it => {
|
||||
set align(center)
|
||||
set text(size: 14pt, weight: "regular")
|
||||
|
||||
pagebreak(weak: true)
|
||||
bold[ДОДАТОК #counter(heading).display(it.numbering)]
|
||||
linebreak()
|
||||
it.body
|
||||
v(spacing * 2, weak: true)
|
||||
}
|
||||
|
||||
= Повний текст розробленого сценарію
|
||||
|
||||
```
|
||||
#!/bin/bash
|
||||
|
||||
if (( $# != 2 )); then echo "Not enough/Too many arguments" >&2; exit 1; fi
|
||||
if (( $1 < 8 )); then echo "Height must be > 7" >&2; exit 2; fi
|
||||
if (( $2 < 7 )); then echo "Width must be > 6" >&2; exit 3; fi
|
||||
|
||||
h=$(( ($1 % 2 != 0) ? $1 - 1 : $1 ))
|
||||
w=$(( ($2 % 2 == 0) ? $2 - 1 : $2 ))
|
||||
lh=$(( $h/2 - 1 ))
|
||||
|
||||
if (( ($w-(2*$lh-1) != 2) )); then echo "Can't draw the tree with provided arguments" >&2; exit 4; fi
|
||||
|
||||
|
||||
ch='*'
|
||||
|
||||
draw_layer() {
|
||||
for ((i=$1; i <= lh; i++)); do
|
||||
printf "%*s" "$(( (w - (2*i-1)) / 2 ))"
|
||||
printf "%*s\n" "$(( 2*i-1 ))" | tr ' ' "$ch"
|
||||
|
||||
ch=$( [ "$ch" = '*' ] && echo '#' || echo '*' )
|
||||
done
|
||||
}
|
||||
|
||||
draw_stem() {
|
||||
for i in {0..1}; do
|
||||
printf "%$(( (h - 3) / 2 ))s"
|
||||
printf "%3s\n" | tr ' ' "#"
|
||||
done
|
||||
}
|
||||
|
||||
draw_snow() {
|
||||
printf "%${w}s\n" | tr ' ' "*"
|
||||
}
|
||||
|
||||
draw_layer 1
|
||||
draw_layer 2
|
||||
draw_stem
|
||||
draw_snow
|
||||
|
||||
i=0
|
||||
while [[ $i -ne 1 ]]; do i=$((i+1)); done
|
||||
until [[ $i -eq 2 ]]; do i=$((i+1)); done
|
||||
|
||||
```
|