TiH0 commited on
Commit
216f2ae
·
verified ·
1 Parent(s): 1228d05

Update prof.py

Browse files
Files changed (1) hide show
  1. prof.py +188 -42
prof.py CHANGED
@@ -93,10 +93,6 @@ def set_two_column_layout(doc, add_separator_line=True, balance_columns=True):
93
  # Set space between columns (reduced for better space utilization)
94
  cols.set(qn('w:space'), '432') # 0.3 inch in twentieths of a point (was 708)
95
 
96
- # Add separator line between columns if requested
97
- if add_separator_line:
98
- cols.set(qn('w:sep'), '1') # This adds the vertical separator line
99
-
100
  # Enable column balancing if requested
101
  if balance_columns:
102
  cols.set(qn('w:equalWidth'), '1') # Equal width columns
@@ -162,27 +158,138 @@ def add_page_break(doc):
162
  doc.add_page_break()
163
 
164
 
165
- def create_course_title(doc, course_number, course_title, theme_color=None):
166
- """Create a course title section in the document and return the paragraph object"""
 
 
 
167
  if theme_color is None:
168
- theme_color = THEME_COLOR
169
 
170
  # Add minimal space before course title
171
  course_para = doc.add_paragraph()
172
  course_para.alignment = WD_ALIGN_PARAGRAPH.CENTER
173
 
174
- # Reduce spacing before and after
175
- course_para.paragraph_format.space_before = Pt(6)
176
- course_para.paragraph_format.space_after = Pt(3)
177
- course_para.paragraph_format.keep_with_next = True # Keep course title with first question
178
- course_para.paragraph_format.keep_together = True # Prevent splitting within paragraph
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
179
 
180
- course_run = course_para.add_run(f"{course_number}. {course_title}")
181
- course_run.font.name = 'Montserrat'
182
- course_run.font.size = Pt(13)
183
- course_run.font.bold = True
184
- course_run.font.color.rgb = None
185
- course_run.font.color.rgb = theme_color
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
186
 
187
  return course_para
188
 
