svv/cal.py
changeset 46 547940cb0e1c
parent 42 fa5694ee0f98
child 47 d79a560af791
--- a/svv/cal.py	Sun Jan 09 15:52:28 2011 +0200
+++ b/svv/cal.py	Sun Jan 09 17:49:15 2011 +0200
@@ -118,7 +118,7 @@
 
     def get_events_for_interval (self, start, end) :
         """
-            Returns list of Order objects for given interval, ordered by start time.
+            Return full list of all Orders taking place during the given datetime interval, ordered by start time
         """
 
         # XXX: bad imports
@@ -130,6 +130,37 @@
             |   (db.between(start, Order.event_start, Order.event_end))
         ).order_by(Order.event_start).all()
 
+    def interval_for_dates (self, dates) :
+        """
+            Returns a datetime (start, end) interval encompassing all of the given dates
+        """
+        
+        # a day is 00:00:00 - 23:59:59
+        start = datetime.datetime.combine(min(dates), datetime.time(0, 0, 0))
+        end = datetime.datetime.combine(max(dates), datetime.time(23, 59, 59))
+        
+        return start, end
+
+    def hours_from_day_start (self, dt) :
+        """
+            Return the number of hours from the start of the day to the given datetime, as a float.
+        """
+
+        HOUR = float(60 * 60)
+
+        # dt - 00:00:00
+        return (dt - dt.replace(hour=0, minute=0, second=0)).seconds / HOUR
+
+    def hours_to_day_end (self, dt) :
+        """
+            Return the number of hours from the given datetime to the day's end, as a float.
+        """
+
+        HOUR = float(60 * 60)
+        
+        # 23:59:59 - dt
+        return (dt.replace(hour=23, minute=59, second=59) - dt).seconds / HOUR
+
     def render_week (self, month, week) :
         """
             Render day rows for given week.
@@ -138,50 +169,74 @@
         # XXX: nasty
         from svv import urls
 
-        # load events for week
-        week_start = datetime.datetime.combine(min(week), datetime.time(0, 0, 0))
-        week_end = datetime.datetime.combine(max(week), datetime.time(23, 59, 59))
-
+        # week as datetime interval
+        week_start, week_end = self.interval_for_dates(week)
+        
+        # all orders for week
         orders = self.get_events_for_interval(week_start, week_end)
 
-        log.debug("Render week %r -> %r: %d", week_start, week_end, len(orders))
-        
         # day headers
         yield tags.tr(class_='week-header')(
             self.render_day_header(month, date) for date in week
         )
 
-        # each even on its own row for now
+        # each event on its own row for now
         for order in orders :
             # start/end date for this week
-            start = min(date for date in week if order.on_date(date))
-            end = max(date for date in week if order.on_date(date))
+            start_day = min(date for date in week if order.on_date(date))
+            end_day = max(date for date in week if order.on_date(date))
             
             # as vector into week
-            leading = (start - min(week)).days
-            length = (end - start).days + 1
-            trailing = (max(week) - end).days
+            leading_days = (start_day - min(week)).days
+            length_days = (end_day - start_day).days + 1
+            trailing_days = (max(week) - end_day).days
 
-            # continues prev/next?
-            prev = (start > order.event_start.date())
-            next = (end < order.event_end.date())
+            # continues prev/next week?
+            prev_week = (start_day > order.event_start.date())
+            next_week = (end_day < order.event_end.date())
 
-            log.debug("Event %r from %r -> %r", order.event_name, start, end)
+            log.debug("Event %r from %r -> %r", order.event_name, start_day, end_day)
+
+            # spec class
+            classes = ' '.join(cls for cls in (
+                'event',
+                'continues-prev' if prev_week else None,
+                'continues-next' if next_week else None,
+            ) if cls)
+
+            # compute dynamic styles
+            styles = []
+
+            # width of each hour in the event's span this week, as a percentage
+            hour_width = 100.0 / (length_days * 24)
+
+            # margin for start hour offset
+            if not prev_week :
+                # starts on this week, so calc how many hours into day
+                styles.append('margin-left: %d%%' % int(hour_width * self.hours_from_day_start(order.event_start)))
+            
+            # margin for end hour offset
+            if not next_week :
+                # ends on this week, se calc how many hours until day end
+                styles.append('margin-right: %d%%' % int(hour_width * self.hours_to_day_end(order.event_end)))
+            
+            # style spec
+            if styles :
+                style = '; '.join(styles)
+
+            else :
+                style = None
 
             yield tags.tr(class_='week-data')(
-                [tags.td("")] * leading,
-                tags.td(colspan=length, class_=(' '.join(cls for cls in (
-                    'event',
-                    'continues-prev' if prev else None,
-                    'continues-next' if next else None,
-                ) if cls)))(
-                    tags.a(href=self.url_for(urls.OrderView, id=order.id))(
-                        tags.div(class_='arrow-left')("") if prev else None,
+                tags.td("", colspan=leading_days) if leading_days else None,
+                tags.td(colspan=length_days, class_=classes)(
+                    tags.a(href=self.url_for(urls.OrderView, id=order.id), style=style)(
+                        tags.div(class_='arrow-left')("") if prev_week else None,
                         order.event_name,
-                        tags.div(class_='arrow-right')("") if next else None,
+                        tags.div(class_='arrow-right')("") if next_week else None,
                     )
                 ),
-                [tags.td("")] * trailing,
+                tags.td("", colspan=trailing_days) if trailing_days else None,
             )
 
     def render_calendar (self, month) :