作者:小小明
需求
有一个Excel文件:
需要按照题目和答案分别生成两个如下格式的word文档:
实现它的核心代码仅三行:
tpl = DocxTemplate("xxxx模板.docx")
tpl.render({'ps': df[columns].values.tolist()})
tpl.save("result/xxxx-yy.docx")
下面我们看看具体的实现过程:
实现过程
首先制作Word模板,编辑民法学必刷470题-题集模板.docx
文件内容如下:
{%p for a,b,c,d in ps %}
第{{ a }}章 第{{ b }}节 第{{ c }}题
{{ d }}
{%p endfor %}
编辑民法学必刷470题-答案模板.docx
文件内容如下:
{%p for a,b,num,answer,text in ps %}
第{{ a }}章 第{{ b }}节 第{{ num }}题
答案:{{ answer }}
知识:
{{ text }}
{%p endfor %}
可以根据需要,任意修改Word模板的样式,生成的内容都会跟着改变。
然后读取数据:
import pandas as pd
from docxtpl import DocxTemplate
df = pd.read_excel("《民法学》必刷分章练习-470题.xlsx")
df.sort_values(["章", "节"], inplace=True)
df
旧题号 | 新题号 | 题 | 章 | 节 | 解析 | 参考答案 | |
---|---|---|---|---|---|---|---|
0 | 8 | 1 | 形式意义上的民法指-\n\nA.民法典 \n\nB.所有调整民事关系的法律规范 | 1 | 1 | 【图】\n形式意义上的民法就是指民法典。法典是按照一定体系将各项法律制度系统编纂在一起的法律… | A |
1 | 1 | 2 | 下列财产关系中属于民法调整的是-\n\nA.财政拨付关系\n\nB.赡养关系\n\nC.税收… | 1 | 2 | 民法调整的社会关系的最本质特点在于其平等性,这是民法区别于其他部门法的根本特点。\n民法的调… | B |
2 | 2 | 3 | 民法的调整对象是-\n\nA.财产关系和人身关系\n\nB.商品经济关系\n\nC.平等主体… | 1 | 2 | 民法的调整对象是平等主体之间的财产关系和人身关系 | C |
3 | 9 | 4 | 下列对民法调整对象的表述正确的是-\n\nA.民法的调整对象是财产关系和人身关系\n\nB.… | 1 | 2 | 9.下列对民法调整对象的表述正确的是( B )\nA.民法的调整对象是财产关系和人身关系【需… | B |
4 | 22 | 5 | 根据民事法律关系是否直接具有财产利益的内容,民事法律关系可分为-\n\nA.财产法律关系与人… | 1 | 2 | 考点:民事法律关系的分类 | A |
… | … | … | … | … | … | … | … |
465 | 462 | 456 | 林某有儿子小强9岁,小强淘气异常。一日小强在邻居王某的鱼缸边玩耍时,另一邻居张某(成年人)对… | 50 | 2 | 第一千一百六十九条 【教唆侵权、帮助侵权】教唆、帮助他人实施侵权行为的,应当与行为人承担连带… | A |
466 | 466 | 457 | 小偷甲在某商场窃得乙的钱包后逃跑,乙发现后急追。甲逃跑中撞上欲借用商场厕所的丙,因商场地板湿… | 50 | 2 | 第一千一百九十八条 宾馆、商场、银行、车站、机场、体育场馆、娱乐场所等经营场所、公共场所的经… | AE |
467 | 467 | 458 | 甲搬家公司指派员工郭某为徐某搬家,郭某担心人手不够,请同乡蒙某帮忙。搬家途中,因郭某忘记拴上… | 50 | 2 | 第一千一百九十一条 用人单位的工作人员因执行工作任务造成他人损害的,由用人单位承担侵权责任。… | C |
468 | 468 | 459 | 甲饲养的一只狗卧在家门前,乙路过甲家前从路边拾起一块石头向狗砸去,狗被激怒后向乙扑去,乙躲闪… | 50 | 2 | 饲养动物损害责任的归责原则\n《民法典》确定我国饲养动物损害责任的二元化归责原则体系,根据具… | ABC |
469 | 469 | 460 | 下列情形可以适用精神损害赔偿的是-\nA.某公司连续一年未发工资,致职工甲忧心忡忡\nB.某… | 50 | 2 | 精神损害赔偿的适用范围:\n一是侵害物质性人格权,可以请求精神损害抚慰金赔偿;\n二是侵害精… | D |
470 rows × 7 columns
此时再调用2次开头说的三行代码就可以了:
tpl = DocxTemplate("民法学必刷470题-题集模板.docx")
tpl.render({'ps': df[["章", "节", "新题号", "题"]].values.tolist()})
tpl.save("result/民法学必刷470题-题集.docx")
tpl = DocxTemplate("民法学必刷470题-答案模板.docx")
tpl.render({'ps': df[["章", "节", "新题号", "参考答案", "解析"]].values.tolist()})
tpl.save("result/民法学必刷470题-答案.docx")
生成结果:
至此,我们就完成了需求。
题目选项平衡
后面打印后,感觉题目空行太多,希望能去掉空行,并保持ABCDE选项的平衡。
一开始写了很多选项平衡算法,但感觉结果都还不够平衡不够美观,在考虑到选项最大5个选项后,我考虑的实现思路是:先判断前4个选项长度之和是否小于50,是的话合并到一行;否则判断,前2个选项的长度之和以及第3第4选项的长度之和,是否同时小于50,是的话,先将前2个选项合并,再将第3第4选项合并;否则前4个选项全部都不合并。最后判断是否存在第5个选项,存在的话尝试跟前面的选项合并,总长度小于50则应用合并。下面是实现代码:
titles = []
for row in df.题.str.split("\n+"):
print(row)
title = row[0].strip()
options = [i.strip() for i in row[1:]]
tmp = []
if len('\t'.join(options[:4])) <= 50:
tmp.append(options[:4])
elif len('\t'.join(options[:2])) <= 50 and len('\t'.join(options[2:4])) <= 50:
tmp.extend([options[:2], options[2:4]])
else:
tmp.extend([[i] for i in options[:4]])
if len(options) == 5:
if len("\t".join(tmp[-1]+[options[4]])) < 50:
tmp[-1].append(options[4])
else:
tmp.append([options[4]])
options = []
for option_arr in tmp:
options.append("\t".join(option_arr))
print(title, options)
titles.append("\n".join([title]+options))
df.题 = titles
重新生成题目Word文档:
tpl = DocxTemplate("民法学必刷470题-题集模板.docx")
tpl.render({'ps': df[["章", "节", "新题号", "题"]].values.tolist()})
tpl.save("result/民法学必刷470题-题集.docx")
结果:
感觉合并效果还不错。
完整代码:
import pandas as pd
from docxtpl import DocxTemplate
df = pd.read_excel("《民法学》必刷分章练习-470题.xlsx")
df.sort_values(["章", "节"], inplace=True)
titles = []
for row in df.题.str.split("\n+"):
print(row)
title = row[0].strip()
options = [i.strip() for i in row[1:]]
tmp = []
if len('\t'.join(options[:4])) <= 50:
tmp.append(options[:4])
elif len('\t'.join(options[:2])) <= 50 and len('\t'.join(options[2:4])) <= 50:
tmp.extend([options[:2], options[2:4]])
else:
tmp.extend([[i] for i in options[:4]])
if len(options) == 5:
if len("\t".join(tmp[-1]+[options[4]])) < 50:
tmp[-1].append(options[4])
else:
tmp.append([options[4]])
options = []
for option_arr in tmp:
options.append("\t".join(option_arr))
print(title, options)
titles.append("\n".join([title]+options))
df.题 = titles
tpl = DocxTemplate("民法学必刷470题-题集模板.docx")
tpl.render({'ps': df[["章", "节", "新题号", "题"]].values.tolist()})
tpl.save("result/民法学必刷470题-题集.docx")
tpl = DocxTemplate("民法学必刷470题-答案模板.docx")
tpl.render({'ps': df[["章", "节", "新题号", "参考答案", "解析"]].values.tolist()})
tpl.save("result/民法学必刷470题-答案.docx")