bk/src/bakalari.nim

209 lines
6.7 KiB
Nim

import httpcore, httpclient, json, options, strformat, tables, times, uri
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
teacher*: string
content*: string
startTime*: DateTime
endTime*: DateTime
Message* = object
title*: string
sender*: string
text*: string
sentTime*: DateTime
Hour* = ref object
number*: string
beginTime*: string
endTime*: string
Room* = ref object
name*: string
abbrev*: string
Subject* = ref object
name*: string
abbrev*: string
Teacher* = ref object
name*: string
abbrev*: string
Lesson* = object
hour*: Hour
room*: Room
subject*: Subject
teacher*: Teacher
change*: Option[string]
Day* = object
lessons*: seq[Lesson]
dayOfWeek*: int
date*: DateTime
Timetable* = object
days*: seq[Day]
const
iso8601 = initTimeFormat("yyyy-MM-dd'T'HH:mm:sszzz")
let
invalidHour = Hour(number: "-", beginTime: "--:--", endTime: "--:--")
invalidRoom = Room(name: "---", abbrev: "---")
invalidSubject = Subject(name: "---", abbrev: "---")
invalidTeacher = Teacher(name: "---", abbrev: "---")
proc newBakalari*(website: Uri, username: string, password: string): Bakalari =
new result
result.website = website
let
api = website / "api/login"
client = newHttpClient()
response = client.post($api, body = &"client_id=ANDR&grant_type=password&username={username}&password={password}")
root = response.body.parseJson
result.accessToken = root{"access_token"}.getStr
result.refreshToken = root{"refresh_token"}.getStr
proc newBakalari*(website: Uri, refreshToken: string): Bakalari =
new result
result.website = website
let
api = website / "api/login"
client = newHttpClient()
response = client.post($api, body = &"client_id=ANDR&grant_type=refresh_token&refresh_token={refreshToken}")
root = response.body.parseJson
result.accessToken = root{"access_token"}.getStr
result.refreshToken = root{"refresh_token"}.getStr
func accessToken*(bakalari: Bakalari): string =
bakalari.accessToken
func refreshToken*(bakalari: Bakalari): string =
bakalari.refreshToken
proc renewTokens(bakalari: Bakalari) =
let
api = bakalari.website / "api/login"
client = newHttpClient()
response = client.post($api, body = &"client_id=ANDR&grant_type=refresh_token&refresh_token={bakalari.refreshToken}")
body = response.body
root = body.parseJson
bakalari.accessToken = root{"access_token"}.getStr
bakalari.refreshToken = root{"refresh_token"}.getStr
proc getEndpoint*(bakalari: Bakalari, endpoint: string): JsonNode =
let
api = bakalari.website / "api/3" / endpoint
client = newHttpClient(headers = newHttpHeaders {
"Authorization": "Bearer " & bakalari.accessToken,
})
response = client.get($api)
body = response.body
bakalari.renewTokens
result = body.parseJson
proc postEndpoint*(bakalari: Bakalari, endpoint: string): JsonNode =
let
api = bakalari.website / "api/3" / endpoint
client = newHttpClient(headers = newHttpHeaders {
"Authorization": "Bearer " & bakalari.accessToken,
})
response = client.post($api)
body = response.body
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:
yield Homework(
id: node{"ID"}.getStr,
subject: node{"Subject"}{"Name"}.getStr,
teacher: node{"Teacher"}.getStr,
content: node{"Content"}.getStr,
startTime: node{"DateStart"}.getStr.parse(iso8601),
endTime: node{"DateEnd"}.getStr.parse(iso8601),
)
iterator messages*(bakalari: Bakalari): Message =
let root = bakalari.postEndpoint("komens/messages/received")
for node in root{"Messages"}.getElems:
yield Message(
title: node{"Title"}.getStr,
sender: node{"Sender"}{"Name"}.getStr,
text: node{"Text"}.getStr,
sentTime: node{"SentDate"}.getStr.parse(iso8601)
)
proc timetable*(bakalari: Bakalari, permanent: bool, date: Option[DateTime] = none(DateTime)): Timetable =
var endpoint = permanent ? "timetable/permanent" ! "timetable/actual"
if date.isSome:
endpoint &= "?date=" & date.unsafeGet.format("yyyy-MM-dd")
let root = bakalari.getEndpoint(endpoint)
var hours: Table[int, Hour]
for hourNode in root{"Hours"}:
hours[hourNode{"Id"}.getInt] = Hour(
number: hourNode{"Caption"}.getStr,
beginTime: hourNode{"BeginTime"}.getStr,
endTime: hourNode{"EndTime"}.getStr,
)
var rooms: Table[string, Room]
for roomNode in root{"Rooms"}:
rooms[roomNode{"Id"}.getStr] = Room(
name: roomNode{"Name"}.getStr,
abbrev: roomNode{"Abbrev"}.getStr,
)
var subjects: Table[string, Subject]
for subjectNode in root{"Subjects"}:
subjects[subjectNode{"Id"}.getStr] = Subject(
name: subjectNode{"Name"}.getStr,
abbrev: subjectNode{"Abbrev"}.getStr,
)
var teachers: Table[string, Teacher]
for teacherNode in root{"Teachers"}:
teachers[teacherNode{"Id"}.getStr] = Teacher(
name: teacherNode{"Name"}.getStr,
abbrev: teacherNode{"Abbrev"}.getStr,
)
for dayNode in root{"Days"}:
var day: Day
for lessonNode in dayNode{"Atoms"}:
var lesson: Lesson
lesson.hour = hours.getOrDefault(lessonNode{"HourId"}.getInt, invalidHour)
lesson.room = rooms.getOrDefault(lessonNode{"RoomId"}.getStr, invalidRoom)
lesson.subject = subjects.getOrDefault(lessonNode{"SubjectId"}.getStr, invalidSubject)
lesson.teacher = teachers.getOrDefault(lessonNode{"TeacherId"}.getStr, invalidTeacher)
if lessonNode{"Change"}.kind != JNull:
lesson.change = some(lessonNode{"Change"}{"Description"}.getStr)
day.lessons.add lesson
day.dayOfWeek = dayNode{"DayOfWeek"}.getInt
day.date = dayNode{"Date"}.getStr.parse(iso8601)
result.days.add day