|
|
|
@ -1,6 +1,7 @@
|
|
|
|
|
import json, options, os, times, unicode, uri
|
|
|
|
|
import htmlparser, json, net, options, os, strscans, times, unicode, uri, xmltree
|
|
|
|
|
from strutils import ffDecimal, formatFloat, replace
|
|
|
|
|
import cligen, colorize, elvis
|
|
|
|
|
from bakalari as baka import newBakalari
|
|
|
|
|
from bakalari as baka import Grade, newBakalari
|
|
|
|
|
|
|
|
|
|
type
|
|
|
|
|
Config = object
|
|
|
|
@ -20,31 +21,40 @@ const
|
|
|
|
|
7: "Sun",
|
|
|
|
|
]
|
|
|
|
|
|
|
|
|
|
proc error(message: string) {.noReturn.} =
|
|
|
|
|
stderr.writeLine("Error: " & message)
|
|
|
|
|
quit QuitFailure
|
|
|
|
|
|
|
|
|
|
proc loadConfig(configFile = defaultConfigFile): Config =
|
|
|
|
|
try:
|
|
|
|
|
defaultConfigFile.readFile.parseJson.to(Config)
|
|
|
|
|
configFile.readFile.parseJson.to(Config)
|
|
|
|
|
except IOError:
|
|
|
|
|
stderr.writeLine "Error: Can't read the config file. Are you signed in?"
|
|
|
|
|
quit QuitFailure
|
|
|
|
|
error "Can't read the config file. Are you signed in?"
|
|
|
|
|
except JsonParsingError, JsonKindError:
|
|
|
|
|
stderr.writeLine "Error: Invalid config file. Make sure it's in JSON format."
|
|
|
|
|
quit QuitFailure
|
|
|
|
|
error "Invalid config file. Make sure it's in JSON format."
|
|
|
|
|
except KeyError:
|
|
|
|
|
stderr.writeLine "Error: The config file doesn't contain all necessary fields."
|
|
|
|
|
quit QuitFailure
|
|
|
|
|
error "The config file doesn't contain all necessary fields."
|
|
|
|
|
|
|
|
|
|
proc saveConfig(configFile = defaultConfigFile, config: Config) =
|
|
|
|
|
try:
|
|
|
|
|
configFile.writeFile((%*config).pretty)
|
|
|
|
|
except IOError:
|
|
|
|
|
stderr.writeLine "Error: Can't write the config file."
|
|
|
|
|
quit QuitFailure
|
|
|
|
|
error "Can't write the config file."
|
|
|
|
|
|
|
|
|
|
template withBakalari(configFile: string, body: untyped): untyped =
|
|
|
|
|
var config = configFile.loadConfig
|
|
|
|
|
let bakalari {.inject.} = newBakalari(config.website.parseUri, config.refreshToken)
|
|
|
|
|
try:
|
|
|
|
|
body
|
|
|
|
|
finally:
|
|
|
|
|
config.refreshToken = bakalari.refreshToken
|
|
|
|
|
configFile.saveConfig(config)
|
|
|
|
|
|
|
|
|
|
proc signin(
|
|
|
|
|
website: string,
|
|
|
|
|
username: string,
|
|
|
|
|
password: string,
|
|
|
|
|
configFile = defaultConfigFile,
|
|
|
|
|
website: string,
|
|
|
|
|
username: string,
|
|
|
|
|
password: string,
|
|
|
|
|
configFile = defaultConfigFile,
|
|
|
|
|
) =
|
|
|
|
|
## sign in to Bakaláři and save the credentials
|
|
|
|
|
let
|
|
|
|
@ -56,20 +66,56 @@ proc signin(
|
|
|
|
|
)
|
|
|
|
|
configFile.saveConfig(config)
|
|
|
|
|
|
|
|
|
|
proc average(grades: seq[Grade]): float =
|
|
|
|
|
var sum, weightSum: float
|
|
|
|
|
for grade in grades:
|
|
|
|
|
var value: int
|
|
|
|
|
if scanf(grade.text, "$i-", value):
|
|
|
|
|
sum += (value.float + 0.5) * grade.weight.float
|
|
|
|
|
weightSum += grade.weight.float
|
|
|
|
|
elif scanf(grade.text, "$i", value):
|
|
|
|
|
sum += value.float * grade.weight.float
|
|
|
|
|
weightSum += grade.weight.float
|
|
|
|
|
return weightSum ? (sum / weightSum) ! 0.0
|
|
|
|
|
|
|
|
|
|
proc grades(
|
|
|
|
|
configFile = defaultConfigFile,
|
|
|
|
|
) =
|
|
|
|
|
## display the list of grades and calculate averages
|
|
|
|
|
withBakalari(configFile):
|
|
|
|
|
for subject in baka.grades(bakalari):
|
|
|
|
|
stdout.writeLine subject.name.fgLightYellow & " " & subject.grades.average.formatFloat(ffDecimal, 2)
|
|
|
|
|
for grade in subject.grades:
|
|
|
|
|
var line = ""
|
|
|
|
|
line &= grade.text.align(2).fgLightMagenta
|
|
|
|
|
line &= " "
|
|
|
|
|
line &= ($grade.weight).align(2).fgLightCyan
|
|
|
|
|
line &= " "
|
|
|
|
|
line &= grade.caption
|
|
|
|
|
stdout.writeLine line
|
|
|
|
|
|
|
|
|
|
proc homework(
|
|
|
|
|
configFile = defaultConfigFile,
|
|
|
|
|
) =
|
|
|
|
|
## display the list of homework
|
|
|
|
|
var config = configFile.loadConfig
|
|
|
|
|
let bakalari = newBakalari(config.website.parseUri, config.refreshToken)
|
|
|
|
|
config.refreshToken = bakalari.refreshToken
|
|
|
|
|
configFile.saveConfig(config)
|
|
|
|
|
for homework in baka.homework(bakalari):
|
|
|
|
|
stdout.writeLine "----------------------------------------------------------------".fgLightGray
|
|
|
|
|
stdout.writeLine (homework.startTime.format("yyyy-MM-dd") & " / " & homework.endTime.format("yyyy-MM-dd")).fgLightCyan
|
|
|
|
|
stdout.writeLine homework.subject.fgLightYellow
|
|
|
|
|
stdout.writeLine homework.teacher.fgLightMagenta
|
|
|
|
|
stdout.writeLine homework.content
|
|
|
|
|
withBakalari(configFile):
|
|
|
|
|
for homework in baka.homework(bakalari):
|
|
|
|
|
stdout.writeLine "----------------------------------------------------------------".fgLightGray
|
|
|
|
|
stdout.writeLine (homework.startTime.format("yyyy-MM-dd") & " / " & homework.endTime.format("yyyy-MM-dd")).fgLightCyan
|
|
|
|
|
stdout.writeLine homework.subject.fgLightYellow
|
|
|
|
|
stdout.writeLine homework.teacher.fgLightMagenta
|
|
|
|
|
stdout.writeLine homework.content
|
|
|
|
|
|
|
|
|
|
proc messages(
|
|
|
|
|
configFile = defaultConfigFile,
|
|
|
|
|
) =
|
|
|
|
|
withBakalari(defaultConfigFile):
|
|
|
|
|
for message in baka.messages(bakalari):
|
|
|
|
|
stdout.writeLine "----------------------------------------------------------------".fgLightGray
|
|
|
|
|
stdout.writeLine message.sentTime.format("yyyy-MM-dd") .fgLightCyan
|
|
|
|
|
stdout.writeLine message.title.fgLightYellow
|
|
|
|
|
stdout.writeLine message.sender.fgLightMagenta
|
|
|
|
|
stdout.writeLine message.text.replace("<br />", "{{br}}").parseHtml.innerText.replace("{{br}}", "\n")
|
|
|
|
|
|
|
|
|
|
proc timetable(
|
|
|
|
|
configFile = defaultConfigFile,
|
|
|
|
@ -83,36 +129,32 @@ proc timetable(
|
|
|
|
|
try:
|
|
|
|
|
date.parse("yyyyMMdd")
|
|
|
|
|
except TimeParseError:
|
|
|
|
|
stderr.writeLine "Error: Can't parse the date. Make sure it's in YYYYMMDD format."
|
|
|
|
|
quit QuitFailure
|
|
|
|
|
error "Can't parse the date. Make sure it's in YYYYMMDD format."
|
|
|
|
|
else:
|
|
|
|
|
nextWeek ? (now() + 1.weeks) ! now()
|
|
|
|
|
var config = configFile.loadConfig
|
|
|
|
|
let bakalari = newBakalari(config.website.parseUri, config.refreshToken)
|
|
|
|
|
config.refreshToken = bakalari.refreshToken
|
|
|
|
|
configFile.saveConfig(config)
|
|
|
|
|
let timetable = baka.timetable(bakalari, permanent, some(date))
|
|
|
|
|
for day in timetable.days:
|
|
|
|
|
if oneDay:
|
|
|
|
|
if date.format("yyyyMMdd") != day.date.format("yyyyMMdd"):
|
|
|
|
|
continue
|
|
|
|
|
stdout.writeLine dayNames[day.dayOfWeek] & " " & day.date.format("yyyy-MM-dd")
|
|
|
|
|
for lesson in day.lessons:
|
|
|
|
|
var line = ""
|
|
|
|
|
line &= lesson.hour.number
|
|
|
|
|
line &= ". "
|
|
|
|
|
line &= lesson.hour.beginTime.align(5, '0'.Rune).fgLightCyan
|
|
|
|
|
line &= "-".fgLightCyan
|
|
|
|
|
line &= lesson.hour.endTime.align(5, '0'.Rune).fgLightCyan
|
|
|
|
|
line &= " "
|
|
|
|
|
line &= lesson.subject.abbrev.align(4).fgLightYellow
|
|
|
|
|
line &= " "
|
|
|
|
|
line &= lesson.teacher.abbrev.align(4).fgLightMagenta
|
|
|
|
|
line &= " "
|
|
|
|
|
line &= lesson.room.abbrev.align(4).fgLightGreen
|
|
|
|
|
stdout.writeLine line
|
|
|
|
|
if lesson.change.isSome:
|
|
|
|
|
stdout.writeLine ("^ " & lesson.change.unsafeGet.fgLightRed).fgLightRed
|
|
|
|
|
withBakalari(configFile):
|
|
|
|
|
let timetable = baka.timetable(bakalari, permanent, some(date))
|
|
|
|
|
for day in timetable.days:
|
|
|
|
|
if oneDay:
|
|
|
|
|
if date.format("yyyyMMdd") != day.date.format("yyyyMMdd"):
|
|
|
|
|
continue
|
|
|
|
|
stdout.writeLine dayNames[day.dayOfWeek] & " " & day.date.format("yyyy-MM-dd")
|
|
|
|
|
for lesson in day.lessons:
|
|
|
|
|
var line = ""
|
|
|
|
|
line &= lesson.hour.number
|
|
|
|
|
line &= ". "
|
|
|
|
|
line &= lesson.hour.beginTime.align(5, '0'.Rune).fgLightCyan
|
|
|
|
|
line &= "-".fgLightCyan
|
|
|
|
|
line &= lesson.hour.endTime.align(5, '0'.Rune).fgLightCyan
|
|
|
|
|
line &= " "
|
|
|
|
|
line &= lesson.subject.abbrev.align(4).fgLightYellow
|
|
|
|
|
line &= " "
|
|
|
|
|
line &= lesson.teacher.abbrev.align(4).fgLightMagenta
|
|
|
|
|
line &= " "
|
|
|
|
|
line &= lesson.room.abbrev.align(4).fgLightGreen
|
|
|
|
|
stdout.writeLine line
|
|
|
|
|
if lesson.change.isSome:
|
|
|
|
|
stdout.writeLine ("^ " & lesson.change.unsafeGet.fgLightRed).fgLightRed
|
|
|
|
|
|
|
|
|
|
try:
|
|
|
|
|
dispatchMulti(
|
|
|
|
@ -122,9 +164,15 @@ try:
|
|
|
|
|
"password": "your password (will not be stored anywhere)",
|
|
|
|
|
"config-file": "where to store the credentials",
|
|
|
|
|
}],
|
|
|
|
|
[grades, help = {
|
|
|
|
|
"config-file": "where the credentials are stored",
|
|
|
|
|
}],
|
|
|
|
|
[homework, help = {
|
|
|
|
|
"config-file": "where the credentials are stored",
|
|
|
|
|
}],
|
|
|
|
|
[messages, help = {
|
|
|
|
|
"config-file": "where the credentials are stored",
|
|
|
|
|
}],
|
|
|
|
|
[timetable, help = {
|
|
|
|
|
"config-file": "where the credentials are stored",
|
|
|
|
|
"date": "any date inside the week you want to display, in YYYYMMDD format (defaults to today)",
|
|
|
|
@ -134,5 +182,6 @@ try:
|
|
|
|
|
}],
|
|
|
|
|
)
|
|
|
|
|
except OSError:
|
|
|
|
|
stderr.writeLine "Error: Generic OS error. Check your internet connection."
|
|
|
|
|
quit QuitFailure
|
|
|
|
|
error "Generic OS error. Check your internet connection."
|
|
|
|
|
except SslError:
|
|
|
|
|
error "Connection interrupted. Check your internet connection."
|
|
|
|
|