diff --git a/Dockerfile b/Dockerfile index 0fff3f1..b3ac090 100644 --- a/Dockerfile +++ b/Dockerfile @@ -3,19 +3,31 @@ FROM python:3.11-slim WORKDIR /app -# Install system dependencies +# Install system dependencies (cached unless this changes) RUN apt-get update && apt-get install -y \ curl \ + gcc \ + g++ \ && rm -rf /var/lib/apt/lists/* +# Upgrade pip and install build tools first (cached) +RUN pip install --upgrade pip setuptools wheel + # Copy requirements first for better caching COPY requirements.txt . # Install Python dependencies -RUN pip install --no-cache-dir -r requirements.txt +# Remove --no-cache-dir to use pip's cache (much faster rebuilds) +# This layer will be cached unless requirements.txt changes +RUN pip install --upgrade pip && \ + pip install -r requirements.txt # Copy application files -COPY . . +# Docker automatically detects file changes via content hash +# If any .py file changes, only this layer and after rebuild (apt-get & pip stay cached!) +COPY *.py ./ +COPY *.yaml* ./ +COPY *.md ./ # Create directories for reports and output RUN mkdir -p /app/reports /app/output diff --git a/api_server.py b/api_server.py index a2da95a..66b15aa 100644 --- a/api_server.py +++ b/api_server.py @@ -81,6 +81,7 @@ def create_app(config_path: Optional[str] = None): try: request_data = request.json or {} download_from_sp = request_data.get('download_from_sharepoint', False) + downloaded_files = [] # Initialize here for scope # Download from SharePoint if requested if download_from_sp: @@ -105,9 +106,10 @@ def create_app(config_path: Optional[str] = None): file_pattern=sp_config.get('file_pattern'), overwrite=sp_config.get('overwrite', True) ) - logger.info(f"Downloaded {len(downloaded)} file(s) from SharePoint") + downloaded_files = downloaded if downloaded else [] + logger.info(f"Downloaded {len(downloaded_files)} file(s) from SharePoint: {downloaded_files}") except Exception as e: - logger.error(f"Failed to download from SharePoint: {e}") + logger.error(f"Failed to download from SharePoint: {e}", exc_info=True) return jsonify({ 'error': f'SharePoint download failed: {str(e)}' }), 500 @@ -131,7 +133,9 @@ def create_app(config_path: Optional[str] = None): 'message': 'Report generated successfully', 'output_file': output_file, 'summary': report_data.get('summary', {}), - 'vendors_count': len(report_data.get('vendors', [])) + 'vendors_count': len(report_data.get('vendors', [])), + 'downloaded_files': len(downloaded_files) if download_from_sp else 0, + 'downloaded_file_names': [Path(f).name for f in downloaded_files] if download_from_sp else [] }) else: return jsonify({ diff --git a/html_generator.py b/html_generator.py index 290273d..447a552 100644 --- a/html_generator.py +++ b/html_generator.py @@ -61,6 +61,11 @@ def get_priority_badge_class(priority: Optional[str]) -> str: return "badge-secondary" +def escape_js_string(s: str) -> str: + """Escape a string for use in JavaScript double-quoted strings.""" + return s.replace('\\', '\\\\').replace('"', '\\"').replace('\n', '\\n').replace('\r', '\\r') + + def generate_html_report(json_path: str, output_path: Optional[str] = None) -> str: """ Generate HTML report from JSON report file. @@ -786,7 +791,7 @@ def generate_html_content(report_data: Dict) -> str:
- {''.join([f'' for vn in vendor_names])} + {''.join(['' for vn in vendor_names])}
@@ -1276,13 +1281,13 @@ def generate_vendor_section(vendor: Dict) -> str:
- - - - - - - + + + + + + +
diff --git a/output/preprocessed_data.txt b/output/preprocessed_data.txt index 829c09b..286954a 100644 --- a/output/preprocessed_data.txt +++ b/output/preprocessed_data.txt @@ -1,6 +1,6 @@ PREPROCESSED EXCEL DATA ================================================================================ -Current Date (Baltimore/Eastern): 2025-11-06 11:37:45 EST +Current Date (Baltimore/Eastern): 2025-11-06 16:50:13 EST Total Items: 162 VENDOR: Amazon diff --git a/output/report.html b/output/report.html index fbc972f..0058910 100644 --- a/output/report.html +++ b/output/report.html @@ -587,7 +587,7 @@

Vendor Punchlist Report

- Generated: 2025-11-06 20:37:46 | + Generated: 2025-11-07 01:50:13 | Total Vendors: 16 | Total Items: 162
@@ -673,7 +673,7 @@
- +
@@ -708,13 +708,13 @@
- - - - - - - + + + + + + +
@@ -860,13 +860,13 @@
- - - - - - - + + + + + + +
@@ -3841,13 +3841,13 @@ KK 11/30 - Fixed the "Unknown" S04 message, verified with AWCS data

- - - - - - - + + + + + + +
@@ -4074,13 +4074,13 @@ KK 10/19 - Done

