All checks were successful
Build / build (push) Successful in 2m14s
Implement new gRPC task handlers for retrieving student grades and exam schedules. This includes adding new parser logic for processing score and exam data, and updating the client capabilities to advertise support for these new task types. - Add `getGrades` and `getExams` handlers in `grpc/handler.go` - Add `parser/encoding.go`, `parser/exam.go`, and `parser/score.go` for data parsing - Update `grpc/client.go` to include `TASK_TYPE_GET_GRADES` and `TASK_TYPE_GET_EXAMS` in capabilities
128 lines
2.6 KiB
Go
128 lines
2.6 KiB
Go
package parser
|
|
|
|
import (
|
|
"fmt"
|
|
"net/http"
|
|
"strings"
|
|
|
|
"schedule_converter/client"
|
|
"schedule_converter/config"
|
|
|
|
"github.com/PuerkitoBio/goquery"
|
|
)
|
|
|
|
type ExamType string
|
|
|
|
const (
|
|
ExamTypeFinal ExamType = "final"
|
|
ExamTypeMidterm ExamType = "midterm"
|
|
ExamTypeMakeup ExamType = "makeup"
|
|
)
|
|
|
|
type ExamEntry struct {
|
|
Course string
|
|
Date string
|
|
Time string
|
|
Week string
|
|
Weekday string
|
|
Type ExamType
|
|
}
|
|
|
|
func examTypeToCode(t ExamType) string {
|
|
switch t {
|
|
case ExamTypeMidterm:
|
|
return "02"
|
|
case ExamTypeMakeup:
|
|
return "03"
|
|
default:
|
|
return "01"
|
|
}
|
|
}
|
|
|
|
func FetchExams(httpClient *http.Client, examType ExamType) ([]ExamEntry, error) {
|
|
examURL := config.BaseURL + "/kscx/queryKsForXs"
|
|
fmt.Printf("[考试] 正在获取考试时间数据 (%s)...\n", examType)
|
|
|
|
data := fmt.Sprintf("xnxq=&kssjd=%s", examTypeToCode(examType))
|
|
req, err := http.NewRequest("POST", examURL, strings.NewReader(data))
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
req.Header = client.GetHeadersWithContentType("application/x-www-form-urlencoded")
|
|
|
|
resp, err := httpClient.Do(req)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
defer resp.Body.Close()
|
|
|
|
html, err := decodeResponseBody(resp)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
exams, err := ParseExamTimeHTML(html, examType)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
fmt.Printf("[考试] 共获取 %d 条考试记录 (%s)\n", len(exams), examType)
|
|
return exams, nil
|
|
}
|
|
|
|
func FetchAllExams(httpClient *http.Client) ([]ExamEntry, error) {
|
|
var allExams []ExamEntry
|
|
for _, et := range []ExamType{ExamTypeFinal, ExamTypeMidterm, ExamTypeMakeup} {
|
|
exams, err := FetchExams(httpClient, et)
|
|
if err != nil {
|
|
fmt.Printf("[考试] 获取%s考试失败: %v\n", et, err)
|
|
continue
|
|
}
|
|
allExams = append(allExams, exams...)
|
|
}
|
|
return allExams, nil
|
|
}
|
|
|
|
func ParseExamTimeHTML(html string, examType ExamType) ([]ExamEntry, error) {
|
|
doc, err := goquery.NewDocumentFromReader(strings.NewReader(html))
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
var exams []ExamEntry
|
|
|
|
doc.Find("table.bot_line").Each(func(_ int, table *goquery.Selection) {
|
|
rows := table.Find("tr")
|
|
if rows.Length() < 2 {
|
|
return
|
|
}
|
|
|
|
rows.Each(func(idx int, row *goquery.Selection) {
|
|
if idx == 0 {
|
|
return
|
|
}
|
|
cells := row.Find("td")
|
|
if cells.Length() < 6 {
|
|
return
|
|
}
|
|
|
|
texts := getCellTexts(cells)
|
|
|
|
exam := ExamEntry{
|
|
Course: safeGet(texts, 1),
|
|
Date: safeGet(texts, 2),
|
|
Time: safeGet(texts, 3),
|
|
Week: safeGet(texts, 4),
|
|
Weekday: safeGet(texts, 5),
|
|
Type: examType,
|
|
}
|
|
|
|
if exam.Course != "" && len(exam.Course) >= 2 {
|
|
exams = append(exams, exam)
|
|
}
|
|
})
|
|
})
|
|
|
|
return exams, nil
|
|
} |