galbendavids commited on
Commit
02b73a5
·
1 Parent(s): 33968c0

כתיבה מחדש של _get_schema_info - הכרה מעמיקה של כל השדות עם משמעות עסקית ודוגמאות אמיתיות

Browse files
Files changed (1) hide show
  1. app/sql_service.py +175 -88
app/sql_service.py CHANGED
@@ -116,119 +116,206 @@ class SQLFeedbackService:
116
 
117
  def _get_schema_info(self) -> str:
118
  """
119
- Generate schema information string for the feedback table.
120
 
121
- This information is provided to the LLM when generating SQL queries
122
- to help it understand the data structure and available columns.
 
123
 
124
  Returns:
125
- A formatted string describing the table schema, column types,
126
- and basic statistics. Used in prompts for SQL query generation.
127
-
128
- Note:
129
- The function includes all available columns in the schema info.
130
  """
131
  if self.df is None:
132
  return "No data available"
133
 
134
- # Build compact schema info with all fields, examples, and usage
135
- all_columns_info = ""
136
-
137
- # Get sample values for each column
138
- def get_sample_values(col_name, n=3):
139
  try:
140
  samples = self.df[col_name].dropna().head(n).tolist()
141
  return [str(s) for s in samples]
142
  except:
143
  return []
144
 
145
- # ID
146
- samples = get_sample_values('ID', 2)
147
- all_columns_info += f"• ID (UUID): מזהה ייחודי. דוגמאות: {', '.join(samples[:2])}\n"
148
-
149
- # ServiceName
150
- samples = get_sample_values('ServiceName', 3)
151
- unique_services = self.df['ServiceName'].nunique() if 'ServiceName' in self.df.columns else 0
152
- all_columns_info += f"• ServiceName (טקסט): שם השירות. דוגמאות: {', '.join(samples[:2])}. יש {unique_services} שירותים ייחודיים\n"
 
153
 
154
- # Level
155
- all_columns_info += "• Level (מספר 1-5): דירוג שביעות רצון. 1=גרוע מאוד, 5=מעולה. דוגמאות: 1, 2, 3, 4, 5\n"
156
 
157
- # Text
158
- samples = get_sample_values('Text', 1)
159
- if samples:
160
- sample_text = samples[0][:50] + "..." if len(samples[0]) > 50 else samples[0]
161
- all_columns_info += f"• Text (טקסט ארוך): תוכן המשוב. דוגמה: '{sample_text}'\n"
162
- else:
163
- all_columns_info += "• Text (טקסט ארוך): תוכן המשוב - ביקורות, תלונות, מחמאות\n"
164
-
165
- # ReferenceNumber
166
- if 'ReferenceNumber' in self.df.columns:
167
- samples = get_sample_values('ReferenceNumber', 3)
168
- all_columns_info += f"• ReferenceNumber (מספר, יכול להיות NULL): מספר הפניה. דוגמאות: {', '.join([str(s) for s in samples[:2]])}\n"
169
-
170
- # RequestID
171
- if 'RequestID' in self.df.columns:
172
- samples = get_sample_values('RequestID', 2)
173
- all_columns_info += f"• RequestID (UUID, יכול להיות NULL): מזהה בקשה. דוגמאות: {samples[0][:20]}...\n"
174
-
175
- # ProcessID
176
- if 'ProcessID' in self.df.columns:
177
- all_columns_info += "• ProcessID (UUID, יכול להיות NULL): מזהה תהליך\n"
178
-
179
- # Date/Time fields - check what actually exists in the dataframe
180
- date_time_cols = []
181
- for col in ['Year', 'year', 'שנה', 'Month', 'month', 'חודש', 'DayOfWeek', 'day_of_week', 'יום_בשבוע',
182
- 'Hour', 'hour', 'שעה', 'DayNight', 'day_night', 'יום_לילה', 'DayOfMonth', 'day_of_month', 'יום_בחודש']:
183
- if col in self.df.columns:
184
- date_time_cols.append(col)
185
-
186
- if date_time_cols:
187
- all_columns_info += "\nשדות תאריך/זמן מחושבים (מוכנים לשימוש - השתמש בהם לשאילתות זמן!):\n"
188
 
