Tornado笔记——用Tornado搭建假单统计考勤系统(六)

这篇和上一篇博文隔的时间有点远了,希望大家还记得我们这个系统之前都做了什么。在上一篇博客中,我们构造了一个复杂的表单和calendar类来实现填写考勤的功能。现在,我们要实现查看考勤以及审批考勤的功能。

6 项目目录结构的调整

现在我们的项目已经有了很多的RequestHandler,把这么多RequestHandler都放在一个main.py文件里会很不好管理,因此,我们需要对项目的结构做一点调整,把这些RequestHandler按功能放到不同的目录里。

调整后的目录结构如下:

 我们主要调整server目录的结构:在server下新建一个名为apps的package,再在apps包中新建timesheet_app和user_app这两个package,分别存放user和timesheet相关功能的RequestHandler。在timesheet_app包中新建timesheet_app.py,把如下RequestHandler从main.py中挪进去:

timesheet_app中的RequestHandler
RequestHandler名称对应功能
TimeSheetIndex考勤
FillTimeSheet填写考勤

在user_app包中新建user_app.py,把如下RequestHandler从main.py中挪进去:

user_app中的RequestHandler
RequestHandler名称对应功能
Register注册用户
Login用户登录
CreateUserGroup创建用户组
ViewUserGroup *查看用户组
PersonalInfo *个人信息查看
UserManage *用户管理
Approve *批准用户
LogOff *注销用户
LogOut *用户登出

翻了翻前面的博客,发现带*的功能还没有写到博客中,等我们将考勤部分写的差不多了再来将这些功能补全。 

 最后,在apps包下建立basehandler.py,将我们的BaseHandler放进去,大家可以自行补全timesheet_app.py、user_app.py和basehandler.py中的import部分。

这样,我们的目录结构就调整完毕,main.py会变的简洁很多,新的main.py代码如下所示:

# server/main.py
import sys
import platform
import os
sys.path.append(os.path.abspath('..'))
from setting.globalsettings import getconfig
from util.users.userutil import hasinit, inituser
import tornado.ioloop
import tornado.web
from server.apps.user_app.user_app import *
from server.apps.timesheet_app.timesheet_app import *
if platform.system() == 'Windows':
    import asyncio
    asyncio.set_event_loop_policy(asyncio.WindowsSelectorEventLoopPolicy())


class Index(BaseHandler):
    def get(self):
        indexpath = gettemplatepath('index.html')
        self.render(indexpath)

def make_app():
    routelist = [
        # 保持不变
    ]
    return tornado.web.Application(routelist,cookie_secret='12f6352#527',autoreload=True,debug=True,template_path='D:\\LeaveManage\\server\\template')

if __name__ == '__main__':
    init_result = hasinit()
    if init_result == False:
        result = inituser()
        if result == 'Fail':
            print('初始化网站失败!')
            exit(0)
    app = make_app()
    port = int(getconfig('PORT'))
    app.listen(port)
    print('系统启动')
    tornado.ioloop.IOLoop.current().start()

这里注意,当我们建立Application时,我们要指定template_path到我们的template目录,否则在调整完目录结构后,tornado不会找到这个目录而是按RequestHandler所在目录去寻找模板文件,那样肯定找不到。

现在,我们可以继续往下进行了,实现查看考勤以及批准考勤的功能。

7 创建考勤事件

在实现查看考勤之前,让我们先实现另一个小功能——创建考勤事件。通过这个功能,可以让我们添加不同的考勤事件,如工作、病假、调休等,以供在之前的填写考勤功能中选择。

该功能的页面如下:

这个页面十分简单, 上半部分列出现有的考勤事件,下半部分的表单用于建立新的表单事件。事件名即上半部分的事件代码,是事件真正存在DB中的值,而事件别名是用于查看考勤时显示出的事件名称。

我们在timesheetutil.py中编写createtimesheetevent函数,用于生成考勤事件:

# timesheetutil.py
# ...
from database.tbltimesheetevent import TimeSheetEvent
def createtimesheetevent(eventcode,nickname):
    timesheetevent = session.query(TimeSheetEvent).filter(TimeSheetEvent.eventcode == eventcode)
    result = 'Fail'
    if type(timesheetevent) is not TimeSheetEvent:
        newtimesheetevent = TimeSheetEvent(eventcode=eventcode,nickname=nickname)
        result = insertdata(newtimesheetevent)
    return result

