Tornado笔记——用Tornado搭建假单统计考勤系统(六)
這篇和上一篇博文隔的時間有點遠了,希望大家還記得我們這個系統(tǒng)之前都做了什么。在上一篇博客中,我們構(gòu)造了一個復(fù)雜的表單和calendar類來實現(xiàn)填寫考勤的功能。現(xiàn)在,我們要實現(xiàn)查看考勤以及審批考勤的功能。
6 項目目錄結(jié)構(gòu)的調(diào)整
現(xiàn)在我們的項目已經(jīng)有了很多的RequestHandler,把這么多RequestHandler都放在一個main.py文件里會很不好管理,因此,我們需要對項目的結(jié)構(gòu)做一點調(diào)整,把這些RequestHandler按功能放到不同的目錄里。
調(diào)整后的目錄結(jié)構(gòu)如下:
?我們主要調(diào)整server目錄的結(jié)構(gòu):在server下新建一個名為apps的package,再在apps包中新建timesheet_app和user_app這兩個package,分別存放user和timesheet相關(guān)功能的RequestHandler。在timesheet_app包中新建timesheet_app.py,把如下RequestHandler從main.py中挪進去:
| RequestHandler名稱 | 對應(yīng)功能 |
| TimeSheetIndex | 考勤 |
| FillTimeSheet | 填寫考勤 |
在user_app包中新建user_app.py,把如下RequestHandler從main.py中挪進去:
| RequestHandler名稱 | 對應(yīng)功能 |
| Register | 注冊用戶 |
| Login | 用戶登錄 |
| CreateUserGroup | 創(chuàng)建用戶組 |
| ViewUserGroup * | 查看用戶組 |
| PersonalInfo * | 個人信息查看 |
| UserManage * | 用戶管理 |
| Approve * | 批準用戶 |
| LogOff * | 注銷用戶 |
| LogOut * | 用戶登出 |
翻了翻前面的博客,發(fā)現(xiàn)帶*的功能還沒有寫到博客中,等我們將考勤部分寫的差不多了再來將這些功能補全。?
?最后,在apps包下建立basehandler.py,將我們的BaseHandler放進去,大家可以自行補全timesheet_app.py、user_app.py和basehandler.py中的import部分。
這樣,我們的目錄結(jié)構(gòu)就調(diào)整完畢,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 asyncioasyncio.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('初始化網(wǎng)站失敗!')exit(0)app = make_app()port = int(getconfig('PORT'))app.listen(port)print('系統(tǒng)啟動')tornado.ioloop.IOLoop.current().start()這里注意,當我們建立Application時,我們要指定template_path到我們的template目錄,否則在調(diào)整完目錄結(jié)構(gòu)后,tornado不會找到這個目錄而是按RequestHandler所在目錄去尋找模板文件,那樣肯定找不到。
現(xiàn)在,我們可以繼續(xù)往下進行了,實現(xiàn)查看考勤以及批準考勤的功能。
7 創(chuàng)建考勤事件
在實現(xiàn)查看考勤之前,讓我們先實現(xiàn)另一個小功能——創(chuàng)建考勤事件。通過這個功能,可以讓我們添加不同的考勤事件,如工作、病假、調(diào)休等,以供在之前的填寫考勤功能中選擇。
該功能的頁面如下:
這個頁面十分簡單, 上半部分列出現(xiàn)有的考勤事件,下半部分的表單用于建立新的表單事件。事件名即上半部分的事件代碼,是事件真正存在DB中的值,而事件別名是用于查看考勤時顯示出的事件名稱。
我們在timesheetutil.py中編寫createtimesheetevent函數(shù),用于生成考勤事件:
# 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這個小函數(shù)沒啥說的,根據(jù)eventcode查一下是否有考勤事件存在,如果沒有的話則創(chuàng)建之。
然后,我們在timesheet_app.py實現(xiàn)CreateTimeSheetEvent RequestHandler:
# timesheet_app.pyclass 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),# ... ] # ...這個頁面也沒什么說的,常規(guī)的抓取數(shù)據(jù)后顯示/插入,不要忘了在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">創(chuàng)建考勤事件</h3><ol class="breadcrumb"><li class="breadcrumb-item"><a href="/">Home</a></li><li class="breadcrumb-item active">創(chuàng)建考勤事件</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">創(chuàng)建</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考勤系統(tǒng)</footer><!-- ============================================================== --><!-- End footer --><!-- ============================================================== --></div>{% end %}在實現(xiàn)了這個功能后,我們就可以按照上圖所示將我們的考勤事件建立起來,以便在之后的查看考勤功能中使用。
8 查看考勤
在準備好了考勤事件后,我們就可以實現(xiàn)查看考勤功能了。我們需要實現(xiàn)一個TimeSheetViewer類,這個類會返回一個key為日期value為事件別名的字典,與之前的Calendar類配合可以讓我們查看用戶填寫的考勤結(jié)果。
打開timesheetutil.py,實現(xiàn)TimeSheetViewer類:
# timesheetutil.pyclass TimeSheetViewer:def __init__(self,username,year,month):self.__username = usernameself.__year = yearself.__month = monthself.__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.stateself.__approveuser = timesheet.approveusernameself.__approvedate = timesheet.approvedatefor 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.nicknameelse: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.__timesheetmapdef getstate(self):return self.__statedef getapproveuser(self):return self.__approveuserdef getapprovedate(self):return self.__approvedate先看看這個類中的參數(shù)介紹:
- __username:用戶名
- __year:年
- __month:月
- __timesheetmap:最終返回的結(jié)果,key為日期value為事件別名的字典
- __state:考勤表狀態(tài)
- __approveuser:批準的用戶
- __approvedate:批準日期
這個類的核心函數(shù)為__gettimesheetinfo。在這個函數(shù)中,首先通過year和month參數(shù)從TimeSheet表中拿到此月的數(shù)據(jù),再調(diào)用之前的Calendar類獲得此月的monthmap。然后,從TimeSheet中獲得考勤表的狀態(tài)、批準的用戶以及批準日期,再按日期去獲得每天的考勤事件;如果某個日期的考勤事件不存在,則用N/A代替。這樣,我們就得到了一個key為日期value為事件別名的字典,以及拿到了考勤表的狀態(tài)、批準用戶以及批準日期。底下的幾個getter函數(shù)就不再贅述了。
我們打開timesheet_app.py,實現(xiàn)ViewTimeSheet:
# timesheet_app.pyclass 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大致一樣,只不過多調(diào)用了新寫的TimeSheetViewer類,以及將其結(jié)果作為參數(shù)傳入render函數(shù)中。依舊不要忘記在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>狀態(tài)</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考勤系統(tǒng)</footer><!-- ============================================================== --><!-- End footer --><!-- ============================================================== --></div>{% end %}這個頁面同樣和填寫考勤的頁面很相似,只不過是把每周日期下的內(nèi)容從下拉表單變?yōu)榱藢嶋H填寫的結(jié)果,并且在底下新增了一個表格用于顯示審批狀態(tài)、審批人以及審批日期。
效果如下所示,在日期下方顯示了填寫的考勤項,最下面顯示考勤狀態(tài)和審批人。
在這篇博客中,我們對項目結(jié)構(gòu)進行了一番調(diào)整,將各類功能分門別類放到不同的包中,使得main.py文件相比以前大為簡潔;實現(xiàn)了一個簡單的小功能——創(chuàng)建考勤事件,這使得我們可以將考勤事件按照別名顯示出來;最后,我們實現(xiàn)了一個TimeSheetViewer類用于生成日期-考勤的map,并構(gòu)造頁面將其顯示出來,同時顯示考勤審批的狀態(tài)。
在下一篇博客中,我們將繼續(xù)實現(xiàn)真正的審批功能,希望大家繼續(xù)關(guān)注~
總結(jié)
以上是生活随笔為你收集整理的Tornado笔记——用Tornado搭建假单统计考勤系统(六)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: A/BTest设计方案
- 下一篇: 我国AI医疗及人工智能医疗公司现状+20