189
- # Year
190
- for year_col in ['Year', 'year', 'שנה']:
191
- if year_col in self.df.columns:
192
- try:
193
- year_min = int(self.df[year_col].min())
194
- year_max = int(self.df[year_col].max())
195
- all_columns_info += f"• {year_col} (מספר): שנה. טווח: {year_min}-{year_max}. דוגמאות: {year_min}, {year_max}\n"
196
- except:
197
- all_columns_info += f" {year_col} (מספר): שנה. דוגמאות: 2020, 2021\n"
198
- break
199
 
200
- # Month
201
- for month_col in ['Month', 'month', 'חודש']:
202
- if month_col in self.df.columns:
203
- all_columns_info += f"• {month_col} (מספר 1-12): חודש. 1=ינואר, 12=דצמבר. דוגמאות: 1, 6, 12\n"
204
- break
 
 
 
 
205
 
206
- # DayOfWeek
207
- for dow_col in ['DayOfWeek', 'day_of_week', 'יום_בשבוע']:
208
- if dow_col in self.df.columns:
209
- samples = get_sample_values(dow_col, 3)
210
- all_columns_info += f"• {dow_col} (טקסט): יום בשבוע. ערכים: Monday-Sunday. דוגמאות: {', '.join(samples[:3])}\n"
211
- break
 
 
 
 
 
212
 
213
- # Hour
214
- for hour_col in ['Hour', 'hour', 'שעה']:
215
- if hour_col in self.df.columns:
216
- all_columns_info += f"• {hour_col} (מספר 0-23): שעה ביום. 0=חצות, 12=צהריים, 23=23:00. דוגמאות: 0, 9, 14, 18, 23\n"
217
- break
 
 
 
218
 
219
- # DayNight
220
- for dn_col in ['DayNight', 'day_night', 'יום_לילה']:
221
- if dn_col in self.df.columns:
222
- samples = get_sample_values(dn_col, 2)
223
- all_columns_info += f" {dn_col} (טקסט): 'יום' או 'לילה'. יום=6:00-18:00, לילה=18:00-6:00. דוגמאות: {', '.join(samples[:2])}\n"
224
- break
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
225
 
226
- schema_info = f"""שם הטבלה: Feedback_transformed (עם אות גדולה F)
 
 
 
 
 
 
 
 
 
 
227
 
228
  שדות בטבלה:
229
  {all_columns_info}
230
 
231
- סטטיסטיקות: {len(self.df)} משובים, {self.df['ServiceName'].nunique()} שירותים, דירוג ממוצע: {self.df['Level'].mean():.2f}
232
  """
233
  return schema_info
234
 
 
116
 
117
  def _get_schema_info(self) -> str:
118
  """
119
+ Generate comprehensive schema information for the feedback table.
120
 
121
+ This function analyzes the actual CSV file structure and provides
122
+ detailed information about each field including business meaning,
123
+ data types, examples, and usage patterns.
124
 
125
  Returns:
126
+ A detailed formatted string describing the table schema with
127
+ business context, examples, and statistics.
 
 
 
128
  """
129
  if self.df is None:
130
  return "No data available"
131
 
132
+ # Helper function to get sample values
133
+ def get_sample_values(col_name, n=5):
 
 
 
134
  try:
135
  samples = self.df[col_name].dropna().head(n).tolist()
136
  return [str(s) for s in samples]
137
  except:
138
  return []
139
 
140
+ # Helper function to get unique values if not too many
141
+ def get_unique_values(col_name, max_show=10):
142
+ try:
143
+ unique_vals = self.df[col_name].dropna().unique().tolist()
144
+ if len(unique_vals) <= max_show:
145
+ return unique_vals
146
+ return unique_vals[:max_show]
147
+ except:
148
+ return []
149
 
150
+ all_columns_info = ""
 
151
 
