feat(coursework-v2): refine abstract, committee lines, and bibliography

This commit is contained in:
ilyaoc
2026-05-26 20:46:42 +02:00
parent 6bcc345717
commit d6037872dd
6 changed files with 508 additions and 39 deletions
+420
View File
@@ -0,0 +1,420 @@
<?xml version="1.0" encoding="UTF-8"?>
<style xmlns="http://purl.org/net/xbiblio/csl"
class="in-text"
version="1.0"
default-locale="uk-UA"
demote-non-dropping-particle="never">
<info>
<title>ДСТУ 8302:2015 — бібліографічні посилання</title>
<title-short>ДСТУ 8302:2015</title-short>
<id>dstu-8302-2015-typst</id>
<link href="https://online.budstandart.com/ua/catalog/doc-page.html?id_doc=64411" rel="documentation"/>
<author>
<name>Custom Typst CSL</name>
</author>
<category citation-format="numeric"/>
<category field="generic-base"/>
<summary>CSL для оформлення переліку джерел за логікою ДСТУ 8302:2015 у Typst/Pandoc: числові посилання, українські скорочення, DOI/URL, дата звернення, коректні вебджерела, статті та нормативні акти.</summary>
<updated>2026-05-25T00:00:00+00:00</updated>
<rights license="http://creativecommons.org/licenses/by-sa/4.0/">This work is licensed under a Creative Commons Attribution-ShareAlike 4.0 License</rights>
</info>
<locale xml:lang="uk-UA">
<terms>
<term name="accessed">дата звернення</term>
<term name="page" form="short">С.</term>
<term name="page" form="symbol">с.</term>
<term name="volume" form="short">Т.</term>
<term name="issue" form="short">№</term>
<term name="edition" form="short">вид.</term>
<term name="editor" form="short">ред.</term>
<term name="translator" form="short">пер.</term>
<term name="et-al">та ін.</term>
<term name="and">,</term>
</terms>
</locale>
<!-- Ініціали після прізвища: Vergara L. M., Park M. -->
<macro name="contributors">
<names variable="author">
<name name-as-sort-order="all"
sort-separator=" "
initialize-with=". "
delimiter=", "
delimiter-precedes-last="never"/>
<substitute>
<names variable="editor">
<name name-as-sort-order="all"
sort-separator=" "
initialize-with=". "
delimiter=", "
delimiter-precedes-last="never"/>
<label form="short" prefix=" (" suffix=")"/>
</names>
<names variable="translator">
<name name-as-sort-order="all"
sort-separator=" "
initialize-with=". "
delimiter=", "
delimiter-precedes-last="never"/>
<label form="short" prefix=" (" suffix=")"/>
</names>
</substitute>
</names>
</macro>
<macro name="title">
<text variable="title"/>
</macro>
<macro name="container">
<text variable="container-title" font-style="italic"/>
</macro>
<macro name="site-or-publisher">
<choose>
<if variable="container-title">
<text variable="container-title"/>
</if>
<else>
<text variable="publisher"/>
</else>
</choose>
</macro>
<macro name="issued-year">
<date variable="issued">
<date-part name="year"/>
</date>
</macro>
<macro name="issued-date">
<date variable="issued">
<date-part name="day" form="numeric-leading-zeros" suffix="."/>
<date-part name="month" form="numeric-leading-zeros" suffix="."/>
<date-part name="year"/>
</date>
</macro>
<macro name="accessed-date">
<date variable="accessed">
<date-part name="day" form="numeric-leading-zeros" suffix="."/>
<date-part name="month" form="numeric-leading-zeros" suffix="."/>
<date-part name="year"/>
</date>
</macro>
<macro name="publisher-place">
<group delimiter=" : ">
<text variable="publisher-place"/>
<text variable="publisher"/>
</group>
</macro>
<macro name="edition">
<group delimiter=" ">
<text variable="edition"/>
<label variable="edition" form="short"/>
</group>
</macro>
<macro name="book-pages">
<choose>
<if variable="number-of-pages">
<group delimiter=" ">
<text variable="number-of-pages"/>
<text term="page" form="symbol"/>
</group>
</if>
<else-if variable="page">
<group delimiter=" ">
<text variable="page"/>
<text term="page" form="symbol"/>
</group>
</else-if>
</choose>
</macro>
<macro name="pages">
<choose>
<if variable="page">
<group delimiter=" ">
<text term="page" form="short"/>
<text variable="page"/>
</group>
</if>
</choose>
</macro>
<macro name="volume-issue">
<group delimiter=", ">
<choose>
<if variable="volume">
<group delimiter=" ">
<text value="Т."/>
<text variable="volume"/>
</group>
</if>
</choose>
<choose>
<if variable="issue">
<group delimiter=" ">
<text value="№"/>
<text variable="issue"/>
</group>
</if>
</choose>
</group>
</macro>
<macro name="url-access">
<choose>
<if variable="URL">
<group delimiter=" ">
<text variable="URL" prefix="URL: "/>
<choose>
<if variable="accessed">
<group delimiter=": " prefix="(" suffix=")">
<text term="accessed"/>
<text macro="accessed-date"/>
</group>
</if>
</choose>
</group>
</if>
</choose>
</macro>
<macro name="doi-or-url">
<choose>
<if variable="DOI">
<text variable="DOI" prefix="DOI: "/>
</if>
<else>
<text macro="url-access"/>
</else>
</choose>
</macro>
<macro name="legal-details">
<choose>
<if variable="genre">
<group delimiter=" : ">
<text macro="title"/>
<group delimiter=" ">
<text variable="genre"/>
<choose>
<if variable="issued">
<group delimiter=" ">
<text value="від"/>
<text macro="issued-date"/>
</group>
</if>
</choose>
<choose>
<if variable="number">
<group delimiter=" ">
<text value="№"/>
<text variable="number"/>
</group>
</if>
</choose>
</group>
</group>
</if>
<else>
<group delimiter=". ">
<text macro="title"/>
<group delimiter=" ">
<choose>
<if variable="issued">
<group delimiter=" ">
<text value="від"/>
<text macro="issued-date"/>
</group>
</if>
</choose>
<choose>
<if variable="number">
<group delimiter=" ">
<text value="№"/>
<text variable="number"/>
</group>
</if>
</choose>
</group>
</group>
</else>
</choose>
</macro>
<citation collapse="citation-number">
<sort>
<key variable="citation-number"/>
</sort>
<layout delimiter=", " prefix="[" suffix="]">
<text variable="citation-number"/>
</layout>
</citation>
<bibliography hanging-indent="false" entry-spacing="0" line-spacing="1">
<sort>
<key variable="citation-number"/>
</sort>
<layout>
<group display="block">
<text display="left-margin" variable="citation-number" suffix=". "/>
<choose>
<!-- Нормативні акти: Цивільний кодекс України : Закон України від 16.01.2003 № 435-IV. URL: ... -->
<if type="legislation bill legal_case treaty" match="any">
<group delimiter=". " suffix=".">
<text macro="legal-details"/>
<text macro="url-access"/>
</group>
</if>
<!-- Наукова стаття -->
<else-if type="article-journal">
<group delimiter=". " suffix=".">
<text macro="contributors"/>
<text macro="title"/>
<text macro="container"/>
<text macro="issued-year"/>
<text macro="volume-issue"/>
<text macro="pages"/>
<text macro="doi-or-url"/>
</group>
</else-if>
<!-- Газетна / журнальна стаття без DOI -->
<else-if type="article-newspaper article-magazine" match="any">
<group delimiter=". " suffix=".">
<text macro="contributors"/>
<text macro="title"/>
<text variable="container-title" font-style="italic"/>
<text macro="issued-date"/>
<text macro="pages"/>
<text macro="url-access"/>
</group>
</else-if>
<!-- Вебсторінки: без дублювання назви замість автора -->
<else-if type="webpage post post-weblog" match="any">
<group delimiter=". " suffix=".">
<text macro="contributors"/>
<text macro="title"/>
<text macro="site-or-publisher"/>
<text macro="issued-date"/>
<text macro="url-access"/>
</group>
</else-if>
<!-- Книга -->
<else-if type="book">
<group delimiter=". " suffix=".">
<text macro="contributors"/>
<text macro="title"/>
<text macro="edition"/>
<group delimiter=", ">
<text macro="publisher-place"/>
<text macro="issued-year"/>
</group>
<text macro="book-pages"/>
<text macro="doi-or-url"/>
</group>
</else-if>
<!-- Розділ у книзі -->
<else-if type="chapter">
<group delimiter=". " suffix=".">
<text macro="contributors"/>
<text macro="title"/>
<group delimiter=" ">
<text value="In:"/>
<text variable="container-title" font-style="italic"/>
</group>
<names variable="editor">
<name name-as-sort-order="all"
sort-separator=" "
initialize-with=". "
delimiter=", "
delimiter-precedes-last="never"/>
<label form="short" prefix=" (" suffix=")"/>
</names>
<group delimiter=", ">
<text macro="publisher-place"/>
<text macro="issued-year"/>
</group>
<text macro="pages"/>
<text macro="doi-or-url"/>
</group>
</else-if>
<!-- Матеріали конференції -->
<else-if type="paper-conference">
<group delimiter=". " suffix=".">
<text macro="contributors"/>
<text macro="title"/>
<group delimiter=" ">
<text value="In:"/>
<text variable="container-title" font-style="italic"/>
</group>
<group delimiter=", ">
<text macro="publisher-place"/>
<text macro="issued-year"/>
</group>
<text macro="pages"/>
<text macro="doi-or-url"/>
</group>
</else-if>
<!-- Дисертації, звіти -->
<else-if type="thesis report" match="any">
<group delimiter=". " suffix=".">
<text macro="contributors"/>
<text macro="title"/>
<text variable="genre"/>
<group delimiter=", ">
<text macro="publisher-place"/>
<text macro="issued-year"/>
</group>
<text macro="book-pages"/>
<text macro="doi-or-url"/>
</group>
</else-if>
<!-- Патенти -->
<else-if type="patent">
<group delimiter=". " suffix=".">
<text macro="contributors"/>
<text macro="title"/>
<group delimiter=" ">
<text value="№"/>
<text variable="number"/>
</group>
<text macro="issued-date"/>
<text macro="url-access"/>
</group>
</else-if>
<!-- Універсальний fallback -->
<else>
<group delimiter=". " suffix=".">
<text macro="contributors"/>
<text macro="title"/>
<text variable="container-title" font-style="italic"/>
<group delimiter=", ">
<text macro="publisher-place"/>
<text macro="issued-year"/>
</group>
<text macro="pages"/>
<text macro="doi-or-url"/>
</group>
</else>
</choose>
</group>
</layout>
</bibliography>
</style>
+7 -1
View File
@@ -65,6 +65,7 @@
/// - title (str): Work title
/// - authors (array): List of author dictionaries
/// - mentors (array): List of mentor dictionaries
/// - committee-members (array): Optional list of commission member dictionaries for the title page
/// - task-list (dict): Task metadata
/// - calendar-plan (dict): Calendar plan table
/// - abstract (dict): Keywords and abstract text
@@ -77,6 +78,7 @@
title: none,
authors: (),
mentors: (),
committee-members: none,
task-list: (),
calendar-plan: (),
abstract: (),
@@ -107,12 +109,14 @@
} else {
abstract
}
let committee_members = committee-members
tp.cw-v2.nure(
university,
title,
authors,
mentors,
committee_members,
task-list,
calendar-plan,
abstract,
@@ -125,10 +129,12 @@
doc
{
show regex("^\\d+\\."): it => [#it#h(0.5cm)]
show block: it => [#it.body#parbreak()]
bibliography(bib-path, title: [Перелік джерел посилання], style: "csl/dstu-3008-2015.csl", full: true)
bibliography(bib-path, title: [Перелік джерел посилання], style: "csl/dstu-8302-2015.csl", full: true)
}
style.appendices(appendices)
+10
View File
@@ -186,6 +186,7 @@
counter(figure.where(kind: raw)).update(0)
counter(figure.where(kind: image)).update(0)
counter(figure.where(kind: table)).update(0)
dstu-table-counter.update(0)
it
}
set figure(numbering: i => context numbering("1.1", counter(heading).get().at(0), i))
@@ -218,6 +219,15 @@
v(double-half-spacing, weak: true)
}
// blocks `like this` aren't welcome, so ` is replaced with "
show raw.where(block: false): it => text(
lang: "uk",
size: 14pt,
hyphenate: false,
weight: "regular",
font: ("Times New Roman", "Liberation Serif"),
)["#it.text"]
it
}
+56 -23
View File
@@ -1,7 +1,7 @@
#import "../../shared.typ": universities
#import "../../helpers.typ": *
#import "../../style.typ": spacing
#import "../../utils.typ": bold, uline, filled-lines
#import "../../utils.typ": bold, uline, filled-lines, hfill
#let note(content) = block(width: 100%, above: 5pt, below: 0pt)[
#set text(size: 10pt)
@@ -69,16 +69,37 @@
#let task-num(n) = box(str(n) + ".")
#let title-field(value) = {
uline(align: center, filled-lines(value))
uline(align: center, value.join())
uline(align: center, [])
note[(тема)]
}
#let commission-lines(members) = {
if members.len() > 0 {
for (i, member) in members.enumerate() {
if i > 0 {
linebreak()
}
let member-display-name = member.at("display-name", default: member.name)
let member-degree = member.at("degree", default: "")
uline(align: left, [#member-degree #member-display-name])
}
} else {
v(0.55em)
line(length: 100%, stroke: 0.5pt)
v(0.55em)
line(length: 100%, stroke: 0.5pt)
v(0.55em)
line(length: 100%, stroke: 0.5pt)
}
}
#let nure(
university,
title,
authors,
mentors,
committee_members,
task-list,
calendar-plan,
abstract,
@@ -90,6 +111,11 @@
) = {
let author = authors.first()
let head-mentor = mentors.first()
let commission-members = if committee_members == none {
mentors.slice(1)
} else {
committee_members
}
let uni = universities.at(university)
let edu-prog = uni.edu-programs.at(author.edu-program)
@@ -177,12 +203,8 @@
#pad(left: 75pt)[
#set par(first-line-indent: 0pt)
Члени комісії (#text(size: 10pt)[Власне ім'я, ПРІЗВИЩЕ, підпис])
#v(0.55em)
#line(length: 100%, stroke: 0.5pt)
#v(0.55em)
#line(length: 100%, stroke: 0.5pt)
#v(0.55em)
#line(length: 100%, stroke: 0.5pt)
#v(0.15em)
#commission-lines(commission-members)
]
],
)
@@ -266,12 +288,12 @@
#v(5.0em)
Дата видачі завдання “#underline(task-list.initial-date.display("[day]"))” #underline(month-gen(task-list.initial-date.month())) #task-list.initial-date.display("[year]")р.
Дата видачі завдання “#underline(task-list.initial-date.display("[day]"))” #underline(month-gen(task-list.initial-date.month())) #task-list.initial-date.display("[year]") р.
#v(1.4em)
Здобувач #uline(align: center, [])
#note[(підпис)]
Здобувач #underline([#hfill(6cm)])
#note[(підпис) #h(8cm)]
#v(1.4em)
@@ -292,7 +314,8 @@
#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 dstu-tables = query(metadata).filter(it => type(it.value) == str and it.value.starts-with("start-dstu-table-")).len()
#let tables = query(figure.where(kind: table)).len() + dstu-tables
#let bibs = bib-count.final().dedup().len()
#let counters = ()
@@ -306,23 +329,33 @@
\
#(
abstract
.keywords
.map(upper)
.sorted(by: (a, b) => {
if is-cyr(a) != is-cyr(b) { is-cyr(a) } else { a < b }
#let keyword-pairs = if abstract.keywords.len() > 0 and type(abstract.keywords.first()) == array {
abstract.keywords.map(pair => (
uk: pair.at(0),
en: pair.at(1),
))
} else if abstract.at("en", default: none) != none and abstract.en.at("keywords", default: none) != none {
abstract.keywords.enumerate().map(((i, uk)) => (
uk: uk,
en: abstract.en.keywords.at(i),
))
} else {
abstract.keywords.map(uk => (uk: uk))
}
#let sorted-keyword-pairs = keyword-pairs.sorted(by: (a, b) => {
if is-cyr(a.uk) != is-cyr(b.uk) { is-cyr(a.uk) } else { a.uk < b.uk }
})
.join(", ")
)
#(sorted-keyword-pairs.map(pair => upper(pair.uk)).join(", "))
\
#abstract.text
#if abstract.at("en", default: none) != none [
#if abstract.at("en", default: none) != none or (keyword-pairs.len() > 0 and keyword-pairs.first().at("en", default: none) != none) [
\
#(abstract.en.keywords.map(upper).join(", "))
#(sorted-keyword-pairs.map(pair => upper(pair.en)).join(", "))
\
@@ -340,7 +373,7 @@
} else {
block(width: 100%)[
#link(el.location())[
ДОДАТОК #it.prefix()#h(0.5em)#it.inner()
Додаток #it.prefix()#h(0.5em)#it.inner()
]
]
}
+2 -1
View File
@@ -210,7 +210,8 @@
#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 dstu-tables = query(metadata).filter(it => type(it.value) == str and it.value.starts-with("start-dstu-table-")).len()
#let tables = query(figure.where(kind: table)).len() + dstu-tables
#let bibs = bib-count.final().dedup().len()
/* TODO: why this stopped working?
#let tables = counter(figure.where(kind: table)).final().at(0)
+12 -13
View File
@@ -20,6 +20,12 @@
(name: "Сокорчук І. П.", display-name: "Ігор СОКОРЧУК", degree: "ст.викл. кафедри ПІ"),
)
#let committee_members = (
(name: "Груздо І.В.", degree: "Доц."),
(name: "Зибіна К.В.", degree: "Ст. викл."),
(name: "Гребенюк В.О.", degree: "Ст. викл."),
)
#let task-list = (
done-date: datetime(year: 2026, month: 12, day: 27),
initial-date: datetime(year: 2026, month: 9, day: 15),
@@ -53,11 +59,11 @@
#let abstract = (
keywords: (
"веб-застосунок",
"інформаційна система",
"курсова робота",
"програмна інженерія",
"тестовий приклад",
("веб-застосунок", "WEB APPLICATION"),
("інформаційна система", "INFORMATION SYSTEM"),
("курсова робота", "COURSEWORK"),
("програмна інженерія", "SOFTWARE ENGINEERING"),
("тестовий приклад", "DEMO SAMPLE"),
),
text: [
Мета даної роботи -- продемонструвати оформлення пояснювальної записки
@@ -74,15 +80,7 @@
шаблону без прив'язки до реального студента, викладача або завершеної роботи.
],
)
#let abstract_en = (
keywords: (
"WEB APPLICATION",
"INFORMATION SYSTEM",
"COURSEWORK",
"SOFTWARE ENGINEERING",
"DEMO SAMPLE",
),
text: [
The purpose of this work is to demonstrate the formatting of a coursework
explanatory note using the new template variant.
@@ -122,6 +120,7 @@
),
authors: authors,
mentors: mentors,
committee-members: committee_members,
task-list: task-list,
calendar-plan: calendar-plan,
abstract: abstract,