1212from utils import (
1313 END_TERM_BEGIN ,
1414 SEM_BEGIN ,
15- GYFT_RECUR_STRS ,
16- get_rfc_time ,
17- hdays ,
1815 holidays ,
16+ dates ,
17+ generate_india_time ,
1918)
19+ from utils import academic_calander_handler
2020from timetable import Course
2121
2222DEBUG = False
@@ -53,7 +53,7 @@ def get_credentials() -> client.Credentials:
5353 return credentials
5454
5555
56- def create_calendar (courses : list [Course ]) -> None :
56+ def create_calendar (courses : list [Course ], is_web : bool = False ) -> None :
5757 r"""
5858 Adds courses to Google Calendar
5959 Args:
@@ -62,77 +62,103 @@ def create_calendar(courses: list[Course]) -> None:
6262 credentials = get_credentials ()
6363 http = credentials .authorize (httplib2 .Http ())
6464 service = discovery .build ("calendar" , "v3" , http = http )
65+
66+ # Helper: get or create the dedicated 'IIT KGP' calendar
67+ def get_or_create_calendar_id (name : str = "IIT KGP" , tz : str = "Asia/Kolkata" ) -> str :
68+ page_token = None
69+ while True :
70+ cl = service .calendarList ().list (pageToken = page_token , maxResults = 250 ).execute ()
71+ for item in cl .get ("items" , []):
72+ if item .get ("summary" ) == name and item .get ("accessRole" ) in ("owner" , "writer" ):
73+ return item ["id" ]
74+ page_token = cl .get ("nextPageToken" )
75+ if not page_token :
76+ break
77+ # Create if not found
78+ body = {"summary" : name , "timeZone" : tz }
79+ created = service .calendars ().insert (body = body ).execute ()
80+ return created ["id" ]
81+
82+ calendar_id = get_or_create_calendar_id ()
6583 batch = service .new_batch_http_request () # To add events in a batch
84+
85+ # Build exhaustive class-off dates once
86+ class_off_days = dates .get_class_off_dates_in_semester ()
87+
88+ # Helper: first occurrence of the course weekday on/after SEM_BEGIN
89+ def first_occurrence_of_day (day_name : str ):
90+ return dates .next_weekday (SEM_BEGIN , day_name )
91+
6692 for course in courses :
93+ first_day = first_occurrence_of_day (course .day )
94+ lecture_begin_dt = generate_india_time (
95+ first_day .year , first_day .month , first_day .day , course .start_time , 0
96+ )
97+ lecture_end_dt = lecture_begin_dt + timedelta (hours = course .duration )
98+
99+ # Build EXDATEs at the lecture start time for class-off days matching the course weekday
100+ exdate_stamps : list [str ] = []
101+ for off_day in class_off_days :
102+ if off_day .weekday () == first_day .weekday ():
103+ ex_dt = generate_india_time (
104+ off_day .year , off_day .month , off_day .day , course .start_time , 0
105+ )
106+ if lecture_begin_dt <= ex_dt < END_TERM_BEGIN :
107+ exdate_stamps .append (ex_dt .strftime ("%Y%m%dT%H%M%S" ))
108+
67109 event = {
68110 "summary" : course .title ,
69111 "location" : course .get_location (),
112+ "description" : course .code ,
70113 "start" : {
71- "dateTime" : get_rfc_time ( course . start_time , course . day )[: - 7 ] ,
114+ "dateTime" : lecture_begin_dt . strftime ( "%Y-%m-%dT%H:%M:%S" ) ,
72115 "timeZone" : "Asia/Kolkata" ,
73116 },
74117 "end" : {
75- "dateTime" : get_rfc_time ( course . end_time , course . day )[: - 7 ] ,
118+ "dateTime" : lecture_end_dt . strftime ( "%Y-%m-%dT%H:%M:%S" ) ,
76119 "timeZone" : "Asia/Kolkata" ,
77120 },
78- }
79-
80- ### making a string to pass in exdate. Changed the time of the string to class start time
81- exdate_str_dict = defaultdict (str )
82-
83- for day in hdays [course .day ]:
84- exdate_str_dict [course .day ] += (
85- day .replace (hour = course .start_time ).strftime ("%Y%m%dT%H%M%S" ) + ","
86- )
87- if exdate_str_dict [course .day ] != None :
88- exdate_str_dict [course .day ] = exdate_str_dict [course .day ][:- 1 ]
89-
90- if (
91- exdate_str_dict [course .day ] != None
92- ): ## if holiday exists on this recurrence, skip it with exdate
93- event ["recurrence" ] = [
94- "EXDATE;TZID=Asia/Kolkata:{}" .format (exdate_str_dict [course .day ]),
121+ "recurrence" : [
122+ # Add EXDATE first if any
123+ * ( ["EXDATE;TZID=Asia/Kolkata:" + "," .join (exdate_stamps )] if exdate_stamps else [] ),
95124 "RRULE:FREQ=WEEKLY;UNTIL={}" .format (
96125 END_TERM_BEGIN .strftime ("%Y%m%dT000000Z" )
97126 ),
98- ]
99-
100- else :
101- event ["recurrence" ] = [
102- "RRULE:FREQ=WEEKLY;UNTIL={}" .format (
103- END_TERM_BEGIN .strftime ("%Y%m%dT000000Z" )
104- )
105- ]
127+ ],
128+ }
106129
107- batch .add (service .events ().insert (calendarId = "primary" , body = event ))
130+ batch .add (service .events ().insert (calendarId = calendar_id , body = event ))
108131 print ("Added " + event ["summary" ])
109-
110132 batch .execute () ## execute batch of timetable
111133
112- # add holidays to calender as events
134+ # add holidays to calendar as all-day events (Asia/Kolkata midnight)
113135 for holiday in holidays :
114- if (
115- holiday [1 ].date () >= date .today ()
116- and holiday [1 ].date () <= END_TERM_BEGIN .date ()
117- ):
118- holiday_event = {
119- "summary" : "INSTITUTE HOLIDAY : " + holiday [0 ],
120- "start" : {
121- "dateTime" : holiday [1 ].strftime ("%Y-%m-%dT00:00:00" ),
122- "timeZone" : "Asia/Kolkata" ,
123- },
124- "end" : {
125- "dateTime" : (holiday [1 ] + timedelta (days = 1 )).strftime (
126- "%Y-%m-%dT00:00:00"
127- ),
128- "timeZone" : "Asia/Kolkata" ,
129- },
130- }
131- insert = (
132- service .events ()
133- .insert (calendarId = "primary" , body = holiday_event )
134- .execute ()
135- )
136+ hdt = holiday [1 ]
137+ start_str = generate_india_time (hdt .year , hdt .month , hdt .day , 0 , 0 ).strftime ("%Y-%m-%dT00:00:00" )
138+ end_dt = generate_india_time (hdt .year , hdt .month , hdt .day , 0 , 0 ) + timedelta (days = 1 )
139+ holiday_event = {
140+ "summary" : "INSTITUTE HOLIDAY : " + holiday [0 ],
141+ "start" : {
142+ "dateTime" : start_str ,
143+ "timeZone" : "Asia/Kolkata" ,
144+ },
145+ "end" : {
146+ "dateTime" : end_dt .strftime ("%Y-%m-%dT00:00:00" ),
147+ "timeZone" : "Asia/Kolkata" ,
148+ },
149+ }
150+ service .events ().insert (calendarId = calendar_id , body = holiday_event ).execute ()
151+
152+ # add academic calendar entries as all-day events
153+ for entry in academic_calander_handler .get_academic_calendar (is_web ):
154+ start_str = entry .start_date .strftime ("%Y-%m-%dT00:00:00" )
155+ end_str = entry .end_date .strftime ("%Y-%m-%dT00:00:00" )
156+ event = {
157+ "summary" : entry .event ,
158+ "start" : {"dateTime" : start_str , "timeZone" : "Asia/Kolkata" },
159+ "end" : {"dateTime" : end_str , "timeZone" : "Asia/Kolkata" },
160+ }
161+ service .events ().insert (calendarId = calendar_id , body = event ).execute ()
136162
137163 print ("\n All events added successfully!\n " )
138164
@@ -144,28 +170,24 @@ def delete_calendar():
144170 credentials = get_credentials ()
145171 http = credentials .authorize (httplib2 .Http ())
146172 service = discovery .build ("calendar" , "v3" , http = http )
147- batch = service .new_batch_http_request () # To add events in a batch
148- print ("Getting the events" )
149- events_result = (
150- service .events ()
151- .list (
152- calendarId = "primary" ,
153- timeMin = SEM_BEGIN .strftime ("%Y-%m-%dT%H:%M:%S.%fZ" ),
154- singleEvents = False ,
155- timeMax = END_TERM_BEGIN .strftime ("%Y-%m-%dT%H:%M:%S.%fZ" ),
156- maxResults = 2500 ,
157- )
158- .execute ()
159- )
160- events = events_result .get ("items" , [])
161- if not events or len (events ) == 0 :
162- print ("No upcoming events found." )
173+
174+ # Find the IIT KGP calendar and delete it entirely
175+ target_name = "IIT KGP"
176+ page_token = None
177+ calendar_id = None
178+ while True :
179+ cl = service .calendarList ().list (pageToken = page_token , maxResults = 250 ).execute ()
180+ for item in cl .get ("items" , []):
181+ if item .get ("summary" ) == target_name and item .get ("accessRole" ) in ("owner" , "writer" ):
182+ calendar_id = item ["id" ]
183+ break
184+ if calendar_id or not cl .get ("nextPageToken" ):
185+ break
186+ page_token = cl .get ("nextPageToken" )
187+
188+ if not calendar_id :
189+ print (f"Calendar '{ target_name } ' not found. Nothing to delete." )
163190 return
164- for event in events :
165- if event .get ("recurrence" , "NoRecur" ) in GYFT_RECUR_STRS :
166- batch .add (
167- service .events ().delete (calendarId = "primary" , eventId = event ["id" ])
168- )
169- print ("Deleted: " , event ["summary" ], event ["start" ])
170- batch .execute ()
171- print ("Deletion done!" )
191+
192+ service .calendars ().delete (calendarId = calendar_id ).execute ()
193+ print (f"Calendar '{ target_name } ' deleted." )
0 commit comments