Compare commits

..

1 commit

31 changed files with 324 additions and 127 deletions

1
.gitignore vendored
View file

@ -4,4 +4,3 @@ scripts/test.py
scripts/.idea
scripts/credentials.json
scripts/token.pickle
__pycache__

View file

@ -23,7 +23,7 @@ SOFTWARE.
MIT License
Copyright (c) 2021,2022 Maximilian Keßler
Copyright (c) 2021 Maximilian Keßler
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal

View file

@ -1,16 +0,0 @@
PKGNAME=university-setup
PREFIX=mkessler
install:
mkdir -p $(DESTDIR)/opt/$(PREFIX)/${PKGNAME}
mkdir -p $(DESTDIR)/etc/opt/$(PREFIX)/${PKGNAME}
cp src/* $(DESTDIR)/opt/$(PREFIX)/${PKGNAME}
cp config/* $(DESTDIR)/etc/opt/$(PREFIX)/${PKGNAME}
install -Dm644 "LICENSE" $(DESTDIR)/usr/share/licenses/$(PREFIX)/${PKGNAME}/LICENSE
uninstall:
rm -rf $(DESTDIR)/opt/$(PREFIX)/${PKGNAME}
rm -rf $(DESTDIR)/etc/opt/$(PREFIX)/${PKGNAME}
rm -rf $(DESTDIR)/usr/share//licenses/$(PREFIX)/${PKGNAME}

View file

@ -1,16 +1,9 @@
# Fork
This fork of [gillescastell/university-setup][setup] contains my personal customatizations to the setup Gilles uses. The main point is that the `info.yaml` file now contains options for specifying the subfolders for the notes, as well as subfolders for the lectures. This helps me re-use the `current_course` directory for other data as well (e.g. my exercise sheets) while having a single dedicated folder for my lecture notes. It is implemented with an additional `Notes` class that handles this. Possibly, one can extend this to e.g. an `Exercises` class that one can add to each course etc.
This fork contains my personal customatizations to the setup Gilles uses. The main point is that the `info.yaml` file now contains options for specifying the subfolders for the notes, as well as subfolders for the lectures. This helps me re-use the `current_course` directory for other data as well (e.g. my exercise sheets) while having a single dedicated folder for my lecture notes. It is implemented with an additional `Notes` class that handles this. Possibly, one can extend this to e.g. an `Exercises` class that one can add to each course etc.
Additionally, this version features a `.courseignore` file you can place in the `ROOT` folder to ignore some directories for the courses.
##
If you want to install this, run `make install` on your favourite unix operating system.
This installs into `/opt/mkessler/university-setup`.
There is also a [PKGBUILD][pkgbuild] for Arch linux available.
# Managing LaTeX lecture notes
This repository complements my [third blog post about my note taking setup](https://castel.dev/post/lecture-notes-3).
@ -21,7 +14,7 @@ This repository complements my [third blog post about my note taking setup](http
ROOT
├── riemann-surfaces
│   ├── info.yaml
│   ├── master.texvllt sollte ich
│   ├── master.tex
│   ├── lec_01.tex
│   ├── ...
│   ├── lec_13.tex
@ -159,7 +152,3 @@ Some utility functions
#### `compile-all-masters.py`
This script updates the `master.tex` files to include all lectures and compiles them. I use when syncing my notes to the cloud. This way I always have access to my compiles notes on my phone.
[setup]: https://github.com/gillescastel/university-setup
[pkgbuild]: https://git.abstractnonsen.se/arch/university-setup-git

265
preamble.tex Normal file
View file

@ -0,0 +1,265 @@
% Some basic packages
\usepackage[utf8]{inputenc}
\usepackage[T1]{fontenc}
\usepackage{textcomp}
\usepackage[dutch]{babel}
\usepackage{url}
\usepackage{graphicx}
\usepackage{float}
\usepackage{booktabs}
\usepackage{enumitem}
\pdfminorversion=7
% Don't indent paragraphs, leave some space between them
\usepackage{parskip}
% Hide page number when page is empty
\usepackage{emptypage}
\usepackage{subcaption}
\usepackage{multicol}
\usepackage{xcolor}
% Other font I sometimes use.
% \usepackage{cmbright}
% Math stuff
\usepackage{amsmath, amsfonts, mathtools, amsthm, amssymb}
% Fancy script capitals
\usepackage{mathrsfs}
\usepackage{cancel}
% Bold math
\usepackage{bm}
% Some shortcuts
\newcommand\N{\ensuremath{\mathbb{N}}}
\newcommand\R{\ensuremath{\mathbb{R}}}
\newcommand\Z{\ensuremath{\mathbb{Z}}}
\renewcommand\O{\ensuremath{\emptyset}}
\newcommand\Q{\ensuremath{\mathbb{Q}}}
\newcommand\C{\ensuremath{\mathbb{C}}}
% Easily typeset systems of equations (French package)
\usepackage{systeme}
% Put x \to \infty below \lim
\let\svlim\lim\def\lim{\svlim\limits}
%Make implies and impliedby shorter
\let\implies\Rightarrow
\let\impliedby\Leftarrow
\let\iff\Leftrightarrow
\let\epsilon\varepsilon
% Add \contra symbol to denote contradiction
\usepackage{stmaryrd} % for \lightning
\newcommand\contra{\scalebox{1.5}{$\lightning$}}
% \let\phi\varphi
% Command for short corrections
% Usage: 1+1=\correct{3}{2}
\definecolor{correct}{HTML}{009900}
\newcommand\correct[2]{\ensuremath{\:}{\color{red}{#1}}\ensuremath{\to }{\color{correct}{#2}}\ensuremath{\:}}
\newcommand\green[1]{{\color{correct}{#1}}}
% horizontal rule
\newcommand\hr{
\noindent\rule[0.5ex]{\linewidth}{0.5pt}
}
% hide parts
\newcommand\hide[1]{}
% si unitx
\usepackage{siunitx}
\sisetup{locale = FR}
% Environments
\makeatother
% For box around Definition, Theorem, \ldots
\usepackage{mdframed}
\mdfsetup{skipabove=1em,skipbelow=0em}
\theoremstyle{definition}
\newmdtheoremenv[nobreak=true]{definitie}{Definitie}
\newmdtheoremenv[nobreak=true]{eigenschap}{Eigenschap}
\newmdtheoremenv[nobreak=true]{gevolg}{Gevolg}
\newmdtheoremenv[nobreak=true]{lemma}{Lemma}
\newmdtheoremenv[nobreak=true]{propositie}{Propositie}
\newmdtheoremenv[nobreak=true]{stelling}{Stelling}
\newmdtheoremenv[nobreak=true]{wet}{Wet}
\newmdtheoremenv[nobreak=true]{postulaat}{Postulaat}
\newmdtheoremenv{conclusie}{Conclusie}
\newmdtheoremenv{toemaatje}{Toemaatje}
\newmdtheoremenv{vermoeden}{Vermoeden}
\newtheorem*{herhaling}{Herhaling}
\newtheorem*{intermezzo}{Intermezzo}
\newtheorem*{notatie}{Notatie}
\newtheorem*{observatie}{Observatie}
\newtheorem*{oef}{Oefening}
\newtheorem*{opmerking}{Opmerking}
\newtheorem*{praktisch}{Praktisch}
\newtheorem*{probleem}{Probleem}
\newtheorem*{terminologie}{Terminologie}
\newtheorem*{toepassing}{Toepassing}
\newtheorem*{uovt}{UOVT}
\newtheorem*{vb}{Voorbeeld}
\newtheorem*{vraag}{Vraag}
\newmdtheoremenv[nobreak=true]{definition}{Definition}
\newtheorem*{eg}{Example}
\newtheorem*{notation}{Notation}
\newtheorem*{previouslyseen}{As previously seen}
\newtheorem*{remark}{Remark}
\newtheorem*{note}{Note}
\newtheorem*{problem}{Problem}
\newtheorem*{observe}{Observe}
\newtheorem*{property}{Property}
\newtheorem*{intuition}{Intuition}
\newmdtheoremenv[nobreak=true]{prop}{Proposition}
\newmdtheoremenv[nobreak=true]{theorem}{Theorem}
\newmdtheoremenv[nobreak=true]{corollary}{Corollary}
% End example and intermezzo environments with a small diamond (just like proof
% environments end with a small square)
\usepackage{etoolbox}
\AtEndEnvironment{vb}{\null\hfill$\diamond$}%
\AtEndEnvironment{intermezzo}{\null\hfill$\diamond$}%
% \AtEndEnvironment{opmerking}{\null\hfill$\diamond$}%
% Fix some spacing
% http://tex.stackexchange.com/questions/22119/how-can-i-change-the-spacing-before-theorems-with-amsthm
\makeatletter
\def\thm@space@setup{%
\thm@preskip=\parskip \thm@postskip=0pt
}
% Exercise
% Usage:
% \oefening{5}
% \suboefening{1}
% \suboefening{2}
% \suboefening{3}
% gives
% Oefening 5
% Oefening 5.1
% Oefening 5.2
% Oefening 5.3
\newcommand{\oefening}[1]{%
\def\@oefening{#1}%
\subsection*{Oefening #1}
}
\newcommand{\suboefening}[1]{%
\subsubsection*{Oefening \@oefening.#1}
}
% \lecture starts a new lecture (les in dutch)
%
% Usage:
% \lecture{1}{di 12 feb 2019 16:00}{Inleiding}
%
% This adds a section heading with the number / title of the lecture and a
% margin paragraph with the date.
% I use \dateparts here to hide the year (2019). This way, I can easily parse
% the date of each lecture unambiguously while still having a human-friendly
% short format printed to the pdf.
\usepackage{xifthen}
\def\testdateparts#1{\dateparts#1\relax}
\def\dateparts#1 #2 #3 #4 #5\relax{
\marginpar{\small\textsf{\mbox{#1 #2 #3 #5}}}
}
\def\@lecture{}%
\newcommand{\lecture}[3]{
\ifthenelse{\isempty{#3}}{%
\def\@lecture{Lecture #1}%
}{%
\def\@lecture{Lecture #1: #3}%
}%
\subsection*{\@lecture}
\marginpar{\small\textsf{\mbox{#2}}}
}
% These are the fancy headers
\usepackage{fancyhdr}
\pagestyle{fancy}
% LE: left even
% RO: right odd
% CE, CO: center even, center odd
% My name for when I print my lecture notes to use for an open book exam.
% \fancyhead[LE,RO]{Gilles Castel}
\fancyhead[RO,LE]{\@lecture} % Right odd, Left even
\fancyhead[RE,LO]{} % Right even, Left odd
\fancyfoot[RO,LE]{\thepage} % Right odd, Left even
\fancyfoot[RE,LO]{} % Right even, Left odd
\fancyfoot[C]{\leftmark} % Center
\makeatother
% Todonotes and inline notes in fancy boxes
\usepackage{todonotes}
\usepackage{tcolorbox}
% Make boxes breakable
\tcbuselibrary{breakable}
% Verbetering is correction in Dutch
% Usage:
% \begin{verbetering}
% Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod
% tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At
% vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren,
% no sea takimata sanctus est Lorem ipsum dolor sit amet.
% \end{verbetering}
\newenvironment{verbetering}{\begin{tcolorbox}[
arc=0mm,
colback=white,
colframe=green!60!black,
title=Opmerking,
fonttitle=\sffamily,
breakable
]}{\end{tcolorbox}}
% Noot is note in Dutch. Same as 'verbetering' but color of box is different
\newenvironment{noot}[1]{\begin{tcolorbox}[
arc=0mm,
colback=white,
colframe=white!60!black,
title=#1,
fonttitle=\sffamily,
breakable
]}{\end{tcolorbox}}
% Figure support as explained in my blog post.
\usepackage{import}
\usepackage{xifthen}
\usepackage{pdfpages}
\usepackage{transparent}
\newcommand{\incfig}[1]{%
\def\svgwidth{\columnwidth}
\import{./figures/}{#1.pdf_tex}
}
% Fix some stuff
% %http://tex.stackexchange.com/questions/76273/multiple-pdfs-with-page-group-included-in-a-single-page-warning
\pdfsuppresswarningpagegroup=1
% My name
\author{Gilles Castel}

View file

@ -1,5 +0,0 @@
google-api-python-client
google-auth-oauthlib
python-dateutil
pytz
PyYAML

View file

@ -1,6 +1,5 @@
from pathlib import Path
import pytz
# default is 'primary', if you are using a separate calendar for your course schedule,
# your calendarId (which you can find by going to your Google Calendar settings, selecting
# the relevant calendar and scrolling down to Calendar ID) probably looks like
@ -11,7 +10,7 @@ USERCALENDARID = 'primary'
CURRENT_COURSE_SYMLINK = Path('~/current_course').expanduser()
CURRENT_COURSE_ROOT = CURRENT_COURSE_SYMLINK.resolve()
CURRENT_COURSE_WATCH_FILE = Path('/tmp/current_course').resolve()
ROOT = Path('~/uni/semester-6').expanduser()
ROOT = Path('~/Uni/semester-5').expanduser()
DATE_FORMAT = '%a %d %b %Y'
LOCALE = "de_DE.utf8"
COURSE_IGNORE_FILE = '.courseignore'
@ -23,11 +22,10 @@ DEFAULT_NEW_LECTURE_HEADER = r'\lecture[]{{{date}}}{{{title}}}'
DEFAULT_NEW_LECTURE_TITLE = 'Untitled'
DEFAULT_LECTURE_SEARCH_REGEX = r'lecture.*({\d*})?{(.*?)}{(.*)}'
DEFAULT_IMPORT_INDENTATION = 4
FALLBACK_COURSE_INFO_FILE = Path(__file__).parent.resolve() / 'fallback.yaml'
TIMEZONE = pytz.timezone('CET')
SCHEDULER_DELAY = 60
DEFAULT_LATEX_COUNTER_AUX_FILE_EXTENSION = '.cnt'
TERMINAL = 'i3-sensible-terminal'
EDITOR = 'vim'
NEW_EXERCISE_SHEET_HEADER = '\n'.join([
r"%! TEX root = ./*.tex",

View file

@ -20,7 +20,7 @@ from google_auth_oauthlib.flow import InstalledAppFlow
from google.auth.transport.requests import Request
from courses import Courses
from config_loader import USERCALENDARID, TIMEZONE, SCHEDULER_DELAY
from config import USERCALENDARID, TIMEZONE, SCHEDULER_DELAY
courses = Courses()

View file

@ -4,7 +4,7 @@ import warnings
import yaml
from typing import List
from config_loader import ROOT, CURRENT_COURSE_ROOT, CURRENT_COURSE_SYMLINK, CURRENT_COURSE_WATCH_FILE, COURSE_IGNORE_FILE, \
from config import ROOT, CURRENT_COURSE_ROOT, CURRENT_COURSE_SYMLINK, CURRENT_COURSE_WATCH_FILE, COURSE_IGNORE_FILE, \
COURSE_INFO_FILE_NAME, FALLBACK_COURSE_INFO_FILE
from notes import Notes
from links import Links
@ -23,7 +23,13 @@ class Course:
f"file in the directory or add the directory to the course ignore file named"
f" '{COURSE_IGNORE_FILE}' in your root directory ({ROOT})")
self.info = {'title': str(path.stem) + ' (unnamed course)'}
if FALLBACK_COURSE_INFO_FILE.is_file():
fallback_file = yaml.safe_load(FALLBACK_COURSE_INFO_FILE.open())
else:
warnings.warn(f"No fallback course info file found. Program might crash if your provided info files do not"
f"have the correct file format or are missing specified values. Provide the fallback course"
f"file at {FALLBACK_COURSE_INFO_FILE}.")
fallback_file = {}
self.info = merge_dictionaries(self.info, fallback_file)
self._notes = None
self._links = None

View file

@ -2,20 +2,15 @@ from file_list import Files, FileHandle, FileType
from pathlib import Path
from typing import Dict
from utils import normalize
from config_loader import NEW_EXERCISE_SHEET_HEADER
from config import NEW_EXERCISE_SHEET_HEADER
class ExerciseWriteUp(FileHandle):
def __init__(self, root_dir: Path, course):
self.root_dir = root_dir
self.course = course
try:
tex_file = next(self.root_dir.rglob('*.tex'))
except StopIteration:
print("No valid '.tex' file found in directory {}, can't instantiate write up here".format(root_dir))
# TODO: raise proper error
exit(1)
FileHandle.__init__(self, tex_file, FileType.tex)
FileHandle.__init__(self, next(self.root_dir.rglob('*.tex')), FileType.tex)
class Exercise:
def __init__(self, course, number: int):
@ -57,6 +52,7 @@ class Exercises(list):
self._solutions = None
self._writeups = None
self._sheets = Files(self.sheet_root)
self.ignored_folders = [self.sheet_root, self.solutions_root]
list.__init__(self, (Exercise(self.course, num) for num in map(lambda s: s.number, self._sheets)))
@property
@ -72,7 +68,7 @@ class Exercises(list):
@property
def writeups(self):
if not self._writeups:
dirs = [d for d in self.root.glob('ub*') if d.is_dir()]
dirs = list(d for d in self.root.iterdir() if d.is_dir() and d not in self.ignored_folders)
self._writeups = sorted((ExerciseWriteUp(d, self.course) for d in dirs), key=lambda e: e.number)
return self._writeups

View file

@ -17,7 +17,6 @@ literature:
notes:
path: '.'
texinputs: '.'
build_dir: '.'
master_file: 'master.tex'
full_file: 'full.tex'
lectures:

29
scripts/ieeeeqnarray.py Normal file
View file

@ -0,0 +1,29 @@
# A python script designed to parse the contents of an \[ \] in latex (given in the arguments), and make a nice IEEEeqnarray* with correct indent and columns from it.
import sys
indent=len(sys.argv[1])-4 # Indent got by the indent of the leading \[ of equation
topline = sys.argv[2].strip() # Get the content of the first line of the equation
level = 0 # Counts the 'level' of stacked braces
position = 0 # Will save the position of the found = in the top line
parsed_line = '' # Parsed top line
for x in topline:
if x in ['(','[','{']:
level+=1
parsed_line+=x
elif x in [')',']','}']:
level-=1
parsed_line+=x
elif level == 0 and x == '=':
position = len(parsed_line)
parsed_line+='& = &'
else:
parsed_line+=x
# Set correct indent and newline to the parsed line
parsed_line = ' '*(indent+4) + parsed_line[0:-1].strip() + ' \\\\\n'
# Print the output, adding \begin{}, the top line, a new line with '& = &' and \end{}
print(" "*indent +"\\begin{IEEEeqnarray*}{rCl}\n" + parsed_line + ' '*(position+indent+4) + "& = & \n" + " "*indent + "\\end{IEEEeqnarray*}")

View file

@ -46,7 +46,7 @@ def main(args):
arglist = []
if len(args) > 1:
path = Path(args[1])
arglist = list(path.glob('*.aux')) + list(path.glob('build/*.aux'))
arglist = list(path.glob('*.aux'))
else:
arglist = ['/home/maximilian/current_course/full.aux']
@ -63,7 +63,6 @@ def main(args):
return command.strip()
if __name__ == '__main__':
selected_label = main(sys.argv)
print(selected_label)

0
src/new-writeup.py → scripts/new-writeup.py Executable file → Normal file
View file

View file

@ -4,7 +4,7 @@ import subprocess
from pathlib import Path
from typing import Dict
from config_loader import LECTURE_START_MARKER, LECTURE_END_MARKER, DEFAULT_IMPORT_INDENTATION, \
from config import LECTURE_START_MARKER, LECTURE_END_MARKER, DEFAULT_IMPORT_INDENTATION, \
DEFAULT_LATEX_COUNTER_AUX_FILE_EXTENSION
from window_subprocess import edit
from lectures import Lectures, number2filename
@ -21,7 +21,6 @@ class Notes:
self.full_file: Path = self.root / self.info['full_file']
self.texinputs: Path = self.root / self.info['texinputs']
self._lectures = None
self.build_dir: Path = self.root / self.info['build_dir']
@staticmethod
def get_header_footer(filepath):
@ -64,11 +63,8 @@ class Notes:
if not setcounters:
return ''
if lec - 1 not in lecture_list and self.full_file:
cnt_file = self.full_file.with_suffix(DEFAULT_LATEX_COUNTER_AUX_FILE_EXTENSION)
if not cnt_file.exists():
cnt_file = self.full_file.parent / 'build' / cnt_file.name
return dict2setcounters(parse_counters(
cnt_file,
self.full_file.with_suffix(DEFAULT_LATEX_COUNTER_AUX_FILE_EXTENSION),
{'lecture': lec}
))
return ''
@ -102,7 +98,7 @@ class Notes:
def open_full(self):
result = subprocess.run(
['zathura', str(self.build_dir / self.full_file.with_suffix('.pdf').name)],
['zathura', str(self.full_file.with_suffix('.pdf'))],
stdout=subprocess.DEVNULL,
stderr=subprocess.DEVNULL
)
@ -110,7 +106,7 @@ class Notes:
def open_terminal(self):
result = subprocess.Popen(
['i3-sensible-terminal'], env=self.environment(), cwd=self.root
['termite'], env=self.environment(), cwd=self.root
)
def compile_master(self):

0
src/open.py → scripts/open.py Executable file → Normal file
View file

View file

@ -2,7 +2,7 @@
import re
from pathlib import Path
from typing import Dict
from config_loader import DEFAULT_IMPORT_INDENTATION
from config import DEFAULT_IMPORT_INDENTATION
def parse_counters(filepath: Path, break_point: Dict) -> Dict:

View file

@ -2,6 +2,7 @@
from courses import Courses
from exercises import Exercises
from rofi import rofi
from config import MAX_LEN
import sys

View file

@ -2,7 +2,7 @@
from courses import Courses
from rofi import rofi
from utils import generate_short_title
from config_loader import MAX_LEN
from config import MAX_LEN
notes = Courses().current.notes
lectures = notes.lectures

View file

@ -3,7 +3,7 @@ from datetime import datetime
from typing import Dict
import warnings
from config_loader import MAX_LEN
from config import MAX_LEN
def beautify(string):

View file

@ -4,20 +4,14 @@ import subprocess
from pathlib import Path
import os
from config_loader import TERMINAL, EDITOR
def edit(filepath: Path, rootpath: Path = None, env=os.environ, servername='tex lecture'):
if not rootpath:
rootpath = filepath.root
subprocess.Popen([
TERMINAL,
"termite",
"-e",
EDITOR,
f"--servername",
f"{servername}",
f"--remote-silent",
f"{str(filepath)}"
f"vim --servername {servername} --remote-silent {str(filepath)}"
], env=env, cwd=str(rootpath))

View file

@ -1,53 +0,0 @@
import shutil
from pathlib import Path
import os
import sys
# We read a configuration file for university setup that is located
# in $XDG_CONFIG_HOME/university-setup/config.cfg
def get_default_file(name: str) -> Path:
# System installation
f1 = Path('/etc/opt/mkessler/university-setup') / name
# no installation, try relative path
f2 = Path(__file__).parent.parent / 'config' / name
if f1.exists():
return f1
if f2.exists():
return f2
raise FileNotFoundError(f'Default file {name} not found, bad installation.')
# Ensure that config.py and fallback.yaml are present
# in the config directory and returns this directory
def get_config_dir() -> Path:
if 'XDG_CONFIG_HOME' in os.environ.keys():
xdg_config_home = Path(os.environ['XDG_CONFIG_HOME']).resolve()
else:
xdg_config_home = Path('~/.config').expanduser().resolve()
config_file = xdg_config_home / 'university-setup' / 'config.py'
fallback_file = xdg_config_home / 'university-setup' / 'fallback.yaml'
config_file.parent.mkdir(exist_ok=True, parents=True)
# Copy defaults if not present already
if not config_file.exists():
shutil.copy(get_default_file('config.py'), config_file)
print(f'Initialized default config file at {str(config_file)}.')
if not fallback_file.exists():
shutil.copy(get_default_file('fallback.yaml'), fallback_file)
print(f'Initialized default fallback file at {str(fallback_file)}.')
return config_file.parent.absolute()
FALLBACK_COURSE_INFO_FILE = get_config_dir() / 'fallback.yaml'
sys.path.append(str(get_config_dir()))
# Note that IDEs will probably complain about this, since they cannot find the module right now
# they might also flag this as an unused import, but the imported config values
# are in turn imported by all the oder scripts
from config import *
# future: potentially check that config in fact defines all symbols that we need