Rakshitjan commited on
Commit
9efdf04
·
verified ·
1 Parent(s): 9c26c21

Update main.py

Browse files
Files changed (1) hide show
  1. main.py +327 -176
main.py CHANGED
@@ -188,199 +188,350 @@
188
 
189
 
190
  # Import necessary libraries
191
- from fastapi import FastAPI, HTTPException, Query
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
192
  from pydantic import BaseModel
193
  import gspread
194
  from google.oauth2.service_account import Credentials
195
  import pandas as pd
196
- from collections import defaultdict
197
- import os
198
- from fastapi.middleware.cors import CORSMiddleware
199
- # Initialize the FastAPI app
200
  app = FastAPI()
201
- app.add_middleware(
202
- CORSMiddleware,
203
- allow_origins=["*"], # You can specify domains instead of "*" to restrict access
204
- allow_credentials=True,
205
- allow_methods=["*"], # Allows all HTTP methods (POST, GET, OPTIONS, etc.)
206
- allow_headers=["*"], # Allows all headers
207
- )
208
- # Step 1: Define a function to get Google Sheets API credentials
209
  def get_credentials():
210
- """Get Google Sheets API credentials from environment variables."""
211
  try:
212
- # Construct the service account info dictionary
213
  service_account_info = {
214
- "type": os.getenv("SERVICE_ACCOUNT_TYPE"),
215
- "project_id": os.getenv("PROJECT_ID"),
216
- "private_key_id": os.getenv("PRIVATE_KEY_ID"),
217
- "private_key": os.getenv("PRIVATE_KEY").replace('\\n', '\n'),
218
- "client_email": os.getenv("CLIENT_EMAIL"),
219
- "client_id": os.getenv("CLIENT_ID"),
220
- "auth_uri": os.getenv("AUTH_URI"),
221
- "token_uri": os.getenv("TOKEN_URI"),
222
- "auth_provider_x509_cert_url": os.getenv("AUTH_PROVIDER_X509_CERT_URL"),
223
- "client_x509_cert_url": os.getenv("CLIENT_X509_CERT_URL"),
224
- "universe_domain": os.getenv("UNIVERSE_DOMAIN")
225
  }
226
  scope = ['https://spreadsheets.google.com/feeds', 'https://www.googleapis.com/auth/drive']
227
  creds = Credentials.from_service_account_info(service_account_info, scopes=scope)
228
  return creds
229
-
230
  except Exception as e:
231
  print(f"Error getting credentials: {e}")
232
  return None
233
 
