11import calendar
22import datetime
3+ from clr import Convert
4+ from System import DateTime
35
46def get_events (dt ):
57 return q .QuerySql ('''
@@ -16,7 +18,9 @@ def get_events(dt):
1618 ORDER BY MeetingDate, o.DivisionId
1719''' .format (dt ))
1820
19- def generate_calendar_html (year , month ):
21+ def generate_calendar_html (date ):
22+ year = date .year
23+ month = date .month
2024 cal = calendar .Calendar (firstweekday = 6 ) # Sunday start
2125 month_days = cal .monthdatescalendar (year , month )
2226
@@ -33,8 +37,9 @@ def generate_calendar_html(year, month):
3337 next_month = 1
3438 next_year += 1
3539
36- prev_link = "?year=%d&month=%d" % (prev_year , prev_month )
37- next_link = "?year=%d&month=%d" % (next_year , next_month )
40+ prev_link = "?d=%d-%d" % (prev_year , prev_month )
41+ next_link = "?d=%d-%d" % (next_year , next_month )
42+ week_link = "?v=w&d=%d-%d-1" % (date .year , date .month )
3843
3944 html = []
4045 html .append ("<style>" )
@@ -44,17 +49,27 @@ def generate_calendar_html(year, month):
4449 html .append ("th { background: #eee; }" )
4550 html .append (".daynum { font-weight: bold; }" )
4651 html .append (".event { margin: 2px 0; padding: 2px; background: #def; border-radius: 3px; }" )
52+ html .append (".nav { display: flex; gap: 1em; }" )
53+ html .append (".nav-l, .nav-r { flex:1; }" )
54+ html .append (".nav-r { text-align:right; }" )
4755 html .append ("</style>" )
4856 # html.append("<h2>%s %d</h2>" % (calendar.month_name[month], year))
4957
5058 model .Title = "Calendar: %s %d" % (calendar .month_name [month ], year )
5159 model .Header = "%s %d" % (calendar .month_name [month ], year )
5260
5361 html .append ("<div class='nav'>" )
62+ html .append ("<div class='nav-l'>" )
5463 html .append ("<a href='%s'>« %s %d</a> | " % (prev_link , calendar .month_name [prev_month ], prev_year ))
5564 html .append ("<strong>%s %d</strong>" % (calendar .month_name [month ], year ))
5665 html .append (" | <a href='%s'>%s %d »</a>" % (next_link , calendar .month_name [next_month ], next_year ))
5766 html .append ("</div>" )
67+
68+ html .append ("<div class='nav-r'>" )
69+ html .append ("<strong>Month</strong>" )
70+ html .append (" | <a href='%s'>Week</a>" % (week_link ))
71+ html .append ("</div>" )
72+ html .append ("</div>" )
5873
5974
6075 html .append ("<table>" )
@@ -79,7 +94,7 @@ def generate_calendar_html(year, month):
7994 if ev .Featured :
8095 classes .append ('feat' )
8196
82- # html.append("<a href='/Meeting/%s'>" % ev.MeetingId)
97+ html .append ("<a href='/Meeting/%s'>" % ev .MeetingId )
8398
8499 html .append ("<div class='%s'>" % ' ' .join (classes ))
85100
@@ -89,31 +104,170 @@ def generate_calendar_html(year, month):
89104 ev .MeetingDate .ToString ("h:mmtt" ) if ev .MeetingEnd is None else ev .MeetingEnd .ToString ("h:mmtt" )
90105 )).lower ().replace (":00" ,"" ))
91106
92- html .append ("</div>" )
93- # html.append("</a>")
107+ html .append ("</div></a>" )
94108 html .append ("</td>" )
95109 html .append ("</tr>" )
96110
97111 html .append ("</table>" )
98112 return "\n " .join (html )
99113
100114
115+ def generate_calendar_week_html (start_date ):
116+ """
117+ Generate an HTML week view calendar with overlap handling,
118+ horizontal hour/half-hour lines, scrollable, and auto-scroll to 8am.
119+ """
120+ start_of_week = start_date - datetime .timedelta (days = start_date .weekday () + 1 if start_date .weekday () != 6 else 0 )
121+ days = [start_of_week + datetime .timedelta (days = i ) for i in range (7 )]
122+
123+ hour_height = 60 # pixels per hour (so 30 min = 30px)
124+ day_start = 0
125+ day_end = 24
126+
127+ prev_date = start_date - datetime .timedelta (days = 7 )
128+ next_date = start_date + datetime .timedelta (days = 7 )
129+
130+ prev_link = "?v=w&d=%d-%d-%d" % (prev_date .year , prev_date .month , prev_date .day )
131+ next_link = "?v=w&d=%d-%d-%d" % (next_date .year , next_date .month , next_date .day )
132+ month_link = "?d=%d-%d-%d" % (next_date .year , next_date .month , next_date .day )
133+
134+ html = []
135+ html .append ("<style>" )
136+ html .append ("div#main { width: 100% !important;}" )
137+ html .append (".week-container { height: calc(100vh - 10em); overflow-y: scroll; border: 1px solid #999; }" )
138+ html .append (".week { display: flex; min-height: %dpx; position: relative; padding-left:3em; }" % ((day_end - day_start ) * hour_height ))
139+ html .append (".daycol { flex: 1; border-left: 1px solid #999; position: relative; }" )
140+ html .append (".dayheader { background: #eee; text-align: center; padding: 4px; font-weight: bold; position: sticky; top: 0; z-index: 2; }" )
141+ html .append (".event { position: absolute; background: #def; border: 1px solid #69c; border-radius: 3px; padding: 2px; font-size: 0.85em; overflow: hidden; }" )
142+ html .append (".feat { font-size: 1.1em; font-weight: bold; }" )
143+ html .append (".timegrid { position: absolute; left: 0; right: 0; border-top: 1px dashed #ccc; font-size: 0.7em; color: #666; }" )
144+ html .append (".nav { display: flex; gap: 1em; }" )
145+ html .append (".nav-l, .nav-r { flex:1; }" )
146+ html .append (".nav-c { text-align:center; }" )
147+ html .append (".nav-r { text-align:right; }" )
148+ html .append ("</style>" )
149+
150+ # JavaScript to scroll to 8am
151+ html .append ("<script>" )
152+ html .append ("window.addEventListener('load', function(){" )
153+ html .append (" var container = document.querySelector('.week-container');" )
154+ html .append (" if(container){ container.scrollTop = %d; }" % (8 * hour_height ))
155+ html .append ("});" )
156+ html .append ("</script>" )
157+
158+ html .append ("<div class='nav'>" )
159+
160+ html .append ("<div class='nav-l'>" )
161+ html .append ("<a href='%s'>« Previous Week</a> | " % prev_link )
162+ html .append ("<a href='%s'>Next Week »</a>" % next_link )
163+ html .append ("</div>" )
164+
165+ html .append ("<div class='nav-c'>" )
166+ html .append ("<strong>Week of %s %d, %s</strong>" % (calendar .month_name [start_date .month ], start_date .day , start_date .year ))
167+ html .append ("</div>" )
168+
169+ html .append ("<div class='nav-r'>" )
170+ html .append ("<a href='%s'>Month</a>" % (month_link ))
171+ html .append (" | <strong>Week</strong>" )
172+ html .append ("</div>" )
173+
174+ html .append ("</div>" )
175+
176+ html .append ("<div class='week-container'>" )
177+ html .append ("<div class='week'>" )
178+
179+ # Time grid lines (shared across all days)
180+ for h in range (day_start , day_end + 1 ):
181+ top = (h - day_start ) * hour_height
182+ html .append ("<div class='timegrid' style='top:%dpx'>%02d:00</div>" % (top , h ))
183+ # half-hour line
184+ if h < day_end :
185+ html .append ("<div class='timegrid' style='top:%dpx'></div>" % (top + hour_height // 2 ))
186+
187+ for day in days :
188+ events = get_events (day )
189+ events = sorted (events , key = lambda e : (e .MeetingDate , e .MeetingEnd or e .MeetingDate ))
190+
191+ day_c = Convert (day , DateTime )
192+ tom_c = day_c .AddDays (1 )
193+
194+ # Allocate lanes for overlaps
195+ lanes = []
196+ event_positions = {}
197+ for ev in events :
198+ placed = False
199+ for lane_index , lane in enumerate (lanes ):
200+ last = lane [- 1 ]
201+ if last .MeetingEnd <= ev .MeetingDate :
202+ lane .append (ev )
203+ event_positions [ev .MeetingId ] = lane_index
204+ placed = True
205+ break
206+ if not placed :
207+ lanes .append ([ev ])
208+ event_positions [ev .MeetingId ] = len (lanes ) - 1
209+ lane_count = max (1 , len (lanes ))
210+
211+ html .append ("<div class='daycol'>" )
212+ html .append ("<div class='dayheader'>%s<br>%d</div>" % (calendar .day_abbr [day .weekday ()], day .day ))
213+
214+ for ev in events :
215+ start_hour = 0 if ev .MeetingDate < day_c else ev .MeetingDate .Hour + ev .MeetingDate .Minute / 60.0
216+ end_hour = 24 if ev .MeetingEnd > tom_c else (ev .MeetingEnd .Hour + ev .MeetingEnd .Minute / 60.0 ) if ev .MeetingEnd else (start_hour + 1 )
217+
218+ if start_hour < day_start : start_hour = day_start
219+ if end_hour > day_end : end_hour = day_end
220+
221+ top = (start_hour - day_start ) * hour_height
222+ height = (end_hour - start_hour ) * hour_height
223+
224+ lane_index = event_positions [ev .MeetingId ]
225+ width = 100.0 / lane_count
226+ left = lane_index * width
227+
228+ classes = ['event' ]
229+ if ev .Featured :
230+ classes .append ('feat' )
231+
232+ html .append ("<a href='/Meeting/%s'>" % ev .MeetingId )
233+ html .append ("<div class='%s' style='top:%dpx; height:%dpx; left:%.2f%%; width:%.2f%%'>" % (
234+ ' ' .join (classes ), top , height , left , width ))
235+ html .append ("%s<br><small>%s - %s</small>" % (
236+ ev .MeetingName ,
237+ ev .MeetingDate .ToString ("h:mmtt" ).lower ().replace (":00" ,"" ),
238+ ev .MeetingEnd .ToString ("h:mmtt" ).lower ().replace (":00" ,"" ) if ev .MeetingEnd else ""
239+ ))
240+ html .append ("</div></a>" )
241+
242+ html .append ("</div>" ) # end daycol
243+
244+ html .append ("</div></div>" ) # end week and container
245+ return "\n " .join (html )
246+
247+
248+
249+
101250if True :
102251
103- today = datetime .date .today ()
104- year = today .year
105- month = today .month
106-
107- if Data .year != '' :
108- try :
109- year = int (Data .year )
110- except :
111- pass
112- if Data .month != '' :
113- try :
114- month = int (Data .month )
115- except :
116- pass
117-
118- html_calendar = generate_calendar_html (year , month )
119- print (html_calendar )
252+ d = datetime .date .today ()
253+
254+ if Data .d != '' :
255+ d = Data .d .split ('-' )
256+ if len (d ) == 2 :
257+ try :
258+ d = datetime .date (int (d [0 ]), int (d [1 ]), 1 )
259+ except :
260+ pass
261+ elif len (d ) == 3 :
262+ try :
263+ d = datetime .date (int (d [0 ]), int (d [1 ]), int (d [2 ]))
264+ except :
265+ pass
266+
267+ if Data .v == 'w' :
268+ print generate_calendar_week_html (d )
269+
270+ else :
271+ print generate_calendar_html (d )
272+
273+ #
0 commit comments