用Python写个空课表生成器

开发背景
刚入大学的CYQ加入了我们学校的学生会,面对繁杂的工作,能“偷懒”就“偷懒”。这不,最近要举办几场活动,部门要安排人员值班,需要十几人的空课表。此时,CYQ自告奋勇,接下了制作任务,没想到,困难重重,效率低下,重复性劳动,小学加减法算得头皮发麻。聪明的CYQ终于在倒下前的那一刻,想起了自己的身份,在午夜时分,完成了这个小程序。


前提准备

  • Python3.7
  • 安装xlrd,xlwd第三方库
    (建议用pip安装,官方原装速度感人,镜像速度贼快)自行百度,python国内镜像源
  • 电脑
  • 南昌航空大学官方总课表(这里仅仅是对我们学校的版本,可自行更改源代码,达到自己的需求)
  • 脑子

需求分析

  • 能够根据一份总的课程表(有课)生成一份空课表Excel表
  • 并能根据开始周数和结束周数进行在线生成
  • 操作要求简单,易操作

功能实现(较为简单)

  • 生成函数
  • 可视化图形界面(日后优化)

问题分析

  • 如何实现对xls文件读取呢?
    我们借用了第三方库xlrd,xlwd对于这两种函数的介绍,这里不细讲。
    我们知道xlrd是用于读取.xls文件的,xlwd是写入.xls文件的,我们可以通过观察,r和w的区别记忆,read和write。
    这里有几个关键的用法
  • xrld部分
    用于打开一个Excel文件
work_book = xlrd.open_workbook('filename.xls')

获取Excel文件的所有sheet,这里我们暂时不用,因为CYQ的课表只有一个sheet

work_sheets = work_book.sheet_names()

通过索引来访问一个表单

table = work_book.sheet_by_index(0)

获取到文档的行数

nrows = table.nrows

类推这是列数

ncols = table.ncols

通过索引来访问第row_index行的所有数据,以字符串形式返回

table.row_values(row_index)

  • 如何实现对xls文件写入操作呢?
  • xrwd部分
    这是对写入文档的编码规定为ascii
workbook = xlwt.Workbook(encoding = 'ascii')

创建一个sheet,名称为sheet_1
里面的cell_overwrite_ok = True参数是为了防止重复操作,导致报错

sheet_1 = workbook.add_sheet('sheet_1',cell_overwrite_ok = True)

新建一个样式

style = xlwt.XFStyle()

字体样式

font = xlwt.Font()

字体类型为这个东东

font.name = 'Times New Roman'

实例化

font.bold = True
style.font = font

这是最重要的,把Contents写入到sheet_1中的row行col列,用你设置的style来写入(也可以不用style,上面的新建样式就可以省去)

sheet_1.write(row,col,"Contens",style)

  • 数据的处理
    首先,确定我们要的数据结构
    用字典用得方便些
    所以用一个字典来存储全部人的数据
all_the_data = {}##存储总的数据

首先获取本目录下的满足要求的文件

import os
files = [d for d in os.listdir('.')]

通过生成器来生成一个文件列表
之后通过一个大循环来获取所需的数据(根据我们课表的特征)
名字在第一行,可以通过正则来匹配
前三行为无效数据,最后一行为无效数据
剩下的为有效数据,通过正则来匹配
大循环开始

for i in files:
    	if (i[-4:]=='.xls' and i != 'Blank_Courses.xls'):
            work_book = xlrd.open_workbook(i) #open file to read contens
            work_sheets = work_book.sheet_names()
            table = work_book.sheet_by_index(0) # by_name('')sheet()[x]
            nrows = table.nrows
            ncols = table.ncols
            name = re.findall(' (.*?) ',table.cell(0,0).value)[0]
            print(name+"OK!\n")
            course = {}#用一个字典来存储一个循环里面一个人的信息
            for i in range(nrows-4):#从上往下遍历表单内容
                li = [] #用一个表存储临时数据
                for j in table.row_values(i+3):
                    li.append(j)
                course[i] = li

以上有个判断,从本目录下的所有xls文件,过滤生成文件,加入到程序的处理链条中
匹配一下名字,正则这样写,会更好一点,名字存在name里面
之后从上到下遍历初始数据
为了得到有效信息,我们继续处理

Course = {}
for index in range(len(course)):
    li_a = [] #创建一个列表,储存每大节课的周
    for i in range(len(course[index])):#从每大节课遍历
        if(i==0): #跳过无效数据
            continue
        li = []#创建一个小列表,存储星期所对的课
        data = re.findall(r'\n.*?\n.*?\n(.*?)\n.*?\n',course[index][i])
        #正则匹配数据
        for j in data:
            i.append(j.replace('[周]','').split(',')) #存储到小列表中
        li_a.append(li) #存储到大列表中
    Course[index] = li_a #将列表存储到字典中,获得了一个完整的有课表

