如何使用 Python 制作时间表的 Images/PDF
How to make Images/PDF of Timetable using Python
我正在解决时间表安排问题,并希望以 PDF 或一组图像的形式打印出最终输出。我有多个部分,每个部分都有自己的时间表。
我为每个部分创建了一个二维数组。该数组的大小为 5 x 5(5 天,每天有 5 个五个时段),数组的每个索引代表一个讲座时段。现在,这个二维数组包含该特定部分时间表中每门课程的讲座。示例输出如下,(它是一个字典,每个键都是一个部分。每个键的值是一个二维数组
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 ']]
请注意CS-1D作为示例,
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 ']]
有两件事我需要处理。首先,每个实验室(以 -L 结尾的课程在 连续时段 中都有讲座。这意味着,我想要单元格(时间表中的两个单元格)表示实验室时横向合并
其次,在某些索引中,两个讲座同时进行。例如,请注意 CS-1D 中星期一的第 4 个槽(0 索引)。 ItIaCT和ECaC-L是两门不同的课程,但同时有讲座。 (在这个 2D Array 中,如果同时发生两个或多个讲座,则在该索引中用 space 分隔它们)。为此,我希望水平划分该讲座时段的单元格以适合两个讲座。
示例最终输出看起来像这样(每个单元格还将告诉讲师正在教授课程以及 class 在哪个房间举行)
我不想要 13 个不同的时段,而是每天只需要 五个时段。我的问题是,
我必须使用 Python 执行此操作,但我不知道如何开始。我有使用算法为每个部分创建的时间表(如上所示),但我无法弄清楚如何从中制作时间表(输出)
其次,我想制作一个 PDF 文件,其中将包含所有部分的时间表。我不知道该怎么做。我假设我需要为每个部分的时间表制作一个 Image,然后将所有这些图像(就像我在上面分享一个部分时间表的一张图像一样)合并到一个 PDF 中。但是,我不知道如何将一张时间表转换为图像。
此外,请注意,我使用纯 HTML 做了类似的东西,我将在下面分享其代码和结果。我正在尝试使用 Python.
复制一些类似的东西
<!DOCTYPE html>
<html>
<style>
.center
{
text-align: center;
}
td{
height:75px;
width:150px;
}
</style>
<body>
<!-- Heading -->
<h1 class="center">BCS-7D</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>
<td class="center">Linear Algebra, Mr. Raheel Ahmad, Room 1</td>
<td class="center">X</td>
<td class="center">X</td>
<td rowspan="6" class="center">
<h2>L<br>U<br>N<br>C<br>H</h2>
</td>
<td colspan="2" class="center">LAB</td>
</tr>
<!-- Tuesday -->
<tr>
<td class="center">
<b>Tuesday</b>
</td>
<td class="center">X</td>
<td colspan="2" class="center">LAB
</td>
<td class="center">X</td>
<td class="center">X</td>
</tr>
<!-- Wednesday -->
<tr>
<td class="center">
<b>Wednesday</b>
</td>
<td class="center">Object Oriented Programming, Ms. Jen Ledger, Room 13<hr>Programming Fundamentals, Mr. Zahid Iqbal, Room 6</td>
<td class="center">X</td>
<td class="center">X</td>
<td class="center">X</td>
<td colspan="3" class="center">X
</td>
</tr>
<!-- Thursday -->
<tr>
<td class="center">
<b>Thursday</b>
</td>
<td class="center">X</td>
<td class="center">X</td>
<td class="center">X</td>
<td colspan="3" class="center">Object Oriented Programming - Lab, Ms. Zain Malik, Lab 6
</td>
</tr>
<!-- Friday -->
<tr>
<td class="center">
<b>Friday</b>
</td>
<td colspan="2" class="center">LAB
</td>
<td class="center">X</td>
<td class="center">X</td>
<td class="center">X</td>
</tr>
</table>
</body>
</html>
输出的屏幕截图,(请注意,这是硬编码布局。实验可以在时间表中的任何位置(对于实验,必须合并两个连续的时段)并且两个讲座位于同一时间也可能随时发生。为此,该讲座时段应该有一个水平分隔符)
有很多方法可以用动态数据生成这样的布局,一般来说 python 我会推荐你使用 excel,或者使用基于网络的布局,然后使用生成您的输出 pdf。
使用HTML操作
如果我们谈论的是基于网络的布局,您将需要使用 beautifulsoup,您的方法将如下所示:
创建一个标准布局,每个位置的单元格都有唯一的 ID(您可以使用与上一个示例类似的内容)。
遍历可能需要拆分的较大容器,您可以使用带有 if 条件的 beautifulsoup 来添加不同的 html,具体取决于您是否需要拆分数据与否,例如:
import bs4
with open("template.html") as t:
text = t.read()
soup = bs4.BeautifulSoup(text)
td = soup.find(id="B6")
entry = soup.new_tag("p")
entry.append("LAB")
# insert the new tag after the current tag
td.insert_after(new_tag)
您可以在此阶段额外动态添加布局,可能逐行添加 td 标签而不是其内容。
最后,完成后您可以使用以下方式保存您的 html 文件:
with open("template.html", "w") as t:
t.write(str(soup))
- 保存后,您需要使用 pdfkit 生成 pdf,这非常简单:
import pdfkit
pdfkit.from_url('template.html', 'out.pdf')
使用Excel操作
我不会深入探讨 excel 方法,但如果您愿意这样做,可以考虑使用 openpyxl and you can see a number of examples here
生成 excel 后,除了 pdf 工具包之外,您还可以使用 pandas 将其转换为 pdf,您可以在 this 问题的答案中找到更多信息,但是为了完整起见,将信息保留在这里:
- 将excel转换为pandas对象
- 将 pandas 对象转换为 html
- 将 html 转换为 pdf
取自 Thomas Devoogdt 的示例代码 answer:
import pandas as pd
import pdfkit
df = pd.read_excel("file.xlsx")
df.to_html("file.html")
pdfkit.from_file("file.html", "file.pdf")
结论
虽然有许多其他方法可以生成这种布局(例如使用 OpenCV),但手动生成您想要的布局可能会变得非常复杂,因此使用 excel 或最好使用 HTML-manipulation(因为你似乎已经使用它并且可以创建你在 html 中想到的布局)会给你一个更灵活的方法来使用该方法。
您可以在 OS(而不是 python)上使用 jinja for html templating and pdfkit to convert to pdf (for pdfkit to work you may have to install wkhtmltopdf。我根据您的数据和您的 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()
organise_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 是一个模板 language/library,所以你可以创建 html 其中包含一些变量和逻辑(大部分在 {}
括号中),所以你不必创建整个 html由 python 本身。
我决定为每个课程创建元组,其中包含课程名称和持续时间(连续 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
)
我正在解决时间表安排问题,并希望以 PDF 或一组图像的形式打印出最终输出。我有多个部分,每个部分都有自己的时间表。
我为每个部分创建了一个二维数组。该数组的大小为 5 x 5(5 天,每天有 5 个五个时段),数组的每个索引代表一个讲座时段。现在,这个二维数组包含该特定部分时间表中每门课程的讲座。示例输出如下,(它是一个字典,每个键都是一个部分。每个键的值是一个二维数组
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 ']]
请注意CS-1D作为示例,
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 ']]
有两件事我需要处理。首先,每个实验室(以 -L 结尾的课程在 连续时段 中都有讲座。这意味着,我想要单元格(时间表中的两个单元格)表示实验室时横向合并
其次,在某些索引中,两个讲座同时进行。例如,请注意 CS-1D 中星期一的第 4 个槽(0 索引)。 ItIaCT和ECaC-L是两门不同的课程,但同时有讲座。 (在这个 2D Array 中,如果同时发生两个或多个讲座,则在该索引中用 space 分隔它们)。为此,我希望水平划分该讲座时段的单元格以适合两个讲座。
示例最终输出看起来像这样(每个单元格还将告诉讲师正在教授课程以及 class 在哪个房间举行)
我不想要 13 个不同的时段,而是每天只需要 五个时段。我的问题是,
我必须使用 Python 执行此操作,但我不知道如何开始。我有使用算法为每个部分创建的时间表(如上所示),但我无法弄清楚如何从中制作时间表(输出)
其次,我想制作一个 PDF 文件,其中将包含所有部分的时间表。我不知道该怎么做。我假设我需要为每个部分的时间表制作一个 Image,然后将所有这些图像(就像我在上面分享一个部分时间表的一张图像一样)合并到一个 PDF 中。但是,我不知道如何将一张时间表转换为图像。
此外,请注意,我使用纯 HTML 做了类似的东西,我将在下面分享其代码和结果。我正在尝试使用 Python.
复制一些类似的东西<!DOCTYPE html>
<html>
<style>
.center
{
text-align: center;
}
td{
height:75px;
width:150px;
}
</style>
<body>
<!-- Heading -->
<h1 class="center">BCS-7D</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>
<td class="center">Linear Algebra, Mr. Raheel Ahmad, Room 1</td>
<td class="center">X</td>
<td class="center">X</td>
<td rowspan="6" class="center">
<h2>L<br>U<br>N<br>C<br>H</h2>
</td>
<td colspan="2" class="center">LAB</td>
</tr>
<!-- Tuesday -->
<tr>
<td class="center">
<b>Tuesday</b>
</td>
<td class="center">X</td>
<td colspan="2" class="center">LAB
</td>
<td class="center">X</td>
<td class="center">X</td>
</tr>
<!-- Wednesday -->
<tr>
<td class="center">
<b>Wednesday</b>
</td>
<td class="center">Object Oriented Programming, Ms. Jen Ledger, Room 13<hr>Programming Fundamentals, Mr. Zahid Iqbal, Room 6</td>
<td class="center">X</td>
<td class="center">X</td>
<td class="center">X</td>
<td colspan="3" class="center">X
</td>
</tr>
<!-- Thursday -->
<tr>
<td class="center">
<b>Thursday</b>
</td>
<td class="center">X</td>
<td class="center">X</td>
<td class="center">X</td>
<td colspan="3" class="center">Object Oriented Programming - Lab, Ms. Zain Malik, Lab 6
</td>
</tr>
<!-- Friday -->
<tr>
<td class="center">
<b>Friday</b>
</td>
<td colspan="2" class="center">LAB
</td>
<td class="center">X</td>
<td class="center">X</td>
<td class="center">X</td>
</tr>
</table>
</body>
</html>
输出的屏幕截图,(请注意,这是硬编码布局。实验可以在时间表中的任何位置(对于实验,必须合并两个连续的时段)并且两个讲座位于同一时间也可能随时发生。为此,该讲座时段应该有一个水平分隔符)
有很多方法可以用动态数据生成这样的布局,一般来说 python 我会推荐你使用 excel,或者使用基于网络的布局,然后使用生成您的输出 pdf。
使用HTML操作
如果我们谈论的是基于网络的布局,您将需要使用 beautifulsoup,您的方法将如下所示:
创建一个标准布局,每个位置的单元格都有唯一的 ID(您可以使用与上一个示例类似的内容)。
遍历可能需要拆分的较大容器,您可以使用带有 if 条件的 beautifulsoup 来添加不同的 html,具体取决于您是否需要拆分数据与否,例如:
import bs4
with open("template.html") as t:
text = t.read()
soup = bs4.BeautifulSoup(text)
td = soup.find(id="B6")
entry = soup.new_tag("p")
entry.append("LAB")
# insert the new tag after the current tag
td.insert_after(new_tag)
您可以在此阶段额外动态添加布局,可能逐行添加 td 标签而不是其内容。
最后,完成后您可以使用以下方式保存您的 html 文件:
with open("template.html", "w") as t:
t.write(str(soup))
- 保存后,您需要使用 pdfkit 生成 pdf,这非常简单:
import pdfkit
pdfkit.from_url('template.html', 'out.pdf')
使用Excel操作
我不会深入探讨 excel 方法,但如果您愿意这样做,可以考虑使用 openpyxl and you can see a number of examples here
生成 excel 后,除了 pdf 工具包之外,您还可以使用 pandas 将其转换为 pdf,您可以在 this 问题的答案中找到更多信息,但是为了完整起见,将信息保留在这里:
- 将excel转换为pandas对象
- 将 pandas 对象转换为 html
- 将 html 转换为 pdf
取自 Thomas Devoogdt 的示例代码 answer:
import pandas as pd
import pdfkit
df = pd.read_excel("file.xlsx")
df.to_html("file.html")
pdfkit.from_file("file.html", "file.pdf")
结论
虽然有许多其他方法可以生成这种布局(例如使用 OpenCV),但手动生成您想要的布局可能会变得非常复杂,因此使用 excel 或最好使用 HTML-manipulation(因为你似乎已经使用它并且可以创建你在 html 中想到的布局)会给你一个更灵活的方法来使用该方法。
您可以在 OS(而不是 python)上使用 jinja for html templating and pdfkit to convert to pdf (for pdfkit to work you may have to install wkhtmltopdf。我根据您的数据和您的 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()
organise_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 是一个模板 language/library,所以你可以创建 html 其中包含一些变量和逻辑(大部分在 {}
括号中),所以你不必创建整个 html由 python 本身。
我决定为每个课程创建元组,其中包含课程名称和持续时间(连续 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
)