234
- # Step 2: Authorize gspread using the credentials
235
- creds = get_credentials()
236
- client = gspread.authorize(creds)
237
-
238
- # Function to get file paths based on coaching code
239
- def get_file_paths(coaching_code):
240
- if coaching_code == '1919':
241
- return {
242
- 'journal': 'https://docs.google.com/spreadsheets/d/1EFf2lr4A10nt4RhIqxCD_fxe-l3sXH09II0TEkMmvhA/edit?usp=drive_link',
243
- 'panic_button': 'https://docs.google.com/spreadsheets/d/1nFZGkCvRV6qS-mhsORhX3dxI0JSge32_UwWgWKl3eyw/edit?usp=drive_link',
244
- 'test': 'https://docs.google.com/spreadsheets/d/13PUHySUXWtKBusjugoe7Dbsm39PwBUfG4tGLipspIx4/edit?usp=drive_link'
245
- }
246
- if coaching_code == '0946':
247
- return {
248
- 'journal': 'https://docs.google.com/spreadsheets/d/1c1TkL7sOUvFn6UPz3gwp135UVjOou9u1weohWzpmx6I/edit?usp=drive_link',
249
- 'panic_button': 'https://docs.google.com/spreadsheets/d/1RhbPQnNNBUthKKJyoW4q6x3uaWl1YSqmsFlfJ2THphE/edit?usp=drive_link',
250
- 'test': 'https://docs.google.com/spreadsheets/d/1JO5wDkfl2fr2ZQenI8OEu48jkWm48veYN1Fsw5Ctkzw/edit?usp=drive_link'
251
- }
252
- # Panic button weightages
253
- academic_weights = {'BACKLOGS': -5, 'MISSED CLASSES': -4, 'NOT UNDERSTANDING': -3, 'BAD MARKS': -3, 'LACK OF MOTIVATION': -3}
254
- non_academic_weights = {'EMOTIONAL FACTORS': -3, 'PROCRASTINATE': -2, 'LOST INTEREST': -4, 'LACK OF FOCUS': -2, 'GOALS NOT ACHIEVED': -2, 'LACK OF DISCIPLINE': -2}
255
-
256
- # Max weighted panic score
257
- max_weighted_panic_score = sum([max(academic_weights.values()) * 3, max(non_academic_weights.values()) * 3])
258
-
259
- # Function to calculate potential score
260
- def calculate_potential_score(row):
261
- # Test score normalization (70% weightage)
262
- if row['test_scores']: # Check if test_scores is not empty
263
- avg_test_score = sum(row['test_scores'].values()) / len(row['test_scores'])
264
- test_score_normalized = (avg_test_score / 40) * 70 # Scale test score to 70
265
- else:
266
- test_score_normalized = 0 # Default value for users with no test scores
267
-
268
- # Panic score calculation (20% weightage)
269
- student_panic_score = 0
270
- if row['panic_button']: # Ensure panic_button is not NaN or empty
271
- for factor, count in row['panic_button'].items():
272
- if factor in academic_weights:
273
- student_panic_score += academic_weights[factor] * count
274
- elif factor in non_academic_weights:
275
- student_panic_score += non_academic_weights[factor] * count
276
- else:
277
- student_panic_score = 0 # Default if no panic button issues
278
-
279
- # Panic score normalized to 20
280
- panic_score = 20 * (1 - (student_panic_score / max_weighted_panic_score) if max_weighted_panic_score != 0 else 1)
281
 
282
- # Journal score calculation (10% weightage)
283
- if pd.notna(row['productivity_yes_no']) and row['productivity_yes_no'] == 'Yes':
284
- if pd.notna(row['productivity_rate']):
285
- journal_score = (float(row['productivity_rate']) / 10) * 10 # Scale journal score to 10
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
286
  else:
287
- journal_score = 0 # Default if productivity_rate is missing
288
- elif pd.notna(row['productivity_yes_no']) and row['productivity_yes_no'] == 'No':
289
- if pd.notna(row['productivity_rate']):
290
- journal_score = (float(row['productivity_rate']) / 10) * 5 # Scale journal score to 5 if "No"
 
 
 
 
291
  else:
292
- journal_score = 0 # Default if productivity_rate is missing
293
- else:
294
- journal_score = 0 # Default if productivity_yes_no is missing
295
-
296
- # Total score based on new weightages
297
- total_potential_score = test_score_normalized + panic_score + journal_score
298
- return total_potential_score
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
299
 
