Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package io.github.tomhula.jecnaapi
import io.github.tomhula.jecnaapi.data.absence.AbsencesPage
import io.github.tomhula.jecnaapi.data.article.NewsPage
import io.github.tomhula.jecnaapi.data.attendance.AttendancesPage
import io.github.tomhula.jecnaapi.data.cert.Certificate
import io.github.tomhula.jecnaapi.data.grade.GradesPage
import io.github.tomhula.jecnaapi.data.notification.Notification
import io.github.tomhula.jecnaapi.data.notification.NotificationReference
Expand Down Expand Up @@ -48,6 +49,7 @@ interface JecnaClient
suspend fun getStudentProfile(username: String): Student
suspend fun getNotifications(): List<NotificationReference>
suspend fun getNotification(notification: NotificationReference): Notification
suspend fun getStudentCertificates(): List<Certificate>

companion object
{
Expand Down
17 changes: 15 additions & 2 deletions src/commonMain/kotlin/io/github/tomhula/jecnaapi/WebJecnaClient.kt
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,7 @@ package io.github.tomhula.jecnaapi

import com.fleeksoft.ksoup.Ksoup
import com.fleeksoft.ksoup.nodes.Document
import io.ktor.client.statement.*
import io.ktor.http.*
import io.github.tomhula.jecnaapi.data.cert.Certificate
import io.github.tomhula.jecnaapi.data.notification.NotificationReference
import io.github.tomhula.jecnaapi.parser.parsers.*
import io.github.tomhula.jecnaapi.util.JecnaPeriodEncoder
Expand All @@ -12,6 +11,8 @@ import io.github.tomhula.jecnaapi.util.SchoolYear
import io.github.tomhula.jecnaapi.util.SchoolYearHalf
import io.github.tomhula.jecnaapi.web.Auth
import io.github.tomhula.jecnaapi.web.AuthenticationException
import io.ktor.client.statement.*
import io.ktor.http.*
import io.ktor.client.HttpClient
import io.ktor.client.plugins.HttpTimeout
import io.ktor.client.plugins.cookies.AcceptAllCookiesStorage
Expand Down Expand Up @@ -83,6 +84,7 @@ class WebJecnaClient(
private val lockerPageParser = LockerPageParser
private val roomsPageParser = RoomsPageParser
private val roomParser = RoomParser(TimetableParser)
private val certificatePageParser = CertificatePageParser

@OptIn(ExperimentalTime::class)
override suspend fun login(auth: Auth): Boolean
Expand Down Expand Up @@ -161,6 +163,16 @@ class WebJecnaClient(
override suspend fun getStudentProfile() = autoLoginAuth?.let { getStudentProfile(it.username)} ?: throw AuthenticationException()
override suspend fun getNotification(notification: NotificationReference) = notificationParser.getNotification(queryStringBody("${PageWebPath.NOTIFICATION}?userStudentRecordId=${notification.recordId}"))
override suspend fun getNotifications() = notificationParser.parse(queryStringBody(PageWebPath.NOTIFICATIONS))
override suspend fun getStudentCertificates(): List<Certificate>
{
val response = query(PageWebPath.CERTIFICATES, parameters = null)
val locationHeader = response.headers[HttpHeaders.Location]

if (locationHeader == "$endpoint/neopravneny-pristup")
return emptyList()

return certificatePageParser.parse(response.bodyAsText())
}

suspend fun setRole(role: Role)
{
Expand Down Expand Up @@ -272,6 +284,7 @@ class WebJecnaClient(
const val STUDENT = "/student"
const val LOCKER = "/locker/student"
const val ROOMS = "/ucebna"
const val CERTIFICATES = "/certification/student"
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package io.github.tomhula.jecnaapi.data.cert

import kotlinx.datetime.LocalDate

data class Certificate(
val dateIssued: LocalDate,
val issuer: String,
val label: String,
)
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package io.github.tomhula.jecnaapi.data.schoolStaff

import io.github.tomhula.jecnaapi.data.SchoolAttendee
import io.github.tomhula.jecnaapi.data.cert.Certificate
import io.github.tomhula.jecnaapi.data.timetable.Timetable

class Teacher(
Expand All @@ -16,7 +17,8 @@ class Teacher(
val cabinet: String? = null,
val tutorOfClass: String? = null,
val consultationHours: String? = null,
val timetable: Timetable? = null
val timetable: Timetable? = null,
val certificates: List<Certificate> = emptyList(),
) : SchoolAttendee(fullName, username, schoolMail, privateMail, phoneNumbers, profilePicturePath)
{
val tag = tag.trim().lowercase().replaceFirstChar { it.uppercaseChar() }
Expand All @@ -34,6 +36,7 @@ class Teacher(
if (tutorOfClass != other.tutorOfClass) return false
if (consultationHours != other.consultationHours) return false
if (tag != other.tag) return false
if (certificates != other.certificates) return false

return true
}
Expand Down Expand Up @@ -64,6 +67,7 @@ class Teacher(
"tutorOfClass=$tutorOfClass, " +
"consultationHours=$consultationHours, " +
"tag='$tag'" +
"certificates=$certificates" +
")"
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package io.github.tomhula.jecnaapi.parser.parsers

import com.fleeksoft.ksoup.Ksoup
import io.github.tomhula.jecnaapi.data.cert.Certificate
import kotlinx.datetime.LocalDate

object CertificatePageParser
{
fun parse(html: String): List<Certificate>
{
val document = Ksoup.parse(html)
val lis = document.select("ul.list li")
val certificates = lis.map { li ->
val labelText = li.select("span.label").text().trim()
val parts = labelText.split(" / ", limit = 2)
val issuer = parts[0]
val rest = parts[1]
val dateParts = rest.split(" ze dne ", limit = 2)
val title = dateParts[0]
val dateIssued = LocalDate.parse(dateParts[1], CommonParser.CZECH_DATE_FORMAT_WITH_PADDING)
Certificate(dateIssued, issuer, title)
}
return certificates
}
}
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
package io.github.tomhula.jecnaapi.parser.parsers

import io.github.tomhula.jecnaapi.data.schoolStaff.Teacher
import io.github.tomhula.jecnaapi.parser.ParseException
import com.fleeksoft.ksoup.Ksoup
import com.fleeksoft.ksoup.nodes.Element
import io.github.tomhula.jecnaapi.data.cert.Certificate
import io.github.tomhula.jecnaapi.data.schoolStaff.Teacher
import io.github.tomhula.jecnaapi.parser.ParseException
import kotlinx.datetime.LocalDate

/** https://www.spsejecna.cz/ucitel/{teacher-tag} */
internal class TeacherParser(private val timetableParser: TimetableParser)
Expand All @@ -14,7 +16,6 @@ internal class TeacherParser(private val timetableParser: TimetableParser)
{
val document = Ksoup.parse(html)
val table = document.selectFirstOrThrow(".userprofile", "data table")

val fullName = getTableValue(table, "Jméno")!!
val tag = getTableValue(table, "Zkratka")!!
val username = getTableValue(table, "Uživatelské jméno")!!
Expand All @@ -28,10 +29,26 @@ internal class TeacherParser(private val timetableParser: TimetableParser)
val consultationHours = getTableValue(table, "Konzultační hodiny")
val tutorOfClass = getTableValue(table, "Třídní učitel")
val profilePicturePath = document.selectFirst(".profilephoto .image img")?.attr("src")

val timetableEle = document.selectFirst("table.timetable")
val timetable = timetableEle?.let { timetableParser.parse(it.outerHtml()) }

val certificates = mutableListOf<Certificate>()
val certList = document.select("ul.certifications > li")
for (li in certList)
{
val date = LocalDate.parse(
li.selectFirst("span.date")?.text()?.trim().orEmpty(),
CommonParser.CZECH_DATE_FORMAT_WITH_PADDING
)
val infoSpan = li.selectFirst("span.info")
val label = infoSpan?.selectFirst("span.label")?.text()?.trim().orEmpty()
val institution = infoSpan?.selectFirst("span.institution")?.text()?.trim().orEmpty()
if (institution.isNotEmpty())
{
certificates.add(Certificate(date, institution, label))
}
}

return Teacher(
fullName = fullName,
username = username,
Expand All @@ -45,7 +62,8 @@ internal class TeacherParser(private val timetableParser: TimetableParser)
cabinet = cabinet,
tutorOfClass = tutorOfClass,
consultationHours = consultationHours,
timetable = timetable
timetable = timetable,
certificates = certificates
)
}
catch (e: Exception)
Expand Down