Compare commits

...

16 Commits
v2.0 ... master

Author SHA1 Message Date
aec6c5efe1 更新.gitlab-ci.yml.bak 2022-02-02 11:36:28 +00:00
bab7ebe761 更新cherry.py, .gitlab-ci.yml 2021-09-04 03:45:01 +00:00
5e2fd4ed75 更新.gitlab-ci.yml, Dockerfile 2021-09-04 03:33:17 +00:00
e7afd9ab7d 更新Dockerfile 2021-09-04 03:23:34 +00:00
378d49f5bb 更新requirements.txt, Dockerfile 2021-09-04 03:14:08 +00:00
181bfbcba3 更新Dockerfile 2021-09-04 03:00:19 +00:00
ElonLi
b660dd033c cherry reborn!!! 2021-09-04 10:50:46 +08:00
ElonLi
ae6fe3ee8f update gitignore 2021-09-04 10:49:57 +08:00
3b33e26617 update ci 2021-09-03 20:02:53 +08:00
e8379607da Merge branch 'master' of ssh://git.lacus.site:1023/lacus/cherrybackend 2021-05-22 17:12:28 +08:00
5b40de742f 添加空教室代码 2021-05-22 17:12:17 +08:00
a7d30aea72 update 2021-03-02 19:45:27 +08:00
6c6fd02657 完成登录 2021-02-17 15:28:07 +08:00
d79dec478b 更新登录 2021-02-08 21:09:02 +08:00
3c99c1bf78 修改数据库以及爬虫 2021-02-06 20:06:07 +08:00
cc33a6a8d5 修改数据库以及爬虫 2021-02-06 20:04:05 +08:00
26 changed files with 1565 additions and 444 deletions

1
.gitignore vendored
View File

@ -1,2 +1,3 @@
__pycache__
.vscode/settings.json
venv

54
.gitlab-ci.yml.bak Normal file
View File

@ -0,0 +1,54 @@
variables:
APP_NAME: "cherry_be"
OUTSIDE_PORT: 5018
INSIDE_PORT: 7980
IMAGE_NAME: "$APP_NAME:$CI_COMMIT_REF_NAME-$CI_COMMIT_SHORT_SHA"
DOCKER_FILE_PATH: "./Dockerfile"
stages:
- build
- clear
- deploy
- rmimg
build:
stage: build
image: docker:latest
services:
- name: docker:dind
tags:
- dockerbase
script:
- ls -a
- docker build -t ${IMAGE_NAME} -f ${DOCKER_FILE_PATH} .
clear:
stage: clear
tags:
- dockerbase
only:
- master
script:
- docker stop ${APP_NAME}
- docker rm ${APP_NAME}
allow_failure: true
deploy:
stage: deploy
tags:
- dockerbase
only:
- master
script:
- ls -a
- docker run -t -d --name ${APP_NAME} -p ${OUTSIDE_PORT}:${INSIDE_PORT} ${IMAGE_NAME}
rmimg:
stage: rmimg
tags:
- dockerbase
only:
- master
script:
- docker image rm `docker image ls -q ${APP_NAME} | tail -1`
allow_failure: true

View File

@ -5,5 +5,5 @@ WORKDIR /app
RUN pip install -r requirements.txt -i https://pypi.tuna.tsinghua.edu.cn/simple
COPY . /app
# ENTRYPOINT ["sh"]
# CMD ["go.sh"]
CMD sh go.sh && python
# CMD sleep 100000000

95
EmptyRoom/main.py Normal file
View File

@ -0,0 +1,95 @@
import json
import requests
from urllib.parse import quote
import base64
from bs4 import BeautifulSoup
import sys
from param import getParam, IDLIST, COURSELIST
import time
class EmptyRoom(object):
def __init__(self, username, password, phone=''):
self.__phone = phone
self.__session = None
self.__ip = None
self.__getEmptyUrl = 'http://jwgls0-cust-edu-cn-8080-p.webvpn.cust.edu.cn:8118/api/ClientTeacher/QueryService/EmptyRoomQueryApi/GetEmptyRoomDataByPage?sf_request_type=ajax'
self.connection(username, password)
# 链接教务 -----------------------------------------------------------------------------
def connection(self, username, password):
try:
self.__session = requests.Session()
# 获取统一身份系统的网页
r = self.__session.get(
url='http://mysso-cust-edu-cn-s.webvpn.cust.edu.cn:8118/cas/login?service=https%3A%2F%2Fwebvpn.cust.edu.cn%2Fauth%2Fcas_validate%3Fentry_id%3D1')
soup = BeautifulSoup(r.text, 'html.parser')
execution = soup.find_all(name='input')[6]['value']
formdata = {
'username': username,
'password': password,
'execution': execution,
'_eventId': 'submit',
'geolocation': ''
}
r = self.__session.post(
url='http://mysso-cust-edu-cn-s.webvpn.cust.edu.cn:8118/cas/login?service=https%3A%2F%2Fwebvpn.cust.edu.cn%2Fauth%2Fcas_validate%3Fentry_id%3D1', data=formdata)
r = self.__session.get(
url='http://portal-cust-edu-cn-s.webvpn.cust.edu.cn:8118/custp/index')
soup = BeautifulSoup(r.text, 'html.parser')
try:
self.__ip = soup.findAll(name='a')[7]['href'][7:].split("-")
except:
return ('账号或者密码错误', 510)
r = self.__session.get(url='http://mysso-cust-edu-cn-s.webvpn.cust.edu.cn:8118/cas/login?service=http://' +
self.__ip[0] + '.' + self.__ip[1] + '.' + self.__ip[2] + '.' + self.__ip[3] + ':8080/welcome', allow_redirects=False)
ticket = r.headers['Location'][72:]
asp_net_sessionid_param = {'Ticket': ticket, 'Url': 'http://' +
self.__ip[0] + '.' + self.__ip[1] + '.' + self.__ip[2] + '.' + self.__ip[3] + ':8080/welcome'}
asp_net_sessionid_param = base64.b64encode(
quote(json.dumps(asp_net_sessionid_param)).encode('utf-8')).decode('utf-8')
asp_net_sessionid_param = {'param': asp_net_sessionid_param}
headers = {'Content-Type': 'application/json'}
r = self.__session.post(url='http://' + self.__ip[0] + '-' + self.__ip[1] + '-' + self.__ip[2] + '-' + self.__ip[3] +
'-8080-p.webvpn.cust.edu.cn:8118/api/LoginApi/LGSSOLocalLogin?sf_request_type=ajax', data=json.dumps(asp_net_sessionid_param), headers=headers)
data = json.loads(r.content.decode('utf-8'))
# 提示未建立教务信息
if data['state'] != 0:
raise Exception('登录出错')
except Exception as e:
print(e)
return ('教务挂了', 512)
# 获取空教室信息
def getEmptyInfo(self, SJ, JSs, Addr):
param = ''
try:
param = getParam(SJ, JSs, Addr)
except Exception as e:
print(e)
return ('数据校验失败', 400)
Headers = {'Content-Type': 'application/json; charset=utf-8'}
formdata = {"param": param, "__permission": {"MenuID": "E93957BB-C05C-4D97-90F6-7839E1A77B62", "Operation": 0},
"__log": {"MenuID": "E93957BB-C05C-4D97-90F6-7839E1A77B62", "Logtype": 6, "Context": "查询"}}
try:
resp = self.__session.post(
self.__getEmptyUrl, data=json.dumps(formdata), headers=Headers)
data = resp.json()['data']['PagingResult']['Rows']
list = []
for content in data:
list.append(content['JSMC'])
return list
except Exception as e:
print(e)
def getEmptyList(self):
today = time.strftime("%Y-%m-%d", time.localtime())
totalList = {}
for building in IDLIST.keys():
totalList[building] = {}
for course in COURSELIST:
totalList[building][course] = self.getEmptyInfo(today, [course], building)
print(totalList)
c = EmptyRoom('2017002372', '623910ert&')
# c.getEmptyInfo('2020-09-06', ['0102'], 'etb1')
c.getEmptyList()

97
EmptyRoom/param.py Normal file
View File

@ -0,0 +1,97 @@
from urllib.request import quote, unquote
import base64
import json
# 西区第一教学楼
WESTID = {
# 西区第一教学楼
'teaching_building_1': 'f74dac26-c58a-4eae-bc46-ff2055b2de19',
# 西区第二教学楼
'teaching_building_2': '201a429b-df6d-489e-9a1d-de7c85f0081e',
# 西区图书馆
'library': '3adb80f2-7e27-4058-a60c-fbb32cb36587'
}
EASTID = {
# 东区第一教学楼
'teaching_building_1': 'd91cc53c-a9ad-4be3-becf-7f3ed62e8762',
# 东区第二教学楼
'teaching_building_2': 'e14b90bd-0c92-422e-b299-7009118104b9',
# 东区第三教学楼
'teaching_building_3': '3534f8ce-f10b-4058-a818-95a116d9bca4',
# 东区前楼
'front_building': '6accca4d-b092-4bdc-b2e0-0c1941782eec'
}
SOUTHID = {
# 南区研究生教学楼
'graduate_building': '20a207f7-65ef-4ae4-9286-2a2b5a73e1c9',
# 南区实训楼
'practical_training_building': 'cb5265e8-84a1-41ed-985b-3920449738aa'
}
IDLIST = {
'wtb1': WESTID['teaching_building_1'],
'wtb2': WESTID['teaching_building_2'],
'wl': WESTID['library'],
'etb1': EASTID['teaching_building_1'],
'etb2': EASTID['teaching_building_2'],
'etb3': EASTID['teaching_building_3'],
'efb': EASTID['front_building'],
'sgb': SOUTHID['graduate_building'],
'sptb': SOUTHID['practical_training_building'],
}
COURSELIST = ['0102', '0304', '0506', '0708', '0910', '1112']
# JSON转base64
def btoa(content):
return base64.b64encode(quote(content).encode())
# base64转JSON
def atob(content):
return unquote(base64.b64decode(content).decode())
def getParam(SJ, JSs, Addr):
try:
checkData(SJ, JSs, Addr)
except Exception as e:
print(e)
raise Exception(str(e))
param = {
"EmptyRoomParam": {
"SJ": SJ,
"JCs": JSs
},
"PagingParam": {
"IsPaging": 1,
"Offset": 0,
"Limit": 500,
"Conditions": {
"PropertyParams": [
{
"Field": "BDJXLXXID",
"Value": IDLIST[Addr]
}
]
}
}
}
return str(btoa(json.dumps(param)))[2:-1]
# 校验数据
def checkData(sj, jss, addr):
# 校验sj
sj_split = sj.split('-')
if len(sj_split) != 3:
raise Exception('SJ错误')
for content in sj_split:
if len(content) == 0:
raise Exception('SJ错误')
# 校验jss
if not isinstance(jss,list):
raise Exception('JSs错误')
for content in jss:
if content not in COURSELIST:
raise Exception('JSs错误')
# 校验addr
if addr not in IDLIST:
raise Exception('Addr错误')

