Install any skill in seconds. Free to start, no credit card required.
Get Started Free →PowerPoint generation for construction: project updates, stakeholder presentations, progress reports, bid presentations. Automated slide creation with charts and data.
.claude/skills/datadrivenconstruction-pptx-construction/SKILL.md| Test case | Without → With | Effect | Δ tokens | Δ turns |
|---|---|---|---|---|
| case-01 | ✗→✓ | ▲ Improved | 73% | 0% |
| case-22 | ✗→✓ | ▲ Improved | 73% | 0% |
| case-05 | ✗→✓ | ▲ Improved | 190% | 0% |
| case-06 | ✗→✓ | ▲ Improved | 162% | 0% |
| case-10 | ✗→✓ | ▲ Improved | 44% | 0% |
Create professional PowerPoint presentations for construction projects using python-pptx. Generate stakeholder updates, progress reports, and bid presentations with automated data visualization.
Generate monthly progress update slides.
pythonfrom pptx import Presentation from pptx.util import Inches, Pt from pptx.enum.chart import XL_CHART_TYPE from pptx.chart.data import CategoryChartData from pptx.dml.color import RGBColor from pptx.enum.text import PP_ALIGN def create_progress_presentation(project_data: dict, output_path: str) -> str: """Create project progress presentation.""" prs = Presentation() prs.slide_width = Inches(13.333) prs.slide_height = Inches(7.5) # Title slide title_slide = prs.slides.add_slide(prs.slide_layouts[0]) title_slide.shapes.title.text = project_data['project_name'] title_slide.placeholders[1].text = f"Progress Report - {project_data['report_date']}" # Executive Summary slide summary_slide = prs.slides.add_slide(prs.slide_layouts[1]) summary_slide.shapes.title.text = "Executive Summary" summary_text = summary_slide.placeholders[1] tf = summary_text.text_frame tf.paragraphs[0].text = f"Overall Progress: {project_data['overall_progress']}%" p = tf.add_paragraph() p.text = f"Schedule Status: {project_data['schedule_status']}" p = tf.add_paragraph() p.text = f"Budget Status: {project_data['budget_status']}" p = tf.add_paragraph() p.text = f"Safety: {project_data['safety_status']}" # Schedule Progress Chart add_schedule_chart(prs, project_data['schedule_data']) # Budget Chart add_budget_chart(prs, project_data['budget_data']) # Key Milestones add_milestones_slide(prs, project_data['milestones']) # Issues & Risks add_issues_slide(prs, project_data.get('issues', [])) # Photos if project_data.get('photos'): add_photos_slide(prs, project_data['photos']) prs.save(output_path) return output_path def add_schedule_chart(prs: Presentation, schedule_data: dict): """Add schedule progress chart.""" slide = prs.slides.add_slide(prs.slide_layouts[5]) slide.shapes.title.text = "Schedule Progress" chart_data = CategoryChartData() chart_data.categories = schedule_data['phases'] chart_data.add_series('Planned', schedule_data['planned']) chart_data.add_series('Actual', schedule_data['actual']) chart = slide.shapes.add_chart( XL_CHART_TYPE.COLUMN_CLUSTERED, Inches(1), Inches(1.5), Inches(11), Inches(5), chart_data ).chart chart.has_legend = True chart.legend.include_in_layout = False def add_budget_chart(prs: Presentation, budget_data: dict): """Add budget status chart.""" slide = prs.slides.add_slide(prs.slide_layouts[5]) slide.shapes.title.text = "Budget Status" chart_data = CategoryChartData() chart_data.categories = ['Budget', 'Committed', 'Spent', 'Forecast'] chart_data.add_series('Amount ($M)', [ budget_data['budget'] / 1_000_000, budget_data['committed'] / 1_000_000, budget_data['spent'] / 1_000_000, budget_data['forecast'] / 1_000_000 ]) chart = slide.shapes.add_chart( XL_CHART_TYPE.BAR_CLUSTERED, Inches(1), Inches(1.5), Inches(11), Inches(5), chart_data ).chart def add_milestones_slide(prs: Presentation, milestones: list): """Add key milestones slide.""" slide = prs.slides.add_slide(prs.slide_layouts[5]) slide.shapes.title.text = "Key Milestones" # Create table rows = len(milestones) + 1 cols = 4 table = slide.shapes.add_table(rows, cols, Inches(0.5), Inches(1.5), Inches(12), Inches(0.5 * rows)).table # Headers headers = ['Milestone', 'Planned', 'Actual/Forecast', 'Status'] for i, header in enumerate(headers): table.cell(0, i).text = header # Data for i, ms in enumerate(milestones, 1): table.cell(i, 0).text = ms['name'] table.cell(i, 1).text = ms['planned_date'] table.cell(i, 2).text = ms.get('actual_date', ms.get('forecast_date', 'TBD')) table.cell(i, 3).text = ms['status'] def add_issues_slide(prs: Presentation, issues: list): """Add issues and risks slide.""" slide = prs.slides.add_slide(prs.slide_layouts[1]) slide.shapes.title.text = "Issues & Risks" if not issues: slide.placeholders[1].text = "No critical issues at this time." return tf = slide.placeholders[1].text_frame for i, issue in enumerate(issues): if i == 0: tf.paragraphs[0].text = f"• {issue['description']} ({issue['status']})" else: p = tf.add_paragraph() p.text = f"• {issue['description']} ({issue['status']})" def add_photos_slide(prs: Presentation, photos: list): """Add site photos slide.""" slide = prs.slides.add_slide(prs.slide_layouts[5]) slide.shapes.title.text = "Site Progress Photos" # Arrange up to 4 photos in grid positions = [ (Inches(0.5), Inches(1.5)), (Inches(6.5), Inches(1.5)), (Inches(0.5), Inches(4)), (Inches(6.5), Inches(4)) ] for i, photo in enumerate(photos[:4]): left, top = positions[i] slide.shapes.add_picture(photo['path'], left, top, width=Inches(5.5))
Create bid/proposal presentations.
pythondef create_bid_presentation(bid_data: dict, output_path: str) -> str: """Create bid presentation for client.""" prs = Presentation() # Title title_slide = prs.slides.add_slide(prs.slide_layouts[0]) title_slide.shapes.title.text = bid_data['project_name'] title_slide.placeholders[1].text = f"Proposal by {bid_data['company_name']}" # Company Overview overview_slide = prs.slides.add_slide(prs.slide_layouts[1]) overview_slide.shapes.title.text = "About Us" tf = overview_slide.placeholders[1].text_frame tf.paragraphs[0].text = f"Years in Business: {bid_data['company']['years']}" for qual in bid_data['company']['qualifications']: p = tf.add_paragraph() p.text = f"• {qual}" # Project Understanding understanding_slide = prs.slides.add_slide(prs.slide_layouts[1]) understanding_slide.shapes.title.text = "Project Understanding" tf = understanding_slide.placeholders[1].text_frame for i, point in enumerate(bid_data['understanding']): if i == 0: tf.paragraphs[0].text = f"• {point}" else: p = tf.add_paragraph() p.text = f"• {point}" # Approach approach_slide = prs.slides.add_slide(prs.slide_layouts[1]) approach_slide.shapes.title.text = "Our Approach" tf = approach_slide.placeholders[1].text_frame for i, step in enumerate(bid_data['approach']): if i == 0: tf.paragraphs[0].text = f"{i+1}. {step}" else: p = tf.add_paragraph() p.text = f"{i+1}. {step}" # Team team_slide = prs.slides.add_slide(prs.slide_layouts[1]) team_slide.shapes.title.text = "Project Team" tf = team_slide.placeholders[1].text_frame for i, member in enumerate(bid_data['team']): if i == 0: tf.paragraphs[0].text = f"{member['role']}: {member['name']}" else: p = tf.add_paragraph() p.text = f"{member['role']}: {member['name']}" # Pricing Summary pricing_slide = prs.slides.add_slide(prs.slide_layouts[5]) pricing_slide.shapes.title.text = "Investment Summary" # Add pricing table rows = len(bid_data['pricing']['items']) + 2 table = pricing_slide.shapes.add_table(rows, 2, Inches(2), Inches(2), Inches(8), Inches(0.5 * rows)).table table.cell(0, 0).text = "Description" table.cell(0, 1).text = "Amount" for i, item in enumerate(bid_data['pricing']['items'], 1): table.cell(i, 0).text = item['description'] table.cell(i, 1).text = f"${item['amount']:,.2f}" table.cell(rows-1, 0).text = "TOTAL" table.cell(rows-1, 1).text = f"${bid_data['pricing']['total']:,.2f}" # Schedule schedule_slide = prs.slides.add_slide(prs.slide_layouts[1]) schedule_slide.shapes.title.text = "Proposed Schedule" tf = schedule_slide.placeholders[1].text_frame tf.paragraphs[0].text = f"Duration: {bid_data['schedule']['duration']}" p = tf.add_paragraph() p.text = f"Start: {bid_data['schedule']['start']}" p = tf.add_paragraph() p.text = f"Completion: {bid_data['schedule']['end']}" prs.save(output_path) return output_path
Generate safety meeting presentations.
pythondef create_safety_presentation(safety_data: dict, output_path: str) -> str: """Create safety meeting presentation.""" prs = Presentation() # Title title_slide = prs.slides.add_slide(prs.slide_layouts[0]) title_slide.shapes.title.text = "Safety Report" title_slide.placeholders[1].text = f"{safety_data['project_name']} - {safety_data['period']}" # KPIs kpi_slide = prs.slides.add_slide(prs.slide_layouts[5]) kpi_slide.shapes.title.text = "Safety KPIs" # Add KPI boxes kpis = [ ('Days Without Incident', str(safety_data['days_without_incident'])), ('Total Recordable Rate', f"{safety_data['trir']:.2f}"), ('Near Misses Reported', str(safety_data['near_misses'])), ('Toolbox Talks', str(safety_data['toolbox_talks'])) ] for i, (label, value) in enumerate(kpis): left = Inches(0.5 + (i % 2) * 6) top = Inches(1.5 + (i // 2) * 2.5) shape = kpi_slide.shapes.add_shape(1, left, top, Inches(5), Inches(2)) shape.text = f"{value}\n{label}" # Incidents (if any) if safety_data.get('incidents'): incident_slide = prs.slides.add_slide(prs.slide_layouts[1]) incident_slide.shapes.title.text = "Incidents This Period" tf = incident_slide.placeholders[1].text_frame for i, incident in enumerate(safety_data['incidents']): if i == 0: tf.paragraphs[0].text = f"• {incident['date']}: {incident['description']}" else: p = tf.add_paragraph() p.text = f"• {incident['date']}: {incident['description']}" # Focus Areas focus_slide = prs.slides.add_slide(prs.slide_layouts[1]) focus_slide.shapes.title.text = "Focus Areas This Week" tf = focus_slide.placeholders[1].text_frame for i, focus in enumerate(safety_data['focus_areas']): if i == 0: tf.paragraphs[0].text = f"• {focus}" else: p = tf.add_paragraph() p.text = f"• {focus}" prs.save(output_path) return output_path
Generate 3-week lookahead slides.
pythondef create_lookahead_presentation(lookahead_data: dict, output_path: str) -> str: """Create 3-week lookahead presentation.""" prs = Presentation() # Title title_slide = prs.slides.add_slide(prs.slide_layouts[0]) title_slide.shapes.title.text = "3-Week Lookahead" title_slide.placeholders[1].text = f"{lookahead_data['project_name']}" # Week by week slides for week_num, week_data in enumerate(lookahead_data['weeks'], 1): slide = prs.slides.add_slide(prs.slide_layouts[5]) slide.shapes.title.text = f"Week {week_num}: {week_data['date_range']}" # Activities table rows = len(week_data['activities']) + 1 table = slide.shapes.add_table(rows, 4, Inches(0.3), Inches(1.5), Inches(12.5), Inches(0.4 * rows)).table # Headers for i, header in enumerate(['Activity', 'Area', 'Crew', 'Status']): table.cell(0, i).text = header # Data for i, activity in enumerate(week_data['activities'], 1): table.cell(i, 0).text = activity['name'] table.cell(i, 1).text = activity['area'] table.cell(i, 2).text = activity['crew'] table.cell(i, 3).text = activity['status'] # Resource Needs resource_slide = prs.slides.add_slide(prs.slide_layouts[1]) resource_slide.shapes.title.text = "Resource Needs" tf = resource_slide.placeholders[1].text_frame for i, need in enumerate(lookahead_data.get('resource_needs', [])): if i == 0: tf.paragraphs[0].text = f"• {need}" else: p = tf.add_paragraph() p.text = f"• {need}" prs.save(output_path) return output_path
python# Example: Generate monthly progress presentation from project data import pandas as pd # Load project data schedule_df = pd.read_excel("schedule_data.xlsx") budget_df = pd.read_excel("budget_data.xlsx") # Prepare presentation data project_data = { 'project_name': 'Downtown Office Tower', 'report_date': 'January 2026', 'overall_progress': 67, 'schedule_status': 'On Track', 'budget_status': '2.3% Under Budget', 'safety_status': '45 Days Without Incident', 'schedule_data': { 'phases': schedule_df['phase'].tolist(), 'planned': schedule_df['planned_pct'].tolist(), 'actual': schedule_df['actual_pct'].tolist() }, 'budget_data': { 'budget': budget_df['budget'].sum(), 'committed': budget_df['committed'].sum(), 'spent': budget_df['spent'].sum(), 'forecast': budget_df['forecast'].sum() }, 'milestones': [ {'name': 'Foundation Complete', 'planned_date': '2025-06-01', 'actual_date': '2025-05-28', 'status': 'Complete'}, {'name': 'Structure Complete', 'planned_date': '2025-12-15', 'actual_date': '2025-12-10', 'status': 'Complete'}, {'name': 'Envelope Complete', 'planned_date': '2026-03-01', 'forecast_date': '2026-02-28', 'status': 'On Track'} ] } create_progress_presentation(project_data, 'reports/monthly_progress_jan2026.pptx')
bashpip install python-pptx
| Case | Status | Duration (ms) | Turns | Tokens | Tool calls | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Without | With | Δ | Without | With | Δ | Without | With | Δ | Without | With | Δ | ||
case-01 | fail→pass | 29,848 | 29,213 | -2% | 1 | 1 | 0% | 6,214 | 10,726 | +73% | 0 | 0 | — |
case-22 | fail→pass | 28,842 | 27,053 | -6% | 1 | 1 | 0% | 6,175 | 10,687 | +73% | 0 | 0 | — |
case-02 | fail→fail | 27,406 | 28,729 | +5% | 1 | 1 | 0% | 6,207 | 10,719 | +73% | 0 | 0 | — |
case-03 | fail→fail | 29,030 | 28,363 | -2% | 1 | 1 | 0% | 6,221 | 10,733 | +73% | 0 | 0 | — |
case-04 | pass→pass | 8,574 | 2,930 | -66% | 1 | 1 | 0% | 1,523 | 5,041 | +231% | 0 | 0 | — |
case-05 | fail→pass | 10,727 | 4,198 | -61% | 1 | 1 | 0% | 1,845 | 5,355 | +190% | 0 | 0 | — |
case-06 | fail→pass | 12,385 | 6,961 | -44% | 1 | 1 | 0% | 2,282 | 5,971 | +162% | 0 | 0 | — |
case-07 | pass→pass | 18,722 | 22,561 | +21% | 1 | 1 | 0% | 3,935 | 9,601 | +144% | 0 | 0 | — |
case-08 | fail→fail | 14,782 | 13,133 | -11% | 1 | 1 | 0% | 3,094 | 7,169 | +132% | 0 | 0 | — |
case-09 | pass→pass | 14,600 | 16,750 | +15% | 1 | 1 | 0% | 2,476 | 7,787 | +214% | 0 | 0 | — |
case-10 | fail→pass | 29,463 | 19,190 | -35% | 1 | 1 | 0% | 6,181 | 8,890 | +44% | 0 | 0 | — |
case-11 | fail→pass | 15,125 | 11,258 | -26% | 1 | 1 | 0% | 2,465 | 6,912 | +180% | 0 | 0 | — |
case-12 | pass→fail | 11,386 | 9,882 | -13% | 1 | 1 | 0% | 2,161 | 6,519 | +202% | 0 | 0 | — |
case-13 | fail→fail | 41,437 | 24,151 | -42% | 1 | 1 | 0% | 4,078 | 9,547 | +134% | 0 | 0 | — |
case-14 | fail→pass | 9,448 | 5,137 | -46% | 1 | 1 | 0% | 1,557 | 5,486 | +252% | 0 | 0 | — |
case-15 | fail→fail | 12,993 | 5,333 | -59% | 1 | 1 | 0% | 1,990 | 5,432 | +173% | 0 | 0 | — |
case-16 | fail→pass | 16,480 | 5,898 | -64% | 1 | 1 | 0% | 2,883 | 5,701 | +98% | 0 | 0 | — |
case-17 | fail→fail | 18,129 | 17,609 | -3% | 1 | 1 | 0% | 3,704 | 8,155 | +120% | 0 | 0 | — |
case-18 | pass→pass | 14,024 | 16,148 | +15% | 1 | 1 | 0% | 2,263 | 7,591 | +235% | 0 | 0 | — |
case-19 | fail→pass | 13,341 | 4,923 | -63% | 1 | 1 | 0% | 2,361 | 5,510 | +133% | 0 | 0 | — |
case-20 | pass→pass | 12,924 | 16,978 | +31% | 1 | 1 | 0% | 2,778 | 8,180 | +194% | 0 | 0 | — |
case-21 | pass→pass | 14,060 | 11,490 | -18% | 1 | 1 | 0% | 2,945 | 6,832 | +132% | 0 | 0 | — |
DecimalAI ran this skill against gemini-3.6-flash twice over the same eval suite — once with the skill loaded and once without — and compared the two runs case by case. 22 cases were attempted. The headline lift of +36 percentage points is the difference between those two pass rates over the 22 comparable cases. 1 case got worse with the skill loaded, and it is included in that figure.
Without the skill loaded, the model failed this case. With it loaded, the same prompt on the same model passed. This is one improved case from the latest verified run; every case, including any that regressed, is in the table above.
Other measured skills in the registry, with their headline benchmark lift.