152
+ # Analyze each column that exists in the dataframe
153
+ for col in self.df.columns:
154
+ col_info = ""
155
+
156
+ # Get column statistics
157
+ dtype = str(self.df[col].dtype)
158
+ non_null_count = self.df[col].notna().sum()
159
+ null_count = self.df[col].isna().sum()
160
+ samples = get_sample_values(col, 3)
161
+
162
+ # ID field
163
+ if col == 'ID':
164
+ col_info = f"• {col} (UUID/טקסט): מזהה ייחודי גלובלי של כל משוב\n"
165
+ col_info += f" - משמעות עסקית: מזהה ייחודי לכל משוב במערכת, מאפשר מעקב, קישור בין משובים, ומניעת כפילויות\n"
166
+ col_info += f" - דוגמאות: {', '.join(samples[:2])}\n"
167
+ col_info += f" - שימוש בשאילתות: WHERE ID = '...', COUNT(DISTINCT ID), GROUP BY ID\n"
168
+
169
+ # ServiceName field
170
+ elif col == 'ServiceName':
171
+ unique_services = self.df[col].nunique()
172
+ unique_samples = get_unique_values(col, 5)
173
+ col_info = f"• {col} (טקסט): שם השירות הדיגיטלי הממשלתי\n"
174
+ col_info += f" - משמעות עסקית: מזהה את השירות שעליו ניתן המשוב. מאפשר ניתוח לפי שירות, השוואה בין שירותים, זיהוי שירותים בעייתיים או מצטיינים\n"
175
+ col_info += f" - יש {unique_services} שירותים ייחודיים במערכת\n"
176
+ col_info += f" - דוגמאות: {', '.join(unique_samples[:3])}\n"
177
+ col_info += f" - שימוש בשאילתות: WHERE ServiceName = '...', GROUP BY ServiceName, COUNT(*) GROUP BY ServiceName\n"
 
 
 
 
 
178
 
179
+ # Level field
180
+ elif col == 'Level':
181
+ level_dist = self.df[col].value_counts().sort_index().to_dict()
182
+ avg_level = self.df[col].mean()
183
+ col_info = f"• {col} (מספר שלם 1-5): דירוג שביעות רצון המשתמש מהשירות\n"
184
+ col_info += f" - משמעות עסקית: מדד שביעות רצון. 1=גרוע מאוד, 2=גרוע, 3=בינוני, 4=טוב, 5=מעולה. מאפשר מדידת שביעות רצון, זיהוי בעיות, ומעקב אחר שיפורים\n"
185
+ col_info += f" - דירוג ממוצע: {avg_level:.2f}\n"
186
+ col_info += f" - חלוקה: {level_dist}\n"
187
+ col_info += f" - שימוש בשאילתות: WHERE Level >= 4 (משובים חיוביים), WHERE Level <= 2 (משובים שליליים), AVG(Level), GROUP BY Level\n"
 
188
 
189
+ # Text field
190
+ elif col == 'Text':
191
+ sample_text = samples[0][:80] + "..." if samples and len(samples[0]) > 80 else (samples[0] if samples else "")
192
+ avg_length = self.df[col].str.len().mean() if self.df[col].dtype == 'object' else 0
193
+ col_info = f"• {col} (טקסט ארוך): התוכן החופשי של המשוב מהמשתמש\n"
194
+ col_info += f" - משמעות עסקית: ביקורות, הצעות לשיפור, תלונות, מחמאות. מאפשר ניתוח איכותי, זיהוי נושאים חוזרים, וקבלת תובנות עסקיות\n"
195
+ col_info += f" - אורך ממוצע: {avg_length:.0f} תווים\n"
196
+ col_info += f" - דוגמה: '{sample_text}'\n"
197
+ col_info += f" - שימוש בשאילתות: WHERE Text LIKE '%מילה%', WHERE Text LIKE '%בעיה%', LENGTH(Text), COUNT(*) WHERE Text IS NOT NULL\n"
198
 
199
+ # ReferenceNumber field
200
+ elif col == 'ReferenceNumber':
201
+ if non_null_count > 0:
202
+ ref_min = int(self.df[col].min())
203
+ ref_max = int(self.df[col].max())
204
+ col_info = f"• {col} (מספר שלם): מספר הפניה פנימי של המשוב\n"
205
+ col_info += f" - משמעות עסקית: מספר הפניה במערכת. מאפשר קישור למסמכים או בקשות קשורות, מעקב אחר תהליכים, וניהול בקשות\n"
206
+ col_info += f" - טווח: {ref_min} - {ref_max}\n"
207
+ col_info += f" - דוגמאות: {', '.join([str(s) for s in samples[:2]])}\n"
208
+ col_info += f" - NULL: {null_count} רשומות ({null_count/len(self.df)*100:.1f}%)\n"
209
+ col_info += f" - שימוש בשאילתות: WHERE ReferenceNumber = 6928, WHERE ReferenceNumber IS NOT NULL\n"
210
 
