在上篇博客中,我们搭建了考勤部分的主页,提供填写考勤和查看考勤的入口。在这篇博客中,将继续为大家带来填写考勤和查看考勤部分的代码。
目前,本系列博客的所有代码都已上传到github,库地址为git@github.com:CapLiu/LeaveManage.git,大家可以上去查看,之后的代码也将更新到这个库中。
5 填写考勤
在介绍填写考勤部分之前,我们先对上篇博文中提到的Calendar类做一点小改动。
这是我们上篇博客的图,可见这个日历是从周三开始的,而不是从周一开始的。事实上,在之前的Calendar日历中,每月的一号是星期几,它就从星期几开始显示,和一般的日历相比有点混乱。
因此,我们要对Calendar类的代码做一些改动,使其永远从周一开始显示日历:
可见,在改动了Calendar类之后,日历会从周一开始显示。
为此,我们要修改TimeSheetCalendar类的__generateweeklist函数,让它找到每月一号所在的那个周一。
# util/timesheet/timesheetutil.py
def __generateweeklist(self):
extra_monthday_map = {}
days = 0
one_week = []
oneday = datetime.timedelta(days=1)
for monthday in self.__monthday_map:
one_week.append(monthday)
days += 1
if len(one_week) == 7:
self.__week_list.append(one_week)
one_week = []
elif days == len(self.__monthday_map):
tmptimeinfo = monthday.split('-')
tmpday = datetime.datetime(int(tmptimeinfo[0]), int(tmptimeinfo[1]), int(tmptimeinfo[2]))
while len(one_week) < 7:
tmpday += oneday
one_week.append(tmpday.strftime('%Y-%m-%d'))
extra_monthday_map[tmpday.strftime('%Y-%m-%d')] = tmpday.weekday()
self.__week_list.append(one_week)
elif days == 1:
# 每月1号若不是周一,则往前找到最近的周一
if self.__monthday_map[monthday] != 'Mon':
tmptimeinfo = monthday.split('-')
tmpday = datetime.datetime(int(tmptimeinfo[0]), int(tmptimeinfo[1]), int(tmptimeinfo[2]))
while tmpday.weekday() != 0:
tmpday -= oneday
one_week.append(tmpday.strftime('%Y-%m-%d'))
extra_monthday_map[tmpday.strftime('%Y-%m-%d')] = tmpday.weekday()
one_week.reverse()
if len(one_week) == 7:
self.__week_list.append(one_week)
one_week = []
self.__monthday_map.update(extra_monthday_map)
在第二个elif块中,我们会判断每月的1号是否为周一,如果不是的话,则往前找,直至找到第一个周一为止,并将这些日期插入one_week。在找到第一个周一后,对one_week做反转,从而得到正序的一周日期;如果算上往前找的天数已经够7天,则会另起一周开始剩余日期的处理。这样就确保了每月1号所在的周要从周一开始。
下面让我们看看填写考勤的后端部分。我们在main.py中建立FillTimeSheet类,用于填写考勤。
# server/main.py
# ...
class FillTimeSheet(BaseHandler):
def get(self,year,month):
#today_date = datetime.datetime.today()
year = int(year.split('=')[1])
month = int(month.split('=')[1])
timesheetcalendar = TimeSheetCalendar(year,month)
timesheetpath = gettemplatepath('timesheet.html')
timesheetcalendar.generatecalendar()
monthday_map = timesheetcalendar.getmonthmap()
week_list = timesheetcalendar.getweeklist()
self.render(timesheetpath, monthdaymap=monthday_map,weeklist=week_list,year=year,month=month)
def make_app():
routelist = [
# ...
(r"/filltimesheet/(year=\d*)&(month=\d*)",FillTimeSheet),
# ...
]
# ...
由于我们要根据不同的年月来生成对应的Calendar,因此我们采用带参数的路由来处理这部分。可以看到,我们路由的写法为/filltimesheet/(year=\d*)&(month=\d*),其含义为在/filltimesheet后要跟year=年&month=月,即/filltimesheet/year=xx&month=xx,小括号为正则表达式中的对字符分组,本身并不是url内容。
在get函数中,我们要先将传入的year具体内容解析出来。当我们访问/filltimesheet/year=yy&month=mm后,我们得到的year变量和month变量的值分别为我们在路由中写的正则表达式的值,即year='year=yy',month='month=mm'。因此,我们使用int(year.split('-')[1])和int(month.split('-')[1])来得到具体的年与月,并将其转换为int型。随后,我们就可以利用year和month来生成一个Calendar对象,并得到它的周列表和月列表,再将其显示在前端上。
我们在template文件夹下建立timesheet.html,将我们获得的日历显示出来:
<!--template/timesheet.html-->
{% extends "base_nav.html" %}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<!-- Tell the browser to be responsive to screen width -->
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="description" content="">
<meta name="author" content="">
<!-- Favicon icon -->
<link rel="icon" type="image/png" sizes="16x16" href="../assets/images/favicon.png">
<title>填写考勤</title>
<!-- Bootstrap Core CSS -->
<link href="../assets/plugins/bootstrap/css/bootstrap.min.css" rel="stylesheet">
{% block css %}
<link href="../css/style.css" rel="stylesheet">
<!-- You can change the theme colors from here -->
<link href="../css/colors/blue.css" id="theme" rel="stylesheet">
{% end %}
<!-- HTML5 Shim and Respond.js IE8 support of HTML5 elements and media queries -->
<!-- WARNING: Respond.js doesn't work if you view the page via file:// -->
<!--[if lt IE 9]>
<script src="https://oss.maxcdn.com/libs/html5shiv/3.7.0/html5shiv.js"></script>
<script src="https://oss.maxcdn.com/libs/respond.js/1.4.2/respond.min.js"></script>
<![endif]-->
</head>
<body class="fix-header card-no-border">
<!-- ============================================================== -->
<!-- Preloader - style you can find in spinners.css -->
<!-- ============================================================== -->
<div class="preloader">
<svg class="circular" viewBox="25 25 50 50">
<circle class="path" cx="50" cy="50" r="20" fill="none" stroke-width="2" stroke-miterlimit="10" /> </svg>
</div>
<!-- ======&#