聪明的你如果对正则感兴趣的话,可以搜索相关资料。
上面我们又对数据进行了处理,将时间匹配到了
程序设计基础
杨老师()
9[周]
B203

我们学校的表单信息是这种格式,正则就按上面这样写
之后我们同理,将数据存储起来

for index in Course:
	L = []
	for i in range(len(Course[index])):
		have = e_to_have(Course[index][i])#生成有课的所有时间点
		ha_not = e_to_not(have,start,end)#生成无课的区间,和所有点
		if(index not in all_the_data.keys()):
			s = '\n'+name #这是
		else:
			s = all_the_data[index][i] + '\n' + name 
			#追加写入,不删除之前的我数据
		temp = ' '#用来判断是不是全员无课的标记
		for j in ha_not:
			temp = temp+j+' '
		if(temp==' '):L.append(' '+s)#标记在这里添加
		else :L.append(s+temp)
	all_the_data[index] = L #存入总的数据字典里

大循环到此结束
以上我们使用了两个函数

def e_to_have(li):
    L = []
    for i in li:#传入的i是一个列表,但只有一个元素,用i[0]表示它的值
        if(len(i[0])<=2):#没有出现-连接(表示区间)时,小于2
            L.append(i[0])
        else:
            sp = i[0].split('-')##进行切片
            for i in range(int(sp[1])-int(sp[0])+1):
            #循环加入单点到有课列表
                L.append(str(int(sp[0])+i))
    return L

转化为无课的区间或者时间点

def e_to_not(li,start,end):
    L = []##生成无课单点表
    for i in range(end-start+1):##循环次数,也表示总的课区间
        if(str(i+start)not in li):
            L.append(i+start)
            #若此单点不在有课表内,就表示无课,加入其中    

    S = []##生成无课区间和单点
    po = 0##初始一个区间头
    for i in range(len(L)-1):
        if(L[i]+1!=L[i+1]):
            if(L[po]!=L[i]):S.append(str(L[po])+'-'+str(L[i]))
            else:S.append(str(L[po]))
            po = i+1
        else:
            if(i==len(L)-2):
                S.append(str(L[po])+'-'+str(L[i+1]))
                #此算法要对最后一个要进行特判,才能加入连续区间
    return S

里面的算法就不细讲了,思路重要
之后我们进行写入操做
将字典里面的数据依次写入到文件里面

#进行写入操作
    workbook = xlwt.Workbook(encoding = 'ascii')
    sheet_1 = workbook.add_sheet('NCHU_Blank_Courses',cell_overwrite_ok = True) #create sheet object
    style = xlwt.XFStyle()
    font = xlwt.Font()
    font.name = 'Times New Roman'
    font.bold = True
    style.font = font #字体样式设置,可以跳过

    for index in range(len(all_the_data)):
        for i in range(len(all_the_data[index])):
            sheet_1.col(i).width = 256*25
            sheet_1.row(index).height = 256*25
            if (all_the_data[index][i][:2] != '  '):
                sheet_1.write(index,i,all_the_data[index][i],style)
            else:
                sheet_1.write(index,i,"全员无课",style)

到这里大概的框架就完成了,接下来就是漫长的迭代过程。或者是shi沉大海

不过我们也不能忘记了用户体验,配置文件README.docx

还是要写一下的,程序开头的初步使用操作也是要写的

程序执行过程中的状态,也是要写的,哪怕甩点什么数据出来也是可以的

麻雀虽小,五脏可还得俱全啊!

最后来甩几张图

python怎么做课表 python写课程表_ci


python怎么做课表 python写课程表_正则_02

最后完整代码

def e_to_have(li):
    L = []
    for i in li:##i是一个列表,但只有一个元素,用i[0]表示它的值
        if(len(i[0])<=2):#没有出现-连接(表示区间)时,小于2
            L.append(i[0])
        else:
            sp = i[0].split('-')##进行切片
            for i in range(int(sp[1])-int(sp[0])+1):##循环加入单点到有课列表
                L.append(str(int(sp[0])+i))
    return L

def e_to_not(li,start,end):
    L = []##生成无课单点表
    for i in range(end-start+1):##循环次数,也表示总的课区间
        if(str(i+start)not in li):
            L.append(i+start)##若此单点不在有课表内,就表示无课,加入其中    

    S = []##生成无课区间和单点
    po = 0##初始一个区间头
    for i in range(len(L)-1):
        if(L[i]+1!=L[i+1]):
            if(L[po]!=L[i]):S.append(str(L[po])+'-'+str(L[i]))
            else:S.append(str(L[po]))
            po = i+1
        else:
            if(i==len(L)-2):
                S.append(str(L[po])+'-'+str(L[i+1]))##此算法要对最后一个要进行特判,才能加入连续区间
    return S
            
