您可以使用jinja https://jinja.palletsprojects.com/en/3.0.x/用于 html 模板和pdfkit https://pypi.org/project/pdfkit/转换为 pdf(要使 pdfkit 工作,您可能需要安装wkhtml转pdf https://wkhtmltopdf.org/在你的操作系统上(不是Python))。我根据您的数据和 html 示例制作了一个直接示例:
from typing import List
import pdfkit
from jinja2 import FileSystemLoader, Environment
input_data = {
"CS-3B": [['', '', 'DS ', '', 'COaAL '], ['', 'COaAL ', '', 'DS ', 'OOP '],
['DS-L ', 'DS-L ', 'OOP-L ', 'OOP-L ', 'FoM '], ['COaAL-L ', 'COaAL-L ', 'OOP ', '', ''],
['', 'FoM ', 'DE ', '', 'DE ']],
"SE-3A": [['', 'OOP-L ', 'OOP-L ', '', 'SRE '], ['SRE ', 'OOP ', 'DS-L ', 'DS-L ', ''], ['', 'DS ', '', '', 'MM '],
['DS ', 'MM ', '', 'LA ', ''], ['OOP ', 'HCI ', '', 'LA ', 'HCI ']],
"CS-7F": [['', '', '', '', ''], ['RSaG ', '', '', '', ''], ['ST ', '', 'RSaG ', '', ''], ['', '', '', '', ''],
['', 'ST ', '', '', '']],
"CS-1C": [['IS ', 'ECaC-L ', 'ECaC-L ', '', 'PF '], ['ECaC ', 'PF-L ', 'PF-L ', 'ECaC-L ', 'ECaC-L '],
['DLD ', 'ECaC ', '', 'PF ', 'ItIaCT '], ['DLD-L ', 'DLD-L ', 'IS ', 'LA ', ''],
['ECaC ', 'ECaC ', 'ItIaCT ', 'DLD ', 'LA ']],
"CS-1D": [['PF-L ', 'PF-L ', 'ECaC-L ', 'ItIaCT ECaC-L ', 'ItIaCT '], ['IS ', 'AP ', 'ECaC-L ', 'ECaC-L ', ''],
['PF ', 'PF ', '', 'ECaC ', ''], ['CaAG ', 'ECaC ', 'ECaC ', '', 'IS '],
['', 'CaAG ', '', 'ECaC ', 'AP ']],
"CS-7A": [['', 'DM ', '', 'PPiI ', 'DS '], ['AI-L ', 'AI-L ', '', 'AI ', 'IS '], ['', '', 'DS ', '', ''],
['SE ', 'SE ', '', 'PPiI ', ''], ['', 'AI ', 'IS ', '', 'DM ']],
"CS-7B": [['', 'DS ', '', 'DS ', 'DM '], ['', '', '', 'PPiI ', ''], ['', 'PPiI ', '', 'SE ', ''],
['', 'DM ', '', 'IS ', ''], ['', '', 'IS ', 'SE ', '']],
"CS-1B": [['LA ', '', '', 'DLD ', 'DLD '], ['ECaC ', 'IS ', '', 'PF ', 'ECaC '],
['ECaC-L ', 'ECaC-L ', 'DLD-L ', 'DLD-L ', 'ItIaCT '], ['ECaC ', 'PF-L ', 'PF-L ', 'ECaC-L ', 'ECaC-L '],
['ECaC ', 'PF ', 'IS ', 'LA ', 'ItIaCT ']],
"CS-1A": [['', 'PF-L ', 'PF-L ', 'ECaC ', ''], ['ECaC ', '', 'ItIaCT ', 'LA ', 'ECaC '],
['PF ECaC-L ', 'ItIaCT ECaC-L ', '', 'DLD-L ', 'DLD-L '], ['IS ', 'PF ', 'ECaC-L ', 'ECaC-L ', ''],
['DLD ', 'IS ', 'LA ', 'DLD ', 'ECaC ']],
"CS-7D": [['AML ', '', 'IS ', '', 'AML '], ['', '', '', '', ''], ['IS ', 'SfMD ', '', '', ''],
['', '', '', '', 'SfMD '], ['PPiI ', '', 'PPiI ', '', '']],
"CS-7C": [['SfMD ', '', '', 'AML ', ''], ['PPiI ', '', '', '', ''], ['', 'SfMD ', '', '', ''],
['', '', 'AML ', 'IS ', ''], ['', '', 'PPiI ', 'IS ', '']],
"CS-3C": [['MM ', 'COaAL-L ', 'COaAL-L ', 'DS ', ''], ['', '', '', '', ''], ['DS-L ', 'DS-L ', 'DS ', '', 'DE '],
['', '', '', '', ''], ['', 'DE ', '', '', 'MM ']],
"CS-5C": [['', 'CN-L ', 'CN-L ', '', 'CN '], ['PaS ', 'CN ', '', '', 'ToA '], ['', '', '', 'SDaA ', 'AP '],
['AP ', '', '', 'ToA ', 'SDaA '], ['', 'PaS ', '', '', '']],
"CS-5B": [['', '', 'WP ', '', ''], ['WP ', 'ToA ', 'MM ', 'CN-L ', 'CN-L '], ['SDaA ', '', '', 'MM ', 'CN '],
['SDaA ', '', '', 'ToA ', ''], ['', '', '', 'CN ', '']],
"CS-1E": [['PF-L ', 'PF-L ', 'AP ', 'ECaC ', 'ECaC '], ['ECaC-L ', 'ECaC-L ', 'PS ', 'ItIaCT ', 'AP '],
['', 'PF ', 'CaAG ', 'ECaC-L ', 'ECaC-L '], ['PS ', '', 'ItIaCT ', '', ''],
['', 'CaAG ', 'PF ', 'ECaC ', 'ECaC ']],
"SE-3B": [['LA ', '', '', '', ''], ['DS ', 'HCI ', '', '', ''], ['DS ', 'LA ', '', '', ''],
['', 'DS-L ', 'DS-L ', 'SRE ', 'F&A '], ['F&A ', 'HCI ', '', '', 'SRE ']],
"SE-5B": [['', '', '', 'PaS ', 'TaBW '], ['SCaD-L ', 'SCaD-L ', 'SCaD ', 'OR ', 'SQE '],
['', '', 'TaBW ', '', 'SCaD '], ['', 'SQE ', '', '', ''], ['PaS ', '', '', '', 'OR ']],
"SE-5A": [['OS-L ', 'OS-L ', 'OS ', 'SCaD-L ', 'SCaD-L '], ['OR ', 'DS ', '', 'OR ', 'TaBW '],
['DS-L ', 'DS-L ', 'PaS ', 'SCaD ', 'OS '], ['', 'SQE ', 'SCaD ', 'PaS ', 'TaBW '],
['', '', 'DS ', '', 'SQE ']],
"CS-3A": [['DS-L ', 'DS-L ', 'LA ', 'CaAG ', 'DS '], ['F&A ', 'DS ', 'DLD ', 'DS ', 'OOP '],
['CaAG ', 'LA ', 'COaAL ', 'OOP-L ', 'OOP-L '], ['DE AP ', 'COaAL-L ', 'COaAL-L ', 'OOP ', 'COaAL '],
['AP ', 'DE ', 'F&A ', 'DLD ', 'DS ']]
}
def organise_input_data(elements: List[List[str]]) -> List[list]:
"""
Organises the input data to find double courses for easier use in templates
"""
new_elements = []
for day in elements:
last_course = None
course_list = []
index = 0
for course in day:
# cleanup data
course = course.strip().replace(" ", "<hr>")
# check if long course (and not lunch time)
if course != "" and course == last_course and index != 3:
course_list.remove((course, 1))
course_list.append((course, 2))
course_list.append(("none", 0))
else:
course_list.append((course.replace(" ", "<hr>"), 1))
last_course = course
index += 1
new_elements.append(course_list)
return new_elements
def generate_html(template, name: str, elements: List[list]) -> str:
new_elements = organise_input_data(elements=elements)
rendered = template.render(
name=name,
monday=new_elements[0],
tuesday=new_elements[1],
wednesday=new_elements[2],
thursday=new_elements[3],
friday=new_elements[4]
)
with open(f"out_{name}.html", "w+") as file:
file.write(rendered)
return rendered
def run():
# Init jinja
file_loader = FileSystemLoader('.')
env = Environment(loader=file_loader)
template = env.get_template('template.html')
full_text = ""
for name, elements in input_data.items():
full_text += generate_html(template=template, name=name, elements=elements)
pdfkit.from_string(full_text, "out.pdf")
if __name__ == '__main__':
run()
组织好的input_data函数仅用于数据准备。
template.html 看起来像:
<!DOCTYPE html>
<html>
<style>
.center
{
text-align: center;
}
td{
height:75px;
width:150px;
}
</style>
<body>
<!-- Heading -->
<h1 class="center">{{name}}</h1>
<!-- Table -->
<table border="5" cellspacing="5" align="center">
<!-- Day/Periods -->
<tr>
<td class="center" ><br>
<b>Day/Period</b></br>
</td>
<td class="center" >
<b>I</b>
</td>
<td class="center" >
<b>II</b>
</td>
<td class="center">
<b>III</b>
</td>
<td class="center">
<b>1:15-1:45</b>
</td>
<td class="center" >
<b>IV</b>
</td>
<td class="center" >
<b>V</b>
</td>
</tr>
<!-- Monday -->
<tr>
<td class="center">
<b>Monday</b></td>
{% for course in monday %}
{% if loop.index == 4 %}
<td rowspan="6" class="center">
<h2>L<br>U<br>N<br>C<br>H</h2>
</td>
{% endif %}
{% if course[1] != 0 %}
<td colspan={{course[1]}} class="center">{{course[0]}}</td>
{% endif %}
{% endfor %}
</tr>
<!-- Tuesday -->
<tr>
<td class="center">
<b>Tuesday</b>
</td>
{% for course in tuesday %}
{% if course[1] != 0 %}
<td colspan={{course[1]}} class="center">{{course[0]}}</td>
{% endif %}
{% endfor %}
</tr>
<!-- Wednesday -->
<tr>
<td class="center">
<b>Wednesday</b>
</td>
{% for course in wednesday %}
{% if course[1] != 0 %}
<td colspan={{course[1]}} class="center">{{course[0]}}</td>
{% endif %}
{% endfor %}
</tr>
<!-- Thursday -->
<tr>
<td class="center">
<b>Thursday</b>
</td>
{% for course in thursday %}
{% if course[1] != 0 %}
<td colspan={{course[1]}} class="center">{{course[0]}}</td>
{% endif %}
{% endfor %}
</tr>
<!-- Friday -->
<tr>
<td class="center">
<b>Friday</b>
</td>
{% for course in friday %}
{% if course[1] != 0 %}
<td colspan={{course[1]}} class="center">{{course[0]}}</td>
{% endif %}
{% endfor %}
</tr>
</table>
</body>
</html>
输出pdf:
解释:
Jinja 是一种模板语言/库,因此您可以使用一些变量和逻辑创建 html(大多数在{}
括号),所以你不必通过 python 本身创建整个 html。
我决定为每个课程创建元组,其中包含课程名称和持续时间(连续 1 或 2 个时段)。如果您控制时间表创建的整个过程,您可以直接生成此数据而不是 input_data 列表。
该示例非常简单,并且非常接近您给定的数据,因此步骤非常容易。您还可以通过在 html/jinja 中使用更多逻辑来创建(在我看来)更干净的 html 文件,例如:
{% for day in days %}
{% with courses=course_list[loop.index-1] %}
{% include 'template_day.html' %}
{% endwith %}
{% endfor %}
以及一个额外的模板文件 (template.html),其中包含每天的 logi:
<tr>
<td class="center">
<b>{{day}}</b>
</td>
{% for course in courses %}
{% if day == "Monday" and loop.index == 4 %}
<td rowspan="6" class="center">
<h2>L<br>U<br>N<br>C<br>H</h2>
</td>
{% endif %}
{% if course[1] != 0 %}
<td colspan={{course[1]}} class="center">{{course[0]}}</td>
{% endif %}
{% endfor %}
</tr>
这样,html 就会短得多(因为所有的日子都在 html 中迭代,而不是手动声明)。因此,如果您想添加更多数据(例如 Room),您只需在一个位置进行更改,而不是在 5 个位置进行更改。渲染调用的新部分也会更短:
rendered = template.render(
name=name,
days=["Monday", "Tuesday", "Wednesday", "Thursday", "Friday"],
course_list=new_elements
)