这个小函数没啥说的,根据eventcode查一下是否有考勤事件存在,如果没有的话则创建之。

然后,我们在timesheet_app.py实现CreateTimeSheetEvent RequestHandler:

# timesheet_app.py

class CreateTimeSheetEvent(BaseHandler):
    def get(self):
        timesheeteventpath = gettemplatepath('createtimesheetevent.html')
        timesheetevents = session.query(TimeSheetEvent)

        self.render(timesheeteventpath,timesheetevents=timesheetevents)

    def post(self):
        eventcode = self.get_argument('eventcode')
        eventnickname = self.get_argument('eventnickname')
        result = createtimesheetevent(eventcode,eventnickname)
        resultpath = gettemplatepath('result.html')
        if result == 'Fail':
            self.render(resultpath,result=result)
        else:
            self.redirect('/createtimesheetevent')

# main.py
# ...
routelist = [
    # ...
    (r"/createtimesheetevent",CreateTimeSheetEvent),
    # ...
]
# ...

这个页面也没什么说的,常规的抓取数据后显示/插入,不要忘了在main.py中注册其路由。

前端页面createtimesheetevent.html的block块代码如下:

<!--createtimesheetevent.html-->

        {% block content %}
        <div class="page-wrapper">
            <!-- ============================================================== -->
            <!-- Container fluid  -->
            <!-- ============================================================== -->
            <div class="container-fluid">
                <!-- ============================================================== -->
                <!-- Bread crumb and right sidebar toggle -->
                <!-- ============================================================== -->
                <div class="row page-titles">
                    <div class="col-md-6 col-8 align-self-center">
                        <h3 class="text-themecolor m-b-0 m-t-0">创建考勤事件</h3>
                        <ol class="breadcrumb">
                            <li class="breadcrumb-item"><a href="/">Home</a></li>
                            <li class="breadcrumb-item active">创建考勤事件</li>
                        </ol>
                    </div>
                </div>
                <!-- ============================================================== -->
                <!-- End Bread crumb and right sidebar toggle -->
                <!-- ============================================================== -->
                <!-- ============================================================== -->
                <!-- Start Page Content -->
                <!-- ============================================================== -->
                <!-- Row -->
                <div class="row">
                    <!-- Column -->
                    <div class="col-lg-8 col-xlg-9 col-md-7">
                        <div class="card">
                            <div class="card-block">
                                <h4 class="card-title">当前考勤事件</h4>
                                <div class="table-responsive">
                                    <table class="table">
                                        <thead>
                                            <tr>
                                                <th>#</th>
                                                <th>事件代码</th>
                                                <th>事件别名</th>
                                            </tr>
                                        </thead>
                                        <tbody>
                                            {% for event in timesheetevents %}
                                            <tr>
                                                <td>{{ event.id }}</td>
                                                <td>{{ escape(event.eventcode) }}</td>
                                                <td>{{ escape(event.nickname) }}</td>
                                            </tr>
                                            {% end %}
                                        </tbody>
                                    </table>
                                </div>
                            </div>
                            <div class="card-block">
                                <form class="form-horizontal form-material" action="/createtimesheetevent" method="post" >
                                    <div class="form-group">
                                        <label class="col-md-12">事件名 *</label>
                                        <div class="col-md-12">
                                            <input type="text" class="form-control form-control-line" required=true name="eventcode" id="eventcode">
                                        </div>
                                        <label class="col-md-12">别名 *</label>
                                        <div class="col-md-12">
                                            <input type="text" class="form-control form-control-line" required=true name="eventnickname" id="eventnickname">
                                        </div>
                                    </div>
                                    <div class="form-group">
                                        <div class="col-sm-12">
                                            <button type="submit" class="btn btn-success">创建</button>
                                        </div>
                                    </div>
                                </form>
                            </div>
                        </div>
                    </div>
                    <!-- Column -->
                </div>
                <!-- Row -->
                <!-- ============================================================== -->
                <!-- End PAge Content -->
                <!-- ============================================================== -->
                </div>
            <!-- ============================================================== -->
            <!-- End Container fluid  -->
            <!-- ============================================================== -->
            <!-- ============================================================== -->
            <!-- footer -->
            <!-- ============================================================== -->
            <footer class="footer text-center">
                © 2020 Tornado考勤系统
            </footer>
            <!-- ============================================================== -->
            <!-- End footer -->
            <!-- ============================================================== -->
        </div>
        {% end %}

