diff --git a/.gitignore b/.gitignore index 23527b2..18b4b64 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ /.idea/ *.sqlite3 +/__pycache__/ \ No newline at end of file diff --git a/dao.py b/dao.py index efaf9ad..1cefcfb 100644 --- a/dao.py +++ b/dao.py @@ -1,14 +1,15 @@ import sqlite3 from datetime import datetime, timedelta +from typing import Generator from flask import request db_path = './data.sqlite3' -db = sqlite3.connect(db_path) -cursor = db.cursor() +ddl_db = sqlite3.connect(db_path) +ddl_cursor = ddl_db.cursor() try: - cursor.execute(''' + ddl_cursor.execute(''' create table if not exists user_menu ( user text, @@ -17,32 +18,40 @@ create table if not exists user_menu primary key (user, datestr) ) ''') - cursor.execute(''' + ddl_cursor.execute(''' create table if not exists roll_result ( datestr text primary key, value text ) ''') - cursor.execute(''' + ddl_cursor.execute(''' create table if not exists menu ( name text primary key, label text ) ''') - db.commit() + ddl_db.commit() finally: - cursor.close() - db.close() + ddl_cursor.close() + ddl_db.close() -def get_user(): +def get_user() -> str: + """ + 根据访问IP决定用户ID + :return: 用户ID + """ client_ip = request.remote_addr return client_ip -def get_user_menu(): +def get_user_menu() -> str: + """ + 获取当前用户的投票内容 + :return: 投票内容 + """ user = get_user() db = sqlite3.connect(db_path) cursor = db.cursor() @@ -63,7 +72,12 @@ def get_user_menu(): db.close() -def set_user_menu(menu, user=None): +def set_user_menu(menu: str, user: str = None) -> None: + """ + 设置用户投票内容 + :param menu: 投票内容 + :param user: 用户,默认当前用户 + """ datestr = datetime.now().strftime('%Y-%m-%d') if user is None: user = get_user() @@ -78,7 +92,10 @@ def set_user_menu(menu, user=None): db.close() -def fetch_all_today_menu(): +def fetch_all_user_today_menu() -> Generator[tuple[str, str], None, None]: + """ + 获取所有用户今日的投票内容 + """ datestr = datetime.now().strftime('%Y-%m-%d') db = sqlite3.connect(db_path) cursor = db.cursor() @@ -94,7 +111,10 @@ def fetch_all_today_menu(): db.close() -def fetch_all_menu(): +def fetch_all_menu() -> Generator[tuple[str, str], None, None]: + """ + 获取所有可点的菜单 + """ db = sqlite3.connect(db_path) cursor = db.cursor() try: @@ -108,7 +128,12 @@ def fetch_all_menu(): db.close() -def fetch_roll_result(interval=0): +def fetch_roll_result(interval: int = 0) -> str | None: + """ + 获取N天前的抽签结果 + :param interval: 间隔,天 + :return: 抽签结果 + """ date = datetime.now() + timedelta(days=interval) datestr = date.strftime('%Y-%m-%d') db = sqlite3.connect(db_path) @@ -125,7 +150,11 @@ def fetch_roll_result(interval=0): db.close() -def set_roll_result(value): +def set_roll_result(value: str) -> None: + """ + 写入抽签结果 + :param value: 抽签结果 + """ datestr = datetime.now().strftime('%Y-%m-%d') db = sqlite3.connect(db_path) cursor = db.cursor() diff --git a/dinner.py b/dinner.py index 53d006e..23f547c 100644 --- a/dinner.py +++ b/dinner.py @@ -1,23 +1,35 @@ import json +from re import Pattern from session import app -from flask import send_from_directory, render_template, request, make_response, abort +from flask import render_template, make_response, abort import re import random from dao import * -MOBILE_AGENTS_PATTERN = re.compile( - r"(android|bb\d+|meego).+mobile|avantgo|bada\/|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|od)|iris|kindle|lge |maemo|midp|mmp|netfront|opera m(ob|in)i|palm( os)?|phone|p(ixi|re)\/|plucker|pocket|psp|series(4|6)0|symbian|treo|up\.(browser|link)|vodafone|wap|windows (ce|phone)|xda|xiino", +MOBILE_AGENTS_PATTERN: Pattern[str] = re.compile( + r"(android|bb\d+|meego).+mobile|avantgo|bada/|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|" + r"ip(hone|od)|iris|kindle|lge |maemo|midp|mmp|netfront|opera m(ob|in)i|palm( os)?|phone|p(ixi|re)/|plucker|" + r"pocket|psp|series([46])0|symbian|treo|up\.(browser|link)|vodafone|wap|windows (ce|phone)|xda|xiino", re.IGNORECASE, ) -def is_mobile_request(user_agent): +def is_mobile_request(user_agent: str) -> bool: + """ + 判断是否是移动端 + :param user_agent: UA + :return: 是否 + """ return bool(MOBILE_AGENTS_PATTERN.search(user_agent)) -def fetch_menu_summary(): - all_menu = list(fetch_all_today_menu()) +def fetch_user_menu_summary() -> dict[str, float]: + """ + 获取今天所有人的投票汇总 + :return: 品类->数量 + """ + all_menu = list(fetch_all_user_today_menu()) if len(all_menu) > 0: menus = list(map(lambda x: json.loads(x[1]), all_menu)) menu_keys = set() @@ -31,10 +43,14 @@ def fetch_menu_summary(): for k in x: result[k] += x[k] return result - return [] + return {} -def within_time(): +def within_time() -> bool: + """ + 判断是否在投票时间内 + :return: 是否 + """ start_time = datetime.strptime(str(datetime.now().date()) + ' 8:00', '%Y-%m-%d %H:%M') end_time = datetime.strptime(str(datetime.now().date()) + ' 17:30', '%Y-%m-%d %H:%M') if start_time < datetime.now() < end_time: @@ -42,15 +58,36 @@ def within_time(): return False -def check_roll(): +def check_roll() -> int: + """ + 判断是否能抽签 + :return: 0 不可以, 1 可以, -1 已抽过 + """ result = fetch_roll_result() if result is not None: return -1 return 0 if within_time() else 1 +def vote_reduce(summary: dict[str, float]) -> dict[str, float]: + """ + 降低昨天点过的餐的中奖率 + :param summary: 投票汇总结果 + :return: 投票汇总结果 + """ + last_result = fetch_roll_result(-1) + for k in summary: + if last_result == k: + summary[k] = summary[k] * 7 / 10 + return summary + + @app.route('/dinner/update') def dinner_update(): + """ + 更新投票 + :return: 响应内容 + """ user_agent_string = request.headers.get('User-Agent') if not is_mobile_request(user_agent_string): abort(403) @@ -75,17 +112,18 @@ def dinner_update(): @app.route('/dinner/roll') def dinner_roll(): + """ + 发起抽签 + :return: 响应 + """ if check_roll() != 1: return make_response(json.dumps(dict(code=-1, data="目前不能抽签"))) - summary = fetch_menu_summary() - last_result = fetch_roll_result(-1) + summary = vote_reduce(fetch_user_menu_summary()) for k in summary: - if last_result == k: - summary[k] = summary[k] * 7 / 10 summary[k] = int(round(summary[k] * 100)) pool = [] for k in summary: - for i in range(summary[k]): + for i in range(int(summary[k])): pool.append(k) random.shuffle(pool) result = pool[0] @@ -95,16 +133,17 @@ def dinner_roll(): @app.route('/dinner') def dinner(): + """ + 主页面 + :return: 响应 + """ menu = get_user_menu() if menu: menu = json.loads(menu) else: menu = {} - summary = fetch_menu_summary() + summary = vote_reduce(fetch_user_menu_summary()) last_result = fetch_roll_result(-1) - for x in summary: - if x == last_result: - summary[x] = summary[x] * 7 / 10 total_vote = sum(value for value in summary.values()) if total_vote == 0: total_vote = 1 diff --git a/start.py b/start.py index 47a8871..f26d227 100644 --- a/start.py +++ b/start.py @@ -1,20 +1,22 @@ -import subprocess, os -from flask import render_template, redirect -import threading +import os +from flask import redirect, send_from_directory +import threading from dinner import * from session import app lock = threading.Lock() + @app.route('/') def index(): - return redirect('dinner') - # return render_template('index.html') + return redirect('dinner') + # return render_template('index.html') + @app.route('/favicon.ico') def favicon(): - return send_from_directory(os.path.join(app.root_path, 'static'), 'fd-logo.png') + return send_from_directory(os.path.join(app.root_path, 'static'), 'fd-logo.png') if __name__ == '__main__': - app.run(host="0.0.0.0", port=80, debug=True, threaded=True) + app.run(host="0.0.0.0", port=80, debug=True, threaded=True)