- - - - - - - + + + + + + +
@@ -4305,13 +4305,13 @@ KK 10/19 - Done

- - - - - - - + + + + + + +
@@ -5660,13 +5660,13 @@ confirmed that it works

- - - - - - - + + + + + + +
@@ -5931,13 +5931,13 @@ https://t.corp.amazon.com/V1969041198
- - - - - - - + + + + + + +
@@ -7108,13 +7108,13 @@ https://t.corp.amazon.com/V1969041198
- - - - - - - + + + + + + +
@@ -7260,13 +7260,13 @@ https://t.corp.amazon.com/V1969041198
- - - - - - - + + + + + + +
@@ -7412,13 +7412,13 @@ https://t.corp.amazon.com/V1969041198
- - - - - - - + + + + + + +
@@ -7564,13 +7564,13 @@ https://t.corp.amazon.com/V1969041198
- - - - - - - + + + + + + +
@@ -7919,13 +7919,13 @@ https://t.corp.amazon.com/V1969041198
- - - - - - - + + + + + + +
@@ -8071,13 +8071,13 @@ https://t.corp.amazon.com/V1969041198
- - - - - - - + + + + + + +
@@ -8266,13 +8266,13 @@ https://t.corp.amazon.com/V1969041198
- - - - - - - + + + + + + +
@@ -8418,13 +8418,13 @@ https://t.corp.amazon.com/V1969041198
- - - - - - - + + + + + + +
@@ -8793,13 +8793,13 @@ https://t.corp.amazon.com/V1969041198
- - - - - - - + + + + + + +
diff --git a/output/report.json b/output/report.json index bc88ee1..65e099c 100644 --- a/output/report.json +++ b/output/report.json @@ -1,5 +1,5 @@ { - "report_generated_at": "2025-11-06T20:37:46.364471", + "report_generated_at": "2025-11-07T01:50:13.559722", "vendors": [ { "vendor_name": "Amazon", diff --git a/sharepoint_downloader.py b/sharepoint_downloader.py index 5715ceb..bfefc22 100644 --- a/sharepoint_downloader.py +++ b/sharepoint_downloader.py @@ -67,9 +67,10 @@ class SharePointDownloader: try: if self.use_app_authentication and self.client_id and self.client_secret: # App authentication (recommended for automation) + logger.info(f"Attempting app authentication with client_id: {self.client_id[:8]}...") credentials = ClientCredential(self.client_id, self.client_secret) self.ctx = ClientContext(self.site_url).with_credentials(credentials) - logger.info("Authenticated with SharePoint using app credentials") + logger.info("Created SharePoint context with app credentials") elif self.username and self.password: # User authentication credentials = UserCredential(self.username, self.password) @@ -80,6 +81,7 @@ class SharePointDownloader: return False # Test connection + logger.info("Testing SharePoint connection...") web = self.ctx.web self.ctx.load(web) self.ctx.execute_query() @@ -87,7 +89,10 @@ class SharePointDownloader: return True except Exception as e: - logger.error(f"SharePoint authentication failed: {e}") + logger.error(f"SharePoint authentication failed: {e}", exc_info=True) + logger.error(f"Site URL: {self.site_url}") + logger.error(f"Client ID: {self.client_id[:8] if self.client_id else 'None'}...") + logger.error(f"Using app auth: {self.use_app_authentication}") return False def download_file( diff --git a/test_docker.ps1 b/test_docker.ps1 new file mode 100644 index 0000000..7a991db --- /dev/null +++ b/test_docker.ps1 @@ -0,0 +1,26 @@ +# Simple Docker test script +Write-Host "=== Testing Docker Image ===" -ForegroundColor Cyan + +Write-Host "`n1. Testing local file..." -ForegroundColor Yellow +python test_syntax.py + +Write-Host "`n2. Building Docker image..." -ForegroundColor Yellow +docker build --no-cache -t vendor-report-api-test . 2>&1 | Select-String "Step|COPY|ERROR" | Select-Object -Last 5 + +Write-Host "`n3. Checking line 794 in LOCAL file:" -ForegroundColor Yellow +Get-Content html_generator.py | Select-Object -Index 793 + +Write-Host "`n4. Checking line 794 in DOCKER image:" -ForegroundColor Yellow +docker run --rm vendor-report-api-test sed -n '794p' /app/html_generator.py 2>&1 + +Write-Host "`n5. Checking line 1284 in LOCAL file:" -ForegroundColor Yellow +Get-Content html_generator.py | Select-Object -Index 1283 + +Write-Host "`n6. Checking line 1284 in DOCKER image:" -ForegroundColor Yellow +docker run --rm vendor-report-api-test sed -n '1284p' /app/html_generator.py 2>&1 + +Write-Host "`n7. Testing Python import in Docker:" -ForegroundColor Yellow +docker run --rm vendor-report-api-test python -c "import html_generator; print('SUCCESS')" 2>&1 + +Write-Host "`n=== Done ===" -ForegroundColor Cyan + diff --git a/test_syntax.py b/test_syntax.py new file mode 100644 index 0000000..b0b20a4 --- /dev/null +++ b/test_syntax.py @@ -0,0 +1,20 @@ +#!/usr/bin/env python3 +"""Quick syntax test for html_generator.py""" + +import sys + +try: + import html_generator + print("SUCCESS: html_generator.py imports without syntax errors!") + print(f"escape_js_string function exists: {hasattr(html_generator, 'escape_js_string')}") + sys.exit(0) +except SyntaxError as e: + print(f"SYNTAX ERROR: {e}") + print(f" File: {e.filename}") + print(f" Line: {e.lineno}") + print(f" Text: {e.text}") + sys.exit(1) +except Exception as e: + print(f"ERROR: {e}") + sys.exit(1) + diff --git a/verify_build.ps1 b/verify_build.ps1 new file mode 100644 index 0000000..3524310 --- /dev/null +++ b/verify_build.ps1 @@ -0,0 +1,55 @@ +# Comprehensive build verification script +Write-Host "=== Vendor Report API Build Verification ===" -ForegroundColor Cyan + +# 1. Verify local file syntax +Write-Host "`n1. Checking local file syntax..." -ForegroundColor Yellow +python -c "import html_generator; print('Local file syntax OK')" 2>&1 +if ($LASTEXITCODE -ne 0) { + Write-Host "Local file has syntax errors!" -ForegroundColor Red + exit 1 +} else { + Write-Host "Local file syntax OK" -ForegroundColor Green +} + +# 2. Check specific lines +Write-Host "`n2. Checking fixed lines..." -ForegroundColor Yellow +$line794 = (Get-Content html_generator.py)[793] +$line1284 = (Get-Content html_generator.py)[1283] + +Write-Host "Line 794: $($line794.Substring(0, [Math]::Min(80, $line794.Length)))..." +if ($line794 -match '\\"') { + Write-Host "Line 794 still has backslash!" -ForegroundColor Red +} else { + Write-Host "Line 794 looks correct (no backslash)" -ForegroundColor Green +} + +Write-Host "Line 1284: $($line1284.Substring(0, [Math]::Min(80, $line1284.Length)))..." +if ($line1284 -match '\\"') { + Write-Host "Line 1284 still has backslash!" -ForegroundColor Red +} else { + Write-Host "Line 1284 looks correct (no backslash)" -ForegroundColor Green +} + +# 3. Build image with --no-cache +Write-Host "`n3. Building Docker image with --no-cache..." -ForegroundColor Yellow +docker build --no-cache -t vendor-report-api-test . 2>&1 | Select-String "Step|COPY|ERROR" | Select-Object -Last 10 + +# 4. Verify what's in the image +Write-Host "`n4. Verifying Docker image contents..." -ForegroundColor Yellow +Write-Host "Line 794 in image:" +docker run --rm vendor-report-api-test sed -n '794p' /app/html_generator.py 2>&1 + +Write-Host "`nLine 1284 in image:" +docker run --rm vendor-report-api-test sed -n '1284p' /app/html_generator.py 2>&1 + +# 5. Test import in Docker +Write-Host "`n5. Testing Python import in Docker container..." -ForegroundColor Yellow +docker run --rm vendor-report-api-test python -c "import html_generator; print('SUCCESS')" 2>&1 +if ($LASTEXITCODE -eq 0) { + Write-Host "✓ Docker image works!" -ForegroundColor Green +} else { + Write-Host "✗ Docker image still has errors!" -ForegroundColor Red +} + +Write-Host "`n=== Verification Complete ===" -ForegroundColor Cyan + diff --git a/verify_docker_image.ps1 b/verify_docker_image.ps1 new file mode 100644 index 0000000..1cf3450 --- /dev/null +++ b/verify_docker_image.ps1 @@ -0,0 +1,32 @@ +# Verify what's actually in the Docker image +param( + [string]$ImageName = "vendor-report-api-test" +) + +Write-Host "=== Verifying Docker Image Contents ===" -ForegroundColor Cyan + +# Build the image +Write-Host "`n1. Building image: $ImageName" -ForegroundColor Yellow +docker build -t $ImageName . 2>&1 | Select-String "COPY|Step" + +# Check line 794 in the image +Write-Host "`n2. Checking line 794 in Docker image:" -ForegroundColor Yellow +docker run --rm $ImageName sed -n '794p' /app/html_generator.py + +# Check line 1284 in the image +Write-Host "`n3. Checking line 1284 in Docker image:" -ForegroundColor Yellow +docker run --rm $ImageName sed -n '1284p' /app/html_generator.py + +# Try to import the module +Write-Host "`n4. Testing Python import in Docker:" -ForegroundColor Yellow +docker run --rm $ImageName python -c "import html_generator; print('SUCCESS')" 2>&1 + +# Compare with local file +Write-Host "`n5. Local file line 794:" -ForegroundColor Yellow +Get-Content html_generator.py | Select-Object -Index 793 + +Write-Host "`n6. Local file line 1284:" -ForegroundColor Yellow +Get-Content html_generator.py | Select-Object -Index 1283 + +Write-Host "`n=== Done ===" -ForegroundColor Cyan +