Hari-Prasath-M91 commited on
Commit
1e71cd5
·
1 Parent(s): e9ec36e
Files changed (2) hide show
  1. app.py +552 -162
  2. dependencies.json +178 -0
app.py CHANGED
@@ -7,6 +7,7 @@ from math import ceil
7
  from fastapi import FastAPI, Query
8
  from contextlib import asynccontextmanager
9
  from datetime import datetime, timedelta
 
10
  from collections import defaultdict
11
  from langchain_openai import ChatOpenAI
12
  from langchain_core.messages import HumanMessage, SystemMessage
@@ -23,6 +24,7 @@ session_state = {
23
  "full_roadmap": None,
24
  "report_data": None,
25
  "final_report": None,
 
26
  "updated_roadmap": None
27
  }
28
 
@@ -35,8 +37,10 @@ async def lifespan(app: FastAPI):
35
  session_state["data"] = json.load(f)
36
  with open("synthesized_full_roadmap.json", "r") as f:
37
  session_state["full_roadmap"] = json.load(f)
 
 
38
  # Process tasks as incomplete
39
- process_task_completion_data()
40
  print("✅ Roadmaps loaded successfully.")
41
  except Exception as e:
42
  print(f"❌ Error loading roadmaps: {e}")
@@ -53,29 +57,52 @@ app = FastAPI(
53
  )
54
 
55
  # Function to mark tasks as incomplete
56
- def process_task_completion_data():
57
- data = session_state['data']
58
  for day in data["schedule"]:
59
  for subject in day["subjects"]:
60
  for task in subject["tasks"]:
61
  task["task_completed"] = False
62
  task["completion_timestamp"] = None
63
- session_state['data'] = data
64
-
65
- def check_tot_time(day, max_hours_per_day):
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
66
  tot_time = 0
67
- for subject in day:
68
  for task in subject["tasks"]:
69
  tot_time += float(task['time'].split(" ")[0])
 
70
 
71
- if tot_time > max_hours_per_day:
72
- return tot_time, True
73
- else:
74
- return tot_time, False
75
-
76
- def shift_roadmap(roadmap, max_hours_per_day):
77
- roadmap = copy.deepcopy(roadmap)
78
  incomplete_tasks_by_subject = defaultdict(list)
 
79
 
80
  prev_day = roadmap[0]
81
  for subject in prev_day["subjects"]:
@@ -86,6 +113,8 @@ def shift_roadmap(roadmap, max_hours_per_day):
86
  incomplete_tasks = [task for task in tasks if task['task_completed'] == False]
87
  completed_tasks = [task for task in tasks if task['task_completed'] == True]
88
 
 
 
89
  # Store incomplete tasks per subject
90
  if incomplete_tasks:
91
  incomplete_tasks_by_subject[subject_name].extend(incomplete_tasks)
@@ -93,171 +122,517 @@ def shift_roadmap(roadmap, max_hours_per_day):
93
  # Keep only completed tasks in the previous day
94
  subject["tasks"] = completed_tasks
95
 
96
-
97
- # Step 2: Redistribute tasks across the next 3 days
98
- for i, next_day in enumerate(roadmap[1:]): # Next 3 days (Day 2, Day 3, Day 4)
99
- for subject in next_day["subjects"]:
100
  subject_name = subject["name"]
101
- if subject_name in incomplete_tasks_by_subject and incomplete_tasks_by_subject[subject_name]:
102
- total_tasks = len(incomplete_tasks_by_subject[subject_name])
103
-
104
- # Task distribution based on 1/6, 2/6, and remaining
105
- if i == 0: # First day gets 1/6 of total
106
- tasks_to_add = ceil(total_tasks * (1 / 6))
107
- elif i == 1: # Second day gets 2/6 of total
108
- tasks_to_add = ceil(total_tasks * (2 / 6))
109
- else: # Remaining tasks on the last day
110
- tasks_to_add = len(incomplete_tasks_by_subject[subject_name])
111
-
112
- # Append tasks to the current day's subject
113
- subject["tasks"].extend(incomplete_tasks_by_subject[subject_name][:tasks_to_add])
114
-
115
- # Remove assigned tasks from backlog
116
- incomplete_tasks_by_subject[subject_name] = incomplete_tasks_by_subject[subject_name][tasks_to_add:]
117
-
118
- # Make sure the time limit doesn't exceed for any day
119
- _, check_time = check_tot_time(next_day["subjects"], max_hours_per_day)
120
- while check_time:
121
- for subject in next_day["subjects"]:
122
- subject_name = subject["name"]
123
- if subject["tasks"]:
124
- task_to_add = subject["tasks"].pop()
125
- incomplete_tasks_by_subject[subject_name].append(task_to_add)
126
- _, check_time = check_tot_time(next_day["subjects"], max_hours_per_day)
127
- if not check_time:
128
- break
129
-
130
- return roadmap, incomplete_tasks_by_subject
131
-
132
- def get_shifted_roadmap(roadmap, dayNumber, max_hours_per_day):
133
- day_index = dayNumber-1
134
- if day_index+4 <= len(roadmap['schedule']):
135
- shifted_roadmap, incomplete_tasks_by_subject = shift_roadmap(roadmap['schedule'][day_index:day_index+4], max_hours_per_day)
136
- else:
137
- shifted_roadmap, incomplete_tasks_by_subject = shift_roadmap(roadmap['schedule'][day_index:], max_hours_per_day)
138
- for day in shifted_roadmap:
139
- new_date = day["date"]
140
-
141
- for idx, existing_day in enumerate(roadmap['schedule']):
142
- if existing_day['date'] == new_date:
143
- roadmap['schedule'][idx] = day
144
- break
145
-
146
- if any(len(v) != 0 for v in incomplete_tasks_by_subject.values()):
147
- next_date = (datetime.strptime(roadmap['schedule'][-1]['date'], "%Y-%m-%d") + timedelta(days=1)).strftime("%Y-%m-%d")
148
- next_day = roadmap['schedule'][-1]['dayNumber'] + 1
149
- subjects = [{"name": subject_name, "tasks": tasks} for subject_name, tasks in incomplete_tasks_by_subject.items()]
150
- roadmap['schedule'].append({
151
- "dayNumber": next_day,
152
- "date": next_date,
153
- "subjects": subjects
154
- })
155
- return roadmap
156
-
157
- # Step 0: Get Subjectwise Chapter and Topic order ready
158
- def get_subjectwise_tasks(roadmap):
159
- sub_tasks = {
160
- "Physics": defaultdict(list),
161
- "Chemistry": defaultdict(list),
162
- "Maths": defaultdict(list)
163
- }
164
-
165
- for day in roadmap["schedule"]:
166
- for subject in day['subjects']:
167
- sub = sub_tasks[subject['name']]
168
- for task in subject['tasks']:
169
- if task['subtopic'] not in sub[task['ChapterName']]:
170
- sub[task['ChapterName']].append(task['subtopic'])
171
-
172
- return sub_tasks
173
 
