iaun 4 лет назад
Сommit
447c0a55dd
18 измененных файлов с 1312 добавлено и 0 удалено
  1. 1 0
      .gitignore
  2. 1 0
      README.md
  3. 36 0
      db_config.py
  4. 63 0
      db_dbutils_init.py
  5. 80 0
      html/index.course.html
  6. 70 0
      html/index.html
  7. 132 0
      html/show.all.html
  8. 205 0
      html/show.html
  9. 103 0
      html/show.new.html
  10. 25 0
      html/testpage.html
  11. 183 0
      mysqlhelper.py
  12. 128 0
      server.py
  13. 3 0
      start.sh
  14. 1 0
      static/jquery.min.js
  15. 5 0
      static/vue.js
  16. 5 0
      static/vue.min.js
  17. 72 0
      sync/sync.py
  18. 199 0
      zk.sql

+ 1 - 0
.gitignore

@@ -0,0 +1 @@
+__pycache__/

+ 1 - 0
README.md

@@ -0,0 +1 @@
+# ZKWEB

+ 36 - 0
db_config.py

@@ -0,0 +1,36 @@
+
+import pymysql
+
+# 数据库信息
+DB_TEST_HOST = "127.0.0.1"
+DB_TEST_PORT = 3306
+DB_TEST_DBNAME = "zk"
+DB_TEST_USER = "user"
+DB_TEST_PASSWORD = "password"
+
+# 数据库连接编码
+DB_CHARSET = "utf8mb4"
+
+# mincached : 启动时开启的闲置连接数量(缺省值 0 开始时不创建连接)
+DB_MIN_CACHED = 10
+
+# maxcached : 连接池中允许的闲置的最多连接数量(缺省值 0 代表不闲置连接池大小)
+DB_MAX_CACHED = 10
+
+# maxshared : 共享连接数允许的最大数量(缺省值 0 代表所有连接都是专用的)如果达到了最大数量,被请求为共享的连接将会被共享使用
+DB_MAX_SHARED = 20
+
+# maxconnecyions : 创建连接池的最大数量(缺省值 0 代表不限制)
+DB_MAX_CONNECYIONS = 50
+
+# blocking : 设置在连接池达到最大数量时的行为(缺省值 0 或 False 代表返回一个错误<toMany......> 其他代表阻塞直到连接数减少,连接被分配)
+DB_BLOCKING = True
+
+# maxusage : 单个连接的最大允许复用次数(缺省值 0 或 False 代表不限制的复用).当达到最大数时,连接会自动重新连接(关闭和重新打开)
+DB_MAX_USAGE = 0
+
+# setsession : 一个可选的SQL命令列表用于准备每个会话,如["set datestyle to german", ...]
+DB_SET_SESSION = None
+
+# creator : 使用连接数据库的模块
+DB_CREATOR = pymysql

+ 63 - 0
db_dbutils_init.py

@@ -0,0 +1,63 @@
+
+from dbutils.pooled_db import PooledDB
+import db_config as config
+
+"""
+@功能:创建数据库连接池
+"""
+
+
+class MyConnectionPool(object):
+    __pool = None
+
+    # def __init__(self):
+    #     self.conn = self.__getConn()
+    #     self.cursor = self.conn.cursor()
+
+    # 创建数据库连接conn和游标cursor
+    def __enter__(self):
+        self.conn = self.__getconn()
+        self.cursor = self.conn.cursor()
+
+    # 创建数据库连接池
+    def __getconn(self):
+        if self.__pool is None:
+            self.__pool = PooledDB(
+                creator=config.DB_CREATOR,
+                mincached=config.DB_MIN_CACHED,
+                maxcached=config.DB_MAX_CACHED,
+                maxshared=config.DB_MAX_SHARED,
+                maxconnections=config.DB_MAX_CONNECYIONS,
+                blocking=config.DB_BLOCKING,
+                maxusage=config.DB_MAX_USAGE,
+                setsession=config.DB_SET_SESSION,
+                host=config.DB_TEST_HOST,
+                port=config.DB_TEST_PORT,
+                user=config.DB_TEST_USER,
+                passwd=config.DB_TEST_PASSWORD,
+                db=config.DB_TEST_DBNAME,
+                use_unicode=False,
+                charset=config.DB_CHARSET
+            )
+        return self.__pool.connection()
+
+    # 释放连接池资源
+    def __exit__(self, exc_type, exc_val, exc_tb):
+        self.cursor.close()
+        self.conn.close()
+
+    # 关闭连接归还给链接池
+    # def close(self):
+    #     self.cursor.close()
+    #     self.conn.close()
+
+    # 从连接池中取出一个连接
+    def getconn(self):
+        conn = self.__getconn()
+        cursor = conn.cursor()
+        return cursor, conn
+
+
+# 获取连接池,实例化
+def get_my_connection():
+    return MyConnectionPool()

+ 80 - 0
html/index.course.html

