├── config.example.py ├── gunicorn.conf.py ├── requirements.txt ├── static └── favicon.ico ├── .github └── FUNDING.yml ├── Dockerfile ├── README.md ├── LICENSE ├── .gitignore ├── app.py └── templates └── index.html /config.example.py: -------------------------------------------------------------------------------- 1 | secret_key = b'_xNzbG4Uz5ukZ#b/' 2 | -------------------------------------------------------------------------------- /gunicorn.conf.py: -------------------------------------------------------------------------------- 1 | workers = 5 2 | worker_class = "gevent" 3 | bind = "0.0.0.0:7577" 4 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | flask 2 | BeautifulSoup4 3 | gunicorn 4 | requests 5 | gevent 6 | lxml 7 | -------------------------------------------------------------------------------- /static/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/idealclover/Fxxk-NJU-Class-Evaluator/master/static/favicon.ico -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: [idealclover] 4 | patreon: idealclover 5 | custom: https://idealclover.cn/pay.png 6 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM python:3.7 2 | ARG KEY=default_key 3 | 4 | WORKDIR /usr/src/app 5 | 6 | COPY requirements.txt ./ 7 | RUN pip install --no-cache-dir -r requirements.txt 8 | RUN touch config.py \ 9 | && echo "secret_key = '$KEY'" >> config.py 10 | 11 | COPY . . 12 | 13 | CMD ["gunicorn", "app:app", "-c", "./gunicorn.conf.py"] -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # 南哪大学课程评估自动化脚本 2 | 3 | > 📢教务处NB! 4 | 5 | 本脚本用来自动进行南哪大学的学期末课程评估 6 | 7 | 默认选项为全部“优秀”,老师表现“较好”,可在源码中进行更改 8 | 9 | 本工具仅用来针对**需要默认好评的课程** 10 | 11 | 请**确保**您其他需要评价的课程已评价完毕 12 | 13 | **上课不易,请不要轻易放弃您的权利** 14 | 15 | ## 使用方法 16 | 17 | 没啥使用方法,自己去 [这里](https://fuck.idealclover.cn/) 瞅 18 | 19 | 我们以南大同学的身份保证不会存储你的学号与密码 源码在这自己看 20 | 21 | ## 部署相关 22 | 23 | ### Docker 24 | 25 | 项目已经上传至 DockerHub 可直接通过 Docker 部署 26 | 27 | ``` 28 | docker create --name=fuck --restart=unless-stopped \ 29 | -e KEY="项目密钥" \ 30 | -p 对外端口:7577 \ 31 | idealclover/fxxk 32 | ``` 33 | 34 | 之后访问 ```ip:对外端口``` 进入网站 35 | 36 | ### Python 37 | 38 | 项目所用版本为 Python 3 39 | 40 | ``` 41 | git clone https://github.com/idealclover/Fxxk-NJU-Class-Evaluator.git 42 | cd Fxxk-NJU-Class-Evaluator 43 | pip3 install --no-cache-dir -r requirements.txt 44 | touch config.py && echo "secret_key = '项目密钥'" >> config.py 45 | gunicorn app:app -c ./gunicorn.conf.py 46 | ``` 47 | 48 | 49 | ## 反馈&贡献 50 | 51 | 欢迎在[issues](https://github.com/idealclover/Fxxk-NJU-Class-Evaluator/issues)区提交bug与反馈 52 | 53 | 欢迎各种形式的pr >v< 54 | 55 | 顺便求一波 stars 蟹蟹ww 56 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 不蠢会死的某翠 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Created by .ignore support plugin (hsz.mobi) 2 | ### macOS template 3 | # General 4 | .DS_Store 5 | .AppleDouble 6 | .LSOverride 7 | 8 | # Icon must end with two \r 9 | Icon 10 | 11 | # Thumbnails 12 | ._* 13 | 14 | # Files that might appear in the root of a volume 15 | .DocumentRevisions-V100 16 | .fseventsd 17 | .Spotlight-V100 18 | .TemporaryItems 19 | .Trashes 20 | .VolumeIcon.icns 21 | .com.apple.timemachine.donotpresent 22 | 23 | # Directories potentially created on remote AFP share 24 | .AppleDB 25 | .AppleDesktop 26 | Network Trash Folder 27 | Temporary Items 28 | .apdisk 29 | 30 | ### Windows template 31 | # Windows thumbnail cache files 32 | Thumbs.db 33 | Thumbs.db:encryptable 34 | ehthumbs.db 35 | ehthumbs_vista.db 36 | 37 | # Dump file 38 | *.stackdump 39 | 40 | # Folder config file 41 | [Dd]esktop.ini 42 | 43 | # Recycle Bin used on file shares 44 | $RECYCLE.BIN/ 45 | 46 | # Windows Installer files 47 | *.cab 48 | *.msi 49 | *.msix 50 | *.msm 51 | *.msp 52 | 53 | # Windows shortcuts 54 | *.lnk 55 | 56 | ### Example user template template 57 | ### Example user template 58 | 59 | # IntelliJ project files 60 | .idea 61 | *.iml 62 | out 63 | gen 64 | ### Python template 65 | # Byte-compiled / optimized / DLL files 66 | __pycache__/ 67 | *.py[cod] 68 | *$py.class 69 | 70 | # C extensions 71 | *.so 72 | 73 | # Distribution / packaging 74 | .Python 75 | build/ 76 | develop-eggs/ 77 | dist/ 78 | downloads/ 79 | eggs/ 80 | .eggs/ 81 | lib/ 82 | lib64/ 83 | parts/ 84 | sdist/ 85 | var/ 86 | wheels/ 87 | pip-wheel-metadata/ 88 | share/python-wheels/ 89 | *.egg-info/ 90 | .installed.cfg 91 | *.egg 92 | MANIFEST 93 | 94 | # PyInstaller 95 | # Usually these files are written by a python script from a template 96 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 97 | *.manifest 98 | *.spec 99 | 100 | # Installer logs 101 | pip-log.txt 102 | pip-delete-this-directory.txt 103 | 104 | # Unit test / coverage reports 105 | htmlcov/ 106 | .tox/ 107 | .nox/ 108 | .coverage 109 | .coverage.* 110 | .cache 111 | nosetests.xml 112 | coverage.xml 113 | *.cover 114 | .hypothesis/ 115 | .pytest_cache/ 116 | 117 | # Translations 118 | *.mo 119 | *.pot 120 | 121 | # Django stuff: 122 | *.log 123 | local_settings.py 124 | db.sqlite3 125 | 126 | # Flask stuff: 127 | instance/ 128 | .webassets-cache 129 | 130 | # Scrapy stuff: 131 | .scrapy 132 | 133 | # Sphinx documentation 134 | docs/_build/ 135 | 136 | # PyBuilder 137 | target/ 138 | 139 | # Jupyter Notebook 140 | .ipynb_checkpoints 141 | 142 | # IPython 143 | profile_default/ 144 | ipython_config.py 145 | 146 | # pyenv 147 | .python-version 148 | 149 | # pipenv 150 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. 151 | # However, in case of collaboration, if having platform-specific dependencies or dependencies 152 | # having no cross-platform support, pipenv may install dependencies that don't work, or not 153 | # install all needed dependencies. 154 | #Pipfile.lock 155 | 156 | # celery beat schedule file 157 | celerybeat-schedule 158 | 159 | # SageMath parsed files 160 | *.sage.py 161 | 162 | # Environments 163 | .env 164 | .venv 165 | env/ 166 | venv/ 167 | ENV/ 168 | env.bak/ 169 | venv.bak/ 170 | 171 | # Spyder project settings 172 | .spyderproject 173 | .spyproject 174 | 175 | # Rope project settings 176 | .ropeproject 177 | 178 | # mkdocs documentation 179 | /site 180 | 181 | # mypy 182 | .mypy_cache/ 183 | .dmypy.json 184 | dmypy.json 185 | 186 | # Pyre type checker 187 | .pyre/ 188 | 189 | config.py 190 | 191 | -------------------------------------------------------------------------------- /app.py: -------------------------------------------------------------------------------- 1 | from flask import Flask, render_template, request, session, redirect, url_for, jsonify 2 | from http.cookies import SimpleCookie 3 | from bs4 import BeautifulSoup 4 | import requests 5 | import base64 6 | 7 | from config import secret_key 8 | 9 | app = Flask(__name__) 10 | app.secret_key = secret_key 11 | 12 | 13 | @app.route('/login', methods=['GET', 'POST']) 14 | def login(): 15 | if request.method == 'POST': 16 | # session['username'] = request.form['username'] 17 | # session['password'] = request.form['password'] 18 | # session['validateCode'] = request.form['validateCode'] 19 | cookie_string = session['cookie'] 20 | cookie = SimpleCookie() 21 | cookie.load(cookie_string) 22 | cookies = {} 23 | for key, morsel in cookie.items(): 24 | cookies[key] = morsel.value 25 | username = request.form['username'] 26 | password = request.form['password'] 27 | validcode = request.form['validateCode'] 28 | r = requests.post('http://elite.nju.edu.cn/jiaowu/login.do', 29 | data={'userName': username, 'password': password, 'ValidateCode': validcode}, 30 | cookies=cookies) 31 | # print(r.content.decode('utf-8')) 32 | tmp = r.content.decode('utf-8') 33 | if tmp.find("验证码错误!") != -1: 34 | return jsonify({"status": "validate error"}) 35 | elif tmp.find("用户名错误!") != -1: 36 | return jsonify({"status": "username error"}) 37 | elif tmp.find("密码错误!") != -1: 38 | return jsonify({"status": "password error"}) 39 | else: 40 | return jsonify({"status": "success"}) 41 | return redirect(url_for('index')) 42 | 43 | 44 | @app.route('/fuck', methods=['GET', 'POST']) 45 | def fuck(): 46 | if request.method == 'POST': 47 | # session['username'] = request.form['username'] 48 | # session['password'] = request.form['password'] 49 | # session['validateCode'] = request.form['validateCode'] 50 | cookie_string = session['cookie'] 51 | cookie = SimpleCookie() 52 | cookie.load(cookie_string) 53 | cookies = {} 54 | for key, morsel in cookie.items(): 55 | cookies[key] = morsel.value 56 | r = requests.get('http://elite.nju.edu.cn/jiaowu/student/evalcourse/courseEval.do?method=currentEvalCourse', 57 | cookies=cookies) 58 | soup = BeautifulSoup(r.content, 'lxml') 59 | trs = soup.find_all('tr') 60 | trs.remove(trs[0]) 61 | 62 | # 获取没评的课并打分 63 | for tr in trs: 64 | if not tr.contents[9].has_attr('id'): 65 | continue 66 | # 跳过已评的课程 67 | if tr.contents[9].string == "已评": 68 | continue 69 | code = tr.contents[9].attrs['id'][2:] 70 | r = requests.post('http://elite.nju.edu.cn/jiaowu/student/evalcourse/courseEval.do', 71 | data={'method': 'currentEvalItem', 'id': code}, cookies=cookies) 72 | tmpSoup = BeautifulSoup(r.content, 'lxml') 73 | 74 | # 获取课程一共需要评价几道题 75 | question = tmpSoup.find('input', type='hidden') 76 | questionNum = int(question.attrs['value']) 77 | 78 | # 组装数据 79 | data = {'question': str(questionNum), 'mulItem1': '1', 'mulItem': ' 1', 'sub': '提 交'} 80 | for i in range(questionNum): 81 | data['question' + str(i + 1)] = '5' 82 | 83 | # 发送评价post 84 | r = requests.post('http://elite.nju.edu.cn/jiaowu/student/evalcourse/courseEval.do?method=submitEval', 85 | data=data, cookies=cookies) 86 | return jsonify({"status": "success"}) 87 | return redirect(url_for('index')) 88 | 89 | 90 | @app.route('/') 91 | def index(): 92 | # return 'Hello, World!' 93 | r = requests.get('http://elite.nju.edu.cn/jiaowu/ValidateCode.jsp') 94 | cookie_string = "; ".join([str(x) + "=" + str(y) for x, y in r.cookies.items()]) 95 | base64_data = base64.b64encode(r.content).decode("utf-8") 96 | base64_data = "data:image/png;base64," + base64_data 97 | # print(cookie_string) 98 | # print(base64_data) 99 | session['cookie'] = cookie_string 100 | return render_template('index.html', validatepic=base64_data) 101 | 102 | 103 | if __name__ == '__main__': 104 | app.run() 105 | -------------------------------------------------------------------------------- /templates/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 开始使用 - 课程自动评估 7 | 8 | 9 | 10 | 11 | 12 | 13 | 20 | 21 | 22 | 23 | 27 | 28 | 29 | 49 | 50 |
51 | 52 |
53 | 54 |
55 | 56 | 58 | 59 | 我们以南大同学的身份保证不会存储你的学号与密码
60 | 欢迎在 GitHub 上查看源码 61 |
62 |
63 |
64 | 65 | 67 | 68 | 我们以南大同学的身份保证不会存储你的学号与密码
69 | 欢迎在 GitHub 上查看源码 70 |
71 |
72 |
73 | 74 |
75 |
76 | 77 |
78 |
79 | 80 |
81 |
82 |
83 | 84 |
85 |
86 | 87 | 111 | 112 | 131 | 132 | 153 | 154 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 249 | 250 | 251 | --------------------------------------------------------------------------------