This commit is contained in:
@@ -96,89 +96,106 @@ func parseEmptyClassroomHTML(html, semester, weekStart, weekEnd string) ([]Empty
|
||||
|
||||
var classrooms []EmptyClassroomEntry
|
||||
|
||||
doc.Find("table").Each(func(_ int, table *goquery.Selection) {
|
||||
// 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
|
||||
}
|
||||
|
||||
// Check if this table has "星期一" in header
|
||||
headerText := ""
|
||||
table.Find("th").Each(func(_ int, th *goquery.Selection) {
|
||||
headerText += th.Text()
|
||||
// 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
|
||||
}
|
||||
}
|
||||
})
|
||||
if !strings.Contains(headerText, "星期一") {
|
||||
if !hasWeekdayHeader {
|
||||
return
|
||||
}
|
||||
|
||||
// Parse period row (second row) to map column index to (day, period)
|
||||
periodRow := rows.Eq(1)
|
||||
periodCells := periodRow.Find("td")
|
||||
periodCellTexts := make([]string, periodCells.Length())
|
||||
periodCells.Each(func(i int, cell *goquery.Selection) {
|
||||
periodCellTexts[i] = strings.TrimSpace(cell.Text())
|
||||
})
|
||||
// Build column mapping: column index -> (dayIndex, periodIndex)
|
||||
colMapping := make(map[int][2]int)
|
||||
|
||||
colToDayPeriod := make(map[int][2]string)
|
||||
// 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++ {
|
||||
var periodText string
|
||||
if colIdx < len(periodCellTexts) {
|
||||
periodText = periodCellTexts[colIdx]
|
||||
} else {
|
||||
periodText = periodLabels[periodIdx]
|
||||
}
|
||||
colToDayPeriod[colIdx] = [2]string{dayNames[dayIdx], periodText}
|
||||
colMapping[colIdx] = [2]int{dayIdx, periodIdx}
|
||||
colIdx++
|
||||
}
|
||||
}
|
||||
|
||||
// Data rows start from row 2+
|
||||
rows.FilterFunction(func(_ int, row *goquery.Selection) bool {
|
||||
return row.Index() >= 2
|
||||
}).Each(func(_ int, row *goquery.Selection) {
|
||||
// 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
|
||||
}
|
||||
|
||||
// Get classroom name from first cell
|
||||
classroomName := strings.TrimSpace(cells.First().Text())
|
||||
// Filter out invalid classroom names
|
||||
if classroomName == "" || len(classroomName) < 2 {
|
||||
return
|
||||
}
|
||||
|
||||
var slots []string
|
||||
var weekdays []string
|
||||
weekdaySet := make(map[string]bool)
|
||||
// Skip non-classroom entries like "线上网络授课"
|
||||
if strings.Contains(classroomName, "线上") || strings.Contains(classroomName, "网络") {
|
||||
return
|
||||
}
|
||||
|
||||
// Collect empty slots for this classroom
|
||||
var slots []string
|
||||
weekdaySet := make(map[int]bool)
|
||||
var weekdays []string
|
||||
|
||||
// Check each time slot column
|
||||
for colIdx := 1; colIdx < cells.Length(); colIdx++ {
|
||||
cell := cells.Eq(colIdx)
|
||||
// Check if cell has kjs_icon div (indicates occupied, not empty)
|
||||
hasIcon := false
|
||||
cell.Find("div").Each(func(_ int, div *goquery.Selection) {
|
||||
if classAttr, exists := div.Attr("class"); exists && strings.Contains(classAttr, "kjs_icon") {
|
||||
hasIcon = true
|
||||
}
|
||||
|
||||
// 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
|
||||
})
|
||||
|
||||
if !hasIcon && colToDayPeriod[colIdx][0] != "" {
|
||||
dayName := colToDayPeriod[colIdx][0]
|
||||
periodText := colToDayPeriod[colIdx][1]
|
||||
var slot string
|
||||
if periodText != "" {
|
||||
slot = fmt.Sprintf("%s 第%s节", dayName, periodText)
|
||||
} else {
|
||||
slot = dayName
|
||||
}
|
||||
// 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)
|
||||
if !weekdaySet[dayName] {
|
||||
weekdaySet[dayName] = true
|
||||
|
||||
if !weekdaySet[dayIdx] {
|
||||
weekdaySet[dayIdx] = true
|
||||
weekdays = append(weekdays, dayName)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Create entry if there are empty slots
|
||||
if len(slots) > 0 {
|
||||
weekStr := ""
|
||||
if weekStart != "" && weekEnd != "" {
|
||||
@@ -259,4 +276,9 @@ func fetchVenues(httpClient *http.Client, buildingCode string) ([]venueInfo, err
|
||||
}
|
||||
|
||||
return venues, nil
|
||||
}
|
||||
}
|
||||
|
||||
// TestParseEmptyClassroomHTML is a test helper function that exports the parser for testing
|
||||
func TestParseEmptyClassroomHTML(html, semester, weekStart, weekEnd string) ([]EmptyClassroomEntry, error) {
|
||||
return parseEmptyClassroomHTML(html, semester, weekStart, weekEnd)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user