From 0db7c83733437602865b338d17e15e91524a11b2 Mon Sep 17 00:00:00 2001 From: insleker Date: Mon, 19 Jan 2026 12:41:06 +0800 Subject: [PATCH] feat: improve format --- .../skills}/week_report_gen/README.md | 0 .../skills}/week_report_gen/SKILL.md | 1 + .../week_report_gen/generate_report.py | 212 +++++++-- ...2026-01-16-T-16-22-3620260116-7-1r1n4h.xls | Bin ...ฎ้€ฑๅ ฑ-ๅฐ็ฃ-่ปŸ้ซ”้ƒจ-ๆ™บ่ƒฝๆŽงๅˆถ็ต„ (20260115).xlsx | Bin .../references/้ …็›ฎ้€ฑๅ ฑ-ๆจกๆฟ.xlsx | Bin .gitignore | 3 + README.md | 166 ------- UV_SETUP_GUIDE.md | 292 ------------- skills/week_report_gen/COMPLETE_GUIDE.md | 405 ------------------ skills/week_report_gen/QUICK_REFERENCE.md | 250 ----------- skills/week_report_gen/VISUAL_WORKFLOW.md | 282 ------------ .../้ …็›ฎ้€ฑๅ ฑ-ๆ™บ่ƒฝๆŽงๅˆถ็ต„-20260116.xlsx | Bin 9897 -> 0 bytes .../้ …็›ฎ้€ฑๅ ฑ-็คบไพ‹่ผธๅ‡บ-20260116.xlsx | Bin 9897 -> 0 bytes 14 files changed, 171 insertions(+), 1440 deletions(-) rename {skills => .claude/skills}/week_report_gen/README.md (100%) rename {skills => .claude/skills}/week_report_gen/SKILL.md (98%) rename {skills => .claude/skills}/week_report_gen/generate_report.py (57%) rename {skills => .claude/skills}/week_report_gen/references/cost-report-2026-01-16-T-16-22-3620260116-7-1r1n4h.xls (100%) rename {skills => .claude/skills}/week_report_gen/references/้ …็›ฎ้€ฑๅ ฑ-ๅฐ็ฃ-่ปŸ้ซ”้ƒจ-ๆ™บ่ƒฝๆŽงๅˆถ็ต„ (20260115).xlsx (100%) rename {skills => .claude/skills}/week_report_gen/references/้ …็›ฎ้€ฑๅ ฑ-ๆจกๆฟ.xlsx (100%) delete mode 100644 UV_SETUP_GUIDE.md delete mode 100644 skills/week_report_gen/COMPLETE_GUIDE.md delete mode 100644 skills/week_report_gen/QUICK_REFERENCE.md delete mode 100644 skills/week_report_gen/VISUAL_WORKFLOW.md delete mode 100644 skills/week_report_gen/references/้ …็›ฎ้€ฑๅ ฑ-ๆ™บ่ƒฝๆŽงๅˆถ็ต„-20260116.xlsx delete mode 100644 skills/week_report_gen/references/้ …็›ฎ้€ฑๅ ฑ-็คบไพ‹่ผธๅ‡บ-20260116.xlsx diff --git a/skills/week_report_gen/README.md b/.claude/skills/week_report_gen/README.md similarity index 100% rename from skills/week_report_gen/README.md rename to .claude/skills/week_report_gen/README.md diff --git a/skills/week_report_gen/SKILL.md b/.claude/skills/week_report_gen/SKILL.md similarity index 98% rename from skills/week_report_gen/SKILL.md rename to .claude/skills/week_report_gen/SKILL.md index 3e9ced4..82fa41c 100644 --- a/skills/week_report_gen/SKILL.md +++ b/.claude/skills/week_report_gen/SKILL.md @@ -295,6 +295,7 @@ print(summary_data) - Column structure - Separator rows between projects - Appropriate column widths + - **Template colors and styles**: When a template file is provided, all cell colors, fonts, borders, and other styling from the template's first 4 rows (headers) are automatically preserved in the output 6. **Multiple Team Members**: When multiple people work on the same project, their names are combined in the "ๅƒ่ˆ‡ไบบๅ“ก" column and hours are summed. diff --git a/skills/week_report_gen/generate_report.py b/.claude/skills/week_report_gen/generate_report.py similarity index 57% rename from skills/week_report_gen/generate_report.py rename to .claude/skills/week_report_gen/generate_report.py index 5b429c7..dfbc6ba 100644 --- a/skills/week_report_gen/generate_report.py +++ b/.claude/skills/week_report_gen/generate_report.py @@ -8,9 +8,98 @@ import pandas as pd import openpyxl from datetime import datetime from openpyxl.styles import Font, Alignment, PatternFill, Border, Side +from copy import copy import os +# Style constants for consistent coloring +HEADER_FILL_COLOR = 'FF2F75B5' # Blue header background +DATA_FILL_COLOR = 'FFE7E6E6' # Light gray data background +WHITE_BORDER = Border( + left=Side(style='thin', color='FFFFFFFF'), + right=Side(style='thin', color='FFFFFFFF'), + top=Side(style='thin', color='FFFFFFFF'), + bottom=Side(style='thin', color='FFFFFFFF') +) + + +def copy_cell_style(source_cell, target_cell): + """ + Copy all styling from source cell to target cell. + """ + if source_cell.has_style: + target_cell.font = copy(source_cell.font) + target_cell.border = copy(source_cell.border) + target_cell.fill = copy(source_cell.fill) + target_cell.number_format = copy(source_cell.number_format) + target_cell.protection = copy(source_cell.protection) + target_cell.alignment = copy(source_cell.alignment) + + +def copy_sheet_with_format(source_sheet, target_sheet, max_rows=10): + """ + Copy formatting from source sheet template to target sheet. + Only copies the first few rows (headers) to preserve template styling. + """ + # Copy row heights + for row_idx in range(1, max_rows + 1): + if row_idx in source_sheet.row_dimensions: + target_sheet.row_dimensions[row_idx].height = source_sheet.row_dimensions[row_idx].height + + # Copy column widths + for col_letter in ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K']: + if col_letter in source_sheet.column_dimensions: + target_sheet.column_dimensions[col_letter].width = source_sheet.column_dimensions[col_letter].width + + # Copy cell formatting for header rows (first max_rows) + for row_idx in range(1, max_rows + 1): + for col_idx in range(1, 12): # Columns A-K + source_cell = source_sheet.cell(row=row_idx, column=col_idx) + target_cell = target_sheet.cell(row=row_idx, column=col_idx) + + # Copy style + copy_cell_style(source_cell, target_cell) + + # Copy merged cells info (we'll handle merging separately) + + # Copy merged cells from template (only header rows) + for merged_range in source_sheet.merged_cells.ranges: + if merged_range.min_row <= max_rows and merged_range.max_row <= max_rows: + try: + target_sheet.merge_cells(str(merged_range)) + except: + pass # Skip if already merged or invalid + + +def style_header_row(ws, row_idx, template_cell=None, col_start=2, col_end=10): + """ + Apply header styling (blue fill, bold, centered, white grid) to a row. + If template_cell is provided, its style is copied before applying borders. + """ + for col_idx in range(col_start, col_end + 1): + cell = ws.cell(row=row_idx, column=col_idx) + if template_cell and template_cell.has_style: + copy_cell_style(template_cell, cell) + else: + cell.fill = PatternFill(start_color=HEADER_FILL_COLOR, end_color=HEADER_FILL_COLOR, fill_type='solid') + cell.font = Font(bold=True) + cell.alignment = Alignment(horizontal='center', vertical='center', wrap_text=True) + cell.border = WHITE_BORDER + + +def style_data_row(ws, row_idx, fill_color=DATA_FILL_COLOR, col_start=2, col_end=10): + """ + Apply data row styling (fill, wrap text, white grid) to a row. + """ + data_fill = PatternFill(start_color=fill_color, end_color=fill_color, fill_type='solid') + data_align = Alignment(vertical='top', wrap_text=True) + for col_idx in range(col_start, col_end + 1): + cell = ws.cell(row=row_idx, column=col_idx) + cell.fill = data_fill + cell.alignment = data_align + cell.border = WHITE_BORDER + + def read_cost_report(file_path): """ Read cost report and parse the data. @@ -150,15 +239,39 @@ def generate_weekly_report(input_file, output_file, template_file=None, team_nam if template_file and os.path.exists(template_file): print(f"Loading template from: {template_file}") wb = openpyxl.load_workbook(template_file) + + # Get the template sheet (usually the first or named 'ๅ‘จๆŠฅ็คบไพ‹') + template_sheet = None + for sheet_name_candidate in ['ๅ‘จๆŠฅ็คบไพ‹', wb.sheetnames[0]]: + if sheet_name_candidate in wb.sheetnames: + template_sheet = wb[sheet_name_candidate] + break + # Create new sheet for this week sheet_name = f"{end_date.strftime('%Y%m%d')}-{team_name}" ws = wb.create_sheet(sheet_name, 0) # Insert at beginning + + # Copy formatting from template if available + if template_sheet: + print(f"Copying formatting from template sheet: {template_sheet.title}") + copy_sheet_with_format(template_sheet, ws, max_rows=4) + + # Unmerge cells beyond row 4 to allow data writing + # Keep header merges (rows 1-3) but clear any others + merged_ranges_to_remove = [] + for merged_range in ws.merged_cells.ranges: + if merged_range.min_row > 4: + merged_ranges_to_remove.append(merged_range) + + for merged_range in merged_ranges_to_remove: + ws.unmerge_cells(str(merged_range)) else: print("Creating new workbook (no template provided)") wb = openpyxl.Workbook() ws = wb.active sheet_name = f"{end_date.strftime('%Y%m%d')}-{team_name}" ws.title = sheet_name + template_sheet = None # Write header ws['B1'] = 'ๅผ˜่จŠ็ง‘ๆŠ€่‚กไปฝๆœ‰้™ๅ…ฌๅธ' @@ -170,6 +283,10 @@ def generate_weekly_report(input_file, output_file, template_file=None, team_nam 'ๅƒ่ˆ‡ไบบๅ“ก', 'ๅทฅๆ—ถ๏ผˆไธชไบบๆŠ•ๅ…ฅ็ ”ๅ‘ๅทฅๆ—ถ', 'ไบคไป˜็‰ฉ', 'ไปฃ็ขผ๏ผˆๅฆ‚ๆœ‰๏ผ‰ๆ˜ฏๅฆไธŠๅ‚ณ', 'ไธ‹ๅ‘จ่จˆ็•ซ'] for col_idx, header in enumerate(headers, start=2): # Start from column B ws.cell(row=4, column=col_idx, value=header) + + # Ensure header row is uniformly blue (including the ไธ‹ๅ‘จ่จˆ็•ซ column) + template_header_cell = template_sheet.cell(row=4, column=2) if 'template_sheet' in locals() and template_sheet else None + style_header_row(ws, row_idx=4, template_cell=template_header_cell) # Write data starting from row 5 current_row = 5 @@ -183,62 +300,67 @@ def generate_weekly_report(input_file, output_file, template_file=None, team_nam ws.cell(row=current_row, column=8, value=row_data['ไบคไป˜็‰ฉ']) ws.cell(row=current_row, column=9, value=row_data['ไปฃ็ขผไธŠๅ‚ณ']) ws.cell(row=current_row, column=10, value=row_data['ไธ‹ๅ‘จ่จˆ็•ซ']) + + # Apply consistent gray fill and white borders to data row + style_data_row(ws, row_idx=current_row) + current_row += 1 # Add separator row (except after last entry) if idx < len(report_df) - 1: - current_row += 1 ws.cell(row=current_row, column=2, value='ๅฐˆๆกˆๅ็จฑ') for col_idx, header in enumerate(headers[1:], start=3): ws.cell(row=current_row, column=col_idx, value=header) + # Apply blue header styling to separator row (no blank white spacer) + style_header_row(ws, row_idx=current_row, template_cell=template_header_cell) current_row += 1 - # Apply formatting - header_fill = PatternFill(start_color='D3D3D3', end_color='D3D3D3', fill_type='solid') - bold_font = Font(bold=True) - center_align = Alignment(horizontal='center', vertical='center', wrap_text=True) - wrap_align = Alignment(vertical='top', wrap_text=True) - - # Format title rows - ws['B1'].font = Font(bold=True, size=14) - ws['B1'].alignment = center_align - ws['B2'].font = Font(bold=True, size=12) - ws['B2'].alignment = center_align - ws['B3'].font = Font(size=10, italic=True) - ws['B3'].alignment = wrap_align - - # Merge cells for headers - ws.merge_cells('B1:J1') - ws.merge_cells('B2:J2') - ws.merge_cells('B3:J3') - - # Format column headers and separator rows - for row in range(1, current_row + 1): - cell_b = ws.cell(row=row, column=2) - if cell_b.value == 'ๅฐˆๆกˆๅ็จฑ' or row == 4: - for col_idx in range(2, 11): - cell = ws.cell(row=row, column=col_idx) - cell.fill = header_fill - cell.font = bold_font - cell.alignment = center_align - - # Set column widths - column_widths = { - 'B': 20, # ๅฐˆๆกˆๅ็จฑ - 'C': 20, # ๅญ้ …็›ฎๅ็จฑ - 'D': 8, # ้€ฒๅบฆ - 'E': 35, # ๆœฌๅ‘จไธป่ฆ้€ฒๅฑ• - 'F': 15, # ๅƒ่ˆ‡ไบบๅ“ก - 'G': 12, # ๅทฅๆ™‚ - 'H': 15, # ไบคไป˜็‰ฉ - 'I': 10, # ไปฃ็ขผไธŠๅ‚ณ - 'J': 20 # ไธ‹ๅ‘จ่จˆ็•ซ - } - - for col, width in column_widths.items(): - ws.column_dimensions[col].width = width + # Apply formatting (only if no template was used) + if not template_sheet: + # Format title rows + ws['B1'].font = Font(bold=True, size=14) + ws['B1'].alignment = Alignment(horizontal='center', vertical='center', wrap_text=True) + ws['B2'].font = Font(bold=True, size=12) + ws['B2'].alignment = Alignment(horizontal='center', vertical='center', wrap_text=True) + ws['B3'].font = Font(size=10, italic=True) + ws['B3'].alignment = Alignment(vertical='top', wrap_text=True) + + # Merge cells for headers + ws.merge_cells('B1:J1') + ws.merge_cells('B2:J2') + ws.merge_cells('B3:J3') + + # Set column widths + column_widths = { + 'B': 20, # ๅฐˆๆกˆๅ็จฑ + 'C': 20, # ๅญ้ …็›ฎๅ็จฑ + 'D': 8, # ้€ฒๅบฆ + 'E': 35, # ๆœฌๅ‘จไธป่ฆ้€ฒๅฑ• + 'F': 15, # ๅƒ่ˆ‡ไบบๅ“ก + 'G': 12, # ๅทฅๆ™‚ + 'H': 15, # ไบคไป˜็‰ฉ + 'I': 10, # ไปฃ็ขผไธŠๅ‚ณ + 'J': 20 # ไธ‹ๅ‘จ่จˆ็•ซ + } + + for col, width in column_widths.items(): + ws.column_dimensions[col].width = width + else: + # Template was used - only apply formatting to separator rows and data cells + # Get header formatting from row 4 + header_cells = [ws.cell(row=4, column=col_idx) for col_idx in range(2, 11)] + + # Apply header formatting to separator rows + for row in range(5, current_row + 1): + cell_b = ws.cell(row=row, column=2) + if cell_b.value == 'ๅฐˆๆกˆๅ็จฑ': + for col_idx in range(2, 11): + target_cell = ws.cell(row=row, column=col_idx) + source_cell = ws.cell(row=4, column=col_idx) + copy_cell_style(source_cell, target_cell) # Apply wrap text to all data cells + wrap_align = Alignment(vertical='top', wrap_text=True) for row in range(5, current_row): for col in range(2, 11): ws.cell(row=row, column=col).alignment = wrap_align diff --git a/skills/week_report_gen/references/cost-report-2026-01-16-T-16-22-3620260116-7-1r1n4h.xls b/.claude/skills/week_report_gen/references/cost-report-2026-01-16-T-16-22-3620260116-7-1r1n4h.xls similarity index 100% rename from skills/week_report_gen/references/cost-report-2026-01-16-T-16-22-3620260116-7-1r1n4h.xls rename to .claude/skills/week_report_gen/references/cost-report-2026-01-16-T-16-22-3620260116-7-1r1n4h.xls diff --git a/skills/week_report_gen/references/้ …็›ฎ้€ฑๅ ฑ-ๅฐ็ฃ-่ปŸ้ซ”้ƒจ-ๆ™บ่ƒฝๆŽงๅˆถ็ต„ (20260115).xlsx b/.claude/skills/week_report_gen/references/้ …็›ฎ้€ฑๅ ฑ-ๅฐ็ฃ-่ปŸ้ซ”้ƒจ-ๆ™บ่ƒฝๆŽงๅˆถ็ต„ (20260115).xlsx similarity index 100% rename from skills/week_report_gen/references/้ …็›ฎ้€ฑๅ ฑ-ๅฐ็ฃ-่ปŸ้ซ”้ƒจ-ๆ™บ่ƒฝๆŽงๅˆถ็ต„ (20260115).xlsx rename to .claude/skills/week_report_gen/references/้ …็›ฎ้€ฑๅ ฑ-ๅฐ็ฃ-่ปŸ้ซ”้ƒจ-ๆ™บ่ƒฝๆŽงๅˆถ็ต„ (20260115).xlsx diff --git a/skills/week_report_gen/references/้ …็›ฎ้€ฑๅ ฑ-ๆจกๆฟ.xlsx b/.claude/skills/week_report_gen/references/้ …็›ฎ้€ฑๅ ฑ-ๆจกๆฟ.xlsx similarity index 100% rename from skills/week_report_gen/references/้ …็›ฎ้€ฑๅ ฑ-ๆจกๆฟ.xlsx rename to .claude/skills/week_report_gen/references/้ …็›ฎ้€ฑๅ ฑ-ๆจกๆฟ.xlsx diff --git a/.gitignore b/.gitignore index 1de76b8..02a7c99 100644 --- a/.gitignore +++ b/.gitignore @@ -161,3 +161,6 @@ cython_debug/ #.idea/ uv.lock +temp/ +output/ +logs/ diff --git a/README.md b/README.md index 3cde97b..0735884 100644 --- a/README.md +++ b/README.md @@ -8,30 +8,6 @@ This repository contains AI skills for automated report generation. Currently in Automatically generates weekly project reports (้ …็›ฎ้€ฑๅ ฑ) from exported Excel time tracking data. -**Features:** -- Reads cost report Excel exports from project management systems -- Aggregates work hours by person and project -- Generates formatted weekly reports following company templates -- Auto-populates team members, work hours, and progress notes -- Creates professional Excel output with proper formatting - -**Use Cases:** -- Weekly project status reporting -- Time tracking data transformation -- Team productivity summaries -- Management reporting automation - -**Quick Start:** -```bash -cd skills/week_report_gen -python generate_report.py -``` - -**Documentation:** -- [SKILL.md](skills/week_report_gen/SKILL.md) - Comprehensive skill documentation -- [README.md](skills/week_report_gen/README.md) - Usage guide and examples -- [QUICK_REFERENCE.md](skills/week_report_gen/QUICK_REFERENCE.md) - AI assistant reference - ## Getting Started ### Prerequisites @@ -39,146 +15,4 @@ python generate_report.py ```bash # Create virtual environment and install dependencies uv sync - -# Activate virtual environment (optional - uv run handles this) -.venv\Scripts\activate # Windows -source .venv/bin/activate # Linux/Mac ``` - -> **๐Ÿ’ก Tip**: This project uses `pyproject.toml` for reliable dependency management with uv. -> See [UV_SETUP_GUIDE.md](UV_SETUP_GUIDE.md) for detailed setup instructions and best practices. - -### Using with AI Assistants - -Simply tell your AI assistant (GitHub Copilot, Claude, etc.): - -> "Generate a weekly report from the cost report file" - -The AI will automatically: -1. Read the skill documentation -2. Process the input data -3. Generate a formatted report -4. Show you a summary - -### Manual Usage - -```python -from skills.week_report_gen.generate_report import generate_weekly_report - -# Generate report -output_file, summary = generate_weekly_report( - input_file='path/to/cost-report.xls', - output_file='path/to/weekly-report.xlsx' -) - -print(f"Report saved: {output_file}") -``` - -## Repository Structure - -``` -report_skill_expm/ -โ”œโ”€โ”€ README.md # This file -โ”œโ”€โ”€ skills/ -โ”‚ โ””โ”€โ”€ week_report_gen/ # Weekly report generator skill -โ”‚ โ”œโ”€โ”€ SKILL.md # Skill documentation -โ”‚ โ”œโ”€โ”€ README.md # User guide -โ”‚ โ”œโ”€โ”€ QUICK_REFERENCE.md # AI assistant reference -โ”‚ โ”œโ”€โ”€ generate_report.py # Main script -โ”‚ โ””โ”€โ”€ references/ # Sample files and templates -โ”‚ โ”œโ”€โ”€ cost-report-*.xls # Example input -โ”‚ โ”œโ”€โ”€ ้ …็›ฎ้€ฑๅ ฑ-ๆจกๆฟ.xlsx # Standard template -โ”‚ โ””โ”€โ”€ ้ …็›ฎ้€ฑๅ ฑ-*.xlsx # Example outputs -โ””โ”€โ”€ .venv/ # Virtual environment (created) -``` - -## Input & Output Examples - -### Input: Cost Report -``` -ๆ—ฅๆœŸ ไฝฟ็”จ่€… ๆดปๅ‹• ๅฐˆๆกˆ ๅ–ฎไฝ -2026-01-12 ben sung Development masterXXX 8 -2026-01-12 Ryan Hsueh - AXEL PLC 8 -2026-01-13 ๅ‚‘ ็พ… Development CNC Library ็งปๆค 4 -``` - -### Output: Weekly Report -``` -ๅผ˜่จŠ็ง‘ๆŠ€่‚กไปฝๆœ‰้™ๅ…ฌๅธ -2026ๅนดๅบฆ้€ฑๅ ฑ(01ๆœˆ12ๆ—ฅ-01ๆœˆ16ๆ—ฅ๏ผ‰ - -ๅฐˆๆกˆๅ็จฑ ๅƒ่ˆ‡ไบบๅ“ก ๅทฅๆ™‚ ๆœฌๅ‘จไธป่ฆ้€ฒๅฑ• -โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ -AXEL PLC Ryan Hsueh 48.0 - -masterXXX ben sung 15.0 Development - migrate to entry based sdo queue -CNC Library็งปๆค ๅ‚‘ ็พ… 8.0 Development - ็จ‹ๅผ็ขผๆ•ด็†ๅŠ้‡ๆง‹ -``` - -## Development - -### Adding New Skills - -1. Create a new directory under `skills/` -2. Add a `SKILL.md` file with comprehensive documentation -3. Implement the skill functionality -4. Add examples and tests -5. Update this README - -### Testing - -```bash -# Test the weekly report generator -cd skills/week_report_gen -python generate_report.py -``` - -## Use Cases - -### 1. Weekly Team Reports -Transform time tracking exports into management-ready weekly reports automatically. - -### 2. Project Status Updates -Quickly see which projects are active, who's working on them, and how many hours were invested. - -### 3. Resource Allocation Analysis -Understand how team members' time is distributed across projects. - -### 4. Management Reporting -Generate standardized reports for management review with minimal manual effort. - -## Roadmap - -Future skills being considered: - -- **Monthly Summary Generator**: Aggregate weekly reports into monthly summaries -- **Project Timeline Generator**: Visualize project progress over time -- **Resource Utilization Report**: Analyze team member utilization rates -- **Cost Analysis Report**: Transform time data into cost estimates -- **Multi-team Comparison**: Compare metrics across different teams - -## Contributing - -Contributions welcome! To add a new skill: - -1. Follow the existing skill structure -2. Provide comprehensive documentation -3. Include example files -4. Test thoroughly -5. Submit a pull request - -## License - -MIT License - See individual skill files for specific licensing information. - -## Support - -For questions or issues: -1. Check the skill-specific documentation in `skills/[skill_name]/` -2. Review example files in `references/` directories -3. Open an issue for bugs or feature requests - -## Acknowledgments - -This project demonstrates how AI skills can automate routine reporting tasks, saving time and reducing errors in data transformation and report generation workflows. \ No newline at end of file diff --git a/UV_SETUP_GUIDE.md b/UV_SETUP_GUIDE.md deleted file mode 100644 index 6c0490f..0000000 --- a/UV_SETUP_GUIDE.md +++ /dev/null @@ -1,292 +0,0 @@ -# Quick Start Guide for UV-based Setup - -## Why UV + pyproject.toml? - -โœ… **Reliable**: All dependencies locked to specific versions -โœ… **Fast**: UV is significantly faster than pip -โœ… **Reproducible**: Same environment across all machines -โœ… **Simple**: One command to set up everything -โœ… **Modern**: Using Python packaging best practices - -## Installation - -### First Time Setup - -```bash -# Install uv (if not already installed) -# Windows (PowerShell) -powershell -ExecutionPolicy ByPass -c "irm https://astral.sh/uv/install.ps1 | iex" - -# Linux/macOS -curl -LsSf https://astral.sh/uv/install.sh | sh - -# Or with pip -pip install uv -``` - -### Project Setup - -```bash -# Clone or navigate to the repository -cd report_skill_expm - -# Create virtual environment and install all dependencies -uv sync - -# That's it! All dependencies are now installed and locked -``` - -## Usage - -### Option 1: Using `uv run` (Recommended) - -No need to activate the virtual environment - uv handles it automatically: - -```bash -# Run the example -uv run python example.py - -# Run the report generator directly -uv run python skills/week_report_gen/generate_report.py - -# Import and use in Python -uv run python -c "from skills.week_report_gen.generate_report import generate_weekly_report; print('Ready!')" -``` - -### Option 2: Traditional Virtual Environment - -```bash -# Activate the virtual environment -.venv\Scripts\activate # Windows -source .venv/bin/activate # Linux/Mac - -# Now you can run Python normally -python example.py -python skills/week_report_gen/generate_report.py -``` - -## What's in pyproject.toml? - -```toml -[project] -name = "report-skill-expm" -version = "1.0.0" -requires-python = ">=3.9" - -dependencies = [ - "openpyxl>=3.1.0", # Excel file handling - "pandas>=2.0.0", # Data processing - "xlrd>=2.0.0", # Reading .xls files -] - -[project.optional-dependencies] -dev = [ - "pytest>=7.0.0", # Testing framework - "black>=23.0.0", # Code formatter - "flake8>=6.0.0", # Linter -] -``` - -## Common Commands - -```bash -# Install/update dependencies -uv sync - -# Install with dev dependencies -uv sync --extra dev - -# Add a new dependency -uv add package-name - -# Remove a dependency -uv remove package-name - -# Update all dependencies -uv sync --upgrade - -# Show installed packages -uv pip list - -# Run any Python script -uv run python your_script.py - -# Run a specific module -uv run python -m skills.week_report_gen.generate_report - -# Start Python REPL with dependencies available -uv run python -``` - -## Benefits Over Manual pip Installation - -### Before (Manual) -```bash -python -m venv .venv -.venv\Scripts\activate -pip install openpyxl pandas xlrd -# Need to remember all packages and versions -# No lock file - different versions on different machines -``` - -### After (UV + pyproject.toml) -```bash -uv sync -# Done! Everything locked and reproducible -``` - -## Dependency Management - -### Adding Dependencies - -```bash -# Add a runtime dependency -uv add requests - -# Add a development dependency -uv add --dev pytest-cov - -# Add with version constraint -uv add "pandas>=2.0,<3.0" -``` - -### Updating Dependencies - -```bash -# Update all dependencies -uv sync --upgrade - -# Update specific package -uv add --upgrade pandas -``` - -### Lock File - -The `uv.lock` file (auto-generated) ensures: -- โœ… Exact versions are used across all environments -- โœ… Transitive dependencies are locked -- โœ… No "works on my machine" issues -- โœ… Fast, deterministic installs - -**Commit `uv.lock` to version control!** - -## Troubleshooting - -### "uv: command not found" - -Install uv first: -```bash -pip install uv -# or -curl -LsSf https://astral.sh/uv/install.sh | sh -``` - -### "No module named 'xxx'" - -Make sure you've run `uv sync`: -```bash -uv sync -``` - -### Virtual Environment Not Found - -Recreate it: -```bash -rm -rf .venv # or rmdir /s .venv on Windows -uv sync -``` - -### Import Errors - -Use `uv run` to automatically activate the environment: -```bash -uv run python your_script.py -``` - -## CI/CD Integration - -### GitHub Actions - -```yaml -- name: Set up Python - uses: actions/setup-python@v4 - with: - python-version: '3.11' - -- name: Install uv - run: pip install uv - -- name: Install dependencies - run: uv sync - -- name: Run tests - run: uv run pytest -``` - -### GitLab CI - -```yaml -test: - script: - - pip install uv - - uv sync - - uv run pytest -``` - -## Migration from requirements.txt - -If you have an old `requirements.txt`: - -```bash -# Import from requirements.txt -uv add $(cat requirements.txt) - -# Or manually add each package -uv add openpyxl pandas xlrd -``` - -## Best Practices - -1. **Always commit `uv.lock`** - Ensures reproducible builds -2. **Use `uv run`** - Simplifies workflow, no activation needed -3. **Keep pyproject.toml clean** - Only list direct dependencies -4. **Use version constraints** - `>=3.1.0` not `==3.1.0` -5. **Separate dev dependencies** - Use `[project.optional-dependencies]` - -## Comparison: pip vs uv - -| Feature | pip | uv | -|---------|-----|-----| -| Speed | ๐ŸŒ Slow | ๐Ÿš€ Fast (10-100x) | -| Lock file | requirements.txt (manual) | uv.lock (automatic) | -| Resolver | Sometimes inconsistent | Always consistent | -| Parallel installs | No | Yes | -| Built-in venv | Need separate commands | Integrated | - -## Resources - -- [UV Documentation](https://github.com/astral-sh/uv) -- [Python Packaging Guide](https://packaging.python.org/) -- [pyproject.toml Specification](https://peps.python.org/pep-0621/) - ---- - -**Quick Reference Card** - -```bash -# Setup -uv sync # Install everything - -# Run -uv run python app.py # Execute with dependencies - -# Manage -uv add pkg # Add dependency -uv remove pkg # Remove dependency -uv sync --upgrade # Update all - -# Dev -uv sync --extra dev # Install dev dependencies -uv run pytest # Run tests -uv run black . # Format code -``` diff --git a/skills/week_report_gen/COMPLETE_GUIDE.md b/skills/week_report_gen/COMPLETE_GUIDE.md deleted file mode 100644 index d5dc2fa..0000000 --- a/skills/week_report_gen/COMPLETE_GUIDE.md +++ /dev/null @@ -1,405 +0,0 @@ -# Weekly Report Generator Skill - Complete Guide - -## ๐ŸŽฏ What This Skill Does - -The **Weekly Report Generator** (`week_report_gen`) is an AI skill that automatically transforms time tracking data from project management systems into professional weekly project reports (้ …็›ฎ้€ฑๅ ฑ). - -### Key Benefits - -โœ… **Saves Time**: Automates report generation from raw time tracking data -โœ… **Reduces Errors**: Eliminates manual data entry and calculation mistakes -โœ… **Consistent Format**: Uses company standard templates every time -โœ… **Smart Aggregation**: Automatically groups work by project and team member -โœ… **AI-Powered**: Works seamlessly with GitHub Copilot, Claude, and other AI assistants - ---- - -## ๐Ÿ“ฆ What's Included - -``` -skills/week_report_gen/ -โ”œโ”€โ”€ SKILL.md # Comprehensive technical documentation -โ”œโ”€โ”€ README.md # User guide with examples -โ”œโ”€โ”€ QUICK_REFERENCE.md # AI assistant reference guide -โ”œโ”€โ”€ generate_report.py # Main Python script -โ””โ”€โ”€ references/ - โ”œโ”€โ”€ cost-report-*.xls # Example input file - โ”œโ”€โ”€ ้ …็›ฎ้€ฑๅ ฑ-ๆจกๆฟ.xlsx # Standard template - โ””โ”€โ”€ ้ …็›ฎ้€ฑๅ ฑ-*.xlsx # Example outputs -``` - ---- - -## ๐Ÿš€ Quick Start - -### 1. Setup (One Time) - -```bash -# From repository root: create venv and install all dependencies -uv sync - -# Activate it (optional - uv run handles this automatically) -.venv\Scripts\activate # Windows -source .venv/bin/activate # Mac/Linux -``` - -### 2. Generate Your First Report - -**Option A: Using AI Assistant** (Easiest) - -Just say: -> "Generate a weekly report from my cost report file" - -**Option B: Command Line** - -```bash -# From repository root -uv run python skills/week_report_gen/generate_report.py - -# Or navigate first -cd skills/week_report_gen -uv run python generate_report.py -``` - -**Option C: Python Script** - -```python -from skills.week_report_gen.generate_report import generate_weekly_report - -output_file, summary = generate_weekly_report( - input_file='cost-report.xls', - output_file='weekly-report.xlsx' -) -``` - ---- - -## ๐Ÿ“Š Input โ†’ Output - -### Input: Cost Report Excel -Raw time tracking data exported from your project management system. - -| ๆ—ฅๆœŸ | ไฝฟ็”จ่€… | ๆดปๅ‹• | ๅฐˆๆกˆ | ๅ–ฎไฝ | -|------|--------|------|------|------| -| 2026-01-12 | ben sung | Development | masterXXX | 8 | -| 2026-01-12 | Ryan Hsueh | - | AXEL PLC | 8 | - -### Output: Weekly Report Excel -Formatted, professional report ready for management review. - -| ๅฐˆๆกˆๅ็จฑ | ๅƒ่ˆ‡ไบบๅ“ก | ๅทฅๆ™‚ | ๆœฌๅ‘จไธป่ฆ้€ฒๅฑ• | ไธ‹ๅ‘จ่จˆ็•ซ | -|----------|----------|------|--------------|----------| -| masterXXX | ben sung | 15.0 | Development
migrate to entry based sdo queue | _(fill in)_ | -| AXEL PLC | Ryan Hsueh | 48.0 | - | _(fill in)_ | - ---- - -## ๐ŸŽจ Key Features - -### 1. Smart Data Aggregation -- Automatically sums hours by project and person -- Groups multiple entries from the same project -- Combines team members who worked on the same project - -### 2. Auto-Population -**Automatically filled:** -- โœ… ๅฐˆๆกˆๅ็จฑ (Project name) -- โœ… ๅƒ่ˆ‡ไบบๅ“ก (Team members) -- โœ… ๅทฅๆ™‚ (Total hours) -- โœ… ๆœฌๅ‘จไธป่ฆ้€ฒๅฑ• (Progress notes from comments) - -**Leave blank for manual input:** -- โฌœ ้€ฒๅบฆ (Progress percentage) -- โฌœ ไบคไป˜็‰ฉ (Deliverables) -- โฌœ ไปฃ็ขผไธŠๅ‚ณ (Code upload status) -- โฌœ ไธ‹ๅ‘จ่จˆ็•ซ (Next week's plan) - -### 3. Professional Formatting -- Company header and branding -- Proper column widths -- Header formatting with gray background -- Separator rows between projects -- Text wrapping for long entries -- Merged cells for titles - -### 4. Date Range Detection -Automatically extracts the reporting period from the data: -``` -2026ๅนดๅบฆ้€ฑๅ ฑ(01ๆœˆ12ๆ—ฅ-01ๆœˆ16ๆ—ฅ๏ผ‰ -``` - ---- - -## ๐Ÿ’ก Usage Examples - -### Example 1: Basic Generation - -```bash -# Navigate to skill directory -cd skills/week_report_gen - -# Run with default settings (uses sample file) -python generate_report.py - -# Output: ้ …็›ฎ้€ฑๅ ฑ-ๆ™บ่ƒฝๆŽงๅˆถ็ต„-20260116.xlsx -``` - -### Example 2: Custom Files - -```bash -# Specify input and output files -python generate_report.py \ - "path/to/your-cost-report.xls" \ - "path/to/output-weekly-report.xlsx" -``` - -### Example 3: Python Integration - -```python -from generate_report import generate_weekly_report - -# Generate report for specific team -output, summary = generate_weekly_report( - input_file='data/cost-report.xls', - output_file='reports/weekly-20260116.xlsx', - template_file='templates/standard.xlsx', - team_name='็ ”็™ผ้ƒจ-AI็ต„' -) - -# View summary -print(f"Generated report with {len(summary)} projects") -print(f"Total hours: {summary['ๅทฅๆ™‚'].sum()}") -``` - -### Example 4: AI Assistant - -``` -You: "Generate a weekly report from cost-report-2026-01-16.xls for the AI team" - -AI: "I'll generate the weekly report for the AI team. - - [Processing...] - - โœ“ Report generated successfully! - - Summary of 5 projects: - - Project A (Team Member 1, Team Member 2, 32 hours) - - Project B (Team Member 3, 16 hours) - ... - - Report saved to: ้ …็›ฎ้€ฑๅ ฑ-AI็ต„-20260116.xlsx - - Please review and fill in: - - Progress percentages - - Deliverables - - Next week's plan" -``` - ---- - -## ๐Ÿ“‹ After Report Generation - -### Manual Steps Required - -1. **Open the Excel file** in your spreadsheet application - -2. **Review "ๆœฌๅ‘จไธป่ฆ้€ฒๅฑ•" (Progress)** - - Check auto-generated progress notes - - Edit for clarity and completeness - - Add any missing accomplishments - -3. **Fill in "้€ฒๅบฆ" (Progress %)** - - Enter progress as decimal (0.5 = 50%) - - Example: 0.75 for 75% complete - -4. **Add "ไบคไป˜็‰ฉ" (Deliverables)** - - List specific deliverables if any - - Include document names, versions, etc. - -5. **Mark "ไปฃ็ขผไธŠๅ‚ณ" (Code Upload)** - - Enter "Y" if code was uploaded - - Enter "N" if not applicable - -6. **Plan "ไธ‹ๅ‘จ่จˆ็•ซ" (Next Week)** - - Outline next week's goals - - Be specific and actionable - -7. **Add Visual Materials** (Optional) - - Insert diagrams, screenshots, etc. - - Reference in the header suggestion - ---- - -## ๐Ÿ”ง Customization - -### Change Team Name - -```python -generate_weekly_report( - input_file='cost.xls', - output_file='report.xlsx', - team_name='ๆ‚จ็š„ๅœ˜้šŠๅ็จฑ' # Your team name -) -``` - -### Adjust Column Widths - -Edit `generate_report.py`: -```python -column_widths = { - 'B': 25, # Wider project name column - 'E': 40, # Wider progress column - # Adjust as needed -} -``` - -### Modify Grouping Logic - -Edit the `format_for_weekly_report()` function to change: -- How projects are grouped -- What information is included in progress -- How team members are listed - ---- - -## ๐Ÿ› Troubleshooting - -### Error: "Module not found" - -**Solution:** -```bash -# Make sure you're in the virtual environment -.venv\Scripts\activate - -# Install dependencies -uv pip install openpyxl pandas xlrd -``` - -### Error: "Column not found" - -**Cause:** Input file has different column names - -**Solution:** -1. Open the input Excel file -2. Check column names match: ๆ—ฅๆœŸ, ไฝฟ็”จ่€…, ๅฐˆๆกˆ, ๅ–ฎไฝ -3. If different, the script will try alternative headers -4. Contact support if issues persist - -### Empty or No Output - -**Check:** -1. Input file has data (not just headers) -2. Data starts at expected row (usually row 2) -3. File isn't corrupted or password-protected -4. Required columns have values (not all empty) - -### Report Looks Wrong - -**Verify:** -1. Template file exists and is valid -2. Using correct template for your organization -3. Excel/LibreOffice can open the file -4. File isn't read-only - ---- - -## ๐Ÿ“š Additional Resources - -### Documentation Files - -- **[SKILL.md](SKILL.md)** - Complete technical documentation - - Detailed API reference - - Function descriptions - - Code examples - - Error handling guide - -- **[README.md](README.md)** - Comprehensive user guide - - Feature overview - - Installation instructions - - Usage examples - - Customization options - -- **[QUICK_REFERENCE.md](QUICK_REFERENCE.md)** - AI assistant guide - - How to use with AI assistants - - Common patterns - - Troubleshooting for AI - - Best practices - -### Example Files - -Located in `references/` directory: -- Input example: `cost-report-*.xls` -- Template: `้ …็›ฎ้€ฑๅ ฑ-ๆจกๆฟ.xlsx` -- Output example: `้ …็›ฎ้€ฑๅ ฑ-ๅฐ็ฃ-่ปŸ้ซ”้ƒจ-ๆ™บ่ƒฝๆŽงๅˆถ็ต„ (20260115).xlsx` - ---- - -## ๐ŸŽ“ Tips & Best Practices - -### For Users - -1. **Run Weekly**: Generate reports at week-end for best workflow -2. **Review Before Sharing**: Always check auto-generated content -3. **Keep Template Updated**: Update template when format changes -4. **Backup Originals**: Keep raw cost reports for audit trail -5. **Consistent Naming**: Use consistent project names in time tracking - -### For Developers - -1. **Test with Real Data**: Use actual cost reports for testing -2. **Handle Edge Cases**: Test with empty data, special characters, etc. -3. **Preserve Formulas**: If using formulas, don't hardcode values -4. **Document Changes**: Update SKILL.md when modifying code -5. **Version Control**: Track changes to template and script - -### For AI Assistants - -1. **Validate First**: Check input file exists before processing -2. **Show Progress**: Display what's being done at each step -3. **Summarize Results**: Show key metrics after generation -4. **Remind Manual Steps**: List fields that need manual input -5. **Handle Errors Gracefully**: Provide helpful error messages - ---- - -## ๐Ÿ“ž Support & Contact - -### Getting Help - -1. **Check Documentation**: Review SKILL.md and README.md -2. **Review Examples**: Look at reference files in `references/` -3. **Test Script**: Run the example script: `python example.py` -4. **Open Issue**: Report bugs or request features - -### Reporting Issues - -When reporting issues, include: -- Error message (full stack trace) -- Input file format (column names, sample data) -- Expected vs actual output -- Version of Python and libraries -- Operating system - ---- - -## ๐Ÿ“ License - -MIT License - See individual skill files for complete terms. - ---- - -## ๐Ÿ™ Acknowledgments - -This skill demonstrates how AI can automate routine reporting tasks, saving valuable time and reducing errors in data transformation workflows. - -**Built with:** -- Python 3.x -- openpyxl (Excel file handling) -- pandas (Data processing) -- Love and attention to detail โค๏ธ - ---- - -**Version:** 1.0.0 -**Last Updated:** January 16, 2026 -**Status:** Production Ready โœ… diff --git a/skills/week_report_gen/QUICK_REFERENCE.md b/skills/week_report_gen/QUICK_REFERENCE.md deleted file mode 100644 index af8d208..0000000 --- a/skills/week_report_gen/QUICK_REFERENCE.md +++ /dev/null @@ -1,250 +0,0 @@ -# Weekly Report Generator - Quick Reference - -## For AI Assistants (GitHub Copilot/Claude) - -When a user asks to generate a weekly report, follow these steps: - -### 1. Identify the Request - -User might say: -- "Generate a weekly report" -- "Create ้ …็›ฎ้€ฑๅ ฑ from cost report" -- "Transform time tracking data into weekly report" -- "Make a weekly project report from the Excel export" - -### 2. Read the Skill Documentation - -```python -# Read the full skill instructions -read_file('skills/week_report_gen/SKILL.md') -``` - -### 3. Use the Generate Report Script - -```python -# Import and run the generator -from skills.week_report_gen.generate_report import generate_weekly_report - -# Generate report with provided file paths -output_file, summary_df = generate_weekly_report( - input_file='path/to/cost-report.xls', - output_file='path/to/output-report.xlsx', - template_file='skills/week_report_gen/references/้ …็›ฎ้€ฑๅ ฑ-ๆจกๆฟ.xlsx', - team_name='ๆ™บ่ƒฝๆŽงๅˆถ็ต„' # Or user-specified team name -) - -print(f"โœ“ Report generated: {output_file}") -print("\nProject Summary:") -for idx, row in summary_df.iterrows(): - print(f"\n{row['ๅฐˆๆกˆๅ็จฑ']}") - print(f" Team: {row['ๅƒ่ˆ‡ไบบๅ“ก']}") - print(f" Hours: {row['ๅทฅๆ™‚']}") -``` - -### 4. Alternative: Run via Terminal - -```bash -cd skills/week_report_gen -python generate_report.py "path/to/cost-report.xls" "path/to/output.xlsx" -``` - -## Common Usage Patterns - -### Pattern 1: User Provides Input File - -``` -User: "Generate weekly report from cost-report-2026-01-16.xls" - -Assistant Actions: -1. Locate the input file -2. Determine output filename (based on date and team) -3. Run generate_weekly_report() -4. Show summary of projects and hours -5. Confirm file saved location -``` - -### Pattern 2: User Wants to Customize Output - -``` -User: "Create weekly report for ็ ”็™ผ้ƒจ team" - -Assistant Actions: -1. Ask for input file if not specified -2. Use team_name parameter: team_name='็ ”็™ผ้ƒจ' -3. Generate report -4. Confirm customization applied -``` - -### Pattern 3: User Has Multiple Cost Reports - -``` -User: "Generate reports for all cost reports in the folder" - -Assistant Actions: -1. List all .xls/.xlsx files in references folder -2. Filter for cost report files -3. Generate report for each -4. Provide summary of all generated reports -``` - -## Key Functions - -### read_cost_report(file_path) -Reads and parses the cost report Excel file. - -**Returns**: DataFrame with columns [ๆ—ฅๆœŸ, ไฝฟ็”จ่€…, ๆดปๅ‹•, ๅฐˆๆกˆ, ๅ–ฎไฝ, etc.] - -### aggregate_work_hours(df) -Groups work hours by user and project. - -**Returns**: -- `summary_df`: Aggregated data -- `start_date`: Report start date -- `end_date`: Report end date - -### format_for_weekly_report(summary_df) -Transforms aggregated data into weekly report format. - -**Returns**: DataFrame ready for Excel output - -### generate_weekly_report(input_file, output_file, template_file, team_name) -Main function that orchestrates the entire process. - -**Returns**: -- `output_file`: Path to generated report -- `report_df`: Summary DataFrame - -## Example AI Responses - -### Example 1: Basic Generation - -``` -User: "Generate a weekly report from the cost report file" - -AI Assistant: -"I'll generate the weekly report from your cost report. - -[Runs generate_report.py] - -โœ“ Weekly report generated successfully! - -Summary of 8 projects: -- AXEL PLC (Ryan Hsueh, 48 hours) -- CNC Library ็งปๆค (ๅ‚‘ ็พ…, 8 hours) -- masterXXX (ben sung, 15 hours) -... - -Report saved to: ้ …็›ฎ้€ฑๅ ฑ-ๆ™บ่ƒฝๆŽงๅˆถ็ต„-20260116.xlsx - -Please review and fill in the following fields: -- ้€ฒๅบฆ (Progress percentages) -- ไบคไป˜็‰ฉ (Deliverables) -- ไปฃ็ขผไธŠๅ‚ณ (Code upload status) -- ไธ‹ๅ‘จ่จˆ็•ซ (Next week's plan)" -``` - -### Example 2: Custom Team Name - -``` -User: "Create a weekly report for the ็ ”็™ผ้ƒจ-AI็ต„ team" - -AI Assistant: -"I'll create a weekly report for the ็ ”็™ผ้ƒจ-AI็ต„ team. - -[Runs with team_name='็ ”็™ผ้ƒจ-AI็ต„'] - -โœ“ Report generated with custom team name! - -File: ้ …็›ฎ้€ฑๅ ฑ-็ ”็™ผ้ƒจ-AI็ต„-20260116.xlsx" -``` - -### Example 3: Batch Processing - -``` -User: "Generate weekly reports for all cost report files" - -AI Assistant: -"I found 3 cost report files. Generating reports... - -1. cost-report-2026-01-16.xls โ†’ ้ …็›ฎ้€ฑๅ ฑ-20260116.xlsx โœ“ -2. cost-report-2026-01-09.xls โ†’ ้ …็›ฎ้€ฑๅ ฑ-20260109.xlsx โœ“ -3. cost-report-2025-12-30.xls โ†’ ้ …็›ฎ้€ฑๅ ฑ-20251230.xlsx โœ“ - -All reports generated successfully!" -``` - -## Troubleshooting Guide for AI - -### Issue: File Not Found - -```python -# Check if file exists -import os -if not os.path.exists(input_file): - # List available files - files = os.listdir('skills/week_report_gen/references') - print(f"Available files: {files}") - # Ask user for correct filename -``` - -### Issue: Missing Columns - -```python -# Check what columns are present -import pandas as pd -df = pd.read_excel(input_file, header=1) -print(f"Available columns: {list(df.columns)}") -# Suggest using different header row or file format -``` - -### Issue: Empty Output - -```python -# Check data was loaded -if len(df) == 0: - print("No data found in file. Check:") - print("1. Correct sheet is being read") - print("2. Data starts at expected row") - print("3. File isn't corrupted") -``` - -## Best Practices for AI Assistants - -1. **Always validate input file exists** before processing -2. **Show summary of data** after reading (number of entries, date range, projects) -3. **Display key metrics** in the output (total hours, number of projects, team members) -4. **Remind user of manual fields** that need to be filled in -5. **Provide file location** of generated report clearly -6. **Handle errors gracefully** with helpful error messages -7. **Suggest next steps** after generation (review, fill in fields, etc.) - -## Quick Code Snippets - -### Check Available Cost Reports -```python -import os -files = [f for f in os.listdir('skills/week_report_gen/references') - if f.startswith('cost-report') and f.endswith(('.xls', '.xlsx'))] -print(f"Found {len(files)} cost report(s): {files}") -``` - -### Validate Input File Structure -```python -import pandas as pd -df = pd.read_excel(file_path, header=1) -required = ['ๆ—ฅๆœŸ', 'ไฝฟ็”จ่€…', 'ๅฐˆๆกˆ', 'ๅ–ฎไฝ'] -missing = [col for col in required if col not in df.columns] -if missing: - print(f"โš  Missing columns: {missing}") -else: - print("โœ“ File structure is valid") -``` - -### Preview Data Before Processing -```python -df = pd.read_excel(file_path, header=1) -print(f"Date range: {df['ๆ—ฅๆœŸ'].min()} to {df['ๆ—ฅๆœŸ'].max()}") -print(f"Total entries: {len(df)}") -print(f"Projects: {', '.join(df['ๅฐˆๆกˆ'].unique()[:5])}") -print(f"Team members: {', '.join(df['ไฝฟ็”จ่€…'].unique()[:5])}") -``` diff --git a/skills/week_report_gen/VISUAL_WORKFLOW.md b/skills/week_report_gen/VISUAL_WORKFLOW.md deleted file mode 100644 index 43c4efc..0000000 --- a/skills/week_report_gen/VISUAL_WORKFLOW.md +++ /dev/null @@ -1,282 +0,0 @@ -# Weekly Report Generator - Visual Workflow - -``` -โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” -โ”‚ WEEKLY REPORT GENERATOR WORKFLOW โ”‚ -โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ - -โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” -โ”‚ INPUT FILE โ”‚ -โ”‚ (Cost Report) โ”‚ -โ”‚ .xls/.xlsx โ”‚ -โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ - โ”‚ - โ”‚ ๆ—ฅๆœŸ ไฝฟ็”จ่€… ๆดปๅ‹• ๅฐˆๆกˆ ๅ–ฎไฝ - โ”‚ 01-12 ben sung Dev masterXXX 8 - โ”‚ 01-12 Ryan - AXEL PLC 8 - โ”‚ 01-13 ๅ‚‘ ็พ… Dev CNC Lib 4 - โ”‚ - โ–ผ -โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” -โ”‚ STEP 1: READ & PARSE โ”‚ -โ”‚ โ€ข Load Excel file โ”‚ -โ”‚ โ€ข Identify columns โ”‚ -โ”‚ โ€ข Clean missing data โ”‚ -โ”‚ โ€ข Extract date range โ”‚ -โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ - โ”‚ - โ”‚ DataFrame with validated data - โ”‚ Date range: 2026-01-12 to 2026-01-16 - โ”‚ - โ–ผ -โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” -โ”‚ STEP 2: AGGREGATE โ”‚ -โ”‚ โ€ข Group by user & project โ”‚ -โ”‚ โ€ข Sum work hours โ”‚ -โ”‚ โ€ข Combine activities โ”‚ -โ”‚ โ€ข Collect comments โ”‚ -โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ - โ”‚ - โ”‚ Project: masterXXX - โ”‚ โ€ข ben sung: 15 hours - โ”‚ โ€ข Activities: Development, Specification - โ”‚ โ€ข Notes: migrate to entry based sdo queue... - โ”‚ - โ–ผ -โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” -โ”‚ STEP 3: FORMAT โ”‚ -โ”‚ โ€ข Group by project โ”‚ -โ”‚ โ€ข List team members โ”‚ -โ”‚ โ€ข Format progress notes โ”‚ -โ”‚ โ€ข Calculate totals โ”‚ -โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ - โ”‚ - โ”‚ Formatted report data ready for Excel - โ”‚ - โ–ผ -โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” -โ”‚ STEP 4: GENERATE EXCEL โ”‚ -โ”‚ โ€ข Load template (if exists) โ”‚ -โ”‚ โ€ข Create new sheet โ”‚ -โ”‚ โ€ข Write headers โ”‚ -โ”‚ โ€ข Write project data โ”‚ -โ”‚ โ€ข Apply formatting โ”‚ -โ”‚ โ€ข Save file โ”‚ -โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ - โ”‚ - โ–ผ -โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” -โ”‚ OUTPUT FILE (Weekly Report) โ”‚ -โ”‚ ้ …็›ฎ้€ฑๅ ฑ-ๆ™บ่ƒฝๆŽงๅˆถ็ต„ โ”‚ -โ”‚ โ”‚ -โ”‚ ๅผ˜่จŠ็ง‘ๆŠ€่‚กไปฝๆœ‰้™ๅ…ฌๅธ โ”‚ -โ”‚ 2026ๅนดๅบฆ้€ฑๅ ฑ(01ๆœˆ12ๆ—ฅ-01ๆœˆ16ๆ—ฅ๏ผ‰ โ”‚ -โ”‚ โ”‚ -โ”‚ ๅฐˆๆกˆ ๅƒ่ˆ‡ไบบๅ“ก ๅทฅๆ™‚ ๆœฌๅ‘จ้€ฒๅฑ• โ”‚ -โ”‚ โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ โ”‚ -โ”‚ AXEL Ryan Hsueh 48.0 - โ”‚ -โ”‚ master ben sung 15.0 Development โ”‚ -โ”‚ XXX migrate to.. โ”‚ -โ”‚ โ”‚ -โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ - - -DATA FLOW DIAGRAM -โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ• - -Raw Time Data โ”€โ”€โ”€โ”€โ”€โ”€โ” - โ”‚ -Export from PM โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ–บ Read Excel โ”€โ”€โ”€โ”€โ–บ Parse Columns -System โ”‚ โ”‚ - โ”‚ โ”‚ -Cost Report File โ”€โ”€โ”€โ”˜ โ–ผ - Validate & Clean - โ”‚ - โ–ผ - Extract Date Range - โ”‚ - โ–ผ - Group by Project โ”€โ”€โ”€โ”€โ” - โ”‚ โ”‚ - โ”‚ โ”‚ - Sum Hours โ—„โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค - โ”‚ โ”‚ - โ”‚ โ”‚ - Combine Teams โ—„โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ - โ”‚ - โ–ผ - Format Progress - โ”‚ - โ–ผ - Load Template - โ”‚ - โ–ผ - Write to Excel - โ”‚ - โ–ผ - Apply Styling - โ”‚ - โ–ผ - Save Output File - โ”‚ - โ–ผ - Weekly Report โœ“ - - -COMPONENT ARCHITECTURE -โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ• - -โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” -โ”‚ SKILL ARCHITECTURE โ”‚ -โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค -โ”‚ โ”‚ -โ”‚ โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚ -โ”‚ โ”‚ User Interface Layer โ”‚ โ”‚ -โ”‚ โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค โ”‚ -โ”‚ โ”‚ โ€ข Command Line Interface โ”‚ โ”‚ -โ”‚ โ”‚ โ€ข AI Assistant Integration โ”‚ โ”‚ -โ”‚ โ”‚ โ€ข Python API โ”‚ โ”‚ -โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ”‚ -โ”‚ โ”‚ โ”‚ -โ”‚ โ–ผ โ”‚ -โ”‚ โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚ -โ”‚ โ”‚ Core Processing Layer โ”‚ โ”‚ -โ”‚ โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค โ”‚ -โ”‚ โ”‚ โ€ข read_cost_report() โ”‚ โ”‚ -โ”‚ โ”‚ โ””โ”€โ–บ Load Excel, validate columns โ”‚ โ”‚ -โ”‚ โ”‚ โ”‚ โ”‚ -โ”‚ โ”‚ โ€ข aggregate_work_hours() โ”‚ โ”‚ -โ”‚ โ”‚ โ””โ”€โ–บ Group data, sum hours, extract dates โ”‚ โ”‚ -โ”‚ โ”‚ โ”‚ โ”‚ -โ”‚ โ”‚ โ€ข format_for_weekly_report() โ”‚ โ”‚ -โ”‚ โ”‚ โ””โ”€โ–บ Transform to report format โ”‚ โ”‚ -โ”‚ โ”‚ โ”‚ โ”‚ -โ”‚ โ”‚ โ€ข generate_weekly_report() โ”‚ โ”‚ -โ”‚ โ”‚ โ””โ”€โ–บ Main orchestration function โ”‚ โ”‚ -โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ”‚ -โ”‚ โ”‚ โ”‚ -โ”‚ โ–ผ โ”‚ -โ”‚ โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚ -โ”‚ โ”‚ Output Generation Layer โ”‚ โ”‚ -โ”‚ โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค โ”‚ -โ”‚ โ”‚ โ€ข Excel Writer (openpyxl) โ”‚ โ”‚ -โ”‚ โ”‚ โ€ข Template Handler โ”‚ โ”‚ -โ”‚ โ”‚ โ€ข Style & Format Applier โ”‚ โ”‚ -โ”‚ โ”‚ โ€ข File Saver โ”‚ โ”‚ -โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ”‚ -โ”‚ โ”‚ -โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ - - โ–ฒ โ–ฒ - โ”‚ โ”‚ - โ”Œโ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ” โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ” - โ”‚ pandas โ”‚ โ”‚ openpyxl โ”‚ - โ”‚ (Data โ”‚ โ”‚ (Excel โ”‚ - โ”‚ Process) โ”‚ โ”‚ Files) โ”‚ - โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ - - -FILE ORGANIZATION -โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ• - -skills/week_report_gen/ -โ”‚ -โ”œโ”€โ”€ ๐Ÿ“„ SKILL.md โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ–บ Technical documentation -โ”‚ โ€ข API reference -โ”‚ โ€ข Function details -โ”‚ โ€ข Code examples -โ”‚ -โ”œโ”€โ”€ ๐Ÿ“„ README.md โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ–บ User guide -โ”‚ โ€ข Installation -โ”‚ โ€ข Quick start -โ”‚ โ€ข Examples -โ”‚ -โ”œโ”€โ”€ ๐Ÿ“„ QUICK_REFERENCE.md โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ–บ AI assistant guide -โ”‚ โ€ข Usage patterns -โ”‚ โ€ข Best practices -โ”‚ -โ”œโ”€โ”€ ๐Ÿ“„ COMPLETE_GUIDE.md โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ–บ Comprehensive guide -โ”‚ โ€ข Everything combined -โ”‚ โ€ข Tips & tricks -โ”‚ -โ”œโ”€โ”€ ๐Ÿ generate_report.py โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ–บ Main script -โ”‚ โ€ข Core functions -โ”‚ โ€ข Entry point -โ”‚ -โ””โ”€โ”€ ๐Ÿ“ references/ - โ”‚ - โ”œโ”€โ”€ ๐Ÿ“Š cost-report-*.xls โ”€โ”€โ”€โ”€โ–บ Example input - โ”œโ”€โ”€ ๐Ÿ“‹ ้ …็›ฎ้€ฑๅ ฑ-ๆจกๆฟ.xlsx โ”€โ”€โ”€โ–บ Template - โ””โ”€โ”€ ๐Ÿ“Š ้ …็›ฎ้€ฑๅ ฑ-*.xlsx โ”€โ”€โ”€โ”€โ”€โ”€โ–บ Example output - - -USAGE PATTERNS -โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ• - -Pattern 1: AI Assistant -โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ -User: "Generate weekly report" - โ”‚ - โ–ผ -AI reads SKILL.md - โ”‚ - โ–ผ -AI runs generate_report.py - โ”‚ - โ–ผ -AI shows summary - โ”‚ - โ–ผ -Report saved โœ“ - - -Pattern 2: Command Line -โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ -$ python generate_report.py input.xls output.xlsx - โ”‚ - โ–ผ -Script processes data - โ”‚ - โ–ผ -Console shows progress - โ”‚ - โ–ผ -Report saved โœ“ - - -Pattern 3: Python Integration -โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ -from generate_report import generate_weekly_report - โ”‚ - โ–ผ -output, summary = generate_weekly_report(...) - โ”‚ - โ–ผ -Use summary data in your code - โ”‚ - โ–ผ -Process complete โœ“ - - -KEY SUCCESS FACTORS -โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ• - -โœ“ Input Validation - โ””โ”€โ–บ Verify file exists, columns present, data valid - -โœ“ Smart Aggregation - โ””โ”€โ–บ Group logically, sum accurately, preserve info - -โœ“ Template Support - โ””โ”€โ–บ Use existing templates, maintain consistency - -โœ“ Error Handling - โ””โ”€โ–บ Clear messages, graceful failures, helpful hints - -โœ“ Formatting - โ””โ”€โ–บ Professional appearance, proper widths, colors - -โœ“ Documentation - โ””โ”€โ–บ Clear examples, complete reference, AI-friendly - -``` diff --git a/skills/week_report_gen/references/้ …็›ฎ้€ฑๅ ฑ-ๆ™บ่ƒฝๆŽงๅˆถ็ต„-20260116.xlsx b/skills/week_report_gen/references/้ …็›ฎ้€ฑๅ ฑ-ๆ™บ่ƒฝๆŽงๅˆถ็ต„-20260116.xlsx deleted file mode 100644 index 186d10674baf862eaaf71486a42a8e3b5693e5e9..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 9897 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

