Remember小助手

作者: 尽情的嘲笑我吧 | 来源:发表于2017-10-28 23:32 被阅读0次
    禅心 · 悟道

    前言

    Mac上有一个自带的备忘录,感觉还挺好用的。然后也想自己动手,做个类似的Remember小工具来玩一下。

    工具类型:胖服务端,瘦客户端的模式。大致的场景就是客户端只管把自己想让被提醒的事项发给服务器端,然后配合自己的本地扫描,对符合要求的memo进行弹框提醒。

    最近对Redis比较着迷一点,被其优雅高效的设计所打动。虽然对于搜索方面支持的不太好,但是搜索的话使用专业的搜索服务就好了。我个人比较崇尚Unix工具系的宗旨:一个工具只专注于做一件事,这也是Redis目前所体现的。

    服务器端

    对于服务器端的设计的初衷,是一个“胖胖的”,做了大部分的工作的形式,但是做着做着,发现客户端做的工作其实也蛮多的。目前服务器端的任务是:

    • 接受用户注册
    • 对memo支持CRUD
    • 对请求进行“安全甄选”

    目录结构

    ➜  server ll *.py
    -rw-r--r--  1 changba164  staff   346B Oct 28 14:53 datastructor.py # 常见数据结构bean
    -rw-r--r--  1 changba164  staff   2.6K Oct 28 18:22 redishelper.py  # redis操作相关的工具类
    -rw-r--r--  1 changba164  staff   4.6K Oct 28 18:42 server.py   # 对客户端提供服务支持
    

    代码比较简单,下面简单来看下具体的内容。

    datastructor.py

    #!/usr/bin python
    # coding: utf8
    # file: .py
    
    import sys
    
    reload(sys)
    sys.setdefaultencoding('utf8')
    import json
    
    class ResponseCode(object):
        """
        服务响应码
        """
        def __init__(self, code, msg):
            self.code = code
            self.msg = msg
    
        def getcode(self):
            return json.dumps({"code":self.code, "msg":self.msg})
    

    redishelper.py

    #!/usr/bin python
    # coding: utf8
    # file: .py
    
    import sys
    
    reload(sys)
    sys.setdefaultencoding('utf8')
    
    import redis
    import hashlib
    import uuid
    import time
    
    class RedisHelper(object):
        """
        redis操作相关的工具类。
        """
        def __init__(self):
            self.rs = redis.Redis(host="localhost", port=6379, db=3, encoding="utf8", charset="utf8")
            # 相关key
            self.uids = "userids:set"
            self.rank = "unfinished:zset:"
            self.unfinished = "unfinished:hash:"
            self.finished = "finished:hash:"
    
        def check_request_valid(self, uid, securitycode):
            if uid is None:
                return False
            if securitycode == hashlib.md5(uid).hexdigest():
                return True
            return False
    
        def register(self, uid):
            if uid is None:
                return False
            self.rs.sadd(self.uids, uid)
            return True
    
        def check_user(self, uid):
            if uid is None:
                return False
            return True if self.rs.sismember(self.uids, uid) else False
    
        def add_memo(self, uid, memoid, memo=""):
            if uid is None:
                return False
            memoid = memoid
            self.rs.sadd(str(uid), memoid)
            self.rs.hset(self.unfinished+str(uid), memoid, memo)
            self.rs.zadd(self.rank+str(uid), memoid, int(memoid))
    
    
        def update_memo(self, uid, memoid, memo):
            if uid is None:
                return False
            if not self.rs.sismember(str(uid), memoid):
                return False
            self.rs.hset(self.unfinished+str(uid), memoid, memo)
            return True
    
        def delete_memo(self, uid, memoid):
            if uid is None:
                return False
            memo = self.rs.hget(self.unfinished+str(uid), memoid)
            self.rs.hset(self.finished+str(uid), memoid, memo)
            self.rs.zrem(self.rank+str(uid), memoid)
            return True
    
        def get_memo(self, uid, memoid):
            if uid is None:
                return None
            return self.rs.hget(self.unfinished+str(uid), memoid)
    
        def get_memos(self, uid, reverse=True):
            if uid is None:
                return None
            memoids = self.get_memoids(uid, reverse)
            print memoids
            memos = []
            for item in memoids:
                memos.append(self.rs.hget(self.unfinished+str(uid), item[0]))
            return memos
    
        def get_memoids(self, uid, reverse=True):
            if uid is None:
                return []
            if reverse == True:
                return [item[0] for item in self.rs.zrevrange(self.rank+str(uid), 0, -1, withscores=True)]
            else:
                return [item[0] for item in self.rs.zrange(self.rank+str(uid), 0, -1, withscores=True)]
    
    

    server.py

    #!/usr/bin python
    # coding: utf8
    # file: .py
    
    import sys
    
    reload(sys)
    sys.setdefaultencoding('utf8')
    from flask import Flask, request
    import redishelper
    import datastructor
    import logging
    import json
    
    logging.basicConfig(
        level=logging.INFO,
        format='%(asctime)s:%(levelname)s %(message)s',
        datafmt="%Y-%m-%d %H:%i:%s",
        filename="/Users/changba164/guo/tools/remeber/server/memo-server.log",
        filemode="a"
    )
    
    app = Flask(__name__)
    helper = redishelper.RedisHelper()
    
    
    @app.route("/", methods=['GET', 'POST'])
    def home():
        return "It works!"
    
    @app.route("/register", methods=["POST"])
    def register():
        uid = request.form["uid"]
        helper.register(uid)
        res = datastructor.ResponseCode(1000, "uid={}".format(uid))
        return res.getcode()
    
    @app.route("/add", methods=["POST"])
    def add():
        uid = request.form['uid']
        securitycode = request.form['securitycode']
        memoid = request.form['memoid']
        memo = request.form['memo']
    
        isuserexists = helper.check_user(uid)
        if isuserexists == False:
            return datastructor.ResponseCode(1001, "该用户还未注册哟!").getcode()
        isvalid = helper.check_request_valid(uid, securitycode)
        logging.info("{}: {}".format(uid, securitycode))
        if isvalid == False:
            return datastructor.ResponseCode(1002, "身份信息不合法!").getcode()
        helper.add_memo(uid, memoid, memo)
        return datastructor.ResponseCode(1000, "memo已经保存啦!").getcode()
    
    @app.route("/update", methods=["POST"])
    def update():
        uid = request.form['uid']
        securitycode = request.form['securitycode']
        memoid = request.form['memoid']
        memo = request.form['memo']
    
        isuserexists = helper.check_user(uid)
        if isuserexists == False:
            return datastructor.ResponseCode(1001, "该用户还未注册哟!").getcode()
        isvalid = helper.check_request_valid(uid, securitycode)
        if isvalid == False:
            return datastructor.ResponseCode(1002, "身份信息不合法!").getcode()
        helper.update_memo(uid, memoid, memo)
        return datastructor.ResponseCode(1000, "memo已经更新啦!").getcode()
    
    @app.route("/delete", methods=["POST"])
    def deletememo():
        uid = request.form['uid']
        securitycode = request.form['securitycode']
        memoid = request.form['memoid']
    
        isuserexists = helper.check_user(uid)
        if isuserexists == False:
            return datastructor.ResponseCode(1001, "该用户还未注册哟!").getcode()
        isvalid = helper.check_request_valid(uid, securitycode)
        if isvalid == False:
            return datastructor.ResponseCode(1002, "身份信息不合法!").getcode()
        helper.delete_memo(uid, memoid)
        return datastructor.ResponseCode(1000, "memo已经删除/完成啦!").getcode()
    
    @app.route("/detail", methods=["POST"])
    def detail():
        uid = request.form['uid']
        securitycode = request.form['securitycode']
        memoid = request.form['memoid']
    
        isuserexists = helper.check_user(uid)
        if isuserexists == False:
            return datastructor.ResponseCode(1001, "该用户还未注册哟!").getcode()
        isvalid = helper.check_request_valid(uid, securitycode)
        if isvalid == False:
            return datastructor.ResponseCode(1002, "身份信息不合法!").getcode()
        detail = helper.get_memo(uid, memoid)
        return datastructor.ResponseCode(1000, detail).getcode()
    
    @app.route("/lists", methods=['POST'])
    def lists():
        uid = request.form['uid']
        securitycode = request.form['securitycode']
        reverse = request.form['reverse']
        isuserexists = helper.check_user(uid)
        if isuserexists == False:
            return datastructor.ResponseCode(1001, "该用户还未注册哟!").getcode()
        isvalid = helper.check_request_valid(uid, securitycode)
        if isvalid == False:
            return datastructor.ResponseCode(1002, "身份信息不合法!").getcode()
        memos = helper.get_memos(uid, reverse)
        res = datastructor.ResponseCode(1000, json.dumps((memos))).getcode()
        return res
    
    @app.route("/recent", methods=['POST'])
    def recentids():
        uid = request.form['uid']
        securitycode = request.form['securitycode']
        isuserexists = helper.check_user(uid)
        if isuserexists == False:
            return datastructor.ResponseCode(1001, "该用户还未注册哟!").getcode()
        isvalid = helper.check_request_valid(uid, securitycode)
        if isvalid == False:
            return datastructor.ResponseCode(1002, "身份信息不合法!").getcode()
        memoid = helper.get_memoids(uid, False)[0]
        result = {
            "memoid": memoid,
            "memo": helper.get_memo(uid, memoid)
        }
        res = datastructor.ResponseCode(1000, json.dumps((result))).getcode()
        return res
    
    
    if __name__ == '__main__':
        app.run(host='localhost', port=9999, debug=True)
    
    

    “瘦客户端”

    客户端所要做的无非是:新增memo,获得最需要处理的memo,并进行弹窗提示等。目前客户端的任务还不算完成,也就简单实现了下。

    ➜  client ls
    testutils.py utils.py
    ➜  client
    

    utils.py

    #!/usr/bin python
    # coding: utf8
    # file: .py
    
    import sys
    
    reload(sys)
    sys.setdefaultencoding('utf8')
    
    import requests
    import json
    import hashlib
    import time
    import datetime
    import tkMessageBox
    from Tkinter import *
    
    def get_security_code(uid):
        return hashlib.md5(uid).hexdigest()
    
    def time_to_stamp(seed):
        """
        将白话点的时间转换为时间戳,格式为: 00:00:00:00 天:小时:分钟:秒
        """
        curtime = int(time.time())
        d, h, m, s = (int(item) for item in str(seed).split(":"))
        targettime = curtime + (d*86400)+ (h*3600)+(m*60)+s
        return targettime
    
    def stamp_to_time(stamp):
        return datetime.datetime.utcfromtimestamp(float(stamp))
    
    def make_dialog(timestr, msg):
        tkMessageBox.showinfo(title=timestr, message=msg)
        # root = Tk()
        # root.title(timestr)
        # frame = Frame(root)
        # Label(frame, text=msg).pack()
        # frame.pack(side=TOP)
        # root.mainloop()
    
    class UserService(object):
        """
        注册用户
        """
        def __init__(self):
            pass
    
        @staticmethod
        def register(uid):
            if uid is None:
                return False
            url = "http://localhost:9999/register"
            response = requests.post(url, data={"uid": uid})
            if response.status_code == 200 and response.json()['code'] == 1000:
                return True
            return False
    
    
    
    
    class MemoHelper(object):
        """
        客户端memo工具类
        """
        def __init__(self):
            self.url = "http://localhost:9999/{}"
    
        def post(self, uid, memoid, memo):
            posturl = self.url.format("add")
            payload = {
                "uid": uid,
                "securitycode": get_security_code(uid),
                "memoid": memoid,
                "memo": memo
            }
            response = requests.post(posturl, data=payload)
            return response.text
        def getmemo(self, uid, memoid):
            url = self.url.format("detail")
            payload = {
                "uid": uid,
                "securitycode": get_security_code(uid),
                "memoid": memoid
            }
            response = requests.post(url, data=payload)
            return response.text
        def getmemos(self, uid, reverse=True):
            url = self.url.format("lists")
            payload = {
                "uid": uid,
                "securitycode": get_security_code(uid),
                "reverse": reverse
            }
            response = requests.post(url, data=payload)
            return response.text
        def getrecent(self, uid):
            url = self.url.format("recent")
            payload = {
                "uid": uid,
                "securitycode": get_security_code(uid)
            }
            response = requests.post(url, data=payload)
            return response.text
        def updatememo(self, uid, memoid, memo):
            url = self.url.format("update")
            payload = {
                "uid": uid,
                "securitycode": get_security_code(uid),
                "memoid": memoid,
                "memo": memo
            }
            response = requests.post(url, data=payload)
            return response.text
        def deletememo(self, uid, memoid):
            url = self.url.format("delete")
            payload = {
                "uid": uid,
                "securitycode": get_security_code(uid),
                "memoid": memoid
            }
            response = requests.post(url, data=payload)
            return response.text
    
    
    class Emitter(object):
        """
        检测到到期任务,则弹出一个提醒框!
        """
        def __init__(self, uid):
            self.uid = str(uid)
            self.memohelper = MemoHelper()
    
        def emit_in(self, timestr, memo):
            timestamp = time_to_stamp(timestr)
            self.memohelper.post(self.uid, timestamp, memo)
    
    
    
        def emit_out(self):
            # 如果时间符合要求就emit出来, 找出距离时间最远的memo。
            data = self.memohelper.getrecent(self.uid)
            data = json.loads(data)
            data = data['msg']
            data = json.loads(data)
            targettime = stamp_to_time(data['memoid'])
            memo = data['memo']
            make_dialog(targettime, memo)
    
    
    if __name__ == '__main__':
    
        emitter = Emitter(201393260)
        emitter.emit_out()
    
    

    总结

    总体来说,这个小工具没有达到我的预期效果,大致有如下几点:

    • 扫描服务可以说没怎么做。
    • “安全”这里简单就是限制客户端请求方式为POST,简单的使用securitycode做了下判断(如果被抓包分析的话,那也没什么用,比较好的做法就是模仿Flask使用PIN码,来进一步提高安全性)。
    • 客户端弹框效果不是很理想,而且对于发布一个POST而言,还没做对应的GUI支持,目前也就在命令行里面使用类似:

    emitin xxxxxxx a:b:c:d
    大致的意思是: 记录 在a天b小时c分钟d秒 后要做的xxxxxxxx事项。

    有兴趣的可以在目前的代码基础上进行优化,这里权当抛砖引玉吧。

    相关文章

      网友评论

        本文标题:Remember小助手

        本文链接:https://www.haomeiwen.com/subject/xfdepxtx.html