NEW: choose CC, list resources and create TT
This commit is contained in:
commit
81907e4c88
|
@ -0,0 +1,10 @@
|
|||
CURRENT_COURSE_LINK_DST: $HOME/CurrentCourse
|
||||
COURSES_ROOT_DIR: $HOME/Documents/Studies/Eth/Semester7
|
||||
|
||||
INFO_FILE: info.yaml
|
||||
TIMETABLE_FILE: timetable.yaml
|
||||
|
||||
CALENDAR: Timetable
|
||||
SEMESTER: HS22
|
||||
SEMESTER_START: 2022-09-19
|
||||
SEMESTER_END: 2022-12-23
|
|
@ -0,0 +1,4 @@
|
|||
title: 'Information Security Lab'
|
||||
short: 'ISL'
|
||||
web: 'https://web.ethz.ch'
|
||||
moodle: 'https://moodle.ethz.ch'
|
|
@ -0,0 +1,236 @@
|
|||
#!/bin/python3
|
||||
|
||||
import argparse
|
||||
from datetime import datetime
|
||||
from datetime import timedelta
|
||||
from datetime import date
|
||||
import os
|
||||
import sys
|
||||
import yaml
|
||||
from pathlib import Path
|
||||
from rofi import Rofi
|
||||
import subprocess
|
||||
import time
|
||||
|
||||
|
||||
APPNAME = "StudyManager"
|
||||
CONFIG_HOME = Path(os.environ["XDG_CONFIG_HOME"]) / APPNAME
|
||||
CONFIG_FILE = CONFIG_HOME / "config"
|
||||
CACHE_HOME = Path(os.environ["XDG_CACHE_HOME"]) / APPNAME
|
||||
|
||||
_config = None
|
||||
|
||||
parser = argparse.ArgumentParser(description="Helps to manage your studies.")
|
||||
parser.add_argument(
|
||||
"-c", "--chose", action="store_true", help="Chose new Current Course."
|
||||
)
|
||||
parser.add_argument(
|
||||
"-l", "--list", action="store_true", help="List resources of Current Course."
|
||||
)
|
||||
parser.add_argument(
|
||||
"-t",
|
||||
"--timetable",
|
||||
action="store_true",
|
||||
help="Insert (/Update) Current Course's timetable into calendar.",
|
||||
)
|
||||
args = vars(parser.parse_args())
|
||||
|
||||
|
||||
def config():
|
||||
global _config
|
||||
if _config is None:
|
||||
|
||||
if not CONFIG_FILE.exists():
|
||||
print(f"Config file {CONFIG_FILE} does not exist. Please create it")
|
||||
sys.exit(1)
|
||||
|
||||
with open(CONFIG_FILE, "r") as f:
|
||||
_config = yaml.safe_load(f)
|
||||
return _config
|
||||
|
||||
|
||||
def onChose():
|
||||
dst = Path(os.path.expandvars(config()["CURRENT_COURSE_LINK_DST"]))
|
||||
coursesRootDir = Path(os.path.expandvars(config()["COURSES_ROOT_DIR"]))
|
||||
|
||||
courses = [x.stem for x in coursesRootDir.iterdir() if x.is_dir()]
|
||||
|
||||
index, key = Rofi().select("Select Current Course", courses)
|
||||
|
||||
# Return on aborted input
|
||||
if key == -1:
|
||||
return
|
||||
|
||||
src = coursesRootDir / courses[index]
|
||||
|
||||
assert src.exists()
|
||||
|
||||
try:
|
||||
os.symlink(src, dst)
|
||||
except FileExistsError:
|
||||
os.remove(dst)
|
||||
os.symlink(src, dst)
|
||||
|
||||
|
||||
def onList():
|
||||
import pyperclip
|
||||
|
||||
currentCourse = Path(os.path.expandvars(config()["CURRENT_COURSE_LINK_DST"]))
|
||||
assert currentCourse.exists()
|
||||
|
||||
infoFile = currentCourse / config()["INFO_FILE"]
|
||||
|
||||
if not infoFile.exists():
|
||||
print(
|
||||
f"{infoFile} does not exists for course {currentCourse.stem}. There is nothing to list."
|
||||
)
|
||||
return
|
||||
|
||||
with open(infoFile, "r") as f:
|
||||
content = yaml.safe_load(f)
|
||||
|
||||
contentList = [f"{key}: {value}" for (key, value) in content.items()]
|
||||
|
||||
index, key = Rofi().select("Select line to copy", contentList)
|
||||
|
||||
# Return on aborted input
|
||||
if key == -1:
|
||||
return
|
||||
|
||||
selectedVal = list(content.values())[index]
|
||||
|
||||
pyperclip.copy(selectedVal)
|
||||
|
||||
|
||||
def onTimetable():
|
||||
currentCourse = Path(os.path.expandvars(config()["CURRENT_COURSE_LINK_DST"]))
|
||||
assert currentCourse.exists()
|
||||
|
||||
timetableFile = currentCourse / config()["TIMETABLE_FILE"]
|
||||
infoFile = currentCourse / config()["INFO_FILE"]
|
||||
|
||||
if not timetableFile.exists():
|
||||
print(
|
||||
f"{timetableFile} does not exists for course {currentCourse.stem}. Not possible to create timetable."
|
||||
)
|
||||
return
|
||||
|
||||
if not infoFile.exists():
|
||||
print(
|
||||
f"{infoFile} does not exists for course {currentCourse.stem}. Not possible to create timetable."
|
||||
)
|
||||
return
|
||||
|
||||
with open(timetableFile, "r") as f:
|
||||
timetables = list(yaml.safe_load(f).items())
|
||||
|
||||
with open(infoFile, "r") as f:
|
||||
info = yaml.safe_load(f)
|
||||
|
||||
semesterStart = config()["SEMESTER_START"]
|
||||
semesterEnd = config()["SEMESTER_END"]
|
||||
semesterEnd = semesterEnd + timedelta(days=1) # khal until is exclusive
|
||||
days = ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"]
|
||||
|
||||
calendar = config()["CALENDAR"]
|
||||
categories = f"{config()['SEMESTER']},{info['short']}" # No space between categories as they are removed anyways
|
||||
|
||||
# Check if timetable entry has already been created in the calendar
|
||||
out = subprocess.run(
|
||||
["khal", "search", f"--include-calendar={calendar}", categories],
|
||||
stdout=subprocess.PIPE,
|
||||
).stdout
|
||||
|
||||
if out not in [b"", b"\n"]:
|
||||
print(f"Possibly colliding timetable entry exists: {out.decode('utf-8')}")
|
||||
while True:
|
||||
choice = input("Do you want to delete them? YES/NO\n").lower()
|
||||
if choice in ["yes", "y"]:
|
||||
# Handle interactive deletion prompt by khal (https://stackoverflow.com/a/50438585/5464989)
|
||||
input_buffer = sys.stdin # a buffer to get the user input from
|
||||
output_buffer = sys.stdout # a buffer to write khal's output to
|
||||
proc = subprocess.Popen(
|
||||
[
|
||||
"khal",
|
||||
"edit",
|
||||
"--show-past",
|
||||
f"--include-calendar{calendar}",
|
||||
categories,
|
||||
],
|
||||
stdin=subprocess.PIPE, # pipe its STDIN so we can write to it
|
||||
stdout=output_buffer, # pipe directly to the output_buffer
|
||||
universal_newlines=True,
|
||||
)
|
||||
time.sleep(0.5) # give some time for khal to forward its stdout
|
||||
print(
|
||||
"", end="", file=output_buffer, flush=True
|
||||
) # print the input prompt
|
||||
print(
|
||||
input_buffer.readline(), file=proc.stdin, flush=True
|
||||
) # forward the user input
|
||||
|
||||
elif choice in ["no", "n"]:
|
||||
break
|
||||
else:
|
||||
print("Please respond with 'yes' or 'no'")
|
||||
|
||||
# Iterate over all lectures/exercises and create entry for each of them
|
||||
cmds = []
|
||||
for (_, e) in timetables: # Drop key of tuple
|
||||
|
||||
title = f"{info['short']} {e['type']}"
|
||||
location = e["location"]
|
||||
until = semesterEnd.strftime("%d/%m/%Y")
|
||||
firstOccurenceDate = semesterStart + timedelta(days=days.index(e["day"]))
|
||||
startDate = firstOccurenceDate.strftime("%d/%m/%Y")
|
||||
startTime = e["start"]
|
||||
endTime = e["end"]
|
||||
|
||||
cmd = [
|
||||
"khal",
|
||||
"new",
|
||||
f"--calendar={calendar}",
|
||||
f"--location='{location}'",
|
||||
f"--categories='{categories}'",
|
||||
"--repeat=weekly",
|
||||
f"--until={until}",
|
||||
startDate,
|
||||
startTime,
|
||||
endTime,
|
||||
title,
|
||||
]
|
||||
|
||||
cmds.append(cmd)
|
||||
|
||||
while True:
|
||||
commandList = "\n".join([" ".join(c) for c in cmds]) ## Prepare for printing
|
||||
choice = input(
|
||||
"Do you want to execute the following commands? YES/NO:\n"
|
||||
+ commandList
|
||||
+ "\n"
|
||||
).lower()
|
||||
if choice in ["yes", "y"]:
|
||||
for c in cmds:
|
||||
subprocess.call(c)
|
||||
print("Done")
|
||||
return
|
||||
elif choice in ["no", "n"]:
|
||||
print("Noting to do. Exiting.")
|
||||
return
|
||||
else:
|
||||
print("Please respond with 'yes' or 'no'\n")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
if args["chose"]:
|
||||
onChose()
|
||||
|
||||
elif args["list"]:
|
||||
onList()
|
||||
|
||||
elif args["timetable"]:
|
||||
onTimetable()
|
||||
|
||||
else:
|
||||
pass
|
|
@ -0,0 +1,18 @@
|
|||
V:
|
||||
day: 'Mon'
|
||||
start: '16:00'
|
||||
end: '18:00'
|
||||
location: 'ML D 28'
|
||||
type: 'V'
|
||||
E:
|
||||
day: 'Wed'
|
||||
start: '08:00'
|
||||
end: '09:00'
|
||||
location: 'CAB G 11'
|
||||
type: 'E'
|
||||
P:
|
||||
day: 'Thu'
|
||||
start: '16:00'
|
||||
end: '19:00'
|
||||
location: 'CHN E 42'
|
||||
type: 'P'
|
Loading…
Reference in New Issue