QuantumChecker 0.2.3__tar.gz → 0.2.5__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.3
3
+ Version: 0.2.5
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
@@ -24,15 +24,17 @@ logging.basicConfig(
24
24
  handlers=[logging.FileHandler("../powerbi_evaluator.log"), logging.StreamHandler()]
25
25
  )
26
26
 
27
- # GeminiFlashModel class remains unchanged
28
27
  class GeminiFlashModel:
29
28
  def __init__(self, api_key: str, model_name: str = "gemini-1.5-flash"):
29
+ logger.info("Initializing GeminiFlashModel with model: %s", model_name)
30
30
  api_key = os.getenv("GEMINI_API_KEY") or api_key
31
31
  if not api_key:
32
+ logger.error("API key not found in environment variables or provided argument")
32
33
  raise ValueError("API key not found in .env file or environment variables.")
33
34
  self.api_key = api_key
34
35
  self.model_name = model_name
35
36
  self.endpoint = f"https://generativelanguage.googleapis.com/v1beta/models/{model_name}:generateContent"
37
+ logger.info("GeminiFlashModel initialized successfully with endpoint: %s", self.endpoint)
36
38
 
37
39
  @retry(
38
40
  stop=stop_after_attempt(3),
@@ -40,14 +42,16 @@ class GeminiFlashModel:
40
42
  retry=retry_if_exception_type((requests.exceptions.RequestException,))
41
43
  )
42
44
  def evaluate(self, question_answer_pairs: List[Dict[str, str]]) -> Dict[str, any]:
43
- logger.info("Starting evaluation of %d Power BI question-answer pairs", len(question_answer_pairs))
45
+ logger.info("Starting evaluation of %d question-answer pairs", len(question_answer_pairs))
44
46
  combined_content = "\n\n".join(
45
47
  f"Question {i}:\n{qa['question']}\n\nAnswer {i}:\n{qa['answer']}\n"
46
48
  for i, qa in enumerate(question_answer_pairs, 1)
47
49
  )
50
+ logger.debug("Prepared combined content for evaluation: %s", combined_content[:100] + "..." if len(combined_content) > 100 else combined_content)
48
51
 
49
52
  headers = {"Content-Type": "application/json"}
50
53
  data = {"contents": [{"parts": [{"text": prompt_text_powerbi(combined_content)}]}]}
54
+ logger.info("Sending API request to %s", self.endpoint)
51
55
  response = requests.post(f"{self.endpoint}?key={self.api_key}", headers=headers, json=data)
52
56
 
53
57
  if response.status_code != 200:
@@ -58,6 +62,7 @@ class GeminiFlashModel:
58
62
  logger.error("API response missing candidates: %s", response_data)
59
63
  raise ValueError("No candidates in API response")
60
64
  generated_text = response_data["candidates"][0]["content"]["parts"][0]["text"]
65
+ logger.info("Received API response, parsing generated text")
61
66
  return self._parse_response(generated_text)
62
67
 
63
68
  @retry(
@@ -66,10 +71,14 @@ class GeminiFlashModel:
66
71
  retry=retry_if_exception_type((requests.exceptions.RequestException,))
67
72
  )
68
73
  def evaluate_visuals(self, question: str, image_folder: str) -> Dict[str, any]:
74
+ logger.info("Starting visual evaluation for question: %s", question)
69
75
  folder_path = Path(image_folder)
70
76
  images = list(folder_path.glob("*.png"))[:3]
71
77
  if not images:
78
+ logger.error("No PNG images found in folder: %s", image_folder)
72
79
  raise ProcessingError(f"No PNG images found in {image_folder}")
80
+ logger.info("Found %d PNG images for evaluation: %s", len(images), [img.name for img in images])
81
+
73
82
  prompt = (
74
83
  "Evaluate the Power BI report visuals based on the given task.\n\n"
75
84
  f"Task: {question}\n\n"
@@ -87,6 +96,7 @@ class GeminiFlashModel:
87
96
  )
88
97
  parts = [{"text": prompt}]
89
98
  for img in images:
99
+ logger.debug("Processing image: %s", img.name)
90
100
  with Image.open(img) as pil_img:
91
101
  pil_img.thumbnail((1024, 1024))
92
102
  img_buffer = io.BytesIO()
@@ -99,23 +109,28 @@ class GeminiFlashModel:
99
109
  })
100
110
  headers = {"Content-Type": "application/json"}
101
111
  data = {"contents": [{"parts": parts}]}
112
+ logger.info("Sending visual evaluation API request to %s", self.endpoint)
102
113
  response = requests.post(f"{self.endpoint}?key={self.api_key}", headers=headers, json=data)
103
114
  if response.status_code != 200:
104
- logger.error("API request failed: Status %d, Response: %s", response.status_code, response.text)
115
+ logger.error("Visual API request failed: Status %d, Response: %s", response.status_code, response.text)
105
116
  raise Exception(f"API call failed: {response.status_code} - {response.text}")
106
117
  response_data = response.json()
107
118
  if not response_data.get("candidates"):
108
- logger.error("API response missing candidates: %s", response_data)
119
+ logger.error("Visual API response missing candidates: %s", response_data)
109
120
  raise ValueError("No candidates in API response")
110
121
  output_text = response_data["candidates"][0]["content"]["parts"][0]["text"]
