举例讲解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使用Tkinter显示网络图片的方法
Apr 24 Python
用yum安装MySQLdb模块的步骤方法
Dec 15 Python
python的变量与赋值详细分析
Nov 08 Python
Python实现嵌套列表去重方法示例
Dec 28 Python
Python爬虫之正则表达式基本用法实例分析
Aug 08 Python
使用11行Python代码盗取了室友的U盘内容
Oct 23 Python
PYTHON发送邮件YAGMAIL的简单实现解析
Oct 28 Python
Python下利用BeautifulSoup解析HTML的实现
Jan 17 Python
logging level级别介绍
Feb 21 Python
使用python处理题库表格并转化为word形式的实现
Apr 14 Python
vscode+PyQt5安装详解步骤
Aug 12 Python
Python中如何处理常见报错
Jan 18 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
php.ini中的php-5.2.0配置指令详解
2008/03/27 PHP
fleaphp下不确定的多条件查询的巧妙解决方法
2008/09/11 PHP
腾讯QQ php程序员面试题目整理
2010/06/08 PHP
解析PHP正则提取或替换img标记属性
2013/06/26 PHP
Thinkphp关闭缓存的方法
2015/06/26 PHP
PHP Header失效的原因分析及解决方法
2016/11/16 PHP
Zend Framework数据库操作技巧总结
2017/02/18 PHP
thinkPHP中U方法加密传递参数功能示例
2018/05/29 PHP
PHP实现获取url地址中顶级域名的方法示例
2019/06/05 PHP
(function(){})()的用法与优点
2007/03/11 Javascript
Prototype Function对象 学习
2009/07/12 Javascript
使用js解决由border属性引起的div宽度问题
2013/11/26 Javascript
js 去除字符串第一位逗号的方法
2014/06/07 Javascript
javascript 对象数组根据对象object key的值排序
2015/03/09 Javascript
Jquery EasyUI Datagrid右键菜单实现方法
2016/12/30 Javascript
用node-webkit把web应用打包成桌面应用(windows环境)
2018/02/01 Javascript
vue项目前端错误收集之sentry教程详解
2019/05/27 Javascript
三分钟教你用Node做一个微信哄女友(基友)神器(面向小白)
2019/06/21 Javascript
利用JS响应式修改vue实现页面的input值
2019/09/02 Javascript
用Python创建声明性迷你语言的教程
2015/04/13 Python
Python实现比较两个列表(list)范围
2015/06/12 Python
Python控制台输出时刷新当前行内容而不是输出新行的实现
2020/02/21 Python
CSS3实现全景图特效示例代码
2018/03/26 HTML / CSS
美国餐厅用品和厨房设备批发网站:KaTom Restaurant Supply
2018/01/27 全球购物
美国家居装饰和豪华家具购物网站:One Kings Lane
2018/12/24 全球购物
国际领先的在线时尚服装和配饰店:DressLily
2019/03/03 全球购物
奥林匹亚体育:Olympia Sports
2020/12/30 全球购物
美国最大的购物网站:Amazon.com(亚马逊美国)
2020/05/23 全球购物
会计电算化专业毕业生自荐信
2013/12/20 职场文书
自荐信需注意事项
2014/01/25 职场文书
上班打牌检讨书
2014/02/07 职场文书
党员学习正风肃纪思想汇报
2014/09/12 职场文书
标枪加油稿
2015/07/22 职场文书
python解决12306登录验证码的实现
2021/04/18 Python
Flask response响应的具体使用
2021/07/15 Python
python标准库ElementTree处理xml
2022/05/20 Python