300
- # Step 11: Define API endpoint to get the sorted potential scores
301
- @app.get("/sorted-potential-scores")
302
- async def get_sorted_potential_scores(coaching_code: str = Query(..., description="Coaching code to determine file paths")):
303
- try:
304
- file_paths = get_file_paths(coaching_code)
305
- if not file_paths:
306
- raise HTTPException(status_code=400, detail="Invalid coaching code")
307
- print("A");
308
- # Open Google Sheets using the URLs
309
- journal_file = client.open_by_url(file_paths['journal']).worksheet('Sheet1')
310
- panic_button_file = client.open_by_url(file_paths['panic_button']).worksheet('Sheet1')
311
- test_file = client.open_by_url(file_paths['test']).worksheet('Sheet1')
312
- print("B");
313
- # Convert the sheets into Pandas DataFrames
314
- journal_df = pd.DataFrame(journal_file.get_all_values())
315
- panic_button_df = pd.DataFrame(panic_button_file.get_all_values())
316
- test_df = pd.DataFrame(test_file.get_all_values())
317
- print("C");
318
- # Label the columns manually since there are no headers
319
- journal_df.columns = ['user_id', 'productivity_yes_no', 'productivity_rate']
320
- panic_button_df.columns = ['user_id', 'panic_button']
321
- print("D")
322
- # Initialize a list for the merged data
323
- merged_data = []
324
-
325
- # Group panic buttons by user_id and combine into a single comma-separated string
326
- panic_button_grouped = panic_button_df.groupby('user_id')['panic_button'].apply(lambda x: ','.join(x)).reset_index()
327
- print("E")
328
- # Merge journal and panic button data
329
- merged_journal_panic = pd.merge(journal_df, panic_button_grouped, on='user_id', how='outer')
330
- print("F")
331
- # Process the test data
332
- test_data = []
333
- for index, row in test_df.iterrows():
334
- user_id = row[0]
335
- i = 1
336
- while i < len(row) and pd.notna(row[i]): # Process chapter and score pairs
337
- chapter = row[i].lower().strip()
338
- score = row[i + 1]
339
- if pd.notna(score):
340
- test_data.append({'user_id': user_id, 'test_chapter': chapter, 'test_score': score})
341
- i += 2
342
- print("G")
343
- # Convert the processed test data into a DataFrame
344
- test_df_processed = pd.DataFrame(test_data)
345
- print("H")
346
- # Merge the journal+panic button data with the test data
347
- merged_data = pd.merge(merged_journal_panic, test_df_processed, on='user_id', how='outer')
348
- print("I")
349
- # Drop rows where all data (except user_id and test_chapter) is missing
350
- merged_data_cleaned = merged_data.dropna(subset=['productivity_yes_no', 'productivity_rate', 'panic_button', 'test_chapter'], how='all')
351
- print("J")
352
- # Group the merged DataFrame by user_id
353
- df = pd.DataFrame(merged_data_cleaned)
354
- print("K")
355
- # Function to process panic button counts and test scores
356
- def process_group(group):
357
- # Panic button counts
358
- panic_button_series = group['panic_button'].dropna()
359
- panic_button_dict = panic_button_series.value_counts().to_dict()
360
-
361
- # Test scores aggregation
362
- test_scores = group[['test_chapter', 'test_score']].dropna()
363
- test_scores['test_score'] = pd.to_numeric(test_scores['test_score'], errors='coerce')
364
-
365
- # Create the test_scores_dict excluding NaN values
366
- test_scores_dict = test_scores.groupby('test_chapter')['test_score'].mean().dropna().to_dict()
367
-
368
- return pd.Series({
369
- 'productivity_yes_no': group['productivity_yes_no'].iloc[0],
370
- 'productivity_rate': group['productivity_rate'].iloc[0],
371
- 'panic_button': panic_button_dict,
372
- 'test_scores': test_scores_dict
373
- })
374
-
375
- # Apply the group processing function
376
- merged_df = df.groupby('user_id').apply(process_group).reset_index()
377
- print("L")
378
- # Calculate potential scores and sort
379
- merged_df['potential_score'] = merged_df.apply(calculate_potential_score, axis=1)
380
- merged_df['potential_score'] = merged_df['potential_score'].round(2)
381
- sorted_df = merged_df[['user_id', 'potential_score']].sort_values(by='potential_score', ascending=False)
382
- print("M")
383
- result = sorted_df.to_dict(orient="records")
384
- return {"sorted_scores": result}
385
- except Exception as e:
386
- raise HTTPException(status_code=500, detail=str(e))
 
188
 
189
 
190
  # Import necessary libraries
