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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: QuantumChecker
3
- Version: 0.2.5
3
+ Version: 0.2.6
4
4
  Summary: A package to evaluate homework submissions in Python, SQL, PowerBI, and SSIS.
5
5
  Author: Qobiljon
6
6
  Author-email: qobiljonkhayrullayev@gmail.com
@@ -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
- with Image.open(img) as pil_img:
101
- pil_img.thumbnail((1024, 1024))
102
- img_buffer = io.BytesIO()
103
- pil_img.save(img_buffer, format="PNG")
104
- parts.append({
105
- "inline_data": {
106
- "mime_type": "image/png",
107
- "data": base64.b64encode(img_buffer.getvalue()).decode('utf-8')
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 is provided for visual evaluation if intended")
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
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: QuantumChecker
3
- Version: 0.2.5
3
+ Version: 0.2.6
4
4
  Summary: A package to evaluate homework submissions in Python, SQL, PowerBI, and SSIS.
5
5
  Author: Qobiljon
6
6
  Author-email: qobiljonkhayrullayev@gmail.com
@@ -2,7 +2,7 @@ from setuptools import setup, find_packages
2
2
 
3
3
  setup(
4
4
  name="QuantumChecker",
5
- version="0.2.5",
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