122
+ logger.info("Received visual API response, parsing output")
111
123
  score_match = re.search(r"Score:\s*(\d+)(?:/100)?", output_text)
112
124
  feedback_match = re.search(r"Feedback:\s*(.*)", output_text, re.DOTALL)
113
- return {
125
+ result = {
114
126
  "score": int(score_match.group(1)) if score_match else 0,
115
127
  "feedback": feedback_match.group(1).strip() if feedback_match else "No visual feedback generated"
116
128
  }
129
+ logger.info("Visual evaluation completed: Score=%d, Feedback=%s", result["score"], result["feedback"][:50] + "..." if len(result["feedback"]) > 50 else result["feedback"])
130
+ return result
117
131
 
118
132
  def _parse_response(self, text: str) -> Dict[str, any]:
133
+ logger.info("Parsing API response text")
119
134
  result = {"score": 0, "feedback": "Evaluation not returned by API.", "issues": [], "recommendations": []}
120
135
  try:
121
136
  lines = text.split("\n")
@@ -127,96 +142,136 @@ class GeminiFlashModel:
127
142
  try:
128
143
  result["score"] = int(line.split(":")[1].split("/")[0].strip())
129
144
  score_found = True
145
+ logger.info("Parsed score: %d", result["score"])
130
146
  except ValueError:
131
147
  result["issues"].append("Failed to parse score from API response")
148
+ logger.error("Failed to parse score from response: %s", line)
132
149
  continue
133
150
  elif score_found:
134
151
  feedback_lines.append(line)
135
152
  if feedback_lines:
136
153
  result["feedback"] = "\n".join(feedback_lines).strip()
154
+ logger.debug("Parsed feedback: %s", result["feedback"][:50] + "..." if len(result["feedback"]) > 50 else result["feedback"])
137
155
  return result
138
156
  except Exception as e:
139
157
  result["issues"].append(str(e))
158
+ logger.error("Error parsing response: %s", str(e))
140
159
  return result
141
160
 
142
161
  class PowerBIProcessor:
143
162
  def extract_datamodel(self, pbit_file_path: str) -> Dict:
163
+ logger.info("Extracting data model from PBIT file: %s", pbit_file_path)
144
164
  if not os.path.exists(pbit_file_path):
165
+ logger.error("PBIT file does not exist: %s", pbit_file_path)
145
166
  raise ProcessingError(f"PBIT file not found: {pbit_file_path}")
146
167
  folder_path = os.path.dirname(pbit_file_path)
147
168
  file_name = os.path.splitext(os.path.basename(pbit_file_path))[0]
148
169
  zip_file = os.path.join(folder_path, f"{file_name}.zip")
149
170
  export_path = os.path.join(folder_path, "export")
171
+ logger.debug("Cleaning up temporary files: %s, %s", zip_file, export_path)
150
172
  self._cleanup(zip_file, export_path)
151
173
  try:
174
+ logger.info("Renaming PBIT to ZIP: %s -> %s", pbit_file_path, zip_file)
152
175
  os.rename(pbit_file_path, zip_file)
153
176
  if not zipfile.is_zipfile(zip_file):
177
+ logger.error("File is not a valid ZIP: %s", zip_file)
154
178
  raise ProcessingError(f"File is not a valid ZIP: {zip_file}")
179
+ logger.info("Extracting ZIP contents to: %s", export_path)
155
180
  with zipfile.ZipFile(zip_file, "r") as zip_ref:
156
181
  zip_ref.extractall(export_path)
157
182
  schema_path = os.path.join(export_path, "DataModelSchema")
158
183
  txt_path = os.path.join(export_path, "DataModelSchema.txt")
184
+ logger.debug("Renaming schema file: %s -> %s", schema_path, txt_path)
159
185
  os.rename(schema_path, txt_path)
186
+ logger.info("Reading DataModelSchema file: %s", txt_path)
160
187
  with open(txt_path, "r", encoding="utf-16-le") as file:
161
- return json.load(file)
188
+ data = json.load(file)
189
+ logger.info("Successfully extracted data model from PBIT file")
190
+ return data
162
191
  except UnicodeDecodeError as e:
163
192
  logger.error("Failed to decode DataModelSchema: %s", str(e))
164
193
  raise ProcessingError(f"Invalid encoding in DataModelSchema: {e}")
165
194
  except Exception as e:
195
+ logger.error("Failed to extract DataModelSchema: %s", str(e))
166
196
  raise ProcessingError(f"Failed to extract DataModelSchema: {e}")
167
197
  finally:
198
+ logger.debug("Cleaning up temporary files after extraction")
168
199
  self._cleanup(zip_file, export_path)
169
200
 
170
201
  def extract_model_data(self, data: Dict) -> Dict:
202
+ logger.info("Extracting model data from data model")
171
203
  try:
172
204
  tables = data.get("model", {}).get("tables", [])
173
205
  relationships = data.get("model", {}).get("relationships", [])
174
- return {
206
+ result = {
175
207
  "Calculated Measures": self._get_measures(tables),
176
208
  "Tables": self._get_tables_and_columns(tables),
177
209
  "Relationships": self._get_relationships(relationships)
178
210
  }
211
+ logger.info("Extracted model data: %d measures, %d tables, %d relationships",
212
+ len(result["Calculated Measures"]), len(result["Tables"]), len(result["Relationships"]))
213
+ return result
179
214
  except Exception as e:
215
+ logger.error("Failed to extract model data: %s", str(e))
180
216
  raise ProcessingError(f"Failed to extract model data: {e}")
181
217
 
182
218
  def process_pdf(self, pdf_path: str, output_dir: str = "outputimages", num_pages: int = 3) -> List[str]:
219
+ logger.info("Processing PDF file: %s", pdf_path)
183
220
  try:
184
221
  if not os.path.exists(pdf_path):
222
+ logger.error("PDF file does not exist: %s", pdf_path)
185
223
  raise ProcessingError(f"PDF file not found: {pdf_path}")
224
+ logger.debug("Creating output directory: %s", output_dir)
186
225
  os.makedirs(output_dir, exist_ok=True)
226
+ logger.info("Converting PDF pages to images (max %d pages)", num_pages)
187
227
  pages = convert_from_path(pdf_path, first_page=1, last_page=num_pages)
188
228
  image_paths = []
189
229
  for i, page in enumerate(pages):
190
230
  image_path = os.path.join(output_dir, f"page_{i + 1}.png")
231
+ logger.debug("Saving page %d as PNG: %s", i + 1, image_path)
191
232
  page.save(image_path, "PNG")
192
233
  image_paths.append(image_path)
234
+ logger.info("Successfully processed %d pages from PDF", len(image_paths))
235
+ logger.debug("Removing original PDF file: %s", pdf_path)
193
236
  os.remove(pdf_path)
194
237
  return image_paths
195
238
  except Exception as e:
239
+ logger.error("Failed to process PDF: %s", str(e))
196
240
  raise ProcessingError(f"Failed to process PDF: {e}")
197
241
 
198
242
  def extract_zip(self, zip_path: str, extract_path: str) -> tuple[str, str | None]:
243
+ logger.info("Extracting ZIP file: %s", zip_path)
199
244
  try:
200
245
  if not os.path.exists(zip_path):
246
+ logger.error("ZIP file does not exist: %s", zip_path)
201
247
  raise ProcessingError(f"ZIP file not found: {zip_path}")
202
248
  if not zipfile.is_zipfile(zip_path):
249
+ logger.error("File is not a valid ZIP: %s", zip_path)
203
250
  raise ProcessingError(f"File is not a valid ZIP: {zip_path}")
251
+ logger.debug("Creating extraction directory: %s", extract_path)
204
252
  os.makedirs(extract_path, exist_ok=True)
205
253
  with zipfile.ZipFile(zip_path, "r") as zip_ref:
254
+ logger.info("Extracting ZIP contents to: %s", extract_path)
206
255
  zip_ref.extractall(extract_path)
207
256
  pbit_files = list(Path(extract_path).glob("*.pbit"))
208
257
  pdf_files = list(Path(extract_path).glob("*.pdf"))
258
+ logger.info("Found %d PBIT files and %d PDF files in ZIP", len(pbit_files), len(pdf_files))
209
259
  if not pbit_files:
260
+ logger.error("No PBIT files found in ZIP")
210
261
  raise ProcessingError("ZIP file must contain at least one .pbit file")
211
262
  if len(pbit_files) > 1:
263
+ logger.error("Multiple PBIT files found in ZIP: %s", [str(p) for p in pbit_files])
212
264
  raise ProcessingError("ZIP file contains multiple .pbit files")
213
265
  pdf_path = str(pdf_files[0]) if pdf_files else None
266
+ logger.info("Extracted PBIT file: %s, PDF file: %s", str(pbit_files[0]), pdf_path)
214
267
  return str(pbit_files[0]), pdf_path
215
268
  except Exception as e:
269
+ logger.error("Failed to extract ZIP file: %s", str(e))
216
270
  raise ProcessingError(f"Failed to extract ZIP file: {e}")
217
271
 
218
272
  @staticmethod
219
273
  def _get_measures(tables: List[Dict]) -> List[Dict]:
274
+ logger.debug("Extracting measures from tables")
220
275
  measures = []
221
276
  for table in tables:
222
277
  if "measures" in table:
@@ -227,37 +282,50 @@ class PowerBIProcessor:
227
282
  "Expression": " ".join(measure.get("expression", "")) if isinstance(measure.get("expression"), list) else measure.get("expression", ""),
228
283
  "FormatString": measure.get("formatString", "")
229
284
  })