`6w8sPf5NiZS|7e>fl|xOYX4(Txv`E4! zY2&^Y1K*68@S!r>q2q>_LQqIR8@z^-Zq`6CwVv)v>|U6#?@S;-OMPdk}KGKV>*R$ht zM3R0QWwL~gy`OOSGy>#ghQA^J*x2lo^8o4#?~J^N7_DJxl6Nwr3e z`{jXJ4Tp;Wrx{UY;%v1gMluH0DvQ%c&E+>duiqQ4Mk=xdNtR${oP<~ggPlv{%n?SMXJf!HS1 zis%b6@@FgQ;{G1$ZRa0@sO@JL3D<-iVb;VkUGrVqW|n*x1Ueo&I`7-tZm-TRbsICE zIWfocw~7>u>UDnx5k{B|m4Qy4MxU-`dIyguiBA>8HhjbSqGtl%Lf~4U&g)I61x@l_ zmQ-aMI+j?GZU=`lU%u+?x1a0%a^DH~H%%+_9GDtjW-a<<_VE6iy}y|HpL6(|wfzZl z2!Sl95;f=!fR{j2erbix3N|yU(?fS(&{*QZF@39IUZhQ7#hKjDJ&|kzwMBmxVd*hJ z_|%@i@*-OsA0UYlPu$;`XNgDZ2)={4TfDSqFuUZvO*H!rdi30C1R3;ESqDxxPC|U zd3($!dwoN_v(MR0XVf{~ulICR;NQ9y{lM2GeqpKs^j|d&+M>2LPNp_a`l{}BrjEM5 z*xIi;D&Nb3`j9j0+ltnKpZ2e-Ht`b!yh-+8`P9is@4MDkL_NUBW8E0$iEHMWSmz# z%qlu6+qCBB#v$_;kAEA}rB{Ktf!DIAm-^y&axSw%JD7JcR8D`^dgjA&mtE;szCqF~QOXvy$5)<&>3xZw<^!wxYGRptEJm^pK zpS3uDVF7@b;*tME|6Q5$C;ZQnm4D#NFF*f(6tMg$;Lnuk-vXNa+u?o-__YT9SAz6U zDSrm={*j{kQegDbjX$OQ4(I(T<Ib5Z^yr;`i-_&=7Yq6`e|uP-6I96`T) J4#lsh{{iy10S5p8 diff --git a/skills/week_report_gen/references/้ …็›ฎ้€ฑๅ ฑ-็คบไพ‹่ผธๅ‡บ-20260116.xlsx b/skills/week_report_gen/references/้ …็›ฎ้€ฑๅ ฑ-็คบไพ‹่ผธๅ‡บ-20260116.xlsx deleted file mode 100644 index 96a6bda89061c70aa2a916c61ac3b158de348d88..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 9897 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 N5%^ckq5SLazW{B70bu|D