@@ -211,14 +318,14 @@ def format_question_block(doc, question_num, question_text, choices, correct_ans
211
  # Question number in Axiforma Black
212
  num_run = question_para.add_run(f"{question_num}. ")
213
  num_run.font.name = 'Inter ExtraBold'
214
- num_run.font.size = Pt(10)
215
  num_run.font.bold = True
216
  num_run.font.color.rgb = theme_color
217
 
218
  # Question text in SF UI Display Med
219
  text_run = question_para.add_run(question_text)
220
  text_run.font.name = 'Inter ExtraBold'
221
- text_run.font.size = Pt(10)
222
 
223
  # Display ALL choices for this question with minimal spacing
224
  choice_paragraphs = []
@@ -242,7 +349,7 @@ def format_question_block(doc, question_num, question_text, choices, correct_ans
242
 
243
  choice_run = choice_para.add_run(f"{choice_letter}- {choice_text}")
244
  choice_run.font.name = 'Inter Display Medium'
245
- choice_run.font.size = Pt(10)
246
 
247
  choice_paragraphs.append(choice_para)
248
 
@@ -298,13 +405,13 @@ def format_question_block(doc, question_num, question_text, choices, correct_ans
298
 
299
  answer_run = left_para.add_run("Réponse:")
300
  answer_run.font.name = 'Inter SemiBold'
301
- answer_run.font.size = Pt(8)
302
  answer_run.font.bold = True
303
  answer_run.font.underline = True
304
 
305
  answer_run = left_para.add_run(f' {correct_answers}')
306
- answer_run.font.name = 'Inter Display Medium'
307
- answer_run.font.size = Pt(8)
308
  answer_run.font.color.rgb = theme_color
309
 
310
  # RIGHT cell - Source
@@ -315,13 +422,13 @@ def format_question_block(doc, question_num, question_text, choices, correct_ans
315
 
316
  source_run = right_para.add_run("Source:")
317
  source_run.font.name = 'Inter SemiBold'
318
- source_run.font.size = Pt(8)
319
  source_run.font.bold = True
320
  source_run.font.underline = True
321
 
322
  source_value_run = right_para.add_run(f" {source}")
323
- source_value_run.font.name = 'Inter Display Medium'
324
- source_value_run.font.size = Pt(8)
325
  source_value_run.font.color.rgb = theme_color
326
 
327
  # Keep with comment if exists
@@ -331,9 +438,9 @@ def format_question_block(doc, question_num, question_text, choices, correct_ans
331
  empty_para = doc.add_paragraph(' ', style='TinySpace')
332
  empty_para.paragraph_format.space_before = Pt(0)
333
  empty_para.paragraph_format.space_after = Pt(0)
334
- empty_para.paragraph_format.line_spacing = Pt(5)
335
  empty_run = empty_para.add_run(' ')
336
- empty_run.font.size = Pt(5)
337
 
338
  # Add comment if exists
339
  if comment and str(comment).strip() and str(comment).lower() != 'nan':
@@ -359,7 +466,7 @@ def add_page_numbers(doc, theme_hex=None):
359
  # ===== HEADER (keep existing text like module name) =====
360
  header = section.header
361
  header.is_linked_to_previous = False
362
- section.header_distance = Cm(0.6)
363
 
364
  # If header is empty, add a blank paragraph
365
  if not header.paragraphs:
@@ -422,7 +529,7 @@ def add_page_numbers(doc, theme_hex=None):
422
  run._r.append(fldChar2)
423
 
424
  run.font.name = 'Montserrat'
425
- run.font.size = Pt(12)
426
  run.font.bold = True
427
  run.font.color.rgb = RGBColor(0, 0, 0)
428
 
@@ -445,7 +552,7 @@ def add_page_numbers(doc, theme_hex=None):
445
  <w:r>
446
  <w:rPr>
447
  <w:rFonts w:ascii="Aptos" w:hAnsi="Aptos"/>
448
- <w:sz w:val="28"/>
449
  <w:color w:val="{theme_hex}"/>
450
  <w:u w:val="single"/>
451
  </w:rPr>
@@ -455,7 +562,7 @@ def add_page_numbers(doc, theme_hex=None):
455
  <w:rPr>
456
  <w:rFonts w:ascii="Montserrat" w:hAnsi="Montserrat"/>
457
  <w:b/>
458
- <w:sz w:val="18"/>
459
  <w:color w:val="{theme_hex}"/>
460
  <w:u w:val="single"/>
461
  </w:rPr>
@@ -778,7 +885,7 @@ def create_flexible_header(section, module_name, sheet_name, display_name=None,
778
  <w:rPr>
779
  <w:rFonts w:ascii="Montserrat" w:hAnsi="Montserrat"/>
780
  <w:b/>
781
- <w:sz w:val="20"/>
782
  <w:color w:val="{theme_hex}"/>
783
  </w:rPr>
784
  <w:t>{module_name_str}</w:t>
@@ -809,7 +916,7 @@ def create_flexible_header(section, module_name, sheet_name, display_name=None,
809
  <w:rPr>
810
  <w:rFonts w:ascii="Montserrat" w:hAnsi="Montserrat"/>
811
  <w:b/>
812
- <w:sz w:val="20"/>
813
  <w:color w:val="{theme_hex}"/>
814
  </w:rPr>
815
  <w:t>{sheet_name_str}</w:t>
@@ -831,6 +938,45 @@ def create_flexible_header(section, module_name, sheet_name, display_name=None,
831
  header_para._p.append(right_textbox_element)
832
 
833
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
834
  def process_excel_to_word(excel_file_path, output_word_path, display_name=None, use_two_columns=True, add_separator_line=True, balance_method="dynamic", theme_hex=None):
835
  """Main function to process Excel and create a Word document with TOC on the first page"""
836
 
@@ -918,9 +1064,9 @@ def process_excel_to_word(excel_file_path, output_word_path, display_name=None,
918
  # ========================================
919
  # ADD THREE EMPTY PAGES AT THE BEGINNING
920
  # ========================================
921
- for i in range(4):
922
  doc.add_paragraph() # Add empty paragraph
923
- if i < 3: # Add page breaks for first 2 pages (3rd page leads to TOC)
924
  doc.add_page_break()
925
 
926
  # TOC helpers
@@ -1121,8 +1267,6 @@ def process_excel_to_word(excel_file_path, output_word_path, display_name=None,
1121
  sectPr.append(cols)
1122
  cols.set(qn('w:num'), '2')
1123
  cols.set(qn('w:space'), '432')
1124
- if add_separator_line:
1125
- cols.set(qn('w:sep'), '1')
1126
  cols.set(qn('w:equalWidth'), '1')
1127
 
1128
  if use_two_columns:
@@ -1133,13 +1277,15 @@ def process_excel_to_word(excel_file_path, output_word_path, display_name=None,
1133
  sectPr.append(cols)
1134
  cols.set(qn('w:num'), '2')
1135
  cols.set(qn('w:space'), '432')
1136
- if add_separator_line:
1137
- cols.set(qn('w:sep'), '1')
1138
  cols.set(qn('w:equalWidth'), '1')
1139
 
1140
  # Use the new flexible header function
1141
  create_flexible_header(section, module_name, first_sheet_name, display_name, theme_hex=theme_hex)
1142
 
 
 
 
 
1143
  # ========== CUSTOMIZE MODULE TITLE APPEARANCE HERE ==========
1144
  MODULE_HEIGHT = 31 # Frame height in points
1145
  MODULE_ROUNDNESS = 50 # Corner roundness % (0=square, 50=pill)
@@ -1208,7 +1354,7 @@ def process_excel_to_word(excel_file_path, output_word_path, display_name=None,
1208
  course_question_count = 1
1209
 
1210
  course_title = cours_titles.get(cours_num, f"COURSE {cours_num}")
1211
- course_para = create_course_title(doc, natural_num, course_title, theme_color)
1212
 
1213
  bm_course_name = sanitize_bookmark_name(f"COURSE_{module_name}_{cours_num}")
1214
  add_bookmark_to_paragraph(course_para, bm_course_name, bookmark_id)
 
93
  # Set space between columns (reduced for better space utilization)
94
  cols.set(qn('w:space'), '432') # 0.3 inch in twentieths of a point (was 708)
95
 
 
 
 
 
96
  # Enable column balancing if requested
97
  if balance_columns:
98
  cols.set(qn('w:equalWidth'), '1') # Equal width columns
 
158
  doc.add_page_break()
159
 
160
 
161
+ def create_course_title(doc, course_number, course_title, theme_color=None, theme_hex=None):
162
+ """Create a course title section with rounded frame (unfilled) matching module style
163
+ Automatically wraps to two lines and doubles height if text is too long"""
164
+ if theme_hex is None:
165
+ theme_hex = THEME_COLOR_HEX
166
  if theme_color is None:
167
+ theme_color = RGBColor.from_string(theme_hex)
168
 
169
  # Add minimal space before course title
170
  course_para = doc.add_paragraph()
171
  course_para.alignment = WD_ALIGN_PARAGRAPH.CENTER
172
 
173
+ # Remove all spacing before and after
174
+ course_para.paragraph_format.space_before = Pt(0)
175
+ course_para.paragraph_format.space_after = Pt(0)
176
+ course_para.paragraph_format.keep_with_next = True
177
+ course_para.paragraph_format.keep_together = True
178
+
179
+ # Format the text
180
+ full_text = f"{course_number}. {course_title}"
181
+ text_length = len(full_text)
182
+
183
+ # ========== CUSTOMIZE COURSE TITLE APPEARANCE HERE ==========
184
+ MAX_CHARS_SINGLE_LINE = 40 # Threshold for wrapping to two lines
185
+ SINGLE_LINE_HEIGHT = 31 # Frame height for single line
186
+ DOUBLE_LINE_HEIGHT = 55 # Frame height for two lines (almost double)
187
+ COURSE_ROUNDNESS = 50 # Corner roundness %
188
+ COURSE_FONT_SIZE = 26 # Font size in half-points (26=13pt)
189
+ COURSE_TEXT_COLOR = theme_hex
190
+ COURSE_STROKE_COLOR = theme_hex
191
+ COURSE_STROKE_WEIGHT = "2pt"
192
+ MAX_WIDTH_PT = 280 # Maximum width in points for the frame
193
+ # ============================================================
194
+
195
+ # Determine if we need two lines
196
+ needs_two_lines = text_length > MAX_CHARS_SINGLE_LINE
197
+
198
+ if needs_two_lines:
199
+ # Split text intelligently at a good breaking point
200
+ words = course_title.split()
201
+ mid_point = len(words) // 2
202
+
203
+ # Try to split at middle, but prefer breaking after shorter first line
204
+ first_line = f"{course_number}. " + " ".join(words[:mid_point])
205
+ second_line = " ".join(words[mid_point:])
206
+
207
+ # Adjust if first line is too long
208
+ while len(first_line) > MAX_CHARS_SINGLE_LINE and mid_point > 1:
209
+ mid_point -= 1
210
+ first_line = f"{course_number}. " + " ".join(words[:mid_point])
211
+ second_line = " ".join(words[mid_point:])
212
+
213
+ text_escaped_line1 = html.escape(first_line)
214
+ text_escaped_line2 = html.escape(second_line)
215
+
216
+ # Use max of both lines for width calculation
217
+ max_line_length = max(len(first_line), len(second_line))
218
+ estimated_width = min((max_line_length * 8), MAX_WIDTH_PT)
219
+ frame_height = DOUBLE_LINE_HEIGHT
220
+
221
+ print(f'Text: {course_title}, charachters: {text_length}')
222
+ print(f'split: {first_line}, {len(first_line)}, {second_line}, {len(second_line)}')
223
+
224
+ # Two-line XML
225
+ text_content = f'''
226
+ <w:r>
227
+ <w:rPr>
228
+ <w:rFonts w:ascii="Montserrat" w:hAnsi="Montserrat"/>
229
+ <w:b/>
230
+ <w:sz w:val="{COURSE_FONT_SIZE}"/>
231
+ <w:color w:val="{COURSE_TEXT_COLOR}"/>
232
+ </w:rPr>
233
+ <w:t>{text_escaped_line1}</w:t>
234
+ </w:r>
235
+ <w:r>
236
+ <w:br/>
237
+ </w:r>
238
+ <w:r>
239
+ <w:rPr>
240
+ <w:rFonts w:ascii="Montserrat" w:hAnsi="Montserrat"/>
241
+ <w:b/>
242
+ <w:sz w:val="{COURSE_FONT_SIZE}"/>
243
+ <w:color w:val="{COURSE_TEXT_COLOR}"/>
244
+ </w:rPr>
245
+ <w:t>{text_escaped_line2}</w:t>
246
+ </w:r>'''
247
+ else:
248
+ # Single line
249
+ estimated_width = min((text_length * 9) + 20, MAX_WIDTH_PT)
250
+ frame_height = SINGLE_LINE_HEIGHT
251
+ text_escaped = html.escape(full_text)
252
+
253
+ print(f'Text: {text_escaped}, charachters: {text_length}')
254
 
255
+ text_content = f'''
256
+ <w:r>
257
+ <w:rPr>
258
+ <w:rFonts w:ascii="Montserrat" w:hAnsi="Montserrat"/>
259
+ <w:b/>
260
+ <w:sz w:val="{COURSE_FONT_SIZE}"/>
261
+ <w:color w:val="{COURSE_TEXT_COLOR}"/>
262
+ </w:rPr>
263
+ <w:t>{text_escaped}</w:t>
264
+ </w:r>'''
265
+
266
+ # Create rounded rectangle shape (UNFILLED with stroke)
267
+ shape_xml = f'''
268
+ <w:r xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main"
269
+ xmlns:v="urn:schemas-microsoft-com:vml">
270
+ <w:pict>
271
+ <v:roundrect style="width:{estimated_width}pt;height:{frame_height}pt"
272
+ arcsize="{COURSE_ROUNDNESS}%"
273
+ filled="f"
274
+ strokecolor="#{COURSE_STROKE_COLOR}"
275
+ strokeweight="{COURSE_STROKE_WEIGHT}">
276
+ <v:textbox inset="0pt,3pt,0pt,3pt" style="v-text-anchor:middle">
277
+ <w:txbxContent>
278
+ <w:p>
279
+ <w:pPr>
280
+ <w:jc w:val="center"/>
281
+ <w:spacing w:before="0" w:after="0"/>
282
+ </w:pPr>{text_content}
283
+ </w:p>
284
+ </w:txbxContent>
285
+ </v:textbox>
286
+ </v:roundrect>
287
+ </w:pict>
288
+ </w:r>
289
+ '''
290
+
291
+ shape_element = parse_xml(shape_xml)
292
+ course_para._p.append(shape_element)
293
 
294
  return course_para
295
 
 
318
  # Question number in Axiforma Black
319
  num_run = question_para.add_run(f"{question_num}. ")
320
  num_run.font.name = 'Inter ExtraBold'
321
+ num_run.font.size = Pt(11)
322
  num_run.font.bold = True
323
  num_run.font.color.rgb = theme_color
324
 
325
  # Question text in SF UI Display Med
326
  text_run = question_para.add_run(question_text)
327
  text_run.font.name = 'Inter ExtraBold'
328
+ text_run.font.size = Pt(11)
329
 
330
  # Display ALL choices for this question with minimal spacing
331
  choice_paragraphs = []
 
349
 
350
  choice_run = choice_para.add_run(f"{choice_letter}- {choice_text}")
351
  choice_run.font.name = 'Inter Display Medium'
352
+ choice_run.font.size = Pt(11)
353
 
354
  choice_paragraphs.append(choice_para)
355
 
 
405
 
406
  answer_run = left_para.add_run("Réponse:")
407
  answer_run.font.name = 'Inter SemiBold'
408
+ answer_run.font.size = Pt(9)
409
  answer_run.font.bold = True
410
  answer_run.font.underline = True
411
 
412
  answer_run = left_para.add_run(f' {correct_answers}')
413
+ answer_run.font.name = 'Inter SemiBold'
414
+ answer_run.font.size = Pt(9)
415
  answer_run.font.color.rgb = theme_color
416
 
417
  # RIGHT cell - Source
 
422
 
423
  source_run = right_para.add_run("Source:")
424
  source_run.font.name = 'Inter SemiBold'
425
+ source_run.font.size = Pt(9)
426
  source_run.font.bold = True
427
  source_run.font.underline = True
428
 
429
  source_value_run = right_para.add_run(f" {source}")
430
+ source_value_run.font.name = 'Inter SemiBold'
431
+ source_value_run.font.size = Pt(9)
432
  source_value_run.font.color.rgb = theme_color
433
 
434
  # Keep with comment if exists
 
438
  empty_para = doc.add_paragraph(' ', style='TinySpace')
439
  empty_para.paragraph_format.space_before = Pt(0)
440
  empty_para.paragraph_format.space_after = Pt(0)
441
+ empty_para.paragraph_format.line_spacing = Pt(7)
442
  empty_run = empty_para.add_run(' ')
443
+ empty_run.font.size = Pt(7)
444
 
445
  # Add comment if exists
446
  if comment and str(comment).strip() and str(comment).lower() != 'nan':
 
466
  # ===== HEADER (keep existing text like module name) =====
467
  header = section.header
468
  header.is_linked_to_previous = False
469
+ section.header_distance = Cm(0.3)
470
 
471
  # If header is empty, add a blank paragraph
472
  if not header.paragraphs:
 
529
  run._r.append(fldChar2)
530
 
531
  run.font.name = 'Montserrat'
532
+ run.font.size = Pt(14)
533
  run.font.bold = True
534
  run.font.color.rgb = RGBColor(0, 0, 0)
535
 
 
552
  <w:r>
553
  <w:rPr>
554
  <w:rFonts w:ascii="Aptos" w:hAnsi="Aptos"/>
555
+ <w:sz w:val="44"/>
556
  <w:color w:val="{theme_hex}"/>
557
  <w:u w:val="single"/>
558
  </w:rPr>
 
562
  <w:rPr>
563
  <w:rFonts w:ascii="Montserrat" w:hAnsi="Montserrat"/>
564
  <w:b/>
565
+ <w:sz w:val="28"/>
566
  <w:color w:val="{theme_hex}"/>
567
  <w:u w:val="single"/>
568
  </w:rPr>
 
885
  <w:rPr>
886
  <w:rFonts w:ascii="Montserrat" w:hAnsi="Montserrat"/>
887
  <w:b/>
888
+ <w:sz w:val="26"/>
889
  <w:color w:val="{theme_hex}"/>
890
  </w:rPr>
891
  <w:t>{module_name_str}</w:t>
 
916
  <w:rPr>
917
  <w:rFonts w:ascii="Montserrat" w:hAnsi="Montserrat"/>
918
  <w:b/>
919
+ <w:sz w:val="26"/>
920
  <w:color w:val="{theme_hex}"/>
921
  </w:rPr>
922
  <w:t>{sheet_name_str}</w:t>
 
938
  header_para._p.append(right_textbox_element)
939
 
940
 
941
+ def add_colored_column_separator(section, theme_hex=None):
942
+ """Add a custom colored vertical line between columns"""
943
+ if theme_hex is None:
944
+ theme_hex = THEME_COLOR_HEX
945
+
946
+ header = section.header
947
+
948
+ # Find or create the first paragraph in header
949
+ if not header.paragraphs:
950
+ header.add_paragraph()
951
+
952
+ header_para = header.paragraphs[0]
953
+
954
+ # Create a vertical line using VML shape
955
+ # The line starts AFTER the header and goes to the bottom
956
+ # Adjust the "from" value to start below header (e.g., 0.8in from top)
957
+ line_xml = f'''
958
+ <w:r xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main"
959
+ xmlns:v="urn:schemas-microsoft-com:vml"
960
+ xmlns:o="urn:schemas-microsoft-com:office:office">
961
+ <w:pict>
962
+ <v:line id="columnSeparator"
963
+ style="position:absolute;left:0;text-align:left;z-index:-1;
964
+ mso-position-horizontal:center;
965
+ mso-position-horizontal-relative:margin;
966
+ mso-position-vertical-relative:page"
967
+ from="0,0.49in" to="0,11.05in"
968
+ strokecolor="#{theme_hex}"
969
+ strokeweight="0.75pt">
970
+ <o:lock v:ext="edit" aspectratio="f"/>
971
+ </v:line>
972
+ </w:pict>
973
+ </w:r>
974
+ '''
975
+
976
+ line_element = parse_xml(line_xml)
977
+ header_para._p.append(line_element)
978
+
979
+
980
  def process_excel_to_word(excel_file_path, output_word_path, display_name=None, use_two_columns=True, add_separator_line=True, balance_method="dynamic", theme_hex=None):
981
  """Main function to process Excel and create a Word document with TOC on the first page"""
982
 
 
1064
  # ========================================
1065
  # ADD THREE EMPTY PAGES AT THE BEGINNING
1066
  # ========================================
1067
+ for i in range(3):
1068
  doc.add_paragraph() # Add empty paragraph
1069
+ if i < 2: # Add page breaks for first 2 pages (3rd page leads to TOC)
1070
  doc.add_page_break()
1071
 
1072
  # TOC helpers
 
1267
  sectPr.append(cols)
1268
  cols.set(qn('w:num'), '2')
1269
  cols.set(qn('w:space'), '432')
 
 
1270
  cols.set(qn('w:equalWidth'), '1')
1271
 
1272
  if use_two_columns:
 
1277
  sectPr.append(cols)
1278
  cols.set(qn('w:num'), '2')
1279
  cols.set(qn('w:space'), '432')
 
 
1280
  cols.set(qn('w:equalWidth'), '1')
1281
 
1282
  # Use the new flexible header function
1283
  create_flexible_header(section, module_name, first_sheet_name, display_name, theme_hex=theme_hex)
1284
 
1285
+ # ADD THE COLORED SEPARATOR
1286
+ if add_separator_line:
1287
+ add_colored_column_separator(section, theme_hex)
1288
+
1289
  # ========== CUSTOMIZE MODULE TITLE APPEARANCE HERE ==========
1290
  MODULE_HEIGHT = 31 # Frame height in points
1291
  MODULE_ROUNDNESS = 50 # Corner roundness % (0=square, 50=pill)
 
1354
  course_question_count = 1
1355
 
1356
  course_title = cours_titles.get(cours_num, f"COURSE {cours_num}")
1357
+ course_para = create_course_title(doc, natural_num, course_title, theme_color, theme_hex=theme_hex)
1358
 
1359
  bm_course_name = sanitize_bookmark_name(f"COURSE_{module_name}_{cours_num}")
1360
  add_bookmark_to_paragraph(course_para, bm_course_name, bookmark_id)