Skip to content

Commit

Permalink
发布alpha1.0版本
Browse files Browse the repository at this point in the history
  • Loading branch information
Arsennnic committed Nov 26, 2019
1 parent 4760c50 commit 791cc47
Show file tree
Hide file tree
Showing 5 changed files with 91 additions and 48 deletions.
56 changes: 37 additions & 19 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,16 +1,22 @@
# USTC-EPC-BOT
# EPC-BOT for USTC
中国科学技术大学EPC系统自动抢课脚本.

## 功能列表

待更新.


## 使用说明

待更新.


## 实现原理

待更新.


## 算法逻辑
## 选课逻辑

### 未达预约上限(4学时)的情况

Expand All @@ -29,43 +35,32 @@

- 比较可预约课程与已预约课程的时间先后. 若存在可预约课程的日期早于已预约课程中的最晚日期, 则考虑进行一次课程替换. 若存在1学时课程与2学时课程的日期相同, 优先选择2学时课程.

<center>

| I | II | III | IV | V |
| - | - | - | - | - |
| 1 | 1 | 1 | 2 | 2 |
| 1 | 1 | 2 | 1 | 2 |
| 1 | 2 | 1 | 1 | |
| 1 | | | | |

</center>

- 将已预约课程按照日期先后排列, 共有以上五种学时组合, 分别讨论之:
+ 组合I:
- 将已预约课程按照日期先后排列, 共有五种学时组合, 分别讨论之:
+ 组合I (1, 1, 1, 1):
- 可预约课程为2学时:
+ 可预约课程日期早于已预约的倒数第2节课, 将倒数第1-2节已预约课程替换为可预约课程.
+ 可预约课程日期晚于已预约的倒数第2节课, 不替换.
- 可预约课程为1学时:
+ 将倒数第1节已预约课程替换为可预约课程.
+ 组合II:
+ 组合II (1, 1, 2):
- 可预约课程为2学时:
+ 将倒数第1节已预约课程替换为可预约课程.
- 可预约课程为1学时:
+ 可预约课程日期早于已预约的倒数第2节课, 将倒数第1-2节已预约课程替换为可预约课程.
+ 可预约课程日期晚于已预约的倒数第2节课, 不替换.
+ 组合III:
+ 组合III (1, 2, 1):
- 可预约课程为2学时:
+ 可预约课程日期早于已预约的倒数第2节课, 将倒数第2节已预约课程替换为可预约课程.
+ 可预约课程日期晚于已预约的倒数第2节课, 不替换.
- 可预约课程为1学时:
+ 将倒数第1节已预约课程替换为可预约课程.
+ 组合IV:
+ 组合IV (2, 1, 1):
- 可预约课程为2学时:
+ 可预约课程日期早于已预约的倒数第2节课, 将倒数第1-2节已预约课程替换为可预约课程.
+ 可预约课程日期晚于已预约的倒数第2节课, 不替换.
- 可预约课程为1学时:
+ 将倒数第1节已预约课程替换为可预约课程.
+ 组合V:
+ 组合V (2, 2):
- 可预约课程为2学时:
+ 将倒数第1节已预约课程替换为可预约课程.
- 可预约课程为1学时:
Expand All @@ -74,3 +69,26 @@

- 循环上述操作, 直至可预约的课程列表为空.


## Q & A

### 使用 PyInstaller 重新打包后, Selenium 运行时有黑色空职台弹出, 如何避免?
打开 Python\Lib\site-packages\selenium\webdriver\common\service.py 文件, 将
```
self.process = subprocess.Popen(cmd, env=self.env, close_fds=platform.system() != 'Windows', stdout=self.log_file, stderr=self.log_file, stdin=PIPE)
```
改写成
```
self.process = subprocess.Popen(cmd, stdin=PIPE, stdout=PIPE ,stderr=PIPE, shell=False, creationflags=0x08000000)
```
或直接用工程目录中的 Selenium\service.py 文件替换原生的 service.py 文件
并重新使用 PyInstaller 打包.


## 参考文献

[1] 木华生. 中科大EPC课程爬取\[OL\]. https://blog.csdn.net/qq_28491207/article/details/84261732, 2018.

[2] David Cortesi, William Caban. PyInstaller Manual\[OL\]. https://pyinstaller.readthedocs.io/.

[3] AhmedWas. Getting Rid of ChromeDirver Console Window with PyInstaller\[OL\]. https://stackoverflow.com/questions/52643556/getting-rid-of-chromedirver-console-window-with-pyinstaller, 2018.
10 changes: 6 additions & 4 deletions epc_bot.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import requests, time, re, json, threading
import os, inspect, requests, time, re, json, threading
from bs4 import BeautifulSoup
from selenium import webdriver
from selenium.webdriver.chrome.options import Options
Expand Down Expand Up @@ -49,9 +49,11 @@ def __init__(self, sid, passwd, filtr, email_addr, email_pwd, gui=None):
options.add_argument("--no-sandbox")
options.add_argument("--headless")
options.add_argument("--disable-gpu")
self.WEBDRIVER = webdriver.Chrome(chrome_options=options,
executable_path="./selenium/chromedriver.exe"
)
work_dir = os.path.realpath(os.path.abspath(
os.path.split(inspect.getfile(inspect.currentframe()))[0]
))
driver_dir = os.path.join(work_dir, "selenium", "chromedriver.exe")
self.WEBDRIVER = webdriver.Chrome(chrome_options=options, executable_path=driver_dir)

