Files
schedule_converter/parser/empty_classroom.go

156 lines
4.1 KiB
Go
Raw Normal View History

package parser
import (
"fmt"
"strings"
"schedule_converter/models"
"github.com/PuerkitoBio/goquery"
)
// ParseEmptyClassroomHTML 解析空教室查询结果 HTML返回空教室条目列表。
func ParseEmptyClassroomHTML(html, semester, weekStart, weekEnd string) ([]models.EmptyClassroomEntry, error) {
if html == "" {
return nil, nil
}
// Clean nbsp entities
cleaned := strings.ReplaceAll(html, "&nbsp", "")
cleaned = strings.ReplaceAll(cleaned, "&", "&")
doc, err := goquery.NewDocumentFromReader(strings.NewReader(cleaned))
if err != nil {
return nil, err
}
dayNames := [7]string{"星期一", "星期二", "星期三", "星期四", "星期五", "星期六", "星期日"}
periodLabels := [6]string{"1,2", "3,4", "5,6", "7,8", "9,10", "11,12"}
var classrooms []models.EmptyClassroomEntry
2026-06-12 20:52:18 +08:00
// Find the main data table with class="dataTable"
doc.Find("table.dataTable").Each(func(_ int, table *goquery.Selection) {
rows := table.Find("tr")
if rows.Length() < 3 {
return
}
2026-06-12 20:52:18 +08:00
// Verify this is the classroom table by checking for weekday headers
hasWeekdayHeader := false
rows.First().Find("th").Each(func(_ int, th *goquery.Selection) {
text := strings.TrimSpace(th.Text())
for _, day := range dayNames {
if strings.Contains(text, day) {
hasWeekdayHeader = true
break
}
}
})
2026-06-12 20:52:18 +08:00
if !hasWeekdayHeader {
return
}
2026-06-12 20:52:18 +08:00
// Build column mapping: column index -> (dayIndex, periodIndex)
colMapping := make(map[int][2]int)
2026-06-12 20:52:18 +08:00
// Start from column 1 (skip first column which is classroom name)
colIdx := 1
for dayIdx := 0; dayIdx < 7; dayIdx++ {
for periodIdx := 0; periodIdx < 6; periodIdx++ {
2026-06-12 20:52:18 +08:00
colMapping[colIdx] = [2]int{dayIdx, periodIdx}
colIdx++
}
}
2026-06-12 20:52:18 +08:00
// Process data rows (skip first 2 header rows)
rows.Each(func(rowIdx int, row *goquery.Selection) {
if rowIdx < 2 {
return
}
cells := row.Find("td")
if cells.Length() < 2 {
return
}
2026-06-12 20:52:18 +08:00
// Get classroom name from first cell
classroomName := strings.TrimSpace(cells.First().Text())
2026-06-12 20:52:18 +08:00
// Filter out invalid classroom names
if classroomName == "" || len(classroomName) < 2 {
return
}
2026-06-12 20:52:18 +08:00
// Skip non-classroom entries like "线上网络授课"
if strings.Contains(classroomName, "线上") || strings.Contains(classroomName, "网络") {
return
}
// Collect empty slots for this classroom
var slots []string
2026-06-12 20:52:18 +08:00
weekdaySet := make(map[int]bool)
var weekdays []string
2026-06-12 20:52:18 +08:00
// Check each time slot column
for colIdx := 1; colIdx < cells.Length(); colIdx++ {
cell := cells.Eq(colIdx)
2026-06-12 20:52:18 +08:00
// Get mapping for this column
mapping, exists := colMapping[colIdx]
if !exists {
continue
}
dayIdx := mapping[0]
periodIdx := mapping[1]
// Check if cell is empty (no kjs_icon div)
isEmpty := true
cell.Find("div.kjs_icon").Each(func(_ int, div *goquery.Selection) {
isEmpty = false
})
2026-06-12 20:52:18 +08:00
// Also check if cell has any content
cellText := strings.TrimSpace(cell.Text())
if cellText != "" {
isEmpty = false
}
if isEmpty {
dayName := dayNames[dayIdx]
periodText := periodLabels[periodIdx]
slot := fmt.Sprintf("%s 第%s节", dayName, periodText)
slots = append(slots, slot)
2026-06-12 20:52:18 +08:00
if !weekdaySet[dayIdx] {
weekdaySet[dayIdx] = true
weekdays = append(weekdays, dayName)
}
}
}
2026-06-12 20:52:18 +08:00
// Create entry if there are empty slots
if len(slots) > 0 {
weekStr := ""
if weekStart != "" && weekEnd != "" {
weekStr = fmt.Sprintf("%s-%s周", weekStart, weekEnd)
}
entry := models.EmptyClassroomEntry{
Classroom: classroomName,
Weekday: strings.Join(weekdays, ", "),
Periods: strings.Join(slots, "\n"),
Term: semester,
Weeks: weekStr,
}
classrooms = append(classrooms, entry)
}
})
})
return classrooms, nil
}
2026-06-12 20:52:18 +08:00
// TestParseEmptyClassroomHTML is a test helper function that exports the parser for testing
func TestParseEmptyClassroomHTML(html, semester, weekStart, weekEnd string) ([]models.EmptyClassroomEntry, error) {
return ParseEmptyClassroomHTML(html, semester, weekStart, weekEnd)
2026-06-12 20:52:18 +08:00
}