Skip to content

Commit

Permalink
Merge pull request #8 from west2-online/courses
Browse files Browse the repository at this point in the history
重构选课列表解析
  • Loading branch information
SchwarzSail authored Sep 21, 2024
2 parents fe52189 + 0f62759 commit 0eb6e48
Show file tree
Hide file tree
Showing 3 changed files with 297 additions and 26 deletions.
247 changes: 234 additions & 13 deletions course.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
package jwch

import (
"regexp"
"sort"
"strconv"
"strings"

"github.com/west2-online/jwch/constants"
Expand Down Expand Up @@ -73,21 +76,239 @@ func (s *Student) GetSemesterCourses(term, viewState, eventValidation string) ([
return nil, errno.HTMLParseError.WithMessage("get course info failed")
}

// 解析调课信息
/*
06周 星期3:5-6节 调至 09周 星期1:7-8节 旗山西1-206
*/
courseInfo11 := strings.Split(utils.InnerTextWithBr(info[11]), "\n")
adjustRegex := regexp.MustCompile(`(\d{2})周 星期(\d):(\d{1,2})-(\d{1,2})节[\s ]*调至[\s ]*(\d{2})周 星期(\d):(\d{1,2})-(\d{1,2})节[\s ]*(\S*)`)
adjustRules := []CourseAdjustRule{}

for i := 0; i < len(courseInfo11); i++ {
courseInfo11[i] = strings.TrimSpace(courseInfo11[i])

if courseInfo11[i] == "" { // 空行
continue
}

adjustMatchArr := adjustRegex.FindStringSubmatch(courseInfo11[i])

if len(adjustMatchArr) < 10 {
return nil, errno.HTMLParseError.WithMessage("get course adjust failed")
}

adjustRules = append(adjustRules, CourseAdjustRule{
OldWeek: utils.SafeAtoi(adjustMatchArr[1]),
OldWeekday: utils.SafeAtoi(adjustMatchArr[2]),
OldStartClass: utils.SafeAtoi(adjustMatchArr[3]),
OldEndClass: utils.SafeAtoi(adjustMatchArr[4]),

NewWeek: utils.SafeAtoi(adjustMatchArr[5]),
NewWeekday: utils.SafeAtoi(adjustMatchArr[6]),
NewStartClass: utils.SafeAtoi(adjustMatchArr[7]),
NewEndClass: utils.SafeAtoi(adjustMatchArr[8]),
NewLocation: adjustMatchArr[9],
})
}

// 解析上课时间、地点,融合调课信息
/*
05-18 星期1:3-4节 铜盘A110
05-17 星期3:1-2节 铜盘A110
05-17 星期5:3-4节 铜盘A110
*/
courseInfo8 := strings.Split(utils.InnerTextWithBr(info[8]), "\n")
scheduleRules := []CourseScheduleRule{}

for i := 0; i < len(courseInfo8); i++ {
courseInfo8[i] = strings.TrimSpace(courseInfo8[i])

if courseInfo8[i] == "" { // 空行
continue
}

lineData := strings.Fields(courseInfo8[i])

if len(lineData) < 3 {
return nil, errno.HTMLParseError.WithMessage("get course info failed")
}

if strings.Contains(lineData[0], "周") { // 处理整周的课程,比如军训
/*
03周 星期1 - 04周 星期7
[0] 03周
[1] 星期1
[2] -
[3] 04周
[4] 星期7
*/
startWeek, _ := strconv.Atoi(strings.TrimSuffix(lineData[0], "周"))
endWeek, _ := strconv.Atoi(strings.TrimSuffix(lineData[3], "周"))
startWeekday, _ := strconv.Atoi(strings.TrimPrefix(lineData[1], "星期"))
endWeekday, _ := strconv.Atoi(strings.TrimPrefix(lineData[4], "星期"))

for weekday := 1; weekday <= 7; weekday++ {
curStartWeek := startWeek
curEndWeek := endWeek

if weekday < startWeekday {
curStartWeek++
}

if weekday > endWeekday {
curEndWeek--
}

if curStartWeek > curEndWeek {
continue
}

scheduleRules = append(scheduleRules, CourseScheduleRule{
Location: "",
StartClass: 1,
EndClass: 8,
StartWeek: curStartWeek,
EndWeek: curEndWeek,
Weekday: weekday,
Single: true,
Double: true,
Adjust: false,
})
}

continue
}

/*
08-16 星期5:7-8节 铜盘A508
[0] 08-16
[1] 星期5:7-8节
[2] 铜盘A508
*/
/*
02-14 星期1:1-2节(双) 旗山西1-206
[0] 02-14
[1] 星期1:1-2节(双)
[2] 旗山西1-206
*/
/*
01-13 星期1:3-4节(单) 旗山西1-206
[0] 01-13
[1] 星期1:3-4节(单)
[2] 旗山西1-206
*/

// 是不是用正则表达式更好一点?
weekInfo := strings.SplitN(lineData[0], "-", 2) // [8, 16]
dayInfo := strings.SplitN(lineData[1], ":", 2) // ["星期5", "7-8节"] or ["星期1", "1-2节(双)"]
classBasicInfo := strings.Split(dayInfo[1], "节") // ["7-8", ""] or ["1-2", "(双)"]
classInfo := strings.Split(classBasicInfo[0], "-") // ["7", "8"]
location := lineData[2]
startClass := utils.SafeAtoi(classInfo[0])
endClass := utils.SafeAtoi(classInfo[1])
startWeek := utils.SafeAtoi(weekInfo[0])
endWeek := utils.SafeAtoi(weekInfo[1])
weekDay := utils.SafeAtoi(strings.TrimPrefix(dayInfo[0], "星期"))
single := !strings.Contains(classBasicInfo[1], "双")
double := !strings.Contains(classBasicInfo[1], "单")

if len(adjustRules) == 0 {
scheduleRules = append(scheduleRules, CourseScheduleRule{
Location: location,
StartClass: startClass,
EndClass: endClass,
StartWeek: startWeek,
EndWeek: endWeek,
Weekday: weekDay,
Single: single,
Double: double,
})
} else {
startWeek := utils.SafeAtoi(weekInfo[0])
endWeek := utils.SafeAtoi(weekInfo[1])
startClass := utils.SafeAtoi(classInfo[0])
endClass := utils.SafeAtoi(classInfo[1])
removedWeeks := []int{}

for _, adjustRule := range adjustRules {
// 匹配是否是对应的调课信息
if adjustRule.OldWeek < startWeek ||
adjustRule.OldWeek > endWeek ||
adjustRule.OldStartClass != startClass ||
adjustRule.OldEndClass != endClass ||
adjustRule.OldWeekday != weekDay {
continue
}

// 记录被去掉的周次
removedWeeks = append(removedWeeks, adjustRule.OldWeek)

// 添加新的课程信息
scheduleRules = append(scheduleRules, CourseScheduleRule{
Location: adjustRule.NewLocation,
StartClass: adjustRule.NewStartClass,
EndClass: adjustRule.NewEndClass,
StartWeek: adjustRule.NewWeek,
EndWeek: adjustRule.NewWeek,
Weekday: adjustRule.NewWeekday,
Single: true,
Double: true,
Adjust: true, // 调课
})
}

sort.Ints(removedWeeks)
// 去掉被调课的周次
curStartWeek := startWeek

for _, removedWeek := range removedWeeks {
if removedWeek == curStartWeek {
curStartWeek++

continue
}

scheduleRules = append(scheduleRules, CourseScheduleRule{
Location: location,
StartClass: startClass,
EndClass: endClass,
StartWeek: curStartWeek,
EndWeek: removedWeek - 1,
Weekday: weekDay,
Single: single,
Double: double,
Adjust: false,
})

curStartWeek = removedWeek + 1
}

if curStartWeek <= endWeek {
scheduleRules = append(scheduleRules, CourseScheduleRule{
Location: location,
StartClass: startClass,
EndClass: endClass,
StartWeek: curStartWeek,
EndWeek: endWeek,
Weekday: weekDay,
Single: single,
Double: double,
Adjust: false,
})
}
}
}

// TODO: performance optimization
res = append(res, &Course{
Type: htmlquery.OutputHTML(info[0], false),
Name: htmlquery.OutputHTML(info[1], false),
Syllabus: "https://jwcjwxt2.fzu.edu.cn:81" + safeExtractRegex(`javascript:pop1\('(.*?)&`, safeExtractionValue(info[2], "a", "href", 0)),
LessonPlan: "https://jwcjwxt2.fzu.edu.cn:81" + safeExtractRegex(`javascript:pop1\('(.*?)&`, safeExtractionValue(info[2], "a", "href", 1)),
PaymentStatus: safeExtractionFirst(info[3], "font"),
Credits: safeExtractionFirst(info[4], "span"),
ElectiveType: utils.GetChineseCharacter(htmlquery.OutputHTML(info[5], false)),
ExamType: utils.GetChineseCharacter(htmlquery.OutputHTML(info[6], false)),
Teacher: htmlquery.OutputHTML(info[7], false),
Classroom: strings.TrimSpace(htmlquery.InnerText(info[8])),
ExamTime: strings.TrimSpace(htmlquery.InnerText(info[9])),
Remark: htmlquery.OutputHTML(info[10], false),
Adjust: htmlquery.OutputHTML(info[11], false),
Name: htmlquery.OutputHTML(info[1], false),
Syllabus: "https://jwcjwxt2.fzu.edu.cn:81" + safeExtractRegex(`javascript:pop1\('(.*?)&`, safeExtractionValue(info[2], "a", "href", 0)),
LessonPlan: "https://jwcjwxt2.fzu.edu.cn:81" + safeExtractRegex(`javascript:pop1\('(.*?)&`, safeExtractionValue(info[2], "a", "href", 1)),
Teacher: htmlquery.OutputHTML(info[7], false),
ScheduleRules: scheduleRules,
RawScheduleRules: strings.Join(courseInfo8, "\n"),
RawAdjust: strings.Join(courseInfo11, "\n"),
Remark: htmlquery.OutputHTML(info[10], false),
})
}

Expand Down
46 changes: 33 additions & 13 deletions model.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,19 +47,39 @@ type Term struct {

// 课程信息
type Course struct {
Type string `json:"type"` // 修读类别
Name string `json:"name"` // 课程名称
PaymentStatus string `json:"paymentstatus"` // 缴费状态
Syllabus string `json:"syllabus"` // 课程大纲
LessonPlan string `json:"lessonplan"` // 课程计划
Credits string `json:"credit"` // 学分
ElectiveType string `json:"electivetype"` // 选课类型
ExamType string `json:"examtype"` // 考试类别
Teacher string `json:"teacher"` // 任课教师
Classroom string `json:"classroom"` // 上课时间地点
ExamTime string `json:"examtime"` // 考试时间地点
Remark string `json:"remark"` // 备注
Adjust string `json:"adjust"` // 调课信息
Name string `json:"name"` // 课程名称
Syllabus string `json:"syllabus"` // 课程大纲
LessonPlan string `json:"lessonplan"` // 课程计划
Teacher string `json:"teacher"` // 任课教师
ScheduleRules []CourseScheduleRule `json:"scheduleRules"` // 上课时间地点规则
RawScheduleRules string `json:"rawScheduleRules"` // 上课时间地点(原始文本)
RawAdjust string `json:"rawAdjust"` // 调课信息(原始文本)
Remark string `json:"remark"` // 备注
}

type CourseScheduleRule struct {
Location string `json:"location"` // 上课地点
StartClass int `json:"startClass"` // 开始节数
EndClass int `json:"endClass"` // 结束节数
StartWeek int `json:"startWeek"` // 开始周
EndWeek int `json:"endWeek"` // 结束周
Weekday int `json:"weekday"` // 星期几
Single bool `json:"single"` // 单周 (PS: 为啥不用 odd)
Double bool `json:"double"` // 双周 (PS: 为啥不用 even)
Adjust bool `json:"adjust"` // 调课
}

type CourseAdjustRule struct {
OldWeek int `json:"oldWeek"` // 原-周次
OldWeekday int `json:"oldWeekday"` // 原-星期几
OldStartClass int `json:"oldStartClass"` // 原-开始节数
OldEndClass int `json:"oldEndClass"` // 原-结束节数

NewWeek int `json:"newWeek"` // 新-周次
NewWeekday int `json:"newWeekday"` // 新-星期几
NewStartClass int `json:"newStartClass"` // 新-开始节数
NewEndClass int `json:"newEndClass"` // 新-结束节数
NewLocation string `json:"newLocation"` // 新-上课地点
}

type Mark struct {
Expand Down
30 changes: 30 additions & 0 deletions utils/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,11 @@ import (
"encoding/hex"
"encoding/json"
"fmt"
"golang.org/x/net/html"
"net/http"
"os"
"reflect"
"strconv"
)

func SaveData(filePath string, data []byte) error {
Expand Down Expand Up @@ -108,3 +110,31 @@ func JSONUnmarshalFromFile(filePath string, v any) error {

return json.Unmarshal(data, v)
}

func InnerTextWithBr(n *html.Node) string {
if n.Type == html.TextNode {
return n.Data
}

if n.Type == html.ElementNode && n.Data == "br" {
return "\n"
}

var buf bytes.Buffer

for c := n.FirstChild; c != nil; c = c.NextSibling {
buf.WriteString(InnerTextWithBr(c))
}

return buf.String()
}

func SafeAtoi(s string) int {
n, err := strconv.Atoi(s)

if err != nil {
return 0
}

return n
}

0 comments on commit 0eb6e48

Please sign in to comment.