本文约有1.8W字,快来数一数 读一读吧


前言

本项目由某不知名学校的高中生开发。
本项目为一个基于python,flask,pymysql的学生打卡系统,开发环境PyCharm 2021.3.2,运行环境Windows server 2022。

  1. 开发背景
    受疫情影响,我们学校进入了网课阶段,往常的打卡手段为微信群的接龙,但是由于消息过多,容易将上课等重要信息覆盖,所以出现了这个项目。
  2. 系统优缺点
    优点:直观看到学生打卡情况
    缺点:同学不喜欢 界面丑陋(很少的css,时间紧没写界面)
  3. 自认为nb之处
    记忆名字:通过记录每次打卡的IP进行查询。
    将画图步骤转化到打卡过程(详情请转到:四、1)

直接看图:

在这里插入图片描述在这里插入图片描述

一、建立基础数据库(数据库名:english punch)

all_name表:用于存储所有学生信息

SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;

-- ----------------------------
-- Table structure for all_name
-- ----------------------------
DROP TABLE IF EXISTS `all_name`;
CREATE TABLE `all_name`  (
  `name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL,
  `class` int(0) NULL DEFAULT NULL,
  PRIMARY KEY (`name`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci ROW_FORMAT = Dynamic;

SET FOREIGN_KEY_CHECKS = 1;

  • name:学生姓名
  • class:学生班级

如图:
在这里插入图片描述

update_table表:用于记录打卡信息

SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;

-- ----------------------------
-- Table structure for update_table
-- ----------------------------
DROP TABLE IF EXISTS `update_table`;
CREATE TABLE `update_table`  (
  `name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL,
  `class` int(0) NULL DEFAULT NULL,
  `date` datetime(0) NOT NULL,
  `ip` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL,
  `vid` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci ROW_FORMAT = Dynamic;

SET FOREIGN_KEY_CHECKS = 1;
  • name:打卡姓名
  • class:学生班级
  • date:打卡时间
  • IP:当前打卡的IP,目的:记忆用户名
  • vid:浏览器唯一标识,目的:为防止重复打卡 (停用了)

如图:
在这里插入图片描述

visit表:存储访问信息

SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;

-- ----------------------------
-- Table structure for visit
-- ----------------------------
DROP TABLE IF EXISTS `visit`;
CREATE TABLE `visit`  (
  `time` datetime(0) NULL DEFAULT NULL,
  `ip` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL,
  `thing` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci ROW_FORMAT = Dynamic;

SET FOREIGN_KEY_CHECKS = 1;
  • time:访问时间
  • IP:访问的IP
  • thing:本次事件

如图
在这里插入图片描述

二、 用处不大的表

或者说是无用表

pharse表:存储成语信息

(本来是英语打卡来着)

SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;

-- ----------------------------
-- Table structure for phrase
-- ----------------------------
DROP TABLE IF EXISTS `phrase`;
CREATE TABLE `phrase`  (
  `question` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL,
  `answer` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL,
  `source` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL,
  `study` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL,
  `abbr` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL,
  `quanpin` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci ROW_FORMAT = Dynamic;

SET FOREIGN_KEY_CHECKS = 1;
  • question:存储问题(成语意思)
  • answer:存储答案
  • source:存储出处
  • study:存储例句
  • abbr:存储首拼
  • quanpin:存储全拼

如图:
在这里插入图片描述

三、写前端

1. 主页面

主页面采用简洁风 (没空写css)
文件:main.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>英语听力接龙{{ date }}</title>
    <meta name="viewport"
        content="width=device-width, initial-scale=1.0, minimum-scale=0.5, maximum-scale=2.0, user-scalable=yes" />
    <!--引入jQuery包用于使用ajax-->
    <script type="text/javascript" src="http://apps.bdimg.com/libs/jquery/2.1.4/jquery.min.js"></script>
    <h4>今天是:{{ date }} 欢迎进行听力接龙</h4>
    <h4>请输入你的名字 是实名制的哦</h4>
    你的名字<input type="text" id="name" name="name" placeholder="姓名" value="{{ name }}">
    <h1></h1>
    <button onclick="add_fun();" id="send_mess">提交打卡</button>
    <button onclick="search_fun();">统计数据</button>
    <button onclick="help_fun()">关于</button>
    <button onclick="heart()">小彩蛋</button>
    <button onclick="p()">猜猜成语</button>
    <h3>彩蛋中的姓名是文本框内容 可以自行编辑</h3>
    <script>
        function heart(){
            $.ajax({
                url : "heart",
                type : "POST",
                data: { "name": $("#name").val()},
                success: function (result) {
                    document.write(result)
                }
            });
        }
        function help_fun() {
            $.ajax({
                url : "about",
                type : "POST",
                success: function (result) {
                    document.write(result)
                }
            });
        }

        function search_fun() {
            $.ajax({
                url:"s",
                type:"POST",
                success:function (res){
                    document.write(res);
                }
            })
        }
        visitorId = "";

        function get_vid(){
            const fpPromise = import('https://openfpcdn.io/fingerprintjs/v3').then(FingerprintJS => FingerprintJS.load())

            // Get the visitor identifier when you need it.
            fpPromise.then(fp => fp.get()).then(result => {
                // This is the visitor identifier:
                  visitorId = result.visitorId;
              });

        }
        window.οnlοad=get_vid();
        function add_fun() {
            if($("#name").val().length===0){
                alert("名字不能留空");
                return;
            }
            $("#send_mess").attr('disabled',true);
            $.ajax({
                url: "add",
                type: "POST",
                data: { "name": $("#name").val(), "vid":visitorId
                     },
                success: function (result) {
                    if (result.message == "OK") {
                        alert("提交成功");
                        document.getElementById("name").value = "";
                    }else if(result.message == "E"){
                        alert("其他错误 请联系周景鑫");
                    }else{
                        alert(result.message);
                    }
                    $("#send_mess").attr('disabled',false);
                }
            });
            
        }

        function p(){
            $.ajax({
                url: "p",
                type: "POST",
                success: function (res) {
                    document.write(res);
                }
            });
        }
    </script>
</head>
<body>

</body>
</html>

如图:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

2. 统计页面

文件:statistics.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=0.5, maximum-scale=2.0, user-scalable=yes" />
    <title>{{ date }}英语听力统计</title>
    <h1>{{ date }}英语听力统计</h1>
    {% if havedate==True %}
        {% for c in classes %}
            <h4>截至到{{ time }}为止</h4>
            <h4>{{ c.get('class') }}班未提交:共{{ c.get('no_s_p') }}人</h4>
            <h4>分别为:{{ c.get('no_s_p_list') }}</h4>
            <h4></h4><h4></h4>

            <h4>{{ c.get('class') }}班已提交:共{{ c.get('s_p') }}人</h4>
            <h4>分别为:{{ c.get('s_p_list') }}</h4>

            <h3>{{ c.get('class') }}班今日提交饼状图:</h3>
            <h3><img src="{{ c.get('pie') }}" alt="饼状图加载失败" width="320" height="240"></h3>
            <h3>{{ c.get('class') }}班最近几日提交对比:</h3>
            <h3><img src="{{ c.get('bar') }}" alt="柱状图加载失败" width="320" height="240"></h3>
        {% endfor %}
    {% else %}
        <h1>今天还没有人打卡哦 等等再来吧</h1>
    {% endif %}
    <input type="button" value="返回" onclick="location.reload();">
</head>
<body>

</body>
</html>

如图
在这里插入图片描述

3. 关于页面

文件:help.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>关于</title>
    <h4>别看了 网站是3.7周景鑫写的 长按加我vx</h4>
    <img src="static\vx.jpg" alt="二维码加载失败" width="180" height="264"/>
    <h4></h4>
    {% for info in infolist %}
        <h6>更新日志:{{ info['time'] }}</h6>
        <h6>{{ info['info'] }}</h6>
    {% endfor %}
    <h4>------------------------------</h4>
    <h4> </h4>
    <h6>由于技术与时间等原因 不足之处敬请谅解</h6>
    <h6>出现问题请联系周景鑫 vx:zjxyyds0307</h6>
    <h2>别看了快去学习吧</h2>
    <input type="button" value="返回" onclick="location.reload();">
</head>
<body>

</body>
</html>

如图
在这里插入图片描述

4. 彩蛋(直接copy的)

看图
在这里插入图片描述
在这里插入图片描述
这两个,都是抄的,就不放代码了…

5. 猜成语

转型之路了属于是
代码:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>无奖成语竞猜</title>
    <script type="text/javascript" src="http://apps.bdimg.com/libs/jquery/2.1.4/jquery.min.js"></script>
    <h3 style="text-align:center">根据意思猜成语</h3>
    <h4 id="t">成语意思和解释</h4>
    <h4 id="question">-</h4>
    <h4 id="fig1"> </h4>
    <h4 id="quanpin"> </h4>
    <h4 id="ts">请输入成语:</h4>
    <input type="text" id="input" placeholder="输入成语">
    <h4> </h4>
    <h4 id="ans"> </h4>
    <h4 id="chuchu"> </h4>
    <h4 id="liju"> </h4>
    <button onclick="send()" id="send">提交一下</button>
    <button onclick="fig_fun()" id="fig_b">提示</button>
    <h4> </h4>
    <input type="button" value="返回" onclick="location.reload();">
    <h6>成语数据由天行api提供</h6>
    <script>
        window.onload = load_all();
        const ans = "答案: {{ answer }}";
        const sp = "首拼: {{ sp }}";
        // 首拼
        const source = "出处: {{ source }}";
        // 出处
        const study = "例句: "+"{{ study }}";
        // 例句
        var fig_flag = 0;

        function load_all(){
            $("#question").text("{{ question }}");
        }

        function ok(){
            $("#ans").text(ans);
            $("#question").text("词义是: {{ question }}");
            $("#chuchu").text(source);
            $("#liju").text(study);
            $("#send").hide();
            $("#fig_b").hide();
            $("#input").hide();
            $("#ts").hide();
            $("#t").hide();
            $("#fig1").hide();
        }

        function fig_fun(){
            if(fig_flag == 2){
                // console.log(ans);
                ok();
            }else if(fig_flag == 1){
                $("#quanpin").text('全拼: {{ quanpin }}');
                fig_flag = fig_flag + 1;
                $("#fig_b").text("告诉我答案");
            }else{
                fig_flag = fig_flag + 1;
                $("#fig1").text(sp);
                $("#fig_b").text("再提示一下");
            }
        }

        function send(){
            if($("#input").val() == "{{ answer }}"){
                alert("恭喜 答对了");
                ok();
            }else{
                alert("还不对哦 再试试");
            }
        }
    </script>
</head>
<body>

</body>
</html>

如图:在这里插入图片描述

四、写后端

1. app.py

代码:

import http.client, urllib, json
import random
from flask import *
from sql_fun import *

app = Flask(__name__)
sql_f = mess_sql()


class info:
    def __init__(self):
        self.c = sql_f.new_statistics()
        sql_f.close_sql()


i = info()


@app.route('/')
def hello_world():  # put application's code here
    return render_template("main.html",
                           date=get_today(),
                           name=sql_f.ip_to_name(request.remote_addr))


@app.route('/add', methods=["POST"])
def add_fun():
    name = str(request.form.get("name"))
    vid = str(request.form.get("vid"))
    print('vid:', vid)
    ans = sql_f.add(name, request.remote_addr, vid)

    sql_f.close_sql()
    if '打卡成功' in ans:
        i.c = sql_f.new_statistics()
        # 在这里画图
        sql_f.close_sql()
    return {'message': ans}


@app.route('/about', methods=['POST'])
def helpfun():
    sql_f.add_visit(request.remote_addr, 'help')
    infol = []
    with open('helplist.txt', 'r', encoding='utf-8') as f:
        infolist = f.readlines()
        index = 0
        m = {}
        for i in infolist:
            index += 1
            if index % 2 == 0:
                m['info'] = i
                infol.append(m)
                m = {}
            else:
                m['time'] = i

    # return render_template('help.html')
    return render_template('help.html', infolist=infol)


@app.route('/heart', methods=['POST'])
def heart_fun():
    name = str(request.form.get("name"))
    a = random.randint(0,9)
    if name == '' or name == ' ':
        name = '专属于你'
    if a > 6:
        sql_f.add_visit(request.remote_addr, name + ':heart')
        print(name + ':heart')
        return render_template('heart.html', name=name)
    else:
        sql_f.add_visit(request.remote_addr, name + ':圣诞树')
        print(name + ':圣诞树')
        return render_template('圣诞树.html', name=name)


@app.route('/s', methods=['POST'])
def s():
    print(request.remote_addr + ':statistics')
    sql_f.add_visit(request.remote_addr, 'statistics')
    return render_template('statistics.html',
                           date=get_today(),
                           havedate=sql_f.today_date_have(),
                           classes=i.c,
                           time=datetime.datetime.now().strftime('%H:%M:%S'))


def get_p_dic():
    conn = http.client.HTTPSConnection('apis.tianapi.com')  # 接口域名
    params = urllib.parse.urlencode({'key': '天行数据api的key'})
    headers = {'Content-type': 'application/x-www-form-urlencoded'}
    conn.request('POST', '/caichengyu/index', params, headers)
    tianapi = conn.getresponse()
    result = tianapi.read()
    data = result.decode('utf-8')
    dict_data = json.loads(data)
    return dict_data


def is_None(s):
    if s == '':
        return '暂无'
    return s


@app.route('/p', methods=['POST', 'GET'])
def p():
    d = get_p_dic()
    print(d)
    print(request.remote_addr + ':猜成语')
    sql_f.add_visit(request.remote_addr, '猜成语')
    sql_f.add_phrase(d)
    return render_template("phrase.html",
                           question=d.get("result").get("question"),
                           answer=d.get("result").get("answer"),
                           source=is_None(d.get("result").get("source")),
                           study=is_None(d.get("result").get("study")),
                           sp=d.get("result").get("abbr"),
                           quanpin=d.get("result").get("pinyin")
                           )


if __name__ == '__main__':
    app.run()

其实也写了一些没有意义的代码 (某种程度上)
这个是程序入口,使用的flask框架

为什么 将画图部分移动到添加信息过程

  1. 提高用户体验:打卡过程中按钮会禁用一段时间(见前端代码),将占用时间的工作放到此过程中非常合适
  2. 提高用户体验:统计数据时非常快,用户会觉得我代码写的好 ,其实是把费时间工作放到其他地方做了。

2. sql_fun内部

文件: __ init__.py

import pymysql
import datetime
import matplotlib.pyplot as plt


def get_today():
    return str(datetime.datetime.now().strftime('%Y-%m-%d'))


plt.rcParams['font.sans-serif'] = ['SimHei']
plt.rcParams['axes.unicode_minus'] = False
plt.yticks(fontsize=26)
plt.xticks(fontsize=26)

class mess_sql:
    def __init__(self):
        # self.word_max = 500
        try:
            self.connect = pymysql.connect(host='localhost',
                                           user='root',
                                           password='',
                                           db='english punch',
                                           charset='gbk')  # 服务器名,账户,密码,数据库名称
        except:
            self.connect = pymysql.connect(host='localhost',
                                           user='root',
                                           password='',
                                           db='english punch',
                                           charset='gbk')  # 服务器名,账户,密码,数据库名称
        self.cur = self.connect.cursor()

    def open_sql(self):
        try:
            self.connect = pymysql.connect(host='localhost',
                                           user='root',
                                           password='',
                                           db='english punch',
                                           charset='gbk')  # 服务器名,账户,密码,数据库名称
        except:
            self.connect = pymysql.connect(host='localhost',
                                           user='root',
                                           password='',
                                           db='english punch',
                                           charset='gbk')  # 服务器名,账户,密码,数据库名称
        self.cur = self.connect.cursor()

    def close_sql(self):
        try:
            if self.connect:
                self.connect.close()
            if self.cur:
                self.cur.close()
        except Exception as e:
            print(e)

    def add(self, name, ip, vid):
        self.open_sql()
        try:
            self.connect.ping(reconnect=True)
            find_name_in_table = "select class from all_name where name='{0}'".format(name)
            # print("find_name_in_table:", find_name_in_table)
            self.cur.execute(find_name_in_table)
            classres = self.cur.fetchall()
            if len(classres) == 0:
                s = '{0} 不在班级总名单中哦 检查一下是不是字打错了'.format(name)
                print(s)
                self.add_visit(ip, s)
                return s

            today = "select date from update_table where date > '{0}' and name ='{1}'".format(
                get_today(), name)
            self.cur.execute(today)
            res = self.cur.fetchall()

            if len(res) != 0:
                s = '今天 {0} 已经打卡了哦 打卡时间为 {1}'.format(name, res[0][0])
                print(s)
                self.add_visit(ip, s)
                return s

            ip_err = "select name from update_table where date > '{0}' and vid='{1}'".format(
                get_today(), vid
            )
            self.cur.execute(ip_err)
            res = self.cur.fetchall()
            # TODO 防止重复ip
            res = []
            if len(res) != 0:
                s = "今天本设备已经为 {0} 打卡啦 不能重复打卡".format(res[0][0])
                print(s)
                self.add_visit(ip, s)
                return s

            add_text = "insert into update_table values ('{0}','{1}','{2}','{3}','{4}');"\
                .format(name, classres[0][0],
                        datetime.datetime.now(),
                        ip, vid)

            self.cur.execute(add_text)
            self.connect.commit()
            s = '{0}打卡成功 打卡时间为:{1}'.format(name, datetime.datetime.now().strftime('%H:%M:%S'))
            print(s)
            return s
        except Exception as e:
            print(e)
            self.add_visit(ip, str(e))
            return 'E'

    def new_statistics(self):
        try:
            self.open_sql()
            self.connect.ping(reconnect=True)
            search_class = 'SELECT class FROM all_name GROUP BY class;'
            self.cur.execute(search_class)
            classes = self.cur.fetchall()
            res_list = []
            for c in classes:
                c = c[0]
                all_name = "select name from all_name where class={0}".format(c)
                self.cur.execute(all_name)
                res = self.cur.fetchall()
                res = [i[0] for i in res]  # list(res)

                tijiao = "select name from update_table where date > '{0}' and class={1}".format(get_today(), c)
                self.cur.execute(tijiao)
                tijiao_list = self.cur.fetchall()
                # tijiao_list = list(tijiao_list)
                tijiao_list = [i[0] for i in tijiao_list]
                # print(tijiao_list)
                yitijiao = len(tijiao_list)
                weitijiao = len(res) - yitijiao
                weitijiao_list = ",".join([i for i in res if i not in tijiao_list])
                yitijiao_list = ",".join([i for i in tijiao_list])
                plt.figure()
                plt.cla()
                plt.title('提交对比', fontsize=26)
                plt.pie([weitijiao, yitijiao], labels=['未提交', '已提交'], autopct='%3.1f%%')
                pie_path = 'static/pie{0}.png'.format(c)
                plt.savefig(pie_path)
                # plt.show()
                bar_path = self.lately(c)
                plt.close('all')
                m = {'class': c,
                     'no_s_p': weitijiao,
                     'no_s_p_list': weitijiao_list,
                     's_p': yitijiao,
                     's_p_list': yitijiao_list,
                     'pie': pie_path,
                     'bar': bar_path
                     }
                res_list.append(m)
            return res_list
        except Exception as e:
            print(e)
            self.add_visit('0.0.0.1', str(e))
            return []

    def lately(self, c):
        try:
            plt.figure(dpi=300, figsize=(10, 10))
            time_list = []
            peo_list = []
            delta = datetime.timedelta(days=-1)
            endtime = datetime.datetime.now() - delta
            for i in range(5):

                starttime = endtime + delta
                # print(str(starttime.strftime('%Y-%m-%d')))
                sql = "select name from update_table where date>'{0}' and date<'{1}' and class={2}".format(starttime.strftime('%Y-%m-%d'),
                                                                                             endtime.strftime('%Y-%m-%d'), c)
                # print('sql line', sql)
                self.connect.ping(reconnect=True)
                self.cur.execute(sql)
                res = self.cur.fetchall()
                # if len(res) == 0:
                #     endtime = starttime
                #     continue
                time_list.append(str(starttime.strftime('%m-%d')))
                peo_list.append(len(res))

                endtime = starttime

            plt.cla()
            plt.legend(fontsize=32)
            plt.title('最近打卡情况对比',fontsize=20)
            x = [i + 1 for i in range(len(time_list))]
            color = ['peru', 'orchid', 'deepskyblue']
            plt.yticks(fontsize=26)
            plt.xticks(fontsize=26)

            plt.xlabel('时间', fontsize=26)
            plt.ylabel('人数', fontsize=26)
            list.reverse(time_list)
            list.reverse(peo_list)
            # time_list = time_list * 2
            # peo_list = peo_list * 2

            plt.xticks(x, time_list)  # 绘制x刻度标签
            b = plt.bar(x, peo_list, color=color, width=0.3)
            plt.bar_label(b, label_type='edge', fontsize=26)
            save_path = 'static/bar{0}.png'.format(c)
            try:
                plt.savefig(save_path)
            except:
                plt.savefig('bar.png')
            return save_path
        except Exception as e:
            print(e)
            self.add_visit('0.0.0.2', str(e))
            return ''

    def ip_to_name(self, ip):
        try:
            self.open_sql()
            sql = "select name from update_table where ip='{0}'".format(ip)
            self.connect.ping(reconnect=True)
            self.cur.execute(sql)
            res = self.cur.fetchall()
            res = [i[0] for i in res]  # list(res)
            res = list(set(res))
            # print(res)
            print('返回的记忆用户名' + ','.join(res))
            self.add_visit(ip, '返回的记忆用户名' + ','.join(res))
            self.close_sql()
            return ','.join(res)
        except Exception as e:
            print(e)
            self.add_visit('0.0.0.3', str(e))
            return ''

    def today_date_have(self):
        try:
            self.open_sql()
            sql = "select name from update_table where date > '{0}'".format(get_today())
            self.connect.ping(reconnect=True)
            self.cur.execute(sql)
            res = self.cur.fetchall()
            self.close_sql()
            return len(res) != 0
        except Exception as e:
            print(e)
            self.add_visit('0.0.0.3', str(e))
            return True

    def add_visit(self, ip, thing):
        try:
            self.open_sql()
            sql = "insert into visit values ('{0}','{1}','{2}');".format(datetime.datetime.now(),ip,thing)
            self.connect.ping(reconnect=True)
            self.cur.execute(sql)
            self.connect.commit()
            self.close_sql()
        except Exception as e:
            print(e)
            # self.add_visit('0.0.0.4', str(e))

    def add_phrase(self, d):
        try:
            self.open_sql()
            question = d.get("result").get("question")
            answer = d.get("result").get("answer")
            source = d.get("result").get("source")
            study = d.get("result").get("study")
            sp = d.get("result").get("abbr")
            quanpin = d.get("result").get("pinyin")
            sql = "insert into phrase values ('{0}','{1}','{2}','{3}','{4}','{5}');".format(
                question, answer, source, study, sp, quanpin
            )
            self.connect.ping(reconnect=True)
            self.cur.execute(sql)
            self.connect.commit()
            self.close_sql()
        except Exception as e:
            print(e)
            self.add_visit('0.0.0.5', str(e))
  • open_sql:打开数据库
  • close_sql:关闭数据库
  • add:添加打卡信息
  • new_statistics:统计本日打卡
  • lately:统计最近几日打卡人数
  • ip_to_name:记忆用户名
  • today_date_have:查询今天是否有人已打卡
  • add_visit:添加访问记录
  • add_phrase:添加一条成语记录

3. helplist.txt

2022-12-02 17:00
添加了"最近几日统计" 增强稳定性
2022-12-03 00:30
添加对代替打卡的防护 同一设备每天只能打卡一人(已停用)
2022-12-03 10:30
根据以前打卡记录自动填充姓名 节省时间(不完全)
2022-12-03 15:00
添加了对班级的管理 方便其他班级的使用
2022-12-03 17:30
添加了小彩蛋
2022-12-04 01:00
修复会将昨天打卡情况显示到今天的问题
2022-12-04 11:30
添加了猜成语功能
2022-12-04 12:40
增强稳定性 修复了进入彩蛋后无法退出的问题 现在进入彩蛋后 点击文字即可退出
2022-12-08 21:40
增强稳定性 修复长时间连接数据库导致的未知错误
2022-12-08 22:50
更新小彩蛋 加入了圣诞树 点击图像可以返回

使用helplist的目的是降低维护成本,不需要每次更新都需要修改h5页面

五、辅助文件

1. run.bat

一键跑项目

@echo off
python -m flask run --host=0.0.0.0 --port=88

2. 项目目录

C:.
│  app.py
│  bar.png
│  helplist.txt
│  run.bat
│  
├─sql_fun
│  │  bar.png
│  │  pie.png
│  │  __init__.py
│  │  
│  └─__pycache__
│          __init__.cpython-38.pyc
│          
├─static
│      bar3.png
│      bar7.png
│      img.png
│      pie3.png
│      pie7.png
│      vx.jpg
│      
├─templates
│      heart.html
│      help.html
│      main.html
│      phrase.html
│      statistics.html
│      圣诞树.html
│      
└─__pycache__
        app.cpython-38.pyc
        

六、留个地方放项目网址

等项目测试打包好会上传到GitHub或其他网站,链接会放这里。

写在最后:生活不易,高中生也叹气。

如果你觉得有用的话麻烦点个赞,或者扫一扫二维码赞赏一下。
在这里插入图片描述在这里插入图片描述

Logo

本社区面向用户介绍CSDN开发云部门内部产品使用和产品迭代功能,产品功能迭代和产品建议更透明和便捷

更多推荐