@@ -0,0 +1,80 @@
+<html lang="zh">
+
+<head>
+    <title>百日题库-题目合集</title>
+    <meta charset="utf-8" />
+    <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
+    <script src="/static/vue.js"></script>
+    <script src="/static/jquery.min.js"></script>
+    <style>
+        .click {
+            font-size: 22px;
+            /*text-decoration: underline;*/
+            user-select: none;
+            cursor: pointer;
+        }
+    </style>
+</head>
+
+<body>
+    <div id="app">
+        <p v-show="courseName!=''">{{courseName}}</p>
+        <p v-show="msg!=''">{{msg}}</p>
+        <p v-show="msg==''" class="click" @click="goAll()">所有</p>
+        <p class="click" v-for="q, i in questions" @click="goQuestion(q.id, q.title, i+1)">{{((i+1
+            <10)?( "0"+(i+1)):( ""+(i+1))) + '-' +q.title}}</p>
+    </div>
+    <script>
+        var URL_PREFIX = "";
+        var app = new Vue({
+            el: '#app',
+            data: {
+                msg: "loading...",
+                course: -1,
+                questions: [],
+                courseName: ""
+            },
+            methods: {
+                getQuestionList() {
+                    var self = this;
+                    $.ajax({
+                        url: URL_PREFIX + "/get/question/list/" + this.course,
+                        success: function(data) {
+                            console.log(data);
+                            if (data["success"]) {
+                                self.msg = "";
+                                self.questions = data["data"]["results"];
+                            }
+                            else {
+                                self.msg = "获取失败";
+                            }
+                        }
+                    });
+                },
+                goQuestion(id, paperName, index) {
+                    location.href = URL_PREFIX + "/show_new/" + id + '?course_name=' + encodeURI(this.courseName) + '&paper_name=' + encodeURI(paperName) + "&index=" + ((index < 10) ? ("0" + index) : ("" + index));
+                },
+                goAll() {
+                    location.href = URL_PREFIX + "/show/all/" + this.course + "?course_name=" + encodeURI(this.courseName);
+                }
+            },
+            mounted() {
+                var search = location.search.substr(1).split('&');
+                for (var i = 0, j = search.length; i < j; i++) {
+                    var s = search[i].split('=');
+                    if (s[0] === "course_name") {
+                        this.courseName = decodeURI(s[1]);
+                    }
+                }
+                if (this.courseName !== '') {
+                    document.title = "百日题库-" + this.courseName;
+                }
+                var url = location.href.split('?')[0].split('/');
+                this.course = url[url.length - 1];
+                this.getQuestionList();
+            }
+        });
+    </script>
+</body>
+
+</html>

+ 70 - 0
html/index.html

@@ -0,0 +1,70 @@
+<html lang="zh">
+
+<head>
+    <title>百日题库-题目合集</title>
+    <meta charset="utf-8" />
+    <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
+    <script src="/static/vue.js"></script>
+    <script src="/static/jquery.min.js"></script>
+    <style>
+        .click {
+            font-size: 22px;
+            /*text-decoration: underline;*/
+            user-select: none;
+            cursor: pointer;
+        }
+    </style>
+</head>
+
+<body>
+    <div id="app">
+        <p v-show="msg!=''">{{msg}}</p>
+        <p v-show="updateTime!=''">更新时间:{{updateTime}}</p>
+        <p class="click" v-for="c in courses" @click="chooseCourse(c.id, c.name)">{{c.name}}</p>
+    </div>
+    <script>
+        var URL_PREFIX = "";
+        var app = new Vue({
+            el: '#app',
+            data: {
+                msg: "loading...",
+                updateTime: "",
+                courses: []
+            },
+            methods: {
+                chooseCourse(id, courseName) {
+                    courseName = courseName.split(' ');
+                    if (courseName.length < 2 || courseName[1] === '') {
+                        courseName = courseName[0];
+                    }
+                    else {
+                        courseName = courseName[1];
+                    }
+                    location.href = URL_PREFIX + "/course/" + id + "?course_name=" + encodeURI(courseName);
+                },
+                getCourse() {
+                    var self = this;
+                    $.ajax({
+                        url: URL_PREFIX + "/get/course",
+                        success: function(data) {
+                            console.log(data);
+                            if (data["success"]) {
+                                self.msg = "";
+                                self.updateTime = data["time"];
+                                self.courses = data["data"]["results"];
+                            }
+                            else {
+                                self.msg = "获取失败";
+                            }
+                        }
+                    });
+                }
+            },
+            mounted() {
+                this.getCourse();
+            }
+        });
+    </script>
+</body>
+
+</html>

+ 132 - 0
html/show.all.html

