Compare commits
24 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 17e6e1421e | |||
| f8d2147fc9 | |||
| 6b80254e0b | |||
| 70d41cc4bc | |||
| 1cf4193ecb | |||
| 4485c14503 | |||
| f351e73844 | |||
| b6d20406f8 | |||
|
|
8ed796a2a5 | ||
| 4ea60b6a9e | |||
| bea92a83f0 | |||
| 856a44ad61 | |||
| ed7bad423c | |||
| d289a7af55 | |||
| b9c2089514 | |||
| acbc3296f2 | |||
| e4ebb36d4e | |||
| c24681aa1c | |||
| 2afa08efce | |||
| c61489ed9c | |||
|
|
db020f765f | ||
|
|
bf880ae585 | ||
|
|
6ea1bbf9c0 | ||
|
|
a3471b4f1d |
@ -41,6 +41,9 @@
|
|||||||
- [x] 把版排好(選課框框改成可下拉(才可以同時看到課表))
|
- [x] 把版排好(選課框框改成可下拉(才可以同時看到課表))
|
||||||
|
|
||||||
# 課程爬蟲使用說明
|
# 課程爬蟲使用說明
|
||||||
|
> 因為學校教務系統更新通識分類的部份很慢,因此目前的程式碼已經修改成無法對應「通識課程分類」的版本,
|
||||||
|
> 實際上線的 data 是依靠「工人智慧」,
|
||||||
|
> 如果有需要爬取資料,建議使用較舊版本的 python code
|
||||||
|
|
||||||
安裝所需套件
|
安裝所需套件
|
||||||
```
|
```
|
||||||
|
|||||||
20
api.py
20
api.py
@ -5,19 +5,23 @@ import sqlite3
|
|||||||
from flask_cors import CORS
|
from flask_cors import CORS
|
||||||
|
|
||||||
app = Flask(__name__)
|
app = Flask(__name__)
|
||||||
CORS(app, resources={r"/.*": {"origins": ["https://snsd0805.com"]}})
|
CORS(app, resources={r"/.*": {"origins": ["https://course.snsd0805.com"]}})
|
||||||
|
|
||||||
def facebookAuth(token):
|
def facebookAuth(token):
|
||||||
url = "https://graph.facebook.com/v9.0/me?access_token={}"
|
url = "https://graph.facebook.com/v9.0/me?access_token={}"
|
||||||
|
|
||||||
response = requests.get(url.format(token))
|
try:
|
||||||
data = json.loads(response.text)
|
response = requests.get(url.format(token), timeout=5)
|
||||||
|
except:
|
||||||
# 若 access code 通過 facebook 驗證
|
|
||||||
if response.status_code == 200:
|
|
||||||
return True, data['id'], data['name']
|
|
||||||
else:
|
|
||||||
return False, None, None
|
return False, None, None
|
||||||
|
else:
|
||||||
|
data = json.loads(response.text)
|
||||||
|
|
||||||
|
# 若 access code 通過 facebook 驗證
|
||||||
|
if response.status_code == 200:
|
||||||
|
return True, data['id'], data['name']
|
||||||
|
else:
|
||||||
|
return False, None, None
|
||||||
|
|
||||||
@app.route('/courseTable', methods=["GET"])
|
@app.route('/courseTable', methods=["GET"])
|
||||||
def get():
|
def get():
|
||||||
|
|||||||
137
generalCourse.in
Normal file
137
generalCourse.in
Normal file
@ -0,0 +1,137 @@
|
|||||||
|
department 特色通識—在地實踐
|
||||||
|
994017
|
||||||
|
994057
|
||||||
|
994065
|
||||||
|
994068
|
||||||
|
994071
|
||||||
|
994075
|
||||||
|
994076
|
||||||
|
994077
|
||||||
|
994078
|
||||||
|
994080
|
||||||
|
994086
|
||||||
|
994089
|
||||||
|
994112
|
||||||
|
994113
|
||||||
|
994114
|
||||||
|
department 特色通識—綠概念
|
||||||
|
993062
|
||||||
|
994001
|
||||||
|
994012
|
||||||
|
994020
|
||||||
|
994024
|
||||||
|
994074
|
||||||
|
994027
|
||||||
|
department 特色通識—東南亞
|
||||||
|
992106
|
||||||
|
994030
|
||||||
|
994096
|
||||||
|
994098
|
||||||
|
994099
|
||||||
|
994102
|
||||||
|
994103
|
||||||
|
994105
|
||||||
|
994108
|
||||||
|
994109
|
||||||
|
994110
|
||||||
|
994111
|
||||||
|
994010
|
||||||
|
department 自然—生命與科學
|
||||||
|
993001
|
||||||
|
993002
|
||||||
|
993022
|
||||||
|
993054
|
||||||
|
993086
|
||||||
|
993093
|
||||||
|
993106
|
||||||
|
993126
|
||||||
|
993131
|
||||||
|
993132
|
||||||
|
993133
|
||||||
|
993137
|
||||||
|
993145
|
||||||
|
993008
|
||||||
|
department 自然—工程與科技
|
||||||
|
993023
|
||||||
|
993052
|
||||||
|
993055
|
||||||
|
993060
|
||||||
|
993064
|
||||||
|
993075
|
||||||
|
993116
|
||||||
|
993156
|
||||||
|
993157
|
||||||
|
993013
|
||||||
|
993066
|
||||||
|
993111
|
||||||
|
993120
|
||||||
|
993143
|
||||||
|
department 社會—社經與管理
|
||||||
|
991094
|
||||||
|
992033
|
||||||
|
992035
|
||||||
|
992110
|
||||||
|
992120
|
||||||
|
992129
|
||||||
|
992141
|
||||||
|
992143
|
||||||
|
992177
|
||||||
|
992191
|
||||||
|
992193
|
||||||
|
992203
|
||||||
|
992205
|
||||||
|
992213
|
||||||
|
992214
|
||||||
|
992216
|
||||||
|
992217
|
||||||
|
992223
|
||||||
|
992062
|
||||||
|
992211
|
||||||
|
992232
|
||||||
|
department 社會—法政與教育
|
||||||
|
984003
|
||||||
|
992076
|
||||||
|
992108
|
||||||
|
992112
|
||||||
|
992178
|
||||||
|
992179
|
||||||
|
992180
|
||||||
|
992188
|
||||||
|
992206
|
||||||
|
992234
|
||||||
|
992185
|
||||||
|
department 人文—歷史哲學與文化
|
||||||
|
991068
|
||||||
|
991075
|
||||||
|
991087
|
||||||
|
991140
|
||||||
|
991144
|
||||||
|
991154
|
||||||
|
991163
|
||||||
|
991192
|
||||||
|
991199
|
||||||
|
991212
|
||||||
|
992073
|
||||||
|
992087
|
||||||
|
992171
|
||||||
|
994044
|
||||||
|
department 人文—文學與藝術
|
||||||
|
460135
|
||||||
|
991040
|
||||||
|
991062
|
||||||
|
991065
|
||||||
|
991069
|
||||||
|
991167
|
||||||
|
991170
|
||||||
|
991183
|
||||||
|
991190
|
||||||
|
991193
|
||||||
|
991201
|
||||||
|
991203
|
||||||
|
991207
|
||||||
|
991209
|
||||||
|
991210
|
||||||
|
991211
|
||||||
|
992176
|
||||||
|
991032
|
||||||
|
991071
|
||||||
170
getData.py
170
getData.py
@ -4,33 +4,39 @@ import os
|
|||||||
import csv
|
import csv
|
||||||
from bs4 import BeautifulSoup as bs
|
from bs4 import BeautifulSoup as bs
|
||||||
|
|
||||||
|
USERNAME = ""
|
||||||
|
PASSWORD = ""
|
||||||
|
YEAR = 1111
|
||||||
|
|
||||||
header = {
|
session = requests.Session()
|
||||||
'User-Agent': 'Mozilla/5.0 (X11; Linux x86_64; rv:79.0) Gecko/20100101 Firefox/79.0',
|
|
||||||
'Cookie': '輸入登入暨大教務系統後所得到的cookie'
|
|
||||||
}
|
|
||||||
|
|
||||||
mainURL = "https://ccweb.ncnu.edu.tw/student/"
|
mainURL = "https://ccweb.ncnu.edu.tw/student/"
|
||||||
courses = []
|
courses = []
|
||||||
generalCourse = []
|
generalCourse = []
|
||||||
|
|
||||||
def getGeneralCourseData(year):
|
def login(username, password):
|
||||||
'''
|
global session
|
||||||
透過年份取得 通識課程分類的csv檔
|
response = session.get('https://ccweb.ncnu.edu.tw/student/login.php')
|
||||||
供後續課程對應。
|
root = bs(response.text, 'html.parser')
|
||||||
|
loginToken = root.find('input', {'name': 'token'}).get('value')
|
||||||
|
|
||||||
先儲存到 generalCourse list,後續再用 courseID 對應通識分類
|
# request login page
|
||||||
'''
|
response = session.post(
|
||||||
|
"https://ccweb.ncnu.edu.tw/student/login.php",
|
||||||
|
data={
|
||||||
|
'token': loginToken,
|
||||||
|
'modal': '0',
|
||||||
|
'username': username,
|
||||||
|
'password': password,
|
||||||
|
'type': 'a'
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
# 教務系統有開放 年度的query
|
# 成功的話 return http 302, redirect
|
||||||
# 但實際操作後似乎僅開放當前學年度
|
if len(response.history)!=0:
|
||||||
response = requests.get(mainURL+"aspmaker_student_common_rank_courses_viewlist.php?x_studentid=0&z_studentid=LIKE&x_year={}&z_year=%3D&cmd=search&export=csv".format(year), headers=header)
|
return True
|
||||||
data = response.text
|
else:
|
||||||
|
return False
|
||||||
courses = data.split('\r\n')[1:-1]
|
|
||||||
for course in courses:
|
|
||||||
course = course.split(',')
|
|
||||||
generalCourse.append(course)
|
|
||||||
|
|
||||||
def curlDepartmentCourseTable(year):
|
def curlDepartmentCourseTable(year):
|
||||||
'''
|
'''
|
||||||
@ -39,74 +45,94 @@ def curlDepartmentCourseTable(year):
|
|||||||
'''
|
'''
|
||||||
print("取得所有課程資料:")
|
print("取得所有課程資料:")
|
||||||
|
|
||||||
response = requests.get(mainURL+"aspmaker_course_opened_semester_stat_viewlist.php?x_year={}&recperpage=ALL".format(year), headers=header)
|
# 切換年度,應該是用 cookie 儲存當前閱覽的年份
|
||||||
data = response.text
|
url = 'https://ccweb6.ncnu.edu.tw/student/aspmaker_course_opened_detail_viewlist.php?cmd=search&t=aspmaker_course_opened_detail_view&z_year=%3D&x_year={}&z_courseid=%3D&x_courseid=&z_cname=LIKE&x_cname=&z_deptid=%3D&x_deptid=&z_division=LIKE&x_division=&z_grade=%3D&x_grade=&z_teachers=LIKE&x_teachers=&z_not_accessible=LIKE&x_not_accessible='
|
||||||
root = bs(data, "html.parser")
|
response = session.get(url.format(year))
|
||||||
|
|
||||||
count = 1
|
# 取得 所有課程的 csv
|
||||||
departmentsTR = root.findAll('tr')[1:] # 清除 thead
|
response = session.get('https://ccweb6.ncnu.edu.tw/student/aspmaker_course_opened_detail_viewlist.php?export=csv')
|
||||||
for tr in departmentsTR:
|
with open("allCourses.csv", "wb") as fp:
|
||||||
name = tr.findAll('td')[4].find('span').find('span').string # 取得 科系名稱
|
fp.write(response.content)
|
||||||
link = mainURL + tr.find('a').get('data-url').replace('amp;', '') # 清除不必要符號, 取得 連結
|
|
||||||
print("擷取{}課程... ({}/{})...".format(name, count, len(departmentsTR)))
|
|
||||||
count += 1
|
|
||||||
extractDepartmentCourseTable(name, link) # 透過連結 開始擷取 各科系課程
|
|
||||||
|
|
||||||
def extractDepartmentCourseTable(departmentName, link):
|
def extractDepartmentCourseTable(year):
|
||||||
'''
|
'''
|
||||||
透過各科系連結取得課程資訊
|
透過各科系連結取得課程資訊
|
||||||
若為通識類別還要跟csv檔資料做對應,取得正確通識類別
|
若為通識類別還要跟csv檔資料做對應,取得正確通識類別
|
||||||
|
|
||||||
對應後存取到 output.json
|
對應後存取到 output.json
|
||||||
'''
|
'''
|
||||||
response = requests.get(link, headers=header)
|
with open("allCourses.csv") as fp:
|
||||||
data = response.text
|
csvData = fp.read()
|
||||||
root = bs(data, "html.parser")
|
|
||||||
|
ans = []
|
||||||
|
courses = csvData.split('"\n')[1:-1]
|
||||||
|
for course in courses:
|
||||||
|
course = course.replace('\n', '.')
|
||||||
|
# print(course)
|
||||||
|
data = course[1:].split('","')
|
||||||
|
|
||||||
courseTR = root.findAll('tr')[1:] # 清除 thead
|
|
||||||
for tr in courseTR:
|
|
||||||
courseObj = {}
|
courseObj = {}
|
||||||
tds = tr.find_all('td')
|
|
||||||
|
|
||||||
courseObj['link'] = mainURL + tds[0].find('a').get('href')
|
baseLink = "https://ccweb6.ncnu.edu.tw/student/aspmaker_course_opened_detail_viewlist.php?cmd=search&t=aspmaker_course_opened_detail_view&z_year=%3D&x_year={}&x_courseid={}"
|
||||||
courseObj['year'] = tds[1].find('span').string
|
courseObj['link'] = baseLink.format(year, data[1].zfill(6))
|
||||||
courseObj['number'] = tds[2].find('span').string
|
courseObj['year'] = data[0]
|
||||||
courseObj['class'] = tds[3].find('span').string
|
courseObj['number'] = data[1]
|
||||||
courseObj['name'] = tds[4].find('span').string
|
courseObj['class'] = data[2]
|
||||||
courseObj['department'] = tds[5].find('span').string
|
courseObj['name'] = data[3]
|
||||||
courseObj['graduated'] = tds[6].find('span').string
|
courseObj['department'] = data[4]
|
||||||
courseObj['grade'] = tds[7].find('span').string
|
courseObj['graduated'] = data[6]
|
||||||
courseObj['teacher'] = tds[8].find('span').string
|
courseObj['grade'] = data[7]
|
||||||
courseObj['place'] = tds[9].find('span').string
|
courseObj['teacher'] = data[8]
|
||||||
courseObj['time'] = tds[11].find('span').string
|
courseObj['place'] = data[9]
|
||||||
|
courseObj['time'] = data[13].replace(' ', '')
|
||||||
|
courseObj['credit'] = data[14]
|
||||||
|
|
||||||
if courseObj['department']=="99, 通識" :
|
ans.append(courseObj)
|
||||||
flag = False
|
|
||||||
for row in generalCourse:
|
|
||||||
if row[2] == '"{}"'.format(courseObj['number']):
|
|
||||||
courseObj['department'] = row[0].replace('"', '')
|
|
||||||
generalCourse.remove(row)
|
|
||||||
flag = True
|
|
||||||
break
|
|
||||||
if not flag:
|
|
||||||
print(" - 找不到對應的通識類別: {} ( {} )".format(courseObj['name'], courseObj['number']))
|
|
||||||
|
|
||||||
courses.append(courseObj)
|
with open("歷年課程資料/{}_output.json".format(year), 'w') as fp:
|
||||||
|
json.dump(ans, fp, ensure_ascii=False)
|
||||||
|
|
||||||
|
def updateGeneralCourse():
|
||||||
|
with open("歷年課程資料/{}_output.json".format(YEAR)) as fp:
|
||||||
|
courses = json.load(fp)
|
||||||
|
|
||||||
|
with open("generalCourse.in") as fp:
|
||||||
|
line = fp.readline()
|
||||||
|
while line:
|
||||||
|
count = 0
|
||||||
|
line = line.split()
|
||||||
|
if len(line) == 2:
|
||||||
|
department = line[1]
|
||||||
|
else:
|
||||||
|
for course in courses:
|
||||||
|
if course['number'] == line[0]:
|
||||||
|
course['department'] = department
|
||||||
|
count += 1
|
||||||
|
if count == 0 and len(line) != 2:
|
||||||
|
print("{} 可能輸入錯誤 - {}".format(line[0], department))
|
||||||
|
line = fp.readline()
|
||||||
|
|
||||||
|
print("還沒有對應到的課程:")
|
||||||
|
for course in courses:
|
||||||
|
if course['department'] == "99, 通識":
|
||||||
|
course['department'] = "99, 通識(未分類)"
|
||||||
|
print("{} {}".format(course['number'], course['name']))
|
||||||
|
|
||||||
|
with open("歷年課程資料/{}_output.json".format(YEAR), "w") as fp:
|
||||||
|
json.dump(courses, fp, ensure_ascii=False)
|
||||||
|
|
||||||
with open('output.json', 'w') as fp:
|
|
||||||
json.dump(courses, fp)
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
year = input("年份: ")
|
while True:
|
||||||
|
username = USERNAME
|
||||||
|
password = PASSWORD
|
||||||
|
if login(username, password):
|
||||||
|
print("登入成功!")
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
print("登入失敗!")
|
||||||
|
|
||||||
getGeneralCourseData(year)
|
curlDepartmentCourseTable(YEAR)
|
||||||
curlDepartmentCourseTable(year)
|
extractDepartmentCourseTable(YEAR)
|
||||||
|
updateGeneralCourse()
|
||||||
print("\n\n=====================")
|
|
||||||
print("未列入追蹤的通識課程")
|
|
||||||
print("=====================\n")
|
|
||||||
|
|
||||||
for notIn in generalCourse:
|
|
||||||
if "體育:" not in notIn[5]:
|
|
||||||
print(" - 未列入追蹤的新通識課程: {}".format(notIn))
|
|
||||||
@ -3,9 +3,9 @@
|
|||||||
<head>
|
<head>
|
||||||
<meta charset="utf-8" />
|
<meta charset="utf-8" />
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no" />
|
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no" />
|
||||||
<meta name="description" content="" />
|
<meta name="description" content="你還在用紙筆或Excel在安排下學期的課表嗎?「暨大排課表」幫你篩選衝堂、科系分類、通識課程分類,讓你輕鬆排課表!" />
|
||||||
<meta name="author" content="" />
|
<meta name="author" content="snsd0805" />
|
||||||
<title>暨大排課表</title>
|
<title>暨大排課表</title>
|
||||||
<!-- Favicon-->
|
<!-- Favicon-->
|
||||||
<link rel="icon" type="image/x-icon" href="assets/img/favicon.ico" />
|
<link rel="icon" type="image/x-icon" href="assets/img/favicon.ico" />
|
||||||
<!-- Font Awesome icons (free version)-->
|
<!-- Font Awesome icons (free version)-->
|
||||||
|
|||||||
@ -8,35 +8,25 @@ var coursesList = {
|
|||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
'getTime': function (timeString) {
|
'getTime': function (timeString) {
|
||||||
if (timeString == null) {
|
let num;
|
||||||
return ""
|
const timeRegex = new RegExp(/^\d[\da-z]*[a-z]$/);
|
||||||
}
|
return timeRegex.test(timeString)
|
||||||
|
? [...timeString].reduce((res, c) => {
|
||||||
ans = []
|
if (Number.isInteger(+c)) {
|
||||||
number = ""
|
num = c;
|
||||||
for (var i of timeString) {
|
return res;
|
||||||
if (i >= "0" && i <= "9") {
|
} else {
|
||||||
number = i
|
return [...res, num + c];
|
||||||
} else if (i >= "a" && i <= "z") {
|
}
|
||||||
ans.push(number + i)
|
}, [])
|
||||||
}
|
: [];
|
||||||
else {
|
|
||||||
ans.push(timeString)
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return ans
|
|
||||||
},
|
},
|
||||||
'isOK': function (course) {
|
'isOK': function (course) {
|
||||||
var time = this.getTime(course.time)
|
var time = this.getTime(course.time)
|
||||||
// console.log(course.name, " ", time)
|
// console.log(course.name, " ", time)
|
||||||
for (t of time) {
|
const isConflict = time.some((t) => this.selectedTime.includes(t))
|
||||||
for (st of this.selectedTime) {
|
|
||||||
if (t == st)
|
return time.length && !isConflict
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
},
|
},
|
||||||
'log': function (name, data) {
|
'log': function (name, data) {
|
||||||
console.log(name, data)
|
console.log(name, data)
|
||||||
|
|||||||
@ -9,6 +9,7 @@ var mainWindow = {
|
|||||||
"user": "",
|
"user": "",
|
||||||
'token': "",
|
'token': "",
|
||||||
'is_print': false,
|
'is_print': false,
|
||||||
|
'creditNum': 0,
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
created() {
|
created() {
|
||||||
@ -96,6 +97,14 @@ var mainWindow = {
|
|||||||
}).then(function (jsonData) {
|
}).then(function (jsonData) {
|
||||||
console.log(jsonData)
|
console.log(jsonData)
|
||||||
main.selectCourses = JSON.parse(jsonData['data'])
|
main.selectCourses = JSON.parse(jsonData['data'])
|
||||||
|
|
||||||
|
var courseSet = new Set()
|
||||||
|
for (var course of main.selectCourses) {
|
||||||
|
if (!courseSet.has(course.number+course.class)) { // 用 courseID + 班別 判斷是否重複
|
||||||
|
main.creditNum += parseFloat(course.credit)
|
||||||
|
courseSet.add(course)
|
||||||
|
}
|
||||||
|
}
|
||||||
})
|
})
|
||||||
.catch(function (err) {
|
.catch(function (err) {
|
||||||
alert("錯誤: " + err)
|
alert("錯誤: " + err)
|
||||||
@ -105,6 +114,12 @@ var mainWindow = {
|
|||||||
'saveCourseTable': function () {
|
'saveCourseTable': function () {
|
||||||
var main = this
|
var main = this
|
||||||
if (this.token != "") {
|
if (this.token != "") {
|
||||||
|
filteredCourses = []
|
||||||
|
for(var tempCourse of main.selectCourses){
|
||||||
|
if(tempCourse.temp == false){
|
||||||
|
filteredCourses.push(tempCourse);
|
||||||
|
}
|
||||||
|
}
|
||||||
fetch('https://api.snsd0805.com/courseTable', {
|
fetch('https://api.snsd0805.com/courseTable', {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: {
|
headers: {
|
||||||
@ -112,7 +127,7 @@ var mainWindow = {
|
|||||||
},
|
},
|
||||||
body: JSON.stringify({
|
body: JSON.stringify({
|
||||||
'token': main.token,
|
'token': main.token,
|
||||||
'data': main.selectCourses
|
'data': filteredCourses
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
.then(function (response) {
|
.then(function (response) {
|
||||||
@ -134,20 +149,18 @@ var mainWindow = {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
'getTime': function (timeString) {
|
'getTime': function (timeString) {
|
||||||
ans = []
|
let num;
|
||||||
number = ""
|
const timeRegex = new RegExp(/^\d[\da-z]*[a-z]$/);
|
||||||
for (var i of timeString) {
|
return timeRegex.test(timeString)
|
||||||
if (i >= "0" && i <= "9") {
|
? [...timeString].reduce((res, c) => {
|
||||||
number = i
|
if (Number.isInteger(+c)) {
|
||||||
} else if (i >= "a" && i <= "z") {
|
num = c;
|
||||||
ans.push(number + i)
|
return res;
|
||||||
}
|
} else {
|
||||||
else {
|
return [...res, num + c];
|
||||||
ans.push(timeString)
|
}
|
||||||
break
|
}, [])
|
||||||
}
|
: [];
|
||||||
}
|
|
||||||
return ans
|
|
||||||
},
|
},
|
||||||
'select': function (department) {
|
'select': function (department) {
|
||||||
this.selectDepartment = department
|
this.selectDepartment = department
|
||||||
@ -163,9 +176,12 @@ var mainWindow = {
|
|||||||
'name': course.name,
|
'name': course.name,
|
||||||
'temp': false,
|
'temp': false,
|
||||||
'number': course.number,
|
'number': course.number,
|
||||||
'class': course.class
|
'class': course.class,
|
||||||
|
'credit': course.credit,
|
||||||
|
'link': course.link
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
this.creditNum += parseFloat(course.credit)
|
||||||
},
|
},
|
||||||
'removeCourse': function (course) {
|
'removeCourse': function (course) {
|
||||||
console.log("remove " + course.name)
|
console.log("remove " + course.name)
|
||||||
@ -174,6 +190,7 @@ var mainWindow = {
|
|||||||
this.selectCourses.splice(i, 1)
|
this.selectCourses.splice(i, 1)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
this.creditNum -= parseFloat(course.credit)
|
||||||
},
|
},
|
||||||
'saveTemp': function (course) {
|
'saveTemp': function (course) {
|
||||||
if (course == null) {
|
if (course == null) {
|
||||||
@ -275,6 +292,7 @@ var mainWindow = {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<br>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-lg-3">
|
<div class="col-lg-3">
|
||||||
<div class="row mx-auto mb-2">
|
<div class="row mx-auto mb-2">
|
||||||
@ -299,8 +317,19 @@ var mainWindow = {
|
|||||||
>
|
>
|
||||||
</course-anslist>
|
</course-anslist>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="row">
|
||||||
|
<div class="col">
|
||||||
|
<div class="card">
|
||||||
|
<div class="card-body">
|
||||||
|
已經選了 {{ creditNum }} 學分
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<br>
|
||||||
|
|
||||||
<div class="col-lg-9 table-responsive " >
|
<div class="col-lg-9 table-responsive " >
|
||||||
<course-table
|
<course-table
|
||||||
id="course-table-div"
|
id="course-table-div"
|
||||||
@ -328,12 +357,11 @@ var mainWindow = {
|
|||||||
</div>
|
</div>
|
||||||
<div class="modal-body">
|
<div class="modal-body">
|
||||||
<ul>
|
<ul>
|
||||||
<li>已經更新為 1092 新課表(包含通識課分類)</li>
|
<li>已經更新為 1101 新學期課表(包含通識課分類)</li>
|
||||||
<li>使用 Facebook API 儲存課表</li>
|
<li>有發現 Bug 可以到 <a href='https://github.com/snsd0805/NCNU_Course/issues'>GitHub</a> 發 issue 或 <a href='mailto: levi900227@gmail.com'>mail</a></li>
|
||||||
<li>新增「下載圖檔」功能</li>
|
<li>請善用「連接 Facebook」功能來儲存課表!</li>
|
||||||
<li>新增「分享課表」功能</li>
|
|
||||||
</ul>
|
</ul>
|
||||||
2021 01/23 更新
|
2021 07/16 更新
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-footer">
|
<div class="modal-footer">
|
||||||
<button type="button" class="btn btn-secondary" data-dismiss="modal">我知道了</button>
|
<button type="button" class="btn btn-secondary" data-dismiss="modal">我知道了</button>
|
||||||
@ -353,7 +381,7 @@ var mainWindow = {
|
|||||||
</div>
|
</div>
|
||||||
<div class="modal-body">
|
<div class="modal-body">
|
||||||
請複製以下網址給你的朋友,跟他分享你的課表<br><br>
|
請複製以下網址給你的朋友,跟他分享你的課表<br><br>
|
||||||
https://snsd0805.com/NCNU_Course/#/share/{{user.id}}
|
https://course.snsd0805.com/#/share/{{user.id}}
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-footer">
|
<div class="modal-footer">
|
||||||
<button type="button" class="btn btn-secondary" data-dismiss="modal">我知道了</button>
|
<button type="button" class="btn btn-secondary" data-dismiss="modal">我知道了</button>
|
||||||
|
|||||||
@ -3,6 +3,7 @@ var courseDiv = {
|
|||||||
template: `
|
template: `
|
||||||
<div style='border: 5px #1abc9c solid; text-align: center;'>
|
<div style='border: 5px #1abc9c solid; text-align: center;'>
|
||||||
{{ course.name }}
|
{{ course.name }}
|
||||||
|
<a v-bind:href="course.link" target="_blank"><i class="fas fa-info-circle"></i></a>
|
||||||
<button type="button"
|
<button type="button"
|
||||||
v-if="!is_shared"
|
v-if="!is_shared"
|
||||||
v-on:click="$emit('remove-course', course)"
|
v-on:click="$emit('remove-course', course)"
|
||||||
@ -54,7 +55,9 @@ var courseTable = {
|
|||||||
'name': c.name,
|
'name': c.name,
|
||||||
'number': c.number,
|
'number': c.number,
|
||||||
'class': c.class,
|
'class': c.class,
|
||||||
'temp': c.temp
|
'temp': c.temp,
|
||||||
|
'credit': c.credit,
|
||||||
|
'link': c.link
|
||||||
}
|
}
|
||||||
|
|
||||||
if(c.time[0]==6 || c.time[0]==7){
|
if(c.time[0]==6 || c.time[0]==7){
|
||||||
|
|||||||
20
ncnu-course-api.service
Normal file
20
ncnu-course-api.service
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
[Unit]
|
||||||
|
Description=NCNU-Course Python Backend API
|
||||||
|
After=network.target
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
Type=simple
|
||||||
|
ExecStart=python3 api.py
|
||||||
|
Restart=always
|
||||||
|
|
||||||
|
WorkingDirectory=/var/www/html/NCNU_Course
|
||||||
|
User=course
|
||||||
|
|
||||||
|
RestartSec=10s
|
||||||
|
|
||||||
|
StandardOutput=syslog
|
||||||
|
StandardOutput=syslog
|
||||||
|
SyslogIdentifier=ncnu-course
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy=multi-user.target
|
||||||
File diff suppressed because one or more lines are too long
1
歷年課程資料/1101_output.json
Normal file
1
歷年課程資料/1101_output.json
Normal file
File diff suppressed because one or more lines are too long
Loading…
Reference in New Issue
Block a user