285
+ logger.debug("Extracted %d measures", len(measures))
230
286
  return measures
231
287
 
232
288
  @staticmethod
233
289
  def _get_tables_and_columns(tables: List[Dict]) -> List[Dict]:
290
+ logger.debug("Extracting tables and columns")
234
291
  table_info = []
235
292
  for table in tables:
236
293
  columns = [{"Column Name": col["name"], "Data Type": col.get("dataType", "Unknown"), "Source Column": col.get("sourceColumn", "N/A"), "Calculated": col.get("type") == "calculated"} for col in table.get("columns", [])]
237
294
  expressions = [part["source"]["expression"] for part in table.get("partitions", []) if part["source"].get("expression")]
238
295
  table_info.append({"Table Name": table["name"], "Columns": columns, "Expressions": expressions})
296
+ logger.debug("Extracted %d tables", len(table_info))
239
297
  return table_info
240
298
 
241
299
  @staticmethod
242
300
  def _get_relationships(relationships: List[Dict]) -> List[Dict]:
243
- return [{"From Table": rel["fromTable"], "From Column": rel["fromColumn"], "To Table": rel["toTable"], "To Column": rel["toColumn"], "Join Behavior": rel.get("joinOnDateBehavior", "N/A")} for rel in relationships]
301
+ logger.debug("Extracting relationships")
302
+ result = [{"From Table": rel["fromTable"], "From Column": rel["fromColumn"], "To Table": rel["toTable"], "To Column": rel["toColumn"], "Join Behavior": rel.get("joinOnDateBehavior", "N/A")} for rel in relationships]
303
+ logger.debug("Extracted %d relationships", len(result))
304
+ return result
244
305
 
