Unverified Commit 47bf74c7 authored by Claude's avatar Claude
Browse files

Add Excel download feature to historical data page

Students can now download historical data as an Excel file for analysis.

Features:
- Download button on historical data page
- Excel file contains 4 sheets:
  1. Summary - Watch model information
  2. Monthly Data - All 120 months of detailed data (demand, revenue, profit)
  3. Yearly Summary - 10 years of aggregated data
  4. Analysis Template - Pre-formatted sheet for Year 11 predictions
- Styled headers with color coding
- Proper column widths for readability
- Filename includes current date

Dependencies:
- Added openpyxl for Excel file generation

This allows students to perform their own analysis in Excel, create charts,
and use formulas before submitting their predictions in the web app.

🤖 Generated with [Claude Code](https://claude.com/claude-code

)

Co-Authored-By: default avatarClaude <noreply@anthropic.com>
parent c6d4c7b5
Loading
Loading
Loading
Loading
+196 −2
Original line number Diff line number Diff line
@@ -5,10 +5,17 @@ Students analyze 10 years of historical data and predict year 11.
Results show financial impact of their predictions.
"""

from flask import Flask, render_template, request, session, jsonify, redirect, url_for
from flask import Flask, render_template, request, session, jsonify, redirect, url_for, send_file
import json
import os
from datetime import datetime
from io import BytesIO
try:
    from openpyxl import Workbook
    from openpyxl.styles import Font, PatternFill, Alignment
    EXCEL_AVAILABLE = True
except ImportError:
    EXCEL_AVAILABLE = False

app = Flask(__name__)
app.secret_key = 'supply-chain-forecast-secret-key-2024'
@@ -244,7 +251,194 @@ def historical():
    return render_template('historical.html',
                          watches=watches,
                          historical_data=historical_data,
                          yearly_summary=yearly_summary)
                          yearly_summary=yearly_summary,
                          excel_available=EXCEL_AVAILABLE)


@app.route('/download_excel')
def download_excel():
    """Generate and download Excel file with historical data"""
    if not EXCEL_AVAILABLE:
        return "Excel export not available. Please install openpyxl.", 500

    training_data = load_training_data()
    watches = training_data['metadata']['watches']
    historical_data = training_data['historical_data']

    # Create workbook
    wb = Workbook()

    # Remove default sheet
    wb.remove(wb.active)

    # Create Summary sheet
    ws_summary = wb.create_sheet("Summary")

    # Header style
    header_fill = PatternFill(start_color="667eea", end_color="667eea", fill_type="solid")
    header_font = Font(bold=True, color="FFFFFF")

    # Summary sheet headers
    ws_summary['A1'] = "HorloML Historical Data Summary"
    ws_summary['A1'].font = Font(bold=True, size=14)
    ws_summary.merge_cells('A1:D1')

    ws_summary['A3'] = "Watch Models"
    ws_summary['A3'].font = header_font
    ws_summary['A3'].fill = header_fill

    # Watch info
    ws_summary['A4'] = "ID"
    ws_summary['B4'] = "Name"
    ws_summary['C4'] = "Category"
    ws_summary['D4'] = "Retail Price"
    for cell in ['A4', 'B4', 'C4', 'D4']:
        ws_summary[cell].font = Font(bold=True)
        ws_summary[cell].fill = PatternFill(start_color="E0E0E0", end_color="E0E0E0", fill_type="solid")

    for idx, watch in enumerate(watches, start=5):
        ws_summary[f'A{idx}'] = watch['id']
        ws_summary[f'B{idx}'] = watch['name']
        ws_summary[f'C{idx}'] = watch['category']
        ws_summary[f'D{idx}'] = f"${watch['sell_price']}"

    # Adjust column widths
    ws_summary.column_dimensions['A'].width = 8
    ws_summary.column_dimensions['B'].width = 20
    ws_summary.column_dimensions['C'].width = 15
    ws_summary.column_dimensions['D'].width = 15

    # Create Monthly Data sheet
    ws_monthly = wb.create_sheet("Monthly Data")

    # Headers
    headers = ['Year', 'Month', 'Date']
    for watch in watches:
        headers.extend([
            f"{watch['name']} - Demand",
            f"{watch['name']} - Revenue",
            f"{watch['name']} - Profit"
        ])

    for col_idx, header in enumerate(headers, start=1):
        cell = ws_monthly.cell(row=1, column=col_idx, value=header)
        cell.font = header_font
        cell.fill = header_fill
        cell.alignment = Alignment(horizontal='center')

    # Data rows
    for row_idx, month_data in enumerate(historical_data, start=2):
        ws_monthly.cell(row=row_idx, column=1, value=month_data['year'])
        ws_monthly.cell(row=row_idx, column=2, value=month_data['month'])
        ws_monthly.cell(row=row_idx, column=3, value=month_data['date'])

        col_idx = 4
        for watch_data in month_data['watches']:
            ws_monthly.cell(row=row_idx, column=col_idx, value=watch_data['demand'])
            ws_monthly.cell(row=row_idx, column=col_idx + 1, value=watch_data['revenue'])
            ws_monthly.cell(row=row_idx, column=col_idx + 2, value=watch_data['profit'])
            col_idx += 3

    # Adjust column widths
    ws_monthly.column_dimensions['A'].width = 8
    ws_monthly.column_dimensions['B'].width = 8
    ws_monthly.column_dimensions['C'].width = 12
    for col in range(4, len(headers) + 1):
        ws_monthly.column_dimensions[ws_monthly.cell(row=1, column=col).column_letter].width = 18

    # Create Yearly Summary sheet
    ws_yearly = wb.create_sheet("Yearly Summary")

    # Headers
    yearly_headers = ['Year']
    for watch in watches:
        yearly_headers.extend([
            f"{watch['name']} - Total Demand",
            f"{watch['name']} - Total Revenue",
            f"{watch['name']} - Total Profit"
        ])

    for col_idx, header in enumerate(yearly_headers, start=1):
        cell = ws_yearly.cell(row=1, column=col_idx, value=header)
        cell.font = header_font
        cell.fill = header_fill
        cell.alignment = Alignment(horizontal='center')

    # Aggregate by year
    yearly_data = {}
    for month_data in historical_data:
        year = month_data['year']
        if year not in yearly_data:
            yearly_data[year] = {watch['id']: {'demand': 0, 'revenue': 0, 'profit': 0} for watch in watches}

        for watch_data in month_data['watches']:
            watch_id = watch_data['watch_id']
            yearly_data[year][watch_id]['demand'] += watch_data['demand']
            yearly_data[year][watch_id]['revenue'] += watch_data['revenue']
            yearly_data[year][watch_id]['profit'] += watch_data['profit']

    # Write yearly data
    for row_idx, (year, data) in enumerate(sorted(yearly_data.items()), start=2):
        ws_yearly.cell(row=row_idx, column=1, value=year)

        col_idx = 2
        for watch in watches:
            ws_yearly.cell(row=row_idx, column=col_idx, value=data[watch['id']]['demand'])
            ws_yearly.cell(row=row_idx, column=col_idx + 1, value=data[watch['id']]['revenue'])
            ws_yearly.cell(row=row_idx, column=col_idx + 2, value=data[watch['id']]['profit'])
            col_idx += 3

    # Adjust column widths
    ws_yearly.column_dimensions['A'].width = 8
    for col in range(2, len(yearly_headers) + 1):
        ws_yearly.column_dimensions[ws_yearly.cell(row=1, column=col).column_letter].width = 20

    # Create Analysis Template sheet
    ws_analysis = wb.create_sheet("Analysis Template")

    ws_analysis['A1'] = "Year 11 Prediction Worksheet"
    ws_analysis['A1'].font = Font(bold=True, size=14)
    ws_analysis.merge_cells('A1:D1')

    ws_analysis['A3'] = "Use this sheet to make your predictions for Year 11"
    ws_analysis['A3'].font = Font(italic=True)
    ws_analysis.merge_cells('A3:D3')

    ws_analysis['A5'] = "Month"
    ws_analysis['A5'].font = Font(bold=True)
    ws_analysis['A5'].fill = header_fill

    for col_idx, watch in enumerate(watches, start=2):
        cell = ws_analysis.cell(row=5, column=col_idx, value=f"{watch['name']} Prediction")
        cell.font = header_font
        cell.fill = header_fill
        cell.alignment = Alignment(horizontal='center')

    # Add months
    months = ['January', 'February', 'March', 'April', 'May', 'June',
              'July', 'August', 'September', 'October', 'November', 'December']
    for row_idx, month in enumerate(months, start=6):
        ws_analysis.cell(row=row_idx, column=1, value=month)

    # Adjust column widths
    ws_analysis.column_dimensions['A'].width = 12
    for col in range(2, len(watches) + 2):
        ws_analysis.column_dimensions[ws_analysis.cell(row=5, column=col).column_letter].width = 25

    # Save to BytesIO
    output = BytesIO()
    wb.save(output)
    output.seek(0)

    # Generate filename with current date
    filename = f"HorloML_Historical_Data_{datetime.now().strftime('%Y%m%d')}.xlsx"

    return send_file(
        output,
        mimetype='application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
        as_attachment=True,
        download_name=filename
    )


@app.route('/predict', methods=['GET', 'POST'])
+1 −0
Original line number Diff line number Diff line
Flask==3.0.0
numpy==2.3.4
Flask-Session==0.5.0
openpyxl==3.1.2
+36 −1
Original line number Diff line number Diff line
@@ -14,9 +14,44 @@

<section class="section">
    <div class="container">
        <!-- Download Button -->
        <div class="columns">
            <div class="column is-12">
                {% if excel_available %}
                <a href="{{ url_for('download_excel') }}" class="button is-success is-medium is-pulled-right">
                    <span class="icon">
                        <i class="fas fa-file-excel"></i>
                    </span>
                    <span>Download as Excel</span>
                </a>
                {% else %}
                <button class="button is-success is-medium is-pulled-right" disabled title="Excel export not available">
                    <span class="icon">
                        <i class="fas fa-file-excel"></i>
                    </span>
                    <span>Download as Excel</span>
                </button>
                {% endif %}
                <div style="clear: both;"></div>
            </div>
        </div>

        <!-- Yearly Summary -->
        <div class="box">
            <div class="level">
                <div class="level-left">
                    <div class="level-item">
                        <h3 class="title is-4">Yearly Summary</h3>
                    </div>
                </div>
                <div class="level-right">
                    <div class="level-item">
                        <div class="notification is-info is-light">
                            <strong>Tip:</strong> Download the Excel file to perform your own analysis!
                        </div>
                    </div>
                </div>
            </div>
            <div class="table-container">
                <table class="table is-fullwidth is-striped">
                    <thead>