def stop(self):
self._STOP_FLAG.set()
Expand Down
63 changes: 41 additions & 22 deletions graphic.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
from tkinter import *
from tkinter.scrolledtext import *
from tkinter.messagebox import showinfo
import json, threading
import os, inspect, json, threading, traceback
from epc_bot import *

class GUI:
Expand All @@ -11,6 +11,10 @@ def __init__(self, master):
self.master.title("EPC-BOT for USTC")
self.master.protocol('WM_DELETE_WINDOW', self.on_gui_destroy)

self.work_dir = os.path.realpath(os.path.abspath(
os.path.split(inspect.getfile(inspect.currentframe()))[0]
))

self.frame_basic = Frame(master)
self.frame_basic.grid(row=0, pady=10)
Label(self.frame_basic, text="--- Basic Settings ---").grid(row=0, columnspan=4)
Expand All @@ -34,22 +38,27 @@ def __init__(self, master):
self.frame_filter = Frame(master)
self.frame_filter.grid(row=1, pady=10)
Label(self.frame_filter, text="--- Filter Settings ---").grid(row=0, columnspan=5)
with open("config.template.json", "r", encoding="utf-8") as template_file:
self.filters_all = json.load(template_file)["filter"]
self.filters_var = list()
self.cbtn_filters = list()
row, col = 1, 0
for i in range(len(self.filters_all)):
if (i%5 == 0):
row = row + 1
col = -1
col = col + 1
filter_str = self.filters_all[i]["wday"][0:3] + ".\t" + self.filters_all[i]["time"]
self.filters_var.append(IntVar())
cbtn_filter = Checkbutton(self.frame_filter, text=filter_str, variable=self.filters_var[i])
cbtn_filter.grid(row=row, column=col, padx=5, pady=2)
cbtn_filter.deselect()
self.cbtn_filters.append(cbtn_filter)
template_dir = os.path.join(self.work_dir, "config.template.json")
if os.path.exists(template_dir):
with open(template_dir, "r", encoding="utf-8") as template_file:
self.filters_all = json.load(template_file)["filter"]
self.filters_var = list()
self.cbtn_filters = list()
row, col = 1, 0
for i in range(len(self.filters_all)):
if (i%5 == 0):
row = row + 1
col = -1
col = col + 1
filter_str = self.filters_all[i]["wday"][0:3] + ".\t" + self.filters_all[i]["time"]
self.filters_var.append(IntVar())
cbtn_filter = Checkbutton(self.frame_filter, text=filter_str, variable=self.filters_var[i])
cbtn_filter.grid(row=row, column=col, padx=5, pady=2)
cbtn_filter.deselect()
self.cbtn_filters.append(cbtn_filter)
else:
showerror(title="Error", message="Configuration template is missing!")
self.master.destroy()

self.btn_start = Button(master, text="Start", command=self.start_bot)
self.btn_start.grid(row=2, ipadx=self.btn_start.winfo_width()*5, pady=10)
Expand All @@ -59,7 +68,8 @@ def __init__(self, master):
## 读取工作目录下存储的配置文件
def read_config(self):
try:
with open("config.json", "r", encoding="utf-8") as config:
config_dir = os.path.join(self.work_dir, "config.json")
with open(config_dir, "r", encoding="utf-8") as config:
config = json.load(config)
self.sid = config["sid"]
self.passwd = config["passwd"]
Expand Down Expand Up @@ -105,17 +115,24 @@ def start_bot(self):
self.write_config()
else:
showinfo(title="Alert", message="Please fill the blanks in basic settings module!")
return

self.master.resizable(False, False)
self.frame_basic.grid_forget()
self.frame_filter.grid_forget()
self.btn_start.grid_forget()
self.console = ScrolledText(self.master)
self.console.pack()
self.update_log("Working Directory:\n%s\n" % self.work_dir)

self.bot = EPCBot(self.sid, self.passwd, self.filters_json, \
self.email_addr, self.email_pwd, self)
self.bot.start()
try:
self.bot = EPCBot(self.sid, self.passwd, self.filters_json, \
self.email_addr, self.email_pwd, self)
self.bot.start()
self.update_log("EPC-Bot is running...\n")
except Exception as e:
self.update_log("Chrome driver is not installed!")
self.update_log(traceback.format_exc())

## 更新EPC-BOT日志到GUI
def update_log(self, text):
Expand All @@ -127,6 +144,8 @@ def update_log(self, text):
## 关闭GUI时杀死子线程
def on_gui_destroy(self):
self.master.destroy()
if self.bot:
try:
self.bot.stop()
except:
pass

6 changes: 6 additions & 0 deletions py2exe.bat
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
pyinstaller --onedir --windowed ^
--add-data="config.template.json;." ^
--add-data="selenium\chromedriver.exe;selenium" ^
--name="epc_bot" ^
main_gui.py
rd /s /q __pycache__
4 changes: 1 addition & 3 deletions selenium/service.py
Original file line number Diff line number Diff line change
Expand Up @@ -69,9 +69,7 @@ def start(self):
try:
cmd = [self.path]
cmd.extend(self.command_line_args())
self.process = subprocess.Popen(cmd, env=self.env,
close_fds=platform.system() != 'Windows',
stdout=self.log_file, stderr=self.log_file, creationflags=134217728)
self.process = subprocess.Popen(cmd, stdin=PIPE, stdout=PIPE ,stderr=PIPE, shell=False, creationflags=0x08000000)
except TypeError:
raise
except OSError as err:
Expand Down

0 comments on commit 791cc47

Please sign in to comment.