Compare commits

...

9 Commits

3 changed files with 92 additions and 19 deletions

View File

@ -1,6 +1,6 @@
# Package
version = "1.3.2"
version = "1.5.0"
author = "Adam Blažek"
description = "CLI client for Bakaláři"
license = "GPL-3.0"
@ -12,6 +12,8 @@ bin = @["bk"]
# Dependencies
requires "nim >= 1.2.4"
requires "cligen >= 1.2.0"
requires "elvis >= 0.2.0"
requires "colorize >= 0.2.0"
requires "elvis >= 0.2.0"
requires "zero_functional >= 1.2.0"

View File

@ -1,11 +1,21 @@
import httpcore, httpclient, json, options, strformat, tables, times, uri
import elvis
import elvis, zero_functional
type
Bakalari* = ref object
website*: Uri
accessToken*: string
refreshToken*: string
Grade* = object
text*: string
weight*: int
caption*: string
addTime*: DateTime
editTime*: DateTime
GradedSubject* = object
name*: string
abbrev*: string
grades*: seq[Grade]
Homework* = object
id*: string
subject*: string
@ -113,6 +123,23 @@ proc postEndpoint*(bakalari: Bakalari, endpoint: string): JsonNode =
bakalari.renewTokens
result = body.parseJson
iterator grades*(bakalari: Bakalari): GradedSubject =
let root = bakalari.getEndpoint("marks")
for subjectNode in root{"Subjects"}:
yield GradedSubject(
name: subjectNode{"Subject"}{"Name"}.getStr,
abbrev: subjectNode{"Subject"}{"Abbrev"}.getStr,
grades: subjectNode{"Marks"} --> (gradeNode) --> map(
Grade(
text: gradeNode{"MarkText"}.getStr,
weight: gradeNode{"Weight"}.getInt,
caption: gradeNode{"Caption"}.getStr,
addTime: gradeNode{"MarkDate"}.getStr.parse(iso8601),
editTime: gradeNode{"EditDate"}.getStr.parse(iso8601),
)
)
)
iterator homework*(bakalari: Bakalari): Homework =
let root = bakalari.getEndpoint("homeworks")
for node in root{"Homeworks"}.getElems:

View File

@ -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,25 +21,25 @@ 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
@ -65,6 +66,34 @@ 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,
) =
@ -77,6 +106,17 @@ proc homework(
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,
date = "",
@ -89,8 +129,7 @@ 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()
withBakalari(configFile):
@ -125,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)",
@ -137,7 +182,6 @@ try:
}],
)
except OSError:
stderr.writeLine "Error: Generic OS error. Check your internet connection."
error "Generic OS error. Check your internet connection."
except SslError:
stderr.writeLine "Error: Connection interrupted. Check your internet connection."
quit QuitFailure
error "Connection interrupted. Check your internet connection."