举例讲解Python的Tornado框架实现数据可视化的教程


Posted in Python onMay 02, 2015

所用拓展模块

    xlrd:

    Python语言中,读取Excel的扩展工具。可以实现指定表单、指定单元格的读取。
    使用前须安装。
    下载地址:https://pypi.python.org/pypi/xlrd
    解压后cd到解压目录,执行 python setup.py install 即可

    datetime:

    Python内置用于操作日期时间的模块

拟实现功能模块

    读xls文件并录入数据库

    根据年、月、日三个参数获取当天的值班情况

        饼状图(当天完成值班任务人数/当天未完成值班任务人数)

        瀑布图(当天所有值班人员的值班情况)

    根据年、月两个参数获取当月的值班情况

    根据年参数获取当年的值班情况

值班制度

    每天一共有6班:

    8:00 - 9:45
    9:45 - 11:20
    13:30 - 15:10
    15:10 - 17:00
    17:00 - 18:35
    19:00 - 22:00

    每个人每天最多值一班。

    仅值班时间及前后半个小时内打卡有效。

    上班、下班均须打卡,缺打卡则视为未值班。

分析Excel表格

我的指纹考勤机可以一次导出最多一个月的打卡记录。有一个问题是,这一个月可能横跨两个月,也可能横跨一年。比如:2015年03月21日-2015年04月20日、2014年12月15日-2015年01月05日。所以写处理方法的时候一定要注意这个坑。

导出的表格如图所示:

举例讲解Python的Tornado框架实现数据可视化的教程

    =。=看起来好像基本没人值班,对,就是这样。
    大家都好懒T。T
    Sign...

简单分析一下,

  •     考勤记录表是文件的第三个sheet
  •     第三行有起止时间
  •     第四行是所有日期的数字
  •     接下来每两行:第一行为用户信息;第二行为考勤记录

思路

决定用3个collection分别储存相关信息:

  1.     user:用户信息,包含id、name、dept
  2.     record:考勤记录,包含id(用户id)、y(年)、m(月)、d(日)、check(打卡记录)
  3.     duty:值班安排,包含id(星期数,例:1表示星期一)、list(值班人员id列表)、user_id:["start_time","end_time"](用户值班开始时间和结束时间)

读取xls文件,将新的考勤记录和新的用户存入数据库。

根据年月日参数查询对应record,查询当天的值班安排,匹配获得当天值班同学的考勤记录。将值班同学的打卡时间和值班时间比对,判断是否正常打卡,计算实际值班时长、实际值班百分比。

之后输出json格式数据,用echarts生成图表。

分析当月、当年的考勤记录同理,不过可能稍微复杂一些。

    所有的讲解和具体思路都放在源码注释里,请继续往下看源码吧~

源码

    main.py

#!/usr/bin/env python
# -*- coding: utf-8 -*-
import os.path

import tornado.auth
import tornado.escape
import tornado.httpserver
import tornado.ioloop
import tornado.options
import tornado.web
from tornado.options import define, options

import pymongo
import time
import datetime
import xlrd

define("port", default=8007, help="run on the given port", type=int)

class Application(tornado.web.Application):
  def __init__(self):
    handlers = [
      (r"/", MainHandler),
      (r"/read", ReadHandler),
      (r"/day", DayHandler),
    ]
    settings = dict(
      template_path=os.path.join(os.path.dirname(__file__), "templates"),
      static_path=os.path.join(os.path.dirname(__file__), "static"),
      debug=True,
      )
    conn = pymongo.Connection("localhost", 27017)
    self.db = conn["kaoqin"]
    tornado.web.Application.__init__(self, handlers, **settings)


class MainHandler(tornado.web.RequestHandler):
  def get(self):
    pass