245
306
  @staticmethod
246
307
  def _cleanup(*paths: str):
308
+ logger.debug("Cleaning up paths: %s", paths)
247
309
  for path in paths:
248
310
  if os.path.exists(path):
249
311
  if os.path.isfile(path):
312
+ logger.debug("Removing file: %s", path)
250
313
  os.remove(path)
251
314
  else:
315
+ logger.debug("Removing directory: %s", path)
252
316
  shutil.rmtree(path, ignore_errors=True)
317
+ logger.debug("Cleanup completed")
253
318
 
254
319
  class PowerBIEvaluator:
255
320
  def __init__(self, api_key: str):
321
+ logger.info("Initializing PowerBIEvaluator")
256
322
  self.api_key = api_key
257
323
  self.model = GeminiFlashModel(api_key)
258
324
  self.processor = PowerBIProcessor()
325
+ logger.info("PowerBIEvaluator initialized successfully")
259
326
 
260
327
  def evaluate(self, questions: List[str], answer_path: str) -> Dict[str, any]:
328
+ logger.info("Starting evaluation for file: %s with %d questions", answer_path, len(questions))
261
329
  try:
262
330
  _, ext = os.path.splitext(answer_path)
263
331
  ext = ext.lower()
@@ -266,13 +334,16 @@ class PowerBIEvaluator:
266
334
  pdf_path = None
267
335
 
268
336
  # Handle input file type
337
+ logger.debug("Checking file extension: %s", ext)
269
338
  if ext == ".zip":
339
+ logger.info("Processing ZIP file")
270
340
  pbit_path, pdf_path = self.processor.extract_zip(answer_path, extract_path)
271
341
  elif ext == ".pbit":
342
+ logger.info("Processing PBIT file directly")
272
343
  pbit_path = answer_path
273
344
  pdf_path = None
274
345
  else:
275
- logger.error("Invalid file type for Power BI: %s", answer_path)
346
+ logger.error("Invalid file type: %s", ext)
276
347
  return {
277
348
  "score": 0,
278
349
  "feedback": f"Invalid file type: {ext}. Expected .pbit or .zip",
@@ -282,9 +353,12 @@ class PowerBIEvaluator:
282
353
 
283
354
  try:
284
355
  # Extract and process the data model from .pbit
356
+ logger.info("Extracting data model from PBIT")
285
357
  data_model = self.processor.extract_datamodel(pbit_path)
358
+ logger.info("Extracting model data")
286
359
  model_data = self.processor.extract_model_data(data_model)
287
360
  answers = [json.dumps(model_data)] * len(questions)
361
+ logger.info("Evaluating DAX with %d question-answer pairs", len(questions))
288
362
  dax_result = self.model.evaluate([{"question": q, "answer": a} for q, a in zip(questions, answers)])
289
363
 
290
364
  # Initialize result with DAX evaluation
@@ -294,24 +368,31 @@ class PowerBIEvaluator:
294
368
  "issues": dax_result["issues"],
295
369
  "recommendations": dax_result["recommendations"]
296
370
  }
371
+ logger.info("DAX evaluation completed: Score=%d", dax_result["score"])
297
372
 
298
373
  # Process PDF and evaluate visuals if present
299
374
  if pdf_path:
375
+ logger.info("Processing PDF for visual evaluation: %s", pdf_path)
300
376
  try:
301
- self.processor.process_pdf(pdf_path)
377
+ image_paths = self.processor.process_pdf(pdf_path)
378
+ logger.info("Evaluating visuals with question: %s", questions[0])
302
379
  visual_result = self.model.evaluate_visuals(questions[0], "outputimages")
303
380
  result["score"] = (dax_result["score"] + visual_result["score"]) // 2
304
381
  result["feedback"] += f"\n\nVisual Feedback:\n{visual_result['feedback']}"
305
382
  result["issues"].extend([f"Visual: {i}" for i in visual_result.get("issues", [])])
306
383
  result["recommendations"].extend(visual_result.get("recommendations", []))
384
+ logger.info("Visual evaluation completed: Score=%d", visual_result["score"])
307
385
  except ProcessingError as e:
308
386
  logger.warning("Failed to process PDF, proceeding with DAX evaluation only: %s", str(e))
309
387
  result["issues"].append(f"Visual evaluation skipped: {str(e)}")
310
388
  result["recommendations"].append("Ensure a valid PDF is provided for visual evaluation if intended")
389
+ else:
390
+ logger.info("No PDF provided, skipping visual evaluation")
311
391
 
392
+ logger.info("Evaluation completed successfully")
312
393
  return result
313
394
  finally:
314
- # Cleanup temporary files and directories
395
+ logger.debug("Cleaning up temporary files and directories")
315
396
  self.processor._cleanup(extract_path, "outputimages")
