Compare commits
14 Commits
62f190c5b1
...
master
Author | SHA1 | Date | |
---|---|---|---|
80b0eb9448 | |||
14c347b2f1 | |||
85e286c2c5 | |||
10e7d50501 | |||
9983b98da0
|
|||
f85d557916
|
|||
cc7a3908f6 | |||
670dd9596e | |||
48af642e4f | |||
99c876c48c | |||
264299a7a8 | |||
e2e0f45588 | |||
ae1eaa3c26 | |||
49e5f8b9b7 |
1
.gitignore
vendored
1
.gitignore
vendored
@@ -1,3 +1,4 @@
|
||||
template_upstream
|
||||
archive
|
||||
template.zip
|
||||
programmes.js
|
||||
|
@@ -2,5 +2,7 @@
|
||||
|
||||
Before you host this yourself, make sure to run the `build.sh` script to create the template archive. Without it, the generator will not work.
|
||||
|
||||
Make sure that you have the `csvkit` and `jq` packages installed on your Arch system, they are needed to generate the list of study programmes.
|
||||
|
||||
[Here's a running instance of this generator, just to get a rough idea.](https://internal.prochazkaml.eu/tultemplategen)
|
||||
|
||||
|
39
build.sh
39
build.sh
@@ -1,17 +1,42 @@
|
||||
mkdir -p archive
|
||||
|
||||
if [ ! -d template_upstream ]; then
|
||||
git clone git@gordon.zumepro.cz:tul/tultemplate2 template_upstream
|
||||
echo -n "const programmes = " > programmes.js
|
||||
curl "https://stag.tul.cz/StagPortletsJSR168/ProhlizeniPrint?stateClass=cz.zcu.stag.portlets168.prohlizeni.browser.BrowserFakultaSearchState&wservice=programy/getStudijniProgramy&outputFormat=CSV&pouzePlatne=true&lang=cs" | csvjson -e cp1250 | jq 'group_by(.fakulta | ascii_downcase) | [.[] | {
|
||||
key: .[0].fakulta | ascii_downcase | if . == "fe" then "ef" else . end | if . == "fa" then "fua" else . end,
|
||||
value: [.[] | {
|
||||
code: .kod | ascii_upcase,
|
||||
type: .typ | ascii_downcase // .titulZkr // .titul,
|
||||
title: .titulZkr // .titul // "bez titulu",
|
||||
form: .forma | ascii_downcase,
|
||||
year: .platnyOd | round,
|
||||
cs_name: .nazevCz // .nazev,
|
||||
en_name: .nazevAn // .nazev
|
||||
}]
|
||||
}] | from_entries' >> programmes.js
|
||||
|
||||
TEMPLATE=template_upstream
|
||||
|
||||
if [ ! -d $TEMPLATE ]; then
|
||||
git clone git@gordon.zumepro.cz:tul/tultemplate2 $TEMPLATE
|
||||
else
|
||||
cd template_upstream
|
||||
git pull
|
||||
cd $TEMPLATE
|
||||
git fetch --tags
|
||||
cd ..
|
||||
fi
|
||||
|
||||
cd $TEMPLATE
|
||||
git checkout $(git describe --tags "$(git rev-list --tags --max-count=1)")
|
||||
cd ..
|
||||
|
||||
TEMPLATE_PACKDIR=$TEMPLATE/pack/bundle
|
||||
|
||||
rm -r $TEMPLATE_PACKDIR
|
||||
cd $TEMPLATE
|
||||
make bundle
|
||||
cd ..
|
||||
|
||||
cp -r \
|
||||
template_upstream/template \
|
||||
template_upstream/citations.bib \
|
||||
template_upstream/Makefile \
|
||||
$TEMPLATE_PACKDIR/. \
|
||||
assignment.pdf \
|
||||
archive
|
||||
|
||||
|
110
data.js
110
data.js
@@ -18,6 +18,10 @@ let steps = {
|
||||
layout: language_layout,
|
||||
result: {
|
||||
lang: "cs"
|
||||
},
|
||||
finalize: (result) => {
|
||||
result.is_cs = result.lang === "cs";
|
||||
result.is_en = result.lang === "en";
|
||||
}
|
||||
},
|
||||
thesis_title: {
|
||||
@@ -50,6 +54,30 @@ let steps = {
|
||||
}
|
||||
}
|
||||
},
|
||||
programme: {
|
||||
condition: () => programmes[steps.faculty.result.name] !== undefined,
|
||||
title: "Který program studujete?",
|
||||
layout: programme_layout,
|
||||
result: {
|
||||
faculty: "", // used for checking whether to clear
|
||||
idx: -1 // -1 = none, 0+ = index into programmes[faculty]
|
||||
},
|
||||
finalize: (result) => {
|
||||
if(result.idx == -1) {
|
||||
result.has_programme = false;
|
||||
return
|
||||
}
|
||||
|
||||
let programme = programmes[steps.faculty.result.name][result.idx];
|
||||
|
||||
result.has_programme = true;
|
||||
result.programme = programme.code + " " + (
|
||||
steps.language.result.lang == "cs" ?
|
||||
programme.cs_name :
|
||||
programme.en_name
|
||||
);
|
||||
}
|
||||
},
|
||||
collaborators: {
|
||||
title: "Vedoucí práce",
|
||||
layout: collaborators_layout,
|
||||
@@ -78,6 +106,24 @@ let steps = {
|
||||
finalize: (result) => {
|
||||
result.include_pdf = result.format === "external";
|
||||
}
|
||||
},
|
||||
other_details: {
|
||||
title: "Ostatní detaily",
|
||||
layout: other_details_layout,
|
||||
result: {
|
||||
has_keywords: true,
|
||||
has_acknowledgement: true
|
||||
}
|
||||
},
|
||||
toolchain: {
|
||||
title: "Jak budete používat Typst?",
|
||||
layout: toolchain_layout,
|
||||
result: {
|
||||
toolchain: "online"
|
||||
},
|
||||
finalize: (result) => {
|
||||
result.include_makefile = result.toolchain === "local";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -174,6 +220,15 @@ const assignment_formats = {
|
||||
}
|
||||
}
|
||||
|
||||
const toolchains = {
|
||||
"online": {
|
||||
name: "Pomocí webového editoru Typstu"
|
||||
},
|
||||
"local": {
|
||||
name: "Pomocí příkazové řádky"
|
||||
}
|
||||
}
|
||||
|
||||
const typst_header = `#import "template/template.typ": *
|
||||
|
||||
#show: tultemplate2.with(
|
||||
@@ -181,20 +236,38 @@ const typst_header = `#import "template/template.typ": *
|
||||
document: "{thesis_type.type}",
|
||||
lang: "{language.lang}",
|
||||
title: (cs: "{thesis_title.cs}", en: "{thesis_title.en}"),
|
||||
specialization: ({language.lang}: "SEM DOPLŇTE SPECIALIZACI"),
|
||||
<other_details.has_keywords: keywords: (
|
||||
cs: ("DOPLŇTE", "SEM", "KLÍČOVÁ", "SLOVA"),
|
||||
en: ("INSERT", "KEYWORDS", "HERE")
|
||||
),
|
||||
><other_details.has_acknowledgement: acknowledgement: (
|
||||
<language.is_cs:cs: "
|
||||
Rád bych poděkoval všem, kteří přispěli ke vzniku tohoto dílka.
|
||||
"><language.is_en:en: "
|
||||
I would like to acknowledge everyone who contributed to the creation of this fine piece of work.
|
||||
">
|
||||
),
|
||||
> abstract: (
|
||||
cs: "
|
||||
Sem vyplňte abstrakt své práce v češtině.
|
||||
",
|
||||
en: "
|
||||
Insert the abstract of your theses in English here.
|
||||
"
|
||||
),
|
||||
author: "{author_info.name}",
|
||||
author_pronouns: "{author_info.pronouns}",
|
||||
<collaborators.has_supervisor: supervisor: "{collaborators.supervisor_name}",
|
||||
><collaborators.has_consultant: consultant: "{collaborators.consultant_name}",
|
||||
><programme.has_programme: programme: ({language.lang}: "{programme.programme}"),
|
||||
><assignment.include_pdf: assignment: "assignment.pdf",
|
||||
> citations: "citations.bib"
|
||||
)
|
||||
|
||||
= Nadpis
|
||||
|
||||
skibidi
|
||||
`;
|
||||
|
||||
function generate_template() {
|
||||
function generate_template_header() {
|
||||
let out = typst_header;
|
||||
|
||||
for(const step_key in steps) {
|
||||
@@ -210,9 +283,12 @@ function generate_template() {
|
||||
console.log("- " + key);
|
||||
|
||||
const search_condition = "<" + step_key + "." + key + ":";
|
||||
const condition_pos = out.search(search_condition);
|
||||
|
||||
if(condition_pos >= 0) {
|
||||
while(true) {
|
||||
const condition_pos = out.search(search_condition);
|
||||
|
||||
if(condition_pos < 0) break;
|
||||
|
||||
const condition_end_pos = out.slice(condition_pos).search(">");
|
||||
|
||||
if(condition_end_pos >= 0) {
|
||||
@@ -225,9 +301,12 @@ function generate_template() {
|
||||
}
|
||||
|
||||
const search_replacement = "{" + step_key + "." + key + "}";
|
||||
const replacement_pos = out.search(search_replacement);
|
||||
|
||||
if(replacement_pos >= 0) {
|
||||
while(true) {
|
||||
const replacement_pos = out.search(search_replacement);
|
||||
|
||||
if(replacement_pos < 0) break;
|
||||
|
||||
const head = out.slice(0, replacement_pos);
|
||||
const meat = result[key];
|
||||
const tail = out.slice(replacement_pos + search_replacement.length);
|
||||
@@ -246,12 +325,25 @@ async function generate_zip() {
|
||||
let assets_zip = await fetch("template.zip");
|
||||
let out_zip = await new JSZip().loadAsync(await assets_zip.blob());
|
||||
|
||||
out_zip.file("thesis.typ", generate_template());
|
||||
let template = await out_zip.file(steps.thesis_type.result.type + ".typ").async("string");
|
||||
template = generate_template_header() + template;
|
||||
|
||||
out_zip.forEach((path, _) => {
|
||||
if(path.match(/^[a-z_]*\.typ$/)) {
|
||||
out_zip.remove(path)
|
||||
}
|
||||
});
|
||||
|
||||
out_zip.file("thesis.typ", template);
|
||||
|
||||
if(!steps.assignment.result.include_pdf) {
|
||||
out_zip.remove("assignment.pdf");
|
||||
}
|
||||
|
||||
if(!steps.toolchain.result.include_makefile) {
|
||||
out_zip.remove("Makefile");
|
||||
}
|
||||
|
||||
return await out_zip.generateAsync({ type: "blob" });
|
||||
}
|
||||
|
||||
|
@@ -5,6 +5,7 @@
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<link rel="stylesheet" href="styles.css">
|
||||
<script type="text/javascript" src="lib/jszip.min.js"></script>
|
||||
<script type="text/javascript" src="programmes.js"></script>
|
||||
<script type="text/javascript" src="ui.js"></script>
|
||||
<script type="text/javascript" src="utils.js"></script>
|
||||
<script type="text/javascript" src="data.js"></script>
|
||||
|
15
styles.css
15
styles.css
@@ -302,6 +302,21 @@ input[type="text"]:disabled {
|
||||
color: lightgray;
|
||||
}
|
||||
|
||||
select {
|
||||
flex-grow: 0;
|
||||
padding: 8px 16px;
|
||||
|
||||
font-family: inherit;
|
||||
font-size: 100%;
|
||||
border: 1px var(--button-border-color) solid;
|
||||
color: black;
|
||||
background-color: var(--button-color);
|
||||
border-radius: 8px;
|
||||
|
||||
box-sizing: border-box;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.zmp-logo {
|
||||
position: absolute;
|
||||
bottom: 20px;
|
||||
|
70
ui.js
70
ui.js
@@ -110,6 +110,29 @@ async function author_info_layout(content, result) {
|
||||
.finish());
|
||||
}
|
||||
|
||||
async function programme_layout(content, result) {
|
||||
let faculty = steps.faculty.result.name;
|
||||
let programmes_faculty = programmes[faculty];
|
||||
let options = [
|
||||
[[-1, "(žádný)"]],
|
||||
programmes_faculty.map((x, idx) => [
|
||||
idx,
|
||||
"(" + x.title + ", " + x.form.slice(0, 4) + ".) " + x.cs_name
|
||||
])
|
||||
].flat().map(x => new ElementBuilder("option")
|
||||
.value(x[0])
|
||||
.text(x[1])
|
||||
.finish());
|
||||
|
||||
content.appendChild(new ElementBuilder("select")
|
||||
.append_all(options)
|
||||
.on_change(e => result.idx = +e.target.value)
|
||||
.value(result.faculty != faculty ? -1 : result.idx)
|
||||
.finish());
|
||||
|
||||
result.faculty = faculty;
|
||||
}
|
||||
|
||||
async function collaborators_layout(content, result) {
|
||||
let supervisor_name = new TextInputBuilder(result, "supervisor_name")
|
||||
.placeholder("Jméno vedoucího práce (vč. titulů), příp. vedoucích")
|
||||
@@ -155,6 +178,43 @@ async function assignment_layout(content, result) {
|
||||
}
|
||||
}
|
||||
|
||||
async function other_details_layout(content, result) {
|
||||
content.appendChild(new BinaryInputBuilder(result, "has_keywords")
|
||||
.label("Vložit klíčová slova")
|
||||
.finish());
|
||||
content.appendChild(new BinaryInputBuilder(result, "has_acknowledgement")
|
||||
.label("Vložit poděkování")
|
||||
.finish());
|
||||
content.appendChild(new ElementBuilder("div.vertical-spacer").finish());
|
||||
content.appendChild(new ElementBuilder("div.hint")
|
||||
.text("Generátor pouze vygeneruje ukázky, jak by tato pole měla vypadat. Samotná klíčová slova či poděkování si poté upravíte dle svých potřeb.")
|
||||
.finish());
|
||||
}
|
||||
|
||||
async function toolchain_layout(content, result) {
|
||||
if(!Object.keys(toolchains).includes(result.toolchain)) {
|
||||
document.querySelector("#next-button").disabled = true;
|
||||
}
|
||||
|
||||
let buttons = new ButtonListBuilder(Object.keys(toolchains))
|
||||
.should_be_pressed((key) => result.toolchain == key)
|
||||
.modify((b, key) => b.class("wide").text(toolchains[key].name))
|
||||
.on_click((key) => {
|
||||
result.toolchain = key;
|
||||
document.querySelector("#next-button").disabled = false;
|
||||
})
|
||||
.finish();
|
||||
|
||||
for(const button of buttons) {
|
||||
content.appendChild(button);
|
||||
}
|
||||
|
||||
content.appendChild(new ElementBuilder("div.vertical-spacer").finish());
|
||||
content.appendChild(new ElementBuilder("div.hint")
|
||||
.text("Toto rozhodnutí pouze ovlivní, zda výsledná šablona bude obsahovat soubor Makefile pro jednodušší použití s příkazovou řádkou. Webový editor Typstu tento soubor nepotřebuje.")
|
||||
.finish());
|
||||
}
|
||||
|
||||
async function run() {
|
||||
startbutton.style.cursor = "auto";
|
||||
startbutton.disabled = true;
|
||||
@@ -196,14 +256,20 @@ async function run() {
|
||||
let steps_len = Object.keys(steps).length;
|
||||
|
||||
while(id < steps_len) {
|
||||
let step = get_step(id);
|
||||
|
||||
if(step.condition && !step.condition()) {
|
||||
if(animation > 0) id++;
|
||||
if(animation < 0) id--;
|
||||
continue;
|
||||
}
|
||||
|
||||
back_button.style.display = (id != 0) ? "" : "none";
|
||||
next_button.innerText = (id < steps_len - 1) ? "Další →" : "Dokončit";
|
||||
next_button.disabled = false;
|
||||
|
||||
container.innerHTML = "";
|
||||
|
||||
let step = get_step(id);
|
||||
|
||||
let title = new ElementBuilder("h1")
|
||||
.html(step.title)
|
||||
.finish();
|
||||
|
13
utils.js
13
utils.js
@@ -61,6 +61,11 @@ class ElementBuilder {
|
||||
return this;
|
||||
}
|
||||
|
||||
on_change(handler) {
|
||||
this.element.onchange = handler;
|
||||
return this;
|
||||
}
|
||||
|
||||
for(fr) {
|
||||
this.element.htmlFor = fr;
|
||||
return this;
|
||||
@@ -230,11 +235,13 @@ class BinaryInputBuilder {
|
||||
let input = new ElementBuilder("input#" + this.key)
|
||||
.type("checkbox")
|
||||
.checked(this.result[this.key])
|
||||
.if(this.update_handler !== null, b => b.on_click(e => {
|
||||
.on_click(e => {
|
||||
console.log(e.target.checked);
|
||||
this.result[this.key] = e.target.checked;
|
||||
this.update_handler(e.target.checked);
|
||||
}))
|
||||
if(this.update_handler !== null) {
|
||||
this.update_handler(e.target.checked);
|
||||
}
|
||||
})
|
||||
.finish();
|
||||
|
||||
let bundle = new ElementBuilder("div.horizontal-list")
|
||||
|
Reference in New Issue
Block a user