class ReadHandler(tornado.web.RequestHandler):
  def get(self):
    #获取collection
    coll_record = self.application.db.record
    coll_user = self.application.db.user
    #读取excel表格
    table = xlrd.open_workbook('/Users/ant/Webdev/python/excel/data.xls')
    #读取打卡记录sheet
    sheet=table.sheet_by_index(2)
    #读取打卡月份范围
    row3 = sheet.row_values(2)
    m1 = int(row3[2][5:7])
    m2 = int(row3[2][18:20])
    #设置当前年份
    y = int(row3[2][0:4])
    #设置当前月份为第一个月份
    m = m1
    #读取打卡日期范围
    row4 = sheet.row_values(3)
    #初始化上一天
    lastday = row4[0]
    #遍历第四行中的日期
    for d in row4:
      #如果日期小于上一个日期
      #说明月份增大,则修改当前月份为第二个月份
      if d < lastday:
        m = m2
        #如果当前两个月份分别为12月和1月
        #说明跨年了,所以年份 +1
        if m1 == 12 and m2 == 1:
          y = y + 1
      #用n计数,范围为 3 到(总行数/2+1)
      #(总行数/2+1)- 3 = 总用户数
      #即遍历所有用户
      for n in range(3, sheet.nrows/2+1):
        #取该用户的第一行,即用户信息行
        row_1 = sheet.row_values(n*2-2)
        #获取用户id
        u_id = row_1[2]
        #获取用户姓名
        u_name = row_1[10]
        #获取用户部门
        u_dept = row_1[20]
        #查询该用户
        user = coll_user.find_one({"id":u_id})
        #如果数据库中不存在该用户则创建新用户
        if not user:
          user = dict()
          user['id'] = u_id
          user['name'] = u_name
          user['dept'] = u_dept
          coll_user.insert(user)
        #取该用户的第二行,即考勤记录行
        row_2 = sheet.row_values(n*2-1)
        #获取改当前日期的下标
        idx = row4.index(d)
        #获取当前用户当前日期的考勤记录
        check_data = row_2[idx]
        #初始化空考勤记录列表
        check = list()
        #5个字符一组,遍历考勤记录并存入考勤记录列表
        for i in range(0,len(check_data)/5):
          check.append(check_data[i*5:i*5+5])
        #查询当前用户当天记录
        record = coll_record.find_one({"y":y, "m":m, "d":d, "id":user['id']})
        #如果记录存在则更新记录
        if record:
          for item in check:
            #将新的考勤记录添加进之前的记录
            if item not in record['check']:
              record['check'].append(item)
              coll_record.save(record)
        #如果记录不存在则插入新纪录
        else:
          record = {"y":y, "m":m, "d":d, "id":user['id'], "check":check}
          coll_record.insert(record)

 

class DayHandler(tornado.web.RequestHandler):
  def get(self):
    #获取年月日参数
    y = self.get_argument("y",None)
    m = self.get_argument("m",None)
    d = self.get_argument("d",None)
    #判断参数是否设置齐全
    if y and m and d:
      #将参数转换为整型数,方便使用
      y = int(y)
      m = int(m)
      d = int(d)
      #获取当天所有记录
      coll_record = self.application.db.record
      record = coll_record.find({"y":y, "m":m, "d":d})
      #获取当天为星期几
      weekday = datetime.datetime(y,m,d).strftime("%w")
      #获取当天值班表
      coll_duty = self.application.db.duty
      duty = coll_duty.find_one({"id":int(weekday)})
      #初始化空目标记录(当天值班人员记录)
      target = list()
      #遍历当天所有记录
      for item in record:
        #当该记录的用户当天有值班任务时,计算并存入target数组
        if int(item['id']) in duty['list']:
          #通过用户id获取该用户值班起止时间
          start = duty[item['id']][0]
          end = duty[item['id']][1]
          #计算值班时长/秒
          date1 = datetime.datetime(y,m,d,int(start[:2]),int(start[-2:]))
          date2 = datetime.datetime(y,m,d,int(end[:2]),int(end[-2:]))
          item['length'] = (date2 - date1).seconds
          #初始化实际值班百分比
          item['per'] = 0
          #初始化上下班打卡时间
          item['start'] = 0
          item['end'] = 0
          #遍历该用户打卡记录
          for t in item['check']:
            #当比值班时间来得早
            if t < start:
              #计算时间差
              date1 = datetime.datetime(y,m,d,int(start[:2]),int(start[-2:]))
              date2 = datetime.datetime(y,m,d,int(t[:2]),int(t[-2:]))
              dif = (date1 - date2).seconds
              #当打卡时间在值班时间前半小时内
              if dif <= 1800:
                #上班打卡成功
                item['start'] = start
            elif t < end:
              #如果还没上班打卡
              if not item['start']:
                #则记录当前时间为上班打卡时间
                item['start'] = t
              else:
                #否则记录当前时间为下班打卡时间
                item['end'] = t
            else:
              #如果已经上班打卡
              if item['start']:
                #计算时间差
                date1 = datetime.datetime(y,m,d,int(end[:2]),int(end[-2:]))
                date2 = datetime.datetime(y,m,d,int(t[:2]),int(t[-2:]))
                dif = (date1 - date2).seconds
                #当打卡时间在值班时间后半小时内
                if dif <= 1800:
                  #下班打卡成功
                  item['end'] = end
          #当上班下班均打卡
          if item['start'] and item['end']:
            #计算实际值班时长
            date1 = datetime.datetime(y,m,d,int(item['start'][:2]),int(item['start'][-2:]))
            date2 = datetime.datetime(y,m,d,int(item['end'][:2]),int(item['end'][-2:]))
            dif = (date2 - date1).seconds
            #计算(实际值班时长/值班时长)百分比
            item['per'] = int(dif/float(item['length']) * 100)
          else:
            #未正常上下班则视为未值班
            item['start'] = 0
            item['end'] = 0
          #将记录添加到target数组中
          target.append(item)
      #输出数据
      self.render("index.html",
        target = target
        )


def main():
  tornado.options.parse_command_line()
  http_server = tornado.httpserver.HTTPServer(Application())
  http_server.listen(options.port)
  tornado.ioloop.IOLoop.instance().start()