211
+ # RequestID field
212
+ elif col == 'RequestID':
213
+ if non_null_count > 0:
214
+ col_info = f"• {col} (UUID/טקסט): מזהה ייחודי של הבקשה המקורית שקשורה למשוב\n"
215
+ col_info += f" - משמעות עסקית: מאפשר קישור בין בקשות למשובים, מעקב אחר תהליכים, וניתוח הקשר בין בקשה למשוב\n"
216
+ col_info += f" - דוגמאות: {samples[0][:30]}...\n"
217
+ col_info += f" - NULL: {null_count} רשומות ({null_count/len(self.df)*100:.1f}%)\n"
218
+ col_info += f" - שימוש בשאילתות: WHERE RequestID = '...', COUNT(DISTINCT RequestID)\n"
219
 
220
+ # ProcessID field
221
+ elif col == 'ProcessID':
222
+ col_info = f"• {col} (UUID/טקסט): מזהה ייחודי של התהליך העסקי שקשור למשוב\n"
223
+ col_info += f" - משמעות עסקית: מאפשר ניתוח לפי תהליכים, זיהוי תהליכים בעייתיים, ומעקב אחר ביצועים\n"
224
+ col_info += f" - NULL: {null_count} רשומות ({null_count/len(self.df)*100:.1f}%)\n"
225
+ col_info += f" - שימוש בשאילתות: WHERE ProcessID = '...', COUNT(DISTINCT ProcessID)\n"
226
+
227
+ # Year field
228
+ elif col == 'Year':
229
+ year_min = int(self.df[col].min())
230
+ year_max = int(self.df[col].max())
231
+ year_dist = self.df[col].value_counts().sort_index().to_dict()
232
+ col_info = f"• {col} (מספר שלם): שנה שבה ניתן המשוב\n"
233
+ col_info += f" - משמעות עסקית: מאפשר ניתוח מגמות לאורך שנים, השוואה בין שנים, זיהוי שיפורים או הידרדרות, ותכנון אסטרטגי\n"
234
+ col_info += f" - טווח: {year_min} - {year_max}\n"
235
+ col_info += f" - חלוקה: {year_dist}\n"
236
+ col_info += f" - שימוש בשאילתות: WHERE Year = 2020, GROUP BY Year, SELECT Year, COUNT(*) GROUP BY Year\n"
237
+
238
+ # Month field
239
+ elif col == 'Month':
240
+ month_min = int(self.df[col].min())
241
+ month_max = int(self.df[col].max())
242
+ month_names = {1: 'ינואר', 2: 'פברואר', 3: 'מרץ', 4: 'אפריל', 5: 'מאי', 6: 'יוני',
243
+ 7: 'יולי', 8: 'אוגוסט', 9: 'ספטמבר', 10: 'אוקטובר', 11: 'נובמבר', 12: 'דצמבר'}
244
+ col_info = f"• {col} (מספר שלם 1-12): חודש בשנה שבו ניתן המשוב\n"
245
+ col_info += f" - משמעות עסקית: מאפשר ניתוח עונתי, זיהוי חודשים בעייתיים או מצטיינים, תכנון משאבים לפי עונות\n"
246
+ col_info += f" - טווח: {month_min} - {month_max} ({month_names.get(month_min, '')} - {month_names.get(month_max, '')})\n"
247
+ col_info += f" - דוגמאות: {', '.join([str(s) for s in samples[:3]])}\n"
248
+ col_info += f" - שימוש בשאילתות: WHERE Month = 1, GROUP BY Month, SELECT Month, COUNT(*) GROUP BY Month ORDER BY Month\n"
249
+
250
+ # DayInMonth field
251
+ elif col == 'DayInMonth':
252
+ day_min = int(self.df[col].min())
253
+ day_max = int(self.df[col].max())
254
+ col_info = f"• {col} (מספר שלם 1-31): יום בחודש שבו ניתן המשוב\n"
255
+ col_info += f" - משמעות עסקית: מאפשר ניתוח לפי ימים בחודש, זיהוי ימים בעייתיים (למשל סוף חודש), וניתוח דפוסים יומיים\n"
256
+ col_info += f" - טווח: {day_min} - {day_max}\n"
257
+ col_info += f" - דוגמאות: {', '.join([str(s) for s in samples[:3]])}\n"
258
+ col_info += f" - שימוש בשאילתות: WHERE DayInMonth = 1, GROUP BY DayInMonth, SELECT DayInMonth, COUNT(*) GROUP BY DayInMonth\n"
259
+
260
+ # DayOfWeek field
261
+ elif col == 'DayOfWeek':
262
+ unique_days = get_unique_values(col, 10)
263
+ day_names_he = {'Monday': 'שני', 'Tuesday': 'שלישי', 'Wednesday': 'רביעי', 'Thursday': 'חמישי',
264
+ 'Friday': 'שישי', 'Saturday': 'שבת', 'Sunday': 'ראשון'}
265
+ col_info = f"• {col} (טקסט): יום בשבוע שבו ניתן המשוב (באנגלית)\n"
266
+ col_info += f" - משמעות עסקית: מאפשר ניתוח לפי ימי השבוע, זיהוי ימים בעייתיים, תכנון כוח אדם, וזיהוי דפוסים שבועיים\n"
267
+ col_info += f" - ערכים אפשריים: Monday, Tuesday, Wednesday, Thursday, Friday, Saturday, Sunday\n"
268
+ col_info += f" - דוגמאות: {', '.join(unique_days[:3])}\n"
269
+ col_info += f" - שימוש בשאילתות: WHERE DayOfWeek = 'Monday', GROUP BY DayOfWeek, SELECT DayOfWeek, COUNT(*) GROUP BY DayOfWeek\n"
270
+
271
+ # Hour field
272
+ elif col == 'Hour':
273
+ hour_min = int(self.df[col].min())
274
+ hour_max = int(self.df[col].max())
275
+ col_info = f"• {col} (מספר שלם 0-23): שעה ביום שבה ניתן המשוב\n"
276
+ col_info += f" - משמעות עסקית: מאפשר ניתוח לפי שעות היום, זיהוי שעות שיא, תכנון זמינות שירות, וזיהוי דפוסים יומיים\n"
277
+ col_info += f" - טווח: {hour_min} - {hour_max} (0=חצות, 12=צהריים, 23=23:00)\n"
278
+ col_info += f" - דוגמאות: {', '.join([str(s) for s in samples[:3]])}\n"
279
+ col_info += f" - שימוש בשאילתות: WHERE Hour >= 9 AND Hour <= 17 (שעות עבודה), GROUP BY Hour, SELECT Hour, COUNT(*) GROUP BY Hour ORDER BY Hour\n"
280
+
281
+ # DayOrNight field
282
+ elif col == 'DayOrNight':
283
+ unique_values = get_unique_values(col, 5)
284
+ col_info = f"• {col} (טקסט): האם המשוב ניתן בשעות היום או הלילה\n"
285
+ col_info += f" - משמעות עסקית: מאפשר ניתוח לפי שעות פעילות, זיהוי הבדלים בין יום ללילה, תכנון זמינות שירות\n"
286
+ col_info += f" - ערכים אפשריים: 'יום' או 'לילה' (יום=6:00-18:00, לילה=18:00-6:00)\n"
287
+ col_info += f" - דוגמאות: {', '.join(unique_values)}\n"
288
+ col_info += f" - שימוש בשאילתות: WHERE DayOrNight = 'יום', GROUP BY DayOrNight, SELECT DayOrNight, COUNT(*) GROUP BY DayOrNight\n"
289
+
290
+ # Default for any other columns
291
+ else:
292
+ if dtype in ['int64', 'float64']:
293
+ val_min = self.df[col].min()
294
+ val_max = self.df[col].max()
295
+ col_info = f"• {col} ({dtype}): מספר. טווח: {val_min} - {val_max}\n"
296
+ else:
297
+ unique_count = self.df[col].nunique()
298
+ col_info = f"• {col} ({dtype}): טקסט. {unique_count} ערכים ייחודיים\n"
299
+ col_info += f" - דוגמאות: {', '.join(samples[:2])}\n"
300
+
301
+ all_columns_info += col_info + "\n"
302
 
303
+ # Build final schema info
304
+ total_records = len(self.df)
305
+ unique_services = self.df['ServiceName'].nunique() if 'ServiceName' in self.df.columns else 0
306
+ avg_level = self.df['Level'].mean() if 'Level' in self.df.columns else 0
307
+
308
+ schema_info = f"""שם הטבלה: Feedback_transformed (עם אות גדולה F - חובה!)
309
+
310
+ סטטיסטיקות כלליות:
311
+ - סך הכל משובים: {total_records}
312
+ - מספר שירותים ייחודיים: {unique_services}
313
+ - דירוג ממוצע: {avg_level:.2f}
314
 
315
  שדות בטבלה:
316
  {all_columns_info}
317
 
318
+ ⚠️ חשוב: כל שאילתה חייבת להתחיל ב-SELECT ולהשתמש ב-FROM Feedback_transformed!
319
  """
320
  return schema_info
321