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.
- {quantumchecker-0.2.3 → quantumchecker-0.2.5}/PKG-INFO +1 -1
- {quantumchecker-0.2.3 → quantumchecker-0.2.5}/QuantumCheck/powerbi_evaluator.py +92 -11
- quantumchecker-0.2.5/QuantumCheck/prompts.py +230 -0
- {quantumchecker-0.2.3 → quantumchecker-0.2.5}/QuantumChecker.egg-info/PKG-INFO +1 -1
- {quantumchecker-0.2.3 → quantumchecker-0.2.5}/setup.py +1 -1
- quantumchecker-0.2.3/QuantumCheck/prompts.py +0 -140
- {quantumchecker-0.2.3 → quantumchecker-0.2.5}/QuantumCheck/__init__.py +0 -0
- {quantumchecker-0.2.3 → quantumchecker-0.2.5}/QuantumCheck/main.py +0 -0
- {quantumchecker-0.2.3 → quantumchecker-0.2.5}/QuantumCheck/python_evaluator.py +0 -0
- {quantumchecker-0.2.3 → quantumchecker-0.2.5}/QuantumCheck/sql_evaluator.py +0 -0
- {quantumchecker-0.2.3 → quantumchecker-0.2.5}/QuantumCheck/ssis_evaluator.py +0 -0
- {quantumchecker-0.2.3 → quantumchecker-0.2.5}/QuantumChecker.egg-info/SOURCES.txt +0 -0
- {quantumchecker-0.2.3 → quantumchecker-0.2.5}/QuantumChecker.egg-info/dependency_links.txt +0 -0
- {quantumchecker-0.2.3 → quantumchecker-0.2.5}/QuantumChecker.egg-info/requires.txt +0 -0
- {quantumchecker-0.2.3 → quantumchecker-0.2.5}/QuantumChecker.egg-info/top_level.txt +0 -0
- {quantumchecker-0.2.3 → quantumchecker-0.2.5}/README.md +0 -0
- {quantumchecker-0.2.3 → quantumchecker-0.2.5}/setup.cfg +0 -0
- {quantumchecker-0.2.3 → quantumchecker-0.2.5}/tests/test.py +0 -0
|
@@ -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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
+
|
|
@@ -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.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
|
|
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
|