Skip to content

Commit a5798bc

Browse files
authored
Merge pull request #31 from intel/update-branch-18304451279
feat: enabled bahasa indonesia in expert advisor (#362)
2 parents 865cd42 + 4d5d2b6 commit a5798bc

File tree

110 files changed

+11104
-7141
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

110 files changed

+11104
-7141
lines changed

backend/generate_pptx.py

Lines changed: 41 additions & 2475 deletions
Large diffs are not rendered by default.

backend/main.py

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,7 @@ def get_result(job_id: str):
139139

140140
class PPTXRequest(BaseModel):
141141
content: dict
142+
language: str | None = "en"
142143

143144

144145
def validate_and_transform_content(content: dict) -> dict:
@@ -154,7 +155,7 @@ def validate_and_transform_content(content: dict) -> dict:
154155
"""
155156
# Ensure required keys exist with default values if missing
156157
transformed_content = {
157-
"title": content.get("title", "Untitled Presentation"),
158+
"title": content.get("title", ""),
158159
"contentType": content.get("contentType", "lecture"),
159160
"difficultyLevel": content.get("difficultyLevel", "intermediate"),
160161
"slides": content.get("slides", []),
@@ -167,13 +168,13 @@ def validate_and_transform_content(content: dict) -> dict:
167168

168169
# Validate slides structure
169170
for slide in transformed_content["slides"]:
170-
slide.setdefault("title", "Untitled Slide")
171+
slide.setdefault("title", "")
171172
slide.setdefault("content", [])
172173
slide.setdefault("notes", "")
173174

174175
# Validate activities structure
175176
for activity in transformed_content["activities"]:
176-
activity.setdefault("title", "Untitled Activity")
177+
activity.setdefault("title", "")
177178
activity.setdefault("description", "")
178179
activity.setdefault("type", "Exercise")
179180
activity.setdefault("duration", "20 minutes")
@@ -192,13 +193,13 @@ def validate_and_transform_content(content: dict) -> dict:
192193

193194
# Validate key terms structure
194195
for term in transformed_content["keyTerms"]:
195-
term.setdefault("term", "Untitled Term")
196-
term.setdefault("definition", "No definition provided.")
196+
term.setdefault("term", "")
197+
term.setdefault("definition", "")
197198

198199
# Validate further readings structure
199200
for reading in transformed_content["furtherReadings"]:
200-
reading.setdefault("title", "Untitled Reading")
201-
reading.setdefault("author", "Unknown Author")
201+
reading.setdefault("title", "")
202+
reading.setdefault("author", "")
202203
reading.setdefault("readingDescription", "")
203204

204205
return transformed_content
@@ -222,7 +223,10 @@ async def generate_pptx(request: PPTXRequest):
222223
print(temp_pptx_path)
223224

224225
# Generate the PPTX file
225-
create_pptx(transformed_content, temp_pptx_path)
226+
lang = (request.language or "en").lower()
227+
if lang not in ["en", "id"]:
228+
lang = "en"
229+
create_pptx(transformed_content, temp_pptx_path, lang)
226230
print(f"Temporary PPTX file created at: {temp_pptx_path}")
227231

228232
if not os.path.exists(temp_pptx_path):

backend/pptx_builder/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
# pptx_builder package initialization

backend/pptx_builder/builder.py

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
import os
2+
import json
3+
import tempfile
4+
from pptx import Presentation
5+
from pptx.util import Inches
6+
from .constants import SLIDE_WIDTH, SLIDE_HEIGHT
7+
from .localization import set_language, t
8+
from .slide_counter import calculate_total_slides
9+
from .sections import (
10+
create_title_slide,
11+
create_agenda_slide,
12+
create_learning_outcomes_slide,
13+
create_key_terms_slide,
14+
create_content_slides,
15+
create_activity_slides,
16+
create_quiz_slides,
17+
create_discussion_slides,
18+
create_further_readings_slides,
19+
create_facilitation_notes_slide,
20+
create_closing_slide,
21+
)
22+
23+
24+
def build_full_presentation(content, language="en"):
25+
set_language(language)
26+
total_slides = calculate_total_slides(content)
27+
prs = Presentation()
28+
prs.slide_width = Inches(SLIDE_WIDTH)
29+
prs.slide_height = Inches(SLIDE_HEIGHT)
30+
create_title_slide(prs, content)
31+
create_agenda_slide(prs, content, total_slides)
32+
create_learning_outcomes_slide(prs, content, total_slides)
33+
create_key_terms_slide(prs, content, total_slides)
34+
create_content_slides(prs, content, total_slides)
35+
create_activity_slides(prs, content, total_slides)
36+
create_quiz_slides(prs, content, total_slides)
37+
create_discussion_slides(prs, content, total_slides)
38+
create_further_readings_slides(prs, content, total_slides)
39+
facilitation_slide = create_facilitation_notes_slide(prs, content, total_slides)
40+
if facilitation_slide:
41+
total_slides += 1 # update for closing slide numbering if needed
42+
create_closing_slide(prs, content, total_slides, total_slides)
43+
return prs
44+
45+
46+
def create_pptx(content: dict, output_path: str, language: str = "en"):
47+
base_dir = os.path.dirname(os.path.abspath(__file__))
48+
normalized_output_path = os.path.abspath(output_path)
49+
allowed_output = os.path.abspath(os.path.join(base_dir, "..", "output"))
50+
parent = os.path.dirname(normalized_output_path)
51+
is_temp = parent.startswith(os.path.abspath(tempfile.gettempdir()))
52+
is_out = normalized_output_path.startswith(allowed_output)
53+
if not (is_temp or is_out):
54+
raise ValueError(
55+
"Security violation: Output path must be in allowed directories"
56+
)
57+
prs = build_full_presentation(content, language)
58+
prs.save(normalized_output_path)
59+
60+
61+
def cli_build(content_path, output_path, language="en"):
62+
with open(content_path, "r") as f:
63+
content = json.load(f)
64+
create_pptx(content, output_path, language)

backend/pptx_builder/constants.py

Lines changed: 177 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,177 @@
1+
from pptx.dml.color import RGBColor
2+
3+
# Dimensions & layout
4+
SLIDE_WIDTH = 10 # inches
5+
SLIDE_HEIGHT = 5.625 # inches
6+
FOOTER_Y = 5.3
7+
CONTENT_START_Y = 1.5
8+
AVAILABLE_CONTENT_HEIGHT = FOOTER_Y - CONTENT_START_Y
9+
MAIN_BULLET_INDENT = 0.5
10+
SUB_BULLET_INDENT = 1.0
11+
SUB_SUB_BULLET_INDENT = 1.5
12+
13+
THEME = {
14+
"use_gradients": True,
15+
"corner_accent": True,
16+
"slide_border": False,
17+
"content_box_shadow": True,
18+
"modern_bullets": True,
19+
"footer_style": "modern", # "modern" or "classic"
20+
}
21+
22+
COLORS = {
23+
"primary": RGBColor(37, 99, 235),
24+
"primary_light": RGBColor(96, 165, 250),
25+
"primary_dark": RGBColor(30, 64, 175),
26+
"secondary": RGBColor(79, 70, 229),
27+
"secondary_light": RGBColor(139, 92, 246),
28+
"secondary_dark": RGBColor(67, 56, 202),
29+
"accent1": RGBColor(139, 92, 246),
30+
"accent2": RGBColor(16, 185, 129),
31+
"accent3": RGBColor(245, 158, 11),
32+
"accent4": RGBColor(239, 68, 68),
33+
"light": RGBColor(243, 244, 246),
34+
"light_alt": RGBColor(249, 250, 251),
35+
"dark": RGBColor(31, 41, 55),
36+
"dark_alt": RGBColor(17, 24, 39),
37+
"text": RGBColor(17, 24, 39),
38+
"text_light": RGBColor(255, 255, 255),
39+
"text_muted": RGBColor(107, 114, 128),
40+
"success": RGBColor(16, 185, 129),
41+
"warning": RGBColor(245, 158, 11),
42+
"error": RGBColor(239, 68, 68),
43+
"info": RGBColor(59, 130, 246),
44+
"background": RGBColor(255, 255, 255),
45+
"background_alt": RGBColor(249, 250, 251),
46+
"royal_blue": RGBColor(65, 105, 225),
47+
"medium_purple": RGBColor(147, 112, 219),
48+
"dark_blue": RGBColor(26, 43, 60),
49+
"teal": RGBColor(20, 184, 166),
50+
"emerald": RGBColor(16, 185, 129),
51+
"gradient_start": RGBColor(65, 105, 225),
52+
"gradient_end": RGBColor(147, 112, 219),
53+
"activity_purple": RGBColor(139, 92, 246),
54+
"activity_blue": RGBColor(37, 99, 235),
55+
"activity_green": RGBColor(16, 185, 129),
56+
"activity_orange": RGBColor(249, 115, 22),
57+
}
58+
59+
GLOBAL_LANG = "en"
60+
61+
LABELS = {
62+
"en": {
63+
"learningOutcomes": "Learning Outcomes",
64+
"byTheEnd": "By the end of this {contentType}, you will be able to:",
65+
"keyTerms": "Key Terms & Concepts",
66+
"agenda": "Agenda",
67+
"continued": " (continued)",
68+
"agendaContinued": " (continued {idx}/{total})",
69+
"introduction": "Introduction",
70+
"mainContent": "Main Content",
71+
"activities": "Activities",
72+
"testYourKnowledge": "Test Your Knowledge",
73+
"quizQuestions": "Quiz Questions ({count})",
74+
"discussionQuestions": "Discussion Questions ({count})",
75+
"additionalResources": "Additional Resources",
76+
"furtherReadings": "Further Readings & Resources",
77+
"activity": "Activity {num}: {title}",
78+
"materialsSuffix": "(Materials)",
79+
"type": "Type",
80+
"duration": "Duration",
81+
"instructions": "Instructions:",
82+
"materialsNeeded": "Materials needed:",
83+
"notesAvailable": "Notes Available",
84+
"quizQuestion": "Quiz Question {num}",
85+
"quizAnswer": "Quiz Answer {num}",
86+
"question": "Question: {text}",
87+
"correctAnswer": "Correct Answer:",
88+
"explanation": "Explanation:",
89+
"groupDiscussion": "Group Discussion:",
90+
"groupInstruction": "Discuss this question with your group and prepare to share your thoughts with the class.",
91+
"facilitatorGuidance": "Facilitator Guidance: Question {num}",
92+
"author": "Author:",
93+
"term": "Term",
94+
"definition": "Definition",
95+
"discussionQuestion": "Discussion Question {num}",
96+
"thankYou": "Thank You!",
97+
"presentation": "Presentation:",
98+
"facilitationNotesSummary": "Facilitation Notes Summary",
99+
"continuedNextSlide": "Continued on next slide...",
100+
"facilitationNotesLabel": "Facilitation Notes:",
101+
"learningObjectiveLabel": "Learning Objective:",
102+
"successCriteriaLabel": "Success criteria:",
103+
"untitledPresentation": "Untitled Presentation",
104+
"untitledReading": "Untitled Reading",
105+
"untitledActivity": "Untitled Activity",
106+
"unknownAuthor": "Unknown Author",
107+
"contentTypeNames": {
108+
"lecture": "Lecture",
109+
"tutorial": "Tutorial",
110+
"workshop": "Workshop",
111+
},
112+
"difficultyNames": {
113+
"introductory": "Introductory Level",
114+
"intermediate": "Intermediate Level",
115+
"advanced": "Advanced Level",
116+
},
117+
},
118+
"id": {
119+
"learningOutcomes": "Capaian Pembelajaran",
120+
"byTheEnd": "Di akhir {contentType} ini, Anda akan dapat:",
121+
"keyTerms": "Istilah Kunci & Konsep",
122+
"agenda": "Agenda",
123+
"continued": " (lanjutan)",
124+
"agendaContinued": " (lanjutan {idx}/{total})",
125+
"introduction": "Pendahuluan",
126+
"mainContent": "Konten Utama",
127+
"activities": "Aktivitas",
128+
"testYourKnowledge": "Uji Pengetahuan Anda",
129+
"quizQuestions": "Soal Kuis ({count})",
130+
"discussionQuestions": "Pertanyaan Diskusi ({count})",
131+
"additionalResources": "Sumber Tambahan",
132+
"furtherReadings": "Bacaan Lanjutan & Sumber",
133+
"activity": "Aktivitas {num}: {title}",
134+
"materialsSuffix": "(Perlengkapan)",
135+
"type": "Jenis",
136+
"duration": "Durasi",
137+
"instructions": "Instruksi:",
138+
"materialsNeeded": "Perlengkapan yang dibutuhkan:",
139+
"notesAvailable": "Catatan Tersedia",
140+
"quizQuestion": "Soal Kuis {num}",
141+
"quizAnswer": "Jawaban Kuis {num}",
142+
"question": "Pertanyaan: {text}",
143+
"correctAnswer": "Jawaban Benar:",
144+
"explanation": "Penjelasan:",
145+
"groupDiscussion": "Diskusi Kelompok:",
146+
"groupInstruction": "Diskusikan pertanyaan ini dengan kelompok Anda dan siapkan untuk berbagi pendapat dengan kelas.",
147+
"facilitatorGuidance": "Panduan Fasilitator: Pertanyaan {num}",
148+
"author": "Penulis:",
149+
"term": "Istilah",
150+
"definition": "Definisi",
151+
"discussionQuestion": "Pertanyaan Diskusi {num}",
152+
"thankYou": "Terima kasih!",
153+
"presentation": "Presentasi:",
154+
"facilitationNotesSummary": "Ringkasan Catatan Fasilitasi",
155+
"continuedNextSlide": "Bersambung di slide berikutnya...",
156+
"facilitationNotesLabel": "Catatan Fasilitasi:",
157+
"learningObjectiveLabel": "Tujuan Pembelajaran:",
158+
"successCriteriaLabel": "Kriteria keberhasilan:",
159+
"untitledPresentation": "Presentasi Tanpa Judul",
160+
"untitledReading": "Bacaan Tanpa Judul",
161+
"untitledActivity": "Aktivitas Tanpa Judul",
162+
"unknownAuthor": "Penulis Tidak Diketahui",
163+
"contentTypeNames": {
164+
"lecture": "Kuliah",
165+
"tutorial": "Tutorial",
166+
"workshop": "Lokakarya",
167+
},
168+
"difficultyNames": {
169+
"introductory": "Tingkat Dasar",
170+
"intermediate": "Tingkat Menengah",
171+
"advanced": "Tingkat Lanjutan",
172+
},
173+
},
174+
}
175+
176+
BULLET_MARKERS = ["•", "*", "-", "○", "◦", "▪", "▫", "◆", "◇", "►", "▻", "▶", "▷"]
177+
SUB_BULLET_MARKERS = ["-", "○", "◦", "▪", "▫"]
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
from . import constants
2+
3+
4+
def set_language(lang: str):
5+
lang = (lang or "en").lower()
6+
constants.GLOBAL_LANG = "id" if lang == "id" else "en"
7+
8+
9+
def t(key: str, **kwargs):
10+
label = constants.LABELS.get(constants.GLOBAL_LANG, {}).get(key, key)
11+
if isinstance(label, dict):
12+
return label
13+
return label.format(**kwargs) if kwargs else label
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
from .title import create_title_slide
2+
from .agenda import create_agenda_slide
3+
from .learning_outcomes import create_learning_outcomes_slide
4+
from .key_terms import create_key_terms_slide
5+
from .content import create_content_slides
6+
from .activities import create_activity_slides
7+
from .quiz import create_quiz_slides
8+
from .discussion import create_discussion_slides
9+
from .readings import create_further_readings_slides
10+
from .facilitation import create_facilitation_notes_slide
11+
from .closing import create_closing_slide
12+
13+
__all__ = [
14+
"create_title_slide",
15+
"create_agenda_slide",
16+
"create_learning_outcomes_slide",
17+
"create_key_terms_slide",
18+
"create_content_slides",
19+
"create_activity_slides",
20+
"create_quiz_slides",
21+
"create_discussion_slides",
22+
"create_further_readings_slides",
23+
"create_facilitation_notes_slide",
24+
"create_closing_slide",
25+
]

0 commit comments

Comments
 (0)