137
README.md
View File

@ -8,13 +8,6 @@ python3 -m venv venv
. venv/bin/activate
// 升级pip
pip install --upgrade pip
// 安装flask...
pip install Flask
pip install requests
pip install bs4
pip install gunicorn
pip install pymongo
pip install flask_cors
// 根据依赖文件安装环境
pip install -r requirements.txt -i https://pypi.tuna.tsinghua.edu.cn/simple
// 设置全局变量
@ -41,15 +34,16 @@ deactivate
510账号或密码错误
511请填写手机号
512教务挂了
513教务报错
200OK
/api/game/schedule/upload
400数据校验失败
401:排名表修改失败
510:排名表修改失败
/api/game/schedule/get
410: 数据校验失败
411:排名表获取失败
400: 数据校验失败
510:排名表获取失败
# game
* /api/game/schedule/upload
@ -60,4 +54,125 @@ deactivate
* sign
* /api/game/schedule/get
* data
* sign
* sign
# 新版改造
## 用户组功能
* [ ] 主进程挂一个我的账号用来进行课表的爬取
* [ ] 第一次登录要进行账号密码的校验,然后对称加密进数据库
* [ ] 每天0点开始刷新所有人的课表以及所有人的成绩
* [ ] 每个用户组的id是五位数字加字母的组合
* [ ] 每个用户组成员不设上限
* [ ] 提供每个用户组下的个人课表以及个人总体课表
* [ ] 允许设置组级别的课程(全组个人课表及组课表可见)
* [ ] 数据库按课程存储字段sid, real_name, course, weeks, weeks_split, teacher, room, is_personal, day, period
* [ ] 登录的时候返回所有必要信息,剩下信息单独返回
* [ ] 组信息
* [ ] 刷新课表使用cookie加速
* [ ] 刷新成绩(使用账号密码
## 背景图片上传
* [ ] 上传图片id保存在数据库和个人信息一起
## 数据库字段
## log
* time
* type
* value
### config
* key
* value
### user
* uid 用户唯一识别码,教务下发
* invite_list 邀请内容
* cid 一卡通号
* sid 学号
* real_name 真实姓名
* pwd 加密后的密码
* setting 用户设置
* bg 课表背景图片
* avatar 头像
* last_update 最后课程更新时间
### group
* group_id 唯一标识
* group_name 用户组名称
* log_list 操作记录
* admin_list 管理列表
* creater_sid 创建者学号
* avatar 头像
### link
* cid 一卡通号
* group_id 用户组id
### user_crouse
* crouse_id 课程id
* sid 学号
* real_name 真实姓名
* crouse 课程名
* weeks 中文周数
* weeks_split 渲染用周数列表
* teacher 教师名
* room 教室名
* is_personal 是否是自定义课程
* day 星期
* period 上课时间
### group_crouse
* crouse_id 课程id
* group_id 用户组id
* sid 学号
* real_name 真实姓名
* crouse 课程名
* weeks 中文周数
* weeks_split 渲染用周数列表
* teacher 教师名
* room 教室名
* day 星期
* period 上课时间
## 数据库错误码
### OK 200
### insert 1
insertUser '学生信息数据库插入失败', 100
insertInvite '用户组邀请数据库插入失败', 101
bindUserGroup '用户与用户组绑定数据库插入失败', 102
insertGroup '用户组数据库插入失败', 103
addLog '操作记录数据库插入失败', 104
groupInsertAdmin '用户组侧加入管理数据库插入失败', 106
userInsertCrouse '用户自定义课程数据库插入失败', 107
userInsertAllCrouse '用户所有课程数据库插入失败', 108
insertRank '排名表数据库插入失败', 109
groupInsertCrouse '用户组课程数据库插入失败', 110
insertLog '系统操作记录数据库插入失败', 111
### find 3
findCookie 'cookie数据库查询失败', 300
findUser '学生信息数据库查询失败', 301
findGroup '用户组信息数据库查询失败', 302
findRank '排名表数据库查询失败', 303
findUserCrouse '用户课程数据库查询失败', 304
findGroupCrouse '用户组课程数据库查询失败', 305
findLog '系统操作记录数据库查询失败', 306
### update 4
updateCookie 'cookie数据库更新失败', 400
updateAvatar '头像数据库更新失败', 401
updateBg '背景图数据库更新失败', 402
updateLastUpdate '数据更新时间数据库更新失败', 403
### delete 5
deleteInvite '用户组邀请数据库删除失败', 500
unbindUserGroup '用户与用户组解绑数据库删除失败', 501
userDeleteAllCrouse '用户所有课程数据库删除失败', 502
groupDeleteAdmin '用户组侧移除管理数据库删除失败', 503
deleteGroup '解散用户组数据库删除失败', 504
userDeleteCrouse '用户自定义课程数据库删除失败', 505
groupDeleteCrouse '用户组课程数据库删除失败', 506
## 接口错误代码
### /login
400 数据不合法
510 学生信息数据库查询失败
511 账号或密码错误
512 用户组信息数据库查询失败
513 请填写手机号
514 教务返回的错误
515 教务挂了
516 用户所有课程数据库删除失败
517 用户所有课程数据库插入失败
518 用户课程数据库查询失败
519 用户组课程数据库查询失败

View File

@ -1,8 +1,8 @@
# 引入文件夹中的文件
# 引入文件夹中的文件
import sys
sys.path.append('./lib')
# 引入处理函数
from allFunction import manageLogin, manageScheduleUpload, manageScheduleGet
from allFunction import manageLogin, manageScheduleUpload, manageScheduleGet, manageSubmitVerificationCode
# 引入flask
from flask import Flask, request, session, redirect
# 初始化app
@ -24,6 +24,11 @@ def login():
res = manageLogin(request)
return res
@app.route('/api/submitVC', methods=['POST'])
def submitVC():
res = manageSubmitVerificationCode(request)
return res
# 更新课表游戏排名信息
@app.route('/api/game/schedule/upload',methods=['POST'])
def schedule_upload():
@ -43,4 +48,4 @@ def miss(e):
# 本地运行启动
if __name__ == '__main__':
app.run(host="0.0.0.0", debug=True, port="80")
app.run(host="0.0.0.0", debug=True, port="7980")

View File

@ -1,11 +1,16 @@
# coding=utf-8
import sys
from requests.sessions import session
sys.path.append('./lib/public')
from crawler import Crawler
import json
from hashlib import md5
from urllib.parse import urlencode, unquote_plus
from db import addRank, getRank
from db import insertRank, findRank
# 主函数
crawlerCache = {}
# 处理登录操作 data:{cid,pwd,sign}
# 这里三个接口公用一个session所以合并成一个接口一个session走到底一次性返回所有数据
def manageLogin(request):
@ -14,16 +19,26 @@ def manageLogin(request):
# MD5校验
checked = checkData(data)
if checked:
# 创建会话
phone = ''
if data.get('phone'):
phone = data['phone']
c = Crawler(data['cid'], data['pwd'], phone)
c = Crawler()
c.defaultInit(data)
res = c.connection()
crawlerCache[data['cid']] = c
return res
else:
return '数据校验失败', 400
def manageSubmitVerificationCode(request):
data = json.loads(request.form['data'])
checked = checkData(data)
if checked:
c = crawlerCache[data['cid']]
res = c.submitVerificationCode(data)
if res[-1] == 200:
c.getGrade()
c.getSchedule()
return c.getData()
grade = c.getGrade()
ownSchedule = c.getOwnSchedule()
crawlerCache[data['cid']] = None
return json.dumps({'grade': grade, 'ownSchedule': ownSchedule}), 200
else:
return res
else:
@ -37,10 +52,10 @@ def manageScheduleUpload(request):
checked = checkData(data_cache)
data_cache.pop('sign')
if checked:
add_res = addRank( data_cache['nick'], data_cache['count'], data_cache['time'])
add_res = insertRank( data_cache['cid'], data_cache['nick'], data_cache['count'], data_cache['time'])
return add_res
else:
return {'errcode': 400, 'errmsg': '数据校验失败'}
return '数据校验失败', 400
# 处理获取课表游戏排名信息
def manageScheduleGet(request):
@ -51,10 +66,10 @@ def manageScheduleGet(request):
data_cache.pop('sign')
if checked:
# 获取排名表
get_res = getRank()
get_res = findRank()
return get_res
else:
return {'errcode': 400, 'errmsg': '数据校验失败'}
return '数据校验失败', 400
# 工具函数

View File

@ -1,25 +0,0 @@
import unittest
from crawler import Crawler
c = Crawler('2017002372', '623910ert&', '15143211127')
class TestCrawler(unittest.TestCase):
# 测试链接
def test_connection(self):
self.assertEqual(c.connection(), ('ok', 200))
#测试获取成绩
def test_grade(self):
self.assertEqual(c.getGrade(), ('ok', 200))
#测试获取课表
def test_schedule(self):
self.assertEqual(c.getSchedule(), ('ok', 200))
#测试返回信息
def test_getData(self):
get_res = c.getData()
self.assertEqual(get_res[1], 200)
if __name__ == '__main__':
unittest.main()

View File

@ -1,51 +0,0 @@
from pymongo import MongoClient
from bson import ObjectId, json_util
# 主环境 (生产环境为production开发环境为development)
setting = 'production'
env = 'ali'
# 获取数据集
def col(arg):
if env == 'coc':
conn = MongoClient('mongodb://coc:qlSfefSor5@0.0.0.0:12236/coc')
else:
conn = MongoClient('mongodb://cherry:fR1jW2xG3bE9@mongo:27017/cherry')
if setting == 'development':
arg += '_test'
if arg == 'rank':
return conn[env].rank
elif arg == 'rank_test':
return conn[env].rank_test
else:
return False
# 向排名表里增加或覆写数据
def addRank(nick, count, time):
try:
col('rank').update({"cid": cid}, {'$setOnInsert': {"nick": nick}, '$set': {
"count": count, "time": time}} , {'upsert': 'true'})
# col('rank').insert_one({"count":count,"time":time,"nick":nick})
except Exception as e:
# 失败
return {'errcode': 401, 'errmsg': '排名表修改失败'}
return {'errcode': 200, 'errmsg': 'ok'}
# 获取排名表所有信息除了id
def getRank():
time_rank = []
count_rank = []
try:
for i in col('rank').find({}, {"_id": 0}).sort([("time", 1), ("count", 1)]).limit(10):
time_rank.append(i)
for i in col('rank').find({}, {"_id": 0}).sort([("count", 1), ("time", 1)]).limit(10):
count_rank.append(i)
except Exception as e:
print(e)
return {'errcode': 411, 'errmsg': '排名表获取失败'}
return {'errcode': 200, 'time_rank': time_rank, 'count_rank': count_rank, 'errmsg': 'ok'}

0
lib/functions.py Normal file
View File

0
lib/process/__init__.py Normal file
View File

217
lib/process/login.py Normal file
View File

@ -0,0 +1,217 @@
from ..public.db import updateLastUpdate, insertUser, findUser, userDeleteAllCrouse, userInsertAllCrouse, findUserCrouse, findGroupCrouse
from ..public.utils import checkData, signCode, findGroup
from ..public.crawler import Crawler
import json
import time
def check(request):
"""
校验数据
目标内容 cid,pwd,phone
"""
try:
data = request.json
if not checkData(data):
raise Exception
if not data.__contains__('cid'):
raise Exception
if not data.__contains__('pwd'):
raise Exception
return data, 200
except Exception as e:
print(e)
return '数据不合法', 400
def manageLogin(request):
"""
用户登录
"""
# 校验数据
check_res = check(request)
# 抛出错误
if check_res[-1] != 200:
return check_res
data = check_res[0]
# 查找用户
find_res = findUser(data['cid'])
# 抛出错误
if find_res[-1] != 200:
return find_res[0], 510
user_info = find_res[0]
# 无用户进行注册
if not user_info:
return sign(data['cid'], data['pwd'])
# 存在用户进行登录
return login(data, user_info)
def manageFindGroup(group_list):
"""
根据组id查询组信息
"""
list = []
for group_id in group_list:
find_res = findGroup(group_id)
if find_res[-1] != 200:
return find_res[0], 512
list.append(find_res[0])
return list, 200
def manageCrawler(cid, pwd, phone):
"""
处理爬虫返回课表和成绩
"""
try:
c = Crawler()
init_res = c.defaultInit(cid, pwd, phone)
if init_res[-1] != 200:
return init_res
get_res = c.getOwnSchedule()
if get_res[-1] != 200:
return get_res
schedule = get_res[0]
get_res = c.getGrade()
if get_res[-1] != 200:
return get_res
grade = get_res[0]
return {
'user_info': init_res[0],
'grade': grade,
'schedule': schedule,
}, 200
except Exception as e:
print(e)
return '教务挂了', 515
def manageNewCrouse(sid, schedule):
"""
向数据库中插入新的课程信息
"""
# 先删除所有课程
del_res = userDeleteAllCrouse(sid)
if del_res[-1] != 200:
return '用户所有课程数据库删除失败', 516
# 添加所有课程
ins_res = userInsertAllCrouse(schedule)
if ins_res[-1] != 200:
return '用户所有课程数据库插入失败', 517
# 更新数据库时间
now = time.time()
updateLastUpdate(sid, now)
return 'OK', 200
def getAllUserCrouse(group_id_list, sid):
"""
查找用户所有的课程包括组课程以及个人课程
"""
find_res = findUserCrouse(sid)
if find_res[-1] != 200:
return '用户课程数据库查询失败', 518
user_crouse = find_res[0]
group_crouse = {}
for group_id in group_id_list:
find_res = findGroupCrouse(group_id)
if find_res[-1] != 200:
return '用户组课程数据库查询失败', 519
group_crouse[group_id] = find_res[0]
return {
'group_crouse': group_crouse,
'user_crouse': user_crouse
}, 200
def manageSign(cid, pwd, phone):
"""
用户注册
"""
# 课表信息以及成绩信息
crawler_res = manageCrawler(
user_info['cid'], user_info['pwd'], user_info.get('phone'))
if crawler_res[-1] != 200:
return crawler_res
schedule = crawler_res[0]['schedule']
grade = crawler_res[0]['grade']
user_info = crawler_res[0]['user_info']
user_info['pwd'] = signCode(pwd)
user_info['invite_list'] = []
user_info['setting'] = {
'bg': ''
}
user_info['avatar'] = 'default'
user_info['last_update'] = time.time()
# 插入用户
ins_res = insertUser(user_info)
user_info.pop('_id')
user_info.pop('pwd')
user_info['group_list'] = []
# 更新新的课程信息
ins_res = manageNewCrouse(user_info['sid'], schedule)
if ins_res[-1] != 200:
return ins_res
# 获取用户所有的课程信息
find_res = getAllUserCrouse(group_id_list, user_info['sid'])
if find_res[-1] != 200:
return find_res
schedule = find_res[0]
return {
'user_info': user_info,
'schedule': schedule,
'grade': grade,
}, 200
def login(data, user_info):
"""
处理登录操作
"""
# 校验密码
if user_info['pwd'] != signCode(data['pwd']):
return '账号或密码错误', 511
# 接下来需要返回组信息,课表信息以及成绩信息
# 组信息
group_id_list = user_info['group_list']
find_res = manageFindGroup(group_id_list)
if find_res[-1] != 200:
return find_res
user_info['group_list'] = find_res[0]
# 课表信息以及成绩信息
crawler_res = manageCrawler(
user_info['cid'], user_info['pwd'], user_info.get('phone'))
if crawler_res[-1] != 200:
return crawler_res
schedule = crawler_res[0]['schedule']
grade = crawler_res[0]['grade']
# 更新新的课程信息
ins_res = manageNewCrouse(user_info['sid'], schedule)
if ins_res[-1] != 200:
return ins_res
# 获取用户所有的课程信息
find_res = getAllUserCrouse(group_id_list, user_info['sid'])
if find_res[-1] != 200:
return find_res
schedule = find_res[0]
user_info.pop('pwd')
return {
'user_info': user_info,
'schedule': schedule,
'grade': grade,
}, 200

0
lib/public/__init__.py Normal file
View File

View File

@ -1,4 +1,5 @@
import json
from json.encoder import JSONEncoder
import requests
from urllib.parse import quote
import base64
@ -6,89 +7,118 @@ from bs4 import BeautifulSoup
import random
import sys
from werkzeug.utils import redirect
from utils import btoa, signCode
from ocr import getCaptcha
class Crawler(object):
def __init__(self, username, password, phone):
self.__username = username
self.__password = password
self.__phone = phone
self.__session = None
self.__student_id = None
self.__student_name = None
self.__grade_data = ''
self.__schedule_data = ''
def __init__(self):
self.__session = requests.Session()
self.__response = None
self.__pwd = None
self.__phone = None
self.cid = None
self.sid = None
self.uid = None
self.real_name = None
self.baseUrl = None
# 获取用户基本信息
def getUserInfo(self):
return {
'cid': self.cid,
'pwd': signCode(self.__pwd),
'sid': self.sid,
'uid': self.uid,
'real_name': self.real_name,
}
# 链接教务 -----------------------------------------------------------------------------
def connection(self):
try:
self.__session = requests.Session()
# 获取统一身份系统的网页
r = self.__session.get(
url='https://mysso.cust.edu.cn/cas/login?service=https://jwgls1.cust.edu.cn/welcome')
url='https://mysso.cust.edu.cn/cas/login?service=https://jwgl.cust.edu.cn/welcome')
soup = BeautifulSoup(r.text, 'html.parser')
execution = soup.find_all(name='input')[6]['value']
execution = soup.find_all(name='input')[3]['value']
r = self.__session.get('https://mysso.cust.edu.cn/cas/captcha')
formdata = {
'username': self.__username,
'password': self.__password,
'username': self.cid,
'password': self.__pwd,
'execution': execution,
'captcha': getCaptcha(r.content),
'_eventId': 'submit',
'geolocation': ''
}
r = self.__session.post(
url='https://mysso.cust.edu.cn/cas/login?service=https://jwgls1.cust.edu.cn/welcome', data=formdata)
url='https://mysso.cust.edu.cn/cas/login?service=https://jwgl.cust.edu.cn/welcome', data = formdata)
soup = BeautifulSoup(r.text, 'html.parser')
flag = soup.find(name='title')
if(flag.text == "手机号设置"):
if self.__phone == '':
return ('请填写手机号', 511)
execution = soup.find_all(name='input')[1]['value']
formdata = {
'phone': self.__phone,
'execution': execution,
'_eventId': 'submit',
'submit': '提交'
}
r = self.__session.post(
url="https://mysso.cust.edu.cn/cas/login?service=https://jwgls1.cust.edu.cn/welcome", data=formdata)
r = self.__session.get(
url='https://portal.cust.edu.cn/custp/index')
soup = BeautifulSoup(r.text, 'html.parser')
try:
if soup.findAll(name='a')[4]['href'] != 'logout':
raise('账号或密码错误')
except:
return ('账号或者密码错误', 510)
r = self.__session.get(url='https://mysso.cust.edu.cn/cas/login?service=https://jwgls1.cust.edu.cn/welcome', allow_redirects=False)
ticket = r.headers['Location'][42:]
asp_net_sessionid_param = {'Ticket': ticket, 'Url': 'https://jwgls1.cust.edu.cn/welcome'}
asp_net_sessionid_param = base64.b64encode(
quote(json.dumps(asp_net_sessionid_param)).encode('utf-8')).decode('utf-8')
asp_net_sessionid_param = {'param': asp_net_sessionid_param}
headers = {'Content-Type': 'application/json'}
r = self.__session.post(url='https://jwgls1.cust.edu.cn/api/LoginApi/LGSSOLocalLogin?sf_request_type=ajax', data=json.dumps(asp_net_sessionid_param), headers=headers)
data = json.loads(r.content.decode('utf-8'))
# 提示未建立教务信息
if data['state'] == 1:
return (data['message'], 513)
self.__student_name = data['data']['StudentDto']['XM']
self.__student_id = data['data']['StudentDto']['XH']
return ('ok', 200)
loginSuccess = (soup.find(name='title').text != "统一身份认证系统")
self.__response = r
if not loginSuccess:
return '账号或者密码错误', 511
return '登录成功', 200
except Exception as e:
print(e)
return ('教务挂了', 512)
return '教务挂了', 515
def submitVerificationCode(self, data):
try:
r = self.__response
soup = BeautifulSoup(r.text, 'html.parser')
execution = soup.find_all(name='input')[2]['value']
formdata = {
'yzm': data['vc'],
'msgType': '',
'execution': execution,
'_eventId': 'submit'
}
r = self.__session.post(
url = "https://mysso.cust.edu.cn/cas/login?service=https://jwgl.cust.edu.cn/welcome", data = formdata, allow_redirects = True)
self.__response = r
return 'gotcha!', 200
except Exception as e:
print(e)
return '教务挂了', 515
# 获取成绩 -----------------------------------------------------------------------------
def getGrade(self):
r = self.__response
urlBase = r.url.split('.')[0]
ticket = r.url.split('?')[-1]
param = base64.b64encode(quote(json.dumps({
"Ticket":ticket.split('=')[-1],
"Url":"https://jwgl.cust.edu.cn/welcome"
})).encode('utf-8'))
self.baseUrl = urlBase
r = self.__session.post(
url = urlBase + '.cust.edu.cn/api/LoginApi/LGSSOLocalLogin',
data = {
"param": param,
"__log": {},
"__permission": {}
})
headers = {'Content-Type': 'application/json'}
r = self.__session.post(
url='https://jwgls1.cust.edu.cn/api/ClientStudent/QueryService/GradeQueryApi/GetDataByStudent?sf_request_type=ajax',
data=json.dumps({"param": "JTdCJTIyU2hvd0dyYWRlVHlwZSUyMiUzQTElN0Q=", "__permission": {"MenuID": "4443798E-EB6E-4D88-BFBD-BB0A76FF6BD5",
"Operation": 0}, "__log": {"MenuID": "4443798E-EB6E-4D88-BFBD-BB0A76FF6BD5", "Logtype": 6, "Context": "查询"}}),
url=urlBase + '.cust.edu.cn/api/ClientStudent/QueryService/GradeQueryApi/GetDataByStudent',
data=json.dumps({
"param": "JTdCJTIyU2hvd0dyYWRlVHlwZSUyMiUzQTAlN0Q=",
"__permission": {
"MenuID": "4443798E-EB6E-4D88-BFBD-BB0A76FF6BD5",
"Operate": "select",
"Operation": 0
},
"__log": {
"MenuID": "4443798E-EB6E-4D88-BFBD-BB0A76FF6BD5",
"Logtype": 6,
"Context": "查询"
}
}),
headers=headers
)
data = json.loads(r.content.decode('utf-8'))
if data['state'] != 0:
return ('教务挂了', 512)
return '教务挂了', 515
# 分解数据并重命名
total = data['data']['GradeStatistics']
split = data['data']['GradeList']
@ -222,113 +252,152 @@ class Crawler(object):
})
total_grade['total_bixiu_GPA'] = total_bixiu_c_x_g / \
total_bixiu_credit
# 合并数据
self.__grade_data = {
'total': total_grade,
'split': grade_list
}
return ('ok', 200)
return {
'total_grade': total_grade,
'grade_list': grade_list
}, 200
# 获取课表 -----------------------------------------------------------------------------
def getSchedule(self):
# 获取当前周数
def getCurWeek(self):
headers = {'Content-Type': 'application/json'}
r = self.__session.post(
url='https://jwgls1.cust.edu.cn/api/ClientStudent/Home/StudentHomeApi/GetHomeCurWeekTime?sf_request_type=ajax',
data=json.dumps({"param": "JTdCJTdE", "__permission": {"MenuID": "F71C97D5-D3E2-4FDA-9209-D7FA8626390E",
"Operation": 0}, "__log": {"MenuID": "F71C97D5-D3E2-4FDA-9209-D7FA8626390E", "Logtype": 6, "Context": "查询"}}),
"Operation": 0}, "__log": {"MenuID": "F71C97D5-D3E2-4FDA-9209-D7FA8626390E", "Logtype": 6, "Context": "查询"}}),
headers=headers
)
CurWeek = json.loads(r.content.decode('utf-8'))['data']['CurWeek']
r = self.__session.post(
url='https://jwgls1.cust.edu.cn/api/ClientStudent/Home/StudentHomeApi/QueryStudentScheduleData?sf_request_type=ajax',
data=json.dumps({"param": "JTdCJTdE", "__permission": {"MenuID": "F71C97D5-D3E2-4FDA-9209-D7FA8626390E",
"Operation": 0}, "__log": {"MenuID": "F71C97D5-D3E2-4FDA-9209-D7FA8626390E", "Logtype": 6, "Context": "查询"}}),
headers=headers
)
data = json.loads(r.content.decode('utf-8'))
if data['state'] != 0:
return ('教务挂了', 512)
return json.loads(
r.content.decode('utf-8'))['data']['CurWeek'], 200
# 处理课表信息
def manageSchedule(self, data):
time = ['AM__TimePieces', 'PM__TimePieces', 'EV__TimePieces']
data = data['data']['AdjustDays']
days_per_week = [0] * 23
lesson = [[0] * 6 for _ in range(7)]
lesson_set = {}
color_set = [0] * 9
color_used = 9
lessons = []
for i in range(7):
for j in range(3):
for k in range(2):
if(data[i][time[j]][k]['Dtos']):
lesson[i][j*2+k] = []
for l in data[i][time[j]][k]['Dtos']:
temp_lesson = {}
Time = [0] * 23
temp_lesson = {
'sid': self.sid,
'real_name': self.real_name,
'is_personal': False,
'day': i,
'period': j*2+k,
'is_groups_course': False,
}
weeks_split = [0] * 23
mod = ''
for m in l['Content']:
if temp_lesson.get(m['Key']):
temp_lesson[m['Key']] += ','+m['Name']
key = m['Key']
if m['Key'] == 'Teacher':
key = 'teacher'
elif m['Key'] == 'Lesson':
key = 'course'
elif m['Key'] == 'Room':
key = 'room'
elif m['Key'] == 'Time':
key = 'weeks'
if temp_lesson.get(key):
temp_lesson[key] += ','+m['Name']
else:
temp_lesson[m['Key']] = m['Name']
if lesson_set.get(l['Content'][0]['Name']):
temp_lesson['color'] = lesson_set[l['Content'][0]['Name']]
else:
color = random.randint(0, 8)
while color_set[color]:
if color_used <= 0:
break
color = random.randint(0, 8)
temp_lesson['color'] = color
lesson_set[l['Content'][0]['Name']] = color
color_used -= 1
color_set[color] = 1
temp_Time = temp_lesson['Time']
temp_lesson['Time'] = temp_Time[0:int(
temp_Time.find('') + 1)]
if '单周' in temp_Time:
temp_lesson[key] = m['Name']
temp_weeks = temp_lesson['weeks']
temp_lesson['weeks'] = temp_weeks[0:int(
temp_weeks.find('') + 1)]
if '单周' in temp_weeks:
mod = 'single'
# temp_Time = temp_Time[0:len(temp_Time)-5]
elif '双周' in temp_Time:
elif '双周' in temp_weeks:
mod = 'double'
# temp_Time = temp_Time[0:len(temp_Time)-5]
else:
mod = 'all'
# temp_Time = temp_Time[0:-1]
zhou_pos = temp_Time.find('')
temp_Time = temp_Time[0:zhou_pos]
temp_Time = temp_Time.split(',')
zhou_pos = temp_weeks.find('')
temp_weeks = temp_weeks[0:zhou_pos]
temp_weeks = temp_weeks.split(',')
index = 0
for n in temp_Time:
temp_Time[index] = n.split('-')
for n in temp_weeks:
temp_weeks[index] = n.split('-')
index += 1
index = 0
for n in temp_Time:
for n in temp_weeks:
if len(n) > 1:
for o in range(int(n[0]), int(n[1]) + 1):
if (o % 2 == 0 and mod == 'double') or (o % 2 == 1 and mod == 'single') or (mod == 'all'):
days_per_week[o] = max(
days_per_week[o], i+1)
Time[o] = 1
weeks_split[o] = 1
else:
Time[o] = 0
weeks_split[o] = 0
else:
days_per_week[int(n[0])] = max(
days_per_week[int(n[0])], i+1)
Time[int(n[0])] = 1
weeks_split[int(n[0])] = 1
index += 1
temp_lesson['Time_split'] = Time
lesson[i][j*2+k].append(temp_lesson)
self.__schedule_data = {'lesson': lesson,
'days_per_week': days_per_week, 'cur_week': CurWeek}
return ('ok', 200)
# 获取信息 -----------------------------------------------------------------------------
def getData(self):
return (
{
'student_id': self.__student_id,
'student_name': self.__student_name,
'grade': self.__grade_data,
'schedule': self.__schedule_data
},
200
temp_lesson['weeks_split'] = weeks_split
lessons.append(temp_lesson)
return lessons, 200
# 获取个人课表
def getOwnSchedule(self):
headers = {'Content-Type': 'application/json'}
r = self.__session.post(
url=self.baseUrl + '.cust.edu.cn/api/ClientStudent/Home/StudentHomeApi/QueryStudentScheduleData',
data=json.dumps({
"param": "JTdCJTdE",
"__permission": {
"MenuID": "00000000-0000-0000-0000-000000000000",
"Operate": "select",
"Operation": "0"
},
"__log": {
"MenuID": "00000000-0000-0000-0000-000000000000",
"Logtype": 6,
"Context": "查询"
}
}),
headers=headers
)
data = json.loads(r.content.decode('utf-8'))
if data['state'] != 0:
return ('教务挂了', 515)
return self.manageSchedule(data)
# 获取他人课表
def getOtherschedule(self):
headers = {'Content-Type': 'application/json'}
params = {"KBLX":"2","CXLX":"0","XNXQ":"20202","CXID":self.uid,"CXZC":"0","JXBLX":""}
params = str(btoa(json.dumps(params)))[2:-1]
r = self.__session.post(
url=self.baseUrl + '.cust.edu.cn/api/ClientStudent/QueryService/OccupyQueryApi/QueryScheduleData?sf_request_type=ajax',
data=json.dumps({"param": params, "__permission": {"MenuID": "F71C97D5-D3E2-4FDA-9209-D7FA8626390E",
"Operation": 0}, "__log": {"MenuID": "F71C97D5-D3E2-4FDA-9209-D7FA8626390E", "Logtype": 6, "Context": "查询"}}),
headers=headers
)
data = json.loads(r.content.decode('utf-8'))
if data['state'] != 0:
return ('教务挂了', 515)
return self.manageSchedule(data)
# 获取cookie
def getCookie(self):
return self.__session.cookies.items(), 200
# 设置cookie
def setCookie(self, cookies):
requests.utils.add_dict_to_cookiejar(
self.__session.cookies, dict(cookies))
return 'OK', 200
# 默认初始化
def defaultInit(self, data):
self.cid = data['cid']
self.__pwd = data['pwd']
self.__phone = data['phone']
self.__session = requests.Session()
# 使用我的cookie初始化用于快速刷新课表
def cookieInit(self, cookies, uid, cid, sid, real_name):
self.cid = cid
self.sid = sid
self.uid = uid
self.real_name = real_name
self.__session = requests.Session()
self.setCookie(cookies)
return self.getOtherschedule()

View File

@ -0,0 +1,43 @@
import unittest
from crawler import Crawler
import time
# time_start=time.time()
c = Crawler()
userinfo = c.defaultInit('2019002380', '@yuning20010329', '15143211127')
print(userinfo[-1])
print(c.getOwnSchedule())
# time_end=time.time()
# print('time cost',time_end-time_start,'s')
# userinfo = userinfo[0]
# print(userinfo)
# print(c.getOwnSchedule())
# print(c.getGrade())
# cookies = c.getCookie()
# cookies = cookies[0]
# print(cookies)
# print(str(cookies))
# cookies = str(cookies)
# cookies = eval(cookies)
# time_start=time.time()
# print(c.cookieInit(cookies, userinfo['uid'], userinfo['cid'], userinfo['sid'], userinfo['real_name']))
# time_end=time.time()
# print('time cost',time_end-time_start,'s')
# c = Crawler('2017002372', '623910ert&', '15143211127')
# c = Crawler('2019002380', '@yuning20010329', '15143211127')
# c.connection()
# c.getOtherschedule("dd709e77-34f8-43f7-8efa-0838fd138430")
# class TestCrawler(unittest.TestCase):
# # 测试链接
# def test_connection(self):
# self.assertEqual(c.connection(), ('ok', 200))
# #测试获取成绩
# def test_grade(self):
# self.assertEqual(c.getGrade(), ('ok', 200))
# #测试获取课表
# def test_schedule(self):
# self.assertEqual(c.getSchedule(), ('ok', 200))
# if __name__ == '__main__':
# unittest.main()

408
lib/public/db.py Normal file
View File

@ -0,0 +1,408 @@
from pymongo import MongoClient
from bson import ObjectId, json_util
# 主环境 (生产环境为production开发环境为development)
ENV = 'production'
def col(arg):
"""
获取数据集
"""
# 链接数据库
conn = MongoClient('mongodb://cherry:fR1jW2xG3bE9@39.96.28.83:27017/cherry')
# 判断环境
if ENV == 'development':
arg += '_test'
return conn.cherry[arg]
def updateCookie(new_cookie):
"""
更新cookie
"""
# 字符串化
new_cookie = str(new_cookie)
try:
col('config').update({'key': 'cookie'}, {'$set': {'value': new_cookie}}, {'upsert': 'true'})
except Exception as e:
print(e)
return 'cookie数据库更新失败', 400
def findCookie():
"""
获取cookie
"""
try:
res = col('config').find_one({'key': 'cookie'}, {'_id': 0})
value = res['value']
cookie = eval(value)
return cookie, 200
except Exception as e:
print(e)
return 'cookie数据库查询失败', 300
def updateLastUpdate(sid, now):
"""
更新最后一次课程更新信息
"""
try:
col('user').update({'sid': sid}, {'$set': {'last_update': now}})
except Exception as e:
print(e)
return '数据更新时间数据库更新失败', 403
def insertUser(userinfo):
""""
插入新学生信息
"""
try:
col('user').insert_one(userinfo)
return 'OK', 200
except Exception as e:
print(e)
return '学生信息数据库插入失败', 100
def findUser(cid):
"""
获取学生信息
在group_list里边存放了[{'group_id'}]
取出之后需要遍历组的信息
"""
try:
userinfo = col('user').aggregate([
{
'$match': {
'cid': cid
}
},
{
'$lookup': {
'from': 'link',
'localField': 'sid',
'foreignField': 'sid',
'as': 'group_list'
}
},
{
'$project': {
'_id': 0,
'group_list._id': 0,
'group_list.sid': 0
}
}
])
return userinfo, 200
except Exception as e:
print(e)
return '学生信息数据库查询失败', 301
def insertInvite(cid, group_id):
"""
对用户添加用户组邀请
测试多次重复邀请用户接受或者删除的时候是否能删掉所有前端Set去重
"""
try:
col('user').update({'cid': cid}, {'$push': {'invite_list': group_id}})
return 'OK', 200
except Exception as e:
print(e)
return '用户组邀请数据库插入失败', 101
def deleteInvite(cid, group_id):
"""
用户或者管理员删除用户组邀请
"""
try:
col('user').update({'cid': cid}, {'$pull': {'invite': {'group_id': group_id}}})
return 'OK', 200
except Exception as e:
print(e)
return '用户组邀请数据库删除失败', 500
def bindUserGroup(sid, group_id):
"""
绑定用户以及用户组
"""
try:
col('link').insert_one({'sid': sid, 'group_id': group_id})
return 'OK', 200
except Exception as e:
print(e)
return '用户与用户组绑定数据库插入失败', 102
def unbindUserGroup(sid, group_id):
"""
解绑用户以及用户组
"""
try:
col('link').remove({'sid': sid, 'group_id': group_id})
return 'OK', 200
except Exception as e:
print(e)
return '用户与用户组解绑数据库删除失败', 501
def updateAvatar(cid, img_id):
"""
用户更新头像
"""
try:
col('user').update({'cid': cid}, {'$set': {'avatar': img_id}})
return 'OK', 200
except Exception as e:
print(e)
return '头像数据库更新失败', 401
def updateBg(cid, img_id):
"""
用户更新背景图片
"""
try:
col('user').update({'cid': cid}, {'$set': {'setting.bg': img_id}})
return 'OK', 200
except Exception as e:
print(e)
return '背景图数据库更新失败', 402
def insertGroup(group_info):
"""
用户创建用户组
"""
try:
col('group').insert_one(group_info)
return 'OK', 200
except Exception as e:
print(e)
return '用户组数据库插入失败', 103
def findGroup(group_id):
"""
查询用户组信息附带用户组中用户信息
"""
try:
groupinfo = col('group').aggregate([
{
'$match': {
'group_id': group_id
}
},
{
'$lookup': {
'from': 'link',
'localField': 'group_id',
'foreignField': 'group_id',
'as': 'user_list'
}
},
{
'$lookup': {
'from': 'user',
'localField': 'cid',
'foreignField': 'cid',
'as': 'user_list'
}
},
{
'$project': {
'_id': 0,
'user_list._id': 0,
'user_list.invite_list': 0,
'user_list.cid':0,
'user_list.pwd':0,
'user_list.setting':0,
'user_list.last_update': 0,
}
}
])
return groupinfo, 200
except Exception as e:
print(e)
return '用户组信息数据库查询失败', 302
def deleteGroup(group_id):
"""
解散用户组
"""
try:
col('group').remove({'group_id': group_id})
return 'OK', 200
except Exception as e:
print(e)
return '解散用户组数据库删除失败', 504
def addLog(group_id, log):
"""
向用户组中添加操作记录
"""
try:
col('group').update({'group_id': group_id}, {'$push': {'log_list': log}})
return 'OK', 200
except Exception as e:
print(e)
return '操作记录数据库插入失败', 104
def groupInsertAdmin(cid, group_id):
"""
向管理组中添加管理
"""
try:
col('group').update({'group_id': group_id}, {'$push': {'admin_list': cid}})
return 'OK', 200
except Exception as e:
print(e)
return '用户组侧加入管理数据库插入失败', 106
def groupDeleteAdmin(cid, group_id):
"""
从管理组中删除管理
"""
try:
col('group').update({'group_id': group_id}, {'$pull': {'admin_list': cid}})
return 'OK', 200
except Exception as e:
print(e)
return '用户组侧移除管理数据库删除失败', 503
def userInsertCrouse(crouse):
"""
用户添加自定义课程
"""
try:
col('user_crouse').insert_one(crouse)
return 'OK', 200
except Exception as e:
print(e)
return '用户自定义课程数据库插入失败', 107
def userDeleteCrouse(crouse_id):
"""
用户删除自定义课程
加入是否自定义的判断防止用户改前端导致删除非定义课程
"""
try:
col('user_crouse').remove({'crouse_id': crouse_id, 'is_personal': False})
return 'OK', 200
except Exception as e:
print(e)
return '用户自定义课程数据库删除失败', 505
def userDeleteAllCrouse(sid):
"""
用户删除所有课程刷新课表用
不删除自定义课程
"""
try:
col('user_crouse').remove({'sid': sid, 'is_personal': False})
return 'OK', 200
except Exception as e:
print(e)
return '用户所有课程数据库删除失败', 502
def userInsertAllCrouse(crouses):
"""
用户批量添加课表刷新课表用
"""
try:
col('user_crouse').insert_many(crouses)
return 'OK', 200
except Exception as e:
print(e)
return '用户所有课程数据库插入失败', 108
def insertRank(cid, nick, count, time):
"""
向排名表里增加或者覆写数据
"""
try:
col('rank').update({"cid": cid}, {'$setOnInsert': {"nick": nick}, '$set': {
"count": count, "time": time}} , {'upsert': 'true'})
# col('rank').insert_one({"count":count,"time":time,"nick":nick})
return 'OK', 200
except Exception as e:
# 失败
return '排名表数据库插入失败', 109
def findRank():
"""
获取所有排名信息
"""
time_rank = []
count_rank = []
try:
for i in col('rank').find({}, {"_id": 0}).sort([("time", 1), ("count", 1)]).limit(10):
time_rank.append(i)
for i in col('rank').find({}, {"_id": 0}).sort([("count", 1), ("time", 1)]).limit(10):
count_rank.append(i)
return {'time_rank': time_rank, 'count_rank': count_rank}, 200
except Exception as e:
print(e)
return '排名表数据库查询失败', 303
def findUserCrouse(sid):
"""
获取所有用户课程
"""
crouse_list = []
try:
for i in col('user_crouse').find({'sid': sid}, {'_id': 0}):
crouse_list.append(i)
return crouse_list, 200
except Exception as e:
print(e)
return '用户课程数据库查询失败', 304
def groupInsertCrouse(crouse):
"""
用户组添加课程
"""
try:
col('group_crouse').insert_one(crouse)
return 'OK', 200
except Exception as e:
print(e)
return '用户组课程数据库插入失败', 110
def groupDeleteCrouse(crouse_id):
"""
用户组删除课程
"""
try:
col('group_crouse').remove({'crouse_id': crouse_id})
return 'OK', 200
except Exception as e:
print(e)
return '用户组课程数据库删除失败', 506
def findGroupCrouse(group_id):
"""
获取所有指定用户组课程
"""
crouse_list = []
try:
for i in col('group_crouse').find({'group_id': group_id}):
crouse_list.append(i)
return crouse_list, 200
except Exception as e:
print(e)
return '用户组课程数据库查询失败', 305
def insertLog(log):
"""
插入操作记录
"""
try:
col('log').insert_one(log)
return 'OK', 200
except Exception as e:
print(e)
return '系统操作记录数据库插入失败', 111
def findLog(has_read_page):
"""
查询操作记录默认50条分割
"""
skip = 50 * (has_read_page - 1)
log_list = []
try:
for i in col('log').find({}).limit(50).skip(skip):
log_list.append(i)
return log_list, 200
except Exception as e:
print(e)
return '系统操作记录数据库查询失败', 306

12
lib/public/ocr.py Normal file
View File

@ -0,0 +1,12 @@
from aip import AipOcr
APP_ID = '24797034'
API_KEY = 'ykebbHq8GaK2cD1sIPy7PEPu'
SECRET_KEY = '4rU7QPC1oGFbZgIbsDvMGcqPky4kv7kV'
import base64
from urllib.parse import quote
import json
client = AipOcr(APP_ID, API_KEY, SECRET_KEY)
def getCaptcha(image):
return client.numbers(image)['words_result'][0]['words']

39
lib/public/utils.py Normal file
View File

@ -0,0 +1,39 @@
from urllib.request import quote, unquote
import base64
import json
from hashlib import md5
def btoa(content):
"""
JSON转base64
"""
return base64.b64encode(quote(content).encode())
def atob(content):
"""
base64转JSON
"""
return unquote(base64.b64decode(content).decode())
def signCode(code):
"""
给str签名用于加密密码
"""
d = str(code)
d = d.replace(' ', '')
md = md5()
md.update(d.encode('utf-8'))
r = md.hexdigest().upper()
return r
def checkData(data):
"""
MD5校验数据
"""
d = data.copy()
try:
d.pop('sign')
except KeyError:
pass
return data['sign'] == signCode(d)

View File

@ -1,203 +0,0 @@
[2020-04-10 10:47:35 +0000] [9] [INFO] Starting gunicorn 20.0.4
[2020-04-10 10:47:35 +0000] [9] [INFO] Listening at: http://0.0.0.0:80 (9)
[2020-04-10 10:47:35 +0000] [9] [INFO] Using worker: sync
[2020-04-10 10:47:35 +0000] [11] [INFO] Booting worker with pid: 11
[2020-04-10 10:47:35 +0000] [12] [INFO] Booting worker with pid: 12
[2020-04-10 10:47:35 +0000] [13] [INFO] Booting worker with pid: 13
[2020-04-10 10:47:35 +0000] [14] [INFO] Booting worker with pid: 14
[2020-04-10 11:58:56 +0000] [10] [INFO] Starting gunicorn 20.0.4
[2020-04-10 11:58:56 +0000] [10] [INFO] Listening at: http://0.0.0.0:80 (10)
[2020-04-10 11:58:56 +0000] [10] [INFO] Using worker: sync
[2020-04-10 11:58:56 +0000] [12] [INFO] Booting worker with pid: 12
[2020-04-10 11:58:56 +0000] [13] [INFO] Booting worker with pid: 13
[2020-04-10 11:58:56 +0000] [14] [INFO] Booting worker with pid: 14
[2020-04-10 11:58:56 +0000] [15] [INFO] Booting worker with pid: 15
[2020-04-10 11:58:56 +0000] [12] [ERROR] Exception in worker process
Traceback (most recent call last):
File "/usr/local/lib/python3.7/site-packages/gunicorn/arbiter.py", line 583, in spawn_worker
worker.init_process()
File "/usr/local/lib/python3.7/site-packages/gunicorn/workers/base.py", line 119, in init_process
self.load_wsgi()
File "/usr/local/lib/python3.7/site-packages/gunicorn/workers/base.py", line 144, in load_wsgi
self.wsgi = self.app.wsgi()
File "/usr/local/lib/python3.7/site-packages/gunicorn/app/base.py", line 67, in wsgi
self.callable = self.load()
File "/usr/local/lib/python3.7/site-packages/gunicorn/app/wsgiapp.py", line 49, in load
return self.load_wsgiapp()
File "/usr/local/lib/python3.7/site-packages/gunicorn/app/wsgiapp.py", line 39, in load_wsgiapp
return util.import_app(self.app_uri)
File "/usr/local/lib/python3.7/site-packages/gunicorn/util.py", line 358, in import_app
mod = importlib.import_module(module)
File "/usr/local/lib/python3.7/importlib/__init__.py", line 127, in import_module
return _bootstrap._gcd_import(name[level:], package, level)
File "<frozen importlib._bootstrap>", line 1006, in _gcd_import
File "<frozen importlib._bootstrap>", line 983, in _find_and_load
File "<frozen importlib._bootstrap>", line 967, in _find_and_load_unlocked
File "<frozen importlib._bootstrap>", line 677, in _load_unlocked
File "<frozen importlib._bootstrap_external>", line 728, in exec_module
File "<frozen importlib._bootstrap>", line 219, in _call_with_frames_removed
File "/app/cherry.py", line 5, in <module>
from allFunction import manageLogin, managePhoto, manageAdd, manageDel, manageGet, manageDetail, manageComment, manageScheduleUpload, manageScheduleGet
ImportError: cannot import name 'managePhoto' from 'allFunction' (./lib/allFunction.py)
[2020-04-10 11:58:56 +0000] [12] [INFO] Worker exiting (pid: 12)
[2020-04-10 11:58:56 +0000] [13] [ERROR] Exception in worker process
Traceback (most recent call last):
File "/usr/local/lib/python3.7/site-packages/gunicorn/arbiter.py", line 583, in spawn_worker
worker.init_process()
File "/usr/local/lib/python3.7/site-packages/gunicorn/workers/base.py", line 119, in init_process
self.load_wsgi()
File "/usr/local/lib/python3.7/site-packages/gunicorn/workers/base.py", line 144, in load_wsgi
self.wsgi = self.app.wsgi()
File "/usr/local/lib/python3.7/site-packages/gunicorn/app/base.py", line 67, in wsgi
self.callable = self.load()
File "/usr/local/lib/python3.7/site-packages/gunicorn/app/wsgiapp.py", line 49, in load
return self.load_wsgiapp()
File "/usr/local/lib/python3.7/site-packages/gunicorn/app/wsgiapp.py", line 39, in load_wsgiapp
return util.import_app(self.app_uri)
File "/usr/local/lib/python3.7/site-packages/gunicorn/util.py", line 358, in import_app
mod = importlib.import_module(module)
File "/usr/local/lib/python3.7/importlib/__init__.py", line 127, in import_module
return _bootstrap._gcd_import(name[level:], package, level)
File "<frozen importlib._bootstrap>", line 1006, in _gcd_import
File "<frozen importlib._bootstrap>", line 983, in _find_and_load
File "<frozen importlib._bootstrap>", line 967, in _find_and_load_unlocked
File "<frozen importlib._bootstrap>", line 677, in _load_unlocked
File "<frozen importlib._bootstrap_external>", line 728, in exec_module
File "<frozen importlib._bootstrap>", line 219, in _call_with_frames_removed
File "/app/cherry.py", line 5, in <module>
from allFunction import manageLogin, managePhoto, manageAdd, manageDel, manageGet, manageDetail, manageComment, manageScheduleUpload, manageScheduleGet
ImportError: cannot import name 'managePhoto' from 'allFunction' (./lib/allFunction.py)
[2020-04-10 11:58:56 +0000] [13] [INFO] Worker exiting (pid: 13)
[2020-04-10 11:58:56 +0000] [14] [ERROR] Exception in worker process
Traceback (most recent call last):
File "/usr/local/lib/python3.7/site-packages/gunicorn/arbiter.py", line 583, in spawn_worker
worker.init_process()
File "/usr/local/lib/python3.7/site-packages/gunicorn/workers/base.py", line 119, in init_process
self.load_wsgi()
File "/usr/local/lib/python3.7/site-packages/gunicorn/workers/base.py", line 144, in load_wsgi
self.wsgi = self.app.wsgi()
File "/usr/local/lib/python3.7/site-packages/gunicorn/app/base.py", line 67, in wsgi
self.callable = self.load()
File "/usr/local/lib/python3.7/site-packages/gunicorn/app/wsgiapp.py", line 49, in load
return self.load_wsgiapp()
File "/usr/local/lib/python3.7/site-packages/gunicorn/app/wsgiapp.py", line 39, in load_wsgiapp
return util.import_app(self.app_uri)
File "/usr/local/lib/python3.7/site-packages/gunicorn/util.py", line 358, in import_app
mod = importlib.import_module(module)
File "/usr/local/lib/python3.7/importlib/__init__.py", line 127, in import_module
return _bootstrap._gcd_import(name[level:], package, level)
File "<frozen importlib._bootstrap>", line 1006, in _gcd_import
File "<frozen importlib._bootstrap>", line 983, in _find_and_load
File "<frozen importlib._bootstrap>", line 967, in _find_and_load_unlocked
File "<frozen importlib._bootstrap>", line 677, in _load_unlocked
File "<frozen importlib._bootstrap_external>", line 728, in exec_module
File "<frozen importlib._bootstrap>", line 219, in _call_with_frames_removed
File "/app/cherry.py", line 5, in <module>
from allFunction import manageLogin, managePhoto, manageAdd, manageDel, manageGet, manageDetail, manageComment, manageScheduleUpload, manageScheduleGet
ImportError: cannot import name 'managePhoto' from 'allFunction' (./lib/allFunction.py)
[2020-04-10 11:58:56 +0000] [14] [INFO] Worker exiting (pid: 14)
[2020-04-10 11:58:56 +0000] [15] [ERROR] Exception in worker process
Traceback (most recent call last):
File "/usr/local/lib/python3.7/site-packages/gunicorn/arbiter.py", line 583, in spawn_worker
worker.init_process()
File "/usr/local/lib/python3.7/site-packages/gunicorn/workers/base.py", line 119, in init_process
self.load_wsgi()
File "/usr/local/lib/python3.7/site-packages/gunicorn/workers/base.py", line 144, in load_wsgi
self.wsgi = self.app.wsgi()
File "/usr/local/lib/python3.7/site-packages/gunicorn/app/base.py", line 67, in wsgi
self.callable = self.load()
File "/usr/local/lib/python3.7/site-packages/gunicorn/app/wsgiapp.py", line 49, in load
return self.load_wsgiapp()
File "/usr/local/lib/python3.7/site-packages/gunicorn/app/wsgiapp.py", line 39, in load_wsgiapp
return util.import_app(self.app_uri)
File "/usr/local/lib/python3.7/site-packages/gunicorn/util.py", line 358, in import_app
mod = importlib.import_module(module)
File "/usr/local/lib/python3.7/importlib/__init__.py", line 127, in import_module
return _bootstrap._gcd_import(name[level:], package, level)
File "<frozen importlib._bootstrap>", line 1006, in _gcd_import
File "<frozen importlib._bootstrap>", line 983, in _find_and_load
File "<frozen importlib._bootstrap>", line 967, in _find_and_load_unlocked
File "<frozen importlib._bootstrap>", line 677, in _load_unlocked
File "<frozen importlib._bootstrap_external>", line 728, in exec_module
File "<frozen importlib._bootstrap>", line 219, in _call_with_frames_removed
File "/app/cherry.py", line 5, in <module>
from allFunction import manageLogin, managePhoto, manageAdd, manageDel, manageGet, manageDetail, manageComment, manageScheduleUpload, manageScheduleGet
ImportError: cannot import name 'managePhoto' from 'allFunction' (./lib/allFunction.py)
[2020-04-10 11:58:56 +0000] [15] [INFO] Worker exiting (pid: 15)
[2020-04-10 12:01:52 +0000] [9] [INFO] Starting gunicorn 20.0.4
[2020-04-10 12:01:52 +0000] [9] [INFO] Listening at: http://0.0.0.0:80 (9)
[2020-04-10 12:01:52 +0000] [9] [INFO] Using worker: sync
[2020-04-10 12:01:52 +0000] [11] [INFO] Booting worker with pid: 11
[2020-04-10 12:01:52 +0000] [12] [INFO] Booting worker with pid: 12
[2020-04-10 12:01:52 +0000] [13] [INFO] Booting worker with pid: 13
[2020-04-10 12:01:53 +0000] [14] [INFO] Booting worker with pid: 14
[2020-04-15 08:34:13,823] ERROR in app: Exception on /api/login [POST]
Traceback (most recent call last):
File "/usr/local/lib/python3.7/site-packages/flask/app.py", line 2446, in wsgi_app
response = self.full_dispatch_request()
File "/usr/local/lib/python3.7/site-packages/flask/app.py", line 1951, in full_dispatch_request
rv = self.handle_user_exception(e)
File "/usr/local/lib/python3.7/site-packages/flask/app.py", line 1820, in handle_user_exception
reraise(exc_type, exc_value, tb)
File "/usr/local/lib/python3.7/site-packages/flask/_compat.py", line 39, in reraise
raise value
File "/usr/local/lib/python3.7/site-packages/flask/app.py", line 1949, in full_dispatch_request
rv = self.dispatch_request()
File "/usr/local/lib/python3.7/site-packages/flask/app.py", line 1935, in dispatch_request
return self.view_functions[rule.endpoint](**req.view_args)
File "/app/cherry.py", line 24, in login
res = manageLogin(request)
File "./lib/allFunction.py", line 23, in manageLogin
schedule = getSchedule(res['ip'], res['s'])
File "./lib/crawler.py", line 265, in getSchedule
for o in range( int(n[0]) , int(n[1]) + 1):
ValueError: invalid literal for int() with base 10: '16周[3'
[2020-04-15 08:39:41 +0000] [9] [INFO] Starting gunicorn 20.0.4
[2020-04-15 08:39:41 +0000] [9] [INFO] Listening at: http://0.0.0.0:80 (9)
[2020-04-15 08:39:41 +0000] [9] [INFO] Using worker: sync
[2020-04-15 08:39:41 +0000] [11] [INFO] Booting worker with pid: 11
[2020-04-15 08:39:41 +0000] [12] [INFO] Booting worker with pid: 12
[2020-04-15 08:39:41 +0000] [13] [INFO] Booting worker with pid: 13
[2020-04-15 08:39:41 +0000] [14] [INFO] Booting worker with pid: 14
[2020-04-15 08:42:08,965] ERROR in app: Exception on /api/login [POST]
Traceback (most recent call last):
File "/usr/local/lib/python3.7/site-packages/flask/app.py", line 2446, in wsgi_app
response = self.full_dispatch_request()
File "/usr/local/lib/python3.7/site-packages/flask/app.py", line 1951, in full_dispatch_request
rv = self.handle_user_exception(e)
File "/usr/local/lib/python3.7/site-packages/flask/app.py", line 1820, in handle_user_exception
reraise(exc_type, exc_value, tb)
File "/usr/local/lib/python3.7/site-packages/flask/_compat.py", line 39, in reraise
raise value
File "/usr/local/lib/python3.7/site-packages/flask/app.py", line 1949, in full_dispatch_request
rv = self.dispatch_request()
File "/usr/local/lib/python3.7/site-packages/flask/app.py", line 1935, in dispatch_request
return self.view_functions[rule.endpoint](**req.view_args)
File "/app/cherry.py", line 24, in login
res = manageLogin(request)
File "./lib/allFunction.py", line 23, in manageLogin
schedule = getSchedule(res['ip'], res['s'])
File "./lib/crawler.py", line 265, in getSchedule
for o in range( int(n[0]) , int(n[1]) + 1):
ValueError: invalid literal for int() with base 10: '4周[3'
[2020-04-15 08:42:29,909] ERROR in app: Exception on /api/login [POST]
Traceback (most recent call last):
File "/usr/local/lib/python3.7/site-packages/flask/app.py", line 2446, in wsgi_app
response = self.full_dispatch_request()
File "/usr/local/lib/python3.7/site-packages/flask/app.py", line 1951, in full_dispatch_request
rv = self.handle_user_exception(e)
File "/usr/local/lib/python3.7/site-packages/flask/app.py", line 1820, in handle_user_exception
reraise(exc_type, exc_value, tb)
File "/usr/local/lib/python3.7/site-packages/flask/_compat.py", line 39, in reraise
raise value
File "/usr/local/lib/python3.7/site-packages/flask/app.py", line 1949, in full_dispatch_request
rv = self.dispatch_request()
File "/usr/local/lib/python3.7/site-packages/flask/app.py", line 1935, in dispatch_request
return self.view_functions[rule.endpoint](**req.view_args)
File "/app/cherry.py", line 24, in login
res = manageLogin(request)
File "./lib/allFunction.py", line 23, in manageLogin
schedule = getSchedule(res['ip'], res['s'])
File "./lib/crawler.py", line 265, in getSchedule
for o in range( int(n[0]) , int(n[1]) + 1):
ValueError: invalid literal for int() with base 10: '4周[3'

View File

@ -1,3 +1,4 @@
baidu-aip==2.2.18.0
beautifulsoup4==4.8.2
bs4==0.0.1
certifi==2019.11.28
@ -15,4 +16,4 @@ requests==2.22.0
six==1.14.0
soupsieve==1.9.5
urllib3==1.25.7
Werkzeug==0.16.0
Werkzeug==0.16.0

42
test/db.py Normal file
View File

@ -0,0 +1,42 @@
from pymongo import MongoClient
from bson import ObjectId, json_util
# 主环境 (生产环境为production开发环境为development)
ENV = 'development'
def col(arg):
"""
获取数据集
"""
# 链接数据库
conn = MongoClient('mongodb://192.168.2:27017/cherry')
# 判断环境
if ENV == 'development':
arg += '_test'
return conn.cherry[arg]
col('user_y').insert({'cid': 1, 'sid': 2})
col('group_y').insert([
{
'group_id': 3,
'avatar': 4
},
{
'group_id': 5,
'avatar': 6
}
])
col('link_y').insert([
{
'sid': 2,
'group_id': 3
},
{
'sid': 2,
'group_id': 5
}
])
print(col('user_y').find({}))
print(col('link_y').find({}))
print(col('group_y').find({}))

97
test/kong.py Normal file
View File

@ -0,0 +1,97 @@
from urllib.request import quote, unquote
import base64
import json
# 西区第一教学楼
WESTID = {
# 西区第一教学楼
'teaching_building_1': 'f74dac26-c58a-4eae-bc46-ff2055b2de19',
# 西区第二教学楼
'teaching_building_2': '201a429b-df6d-489e-9a1d-de7c85f0081e',
# 西区图书馆
'library': '3adb80f2-7e27-4058-a60c-fbb32cb36587'
}
EASTID = {
# 东区第一教学楼
'teaching_building_1': 'd91cc53c-a9ad-4be3-becf-7f3ed62e8762',
# 东区第二教学楼
'teaching_building_2': 'e14b90bd-0c92-422e-b299-7009118104b9',
# 东区第三教学楼
'teaching_building_3': '3534f8ce-f10b-4058-a818-95a116d9bca4',
# 东区前楼
'front_building': '6accca4d-b092-4bdc-b2e0-0c1941782eec'
}
SOUTHID = {
# 南区研究生教学楼
'graduate_building': '20a207f7-65ef-4ae4-9286-2a2b5a73e1c9',
# 南区实训楼
'practical_training_building': 'cb5265e8-84a1-41ed-985b-3920449738aa'
}
IDLIST = {
'wtb1': WESTID['teaching_building_1'],
'wtb2': WESTID['teaching_building_2'],
'wl': WESTID['library'],
'etb1': EASTID['teaching_building_1'],
'etb2': EASTID['teaching_building_2'],
'etb3': EASTID['teaching_building_3'],
'efb': EASTID['front_building'],
'sgb': SOUTHID['graduate_building'],
'sptb': SOUTHID['practical_training_building'],
}
COURSELIST = ['0102', '0304', '0506', '0708', '0910', '1112']
# JSON转base64
def btoa(content):
return base64.b64encode(quote(content).encode())
# base64转JSON
def atob(content):
return unquote(base64.b64decode(content).decode())
def getParam(SJ, JSs, Addr):
try:
checkData(SJ, JSs, Addr)
except Exception as e:
print(e)
raise Exception(str(e))
param = {
"EmptyRoomParam": {
"SJ": SJ,
"JCs": JSs
},
"PagingParam": {
"IsPaging": 1,
"Offset": 0,
"Limit": 500,
"Conditions": {
"PropertyParams": [
{
"Field": "BDJXLXXID",
"Value": IDLIST[Addr]
}
]
}
}
}
return str(btoa(json.dumps(param)))[2:-1]
# 校验数据
def checkData(sj, jss, addr):
# 校验sj
sj_split = sj.split('-')
if len(sj_split) != 3:
raise Exception('SJ错误')
for content in sj_split:
if len(content) == 0:
raise Exception('SJ错误')
# 校验jss
if not isinstance(jss,list):
raise Exception('JSs错误')
for content in jss:
if content not in COURSELIST:
raise Exception('JSs错误')
# 校验addr
if addr not in IDLIST:
raise Exception('Addr错误')

86
test/lesson.py Normal file
View File

@ -0,0 +1,86 @@
from urllib.request import quote, unquote
import base64
import json
def btoa(content):
"""
JSON转base64
"""
return base64.b64encode(quote(content).encode())
def atob(content):
"""
base64转JSON
"""
return unquote(base64.b64decode(content).decode())
print(atob("JTdCJTIyS0JMWCUyMiUzQSUyMjIlMjIlMkMlMjJDWExYJTIyJTNBJTIyMCUyMiUyQyUyMlhOWFElMjIlM0ElMjIyMDIwMiUyMiUyQyUyMkNYSUQlMjIlM0ElMjJkZDcwOWU3Ny0zNGY4LTQzZjctOGVmYS0wODM4ZmQxMzg0MzAlMjIlMkMlMjJDWFpDJTIyJTNBJTIyMCUyMiUyQyUyMkpYQkxYJTIyJTNBJTIyJTIyJTdE"))
"""
param = {
"pagingParam": {
"IsPaging": false,
"Offset": 0,
"Limit": 50,
"Orders": {
"PropertyParams": [
{
"Field": "ClassInfo.ProfessionInfoYear.NF",
"IsDesc": true
},
{
"Field": "ClassInfo.ProfessionInfoYear.Department.DWBH",
"IsDesc": false
},
{
"Field": "ClassInfo.ProfessionInfoYear.XNZYBH",
"IsDesc": false
},
{
"Field": "ClassInfo.BJBH",
"IsDesc": false
},
{
"Field": "XH",
"IsDesc": false
}
]
},
"Searchs": {
"PropertyParams": [
]
},
"Conditions": {
"PropertyParams": [
{
"Field": "ClassInfo.ProfessionInfoYear.NF",
"Value": "2019",
"Operation": 3,
"Logic": 0
},
{
"Field": "ClassInfo.ProfessionInfoYear.Department.BDDWXXID",
"Value": "3c438133-0ede-443f-8fb7-1a7a58c7e104",
"Operation": 3,
"Logic": 0
},
{
"Field": "ClassInfo.ProfessionInfoYear.BDZYXXNDID",
"Value": "d70fd0d5-7521-4a31-a068-70f484daa8ef",
"Operation": 3,
"Logic": 0
},
{
"Field": "ClassInfo.BDBJXXID",
"Value": "33eb5cf7-1688-44e6-bd09-fc08a2b3e8a5",
"Operation": 3,
"Logic": 0
}
]
}
}
}
"""

4
test/some.py Normal file
View File

@ -0,0 +1,4 @@
a = [1,2,3]
b = a
a = 1
print(b)