if __name__ == "__main__":
  main()

  index.html

{
{% for item in target %}
  {
   'id':{{ item['id'] }},
   'start':{{ item['start'] }},
   'end':{{ item['end'] }},
   'length':{{ item['length'] }}, 
   'per':{{ item['per'] }}
   }
{% end %}
}

最后

暂时只写到读文件和查询某天值班情况,之后会继续按照之前的计划把这个小应用写完的。

因为涉及到一堆小伙伴的隐私,所以没有把测试文件发上来。不过如果有想实际运行看看的同学可以跟我说,我把文件发给你。

可能用到的一条数据库插入语句:db.duty.insert({"id":5,"list":[1,2],1:["19:00","22:00"],2:["19:00","22:00"]})

希望对像我一样的beginner们有帮助!

Python 相关文章推荐
python多线程编程中的join函数使用心得
Sep 02 Python
Python使用urllib模块的urlopen超时问题解决方法
Nov 08 Python
python通过pil模块获得图片exif信息的方法
Mar 16 Python
Python 编码规范(Google Python Style Guide)
May 05 Python
Python 查找list中的某个元素的所有的下标方法
Jun 27 Python
详解Python中is和==的区别
Mar 21 Python
Python基础之循环语句用法示例【for、while循环】
Mar 23 Python
详解python列表生成式和列表生成式器区别
Mar 27 Python
python3 小数位的四舍五入(用两种方法解决round 遇5不进)
Apr 11 Python
简单了解python中的与或非运算
Sep 18 Python
keras tensorflow 实现在python下多进程运行
Feb 06 Python
Python实现aes加密解密多种方法解析
May 15 Python
Python的Bottle框架中返回静态文件和JSON对象的方法
Apr 30 #Python
使用Python编写提取日志中的中文的脚本的方法
Apr 30 #Python
简单的连接MySQL与Python的Bottle框架的方法
Apr 30 #Python
Python的Bottle框架中实现最基本的get和post的方法的教程
Apr 30 #Python
Python中使用Beautiful Soup库的超详细教程
Apr 30 #Python
Python中正则表达式的详细教程
Apr 30 #Python
详解在Python程序中使用Cookie的教程
Apr 30 #Python
You might like
人大复印资料处理程序_补充篇
2006/10/09 PHP
php split汉字
2009/06/05 PHP
有关PHP性能优化的介绍
2013/06/20 PHP
php防止SQL注入详解及防范
2013/11/12 PHP
php中smarty实现多模版网站的方法
2015/06/11 PHP
baidu博客的编辑友情链接的新的层窗口!经典~支持【FF】
2007/02/09 Javascript
javascript时间差插件分享
2016/07/18 Javascript
jQuery+HTML5+CSS3制作支持响应式布局时间轴插件
2016/08/10 Javascript
Angular懒加载机制刷新后无法回退的快速解决方法
2016/08/30 Javascript
jQuery表单插件ajaxForm实例详解
2017/01/17 Javascript
用vue和node写的简易购物车实现
2017/04/25 Javascript
vue的事件绑定与方法详解
2017/08/16 Javascript
Angular 向组件传递模板的两种方法
2018/02/23 Javascript
react build 后打包发布总结
2018/08/24 Javascript
Layui 带多选框表格监听事件以及按钮自动点击写法实例
2019/09/02 Javascript
JavaScript判断浏览器版本的方法
2019/11/03 Javascript
openlayers4实现点动态扩散
2020/08/17 Javascript
[00:43]魔廷新尊——痛苦女王至宝捆绑包
2020/06/12 DOTA
python后端接收前端回传的文件方法
2019/01/02 Python
python对绑定事件的鼠标、按键的判断实例
2019/07/17 Python
python实现批量处理将图片粘贴到另一张图片上并保存
2019/12/12 Python
Python3-异步进程回调函数(callback())介绍
2020/05/02 Python
如何快速一次性卸载所有python包(第三方库)呢
2020/10/20 Python
纯css3无js实现的Android Logo(有简单动画)
2013/01/21 HTML / CSS
世界上最大的专业美容用品零售商:Sally Beauty
2017/07/02 全球购物
信息部岗位职责
2013/11/12 职场文书
乡领导班子四风问题对照检查材料
2014/09/25 职场文书
2015年乡镇扶贫工作总结
2015/04/08 职场文书
2015年市场部工作总结
2015/04/30 职场文书
迎新生欢迎词2015
2015/07/16 职场文书
《我要的是葫芦》教学反思
2016/02/18 职场文书
深入理解以DEBUG方式线程的底层运行原理
2021/06/21 Java/Android
MyBatis自定义SQL拦截器示例详解
2021/10/24 Java/Android
node快速搭建后台的实现步骤
2022/02/18 NodeJs
python中validators库的使用方法详解
2022/09/23 Python
td 内容自动换行 table表格td设置宽度后文字太多自动换行
2022/12/24 HTML / CSS