@@ -0,0 +1,132 @@
+<html lang="zh">
+
+<head>
+    <title></title>
+    <meta charset="utf-8" />
+    <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
+    <script src="/static/vue.js"></script>
+    <script src="/static/jquery.min.js"></script>
+    <style>
+        p {
+            white-space: pre-line;
+        }
+    </style>
+</head>
+
+<body>
+    <div id="app">
+        <p v-show="!ajaxFinish">{{msg}}</p>
+        <!--<input v-show="ajaxFinish" type="checkbox" v-model="showAnswer" /><span v-show="ajaxFinish">显示答案</span>-->
+        <p id="tools" v-show="ajaxFinish"><input type="checkbox" v-model="showAnswer"/><span>显示答案</span><input type="button" @click="print" value="打印"/></p>
+        <div v-show="ajaxFinish" v-for="data in datas">
+            <div v-if="data!=null">
+                <!--data-->
+                <h1>{{data.title}}</h1>
+                <div v-for="group in data.groups">
+                    <h2>{{group.title}}</h2>
+                    <p v-if="group.memo!=''" v-html="group.memo"></p>
+                    <div v-for="question,index in group.questions">
+                        <p style="font-weight:bold;" v-html="(index+1)+ '. ' + question.title"></p>
+                        <ul>
+                            <li v-for="option in question.options" v-html="option.value + '. '+option.text"></li>
+                        </ul>
+                        <p v-show="showAnswer" v-html="'<b>答案:</b>'+question.answer.join(';')"></p>
+                        <p v-if="question.explanation" v-show="showAnswer" v-html="'<b>题解:</b>'+question.explanation"></p>
+                        <hr>
+                    </div>
+                </div>
+            </div>
+        </div>
+    </div>
+    <script>
+        var URL_PREFIX = "";
+        var app = new Vue({
+            el: '#app',
+            data: {
+                courses: [],
+                datas: [],
+                msg: "loading...",
+                showAnswer: true,
+                finishCount: 0,
+                ajaxFinish: false
+            },
+            methods: {
+                print() {
+                    var tools = document.getElementById("tools");
+                    tools.style.display = "none";
+                    print();
+                    tools.style.display = "";
+                },
+                getCourse() {
+                    var url = location.href.split('?')[0].split('/');
+                    var self = this;
+                    $.ajax({
+                        url: URL_PREFIX + "/get/question/list/" + url[url.length - 1],
+                        type: "GET",
+                        success: function(data) {
+                            if (data["success"]) {
+                                if (data["data"] && data["data"]["results"]) {
+                                    var res = data["data"]["results"];
+                                    for (var i = 0, j = res.length; i < j; i++) {
+                                        self.courses.push(res[i]["id"]);
+                                    }
+                                    self.getContentLoop();
+                                }
+                                if (document.title === "") {
+                                    document.title = "百日题库-" + data["data"]["title"];
+                                }
+                            }
+                            else {
+                                self.msg = "获取失败";
+                            }
+                        }
+                    });
+                },
+                getContentLoop() {
+                    for (var i = 0, j = this.courses.length; i < j; i++) {
+                        this.getContentById(this.courses[i], i);
+                    }
+                },
+                getContentById(id, pos) {
+                    var self = this;
+                    $.ajax({
+                        url: URL_PREFIX + "/get/question/content_object/" + id,
+                        type: "GET",
+                        success: function(data) {
+                            self.finishCount++;
+                            if (data["success"]) {
+                                self.$set(self.datas, pos, data["data"]);
+                                //self.datas[pos] = data["data"];
+                            }
+                            else {
+                                self.msg = "获取失败";
+                            }
+                            console.log(self.finishCount, self.courses.length);
+                            if (self.finishCount == self.courses.length) {
+                                self.ajaxFinish = true;
+                            }
+                        }
+                    })
+                }
+            },
+            mounted() {
+                var search = location.search.substr(1).split('&');
+                var courseName = "";
+                for (var i = 0, j = search.length; i < j; i++) {
+                    var s = search[i].split('=');
+                    switch (s[0]) {
+                        case "course_name":
+                            courseName = s[1];
+                            break;
+                    }
+                }
+                if (courseName !== "") {
+                    document.title = "百日题库-" + decodeURI(courseName);
+                }
+                this.getCourse();
+            }
+        });
+    </script>
+</body>
+
+</html>

+ 205 - 0
html/show.html

