feat: better parsing & display
This commit is contained in:
@@ -2,6 +2,7 @@ package agzam.vyatsu.data.remote.pdf
|
|||||||
|
|
||||||
import agzam.vyatsu.domain.model.Day
|
import agzam.vyatsu.domain.model.Day
|
||||||
import agzam.vyatsu.domain.model.Lesson
|
import agzam.vyatsu.domain.model.Lesson
|
||||||
|
import agzam.vyatsu.domain.model.LessonType
|
||||||
import agzam.vyatsu.domain.model.TimeSlot
|
import agzam.vyatsu.domain.model.TimeSlot
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
import com.tom_roush.pdfbox.pdmodel.PDDocument
|
import com.tom_roush.pdfbox.pdmodel.PDDocument
|
||||||
@@ -25,6 +26,10 @@ object PdfScheduleParser {
|
|||||||
private fun timeFrom(timeString: String?): LocalTime? { return runCatching { LocalTime.parse(timeString, TIME_FORMAT) }.getOrNull() }
|
private fun timeFrom(timeString: String?): LocalTime? { return runCatching { LocalTime.parse(timeString, TIME_FORMAT) }.getOrNull() }
|
||||||
private fun dateFrom(dateString: String?): LocalDate? { return runCatching { LocalDate.parse(dateString, DATE_FORMAT) }.getOrNull() }
|
private fun dateFrom(dateString: String?): LocalDate? { return runCatching { LocalDate.parse(dateString, DATE_FORMAT) }.getOrNull() }
|
||||||
|
|
||||||
|
private val ROOM_REGEX = Regex("""(?<=^|\s)\d{1,2}-\d{3}[_a-zA-Zа-яА-Я]?(?=\s|$)""")
|
||||||
|
private val LESSON_TYPE_REGEX = Regex(LessonType.entries.joinToString(separator = "|") { Regex.escape(it.label) }.let { """(?<=^|\s)($it)(?=\s|$)""" } )
|
||||||
|
private val SUBGROUP_REGEX = Regex("""\d{2}\sподгруппа""", RegexOption.IGNORE_CASE)
|
||||||
|
|
||||||
fun parse(pdfBytes: ByteArray): List<Day> {
|
fun parse(pdfBytes: ByteArray): List<Day> {
|
||||||
Log.d(TAG, "Parsing ${pdfBytes.size} bytes")
|
Log.d(TAG, "Parsing ${pdfBytes.size} bytes")
|
||||||
val doc = try {
|
val doc = try {
|
||||||
@@ -35,6 +40,8 @@ object PdfScheduleParser {
|
|||||||
}
|
}
|
||||||
|
|
||||||
val days = mutableListOf<Day>()
|
val days = mutableListOf<Day>()
|
||||||
|
var group: String? = null
|
||||||
|
|
||||||
try {
|
try {
|
||||||
for (pageIndex in 0 until doc.numberOfPages) {
|
for (pageIndex in 0 until doc.numberOfPages) {
|
||||||
Log.v(TAG, "============ Page $pageIndex ============")
|
Log.v(TAG, "============ Page $pageIndex ============")
|
||||||
@@ -100,9 +107,13 @@ object PdfScheduleParser {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
for (dayCell in daysColumn.cells) {
|
for (dayCell in daysColumn.cells) {
|
||||||
val day = content(dayCell).split(' ')
|
val day = content(dayCell).split(' ')
|
||||||
|
if(day.size == 1 && day.first() == "День") {
|
||||||
|
val groupCell = select(lessonsColumn, dayCell).firstOrNull() ?: continue
|
||||||
|
group = content(groupCell)
|
||||||
|
continue
|
||||||
|
}
|
||||||
if(day.size != 2) continue // XXX: добавить защиту от заголовка и выбрасывать исключения вместо пропуска?
|
if(day.size != 2) continue // XXX: добавить защиту от заголовка и выбрасывать исключения вместо пропуска?
|
||||||
|
|
||||||
val date = dateFrom(day[1]) ?: continue
|
val date = dateFrom(day[1]) ?: continue
|
||||||
@@ -124,7 +135,55 @@ object PdfScheduleParser {
|
|||||||
val content = content(lessonCell)
|
val content = content(lessonCell)
|
||||||
Log.v(TAG, "- $content")
|
Log.v(TAG, "- $content")
|
||||||
// TODO: парсинг контента лекции
|
// TODO: парсинг контента лекции
|
||||||
lessons.add(Lesson(raw=content))
|
|
||||||
|
// Поиск кабинета в строке, ибо он последний, если их несколько - то скорее всего разделение на подгруппы
|
||||||
|
var found = 0
|
||||||
|
var currentStart = 0
|
||||||
|
for (roomMatch in ROOM_REGEX.findAll(content)) {
|
||||||
|
found++
|
||||||
|
|
||||||
|
val raw = content.substring(currentStart, roomMatch.range.last + 1)
|
||||||
|
currentStart = roomMatch.range.last + 1
|
||||||
|
|
||||||
|
// Предполагаемый формат: [группа], [XX подгруппа] <предмет> <тип> [id + тип] <ФИО> <кабинет>
|
||||||
|
|
||||||
|
val room = roomMatch.value
|
||||||
|
|
||||||
|
val typeMatch = LESSON_TYPE_REGEX.findAll(raw).toList()
|
||||||
|
if(typeMatch.isEmpty()) {
|
||||||
|
lessons.add(Lesson(
|
||||||
|
raw=raw,
|
||||||
|
room=room,
|
||||||
|
))
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
var subject = raw.substring(0, typeMatch.first().range.first).trim() // TODO: сейчас [группа], [XX подгруппа] <предмет> - можно достать подгруппу
|
||||||
|
val type = LessonType.fromRaw(typeMatch.first().value.trim())
|
||||||
|
val teacher = raw.substring(typeMatch.last().range.last+1, raw.length - room.length).trim()
|
||||||
|
var subgroup: String? = null
|
||||||
|
Log.d(TAG, subject + " " + group)
|
||||||
|
if(group != null && subject.startsWith("$group,")) {
|
||||||
|
subject = subject.substring(group.length + 1).trim()
|
||||||
|
val subgroupMatch = SUBGROUP_REGEX.find(subject)
|
||||||
|
if(subgroupMatch != null) {
|
||||||
|
subgroup = subgroupMatch.value.trim()
|
||||||
|
subject = subject.removeRange(subgroupMatch.range).trim()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
lessons.add(Lesson(
|
||||||
|
raw=raw,
|
||||||
|
subject=subject,
|
||||||
|
room=room,
|
||||||
|
type=type,
|
||||||
|
teacher=teacher,
|
||||||
|
subgroup=subgroup
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
if(found == 0) lessons.add(Lesson(raw=content))
|
||||||
|
|
||||||
}
|
}
|
||||||
intervals.add(TimeSlot(start = start, end = end, lessons = lessons))
|
intervals.add(TimeSlot(start = start, end = end, lessons = lessons))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -285,13 +285,18 @@ private fun LessonSlot(slotIndex: Int, slot: TimeSlot) {
|
|||||||
} else {
|
} else {
|
||||||
slot.lessons.forEach { lesson ->
|
slot.lessons.forEach { lesson ->
|
||||||
Column(Modifier.fillMaxWidth().padding(vertical = 2.dp)) {
|
Column(Modifier.fillMaxWidth().padding(vertical = 2.dp)) {
|
||||||
|
Text(
|
||||||
|
lesson.type.label,
|
||||||
|
style = MaterialTheme.typography.bodySmall,
|
||||||
|
color = MaterialTheme.colorScheme.onSurfaceVariant
|
||||||
|
)
|
||||||
|
|
||||||
Text(
|
Text(
|
||||||
lesson.subject?.takeIf { it.isNotBlank() } ?: lesson.raw,
|
lesson.subject?.takeIf { it.isNotBlank() } ?: lesson.raw,
|
||||||
style = MaterialTheme.typography.bodyLarge
|
style = MaterialTheme.typography.bodyLarge
|
||||||
)
|
)
|
||||||
Text(
|
Text(
|
||||||
listOfNotNull(
|
listOfNotNull(
|
||||||
lesson.type.label,
|
|
||||||
lesson.subgroup,
|
lesson.subgroup,
|
||||||
lesson.teacher,
|
lesson.teacher,
|
||||||
lesson.room
|
lesson.room
|
||||||
|
|||||||
Reference in New Issue
Block a user