191
+ # from fastapi import FastAPI, HTTPException, Query
192
+ # from pydantic import BaseModel
193
+ # import gspread
194
+ # from google.oauth2.service_account import Credentials
195
+ # import pandas as pd
196
+ # from collections import defaultdict
197
+ # import os
198
+ # from fastapi.middleware.cors import CORSMiddleware
199
+ # # Initialize the FastAPI app
200
+ # app = FastAPI()
201
+ # app.add_middleware(
202
+ # CORSMiddleware,
203
+ # allow_origins=["*"], # You can specify domains instead of "*" to restrict access
204
+ # allow_credentials=True,
205
+ # allow_methods=["*"], # Allows all HTTP methods (POST, GET, OPTIONS, etc.)
206
+ # allow_headers=["*"], # Allows all headers
207
+ # )
208
+ # # Step 1: Define a function to get Google Sheets API credentials
209
+ # def get_credentials():
210
+ # """Get Google Sheets API credentials from environment variables."""
211
+ # try:
212
+ # # Construct the service account info dictionary
213
+ # service_account_info = {
214
+ # "type": os.getenv("SERVICE_ACCOUNT_TYPE"),
215
+ # "project_id": os.getenv("PROJECT_ID"),
216
+ # "private_key_id": os.getenv("PRIVATE_KEY_ID"),
217
+ # "private_key": os.getenv("PRIVATE_KEY").replace('\\n', '\n'),
218
+ # "client_email": os.getenv("CLIENT_EMAIL"),
219
+ # "client_id": os.getenv("CLIENT_ID"),
220
+ # "auth_uri": os.getenv("AUTH_URI"),
221
+ # "token_uri": os.getenv("TOKEN_URI"),
222
+ # "auth_provider_x509_cert_url": os.getenv("AUTH_PROVIDER_X509_CERT_URL"),
223
+ # "client_x509_cert_url": os.getenv("CLIENT_X509_CERT_URL"),
224
+ # "universe_domain": os.getenv("UNIVERSE_DOMAIN")
225
+ # }
226
+ # scope = ['https://spreadsheets.google.com/feeds', 'https://www.googleapis.com/auth/drive']
227
+ # creds = Credentials.from_service_account_info(service_account_info, scopes=scope)
228
+ # return creds
229
+
230
+ # except Exception as e:
231
+ # print(f"Error getting credentials: {e}")
232
+ # return None
233
+
234
+ # # Step 2: Authorize gspread using the credentials
235
+ # creds = get_credentials()
236
+ # client = gspread.authorize(creds)
237
+
238
+ # # Function to get file paths based on coaching code
239
+ # def get_file_paths(coaching_code):
240
+ # if coaching_code == '1919':
241
+ # return {
242
+ # 'journal': 'https://docs.google.com/spreadsheets/d/1EFf2lr4A10nt4RhIqxCD_fxe-l3sXH09II0TEkMmvhA/edit?usp=drive_link',
243
+ # 'panic_button': 'https://docs.google.com/spreadsheets/d/1nFZGkCvRV6qS-mhsORhX3dxI0JSge32_UwWgWKl3eyw/edit?usp=drive_link',
244
+ # 'test': 'https://docs.google.com/spreadsheets/d/13PUHySUXWtKBusjugoe7Dbsm39PwBUfG4tGLipspIx4/edit?usp=drive_link'
245
+ # }
246
+ # if coaching_code == '0946':
247
+ # return {
248
+ # 'journal': 'https://docs.google.com/spreadsheets/d/1c1TkL7sOUvFn6UPz3gwp135UVjOou9u1weohWzpmx6I/edit?usp=drive_link',
249
+ # 'panic_button': 'https://docs.google.com/spreadsheets/d/1RhbPQnNNBUthKKJyoW4q6x3uaWl1YSqmsFlfJ2THphE/edit?usp=drive_link',
250
+ # 'test': 'https://docs.google.com/spreadsheets/d/1JO5wDkfl2fr2ZQenI8OEu48jkWm48veYN1Fsw5Ctkzw/edit?usp=drive_link'
251
+ # }
252
+ # # Panic button weightages
253
+ # academic_weights = {'BACKLOGS': -5, 'MISSED CLASSES': -4, 'NOT UNDERSTANDING': -3, 'BAD MARKS': -3, 'LACK OF MOTIVATION': -3}
254
+ # non_academic_weights = {'EMOTIONAL FACTORS': -3, 'PROCRASTINATE': -2, 'LOST INTEREST': -4, 'LACK OF FOCUS': -2, 'GOALS NOT ACHIEVED': -2, 'LACK OF DISCIPLINE': -2}
255
+
256
+ # # Max weighted panic score
257
+ # max_weighted_panic_score = sum([max(academic_weights.values()) * 3, max(non_academic_weights.values()) * 3])
258
+
259
+ # # Function to calculate potential score
260
+ # def calculate_potential_score(row):
261
+ # # Test score normalization (70% weightage)
262
+ # if row['test_scores']: # Check if test_scores is not empty
263
+ # avg_test_score = sum(row['test_scores'].values()) / len(row['test_scores'])
264
+ # test_score_normalized = (avg_test_score / 40) * 70 # Scale test score to 70
265
+ # else:
266
+ # test_score_normalized = 0 # Default value for users with no test scores
267
+
268
+ # # Panic score calculation (20% weightage)
269
+ # student_panic_score = 0
270
+ # if row['panic_button']: # Ensure panic_button is not NaN or empty
271
+ # for factor, count in row['panic_button'].items():
272
+ # if factor in academic_weights:
273
+ # student_panic_score += academic_weights[factor] * count
274
+ # elif factor in non_academic_weights:
275
+ # student_panic_score += non_academic_weights[factor] * count
276
+ # else:
277
+ # student_panic_score = 0 # Default if no panic button issues
278
+
279
+ # # Panic score normalized to 20
280
+ # panic_score = 20 * (1 - (student_panic_score / max_weighted_panic_score) if max_weighted_panic_score != 0 else 1)
281
+
282
+ # # Journal score calculation (10% weightage)
283
+ # if pd.notna(row['productivity_yes_no']) and row['productivity_yes_no'] == 'Yes':
284
+ # if pd.notna(row['productivity_rate']):
285
+ # journal_score = (float(row['productivity_rate']) / 10) * 10 # Scale journal score to 10
286
+ # else:
287
+ # journal_score = 0 # Default if productivity_rate is missing
288
+ # elif pd.notna(row['productivity_yes_no']) and row['productivity_yes_no'] == 'No':
289
+ # if pd.notna(row['productivity_rate']):
290
+ # journal_score = (float(row['productivity_rate']) / 10) * 5 # Scale journal score to 5 if "No"
291
+ # else:
292
+ # journal_score = 0 # Default if productivity_rate is missing
293
+ # else:
294
+ # journal_score = 0 # Default if productivity_yes_no is missing
295
+
296
+ # # Total score based on new weightages
297
+ # total_potential_score = test_score_normalized + panic_score + journal_score
298
+ # return total_potential_score
299
+
300
+ # # Step 11: Define API endpoint to get the sorted potential scores
301
+ # @app.get("/sorted-potential-scores")
302
+ # async def get_sorted_potential_scores(coaching_code: str = Query(..., description="Coaching code to determine file paths")):
303
+ # try:
304
+ # file_paths = get_file_paths(coaching_code)
305
+ # if not file_paths:
306
+ # raise HTTPException(status_code=400, detail="Invalid coaching code")
307
+ # print("A");
308
+ # # Open Google Sheets using the URLs
309
+ # journal_file = client.open_by_url(file_paths['journal']).worksheet('Sheet1')
310
+ # panic_button_file = client.open_by_url(file_paths['panic_button']).worksheet('Sheet1')
311
+ # test_file = client.open_by_url(file_paths['test']).worksheet('Sheet1')
312
+ # print("B");
313
+ # # Convert the sheets into Pandas DataFrames
314
+ # journal_df = pd.DataFrame(journal_file.get_all_values())
315
+ # panic_button_df = pd.DataFrame(panic_button_file.get_all_values())
316
+ # test_df = pd.DataFrame(test_file.get_all_values())
317
+ # print("C");
318
+ # # Label the columns manually since there are no headers
319
+ # journal_df.columns = ['user_id', 'productivity_yes_no', 'productivity_rate']
320
+ # panic_button_df.columns = ['user_id', 'panic_button']
321
+ # print("D")
322
+ # # Initialize a list for the merged data
323
+ # merged_data = []
324
+
325
+ # # Group panic buttons by user_id and combine into a single comma-separated string
326
+ # panic_button_grouped = panic_button_df.groupby('user_id')['panic_button'].apply(lambda x: ','.join(x)).reset_index()
327
+ # print("E")
328
+ # # Merge journal and panic button data
329
+ # merged_journal_panic = pd.merge(journal_df, panic_button_grouped, on='user_id', how='outer')
330
+ # print("F")
331
+ # # Process the test data
332
+ # test_data = []
333
+ # for index, row in test_df.iterrows():
334
+ # user_id = row[0]
335
+ # i = 1
336
+ # while i < len(row) and pd.notna(row[i]): # Process chapter and score pairs
337
+ # chapter = row[i].lower().strip()
338
+ # score = row[i + 1]
339
+ # if pd.notna(score):
340
+ # test_data.append({'user_id': user_id, 'test_chapter': chapter, 'test_score': score})
341
+ # i += 2
342
+ # print("G")
343
+ # # Convert the processed test data into a DataFrame
344
+ # test_df_processed = pd.DataFrame(test_data)
345
+ # print("H")
346
+ # # Merge the journal+panic button data with the test data
347
+ # merged_data = pd.merge(merged_journal_panic, test_df_processed, on='user_id', how='outer')
348
+ # print("I")
349
+ # # Drop rows where all data (except user_id and test_chapter) is missing
350
+ # merged_data_cleaned = merged_data.dropna(subset=['productivity_yes_no', 'productivity_rate', 'panic_button', 'test_chapter'], how='all')
351
+ # print("J")
352
+ # # Group the merged DataFrame by user_id
353
+ # df = pd.DataFrame(merged_data_cleaned)
354
+ # print("K")
355
+ # # Function to process panic button counts and test scores
356
+ # def process_group(group):
357
+ # # Panic button counts
358
+ # panic_button_series = group['panic_button'].dropna()
359
+ # panic_button_dict = panic_button_series.value_counts().to_dict()
360
+
361
+ # # Test scores aggregation
362
+ # test_scores = group[['test_chapter', 'test_score']].dropna()
363
+ # test_scores['test_score'] = pd.to_numeric(test_scores['test_score'], errors='coerce')
364
+
365
+ # # Create the test_scores_dict excluding NaN values
366
+ # test_scores_dict = test_scores.groupby('test_chapter')['test_score'].mean().dropna().to_dict()
367
+
368
+ # return pd.Series({
369
+ # 'productivity_yes_no': group['productivity_yes_no'].iloc[0],
370
+ # 'productivity_rate': group['productivity_rate'].iloc[0],
371
+ # 'panic_button': panic_button_dict,
372
+ # 'test_scores': test_scores_dict
373
+ # })
374
+
375
+ # # Apply the group processing function
376
+ # merged_df = df.groupby('user_id').apply(process_group).reset_index()
377
+ # print("L")
378
+ # # Calculate potential scores and sort
379
+ # merged_df['potential_score'] = merged_df.apply(calculate_potential_score, axis=1)
380
+ # merged_df['potential_score'] = merged_df['potential_score'].round(2)
381
+ # sorted_df = merged_df[['user_id', 'potential_score']].sort_values(by='potential_score', ascending=False)
382
+ # print("M")
383
+ # result = sorted_df.to_dict(orient="records")
384
+ # return {"sorted_scores": result}
385
+ # except Exception as e:
386
+ # raise HTTPException(status_code=500, detail=str(e))
387
+
388
+
389
+
390
+
391
+ from fastapi import FastAPI, HTTPException
392
  from pydantic import BaseModel
