QuantumChecker 0.2.5__tar.gz → 0.2.6__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {quantumchecker-0.2.5 → quantumchecker-0.2.6}/PKG-INFO +1 -1
- {quantumchecker-0.2.5 → quantumchecker-0.2.6}/QuantumCheck/powerbi_evaluator.py +52 -16
- {quantumchecker-0.2.5 → quantumchecker-0.2.6}/QuantumChecker.egg-info/PKG-INFO +1 -1
- {quantumchecker-0.2.5 → quantumchecker-0.2.6}/setup.py +1 -1
- {quantumchecker-0.2.5 → quantumchecker-0.2.6}/QuantumCheck/__init__.py +0 -0
- {quantumchecker-0.2.5 → quantumchecker-0.2.6}/QuantumCheck/main.py +0 -0
- {quantumchecker-0.2.5 → quantumchecker-0.2.6}/QuantumCheck/prompts.py +0 -0
- {quantumchecker-0.2.5 → quantumchecker-0.2.6}/QuantumCheck/python_evaluator.py +0 -0
- {quantumchecker-0.2.5 → quantumchecker-0.2.6}/QuantumCheck/sql_evaluator.py +0 -0
- {quantumchecker-0.2.5 → quantumchecker-0.2.6}/QuantumCheck/ssis_evaluator.py +0 -0
- {quantumchecker-0.2.5 → quantumchecker-0.2.6}/QuantumChecker.egg-info/SOURCES.txt +0 -0
- {quantumchecker-0.2.5 → quantumchecker-0.2.6}/QuantumChecker.egg-info/dependency_links.txt +0 -0
- {quantumchecker-0.2.5 → quantumchecker-0.2.6}/QuantumChecker.egg-info/requires.txt +0 -0
- {quantumchecker-0.2.5 → quantumchecker-0.2.6}/QuantumChecker.egg-info/top_level.txt +0 -0
- {quantumchecker-0.2.5 → quantumchecker-0.2.6}/README.md +0 -0
- {quantumchecker-0.2.5 → quantumchecker-0.2.6}/setup.cfg +0 -0
- {quantumchecker-0.2.5 → quantumchecker-0.2.6}/tests/test.py +0 -0
|
@@ -13,6 +13,7 @@ from dotenv import load_dotenv
|
|
|
13
13
|
from PIL import Image
|
|
14
14
|
import io
|
|
15
15
|
import base64
|
|
16
|
+
import PyPDF2 # Added for PDF validation
|
|
16
17
|
|
|
17
18
|
from .prompts import prompt_text_powerbi
|
|
18
19
|
|
|
@@ -97,16 +98,23 @@ class GeminiFlashModel:
|
|
|
97
98
|
parts = [{"text": prompt}]
|
|
98
99
|
for img in images:
|
|
99
100
|
logger.debug("Processing image: %s", img.name)
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
101
|
+
try:
|
|
102
|
+
with Image.open(img) as pil_img:
|
|
103
|
+
if pil_img.size[0] == 0 or pil_img.size[1] == 0:
|
|
104
|
+
logger.error("Invalid image dimensions for %s", img.name)
|
|
105
|
+
raise ProcessingError(f"Invalid image dimensions for {img.name}")
|
|
106
|
+
pil_img.thumbnail((1024, 1024))
|
|
107
|
+
img_buffer = io.BytesIO()
|
|
108
|
+
pil_img.save(img_buffer, format="PNG")
|
|
109
|
+
parts.append({
|
|
110
|
+
"inline_data": {
|
|
111
|
+
"mime_type": "image/png",
|
|
112
|
+
"data": base64.b64encode(img_buffer.getvalue()).decode('utf-8')
|
|
113
|
+
}
|
|
114
|
+
})
|
|
115
|
+
except Exception as e:
|
|
116
|
+
logger.error("Failed to process image %s: %s", img.name, str(e))
|
|
117
|
+
raise ProcessingError(f"Failed to process image {img.name}: {str(e)}")
|
|
110
118
|
headers = {"Content-Type": "application/json"}
|
|
111
119
|
data = {"contents": [{"parts": parts}]}
|
|
112
120
|
logger.info("Sending visual evaluation API request to %s", self.endpoint)
|
|
@@ -124,8 +132,15 @@ class GeminiFlashModel:
|
|
|
124
132
|
feedback_match = re.search(r"Feedback:\s*(.*)", output_text, re.DOTALL)
|
|
125
133
|
result = {
|
|
126
134
|
"score": int(score_match.group(1)) if score_match else 0,
|
|
127
|
-
"feedback": feedback_match.group(1).strip() if feedback_match else "No visual feedback generated"
|
|
135
|
+
"feedback": feedback_match.group(1).strip() if feedback_match else "No visual feedback generated",
|
|
136
|
+
"issues": []
|
|
128
137
|
}
|
|
138
|
+
if not score_match:
|
|
139
|
+
result["issues"].append("Failed to parse score from visual API response")
|
|
140
|
+
logger.warning("Failed to parse score from visual API response")
|
|
141
|
+
if not feedback_match:
|
|
142
|
+
result["issues"].append("Failed to parse feedback from visual API response")
|
|
143
|
+
logger.warning("Failed to parse feedback from visual API response")
|
|
129
144
|
logger.info("Visual evaluation completed: Score=%d, Feedback=%s", result["score"], result["feedback"][:50] + "..." if len(result["feedback"]) > 50 else result["feedback"])
|
|
130
145
|
return result
|
|
131
146
|
|
|
@@ -221,10 +236,24 @@ class PowerBIProcessor:
|
|
|
221
236
|
if not os.path.exists(pdf_path):
|
|
222
237
|
logger.error("PDF file does not exist: %s", pdf_path)
|
|
223
238
|
raise ProcessingError(f"PDF file not found: {pdf_path}")
|
|
239
|
+
# Validate PDF
|
|
240
|
+
try:
|
|
241
|
+
with open(pdf_path, "rb") as f:
|
|
242
|
+
pdf_reader = PyPDF2.PdfReader(f)
|
|
243
|
+
if len(pdf_reader.pages) == 0:
|
|
244
|
+
logger.error("PDF is empty: %s", pdf_path)
|
|
245
|
+
raise ProcessingError(f"PDF is empty: {pdf_path}")
|
|
246
|
+
logger.info("PDF validated, contains %d pages", len(pdf_reader.pages))
|
|
247
|
+
except Exception as e:
|
|
248
|
+
logger.error("Invalid PDF file: %s", str(e))
|
|
249
|
+
raise ProcessingError(f"Invalid PDF file: {str(e)}")
|
|
224
250
|
logger.debug("Creating output directory: %s", output_dir)
|
|
225
251
|
os.makedirs(output_dir, exist_ok=True)
|
|
226
252
|
logger.info("Converting PDF pages to images (max %d pages)", num_pages)
|
|
227
|
-
pages = convert_from_path(pdf_path, first_page=1, last_page=num_pages)
|
|
253
|
+
pages = convert_from_path(pdf_path, first_page=1, last_page=min(num_pages, len(pdf_reader.pages)))
|
|
254
|
+
if not pages:
|
|
255
|
+
logger.error("No pages converted from PDF: %s", pdf_path)
|
|
256
|
+
raise ProcessingError(f"No pages converted from PDF: {pdf_path}")
|
|
228
257
|
image_paths = []
|
|
229
258
|
for i, page in enumerate(pages):
|
|
230
259
|
image_path = os.path.join(output_dir, f"page_{i + 1}.png")
|
|
@@ -232,12 +261,12 @@ class PowerBIProcessor:
|
|
|
232
261
|
page.save(image_path, "PNG")
|
|
233
262
|
image_paths.append(image_path)
|
|
234
263
|
logger.info("Successfully processed %d pages from PDF", len(image_paths))
|
|
235
|
-
logger.debug("Removing original PDF file: %s", pdf_path)
|
|
236
|
-
os.remove(pdf_path)
|
|
237
264
|
return image_paths
|
|
238
265
|
except Exception as e:
|
|
239
266
|
logger.error("Failed to process PDF: %s", str(e))
|
|
240
|
-
raise ProcessingError(f"Failed to process PDF: {e}")
|
|
267
|
+
raise ProcessingError(f"Failed to process PDF: {str(e)}")
|
|
268
|
+
finally:
|
|
269
|
+
logger.debug("Not removing PDF file to allow debugging: %s", pdf_path)
|
|
241
270
|
|
|
242
271
|
def extract_zip(self, zip_path: str, extract_path: str) -> tuple[str, str | None]:
|
|
243
272
|
logger.info("Extracting ZIP file: %s", zip_path)
|
|
@@ -375,6 +404,9 @@ class PowerBIEvaluator:
|
|
|
375
404
|
logger.info("Processing PDF for visual evaluation: %s", pdf_path)
|
|
376
405
|
try:
|
|
377
406
|
image_paths = self.processor.process_pdf(pdf_path)
|
|
407
|
+
if not image_paths:
|
|
408
|
+
logger.error("No images generated from PDF: %s", pdf_path)
|
|
409
|
+
raise ProcessingError("No images generated from PDF")
|
|
378
410
|
logger.info("Evaluating visuals with question: %s", questions[0])
|
|
379
411
|
visual_result = self.model.evaluate_visuals(questions[0], "outputimages")
|
|
380
412
|
result["score"] = (dax_result["score"] + visual_result["score"]) // 2
|
|
@@ -385,7 +417,11 @@ class PowerBIEvaluator:
|
|
|
385
417
|
except ProcessingError as e:
|
|
386
418
|
logger.warning("Failed to process PDF, proceeding with DAX evaluation only: %s", str(e))
|
|
387
419
|
result["issues"].append(f"Visual evaluation skipped: {str(e)}")
|
|
388
|
-
result["recommendations"].append("Ensure a valid PDF
|
|
420
|
+
result["recommendations"].append("Ensure a valid PDF with Power BI visuals is provided")
|
|
421
|
+
except Exception as e:
|
|
422
|
+
logger.error("Unexpected error during visual evaluation: %s", str(e))
|
|
423
|
+
result["issues"].append(f"Visual evaluation failed: {str(e)}")
|
|
424
|
+
result["recommendations"].append("Check PDF file and API connectivity")
|
|
389
425
|
else:
|
|
390
426
|
logger.info("No PDF provided, skipping visual evaluation")
|
|
391
427
|
|
|
@@ -2,7 +2,7 @@ from setuptools import setup, find_packages
|
|
|
2
2
|
|
|
3
3
|
setup(
|
|
4
4
|
name="QuantumChecker",
|
|
5
|
-
version="0.2.
|
|
5
|
+
version="0.2.6",
|
|
6
6
|
author="Qobiljon",
|
|
7
7
|
author_email="qobiljonkhayrullayev@gmail.com",
|
|
8
8
|
description="A package to evaluate homework submissions in Python, SQL, PowerBI, and SSIS.",
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|