diff --git a/backend/Dockerfile b/backend/Dockerfile
index 52e528a1..2daac8de 100644
--- a/backend/Dockerfile
+++ b/backend/Dockerfile
@@ -44,6 +44,9 @@ RUN /opt/venv/bin/pip install --no-cache-dir -r requirements.txt
# Install required browsers for playwright
RUN playwright install
+# Install wkhtmltopdf
+RUN apt-get install -y wkhtmltopdf
+
# Copy your application into the Docker image
WORKDIR /opt/copilot/backend
COPY . .
diff --git a/backend/app/connectors/grafana/reporting/report-template-test.html b/backend/app/connectors/grafana/reporting/report-template-test.html
new file mode 100644
index 00000000..b66a7937
--- /dev/null
+++ b/backend/app/connectors/grafana/reporting/report-template-test.html
@@ -0,0 +1,59 @@
+
+
+
+
+ Grafana Dashboard Report
+
+
+
+
+
Screenshots
+ {% for panel in panels %}
+
+
+
Page {{ loop.index }}
+
+ {% endfor %}
+
+
+
diff --git a/backend/app/connectors/grafana/services/reporting.py b/backend/app/connectors/grafana/services/reporting.py
index fb049c10..deefe68a 100644
--- a/backend/app/connectors/grafana/services/reporting.py
+++ b/backend/app/connectors/grafana/services/reporting.py
@@ -5,6 +5,11 @@
from playwright.async_api import async_playwright
from loguru import logger
import base64
+import os
+import sys
+import subprocess
+import pdfkit
+from jinja2 import Environment, FileSystemLoader
from sqlalchemy.ext.asyncio import AsyncSession
from app.connectors.grafana.schema.reporting import GenerateReportRequest, GenerateReportResponse, Base64Image
from app.utils import get_connector_attribute
@@ -13,6 +18,11 @@
from app.connectors.grafana.schema.reporting import GrafanaOrganizationDashboards
from app.connectors.grafana.schema.reporting import GrafanaOrganizations
from app.connectors.grafana.utils.universal import create_grafana_client
+from reportlab.lib.pagesizes import letter
+from reportlab.pdfgen import canvas
+from reportlab.platypus import SimpleDocTemplate, Image
+from reportlab.lib.units import inch
+from pathlib import Path
async def get_orgs() -> List[GrafanaOrganizations]:
@@ -101,14 +111,62 @@ async def check_login_success(page):
async def capture_screenshots(page, urls):
base64_images = []
- for url in urls:
+ for i, url in enumerate(urls):
await page.goto(url)
await page.wait_for_load_state(state='networkidle')
screenshot = await page.screenshot(type='png')
base64_image = base64.b64encode(screenshot).decode('utf-8')
- base64_images.append({"url": url, "base64_image": base64_image})
+ width = await page.evaluate('window.innerWidth')
+ height = await page.evaluate('window.innerHeight')
+ base64_images.append({
+ 'url': url,
+ 'base64_image': base64_image,
+ 'width': width,
+ 'height': height,
+ 'page_number': i + 1
+ })
return base64_images
+def get_wkhtmltopdf_path():
+ if sys.platform == 'win32':
+ return 'C:\\Program Files\\wkhtmltopdf\\bin\\wkhtmltopdf.exe'
+ elif sys.platform == 'darwin':
+ return '/usr/local/bin/wkhtmltopdf'
+ else:
+ try:
+ # Try to find the path using 'which' command in Linux
+ path = subprocess.check_output(['which', 'wkhtmltopdf'])
+ return path.strip()
+ except Exception as e:
+ logger.error(f"Could not find wkhtmltopdf: {e}")
+ return None
+
+def create_pdf(html_string):
+ logger.info(f"Creating PDF from HTML: {html_string}")
+ wkhtmltopdf_path = get_wkhtmltopdf_path()
+ if wkhtmltopdf_path is None:
+ logger.error("Cannot create PDF without wkhtmltopdf")
+ return
+ config = pdfkit.configuration(wkhtmltopdf=wkhtmltopdf_path)
+ pdfkit.from_string(html_string, 'report.pdf', configuration=config)
+
+
+def generate_html(base64_images):
+ # Load the template
+ templates_dir = Path(__file__).parent / '../reporting'
+ env = Environment(loader=FileSystemLoader(templates_dir))
+ template = env.get_template('report-template-test.html')
+
+ # Define the context
+ context = {
+ 'panels': base64_images, # Assuming this is adjusted to contain base64 encoded images
+ }
+
+ # Render the template with the context
+ html_string = template.render(context)
+
+ return html_string
+
async def generate_report(
request: GenerateReportRequest,
session: AsyncSession
@@ -124,6 +182,8 @@ async def generate_report(
return
base64_images = await capture_screenshots(page, request.urls)
await browser.close()
+ html_string = generate_html(base64_images)
+ create_pdf(html_string)
return GenerateReportResponse(
base64_images=[Base64Image(url=img['url'], base64_image=img['base64_image']) for img in base64_images],
message="Report generated successfully",
diff --git a/backend/report.pdf b/backend/report.pdf
new file mode 100644
index 00000000..2dffe4fe
Binary files /dev/null and b/backend/report.pdf differ
diff --git a/backend/requirements.txt b/backend/requirements.txt
index 6e0a4f80..d2889880 100644
--- a/backend/requirements.txt
+++ b/backend/requirements.txt
@@ -117,6 +117,7 @@ pytest-playwright==0.4.4
python-dateutil==2.8.2
python-dotenv==1.0.0
python-jose==3.3.0
+pdfkit==1.0.0
python-magic==0.4.27
python-multipart==0.0.6
pytz==2023.3.post1
@@ -128,6 +129,7 @@ regex==2023.10.3
reportlab==4.0.5
requests==2.31.0
requests-cache==1.1.0
+Jinja2=3.1.2
rfc3339-validator==0.1.4
rfc3986-validator==0.1.1
rich==13.6.0