def main():
    print("=========================\n使用前,阅读使用文档(README.docx)\n目前只可以使用南昌航空大学课程表\n请将成员课表(.xls文件)放在本程序目录下\n目录下不要放其他文件\n本程序上级目录尽量不要为中文名\nMADE FOR NCHU WITH PYTHON\n=========================")
    try:
        print("请输入课程周数内的合理数字\n")
        start = int(input("请输入开始周数:\n"))
        end = int(input("请输入结束周数:\n"))
        if(start > 25 or start < 0 or end > 25 or end < 0 or start > end):
            print("请输入课程合理的数字\n你的输入有误,程序自动退出")
            input()
            return
    except:
        print("请输入合理的数字\n你的输入有误,程序自动退出")
        input()
        return
    import re,os
    import xlrd #用来读取excel文件,不能修改数据
    import xlwt #创建Excel文件并对其进行操作,但不能对已有的Excel文件进行修改
    import xlutils #对已有的Excel文件进行修改
    all_the_data = {}##存储总的数据
    files = [d for d in os.listdir('.')]
    
    for i in files:
        if (i[-4:]=='.xls' and i != 'Blank_Courses.xls'):
        ####################################循环体
            work_book = xlrd.open_workbook(i) #open file to read contens
            work_sheets = work_book.sheet_names()
            table = work_book.sheet_by_index(0) # by_name('')sheet()[x]
            nrows = table.nrows
            ncols = table.ncols
            name = re.findall(' (.*?) ',table.cell(0,0).value)[0]
            print(name+"OK!\n")
            course = {}
            for i in range(nrows-4):#从上往下遍历
                li = [] #用一个表存储数据
                for j in table.row_values(i+3):
                    li.append(j)
                course[i] = li

            Course = {}
            for index in range(len(course)):
                li_a = [] #创建一个列表,储存每大节课的周
                for i in range(len(course[index])):#从每大节课遍历
                    if(i==0): #跳过无效数据
                        continue
                    li = []#创建一个小列表,存储星期所对的课
                    data = re.findall(r'\n.*?\n.*?\n(.*?)\n.*?\n',course[index][i])#正则匹配数据
                    for j in data:
                        li.append(j.replace('[周]','').split(',')) #存储到小列表中
                    li_a.append(li) #存储到大列表中
                Course[index] = li_a #将列表存储到字典中,获得了一个完整的有课表
            for index in Course:
                L = []
                for i in range(len(Course[index])):
                    have = e_to_have(Course[index][i])
                    ha_not = e_to_not(have,start,end)
                    if(index not in all_the_data.keys()):
                        s = '\n'+name
                    else:
                        s = all_the_data[index][i] + '\n' + name
                    temp = ' '
                    for j in ha_not:
                        temp = temp+j+' '
                    if(temp==' '):L.append(' '+s)
                    else :L.append(s+temp)
                all_the_data[index] = L
        ##############################循环体
        #进行写入操作
    workbook = xlwt.Workbook(encoding = 'ascii')
    sheet_1 = workbook.add_sheet('NCHU_Blank_Courses',cell_overwrite_ok = True) #create sheet object
    style = xlwt.XFStyle()
    font = xlwt.Font()
    font.name = 'Times New Roman'
    font.bold = True
    style.font = font

    for index in range(len(all_the_data)):
        for i in range(len(all_the_data[index])):
            sheet_1.col(i).width = 256*25
            sheet_1.row(index).height = 256*25
            if (all_the_data[index][i][:2] != '  '):
                sheet_1.write(index,i,all_the_data[index][i],style)
            else:
                sheet_1.write(index,i,"全员无课",style)
    #sheet_1.write(index,i,s,style)    
    #sheet_1.write(r,c,val,style) #write dates in the sheet whith style        

    workbook.save('Blank_Courses.xls')
    print("=========================\nCongratulations!生成空课表成功!\n若程序出现问题可以互相交流QQ1614143338\n=========================")
    input()
#    print(Course)

if __name__ == '__main__':
    main()

写的不是很好,由于写入文件需要在同一函数中,创建文件和保存文件,导致很难再拆分主函数里面的功能。那就为了代码的集中,区域模块化,干脆就把读取数据也写入到主函数中。上述的原因导致不能较好的模块化程序,引发一些程序的漏掉和不稳定性,望大佬们指正。也可以一起交流学习。