Merge branch 'master' of ssh://git.betalupi.com:33/Mark/ormc-handouts
This commit is contained in:
commit
204ee9068e
6
.gitignore
vendored
6
.gitignore
vendored
@ -4,13 +4,11 @@ __pycache__
|
||||
*-ignore
|
||||
|
||||
# Output files
|
||||
/output
|
||||
/output.zip
|
||||
main.pdf
|
||||
**/build
|
||||
|
||||
# CI files
|
||||
/_build
|
||||
/_output
|
||||
|
||||
# TeX build files
|
||||
*.synctex.gz*
|
||||
*.latexmk
|
||||
|
@ -8,7 +8,7 @@ Create a multiplication table for $\mathbb{Z}_4$:
|
||||
|
||||
\begin{center}
|
||||
\begin{tabular}{c | c c c c}
|
||||
\times & 0 & 1 & 2 & 3 \\
|
||||
$\times$ & 0 & 1 & 2 & 3 \\
|
||||
\hline
|
||||
0 & ? & ? & ? & ? \\
|
||||
1 & ? & ? & ? & ? \\
|
||||
|
@ -220,10 +220,10 @@
|
||||
Show that for any two functions $g: Y \to W$ and $h: Y \to W$, if
|
||||
$g \circ f = h \circ f \implies g = h$.
|
||||
|
||||
\item[\star] Let $f: X \to Y$ be a function where for any set $Z$ and functions $g: Z \to X$ and $h: Z \to X$,
|
||||
\item[$\star$] Let $f: X \to Y$ be a function where for any set $Z$ and functions $g: Z \to X$ and $h: Z \to X$,
|
||||
$f \circ g = f \circ h \implies g = h$. Show that $f$ is injective.
|
||||
|
||||
\item[\star] Let $f: X \to Y$ be a function where for any set $W$ and functions $g: Y \to W$ and $h: Y \to W$,
|
||||
\item[$\star$] Let $f: X \to Y$ be a function where for any set $W$ and functions $g: Y \to W$ and $h: Y \to W$,
|
||||
$g \circ f = h \circ f \implies g = h$. Show f is surjective.
|
||||
\end{itemize}
|
||||
|
||||
|
@ -1,7 +1,7 @@
|
||||
# ORMC Handouts
|
||||
|
||||
This repository contains all the handouts I've written for the [ORMC](https://circles.math.ucla.edu/circles/). \
|
||||
You can find the latest PDFs [here](https://nc.betalupi.com/s/ormc-handouts).
|
||||
You can find the latest PDFs [here](https://static.betalupi.com/ormc).
|
||||
|
||||
**For my students:** Handouts will appear here a few days before class. Please don't look at them (or their solutions) beforehand, that spoils all the fun!
|
||||
|
||||
@ -21,7 +21,7 @@ Grade levels are estimates.
|
||||
|
||||
## 🛠️ Building these Handouts
|
||||
|
||||
Automatic builds are [here](https://nc.betalupi.com/s/ormc-handouts), if you just want the PDFs.
|
||||
Automatic builds are [here](https://static.betalupi.com/ormc), if you just want the PDFs.
|
||||
|
||||
If you want to edit these files, you'll need to download a custom document class. \
|
||||
To compile one handout, do the following:
|
||||
|
128
build.sh
Executable file
128
build.sh
Executable file
@ -0,0 +1,128 @@
|
||||
#!/usr/bin/env bash
|
||||
#
|
||||
# This script builds every document in this repo,
|
||||
# (handout and solutions, if they exist), and creates
|
||||
# a zip of all resulting files.
|
||||
|
||||
|
||||
|
||||
# Where we're running this script
|
||||
run_dir=$(pwd)
|
||||
|
||||
# Output files
|
||||
target_dir="${run_dir}/output"
|
||||
target_zip="${run_dir}/output.zip"
|
||||
|
||||
|
||||
|
||||
|
||||
# Clean up previous build
|
||||
rm -drf "${target_dir}"
|
||||
rm -f "${target_zip}"
|
||||
|
||||
|
||||
# Build one document.
|
||||
# Args: <target_dir> <job_name> <doc_dir> <main_file>
|
||||
# target_dir: move output pdf to this directory
|
||||
# job_name: name of this document. Output is saved as job_name.pdf
|
||||
# doc_dir: cd here before building.
|
||||
# main_file: build this tex file. Usually main.tex
|
||||
function build() {
|
||||
local target_dir="${1}"
|
||||
local job_name="${2}"
|
||||
local doc_dir="${3}"
|
||||
local main_file="${4}"
|
||||
|
||||
echo "|> Building ${job_name}..."
|
||||
cd "${doc_dir}"
|
||||
|
||||
|
||||
# Build handout
|
||||
echo "\\def\\argNoSolutions{1}\\input{${main_file}}" | \
|
||||
tectonic \
|
||||
--outfmt pdf \
|
||||
--chatter minimal \
|
||||
-
|
||||
|
||||
stat=$?
|
||||
|
||||
if [[ $stat == 0 ]]; then
|
||||
mkdir -p "${target_dir}"
|
||||
mv texput.pdf "${target_dir}/${job_name}.pdf"
|
||||
else
|
||||
rmdir --ignore-fail-on-non-empty "${target_dir}"
|
||||
rm -f texput.pdf
|
||||
echo "|> Handout build failed"
|
||||
echo ""
|
||||
fi
|
||||
|
||||
|
||||
# Build solutions
|
||||
echo "\\def\\argYesSolutions{1}\\input{${main_file}}" | \
|
||||
tectonic \
|
||||
--outfmt pdf \
|
||||
--chatter minimal \
|
||||
-
|
||||
|
||||
stat=$?
|
||||
|
||||
if [[ $stat == 0 ]]; then
|
||||
mkdir -p "${target_dir}"
|
||||
mv texput.pdf "${target_dir}/${job_name}.sols.pdf"
|
||||
else
|
||||
rmdir --ignore-fail-on-non-empty "${target_dir}"
|
||||
rm -f texput.pdf
|
||||
echo "|> Solution build failed"
|
||||
fi
|
||||
|
||||
# Clean up if files contents are identical
|
||||
if cmp --silent -- "${target_dir}/${job_name}.sols.pdf" "${target_dir}/${job_name}.pdf"; then
|
||||
echo "|> Versions identical, removing ${job_name}.sols.pdf"
|
||||
rm "${target_dir}/${job_name}.sols.pdf"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
|
||||
cd "${run_dir}"
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
for d in "${run_dir}/Misc/Warm-Ups"/*.tex ; do
|
||||
file="$(basename "${d}")"
|
||||
build \
|
||||
"${target_dir}/Warm-Ups" \
|
||||
"${file%.*}" \
|
||||
"${run_dir}/Misc/Warm-Ups" \
|
||||
"${file}"
|
||||
done
|
||||
|
||||
for d in "${run_dir}/Advanced"/*/ ; do
|
||||
doc_dir=$(realpath "${d}")
|
||||
job_name="$(basename "${doc_dir}")"
|
||||
|
||||
build \
|
||||
"${target_dir}/Advanced" \
|
||||
"${job_name}" \
|
||||
"${doc_dir}" \
|
||||
"main.tex"
|
||||
done
|
||||
|
||||
for d in "${run_dir}/Intermediate"/*/ ; do
|
||||
doc_dir=$(realpath "${d}")
|
||||
job_name="$(basename "${doc_dir}")"
|
||||
|
||||
build \
|
||||
"${target_dir}/Intermediate" \
|
||||
"${job_name}" \
|
||||
"${doc_dir}" \
|
||||
"main.tex"
|
||||
done
|
||||
|
||||
|
||||
# cd so paths in zip are relative
|
||||
cd "${target_dir}"
|
||||
zip -FSr "${target_zip}" .
|
28
resources/scripts/Jenkinsfile
vendored
28
resources/scripts/Jenkinsfile
vendored
@ -1,28 +0,0 @@
|
||||
pipeline {
|
||||
agent none
|
||||
environment {
|
||||
NC_CRED = credentials("nc-jenkins-cred")
|
||||
NO_TEST = true
|
||||
}
|
||||
stages {
|
||||
stage("Build") {
|
||||
agent {
|
||||
docker {
|
||||
image "git.betalupi.com/mark/latex:latest"
|
||||
}
|
||||
}
|
||||
steps {
|
||||
// Keep these in one "sh" directive.
|
||||
// Todo: create setup files so we don't
|
||||
// have to pip install manually here.
|
||||
// Maybe integrate python packages into the docker container?
|
||||
sh '''
|
||||
python -m venv venv
|
||||
source venv/bin/activate
|
||||
pip install tomli webdavclient3
|
||||
python resources/scripts/build.py
|
||||
'''
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,155 +0,0 @@
|
||||
# Build script for CI.
|
||||
# This file is run from the root of the repository
|
||||
|
||||
import os
|
||||
import tomli
|
||||
from pathlib import Path
|
||||
import shutil
|
||||
|
||||
from webdav3.client import Client
|
||||
|
||||
import helpers.tex as tex
|
||||
|
||||
conf_path = "resources/scripts/build.toml"
|
||||
|
||||
# Prepare envvars
|
||||
e = os.environ.copy()
|
||||
e["TEXINPUTS"] = f"::/usr/share/texmf-dist/tex//"
|
||||
print(Path.cwd())
|
||||
|
||||
# If true, rebuild everything
|
||||
# without checking for changes.
|
||||
# Currently unused.
|
||||
if "FORCE_ALL" in e:
|
||||
force_all = e["FORCE_ALL"] == "true"
|
||||
else:
|
||||
force_all = False
|
||||
|
||||
if "NO_TEST" in e:
|
||||
test = e["NO_TEST"] == "false"
|
||||
else:
|
||||
test = True
|
||||
|
||||
|
||||
# Used only for debug
|
||||
class FakeClient:
|
||||
def __init__(self):
|
||||
pass
|
||||
|
||||
def mkdir(self, path):
|
||||
print(f"[Dry run] Making dir {path}")
|
||||
|
||||
def upload_sync(self, local_path, remote_path):
|
||||
print(f"[Dry run] Synced {local_path} to {remote_path}")
|
||||
|
||||
def clean(self, path):
|
||||
print(f"[Dry run] Cleaning {path}")
|
||||
|
||||
|
||||
|
||||
# Load configuration
|
||||
with open(conf_path, mode="rb") as fp:
|
||||
config = tomli.load(fp)
|
||||
|
||||
# Prepare directories
|
||||
builddir = Path().absolute() / config["core"]["build_dir"]
|
||||
output = Path().absolute() / config["core"]["output_dir"]
|
||||
output.mkdir(parents = True, exist_ok = True)
|
||||
builddir.mkdir(parents = True, exist_ok = True)
|
||||
|
||||
|
||||
|
||||
if not test:
|
||||
# Connect to webdav
|
||||
client = Client({
|
||||
"webdav_login": e["NC_CRED_USR"],
|
||||
"webdav_password": e["NC_CRED_PSW"],
|
||||
"webdav_hostname": e["DAV_HOSTNAME"], # https://host:port
|
||||
"webdav_root": e["DAV_ROOT"] # the rest of the url
|
||||
# client.list() will break if we do not seperate the hostname
|
||||
# and the root path.
|
||||
})
|
||||
else:
|
||||
client = FakeClient()
|
||||
|
||||
|
||||
def upload_dir(local_path: Path):
|
||||
for p in local_path.rglob("*"):
|
||||
r = p.relative_to(output)
|
||||
if p.is_dir():
|
||||
client.mkdir(str(r))
|
||||
|
||||
elif p.is_file():
|
||||
client.upload_sync( # type: ignore
|
||||
remote_path = str(r),
|
||||
local_path = p
|
||||
)
|
||||
|
||||
|
||||
# Print envvars for debug
|
||||
#print(e)
|
||||
|
||||
|
||||
for i, item in enumerate(config["dir-of-dirs"]):
|
||||
p = Path(item["path"])
|
||||
|
||||
for s in p.iterdir():
|
||||
# s: path to directory,
|
||||
# # relative to repo root
|
||||
if not s.is_dir():
|
||||
continue
|
||||
|
||||
print(f"Building {s}/main.tex")
|
||||
|
||||
# Make pdf
|
||||
pdf_nosolutions = tex.build(
|
||||
s,
|
||||
mainfile = "main.tex",
|
||||
jobname = f"{i}-nosols",
|
||||
build_dir = builddir,
|
||||
solutions = False,
|
||||
|
||||
env = e,
|
||||
test = test
|
||||
)
|
||||
|
||||
pdf_solutions = tex.build(
|
||||
s,
|
||||
mainfile = "main.tex",
|
||||
jobname = f"{i}-sols",
|
||||
build_dir = builddir,
|
||||
solutions = True,
|
||||
|
||||
env = e,
|
||||
test = test
|
||||
)
|
||||
|
||||
target_dir = output / s.parent
|
||||
target_dir.mkdir(parents = True, exist_ok = True)
|
||||
|
||||
shutil.copy(
|
||||
pdf_nosolutions,
|
||||
target_dir / (s.stem + ".pdf")
|
||||
)
|
||||
|
||||
shutil.copy(
|
||||
pdf_solutions,
|
||||
target_dir / (s.stem + ".sols.pdf")
|
||||
)
|
||||
|
||||
|
||||
# TODO: this breaks when the output directory is empty.
|
||||
# Fix it eventually.
|
||||
if not test:
|
||||
# Delete old files
|
||||
for i in client.list():
|
||||
if i.endswith("/"):
|
||||
i = i[:-1]
|
||||
client.clean(i)
|
||||
|
||||
upload_dir(output)
|
||||
|
||||
# Remove output files so they don't persist
|
||||
# shutil.rmtree(output)
|
||||
# Shouldn't be necessary if you tell
|
||||
# jenkins to "clean before checkout."
|
@ -1,12 +0,0 @@
|
||||
# All paths are relative to the root of this repository.
|
||||
|
||||
[core]
|
||||
build_dir = "_build"
|
||||
output_dir = "_output"
|
||||
|
||||
|
||||
[[dir-of-dirs]]
|
||||
path = "Advanced"
|
||||
|
||||
[[dir-of-dirs]]
|
||||
path = "Intermediate"
|
@ -1,60 +0,0 @@
|
||||
from pathlib import Path
|
||||
import subprocess
|
||||
|
||||
def build(
|
||||
path: Path,
|
||||
*,
|
||||
solutions: bool = True,
|
||||
mainfile: str = "main.tex",
|
||||
jobname: str = "main",
|
||||
build_dir: Path = Path("build"),
|
||||
|
||||
test = False,
|
||||
env: dict = {},
|
||||
) -> Path:
|
||||
"""
|
||||
Build a directory using XeTeX.
|
||||
|
||||
Args:
|
||||
path (Path): directory to build
|
||||
solutions (bool, optional): Create a handout with solutions? Defaults to True.
|
||||
mainfile (str, optional): Path to main TeX file, relative to path. Defaults to "main.tex".
|
||||
jobname (str, optional): TeX job name. Defaults to "main".
|
||||
build_dir (Path, optional): Where to place build files. Defaults to Path("build").
|
||||
|
||||
test (bool, optional): If true, dry run. Defaults to False.
|
||||
env (dict): Dictionary of environment variables.
|
||||
|
||||
Returns:
|
||||
Path: Absolute path to output pdf.
|
||||
"""
|
||||
|
||||
# Make temporary file for TeX build arg hack
|
||||
if solutions:
|
||||
with (build_dir/"tmp.tex").open("w") as f:
|
||||
f.write("\\def\\argYesSolutions{1}\\input{" + mainfile + "}")
|
||||
else:
|
||||
with (build_dir/"tmp.tex").open("w") as f:
|
||||
f.write("\\def\\argNoSolutions{1}\\input{" + mainfile + "}")
|
||||
|
||||
cmd = subprocess.run([
|
||||
"latexmk",
|
||||
"-interaction=nonstopmode",
|
||||
"-file-line-error",
|
||||
f"-outdir={build_dir}",
|
||||
"-xelatex",
|
||||
f"-jobname={jobname}",
|
||||
build_dir/"tmp.tex"
|
||||
],
|
||||
cwd = path,
|
||||
env = env,
|
||||
stdout = None if test else subprocess.DEVNULL,
|
||||
stderr = None if test else subprocess.DEVNULL
|
||||
)
|
||||
|
||||
assert cmd.returncode == 0
|
||||
|
||||
# Remove tmp file
|
||||
(build_dir / "tmp.tex").unlink()
|
||||
|
||||
return build_dir / f"{jobname}.pdf"
|
Loading…
x
Reference in New Issue
Block a user