393
  import gspread
394
  from google.oauth2.service_account import Credentials
395
  import pandas as pd
396
+
 
 
 
397
  app = FastAPI()
398
+
399
+ # Model for request
400
+ class CoachingCodeRequest(BaseModel):
401
+ coachingCode: str
402
+
403
+ # Function to get credentials
 
 
404
  def get_credentials():
 
405
  try:
 
406
  service_account_info = {
407
+ "type": userdata.get("SERVICE_ACCOUNT_TYPE"),
408
+ "project_id": userdata.get("PROJECT_ID"),
409
+ "private_key_id": userdata.get("PRIVATE_KEY_ID"),
410
+ "private_key": userdata.get("PRIVATE_KEY").replace('\\n', '\n'),
411
+ "client_email": userdata.get("CLIENT_EMAIL"),
412
+ "client_id": userdata.get("CLIENT_ID"),
413
+ "auth_uri": userdata.get("AUTH_URI"),
414
+ "token_uri": userdata.get("TOKEN_URI"),
415
+ "auth_provider_x509_cert_url": userdata.get("AUTH_PROVIDER_X509_CERT_URL"),
416
+ "client_x509_cert_url": userdata.get("CLIENT_X509_CERT_URL"),
417
+ "universe_domain": userdata.get("UNIVERSE_DOMAIN")
418
  }