@@ -0,0 +1,205 @@
+<html lang="zh">
+
+<head>
+    <title></title>
+    <meta charset="utf-8" />
+    <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
+    <script src="/static/vue.js"></script>
+    <script src="/static/jquery.min.js"></script>
+</head>
+
+<body>
+    <div id="app">
+        <p v-show="msg!=''">{{msg}}</p>
+        <div v-if="data!=null">
+            <!--data-->
+            <h1>{{data.title}}</h1>
+            <div v-for="st in data.st">
+                <h2>{{st.title}}</h2>
+                <div v-for="ti in st.ti">
+                    <p v-html="ti.title"></p>
+                    <ul>
+                        <li v-for="option in ti.options" v-html="option"></li>
+                    </ul>
+                    <p v-html="'答案:'+ti.answer"></p>
+                    <p v-show="ti.exp!=''" v-html="'题解:'+ti.exp"></p>
+                </div>
+            </div>
+        </div>
+    </div>
+    <script>
+        var URL_PREFIX = "";
+        var app = new Vue({
+            el: '#app',
+            data: {
+                data: null,
+                msg: "loading..."
+            },
+            methods: {
+                getContentById() {
+                    var url = location.href.split('/');
+                    var self = this;
+                    $.ajax({
+                        url: URL_PREFIX + "/get/question/content/" + url[url.length - 1],
+                        type: "GET",
+                        success: function(data) {
+                            if (data["success"]) {
+                                data = data["data"];
+                                data = self.encodeHtml(data);
+                                data = self.text2img(data);
+                                data = self.text2tag(data, "上");
+                                data = self.text2tag(data, "下");
+                                data = self.text2tag(data, "划");
+                                self.textFormat(data);
+                            }
+                            else {
+                                self.msg = "获取失败";
+                            }
+                        }
+                    })
+                },
+                textFormat(text) {
+                    text = text.split('\n');
+                    var data = {
+                        title: "",
+                        st: []
+                    };
+                    var stCursor = -1,
+                        tiCursor;
+                    var isTitle = -1;
+                    for (var i = 0, j = text.length; i < j; i++) {
+                        if (i === 0) {
+                            data["title"] = text[0];
+                            document.title = "百日题库-"+text[0];
+                            continue;
+                        }
+                        // if (text[i].search(/^[^-~]、|[^~]*.[^~]*单选题|[^~]*.[^~]*每小题/) !== -1) { // 子标题
+                        if (text[i].search(/^[一|二|三|四|五|六|七|八|九|十](.*?)题/) !== -1) { // 子标题
+                            var st = { title: text[i], ti: [] };
+                            data["st"].push(st);
+                            stCursor++;
+                            tiCursor = -1;
+                            continue;
+                        }
+                        if (text[i].search(/^\d+\./) !== -1) { // 题目
+                            var tiTemp = { "title": text[i], options: [], answer: "", exp: "" }
+                            data["st"][stCursor]["ti"].push(tiTemp);
+                            tiCursor++;
+                            isTitle = 1;
+                            continue;
+                        }
+                        if (text[i].search(/^\[图/) !== -1) { // 图片单独一行,直接追加
+                            try {
+                                if (isTitle === 1) {
+                                    data["st"][stCursor]["ti"][tiCursor]["title"] += text[i];
+                                }
+                                else if (isTitle === 0) {
+                                    data["st"][stCursor]["ti"][tiCursor]["answer"] += text[i];
+                                }
+                            }
+                            catch (e) {
+
+                            }
+                            continue;
+                        }
+                        if (text[i].search(/^[A-Z]\.[^~]* /) !== -1) { // 题目选项(空格)
+                            var temp = text[i].split(' ');
+                            for (var k = 0, l = temp.length; k < l; k++) {
+                                if (temp[k] !== "") {
+                                    data["st"][stCursor]["ti"][tiCursor]['options'].push(temp[k]);
+                                }
+                            }
+                            continue;
+                        }
+                        if (text[i].search(/^[A-Z]\.[^~]*\t/) !== -1) { // 题目选项(tab)
+                            var temp = text[i].split('\t');
+                            for (var k = 0, l = temp.length; k < l; k++) {
+                                if (temp[k] !== "") {
+                                    data["st"][stCursor]["ti"][tiCursor]['options'].push(temp[k]);
+                                }
+                            }
+                            continue;
+                        }
+                        if (text[i].search(/^[A-Z]\./) !== -1) { // 题目选项
+                            data["st"][stCursor]["ti"][tiCursor]['options'].push(text[i]);
+                            continue;
+                        }
+                        if (text[i].search(/^(\d)/) !== -1) { // 答案(大括号)
+                            data["st"][stCursor]["ti"][tiCursor]['answer'] += "<br>" + text[i];
+                            continue;
+                        }
+                        if (text[i].search(/^[①|②|③|④|⑤|⑥|⑦|⑧|⑨|⑩|⑪|⑫|⑬|⑭|⑮|⑯|⑰|⑱|⑲|⑳]/) !== -1) { // 答案(圆圈数字)
+                            data["st"][stCursor]["ti"][tiCursor]['answer'] += "<br>" + text[i];
+                            continue;
+                        }
+                        if (text[i].search(/^答案:/) !== -1) { // 答案
+                            data["st"][stCursor]["ti"][tiCursor]['answer'] = text[i].split(':')[1];
+                            isTitle = 0;
+                            continue;
+                        }
+                        if (text[i].search(/^题解:/) !== -1) { // 题解
+                            data["st"][stCursor]["ti"][tiCursor]['exp'] = text[i].split(':')[1];
+                            continue;
+                        }
+                        // 格式外的内容?
+                        try {
+                            if (isTitle === 1) {
+                                data["st"][stCursor]["ti"][tiCursor]["title"] += "<br>" + text[i];
+                            }
+                            else if (isTitle === 0) {
+                                data["st"][stCursor]["ti"][tiCursor]["answer"] += "<br>" + text[i];
+                            }
+                        }
+                        catch (e) {
+
+                        }
+                    }
+                    console.log(data);
+                    this.data = data;
+                    this.msg = "";
+                    // console.log(JSON.stringify(data));
+                },
+                encodeHtml(str) {
+                    var temp = "";
+                    if (str.length == 0) return "";
+                    temp = str.replace(/&/g, "&amp;");
+                    temp = temp.replace(/</g, "&lt;");
+                    temp = temp.replace(/>/g, "&gt;");
+                    temp = temp.replace(/\'/g, "&#39;");
+                    temp = temp.replace(/\"/g, "&quot;");
+                    // 附加
+                    temp = temp.replace(/\\,/g, ", ");
+                    return temp;
+                },
+                text2tag(s, name) {
+                    var tag = "";
+                    switch (name) {
+                        case "上":
+                            tag = "sup";
+                            break;
+                        case "下":
+                            tag = "sub";
+                            break;
+                        case "划":
+                            tag = "u";
+                            break;
+                    }
+                    var re = new RegExp(`\\[${name}:(.*?)]`, 'g');
+                    return s.replace(re, `<${tag}>\$1</${tag}>`);
+                },
+
+                text2img(str) {
+                    var url = location.href.split('/');
+                    var contentId = url[url.length - 1];
+                    return str.replace(/\[图:(.*?)\]/g, `<img src="https://sdjrzk-1251357229.cos.ap-guangzhou.myqcloud.com/exam/paper/${contentId}/images/\$1"></img>`)
+                    // https://sdjrzk-1251357229.cos.ap-guangzhou.myqcloud.com/exam/paper/564/images/1810.png
+                }
+            },
+            mounted() {
+                this.getContentById();
+            }
+        });
+    </script>
+</body>
+
+</html>

+ 103 - 0
html/show.new.html

@@ -0,0 +1,103 @@
+<html lang="zh">
+
+<head>
+    <title></title>
+    <meta charset="utf-8" />
+    <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
+    <script src="/static/vue.js"></script>
+    <script src="/static/jquery.min.js"></script>
+    <style>
+        p {
+            white-space: pre-line;
+        }
+    </style>
+</head>
+
+<body>
+    <div id="app">
+        <p v-show="msg!=''">{{msg}}</p>
+        <div v-if="data!=null">
+            <!--data-->
+            <p id="tools"><input type="checkbox" v-model="showAnswer"/><span>显示答案</span><input type="button" @click="print" value="打印"/></p>
+            <h1>{{data.title}}</h1>
+            <div v-for="group in data.groups">
+                <h2>{{group.title}}</h2>
+                <p v-if="group.memo!=''" v-html="group.memo"></p>
+                <div v-for="question,index in group.questions">
+                    <p style="font-weight:bold;" v-html="(index+1)+ '. ' + question.title"></p>
+                    <ul>
+                        <li v-for="option in question.options" v-html="option.value + '. '+option.text"></li>
+                    </ul>
+                    <p v-show="showAnswer" v-html="'<b>答案:</b>'+question.answer.join(';')"></p>
+                    <p v-if="question.explanation" v-show="showAnswer" v-html="'<b>题解:</b>'+question.explanation"></p>
+                    <hr>
+                </div>
+            </div>
+        </div>
+    </div>
+    <script>
+        var URL_PREFIX = "";
+        var app = new Vue({
+            el: '#app',
+            data: {
+                data: null,
+                msg: "loading...",
+                showAnswer: true,
+            },
+            methods: {
+                getContentById() {
+                    var url = location.href.split('?')[0].split('/');
+                    var self = this;
+                    $.ajax({
+                        url: URL_PREFIX + "/get/question/content_object/" + url[url.length - 1],
+                        type: "GET",
+                        success: function(data) {
+                            if (data["success"]) {
+                                self.data = data["data"];
+                                if (document.title === "") {
+                                    document.title = "百日题库-" + data["data"]["title"];
+                                }
+                                self.msg = "";
+                            }
+                            else {
+                                self.msg = "获取失败";
+                            }
+                        }
+                    });
+                },
+                print() {
+                  var tools = document.getElementById("tools");
+                  tools.style.display = "none";
+                  print();
+                  tools.style.display = "";
+                }
+            },
+            mounted() {
+                var search = location.search.substr(1).split('&');
+                var courseName = "";
+                var paperName = "";
+                var index = "";
+                for (var i = 0, j = search.length; i < j; i++) {
+                    var s = search[i].split('=');
+                    switch (s[0]) {
+                        case "course_name":
+                            courseName = s[1];
+                            break;
+                        case "paper_name":
+                            paperName = s[1];
+                            break;
+                        case "index":
+                            index = s[1];
+                            break;
+                    }
+                }
+                if (courseName !== "" && paperName !== "" && index !== "") {
+                    document.title = "百日题库-" + decodeURI(courseName) + "-" + index + "-" + decodeURI(paperName);
+                }
+                this.getContentById();
+            }
+        });
+    </script>
+</body>
+
+</html>

+ 25 - 0
html/testpage.html

@@ -0,0 +1,25 @@
+<html lang="zh">
+
+<head>
+    <title></title>
+    <meta charset="utf-8" />
+    <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
+    <script src="/static/vue.js"></script>
+    <script src="/static/jquery.min.js"></script>
+    
+</head>
+
+<body>
+   <video id="video" width="100%" height="100%"
+    controls="controls" autoplay="autoplay"
+    x-webkit-airplay="true" x5-video-player-fullscreen="true"
+    preload="auto" playsinline="true" webkit-playsinline
+    x5-video-player-typ="h5">
+   </video>
+   <script>
+       document.getElementById('video').src = "http://hls.videocc.net/ae96de56b5/a/ae96de56b522c4b7cde383df7154dcfe_1.m3u8";
+       
+   </script>
+</body>
+
+</html>

+ 183 - 0
mysqlhelper.py

@@ -0,0 +1,183 @@
+
+from db_dbutils_init import get_my_connection
+
+"""执行语句查询有结果返回结果没有返回0;增/删/改返回变更数据条数,没有返回0"""
+
+
+class MySqLHelper(object):
+    def __init__(self):
+        self.db = get_my_connection()  # 从数据池中获取连接
+
+    def __new__(cls, *args, **kwargs):
+        if not hasattr(cls, 'inst'):  # 单例
+            cls.inst = super(MySqLHelper, cls).__new__(cls, *args, **kwargs)
+        return cls.inst
+
+    # 封装执行命令
+    def execute(self, sql, param=None, autoclose=False):
+        """
+        【主要判断是否有参数和是否执行完就释放连接】
+        :param sql: 字符串类型,sql语句
+        :param param: sql语句中要替换的参数"select %s from tab where id=%s" 其中的%s就是参数
+        :param autoclose: 是否关闭连接
+        :return: 返回连接conn和游标cursor
+        """
+        cursor, conn = self.db.getconn()  # 从连接池获取连接
+        count = 0
+        try:
+            # count : 为改变的数据条数
+            if param:
+                count = cursor.execute(sql, param)
+            else:
+                count = cursor.execute(sql)
+            conn.commit()
+            if autoclose:
+                self.close(cursor, conn)
+        except Exception as e:
+            pass
+        return cursor, conn, count
+
+    # 执行多条命令
+    # def executemany(self, lis):
+    #     """
+    #     :param lis: 是一个列表,里面放的是每个sql的字典'[{"sql":"xxx","param":"xx"}....]'
+    #     :return:
+    #     """
+    #     cursor, conn = self.db.getconn()
+    #     try:
+    #         for order in lis:
+    #             sql = order['sql']
+    #             param = order['param']
+    #             if param:
+    #                 cursor.execute(sql, param)
+    #             else:
+    #                 cursor.execute(sql)
+    #         conn.commit()
+    #         self.close(cursor, conn)
+    #         return True
+    #     except Exception as e:
+    #         print(e)
+    #         conn.rollback()
+    #         self.close(cursor, conn)
+    #         return False
+
+    # 释放连接
+    def close(self, cursor, conn):
+        """释放连接归还给连接池"""
+        cursor.close()
+        conn.close()
+
+    # 查询所有
+    def selectall(self, sql, param=None):
+        try:
+            cursor, conn, count = self.execute(sql, param)
+            res = cursor.fetchall()
+            return res
+        except Exception as e:
+            print(e)
+            self.close(cursor, conn)
+            return count
+
+    # 查询单条
+    def selectone(self, sql, param=None):
+        try:
+            cursor, conn, count = self.execute(sql, param)
+            res = cursor.fetchone()
+            self.close(cursor, conn)
+            return res
+        except Exception as e:
+            print("error_msg:", e.args)
+            self.close(cursor, conn)
+            return count
+
+    # 增加
+    def insertone(self, sql, param):
+        try:
+            cursor, conn, count = self.execute(sql, param)
+            # _id = cursor.lastrowid()  # 获取当前插入数据的主键id,该id应该为自动生成为好
+            conn.commit()
+            self.close(cursor, conn)
+            return count
+            # 防止表中没有id返回0
+            # if _id == 0:
+            #     return True
+            # return _id
+        except Exception as e:
+            print(e)
+            conn.rollback()
+            self.close(cursor, conn)
+            return count
+
+    # 增加多行
+    def insertmany(self, sql, param):
+        """
+        :param sql:
+        :param param: 必须是元组或列表[(),()]或((),())
+        :return:
+        """
+        cursor, conn, count = self.db.getconn()
+        try:
+            cursor.executemany(sql, param)
+            conn.commit()
+            return count
+        except Exception as e:
+            print(e)
+            conn.rollback()
+            self.close(cursor, conn)
+            return count
+
+    # 删除
+    def delete(self, sql, param=None):
+        try:
+            cursor, conn, count = self.execute(sql, param)
+            self.close(cursor, conn)
+            return count
+        except Exception as e:
+            print(e)
+            conn.rollback()
+            self.close(cursor, conn)
+            return count
+
+    # 更新
+    def update(self, sql, param=None):
+        try:
+            cursor, conn, count = self.execute(sql, param)
+            conn.commit()
+            self.close(cursor, conn)
+            return count
+        except Exception as e:
+            print(e)
+            conn.rollback()
+            self.close(cursor, conn)
+            return count
+
+
+if __name__ == '__main__':
+    db = MySqLHelper()
+    # # 查询单条
+    # sql1 = 'select * from userinfo where name=%s'
+    # args = 'python'
+    # ret = db.selectone(sql=sql1, param=args)
+    # print(ret)  # (None, b'python', b'123456', b'0')
+    # 增加单条
+    # sql2 = 'insert into userinfo (name,password) VALUES (%s,%s)'
+    # ret = db.insertone(sql2, ('old2','22222'))
+    # print(ret)
+    # 增加多条
+    # sql3 = 'insert into userinfo (name,password) VALUES (%s,%s)'
+    # li = li = [
+    #     ('分省', '123'),
+    #     ('到达','456')
+    # ]
+    # ret = db.insertmany(sql3,li)
+    # print(ret)
+    # 删除
+    # sql4 = 'delete from  userinfo WHERE name=%s'
+    # args = 'xxxx'
+    # ret = db.delete(sql4, args)
+    # print(ret)
+    # 更新
+    # sql5 = r'update userinfo set password=%s WHERE name LIKE %s'
+    # args = ('993333993', '%old%')
+    # ret = db.update(sql5, args)
+    # print(ret)

+ 128 - 0
server.py

@@ -0,0 +1,128 @@
+from flask import Flask,render_template,Response,request
+import time
+import mysqlhelper
+import flask
+import json
+import datetime
+
+# json时间问题和bytes问题解决
+class JsonEncoder(json.JSONEncoder):
+    def default(self, obj):
+        if isinstance(obj,datetime.datetime):
+            return obj.strftime("%Y-%m-%d %H:%M:%S")
+        elif isinstance(obj,bytes):
+            return str(obj, 'utf-8')
+        else:
+            return json.JSONEncoder.default(self,obj)
+
+app=Flask(__name__, static_folder='static',template_folder='static')
+app.jinja_env.auto_reload=True
+
+# Response('{"success":false}', mimetype='application/json')
+@app.route('/testpage')
+def testpage():
+    try:
+        with open('html/testpage.html', 'r') as f:
+            return f.read()
+    except:
+        return ""
+
+# Response('{"success":false}', mimetype='application/json')
+
+@app.route('/show/<id>')
+def show(id):
+    try:
+        with open('html/show.html', 'r') as f:
+            return f.read()
+    except:
+        return ""
+        
+@app.route('/show_new/<id>')
+def show_new(id):
+    try:
+        with open('html/show.new.html', 'r') as f:
+            return f.read()
+    except:
+        return ""
+
+
+@app.route('/show/all/<id>')
+def show_all(id):
+    try:
+        with open('html/show.all.html', 'r') as f:
+            return f.read()
+    except:
+        return ""
+
+
+@app.route('/')
+def index():
+    try:
+        with open('html/index.html', 'r') as f:
+            return f.read()
+    except:
+        return ""
+        
+@app.route('/course/<id>')
+def index_course(id):
+    try:
+        with open('html/index.course.html', 'r') as f:
+            return f.read()
+    except:
+        return ""
+        
+@app.route('/get/course')
+def get_course():
+    data = db.selectone("select time, data from course")
+    if isinstance(data, int):
+        # 失败
+        return Response('{"success":0}', mimetype='application/json')
+    d = {
+        "success":1,
+        "time": data[0],
+        "data": json.loads(data[1])
+    }
+    return Response(json.dumps(d, cls=JsonEncoder, ensure_ascii=False), mimetype='application/json')
+        
+@app.route('/get/question/list/<id>')
+def get_question_list(id):
+    data = db.selectone("select data from question_list where course_id = %s", id)
+    if isinstance(data, int) or not data:
+        # 失败
+        return Response('{"success":0}', mimetype='application/json')
+    d = {
+        "success":1,
+        "data": json.loads(data[0])
+    }
+    return Response(json.dumps(d, cls=JsonEncoder, ensure_ascii=False), mimetype='application/json')
+
+@app.route('/get/question/content/<id>')
+def get_question_content(id):
+    data = db.selectone("select content from question_content where question_id = %s", id)
+    if isinstance(data, int) or not data:
+        # 失败
+        return Response('{"success":0}', mimetype='application/json')
+    d = {
+        "success":1,
+        "data": str(data[0], 'utf-8')
+    }
+    return Response(json.dumps(d, cls=JsonEncoder, ensure_ascii=False), mimetype='application/json')
+
+@app.route('/get/question/content_object/<id>')
+def get_question_content_object(id):
+    data = db.selectone("select content_object from question_content_object where question_id = %s", id)
+    if isinstance(data, int) or not data:
+        # 失败
+        return Response('{"success":0}', mimetype='application/json')
+    d = {
+        "success":1,
+        "data": json.loads(data[0])
+    }
+    return Response(json.dumps(d, cls=JsonEncoder, ensure_ascii=False), mimetype='application/json')
+
+
+
+    
+if __name__ == '__main__':
+    db = mysqlhelper.MySqLHelper()
+    app.run(host='127.0.0.1',port=8381)

+ 3 - 0
start.sh

@@ -0,0 +1,3 @@
+#!/bin/bash
+cd /root/mine/zkweb/
+/usr/bin/python3 /root/mine/zkweb/server.py

Разница между файлами не показана из-за своего большого размера
+ 1 - 0
static/jquery.min.js


Разница между файлами не показана из-за своего большого размера
+ 5 - 0
static/vue.js


Разница между файлами не показана из-за своего большого размера
+ 5 - 0
static/vue.min.js


+ 72 - 0
sync/sync.py

@@ -0,0 +1,72 @@
+import requests
+import json
+import pymysql
+import datetime
+
+# def getHeader():
+#     return {
+#         "User-Agent":"Mozilla/5.0 (Linux; Android 10; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/77.0.3865.120 MQQBrowser/6.2 TBS/045511 Mobile Safari/537.36 MMWEBID/1122 MicroMessenger/7.0.20.1781(0x27001439) Process/toolsmp WeChat/arm64 NetType/4G Language/zh_CN ABI/arm64"
+#     }
+
+
+db = pymysql.connect(host="localhost", user="root", password="password", db="zk")
+
+
+# json时间问题和bytes问题解决
+class JsonEncoder(json.JSONEncoder):
+    def default(self, obj):
+        if isinstance(obj,datetime.datetime):
+            return obj.strftime("%Y-%m-%d %H:%M:%S")
+        elif isinstance(obj,bytes):
+            return str(obj, 'utf-8')
+        else:
+            return json.JSONEncoder.default(self,obj)
+
+
+def start():
+    session = requests.session()
+    f_login(session)
+    
+def f_login(session):
+    session.post("https://sdjrzk.xuanyun.tech/api/auth/user/login/", {"username":"ZK_USER","password":"ZK_PASSWORD"})
+    f_getCourse(session)
+
+
+def f_getCourse(session):
+    r = session.get("https://sdjrzk.xuanyun.tech/api/course/course/?page_size=100")
+    data = json.loads(r.text)
+    res = data["results"]
+    cursor = db.cursor()
+    sql="call insert_course(%s)"
+    cursor.execute(sql, r.text)
+    db.commit()
+    #ids = []e
+    for i in res:
+        #ids.append(i["id"])
+        f_getQuestionList(session, str(i["id"]))
+
+def f_getQuestionList(session, id):
+    r = session.get("https://sdjrzk.xuanyun.tech/api/exam/paper/?owner_type=50&relation_limit=course.course&page_size=300&owner_id="+id)
+    data = json.loads(r.text)
+    res = data["results"]
+    cursor = db.cursor()
+    sql="call insert_question_list(%s, %s)"
+    cursor.execute(sql, (id, r.text))
+    db.commit()
+    for i in res:
+        f_getQuestionContent(session, str(i["id"]))
+    
+def f_getQuestionContent(session, id):
+    r = session.get("https://sdjrzk.xuanyun.tech/api/exam/paper/"+id+"/")
+    print(id)
+    data = json.loads(r.text)
+    content = data["content"]
+    content_object = json.dumps(data["content_object"], cls=JsonEncoder, ensure_ascii=False)
+    cursor = db.cursor()
+    sql="call insert_question_content(%s, %s, %s)"
+    cursor.execute(sql, (id, content, content_object))
+    db.commit()
+    # print(r.text)
+
+if __name__ == '__main__':
+    start()

+ 199 - 0
zk.sql

@@ -0,0 +1,199 @@
+-- MySQL dump 10.14  Distrib 5.5.68-MariaDB, for Linux (x86_64)
+--
+-- Host: localhost    Database: zk
+-- ------------------------------------------------------
+-- Server version	5.5.68-MariaDB
+
+/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */;
+/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */;
+/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */;
+/*!40101 SET NAMES utf8 */;
+/*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */;
+/*!40103 SET TIME_ZONE='+00:00' */;
+/*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */;
+/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */;
+/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */;
+/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */;
+
+--
+-- Table structure for table `course`
+--
+
+DROP TABLE IF EXISTS `course`;
+/*!40101 SET @saved_cs_client     = @@character_set_client */;
+/*!40101 SET character_set_client = utf8 */;
+CREATE TABLE `course` (
+  `time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
+  `data` text,
+  PRIMARY KEY (`time`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
+/*!40101 SET character_set_client = @saved_cs_client */;
+
+--
+-- Dumping data for table `course`
+--
+
+LOCK TABLES `course` WRITE;
+/*!40000 ALTER TABLE `course` DISABLE KEYS */;
+/*!40000 ALTER TABLE `course` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Table structure for table `question_content`
+--
+
+DROP TABLE IF EXISTS `question_content`;
+/*!40101 SET @saved_cs_client     = @@character_set_client */;
+/*!40101 SET character_set_client = utf8 */;
+CREATE TABLE `question_content` (
+  `question_id` int(11) NOT NULL,
+  `content` text,
+  PRIMARY KEY (`question_id`) USING BTREE
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
+/*!40101 SET character_set_client = @saved_cs_client */;
+
+--
+-- Dumping data for table `question_content`
+--
+
+LOCK TABLES `question_content` WRITE;
+/*!40000 ALTER TABLE `question_content` DISABLE KEYS */;
+/*!40000 ALTER TABLE `question_content` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Table structure for table `question_content_object`
+--
+
+DROP TABLE IF EXISTS `question_content_object`;
+/*!40101 SET @saved_cs_client     = @@character_set_client */;
+/*!40101 SET character_set_client = utf8 */;
+CREATE TABLE `question_content_object` (
+  `question_id` int(11) NOT NULL,
+  `content_object` text,
+  PRIMARY KEY (`question_id`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
+/*!40101 SET character_set_client = @saved_cs_client */;
+
+--
+-- Dumping data for table `question_content_object`
+--
+
+LOCK TABLES `question_content_object` WRITE;
+/*!40000 ALTER TABLE `question_content_object` DISABLE KEYS */;
+/*!40000 ALTER TABLE `question_content_object` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Table structure for table `question_list`
+--
+
+DROP TABLE IF EXISTS `question_list`;
+/*!40101 SET @saved_cs_client     = @@character_set_client */;
+/*!40101 SET character_set_client = utf8 */;
+CREATE TABLE `question_list` (
+  `course_id` int(11) NOT NULL,
+  `data` text,
+  PRIMARY KEY (`course_id`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
+/*!40101 SET character_set_client = @saved_cs_client */;
+
+--
+-- Dumping data for table `question_list`
+--
+
+LOCK TABLES `question_list` WRITE;
+/*!40000 ALTER TABLE `question_list` DISABLE KEYS */;
+/*!40000 ALTER TABLE `question_list` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Dumping routines for database 'zk'
+--
+/*!50003 DROP PROCEDURE IF EXISTS `insert_course` */;
+/*!50003 SET @saved_cs_client      = @@character_set_client */ ;
+/*!50003 SET @saved_cs_results     = @@character_set_results */ ;
+/*!50003 SET @saved_col_connection = @@collation_connection */ ;
+/*!50003 SET character_set_client  = utf8mb4 */ ;
+/*!50003 SET character_set_results = utf8mb4 */ ;
+/*!50003 SET collation_connection  = utf8mb4_general_ci */ ;
+/*!50003 SET @saved_sql_mode       = @@sql_mode */ ;
+/*!50003 SET sql_mode              = '' */ ;
+DELIMITER ;;
+CREATE DEFINER=`root`@`%` PROCEDURE `insert_course`(d text)
+BEGIN
+	
+	delete from `course`;
+	insert into `course` values (now(), d);
+END ;;
+DELIMITER ;
+/*!50003 SET sql_mode              = @saved_sql_mode */ ;
+/*!50003 SET character_set_client  = @saved_cs_client */ ;
+/*!50003 SET character_set_results = @saved_cs_results */ ;
+/*!50003 SET collation_connection  = @saved_col_connection */ ;
+/*!50003 DROP PROCEDURE IF EXISTS `insert_question_content` */;
+/*!50003 SET @saved_cs_client      = @@character_set_client */ ;
+/*!50003 SET @saved_cs_results     = @@character_set_results */ ;
+/*!50003 SET @saved_col_connection = @@collation_connection */ ;
+/*!50003 SET character_set_client  = utf8mb4 */ ;
+/*!50003 SET character_set_results = utf8mb4 */ ;
+/*!50003 SET collation_connection  = utf8mb4_general_ci */ ;
+/*!50003 SET @saved_sql_mode       = @@sql_mode */ ;
+/*!50003 SET sql_mode              = '' */ ;
+DELIMITER ;;
+CREATE DEFINER=`root`@`%` PROCEDURE `insert_question_content`(id int, d text, co text)
+BEGIN
+	
+	if not EXISTS(SELECT 1 FROM `question_content` where `question_id` = id) THEN
+		insert into `question_content` values (id, d);
+	ELSE
+		update `question_content` set `content` = d where `question_id` = id;
+	END IF;
+	
+	if not EXISTS(SELECT 1 FROM `question_content_object` where `question_id` = id) THEN
+		insert into `question_content_object` values (id, co);
+	ELSE
+		update `question_content_object` set `content_object` = co where `question_id` = id;
+	END IF;
+	
+END ;;
+DELIMITER ;
+/*!50003 SET sql_mode              = @saved_sql_mode */ ;
+/*!50003 SET character_set_client  = @saved_cs_client */ ;
+/*!50003 SET character_set_results = @saved_cs_results */ ;
+/*!50003 SET collation_connection  = @saved_col_connection */ ;
+/*!50003 DROP PROCEDURE IF EXISTS `insert_question_list` */;
+/*!50003 SET @saved_cs_client      = @@character_set_client */ ;
+/*!50003 SET @saved_cs_results     = @@character_set_results */ ;
+/*!50003 SET @saved_col_connection = @@collation_connection */ ;
+/*!50003 SET character_set_client  = utf8mb4 */ ;
+/*!50003 SET character_set_results = utf8mb4 */ ;
+/*!50003 SET collation_connection  = utf8mb4_general_ci */ ;
+/*!50003 SET @saved_sql_mode       = @@sql_mode */ ;
+/*!50003 SET sql_mode              = '' */ ;
+DELIMITER ;;
+CREATE DEFINER=`root`@`%` PROCEDURE `insert_question_list`(id int, d text)
+BEGIN
+	
+	if not EXISTS(SELECT 1 FROM `question_list` where `course_id` = id) THEN
+		insert into `question_list` values (id, d);
+	ELSE
+		update `question_list` set `data` = d where `course_id` = id;
+	END IF;
+END ;;
+DELIMITER ;
+/*!50003 SET sql_mode              = @saved_sql_mode */ ;
+/*!50003 SET character_set_client  = @saved_cs_client */ ;
+/*!50003 SET character_set_results = @saved_cs_results */ ;
+/*!50003 SET collation_connection  = @saved_col_connection */ ;
+/*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */;
+
+/*!40101 SET SQL_MODE=@OLD_SQL_MODE */;
+/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */;
+/*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */;
+/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */;
+/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */;
+/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */;
+/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */;
+
+-- Dump completed on 2021-07-25 23:00:31

Некоторые файлы не были показаны из-за большого количества измененных файлов