316
397
  except Exception as e:
317
398
  logger.exception("Failed to evaluate Power BI file %s: %s", answer_path, str(e))
@@ -0,0 +1,230 @@
1
+ def prompt_text_python(combined_content):
2
+ return (
3
+ "You are an expert Python instructor evaluating beginner Python code. "
4
+ "Focus on syntax, logic, code readability, and adherence to Python best practices (e.g., PEP 8).\n\n"
5
+ "Your evaluation should:\n"
6
+ "- Focus on clarity, correctness, and understanding of the Python content\n"
7
+ "- Be constructive and encouraging (students are beginners)\n"
8
+ "- Highlight both strengths and areas for improvement\n"
9
+ "- Identify major mistakes or misunderstandings (e.g., syntax errors, incorrect logic, missing components and conceptual part)\n"
10
+ "- Be concise but insightful\n\n"
11
+ "- If the student's answer is incomplete or too simplistic to fully address the question, "
12
+ "explain that the response lacks depth or coverage, but do not provide the missing or correct answer. "
13
+ "Encourage the student to research further or review the relevant concepts.\n"
14
+ "- If the student's submission is off-topic or unrelated to the question, "
15
+ "clearly state that the response does not address the question's requirements and "
16
+ "explain why it is irrelevant. Encourage the student to review the question carefully and "
17
+ "focus on the relevant Python concepts without providing the correct solution."
18
+ "Provide feedback in this format:\n\n"
19
+ "=== COMPREHENSIVE EVALUATION ===\n\n"
20
+ "OVERALL SCORE: /100\n\n"
21
+ "FEEDBACK SUMMARY:\n"
22
+ "- What was done well\n"
23
+ "- What needs improvement\n"
24
+ "- Any major issues (e.g., logic errors, misunderstanding, incomplete solutions)\n\n"
25
+ "KEY ADVICE:\n"
26
+ "- Top 2 or 3 suggestions to improve Python skills\n"
27
+ "- Highlight any concepts to revisit\n"
28
+ "- Encourage further learning and effort\n\n"
29
+ f"{combined_content}\n"
30
+ "=== EVALUATION COMPLETE ===\n\n"
31
+ "Notes:\n"
32
+ "- Be honest but supportive\n"
33
+ "- Include specific examples from the provided answers if helpful\n"
34
+ "- Keep language beginner-friendly\n"
35
+ "- Do not give too low marks. You may add from 5 up to 10 additional marks for effort or "
36
+ "partial relevance, ensuring the score does not exceed 100."
37
+ )
38
+
39
+
40
+ def prompt_text_sql(combined_content: str):
41
+ return (
42
+ "You are a SQL expert evaluating beginner SQL queries. "
43
+ "Focus on query correctness, efficiency, proper use of SQL syntax (e.g., SELECT, JOIN, WHERE), "
44
+ "and alignment with the question's requirements.\n\n"
45
+ "Your evaluation should:\n"
46
+ "- Focus on clarity, correctness, and understanding of the SQL content\n"
47
+ "- Be constructive and encouraging (students are beginners)\n"
48
+ "- Highlight both strengths and areas for improvement\n"
49
+ "- Identify major mistakes or misunderstandings (e.g., syntax errors, incorrect logic, missing components)\n"
50
+ "- Also assess whether the student’s answer demonstrates a proper understanding of the "
51
+ "SQL Server concepts being tested (e.g., joins, subqueries, indexing, optimization, "
52
+ "if required in homework's task), not just correct syntax.\n"
53
+ "- Be concise but insightful\n"
54
+ "- Look for correct use of SQL Server-specific features (e.g., Common Table Expressions, "
55
+ "Window Functions, transactions, if required in homework's task)\n"
56
+ "- If the student's answer is incomplete or too simplistic to fully address the question, "
57
+ "clearly state that it lacks sufficient detail or misses key components, but do not provide "
58
+ "the missing parts or solutions. Instead, suggest they revisit the relevant "
59
+ "concepts (e.g., joins, subqueries, indexing, if lacks) and encourage deeper exploration.\n"
60
+ "- If the student's submission is off-topic or unrelated to the question, "
61
+ "clearly state that the response does not address the question's requirements and "
62
+ "explain why it is irrelevant. Encourage the student to review the "
63
+ "question carefully and focus on the relevant SQL Server concepts without providing the correct solution."
64
+ "- Check for query optimization and adherence to the question's intent\n\n"
65
+ "Provide feedback in this format:\n\n"
66
+ "=== COMPREHENSIVE EVALUATION ===\n\n"
67
+ "OVERALL SCORE: <score>/100\n\n"
68
+ "FEEDBACK SUMMARY:\n"
69
+ "- What was done well\n"
70
+ "- What needs improvement\n"
71
+ "- Any major issues (e.g., logic errors, misunderstanding, incomplete solutions)\n\n"
72
+ "KEY ADVICE:\n"
73
+ "- Top 2 or 3 suggestions to improve SQL skills\n"
74
+ "- Highlight any concepts to revisit\n"
75
+ "- Encourage further learning and effort\n\n"
76
+ f"{combined_content}\n"
77
+ "=== EVALUATION COMPLETE ===\n\n"
78
+ "Notes:\n"
79
+ "- Be honest but supportive\n"
80
+ "- Include specific examples from the provided answers if helpful\n"
81
+ "- Keep language beginner-friendly\n"
82
+ "- Do not give too low marks. You may add from 5 up to 10 additional marks for "
83
+ "effort or partial relevance, ensuring the score does not exceed 100."
84
+ )
85
+
86
+ def prompt_text_ssis(combined_content):
87
+ return (
88
+ "You are a data engineer reviewing an SSIS package (.dtsx) summary. "
89
+ "Evaluate how well the package addresses the question, focusing on the correctness of tasks, "
90
+ "data flow, control flow, and configurations.\n\n"
91
+ "Your evaluation should:\n"
92
+ "- Assess how well the package addresses the question overall\n"
93
+ "- Focus on clarity, accuracy, and a basic understanding of key SSIS components "
94
+ "(e.g., Control Flow, Data Flow, Connection Managers)\n"
95
+ "- Be supportive and constructive — students are new to SSIS, so encourage learning and reward effort\n"
96
+ "- Highlight what was done well and gently suggest what could be improved\n"
97
+ "- Point out only major issues when necessary (e.g., missing essential components, "
98
+ "incorrect configurations, or clear misunderstandings)\n"
99
+ "- Keep feedback clear, concise, and insightful\n"
100
+ "- Also assess whether the student’s submission demonstrates a proper understanding of "
101
+ "SSIS concepts being tested (e.g., ETL processes, control flow sequencing, error handling), not just technical correctness\n"
102
+ "- Check for proper use of control flow tasks, data flow transformations, precedence constraints, "
103
+ "error handling (e.g., OnError events), and connection manager configurations\n"
104
+ "- If the student's submission is incomplete or too simplistic to fully address the question, "
105
+ "clearly state that it lacks sufficient detail or misses key components, "
106
+ "but do not provide the missing parts or solutions. Instead, suggest they revisit the relevant "
107
+ "SSIS concepts (e.g., control flow, data flow, error handling) and encourage deeper exploration\n"
108
+ "- If the student's submission is off-topic or unrelated to the question, "
109
+ "clearly state that the response does not address the question's requirements and "
110
+ "explain why it is irrelevant. Encourage the student to review the question carefully and "
111
+ "focus on the relevant SSIS concepts without providing the correct solution\n"
112
+ "- Understand that simple packages may only use one Data Flow Task, and that’s perfectly fine\n"
113
+ "- If scheduling (e.g., daily at 7 AM) is not included, just note it briefly — "
114
+ "it may be handled by SQL Server Agent and should not impact the score significantly (no more than 5–10 points)\n\n"
115
+ "When provided, check that:\n"
116
+ "- Data flow connections are properly linked\n"
117
+ "- Data types match the destination schema\n\n"
118
+ "Important Scoring Note:\n"
119
+ "Always give credit for effort, even if there are technical gaps. It’s better to nudge students forward "
120
+ "than to discourage them. Start from a generous baseline and avoid very low scores unless the submission "
121
+ "shows no attempt. Remember the student is not a pro programmer, so avoid low scores just because best "
122
+ "practices weren’t followed exactly. Score mainly based on what was asked. "
123
+ "Provide feedback in this format:\n\n"
124
+ "=== COMPREHENSIVE EVALUATION ===\n\n"
125
+ "OVERALL SCORE: <score>/100\n\n"
126
+ "FEEDBACK SUMMARY:\n"
127
+ "- What was done well\n"
128
+ "- What needs improvement\n"
129
+ "- Any major issues (e.g., logic errors, misunderstandings, incomplete solutions)\n\n"
130
+ "KEY ADVICE:\n"
131
+ "- Top 2-3 suggestions to improve SSIS skills\n"
132
+ "- Concepts to revisit\n"
133
+ "- Encouragement to keep learning and improving\n\n"
134
+ f"{combined_content}\n"
135
+ "=== EVALUATION COMPLETE ===\n\n"
136
+ "Notes:\n"
137
+ "- Be honest but supportive\n"
138
+ "- Include specific examples from the provided summary if helpful\n"
139
+ "- Keep language beginner-friendly\n"
140
+ "- Do not give too low marks. From 5 up to 10 additional marks for effort or partial relevance, ensuring the score does not exceed 100."
141
+ )
142
+
143
+ def prompt_text_powerbi(combined_content: str):
144
+ return (
145
+ "You are a BI professional evaluating Power BI report solutions, including DAX formulas, "
146
+ "data models, and visual design based on the given task.\n\n"
147
+ "Your evaluation should:\n"
148
+ "- Focus on clarity, correctness, and understanding of Power BI content (DAX, data models, visuals)\n"
149
+ "- Be constructive and encouraging (students are beginners)\n"
150
+ "- Highlight strengths and areas for improvement\n"
151
+ "- Identify major mistakes (e.g., incorrect DAX, poor data modeling, unclear visuals)\n"
152
+ "- Be concise but insightful\n"
153
+ "- Evaluate proper configuration of data model relationships, correctness and logic of DAX formulas, and "
154
+ "clarity of visuals (e.g., appropriate chart types, layout, readability, proper filtering)\n"
155
+ "- Also assess whether the student’s submission demonstrates a proper understanding of "
156
+ "Power BI concepts being tested (e.g., data modeling, DAX calculations, visualization principles), not just technical correctness\n"
157
+ "- If the student's submission is incomplete or too simplistic to fully address the question, "
158
+ "clearly state that it lacks sufficient detail or misses key components, but do not provide "
159
+ "the missing parts or solutions. Instead, suggest they revisit the relevant "
160
+ "Power BI concepts (e.g., data modeling, DAX, or visualization) and encourage deeper exploration\n"
161
+ "- If the student's submission is off-topic or unrelated to the question, "
162
+ "clearly state that the response does not address the question's requirements and "
163
+ "explain why it is irrelevant. Encourage the student to review the question carefully and "
164
+ "focus on the relevant Power BI concepts without providing the correct solution\n"
165
+ "- Do not penalize for advanced efficiency, data source paths, or separate measure tables\n"
166
+ "- Do not lower marks for redundant date tables or missing advanced design features\n\n"
167
+ "Provide feedback in this format:\n\n"
168
+ "=== COMPREHENSIVE EVALUATION ===\n\n"
169
+ "OVERALL SCORE: <score>/100\n\n"
170
+ "FEEDBACK SUMMARY:\n"
171
+ "- What was done well\n"
172
+ "- What needs improvement\n"
173
+ "- Any major issues (e.g., incorrect DAX, missing visuals, poor relationships)\n\n"
174
+ "KEY ADVICE:\n"
175
+ "- Top 2-3 suggestions to improve Power BI skills\n"
176
+ "- Highlight any concepts to revisit\n"
177
+ "- Encourage further learning and effort\n\n"
178
+
179
+ f"{combined_content}\n"
180
+ "=== EVALUATION COMPLETE ===\n\n"
181
+ "Notes:\n"
182
+ "- Be honest but supportive\n"
183
+ "- Include specific examples from the provided answers if helpful\n"
184
+ "- Keep language beginner-friendly\n"
185
+ "- Score submissions based on alignment with the question, effort, and technical correctness. "
186
+ "Off-topic or incomplete submissions should generally score low (e.g., 10-30/100), "
187
+ "but add from 5 up to 10 marks for effort or partial relevance, ensuring the score does not exceed 100."
188
+ )
189
+
190
+
191
+
192
+
193
+
194
+
195
+
196
+
197
+
198
+
199
+
200
+
201
+
202
+
203
+
204
+
205
+
206
+
207
+
208
+
209
+
210
+
211
+
212
+
213
+
214
+
215
+
216
+
217
+
218
+
219
+
220
+
221
+
222
+
223
+
224
+
225
+
226
+
227
+
228
+
229
+
230
+
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: QuantumChecker
3
- Version: 0.2.3
3
+ Version: 0.2.5
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.3",
5
+ version="0.2.5",
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.",
@@ -1,140 +0,0 @@
1
-
2
- def prompt_text_python(combined_content):
3
- return (
4
- "You are an expert Python instructor evaluating beginner Python code. "
5
- "Focus on syntax, logic, code readability, and adherence to Python best practices (e.g., PEP 8).\n\n"
6
- "IMPORTANT: First, check if the student's answer is relevant to Python. "
7
- "If it is clearly from a different subject (e.g., SQL, Power BI), assign a low score (10–25/100) "
8
- "and explain the mismatch supportively.\n\n"
9
- "Your evaluation should:\n"
10
- "- Focus on clarity, correctness, and understanding of the Python content\n"
11
- "- Be constructive and encouraging (students are beginners)\n"
12
- "- Highlight both strengths and areas for improvement\n"
13
- "- Identify major mistakes or misunderstandings (e.g., syntax errors, incorrect logic, missing components)\n"
14
- "- Be concise but insightful\n\n"
15
- "Provide feedback in this format:\n\n"
16
- "=== COMPREHENSIVE EVALUATION ===\n\n"
17
- "OVERALL SCORE: <score>/100\n\n"
18
- "FEEDBACK SUMMARY:\n"
19
- "- What was done well\n"
20
- "- What needs improvement\n"
21
- "- Any major issues (e.g., logic errors, misunderstanding, incomplete solutions)\n\n"
22
- "KEY ADVICE:\n"
23
- "- Top 2-3 suggestions to improve Python skills\n"
24
- "- Highlight any concepts to revisit\n"
25
- "- Encourage further learning and effort\n\n"
26
- f"{combined_content}\n"
27
- "=== EVALUATION COMPLETE ===\n\n"
28
- "Notes:\n"
29
- "- Be honest but supportive\n"
30
- "- Include specific examples from the provided answers if helpful\n"
31
- "- Keep language beginner-friendly\n"
32
- "- Do not give too low marks unless the answer is entirely unrelated."
33
- )
34
-
35
- def prompt_text_sql(combined_content):
36
- return (
37
- "You are a SQL expert evaluating beginner SQL queries. "
38
- "Focus on query correctness, efficiency, proper use of SQL syntax, and alignment with the question's requirements.\n\n"
39
- "IMPORTANT: First, check if the student's answer is relevant to SQL. "
40
- "If the answer is clearly about a different subject (e.g., Python or Power BI), assign a low score (10–25/100) "
41
- "and explain the mismatch in a supportive way.\n\n"
42
- "Your evaluation should:\n"
43
- "- Focus on clarity, correctness, and understanding of the SQL content\n"
44
- "- Be constructive and encouraging (students are beginners)\n"
45
- "- Highlight both strengths and areas for improvement\n"
46
- "- Identify major mistakes or misunderstandings\n"
47
- "- Be concise but insightful\n"
48
- "- Check for query optimization and adherence to the question's intent\n\n"
49
- "Provide feedback in this format:\n\n"
50
- "=== COMPREHENSIVE EVALUATION ===\n\n"
51
- "OVERALL SCORE: <score>/100\n\n"
52
- "FEEDBACK SUMMARY:\n"
53
- "- What was done well\n"
54
- "- What needs improvement\n"
55
- "- Any major issues (e.g., logic errors, misunderstanding, incomplete solutions)\n\n"
56
- "KEY ADVICE:\n"
57
- "- Top 2-3 suggestions to improve SQL skills\n"
58
- "- Highlight any concepts to revisit\n"
59
- "- Encourage further learning and effort\n\n"
60
- "FEEDBACK SUMMARY (in Uzbek):\n"
61
- "- Nima yaxshi bajarilgan\n"
62
- "- Nimalar ustida ishlash kerak\n"
63
- "- Jiddiy xatoliklar yoki noto‘g‘ri tushunchalar\n\n"
64
- f"{combined_content}\n"
65
- "=== EVALUATION COMPLETE ===\n\n"
66
- "Notes:\n"
67
- "- Be honest but supportive\n"
68
- "- Include specific examples from the provided answers if helpful\n"
69
- "- Keep language beginner-friendly\n"
70
- "- Do not give too low marks unless the subject is clearly unrelated."
71
- )
72
-
73
- def prompt_text_ssis(combined_content):
74
- return (
75
- "You are a data engineer reviewing an SSIS package (.dtsx) summary. "
76
- "Evaluate the correctness of tasks, data flow, control flow, and configurations.\n\n"
77
- "IMPORTANT: First, check if the answer is related to SSIS. "
78
- "If the answer is clearly unrelated (e.g., contains Python or SQL code), assign a low score (10–25/100) "
79
- "and explain the mismatch supportively.\n\n"
80
- "Your evaluation should:\n"
81
- "- Assess how well the package addresses the question\n"
82
- "- Focus on clarity, accuracy, and understanding of key SSIS components\n"
83
- "- Be supportive and constructive\n"
84
- "- Highlight what was done well and what could be improved\n"
85
- "- Point out only major issues if necessary\n"
86
- "- Keep feedback clear and insightful\n"
87
- "- Do not penalize for lack of advanced scheduling (e.g., SQL Agent use)\n\n"
88
- "Provide feedback in this format:\n\n"
89
- "=== COMPREHENSIVE EVALUATION ===\n\n"
90
- "OVERALL SCORE: <score>/100\n\n"
91
- "FEEDBACK SUMMARY:\n"
92
- "- What was done well\n"
93
- "- What needs improvement\n"
94
- "- Any major issues (e.g., logic errors, misunderstandings, incomplete solutions)\n\n"
95
- "KEY ADVICE:\n"
96
- "- Top 2-3 suggestions to improve SSIS skills\n"
97
- "- Concepts to revisit\n"
98
- "- Encouragement to keep learning and improving\n\n"
99
- f"{combined_content}\n"
100
- "=== EVALUATION COMPLETE ===\n\n"
101
- "Notes:\n"
102
- "- Be honest but supportive\n"
103
- "- Include specific examples if helpful\n"
104
- "- Keep language beginner-friendly\n"
105
- "- Give credit for effort, even if technically incorrect. Use low scores only if clearly unrelated."
106
- )
107
-
108
- def prompt_text_powerbi(combined_content):
109
- return (
110
- "You are a BI professional evaluating Power BI report solutions, including DAX formulas, "
111
- "data models, and visual design.\n\n"
112
- "IMPORTANT: First, check if the student's answer is related to Power BI. "
113
- "If it is clearly from a different domain (e.g., Python or SQL code), assign a low score (10–25/100) "
114
- "and clearly explain the mismatch.\n\n"
115
- "Your evaluation should:\n"
116
- "- Focus on clarity, correctness, and understanding of Power BI content\n"
117
- "- Be constructive and encouraging (students are beginners)\n"
118
- "- Highlight strengths and areas for improvement\n"
119
- "- Identify major mistakes (e.g., incorrect DAX, poor data modeling)\n"
120
- "- Be concise but insightful\n"
121
- "- Evaluate DAX, visuals, and data model structure\n"
122
- "- Avoid penalizing for advanced design features or best practices\n\n"
123
- "Provide feedback in this format:\n\n"
124
- "=== COMPREHENSIVE EVALUATION ===\n\n"
125
- "OVERALL SCORE: <score>/100\n\n"
126
- "FEEDBACK SUMMARY:\n"
127
- "- What was done well\n"
128
- "- What needs improvement\n"
129
- "- Any major issues (e.g., incorrect DAX, missing visuals, poor relationships)\n\n"
130
- "KEY ADVICE:\n"
131
- "- Top 2-3 suggestions to improve Power BI skills\n"
132
- "- Highlight any concepts to revisit\n"
133
- "- Encourage further learning and effort\n\n"
134
- f"{combined_content}\n"
135
- "=== EVALUATION COMPLETE ===\n\n"
136
- "Notes:\n"
137
- "- Be honest but supportive\n"
138
- "- If the subject is mismatched, clearly state that in feedback and give a low score (10–25/100)\n"
139
- "- Keep language beginner-friendly"
140
- )
File without changes
File without changes