如何使用 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 个不同的时段,而是每天只需要 五个时段。我的问题是,

此外,请注意,我使用纯 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,您的方法将如下所示:

  1. 创建一个标准布局,每个位置的单元格都有唯一的 ID(您可以使用与上一个示例类似的内容)。

  2. 遍历可能需要拆分的较大容器,您可以使用带有 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)
  1. 您可以在此阶段额外动态添加布局,可能逐行添加 td 标签而不是其内容。

  2. 最后,完成后您可以使用以下方式保存您的 html 文件:

with open("template.html", "w") as t:
    t.write(str(soup))
  1. 保存后,您需要使用 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 问题的答案中找到更多信息,但是为了完整起见,将信息保留在这里:

  1. 将excel转换为pandas对象
  2. 将 pandas 对象转换为 html
  3. 将 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
    )