在实现了这个功能后,我们就可以按照上图所示将我们的考勤事件建立起来,以便在之后的查看考勤功能中使用。

8 查看考勤

在准备好了考勤事件后,我们就可以实现查看考勤功能了。我们需要实现一个TimeSheetViewer类,这个类会返回一个key为日期value为事件别名的字典,与之前的Calendar类配合可以让我们查看用户填写的考勤结果。

打开timesheetutil.py,实现TimeSheetViewer类:

# timesheetutil.py

class TimeSheetViewer:
    def __init__(self,username,year,month):
        self.__username = username
        self.__year = year
        self.__month = month
        self.__timesheetmap = {}
        self.__state = ''
        self.__approveuser = ''
        self.__approvedate = ''

    def __gettimesheetinfo(self):
        timesheet = session.query(TimeSheet).filter(and_(TimeSheet.username == self.__username,TimeSheet.year == self.__year, TimeSheet.month == self.__month)).first()
        timecalendar = TimeSheetCalendar(self.__year,self.__month)
        timecalendar.generatecalendar()
        monthmap = timecalendar.getmonthmap()
        print(type(timesheet))
        if type(timesheet) is TimeSheet:
            self.__state = timesheet.state
            self.__approveuser = timesheet.approveusername
            self.__approvedate = timesheet.approvedate
            for days in monthmap:
                tmpday = int(days.split('-')[2])
                tmpmonth = int(days.split('-')[1])
                dayinfo = 'day' + str(tmpday)
                if tmpmonth == self.__month:
                    event = session.query(TimeSheetEvent).filter(TimeSheetEvent.eventcode == getattr(timesheet,dayinfo,'')).first()
                    if type(event) is TimeSheetEvent:
                        self.__timesheetmap[days] = event.nickname
                    else:
                        self.__timesheetmap[days] = 'N/A'
                else:
                    self.__timesheetmap[days] = 'N/A'
        else:
            for days in monthmap:
                self.__timesheetmap[days] = 'N/A'

    def gettimesheetmap(self):
        self.__gettimesheetinfo()
        return self.__timesheetmap

    def getstate(self):
        return self.__state

    def getapproveuser(self):
        return self.__approveuser

    def getapprovedate(self):
        return self.__approvedate

先看看这个类中的参数介绍:

  • __username:用户名
  • __year:年
  • __month:月
  • __timesheetmap:最终返回的结果,key为日期value为事件别名的字典
  • __state:考勤表状态
  • __approveuser:批准的用户
  • __approvedate:批准日期

这个类的核心函数为__gettimesheetinfo。在这个函数中,首先通过year和month参数从TimeSheet表中拿到此月的数据,再调用之前的Calendar类获得此月的monthmap。然后,从TimeSheet中获得考勤表的状态、批准的用户以及批准日期,再按日期去获得每天的考勤事件;如果某个日期的考勤事件不存在,则用N/A代替。这样,我们就得到了一个key为日期value为事件别名的字典,以及拿到了考勤表的状态、批准用户以及批准日期。底下的几个getter函数就不再赘述了。

我们打开timesheet_app.py,实现ViewTimeSheet:

# timesheet_app.py

class ViewTimeSheet(BaseHandler):
    def get(self,year,month):
        username = ''
        bytes_user = self.get_secure_cookie('currentuser')
        if type(bytes_user) is bytes:
            username = str(bytes_user, encoding='utf-8')
        year = int(year.split('=')[1])
        month = int(month.split('=')[1])
        timesheetviewer = TimeSheetViewer(username=username,year=year,month=month)
        timesheetviewer.gettimesheetmap()
        timesheetcalendar = TimeSheetCalendar(year, month)
        timesheetcalendar.generatecalendar()
        monthday_map = timesheetcalendar.getmonthmap()
        week_list = timesheetcalendar.getweeklist()
        timesheet_map = timesheetviewer.gettimesheetmap()
        timesheet_state = timesheetviewer.getstate()
        timesheet_approveuser = timesheetviewer.getapproveuser()
        timesheet_approvedate = timesheetviewer.getapprovedate()
        viewtimesheetpath = gettemplatepath('viewtimesheet.html')
        self.render(viewtimesheetpath, monthdaymap=monthday_map,weeklist=week_list,timesheetmap=timesheet_map,timesheetstate=timesheet_state,
                    timesheetapprovedate=timesheet_approvedate,timesheetapproveuser=timesheet_approveuser)