174
- # Step 1: Extract all tasks per subject and track time allocation per day
175
- def extract_tasks(roadmap):
176
- subjectwise_tasks = defaultdict(list)
177
- daily_subjectwise_time_allocation = defaultdict(lambda: defaultdict(float))
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
178
 
179
- for day_index, day in enumerate(roadmap['schedule']):
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
180
  for subject in day["subjects"]:
181
- subject_name = subject["name"]
182
- total_time = sum(float(task['time'].split(" ")[0]) for task in subject["tasks"])
183
-
184
- daily_subjectwise_time_allocation[day_index][subject_name] = total_time
185
- subjectwise_tasks[subject_name].extend(subject["tasks"])
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
186
 
187
- return subjectwise_tasks, daily_subjectwise_time_allocation
 
188
 
189
- # Step 2: Sort all tasks for each subject
190
- def sort_tasks(tasks, reference):
191
- task_type_priority = {
192
- "Concept Understanding": 0,
193
- "Question Practice": 1,
194
- "Revision": 2,
195
- "Test": 3
196
- }
197
- chapter_order = list(reference.keys())
 
 
 
 
 
 
 
198
 
199
- def task_sort_key(task):
200
- chapter = task["ChapterName"]
201
- subtopic = task["subtopic"]
202
- type_priority = task_type_priority.get(task["type"], 99)
203
 
204
- chapter_idx = chapter_order.index(chapter) if chapter in reference else float('inf')
205
- subtopic_idx = reference[chapter].index(subtopic) if subtopic in reference.get(chapter, []) else float('inf')
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
206
 
207
- return (chapter_idx, subtopic_idx, type_priority)
 
 
208
 
209
- return sorted(tasks, key=task_sort_key)
 
 
210
 
211
- # Helper function to get task time in hours
212
- def get_task_time(task):
213
- return float(task['time'].split(" ")[0])
214
 
215
- # Step 3: Sort the roadmap by arranging the sorted tasks, preserving original time allocation
216
- def shift_and_sort_the_roadmap(full_roadmap, roadmap, dayNumber, max_hours_per_day):
217
  roadmap = copy.deepcopy(roadmap)
218
- roadmap = get_shifted_roadmap(roadmap, dayNumber, max_hours_per_day)
219
- subject_refs = get_subjectwise_tasks(full_roadmap) # Load the full roadmap to obtain the correct chapter orders
220
- subject_all_tasks, subject_day_time_allocation = extract_tasks(roadmap)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
221
 
222
- # Sort all tasks for each subject
223
- for subject in subject_all_tasks:
224
- subject_all_tasks[subject] = sort_tasks(subject_all_tasks[subject], subject_refs[subject])
225
 
226
- # Redistribute tasks based on time allocation, strictly maintaining sequence
227
- for day_index, day in enumerate(roadmap['schedule']):
228
- day_time = 0
229
- for subject in day["subjects"]:
230
- subject_name = subject["name"]
231
- target_time = subject_day_time_allocation[day_index][subject_name]
 
 
232
 
233
- selected_tasks = []
234
- current_time = 0
235
- tasks = subject_all_tasks[subject_name]
236
- while tasks and current_time < target_time:
237
- next_task = tasks[0] # Take the next task from the sorted sequence
238
  task_time = get_task_time(next_task)
239
 
240
- if day_time + task_time <= max_hours_per_day: # Allow if its under the max limit
241
- selected_tasks.append(tasks.pop(0))
242
- current_time += task_time
243
  day_time += task_time
244
- else:
245
- # If Task doesn't fit, save for next day
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
246
  break
247
 
248
- if day_index == len(roadmap['schedule']) - 1:
249
- if tasks:
250
- for task in tasks:
251
- selected_tasks.append(task)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
252
 
253
- # Update the subject's tasks
254
- subject["tasks"] = selected_tasks
255
- subject_all_tasks[subject_name] = tasks
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
256
 
257
  with open("current_roadmap.json", "w") as f:
258
- json.dump(roadmap, f, indent=4)
259
 
260
- session_state['updated_roadmap'] = roadmap
261
 
262
  # AGENT 2
263
  def generate_sql_for_report(llm, prompt):
