Please note this question is an extension of this previously asked question: How to make Images/PDF of Timetable using Python
I am working on a program that generates randomized Timetable based on an algorithm. For the Final Output of that program, I require a Timetable to be stored in a PDF File.
There are multiple sections and each section must have its own timetable/schedule. Each Section can have multiple Courses whose lectures will be allocated on different slots from Monday to Friday by the algorithm. For my timetable,
As an Example, I have created below a dictionary where key represents the Section and the items have a 2D Array of size 5x5. Each Index of that 2D array contains the course details for which the lecture will take place in that slot.
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 ']]
This, however, only contains "strings'. While, in actual data, there would be object of a course instead of that string. That object will contain different information regarding that course for which the lecture is going to take place on that specific slot of the day.
While generating the timetable, there are three major concerns that must be resolved,
Lectures for "Labs" are held over two consecutive slots. So, for all the labs, those two cells in the table must be merged
It is possible that at the same time, two or more than two different lectures are taking place for one section. In such a case, the cell of that slot must be horizontally divided into two or more cells
It is also possible that at the same time, two lectures are being held at one slot and one of those two lectures can be of Lab. In such a case, two consecutive cells should be merged and also horizontally divided to accomadate both the lectures.
With help of previous post, I was able to transform this Data into PDF format using Jinja library. However, it was only doing it on strings, by differentiating between consecutive lectures with space character. Now, I have complete objects and I am unable to do the same with them.
I placed a list of objects on each index of 2D Array. That list may contain no object, one object or multiple objects (one object represents one course). Moreover, there is a single object for a Lab course (and that single object is added to the two consecutive indexes in which lab lecture is being held)
Following is the complete code (I have created sample data for only two sections)
from typing import List
import pdfkit
from pdfkit.api import configuration
from jinja2 import FileSystemLoader, Environment
wkhtml_path = pdfkit.configuration(wkhtmltopdf = "C:\\Program Files\\wkhtmltopdf\\bin\\wkhtmltopdf.exe") #by using configuration you can add path value.
class section:
def __init__(self, id, course, section, instructor, room):
self.id = id
self.course = course
self.section = section
self.instructor = instructor
self.room = room
def generate_data():
data = {}
# For CS-1A
data["CS-1A"] = [[[] for i in range(5)] for j in range(5)]
data["CS-1A"][0][0].append(section(1, "Advance Programming", "CS-1A", "Mr. Ashas", "Room #2"))
data["CS-1A"][0][0].append(section(2, "Programming Fundamentals", "CS-1A", "Bilal", "Room #1"))
data["CS-1A"][0][1].append(section(3, "Applied Physics", "CS-1A", "Muhammad Bilal", "Room #5"))
obj = section(4, "Programming Lab", "CS-1A", "Mr. Abid", "Lab #01")
data["CS-1A"][0][1].append(obj)
data["CS-1A"][0][2].append(obj)
data["CS-1A"][0][4].append(section(5, "English Communication", "CS-1A1", "Ms. Ayeza", "Cal Lab"))
data["CS-1A"][0][4].append(section(6, "English Communication", "CS-1A2", "Ms. Ayesha", "GP Lab"))
data["CS-1A"][1][1].append(section(7, "Linear Algebra", "CS-1A", "Ms. Zain", "Romm #2"))
obj = section(8, "English Lab", "CS-1A", "Ms. Abida", "Lab #04")
data["CS-1A"][1][3].append(obj)
data["CS-1A"][1][4].append(obj)
data["CS-1A"][2][0].append(section(9, "Social Studies", "CS-1A", "Mr. Zain Iqbal", "Room #14"))
data["CS-1A"][2][4].append(section(10, "Programming Fundamentals", "CS-1A", "Bilal", "Room #15"))
# No lectures on Thursday i.e. 3rd index
obj = section(11, "English Lab", "CS-1A", "Ms. Abida", "Lab #03")
data["CS-1A"][4][1].append(obj)
data["CS-1A"][4][2].append(obj)
data["CS-1A"][4][2].append(section(12, "English Communication", "CS-1A1", "Ms. Ayeza", "Room #3"))
data["CS-1A"][4][2].append(section(13, "English Communication", "CS-1A2", "Ms. Ayesha", "GP Lab"))
# For CS-1B
data["CS-1B"] = [[[] for i in range(5)] for j in range(5)]
data["CS-1B"][0][1].append(section(14, "Advance Programming", "CS-1B", "Mr. Ali Hassan", "Room #2"))
data["CS-1B"][0][2].append(section(15, "English Comprehension and Communication", "CS-1B", "Mr. Bilal", "Room #1"))
obj = section(15, "Software for Mobile and Devices", "CS-1B", "Mr. Jacob", "Room #5")
data["CS-1B"][1][1].append(obj)
data["CS-1B"][1][2].append(obj)
data["CS-1B"][1][3].append(section(17, "Calculus and Geometrical Analytics", "CS-1B", "Ms. Nomi Khan", "Room #06"))
data["CS-1B"][2][3].append(section(18, "Introduction to Information and Communication Technology - Lab", "CS-1B1", "Ms. Sarah Niaz", "Lab #01"))
data["CS-1B"][2][3].append(section(19, "Introduction to Information and Communication Technology - Lab", "CS-1B2", "Ms. Julia John", "Lab #06"))
data["CS-1B"][2][4].append(section(20, "English Comprehenseion and Communication", "Cs-1B", "Mr. Lionel Boyle", "Room #02"))
data["CS-1B"][3][3].append(section(21, "Programming Fundamentals", "CS-1B", "Mr. Alex Niles", "Room #3"))
data["CS-1B"][4][2].append(section(22, "Linear Algebra", "CS-1B", "Ms. Ayesha", "GP Lab"))
return data
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:
# check if long course (and not lunch time)
if not course:
course_list.append(("", "", "", 1))
else:
if course[0] == last_course and index != 3:
#for sub_course in course:
course_list.remove((course[0].course, course[0].section, course[0].instructor, 1))
course_list.append((course[0].course, course[0].section, course[0].instructor, 2))
course_list.append(("none", "none", "none", 0))
last_course = course[0]
else:
#for sub_course in course:
course_list.append((course[0].course, course[0].section, course[0].instructor, 1))
last_course = course[0]
index += 1
#print(course_list)
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(input_data):
# 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, "out1.pdf", configuration = wkhtml_path)
if __name__ == '__main__':
data = generate_data()
run(data)
For now, it will only consider the first Course and will place it in the Timetable, while ignoring the rest of the courses allocated on that same slot.
A sample Final Output would be like this (but with only 5 slots, not 13 slots like here)
The template.html
file for my code has this following code right now,
<!DOCTYPE html>
<html>
<style>
.center
{
text-align: center;
}
.left
{
text-align: left;
margin-left: 6px;
/*margin-top: 10px;*/
}
.right
{
text-align: right; margin-right: 4px;
}
.teacher
{
margin-left: 4px;
}
td{
height:100px;
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[3] != 0 %}
<td colspan={{course[3]}}>
<p class="left">{{course[0]}}</p>
<p class="right">{{course[1]}}</p>
<p class="teacher">{{course[2]}}</p>
</td>
{% endif %}
{% endfor %}
</tr>
<!-- Tuesday -->
<tr>
<td class="center">
<b>Tuesday</b>
</td>
{% for course in tuesday %}
{% if course[3] != 0 %}
<td colspan={{course[3]}}>
<p class="left">{{course[0]}}</p>
<p class="right">{{course[1]}}</p>
<p class="teacher">{{course[2]}}</p>
</td>
{% endif %}
{% endfor %}
</tr>
<!-- Wednesday -->
<tr>
<td class="center">
<b>Wednesday</b>
</td>
{% for course in wednesday %}
{% if course[3] != 0 %}
<td colspan={{course[3]}}>
<p class="left">{{course[0]}}</p>
<p class="right">{{course[1]}}</p>
<p class="teacher">{{course[2]}}</p></td>
{% endif %}
{% endfor %}
</tr>
<!-- Thursday -->
<tr>
<td class="center">
<b>Thursday</b>
</td>
{% for course in thursday %}
{% if course[3] != 0 %}
<td colspan={{course[3]}}>
<p class="left">{{course[0]}}</p>
<p class="right">{{course[1]}}</p>
<p class="teacher">{{course[2]}}</p></td>
{% endif %}
{% endfor %}
</tr>
<!-- Friday -->
<tr>
<td class="center">
<b>Friday</b>
</td>
{% for course in friday %}
{% if course[3] != 0 %}
<td colspan={{course[3]}}>
<p class="left">{{course[0]}}</p>
<p class="right">{{course[1]}}</p>
<p class="teacher">{{course[2]}}</p>
</td>
{% endif %}
{% endfor %}
</tr>
</table>
</body>
</html>
I am not much familiar with Jinja, so this answer might not be the most efficient one.
By using basic hard coding in your Template.HTML file, I was able to achieve the results you are trying to. For this, I used the same code that was given by D-E-N
in your previous question.
I combined all the attributes of your object into a string
@
(like Course and Teacher)space character
, I used a _
character to represent space character
in the attributes.space character
(just like in the code provided by D-E-N
)Here's the updated code of yours with these changes,
from typing import List
import pdfkit
from pdfkit.api import configuration
from jinja2 import FileSystemLoader, Environment
wkhtml_path = pdfkit.configuration(wkhtmltopdf = "C:\\Program Files\\wkhtmltopdf\\bin\\wkhtmltopdf.exe") #by using configuration you can add path value.
class section:
def __init__(self, id, course, section, instructor, room):
self.id = id
self.course = course
self.section = section
self.instructor = instructor
self.room = room
def generate_data():
data = {}
# For CS-1A
data["CS-1A"] = [["" for i in range(5)] for j in range(5)]
data["CS-1A"][0][0] += "Advance_Programming@Mr.Ashas@Room_#2 "
data["CS-1A"][0][0] += "Applied_Physics@Bilal@Room_#1 "
data["CS-1A"][0][1] += "Programming_Fundamentals@Muhammad_Bilal@Room_#1 "
data["CS-1A"][0][3] += "Programming_Fundamentals_Lab@Mr._Abid@Lab_#01 "
data["CS-1A"][0][4] += "Programming_Fundamentals_Lab@Mr._Abid@Lab_#01 "
# You may change all these for testing purposes.
# data["CS-1A"][0][0].append(section(2, "Programming Fundamentals", "CS-1A", "Bilal", "Room #1"))
# data["CS-1A"][0][1].append(section(3, "Applied Physics", "CS-1A", "Muhammad Bilal", "Room #5"))
# obj = section(4, "Programming Lab", "CS-1A", "Mr. Abid", "Lab #01")
# data["CS-1A"][0][1].append(obj)
# data["CS-1A"][0][2].append(obj)
# data["CS-1A"][0][4].append(section(5, "English Communication", "CS-1A1", "Ms. Ayeza", "Cal Lab"))
# data["CS-1A"][0][4].append(section(6, "English Communication", "CS-1A2", "Ms. Ayesha", "GP Lab"))
# data["CS-1A"][1][1].append(section(7, "Linear Algebra", "CS-1A", "Ms. Zain", "Romm #2"))
# obj = section(8, "English Lab", "CS-1A", "Ms. Abida", "Lab #04")
# data["CS-1A"][1][3].append(obj)
# data["CS-1A"][1][4].append(obj)
# data["CS-1A"][2][0].append(section(9, "Social Studies", "CS-1A", "Mr. Zain Iqbal", "Room #14"))
# data["CS-1A"][2][4].append(section(10, "Programming Fundamentals", "CS-1A", "Bilal", "Room #15"))
# # No lectures on Thursday i.e. 3rd index
# obj = section(11, "English Lab", "CS-1A", "Ms. Abida", "Lab #03")
# data["CS-1A"][4][1].append(obj)
# data["CS-1A"][4][2].append(obj)
# data["CS-1A"][4][2].append(section(12, "English Communication", "CS-1A1", "Ms. Ayeza", "Room #3"))
# data["CS-1A"][4][2].append(section(13, "English Communication", "CS-1A2", "Ms. Ayesha", "GP Lab"))
#
#
#
# # For CS-1B
# data["CS-1B"] = [[[] for i in range(5)] for j in range(5)]
#
# data["CS-1B"][0][1].append(section(14, "Advance Programming", "CS-1B", "Mr. Ali Hassan", "Room #2"))
# data["CS-1B"][0][2].append(section(15, "English Comprehension and Communication", "CS-1B", "Mr. Bilal", "Room #1"))
# obj = section(15, "Software for Mobile and Devices", "CS-1B", "Mr. Jacob", "Room #5")
# data["CS-1B"][1][1].append(obj)
# data["CS-1B"][1][2].append(obj)
# data["CS-1B"][1][3].append(section(17, "Calculus and Geometrical Analytics", "CS-1B", "Ms. Nomi Khan", "Room #06"))
# data["CS-1B"][2][3].append(section(18, "Introduction to Information and Communication Technology - Lab", "CS-1B1", "Ms. Sarah Niaz", "Lab #01"))
# data["CS-1B"][2][3].append(section(19, "Introduction to Information and Communication Technology - Lab", "CS-1B2", "Ms. Julia John", "Lab #06"))
# data["CS-1B"][2][4].append(section(20, "English Comprehenseion and Communication", "Cs-1B", "Mr. Lionel Boyle", "Room #02"))
# data["CS-1B"][3][3].append(section(21, "Programming Fundamentals", "CS-1B", "Mr. Alex Niles", "Room #3"))
# data["CS-1B"][4][2].append(section(22, "Linear Algebra", "CS-1B", "Ms. Ayesha", "GP Lab"))
return data
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(input_data):
# 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", configuration = wkhtml_path)
if __name__ == '__main__':
data = generate_data()
run(data)
Major changes are done in the template.html
file to achieve what you are trying to achieve. Here's the Code,
<!DOCTYPE html>
<html>
<style>
.center
{
text-align: center;
}
.left
{
text-align: left;
margin-left: 6px;
/*margin-top: 10px;*/
}
.right
{
text-align: right; margin-right: 4px;
}
.teacher
{
margin-left: 4px;
}
td{
height:175px;
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]}}>
{% set count = [] %}
{%- for y in range(0, 5) -%}
{%- if count|length < course[0]|length -%}
<p class="left">
{%- for z in range(count|length, course[0]|length) -%}
{%- if course[0][count|length] == '@' -%}
{%- else -%}
{% set __ = count.append(1) %}
{%- if course[0][count|length-1] == '_' -%}
{{' '}}
{%- else -%}
{{course[0][count|length-1]}}
{%- endif -%}
{%- endif -%}
{%- endfor -%}
</p>
{% set __ = count.append(1) %}
<p class = "right">
{%- for z in range(count|length, course[0]|length) -%}
{%- if course[0][count|length] == '@' -%}
{%- else -%}
{% set __ = count.append(1) %}
{%- if course[0][count|length-1] == '_' -%}
{{' '}}
{%- else -%}
{{course[0][count|length-1]}}
{%- endif -%}
{%- endif -%}
{%- endfor -%}
</p>
{% set __ = count.append(1) %}
<p class = "teacher">
{%- for z in range(count|length, course[0]|length) -%}
{%- if course[0][count|length] == '@' -%}
{%- else -%}
{% set __ = count.append(1) %}
{%- if course[0][count|length-1] == '_' -%}
{{' '}}
{%- else -%}
{{course[0][count|length-1]}}
{%- endif -%}
{%- endif -%}
{%- endfor -%}
</p>
<!-- <p class="left">{{course[0]}}</p>
<p class="right">{{course[1]}}</p>
<p class="teacher">{{course[2]}}</p> -->
{%- endif -%}
{%- endfor -%}
</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]}}>
{% set count = [] %}
{%- for y in range(0, 5) -%}
{%- if count|length < course[0]|length -%}
<p class="left">
{%- for z in range(count|length, course[0]|length) -%}
{%- if course[0][count|length] == '@' -%}
{%- else -%}
{% set __ = count.append(1) %}
{%- if course[0][count|length-1] == '_' -%}
{{' '}}
{%- else -%}
{{course[0][count|length-1]}}
{%- endif -%}
{%- endif -%}
{%- endfor -%}
</p>
{% set __ = count.append(1) %}
<p class = "right">
{%- for z in range(count|length, course[0]|length) -%}
{%- if course[0][count|length] == '@' -%}
{%- else -%}
{% set __ = count.append(1) %}
{%- if course[0][count|length-1] == '_' -%}
{{' '}}
{%- else -%}
{{course[0][count|length-1]}}
{%- endif -%}
{%- endif -%}
{%- endfor -%}
</p>
{% set __ = count.append(1) %}
<p class = "teacher">
{%- for z in range(count|length, course[0]|length) -%}
{%- if course[0][count|length] == '@' -%}
{%- else -%}
{% set __ = count.append(1) %}
{%- if course[0][count|length-1] == '_' -%}
{{' '}}
{%- else -%}
{{course[0][count|length-1]}}
{%- endif -%}
{%- endif -%}
{%- endfor -%}
</p>
<!-- <p class="left">{{course[0]}}</p>
<p class="right">{{course[1]}}</p>
<p class="teacher">{{course[2]}}</p> -->
{%- endif -%}
{%- endfor -%}
</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]}}>
{% set count = [] %}
{%- for y in range(0, 5) -%}
{%- if count|length < course[0]|length -%}
<p class="left">
{%- for z in range(count|length, course[0]|length) -%}
{%- if course[0][count|length] == '@' -%}
{%- else -%}
{% set __ = count.append(1) %}
{%- if course[0][count|length-1] == '_' -%}
{{' '}}
{%- else -%}
{{course[0][count|length-1]}}
{%- endif -%}
{%- endif -%}
{%- endfor -%}
</p>
{% set __ = count.append(1) %}
<p class = "right">
{%- for z in range(count|length, course[0]|length) -%}
{%- if course[0][count|length] == '@' -%}
{%- else -%}
{% set __ = count.append(1) %}
{%- if course[0][count|length-1] == '_' -%}
{{' '}}
{%- else -%}
{{course[0][count|length-1]}}
{%- endif -%}
{%- endif -%}
{%- endfor -%}
</p>
{% set __ = count.append(1) %}
<p class = "teacher">
{%- for z in range(count|length, course[0]|length) -%}
{%- if course[0][count|length] == '@' -%}
{%- else -%}
{% set __ = count.append(1) %}
{%- if course[0][count|length-1] == '_' -%}
{{' '}}
{%- else -%}
{{course[0][count|length-1]}}
{%- endif -%}
{%- endif -%}
{%- endfor -%}
</p>
<!-- <p class="left">{{course[0]}}</p>
<p class="right">{{course[1]}}</p>
<p class="teacher">{{course[2]}}</p> -->
{%- endif -%}
{%- endfor -%}
</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]}}>
{% set count = [] %}
{%- for y in range(0, 5) -%}
{%- if count|length < course[0]|length -%}
<p class="left">
{%- for z in range(count|length, course[0]|length) -%}
{%- if course[0][count|length] == '@' -%}
{%- else -%}
{% set __ = count.append(1) %}
{%- if course[0][count|length-1] == '_' -%}
{{' '}}
{%- else -%}
{{course[0][count|length-1]}}
{%- endif -%}
{%- endif -%}
{%- endfor -%}
</p>
{% set __ = count.append(1) %}
<p class = "right">
{%- for z in range(count|length, course[0]|length) -%}
{%- if course[0][count|length] == '@' -%}
{%- else -%}
{% set __ = count.append(1) %}
{%- if course[0][count|length-1] == '_' -%}
{{' '}}
{%- else -%}
{{course[0][count|length-1]}}
{%- endif -%}
{%- endif -%}
{%- endfor -%}
</p>
{% set __ = count.append(1) %}
<p class = "teacher">
{%- for z in range(count|length, course[0]|length) -%}
{%- if course[0][count|length] == '@' -%}
{%- else -%}
{% set __ = count.append(1) %}
{%- if course[0][count|length-1] == '_' -%}
{{' '}}
{%- else -%}
{{course[0][count|length-1]}}
{%- endif -%}
{%- endif -%}
{%- endfor -%}
</p>
<!-- <p class="left">{{course[0]}}</p>
<p class="right">{{course[1]}}</p>
<p class="teacher">{{course[2]}}</p> -->
{%- endif -%}
{%- endfor -%}
</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]}}>
{% set count = [] %}
{%- for y in range(0, 5) -%}
{%- if count|length < course[0]|length -%}
<p class="left">
{%- for z in range(count|length, course[0]|length) -%}
{%- if course[0][count|length] == '@' -%}
{%- else -%}
{% set __ = count.append(1) %}
{%- if course[0][count|length-1] == '_' -%}
{{' '}}
{%- else -%}
{{course[0][count|length-1]}}
{%- endif -%}
{%- endif -%}
{%- endfor -%}
</p>
{% set __ = count.append(1) %}
<p class = "right">
{%- for z in range(count|length, course[0]|length) -%}
{%- if course[0][count|length] == '@' -%}
{%- else -%}
{% set __ = count.append(1) %}
{%- if course[0][count|length-1] == '_' -%}
{{' '}}
{%- else -%}
{{course[0][count|length-1]}}
{%- endif -%}
{%- endif -%}
{%- endfor -%}
</p>
{% set __ = count.append(1) %}
<p class = "teacher">
{%- for z in range(count|length, course[0]|length) -%}
{%- if course[0][count|length] == '@' -%}
{%- else -%}
{% set __ = count.append(1) %}
{%- if course[0][count|length-1] == '_' -%}
{{' '}}
{%- else -%}
{{course[0][count|length-1]}}
{%- endif -%}
{%- endif -%}
{%- endfor -%}
</p>
<!-- <p class="left">{{course[0]}}</p>
<p class="right">{{course[1]}}</p>
<p class="teacher">{{course[2]}}</p> -->
{%- endif -%}
{%- endfor -%}
</td>
{% endif %}
{% endfor %}
</tr>
</table>
</body>
</html>
This produces the following result,