# main.py
# ...
routelist = [
    # ...
    (r"/viewtimesheet/(year=\d*)&(month=\d*)",ViewTimeSheet),
    # ...
]
# ...

这个页面和之前的FillTimeSheet大致一样,只不过多调用了新写的TimeSheetViewer类,以及将其结果作为参数传入render函数中。依旧不要忘记在main.py中注册路由。

viewtimesheet.html的block块如下:

<!--viewtimesheet.html-->

        {% block content %}
        <div class="page-wrapper">
            <!-- ============================================================== -->
            <!-- Container fluid  -->
            <!-- ============================================================== -->
            <div class="container-fluid">
                <!-- ============================================================== -->
                <!-- Bread crumb and right sidebar toggle -->
                <!-- ============================================================== -->
                <div class="row page-titles">
                    <div class="col-md-6 col-8 align-self-center">
                        <h3 class="text-themecolor m-b-0 m-t-0">查看考勤</h3>
                        <ol class="breadcrumb">
                            <li class="breadcrumb-item"><a href="/timesheetindex">考勤</a></li>
                            <li class="breadcrumb-item active">查看考勤</li>
                        </ol>
                    </div>
                </div>
                <!-- ============================================================== -->
                <!-- End Bread crumb and right sidebar toggle -->
                <!-- ============================================================== -->
                <!-- ============================================================== -->
                <!-- Start Page Content -->
                <!-- ============================================================== -->
                <div class="row">
                    <!-- column -->
                    <div class="col-sm-12">
                        <div class="card">
                            <div class="card-block">
                                <h4 class="card-title">查看考勤</h4>
                                <div class="table-responsive">
                                        {% for week in weeklist %}
                                        <table class="table">
                                        <thead>
                                            <tr>
                                                {% for day in week %}
                                                <th>{{ day }}
                                                {% if monthdaymap[day] == 'Mon' or monthdaymap[day] == 'Tues' or monthdaymap[day] == 'Wed' or monthdaymap[day] == 'Thur' or monthdaymap[day] == 'Fri' or monthdaymap[day] == 'Sat' or monthdaymap[day] == 'Sun' %}
                                                ({{ monthdaymap[day] }})
                                                {% else %}
                                                (N/A)
                                                {% end %}
                                                </th>
                                                {% end %}
                                            </tr>
                                        </thead>
                                        <tbody>
                                            <tr>
                                                {% for day in week %}
                                                <td>
                                                    {{ timesheetmap[day] }}
                                                </td>
                                                {% end %}
                                            </tr>
                                        </tbody>
                                        </table>
                                        {% end %}
                                        <table class="table">
                                        <thead>
                                            <tr>
                                                <th>状态</th>
                                                <th>审批人</th>
                                                <th>审批日期</th>
                                            </tr>
                                        </thead>
                                        <tbody>
                                            <tr>
                                                <td>{{ timesheetstate }}</td>
                                                <td>{{ timesheetapproveuser }}</td>
                                                <td>{{ timesheetapprovedate }}</td>
                                            </tr>
                                        </tbody>
                                        </table>

                                </div>
                            </div>
                        </div>
                    </div>
                </div>
                <!-- ============================================================== -->
                <!-- End PAge Content -->
                <!-- ============================================================== -->
            </div>
            <!-- ============================================================== -->
            <!-- End Container fluid  -->
            <!-- ============================================================== -->
            <!-- ============================================================== -->
            <!-- footer -->
            <!-- ============================================================== -->
            <footer class="footer text-center">
                © 2020 Tornado考勤系统
            </footer>
            <!-- ============================================================== -->
            <!-- End footer -->
            <!-- ============================================================== -->
        </div>
        {% end %}

这个页面同样和填写考勤的页面很相似,只不过是把每周日期下的内容从下拉表单变为了实际填写的结果,并且在底下新增了一个表格用于显示审批状态、审批人以及审批日期。

效果如下所示,在日期下方显示了填写的考勤项,最下面显示考勤状态和审批人。

在这篇博客中,我们对项目结构进行了一番调整,将各类功能分门别类放到不同的包中,使得main.py文件相比以前大为简洁;实现了一个简单的小功能——创建考勤事件,这使得我们可以将考勤事件按照别名显示出来;最后,我们实现了一个TimeSheetViewer类用于生成日期-考勤的map,并构造页面将其显示出来,同时显示考勤审批的状态。

在下一篇博客中,我们将继续实现真正的审批功能,希望大家继续关注~

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值