@@ -813,11 +1188,27 @@ def agent1(choice: Optional[str] = Query("Four Day Roadmap", description="Choose
813
  return {"message": "No report data available."}
814
 
815
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
816
  # --- AGENT 2: Roadmap Manager (Roadmap Manager Page) ---
817
  @app.get("/agent2")
818
  def agent2(
819
- dayNumber: int = Query(1, description="Today's day number for rescheduling tasks"),
820
- max_hours_per_day: int = Query(8, description="Maximum number of hours per day")
 
821
  ):
822
  """
823
  Agent 2 - Roadmap Manager: Processes tasks and optimizes the roadmap based on user input.
@@ -827,12 +1218,11 @@ def agent2(
827
  return {"error": "Roadmap data not loaded. Load data first."}
828
 
829
  # Optimize task distribution with user input
830
- shift_and_sort_the_roadmap(
831
- session_state["full_roadmap"],
832
- session_state["data"],
833
- dayNumber=dayNumber,
834
- max_hours_per_day=max_hours_per_day
835
- )
836
 
837
  # Return full updated roadmap
838
  if session_state["data"] and session_state["updated_roadmap"]:
 
7
  from fastapi import FastAPI, Query
8
  from contextlib import asynccontextmanager
9
  from datetime import datetime, timedelta
10
+ from dateutil import parser
11
  from collections import defaultdict
12
  from langchain_openai import ChatOpenAI
13
  from langchain_core.messages import HumanMessage, SystemMessage
 
24
  "full_roadmap": None,
25
  "report_data": None,
26
  "final_report": None,
27
+ "dependencies": None,
28
  "updated_roadmap": None
29
  }
30
 
 
37
  session_state["data"] = json.load(f)
38
  with open("synthesized_full_roadmap.json", "r") as f:
39
  session_state["full_roadmap"] = json.load(f)
40
+ with open("dependencies.json", 'r') as file:
41
+ session_state["dependencies"] = json.load(file)
42
  # Process tasks as incomplete
43
+ process_task_data()
44
  print("✅ Roadmaps loaded successfully.")
45
  except Exception as e:
46
  print(f"❌ Error loading roadmaps: {e}")
 
57
  )
58
 
59
  # Function to mark tasks as incomplete
60
+ def process_task_data():
61
+ data = session_state["data"]
62
  for day in data["schedule"]:
63
  for subject in day["subjects"]:
64
  for task in subject["tasks"]:
65
  task["task_completed"] = False
66
  task["completion_timestamp"] = None
67
+ task["rescheduled"] = 0
68
+ session_state["data"] = data
69
+ print("Task data processed!")
70
+
71
+ def add_test(roadmap, date, physics = [], chemistry = [], maths = []):
72
+ date = parser.parse(date).strftime("%Y-%m-%d")
73
+ for i, day in enumerate(roadmap["schedule"]):
74
+ if day["date"] == date:
75
+ roadmap["schedule"][i] = {
76
+ "dayNumber": day['dayNumber'],
77
+ "date": date,
78
+ "test_portion": [
79
+ {
80
+ "name": "Physics",
81
+ "chapters": physics
82
+ },
83
+ {
84
+ "name": "Chemistry",
85
+ "chapters": chemistry
86
+ },
87
+ {
88
+ "name": "Maths",
89
+ "chapters": maths
90
+ }
91
+ ],
92
+ "subjects": day['subjects']
93
+ }
94
+ return roadmap
95
+
96
+ def check_tot_time(day):
97
  tot_time = 0
98
+ for subject in day['subjects']:
99
  for task in subject["tasks"]:
100
  tot_time += float(task['time'].split(" ")[0])
101
+ return tot_time
102
 
103
+ def extract_tasks(roadmap, test_portions=None, dependencies=None):
 
 
 
 
 
 
104
  incomplete_tasks_by_subject = defaultdict(list)
105
+ subjectwise_tasks = defaultdict(list)
106
 
107
  prev_day = roadmap[0]
108
  for subject in prev_day["subjects"]:
 
113
  incomplete_tasks = [task for task in tasks if task['task_completed'] == False]
114
  completed_tasks = [task for task in tasks if task['task_completed'] == True]
115
 
116
+ for task in incomplete_tasks:
117
+ task['rescheduled'] += 1
118
  # Store incomplete tasks per subject
119
  if incomplete_tasks:
120
  incomplete_tasks_by_subject[subject_name].extend(incomplete_tasks)
 
122
  # Keep only completed tasks in the previous day
123
  subject["tasks"] = completed_tasks
124
 
125
+ for day_index, day in enumerate(roadmap[1:]):
126
+ for subject in day["subjects"]:
 
 
127
  subject_name = subject["name"]
128
+ subjectwise_tasks[subject_name].extend(subject["tasks"])
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
129
 
130
+ if test_portions and dependencies:
131
+ dependent_tasks_by_subject = defaultdict(list)
132
+ dependent_chapters = set()
133
+ for subject in test_portions:
134
+ sub_name = subject['name']
135
+ for chapter in subject['chapters']:
136
+ if chapter in dependencies[sub_name]:
137
+ dependent_chapters.update(dependencies[sub_name][chapter])
138
+
139
+ for subject, tasks in subjectwise_tasks.items():
140
+ retained_tasks = []
141
+ for task in tasks:
142
+ if task.get("ChapterName") in dependent_chapters:
143
+ dependent_tasks_by_subject[subject].append(task)
144
+ else:
145
+ retained_tasks.append(task)
146
+ subjectwise_tasks[subject] = retained_tasks
147
+
148
+ for subject, tasks in incomplete_tasks_by_subject.items():
149
+ retained_tasks = []
150
+ for task in tasks:
151
+ if task.get("ChapterName") in dependent_chapters:
152
+ dependent_tasks_by_subject[subject].append(task)
153
+ else:
154
+ retained_tasks.append(task)
155
+ incomplete_tasks_by_subject[subject] = retained_tasks
156
+ return roadmap, subjectwise_tasks, incomplete_tasks_by_subject, dependent_tasks_by_subject
157
+ return roadmap, subjectwise_tasks, incomplete_tasks_by_subject
158
 
159
+ def get_task_time(task):
160
+ return round(float(task['time'].split(" ")[0]), 3)
161
+
162
+ def calculate_time_distribution(roadmap, incomplete_tasks, incomplete_tasks_by_subject, max_hours_per_day):
163
+ total_hours = 0
164
+ num_days = len(roadmap[1:])
165
+ extra_day=False
166
+
167
+ extra_hours = 0
168
+ if incomplete_tasks_by_subject:
169
+ for subject in incomplete_tasks_by_subject:
170
+ for task in incomplete_tasks_by_subject[subject]:
171
+ extra_hours += get_task_time(task)
172
+ extra_day=True
173
+
174
+ for subject in incomplete_tasks:
175
+ for task in incomplete_tasks[subject]:
176
+ total_hours += get_task_time(task)
177
+
178
+ for day in roadmap[1:]:
179
+ if day['dayNumber'] >= 550:
180
+ max_hours_per_day = 16
181
  for subject in day["subjects"]:
182
+ for task in subject["tasks"]:
183
+ total_hours += get_task_time(task)
184
+
185
+ if num_days <= 0:
186
+ return [], [total_hours + extra_hours] if total_hours+extra_hours > 0 else []
187
+ max_possible_hours = num_days * max_hours_per_day
188
+
189
+ if total_hours <= max_possible_hours and not extra_day:
190
+ # Calculate base hours per day (minimum)
191
+ base_hours = total_hours // num_days
192
+
193
+ # Calculate remaining hours
194
+ remaining_hours = total_hours - (base_hours * num_days)
195
+
196
+ # Start with all days having base hours
197
+ distribution = [base_hours] * num_days
198
+
199
+ # Distribute remaining hours starting from the last day
200
+ for i in range(num_days - 1, -1, -1):
201
+ if remaining_hours > 0:
202
+ additional = min(1, remaining_hours, max_hours_per_day - distribution[i])
203
+ distribution[i] += additional
204
+ remaining_hours -= additional
205
+
206
+ return distribution, []
207
+
208
+ # Otherwise, max out all current days and prepare for extra days
209
+ distribution = [max_hours_per_day] * num_days
210
+ remaining_hours = total_hours - max_possible_hours
211
+
212
+ if extra_day:
213
+ base_hours = total_hours // num_days
214
+ remaining_hours = total_hours - (base_hours * num_days)
215
+ distribution = [base_hours] * num_days
216
+ for i in range(num_days - 1, -1, -1):
217
+ if remaining_hours > 0:
218
+ additional = min(1, remaining_hours, max_hours_per_day - distribution[i])
219
+ distribution[i] += additional
220
+ remaining_hours -= additional
221
+ remaining_hours = extra_hours
222
+
223
+ extra_distribution = []
224
+ while remaining_hours > 0:
225
+ hours = min(max_hours_per_day, remaining_hours)
226
+ extra_distribution.append(hours)
227
+ remaining_hours -= hours
228
+
229
+ return distribution, extra_distribution
230
+
231
+ def add_tasks_for_extra_days(subject_all_tasks, incomplete_tasks, extra_day_tasks, extra_distribution, ratio, max_hours_per_day):
232
+ subject_names = list(subject_all_tasks.keys()) or list(incomplete_tasks.keys())
233
+ has_incomplete_tasks = any(tasks for tasks in incomplete_tasks.values())
234
+ for i, target_time in enumerate(extra_distribution):
235
+ day_time = 0
236
+ if subject_all_tasks:
237
+ regular_task_limit = ceil(target_time * ratio[0] / 100) if has_incomplete_tasks else target_time
238
+ incomplete_task_limit = ceil(target_time * ratio[1] / 100) if has_incomplete_tasks else 0
239
+ else:
240
+ regular_task_limit = 0
241
+ incomplete_task_limit = target_time
242
+
243
+ # Create a new day with subjects
244
+ new_day = {"subjects": [{"name": n, "tasks": []} for n in subject_names]}
245
+
246
+ # Step 1: Allocate regular tasks up to their limit
247
+ regular_time = 0
248
+ while regular_time < regular_task_limit and day_time < max_hours_per_day:
249
+ added = False
250
+ for subject in new_day["subjects"]:
251
+ subject_name = subject["name"]
252
+ if not subject_all_tasks[subject_name]:
253
+ continue
254
 
255
+ next_task = subject_all_tasks[subject_name][0]
256
+ task_time = get_task_time(next_task)
257
 
258
+ if regular_time + task_time <= regular_task_limit and day_time + task_time <= max_hours_per_day:
259
+ subject["tasks"].append(subject_all_tasks[subject_name].pop(0))
260
+ regular_time += task_time
261
+ day_time += task_time
262
+ added = True
263
+ if not added:
264
+ break
265
+
266
+ # Step 2: Allocate incomplete tasks up to their limit
267
+ incomplete_time = 0
268
+ while incomplete_time < incomplete_task_limit and day_time < max_hours_per_day:
269
+ added = False
270
+ for subject in new_day["subjects"]:
271
+ subject_name = subject["name"]
272
+ if not incomplete_tasks[subject_name]:
273
+ continue
274
 
275
+ next_task = incomplete_tasks[subject_name][0]
276
+ task_time = get_task_time(next_task)
 
 
277
 
278
+ if incomplete_time + task_time <= incomplete_task_limit and day_time + task_time <= max_hours_per_day:
279
+ subject["tasks"].append(incomplete_tasks[subject_name].pop(0))
280
+ incomplete_time += task_time
281
+ day_time += task_time
282
+ added = True
283
+ if not added:
284
+ break
285
+
286
+ # Step 3: Use remaining time for additional regular tasks if available
287
+ if day_time < target_time:
288
+ while day_time < target_time:
289
+ added = False
290
+ for subject in new_day["subjects"]:
291
+ subject_name = subject["name"]
292
+ if not subject_all_tasks[subject_name]:
293
+ continue
294
+
295
+ next_task = subject_all_tasks[subject_name][0]
296
+ task_time = get_task_time(next_task)
297
+
298
+ if day_time + task_time <= max_hours_per_day:
299
+ subject["tasks"].append(subject_all_tasks[subject_name].pop(0))
300
+ day_time += task_time
301
+ added = True
302
+ if day_time > target_time:
303
+ break
304
+ if not added:
305
+ break
306
+ if i == len(extra_distribution) - 1:
307
+ for subject in new_day["subjects"]:
308
+ subject_name = subject["name"]
309
 
310
+ # Add remaining regular tasks
311
+ while subject_all_tasks[subject_name]:
312
+ subject["tasks"].append(subject_all_tasks[subject_name].pop(0))
313
 
314
+ # Add remaining incomplete tasks
315
+ while incomplete_tasks[subject_name]:
316
+ subject["tasks"].append(incomplete_tasks[subject_name].pop(0))
317
 
318
+ extra_day_tasks.append(new_day)
319
+ return extra_day_tasks
 
320
 
321
+ def shift_the_roadmap(roadmap, max_hours_per_day, ratio=(80, 20), dependencies=None, test_portions=None):
 
322
  roadmap = copy.deepcopy(roadmap)
323
+ # Extract tasks based on ratio mode
324
+ if ratio == (80, 20):
325
+ roadmap, subject_all_tasks, incomplete_tasks = extract_tasks(roadmap)
326
+ dependent_tasks = None
327
+ incomplete_tasks_by_subject = None
328
+ else:
329
+ roadmap, subject_all_tasks, incomplete_tasks_by_subject, dependent_tasks = extract_tasks(
330
+ roadmap, test_portions, dependencies
331
+ )
332
+ incomplete_tasks = dependent_tasks
333
+
334
+ # Distribute time across days
335
+ time_distribution, extra_distribution = calculate_time_distribution(roadmap, incomplete_tasks,
336
+ incomplete_tasks_by_subject,
337
+ max_hours_per_day)
338
+ # Check if there are any incomplete tasks
339
+ has_incomplete_tasks = any(tasks for tasks in incomplete_tasks.values())
340
+
341
+ # Prepare containers for task assignments
342
+ pending_regular_tasks = defaultdict(lambda: defaultdict(list))
343
+ pending_incomplete_tasks = defaultdict(lambda: defaultdict(list))
344
+
345
+ # Redistribute tasks for each day
346
+ for day_index, day in enumerate(roadmap[1:], 1):
347
+ target_time = time_distribution[day_index - 1]
348
+ day_time = 0
349
 
350
+ # Set task limits based on whether incomplete tasks exist
351
+ regular_task_limit = ceil(target_time * ratio[0] / 100) if has_incomplete_tasks else target_time
352
+ incomplete_task_limit = ceil(target_time * ratio[1] / 100) if has_incomplete_tasks else 0
353
 
354
+ # Step 1: Allocate regular tasks up to their limit (either 80% or 100%)
355
+ regular_time = 0
356
+ while regular_time < regular_task_limit and day_time < max_hours_per_day:
357
+ added = False
358
+ for subject in day["subjects"]:
359
+ subject_name = subject["name"]
360
+ if not subject_all_tasks[subject_name]:
361
+ continue
362
 
363
+ next_task = subject_all_tasks[subject_name][0]
 
 
 
 
364
  task_time = get_task_time(next_task)
365
 
366
+ if regular_time + task_time <= regular_task_limit and day_time + task_time <= max_hours_per_day:
367
+ pending_regular_tasks[day_index][subject_name].append(subject_all_tasks[subject_name].pop(0))
368
+ regular_time += task_time
369
  day_time += task_time
370
+ added = True
371
+ if not added:
372
+ break
373
+
374
+ # Step 2: Allocate incomplete tasks if they exist
375
+ if has_incomplete_tasks and incomplete_task_limit > 0:
376
+ incomplete_time = 0
377
+ while incomplete_time < incomplete_task_limit and day_time < max_hours_per_day:
378
+ added = False
379
+ for subject in day["subjects"]:
380
+ subject_name = subject["name"]
381
+ if not incomplete_tasks[subject_name]:
382
+ continue
383
+
384
+ next_task = incomplete_tasks[subject_name][0]
385
+ task_time = get_task_time(next_task)
386
+
387
+ if incomplete_time + task_time <= incomplete_task_limit and day_time + task_time <= max_hours_per_day:
388
+ pending_incomplete_tasks[day_index][subject_name].append(incomplete_tasks[subject_name].pop(0))
389
+ incomplete_time += task_time
390
+ day_time += task_time
391
+ added = True
392
+
393
+ # Check if we've depleted all incomplete tasks
394
+ if not any(tasks for tasks in incomplete_tasks.values()):
395
+ has_incomplete_tasks = False
396
+ break
397
+ if not added:
398
  break
399
 
400
+ # Step 3: Use remaining time for additional regular tasks if available
401
+ if day_time < target_time:
402
+ while day_time < target_time:
403
+ added = False
404
+ for subject in day["subjects"]:
405
+ subject_name = subject["name"]
406
+ if not subject_all_tasks[subject_name]:
407
+ continue
408
+
409
+ next_task = subject_all_tasks[subject_name][0]
410
+ task_time = get_task_time(next_task)
411
+
412
+ if day_time + task_time <= max_hours_per_day:
413
+ pending_regular_tasks[day_index][subject_name].append(subject_all_tasks[subject_name].pop(0))
414
+ day_time += task_time
415
+ added = True
416
+ if day_time > target_time:
417
+ break
418
+ if not added:
419
+ break
420
 
421
+ extra_day_tasks = []
422
+ if extra_distribution:
423
+ if incomplete_tasks_by_subject:
424
+ for subject, tasks in incomplete_tasks_by_subject.items():
425
+ incomplete_tasks[subject].extend(tasks)
426
+ extra_day_tasks = add_tasks_for_extra_days(subject_all_tasks,
427
+ incomplete_tasks,
428
+ extra_day_tasks,
429
+ extra_distribution,
430
+ (80, 20),
431
+ max_hours_per_day)
432
+ # Final appending of tasks
433
+ for day_index, day in enumerate(roadmap[1:], 1):
434
+ for subject in day["subjects"]:
435
+ subject_name = subject["name"]
436
+ subject["tasks"] = (
437
+ pending_regular_tasks[day_index][subject_name] +
438
+ pending_incomplete_tasks[day_index][subject_name]
439
+ )
440
+ else:
441
+ for day_index, day in enumerate(roadmap[1:], 1):
442
+ if day_index == len(roadmap) - 1:
443
+ for subject in day["subjects"]:
444
+ subject_name = subject["name"]
445
+
446
+ # Add remaining regular tasks
447
+ while subject_all_tasks[subject_name]:
448
+ task = subject_all_tasks[subject_name].pop(0)
449
+ pending_regular_tasks[day_index][subject_name].append(task)
450
+
451
+ # Add remaining incomplete tasks
452
+ while incomplete_tasks[subject_name]:
453
+ task = incomplete_tasks[subject_name].pop(0)
454
+ pending_incomplete_tasks[day_index][subject_name].append(task)
455
+
456
+ # Final appending of tasks
457
+ for subject in day["subjects"]:
458
+ subject_name = subject["name"]
459
+ subject["tasks"] = (
460
+ pending_regular_tasks[day_index][subject_name] +
461
+ pending_incomplete_tasks[day_index][subject_name]
462
+ )
463
+ return roadmap, extra_day_tasks
464
+
465
+ def update_roadmap(current_roadmap, current_dayNumber, max_hours_per_day, dependencies, no_of_revision_days = 2):
466
+ if current_dayNumber == 1:
467
+ return current_roadmap
468
+ current_roadmap = copy.deepcopy(current_roadmap)
469
+ day_index = current_dayNumber-2
470
+
471
+ # Check if a test exists in any specified day
472
+ for day in current_roadmap['schedule']:
473
+ if 'test_portion' in day:
474
+ test_index = current_roadmap['schedule'].index(day)
475
+ if test_index > (current_dayNumber-1):
476
+ time_to_test = test_index - (current_dayNumber-1)
477
+ test_portions = day['test_portion']
478
+ break
479
+ else:
480
+ test_index = None
481
+ break
482
+
483
+
484
+ extra_rev_days = max(no_of_revision_days - 2, 0)
485
+
486
+ # Determine scheduling strategy based on time to test
487
+ if test_index is not None:
488
+ if 30 >= time_to_test > 25:
489
+ # Far from test: Normal scheduling with backlog reduction
490
+ before_checkpoint = current_roadmap['schedule'][day_index:day_index+(time_to_test-25)]
491
+ after_checkpoint = current_roadmap['schedule'][day_index+(time_to_test-25):]
492
+ max_hours_per_day = 16
493
+ ratio = (80, 20)
494
+ test_portions = None
495
+ dependencies = None
496
+ elif 25 >= time_to_test > (10 + extra_rev_days):
497
+ # Mid-range: focus on current coursework
498
+ before_checkpoint = current_roadmap['schedule'][day_index:day_index+(time_to_test-(10+extra_rev_days))]
499
+ after_checkpoint = current_roadmap['schedule'][day_index+(time_to_test-(10+extra_rev_days)):]
500
+ max_hours_per_day = 16
501
+ ratio = (80, 20)
502
+ test_portions = None
503
+ dependencies = None
504
+ elif (10 + extra_rev_days) >= time_to_test > no_of_revision_days:
505
+ # Approaching test: Balance current work with test preparation
506
+ before_checkpoint = current_roadmap['schedule'][day_index:day_index+(time_to_test-no_of_revision_days)]
507
+ after_checkpoint = current_roadmap['schedule'][day_index+(time_to_test-no_of_revision_days):]
508
+ max_hours_per_day = 16
509
+ ratio = (50, 50)
510
+ elif 0 < time_to_test <= no_of_revision_days:
511
+ # Final revision period: Focus entirely on test preparation
512
+ before_checkpoint = current_roadmap['schedule'][day_index:test_index]
513
+ after_checkpoint = current_roadmap['schedule'][test_index:]
514
+ max_hours_per_day = 16
515
+ ratio = (0, 100)
516
+ else:
517
+ # No upcoming test: Normal scheduling
518
+ if day_index + 4 <= len(current_roadmap['schedule']):
519
+ before_checkpoint = current_roadmap['schedule'][day_index:day_index+4]
520
+ after_checkpoint = current_roadmap['schedule'][day_index+4:]
521
+ else:
522
+ print("Helloo")
523
+ before_checkpoint = current_roadmap['schedule'][day_index:]
524
+ after_checkpoint = []
525
+ ratio = (80, 20)
526
+ test_portions = None
527
+ dependencies = None
528
+ new_roadmap, extra_day_tasks = shift_the_roadmap(before_checkpoint,
529
+ max_hours_per_day,
530
+ ratio,
531
+ dependencies,
532
+ test_portions)
533
+ for day in new_roadmap:
534
+ new_date = day["date"]
535
+
536
+ for idx, existing_day in enumerate(current_roadmap['schedule']):
537
+ if existing_day['date'] == new_date:
538
+ current_roadmap['schedule'][idx] = day
539
+ ckp_idx = idx
540
+ break
541
+ if extra_day_tasks:
542
+ for day in extra_day_tasks:
543
+ for subject in day["subjects"]:
544
+ for task in subject['tasks']:
545
+ task["Critical_Notification"] = "Unable to schedule - Too many backlogs"
546
+
547
+ num_extra_days = len(extra_day_tasks)
548
+ if test_index is not None:
549
+ if 30 >= time_to_test > (10 + extra_rev_days):
550
+ new_checkpoint = copy.deepcopy(after_checkpoint)
551
+ day = copy.deepcopy(after_checkpoint[0])
552
+ for subject in day['subjects']:
553
+ sub_name = subject["name"]
554
+ subject['tasks'] = [
555
+ task for day in extra_day_tasks
556
+ for subj in day["subjects"]
557
+ if subj["name"] == sub_name
558
+ for task in subj["tasks"]
559
+ ]
560
+ day["dayNumber"] = new_checkpoint[0]["dayNumber"] - 1
561
+ day["date"] = (datetime.strptime(new_checkpoint[0]["date"], "%Y-%m-%d")
562
+ - timedelta(days=1)).strftime("%Y-%m-%d")
563
+ new_checkpoint.insert(0, day)
564
+ curr_roadmap, extra_days = shift_the_roadmap(roadmap=new_checkpoint,
565
+ max_hours_per_day = max_hours_per_day,
566
+ ratio = ratio,
567
+ dependencies = dependencies,
568
+ test_portions = test_portions)
569
+ new_roadmap = current_roadmap['schedule'][:ckp_idx+1]
570
+ new_roadmap.extend(curr_roadmap[1:])
571
+ current_roadmap['schedule'] = new_roadmap
572
+ elif 0 < time_to_test <= (10 + extra_rev_days):
573
+ # Step 1: Add empty days at the end
574
+ last_day = current_roadmap['schedule'][-1]
575
+ last_date = datetime.strptime(last_day["date"], "%Y-%m-%d")
576
+ last_day_number = last_day["dayNumber"]
577
+ for i in range(num_extra_days):
578
+ new_day = {
579
+ "dayNumber": last_day_number + i + 1,
580
+ "date": (last_date + timedelta(days=i + 1)).strftime("%Y-%m-%d"),
581
+ "subjects": []
582
+ }
583
+ current_roadmap['schedule'].append(new_day)
584
+
585
+ # Step 2: Shift 'subject' key from test_index to end in reverse order
586
+ total_days = len(current_roadmap['schedule'])
587
+ for i in range(total_days - num_extra_days - 1, test_index - 1, -1):
588
+ from_day = current_roadmap['schedule'][i]
589
+ to_day = current_roadmap['schedule'][i + num_extra_days]
590
+
591
+ to_day["subjects"] = from_day["subjects"]
592
+
593
+ # Step 3: Insert the extra_day_tasks into the cleared slots starting at test_index
594
+ for i, new_task_day in enumerate(extra_day_tasks):
595
+ target_day = current_roadmap['schedule'][test_index + i]
596
+ target_day["subjects"] = new_task_day["subjects"]
597
+
598
+ else:
599
+ if day_index + 4 <= len(current_roadmap['schedule']):
600
+ new_checkpoint = copy.deepcopy(after_checkpoint)
601
+ day = copy.deepcopy(after_checkpoint[0])
602
+ for subject in day['subjects']:
603
+ sub_name = subject["name"]
604
+ subject['tasks'] = [
605
+ task for day in extra_day_tasks
606
+ for subj in day["subjects"]
607
+ if subj["name"] == sub_name
608
+ for task in subj["tasks"]
609
+ ]
610
+
611
+ day["dayNumber"] = new_checkpoint[0]["dayNumber"] - 1
612
+ day["date"] = (datetime.strptime(new_checkpoint[0]["date"], "%Y-%m-%d")
613
+ - timedelta(days=1)).strftime("%Y-%m-%d")
614
+ new_checkpoint.insert(0, day)
615
+ curr_roadmap, extra_days = shift_the_roadmap(roadmap=new_checkpoint,
616
+ max_hours_per_day = max_hours_per_day,
617
+ ratio = ratio,
618
+ dependencies = dependencies,
619
+ test_portions = test_portions)
620
+ new_roadmap = current_roadmap['schedule'][:ckp_idx+1]
621
+ new_roadmap.extend(curr_roadmap[1:])
622
+ current_roadmap['schedule'] = new_roadmap
623
+ else:
624
+ for tasks in extra_day_tasks:
625
+ day = copy.deepcopy(new_roadmap[-1])
626
+ day["dayNumber"] = current_roadmap['schedule'][-1]["dayNumber"] + 1
627
+ day["date"] = (datetime.strptime(current_roadmap['schedule'][-1]["date"], "%Y-%m-%d")
628
+ + timedelta(days=1)).strftime("%Y-%m-%d")
629
+ day['subjects'] = tasks['subjects']
630
+ current_roadmap['schedule'].append(day)
631
 
632
  with open("current_roadmap.json", "w") as f:
633
+ json.dump(current_roadmap, f, indent=4)
634
 
635
+ session_state['updated_roadmap'] = current_roadmap
636
 
637
  # AGENT 2
638
  def generate_sql_for_report(llm, prompt):
 
1188
  return {"message": "No report data available."}
1189
 
1190
 
1191
+ @app.get("/testscheduler")
1192
+ def testscheduler(
1193
+ date : str = Query("2025-02-23", description="Enter the date to schedule the test"),
1194
+ physics : list = Query(["Properties of Solids and Liquids"], description="Enter the chapters for test in physics as a list"),
1195
+ chemistry : list = Query(["Equilibrium"], description="Enter the chapters for test in chemistry as a list"),
1196
+ maths : list = Query(["Limits,Continuity and Differentiability"], description="Enter the chapters for test in maths as a list"),
1197
+ ):
1198
+ """
1199
+ Helps in Scheduling of Tests in the roadmap
1200
+ """
1201
+
1202
+ session_state["data"] = add_test(session_state["data"], date, physics, chemistry, maths)
1203
+
1204
+ return {"sucessful": "Test Succesfully Scheduled in the roadmap"}
1205
+
1206
  # --- AGENT 2: Roadmap Manager (Roadmap Manager Page) ---
1207
  @app.get("/agent2")
1208
  def agent2(
1209
+ current_dayNumber: int = Query(2, description="Today's day number for rescheduling tasks"),
1210
+ max_hours_per_day: int = Query(8, description="Maximum number of hours per day"),
1211
+ no_of_revision_days: int = Query(2, description="Number of days needed for revision in case of a test")
1212
  ):
1213
  """
1214
  Agent 2 - Roadmap Manager: Processes tasks and optimizes the roadmap based on user input.
 
1218
  return {"error": "Roadmap data not loaded. Load data first."}
1219
 
1220
  # Optimize task distribution with user input
1221
+ update_roadmap(current_roadmap = session_state["data"],
1222
+ current_dayNumber = current_dayNumber,
1223
+ max_hours_per_day = max_hours_per_day,
1224
+ dependencies = session_state["dependencies"],
1225
+ no_of_revision_days = no_of_revision_days)
 
1226
 
1227
  # Return full updated roadmap
1228
  if session_state["data"] and session_state["updated_roadmap"]:
dependencies.json ADDED
@@ -0,0 +1,178 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "Physics": {
3
+ "Physics and Measurement": [],
4
+ "Kinematics": [
5
+ "Physics and Measurement"
6
+ ],
7
+ "Laws of Motion": [
8
+ "Kinematics"
9
+ ],
10
+ "Work, Energy and Power": [
11
+ "Laws of Motion",
12
+ "Kinematics"
13
+ ],
14
+ "Rotational Motion": [
15
+ "Work, Energy and Power",
16
+ "Laws of Motion",
17
+ "Kinematics"
18
+ ],
19
+ "Gravitation": [
20
+ "Laws of Motion",
21
+ "Work, Energy and Power"
22
+ ],
23
+ "Properties of Solids and Liquids": [
24
+ "Rotational Motion"
25
+ ],
26
+ "Thermodynamics": [
27
+ "Properties of Solids and Liquids"
28
+ ],
29
+ "Kinetic Theory of Gases": [
30
+ "Thermodynamics"
31
+ ],
32
+ "Oscillations and Waves": [
33
+ "Work, Energy and Power",
34
+ "Kinematics"
35
+ ],
36
+ "Electrostatics": [
37
+ "Physics and Measurement",
38
+ "Vector Algebra"
39
+ ],
40
+ "Current Electricity": [
41
+ "Electrostatics"
42
+ ],
43
+ "Magnetic Effects of Current and Magnetism": [
44
+ "Current Electricity"
45
+ ],
46
+ "Electromagnetic Induction and Alternating Currents": [
47
+ "Magnetic Effects of Current and Magnetism",
48
+ "Current Electricity"
49
+ ],
50
+ "Optics": [
51
+ "Electrostatics",
52
+ "Wave Optics Basics"
53
+ ],
54
+ "Dual Nature of Matter and Radiation": [
55
+ "Optics",
56
+ "Kinetic Theory of Gases"
57
+ ],
58
+ "Atoms and Nuclei": [
59
+ "Physics and Measurement"
60
+ ],
61
+ "Electronic Devices": [
62
+ "Current Electricity",
63
+ "Electromagnetic Induction and Alternating Currents"
64
+ ],
65
+ "Communication Systems": [
66
+ "Electronic Devices"
67
+ ]
68
+ },
69
+ "Chemistry": {
70
+ "Some Basic Concepts in Chemistry": [],
71
+ "Atomic Structure": [
72
+ "Some Basic Concepts in Chemistry"
73
+ ],
74
+ "Chemical Bonding and Molecular Structure": [
75
+ "Atomic Structure"
76
+ ],
77
+ "Chemical Thermodynamics": [
78
+ "Some Basic Concepts in Chemistry",
79
+ "Atomic Structure"
80
+ ],
81
+ "Solutions": [
82
+ "Some Basic Concepts in Chemistry",
83
+ "Chemical Thermodynamics"
84
+ ],
85
+ "Equilibrium": [
86
+ "Chemical Thermodynamics",
87
+ "Solutions"
88
+ ],
89
+ "Redox Reactions and Electrochemistry": [
90
+ "Chemical Thermodynamics",
91
+ "Equilibrium"
92
+ ],
93
+ "Chemical Kinetics": [
94
+ "Chemical Thermodynamics",
95
+ "Equilibrium"
96
+ ],
97
+ "Classification of Elements and Periodicity in Properties": [
98
+ "Atomic Structure"
99
+ ],
100
+ "P Block Elements": [
101
+ "Classification of Elements and Periodicity in Properties"
102
+ ],
103
+ "D and F Block Elements": [
104
+ "Classification of Elements and Periodicity in Properties"
105
+ ],
106
+ "Coordination Compounds": [
107
+ "Chemical Bonding and Molecular Structure",
108
+ "P Block Elements",
109
+ "D and F Block Elements"
110
+ ],
111
+ "Purification and Characteristics of Organic Compounds": [],
112
+ "Some Basic Principles of Organic Chemistry": [],
113
+ "Hydrocarbons": [
114
+ "Some Basic Principles of Organic Chemistry"
115
+ ],
116
+ "Organic Compounds Containing Halogens": [
117
+ "Some Basic Principles of Organic Chemistry",
118
+ "Hydrocarbons"
119
+ ],
120
+ "Organic Compounds Containing Oxygen": [
121
+ "Some Basic Principles of Organic Chemistry",
122
+ "Hydrocarbons"
123
+ ],
124
+ "Organic Compounds Containing Nitrogen": [
125
+ "Some Basic Principles of Organic Chemistry",
126
+ "Hydrocarbons"
127
+ ],
128
+ "Biomolecules": [
129
+ "Organic Compounds Containing Oxygen",
130
+ "Organic Compounds Containing Nitrogen"
131
+ ]
132
+ },
133
+ "Maths": {
134
+ "Sets, Relations and Functions": [],
135
+ "Complex Numbers and Quadratic Equations": [
136
+ "Sets, Relations and Functions"
137
+ ],
138
+ "Matrices and Determinants": [
139
+ "Sets, Relations and Functions"
140
+ ],
141
+ "Permutations and Combinations": [
142
+ "Sets, Relations and Functions"
143
+ ],
144
+ "Binomial Theorem and its Simple Application": [
145
+ "Sets, Relations and Functions"
146
+ ],
147
+ "Sequence and Series": [
148
+ "Sets, Relations and Functions"
149
+ ],
150
+ "Limits, Continuity and Differentiability": [
151
+ "Sets, Relations and Functions",
152
+ "Binomial Theorem and its Simple Application"
153
+ ],
154
+ "Integral Calculus": [
155
+ "Limits, Continuity and Differentiability"
156
+ ],
157
+ "Differential Equations": [
158
+ "Integral Calculus",
159
+ "Limits, Continuity and Differentiability"
160
+ ],
161
+ "Co-ordinate Geometry": [
162
+ "Sets, Relations and Functions"
163
+ ],
164
+ "Three Dimensional Geometry": [
165
+ "Co-ordinate Geometry"
166
+ ],
167
+ "Vector Algebra": [
168
+ "Co-ordinate Geometry"
169
+ ],
170
+ "Statistics and Probability": [
171
+ "Sets, Relations and Functions"
172
+ ],
173
+ "Trigonometry": [
174
+ "Sets, Relations and Functions",
175
+ "Limits, Continuity and Differentiability"
176
+ ]
177
+ }
178
+ }