419
  scope = ['https://spreadsheets.google.com/feeds', 'https://www.googleapis.com/auth/drive']
420
  creds = Credentials.from_service_account_info(service_account_info, scopes=scope)
421
  return creds
 
422
  except Exception as e:
423
  print(f"Error getting credentials: {e}")
424
  return None
425
 
426
+ # Select files based on coaching code
427
+ def select_files(coaching_code):
428
+ creds = get_credentials()
429
+ client = gspread.authorize(creds)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
430
 
431
+ if coaching_code == "1919":
432
+ journal_file = client.open_by_url('https://docs.google.com/spreadsheets/d/1EFf2lr4A10nt4RhIqxCD_fxe-l3sXH09II0TEkMmvhA/edit?gid=0#gid=0').worksheet('Sheet1')
433
+ panic_button_file = client.open_by_url('https://docs.google.com/spreadsheets/d/1nFZGkCvRV6qS-mhsORhX3dxI0JSge32_UwWgWKl3eyw/edit?gid=0#gid=0').worksheet('Sheet1')
434
+ test_file = client.open_by_url('https://docs.google.com/spreadsheets/d/13PUHySUXWtKBusjugoe7Dbsm39PwBUfG4tGLipspIx4/edit?gid=0#gid=0').worksheet('Sheet1')
435
+ else:
436
+ raise HTTPException(status_code=404, detail="Invalid coaching code")
437
+
438
+ return journal_file, panic_button_file, test_file
439
+
440
+ # Main route to get sorted scores
441
+ @app.post("/get_sorted_scores")
442
+ async def get_sorted_scores(data: CoachingCodeRequest):
443
+ journal_file, panic_button_file, test_file = select_files(data.coachingCode)
444
+
445
+ # Load data into DataFrames
446
+ journal_df = pd.DataFrame(journal_file.get_all_values())
447
+ panic_button_df = pd.DataFrame(panic_button_file.get_all_values())
448
+ test_df = pd.DataFrame(test_file.get_all_values())
449
+
450
+ # Processing logic
451
+ panic_data = []
452
+ for index, row in panic_button_df.iterrows():
453
+ user_id = row[0]
454
+ row_pairs = row[1:].dropna().to_list()[-5:]
455
+ for i in range(0, len(row_pairs), 2):
456
+ panic = row_pairs[i].upper().strip()
457
+ if pd.notna(panic):
458
+ panic_data.append({'user_id': user_id, 'panic_button': panic})
459
+ panic_df_processed = pd.DataFrame(panic_data)
460
+
461
+ test_data = []
462
+ for index, row in test_df.iterrows():
463
+ user_id = row[0]
464
+ row_pairs = row[1:].dropna().to_list()
465
+ chapter_scores = {}
466
+ for i in range(0, len(row_pairs), 2):
467
+ chapter = row_pairs[i].lower().strip()
468
+ score = row_pairs[i + 1]
469
+ if pd.notna(score):
470
+ if chapter not in chapter_scores:
471
+ chapter_scores[chapter] = []
472
+ chapter_scores[chapter].append(score)
473
+ for chapter, scores in chapter_scores.items():
474
+ last_5_scores = scores[-5:]
475
+ for score in last_5_scores:
476
+ test_data.append({'user_id': user_id, 'test_chapter': chapter, 'test_score': score})
477
+ test_df_processed = pd.DataFrame(test_data)
478
+
479
+ journal_data = []
480
+ for index, row in journal_df.iterrows():
481
+ user_id = row[0]
482
+ row_pairs = row[1:].dropna().to_list()[-10:]
483
+ for i in range(0, len(row_pairs), 2):
484
+ productivity_yes_no = row_pairs[i].lower().strip()
485
+ productivity_rate = row_pairs[i + 1]
486
+ if pd.notna(productivity_rate):
487
+ journal_data.append({'user_id': user_id, 'productivity_yes_no': productivity_yes_no, 'productivity_rate': productivity_rate})
488
+ journal_df_processed = pd.DataFrame(journal_data)
489
+
490
+ merged_journal_panic = pd.merge(panic_df_processed, journal_df_processed, on='user_id', how='outer')
491
+ merged_data = pd.merge(merged_journal_panic, test_df_processed, on='user_id', how='outer')
492
+ merged_data_cleaned = merged_data.dropna(subset=['productivity_yes_no', 'productivity_rate', 'panic_button', 'test_chapter'], how='all')
493
+
494
+ # Define scoring weights
495
+ academic_weights = {'BACKLOGS': -5, 'MISSED CLASSES': -4, 'NOT UNDERSTANDING': -3, 'BAD MARKS': -3, 'LACK OF MOTIVATION': -3}
496
+ non_academic_weights = {'EMOTIONAL FACTORS': -3, 'PROCRASTINATE': -2, 'LOST INTEREST': -4, 'LACK OF FOCUS': -2, 'GOALS NOT ACHIEVED': -2, 'LACK OF DISCIPLINE': -2}
497
+ max_weighted_panic_score = sum([max(academic_weights.values()) * 3, max(non_academic_weights.values()) * 3])
498
+
499
+ def calculate_potential_score(row):
500
+ if row['test_scores']:
501
+ avg_test_score = sum(row['test_scores'].values()) / len(row['test_scores'])
502
+ test_score_normalized = (avg_test_score / 40) * 70
503
  else:
504
+ test_score_normalized = 0
505
+ student_panic_score = 0
506
+ if row['panic_button']:
507
+ for factor, count in row['panic_button'].items():
508
+ if factor in academic_weights:
509
+ student_panic_score += academic_weights[factor] * count
510
+ elif factor in non_academic_weights:
511
+ student_panic_score += non_academic_weights[factor] * count
512
  else:
513
+ student_panic_score = 0
514
+ panic_score = 20 * (1 - (student_panic_score / max_weighted_panic_score) if max_weighted_panic_score != 0 else 1)
515
+ if pd.notna(row['productivity_yes_no']) and row['productivity_yes_no'] == 'Yes':
516
+ if pd.notna(row['productivity_rate']):
517
+ journal_score = (float(row['productivity_rate']) / 10) * 10
518
+ else:
519
+ journal_score = 0
520
+ elif pd.notna(row['productivity_yes_no']) and row['productivity_yes_no'] == 'No':
521
+ if pd.notna(row['productivity_rate']):
522
+ journal_score = (float(row['productivity_rate']) / 10) * 5
523
+ else:
524
+ journal_score = 0
525
+ else:
526
+ journal_score = 0
527
+ total_potential_score = test_score_normalized + panic_score + journal_score
528
+ return total_potential_score
529
+
530
+ merged_df = merged_data_cleaned.groupby('user_id').apply(process_group).reset_index()
531
+ merged_df['potential_score'] = merged_df.apply(calculate_potential_score, axis=1)
532
+ merged_df['potential_score'] = merged_df['potential_score'].round(2)
533
+ sorted_df = merged_df[['user_id', 'potential_score']].sort_values(by='potential_score', ascending=False)
534
+ result = sorted_df.to_dict(orient="records")
535
+
536
+ return {"sorted_scores": result}
537