[hamster-applet] some more progress - adjusting start date when looking on week/month showing major ticks



commit add66f036aa2ff0c5d7433f14f085a84a14a8733
Author: Toms Bauģis <toms baugis gmail com>
Date:   Sun Dec 20 16:39:00 2009 +0000

    some more progress - adjusting start date when looking on week/month
    showing major ticks

 hamster/widgets/newtimeline.py |  251 +++++++++++++++++++++++++---------------
 1 files changed, 158 insertions(+), 93 deletions(-)
---
diff --git a/hamster/widgets/newtimeline.py b/hamster/widgets/newtimeline.py
index dfcd222..ba3fdd1 100644
--- a/hamster/widgets/newtimeline.py
+++ b/hamster/widgets/newtimeline.py
@@ -24,6 +24,8 @@ from .hamster import graphics, stuff
 from .hamster.configuration import GconfStore
 
 import datetime as dt
+import calendar
+
 from bisect import bisect, bisect_left
 
 class NewTimeLine(graphics.Area):
@@ -31,9 +33,13 @@ class NewTimeLine(graphics.Area):
     
     def __init__(self):
         graphics.Area.__init__(self)
-        self.start_date, self.end_date = None, None
+        self.start_time, self.end_time = None, None
         self.facts = []
         self.title = ""
+        self.day_start = GconfStore().get_day_start()
+        self.minor_tick = None
+        
+        self.tick_totals = {}
 
         
     def draw(self, facts, start_date, end_date):
@@ -41,24 +47,104 @@ class NewTimeLine(graphics.Area):
 
         self.set_title(start_date, end_date) # we will forget about all our magic manipulations for the title
 
+        # we will operate in minutes since and until the day start
+        start_time = dt.datetime.combine(start_date, self.day_start.replace(minute=0))
+        end_time = dt.datetime.combine(end_date, self.day_start.replace(minute=0)) + dt.timedelta(days = 1)
+
+        fact_start_time, fact_end_time = start_time, end_time
+        if facts:
+            fact_start_time = facts[0]["start_time"]
+            fact_end_time = facts[-1]["start_time"] + facts[-1]["delta"]
+
+        self.start_time = min([start_time, fact_start_time])
+        self.end_time = max([end_time, fact_end_time])
+
+        if self.start_time > self.end_time: # make sure end is after start
+            self.start_time, self.end_time = self.end_time, self.start_time
 
-        day_start = GconfStore().get_day_start()
 
-        start_date = dt.datetime.combine(start_date, day_start.replace(minute=0))
-        end_date = dt.datetime.combine(end_date, day_start.replace(minute=0)) + dt.timedelta(days = 1)
+        days = (self.end_time - self.start_time).days
+        
+
+        # determine fraction and do addittional start time move
+        if days > 300:
+            self.minor_tick = dt.timedelta(days = 30) #this is approximate and will be replaced by exact days in month
+            # make sure we start on first day of month
+            self.start_time = self.start_time - dt.timedelta(self.start_time.day - 1)
+
+        elif days > 100:
+            self.minor_tick = dt.timedelta(days = 7)
+            # make sure we start week on first day
+            #set to monday
+            self.start_time = self.start_time - dt.timedelta(self.start_time.weekday() + 1)
+            # look if we need to start on sunday or monday
+            self.start_time = self.start_time + dt.timedelta(stuff.locale_first_weekday())
+            
+        elif days > 2:
+            self.minor_tick = dt.timedelta(days = 1)
+        else:
+            self.minor_tick = dt.timedelta(seconds = 60 * 60)
 
 
-        self.start_date = min([start_date, facts[0]["start_time"]])
-        self.end_date = max([end_date, facts[-1]["start_time"] + facts[-1]["delta"]])
+        print self.start_time, self.end_time
+        self.count_hours()
         
-        if self.start_date > self.end_date:
-            self.start_date, self.end_date = self.end_date, self.start_date
 
-        if self.end_date.time == dt.time(0,0):
-            self.end_date += dt.timedelta(days=1)
 
         self.redraw_canvas()
 
+    def count_hours(self):
+        #go through facts and make array of time used by our fraction
+        fractions = []
+        
+        current_time = self.start_time
+
+        minor_tick = self.minor_tick
+        while current_time <= self.end_time:
+            
+            # if minor tick is month, the starting date will have been
+            # already adjusted to the first
+            # now we have to make sure to move month by month
+            if self.minor_tick >= dt.timedelta(days=28): 
+                minor_tick = dt.timedelta(calendar.monthrange(current_time.year, current_time.month)[1]) # days in month
+            
+            fractions.append(current_time)
+            current_time += minor_tick
+        
+        hours = [0] * len(fractions)
+        
+        tick_minutes = float(stuff.duration_minutes(self.minor_tick))
+
+        for fact in self.facts:
+            end_time = fact["start_time"] + fact["delta"] # the thing about ongoing task - it has no end time
+            
+            # find in which fraction the fact starts and
+            # add duration up to the border of tick to that fraction
+            # then move cursor to the start of next fraction
+            first_index = bisect_left(fractions, fact["start_time"]) - 1
+            step_time = fractions[first_index]
+            first_end = min(end_time, step_time + self.minor_tick)
+            first_tick = stuff.duration_minutes(first_end - fact["start_time"]) / tick_minutes
+            
+            hours[first_index] += first_tick
+            step_time = step_time + self.minor_tick
+
+            # now go through ticks until we reach end of the time
+            while step_time < end_time:
+                index = bisect_left(fractions, step_time)
+                interval = min([1, stuff.duration_minutes(end_time - step_time) / tick_minutes])
+                hours[index] += interval
+                
+                step_time += self.minor_tick
+
+
+        # now normalize
+        max_hour = max(hours)
+        hours = [hour / (max_hour or 1) for hour in hours]
+
+        self.tick_totals = zip(fractions, hours)
+
+
     def set_title(self, start_date, end_date):
         dates_dict = stuff.dateDict(start_date, "start_")
         dates_dict.update(stuff.dateDict(end_date, "end_"))
@@ -92,79 +178,87 @@ class NewTimeLine(graphics.Area):
 
     def on_expose(self):
         self.fill_area(0, 0, self.width, self.height, "#fafafa")
-        bar_width, time_step = self.figure_time_fraction()
-        
-        self.context.translate(0.5, 0.5) #move half a pixel to get sharp lines
-
-
-        #go through facts and make array of time used by our fraction
-        distribution = []  # TODO - think of a better name for a variable
         
-        current_time = self.start_date
-        while current_time <= self.end_date:
-            distribution.append(current_time)
-            current_time += time_step
+        total_minutes = stuff.duration_minutes(self.end_time - self.start_time)
+        tick_minutes = stuff.duration_minutes(self.minor_tick)
         
-        hours = [0] * len(distribution)
+        bar_width = self.width / (total_minutes / float(tick_minutes))
         
-        step_minutes = float(stuff.duration_minutes(time_step))
+        self.context.translate(0.5, 0.5) #move half a pixel to get sharp lines (hypothetically)
 
-        for fact in self.facts:
-            
-            first_index = bisect_left(distribution, fact["start_time"]) - 1
-            
-            step_time = distribution[first_index]
-            first_end = min(fact["start_time"] + fact["delta"], step_time + time_step)
-
-            interval = stuff.duration_minutes(first_end - fact["start_time"]) / step_minutes
+        # the bars        
+        x = 1
+        for current_time, total in self.tick_totals:
+            bar_size = round(self.height * total * 0.9)
             
-            hours[first_index] += interval
-
-            step_time = step_time + time_step
-            while step_time < fact["start_time"] + fact["delta"]:
-                index = bisect_left(distribution, step_time)
-                
-                interval = min([1, stuff.duration_minutes(fact["start_time"] + fact["delta"] - step_time) / step_minutes])
-                hours[index] += interval
-                
-                step_time += time_step
+            self.fill_area(round(x), self.height - bar_size, round(bar_width * 0.9), bar_size, "#eeeeee")
+            x += bar_width
 
 
-        max_hour = max(hours)
-        hours = [hour / max_hour for hour in hours]
 
-        per_interval = dict(zip(distribution, hours))
+        # major ticks
+        if (self.end_time - self.start_time) < dt.timedelta(days=2):  # about the same day
+            major_step = dt.timedelta(seconds = 60 * 60)
+        else:
+            major_step = dt.timedelta(days=1)
         
         x = 1
-        current_time = self.start_date
-        while current_time <= self.end_date:
-            bar_size = round(self.height * per_interval[current_time] * 0.9)
-            
-            self.fill_area(round(x), self.height - bar_size, round(bar_width * 0.9), bar_size, "#eeeeee")
-            current_time += time_step
-            x += bar_width
+        major_tick_step = self.width / (total_minutes / float(stuff.duration_minutes(major_step)))
+        current_time = self.start_time
+        while current_time <= self.end_time:
+            if (self.end_time - self.start_time) < dt.timedelta(days=2):  # about the same day
+                if current_time.time() == dt.time(0,0):
+                    self.fill_area(round(x), 0, 1, self.height, "#666666")
+
+            elif self.minor_tick == dt.timedelta(days=1):  # when going day by day, tick on week, month and year change
+                if current_time.weekday() == 0:
+                    self.fill_area(round(x), 0, 1, self.height, "#999999")
+
+                elif current_time.day == 1:
+                    self.fill_area(round(x), 0, 1, self.height, "#666666")
+
+                elif current_time.timetuple().tm_yday == 1:
+                    self.fill_area(round(x), 0, 1, self.height, "#00FF00")
+    
+            elif self.minor_tick == dt.timedelta(days=7):  # when going week by week, tick on month and year change
+                if current_time.day == 1:
+                    self.fill_area(round(x), 0, 1, self.height, "#666666")
 
-        print time_step
-        step_format = "%H:%M"
-        if time_step < dt.timedelta(seconds = 60 * 60):
-            step_format = "%H:%M"
-        elif time_step < dt.timedelta(days = 1):
+                elif current_time.timetuple().tm_yday == 1:
+                    self.fill_area(round(x), 0, 1, self.height, "#00FF00")
+    
+            elif self.minor_tick >= dt.timedelta(days=28):  # when going by month, tick on year change
+                if current_time.timetuple().tm_yday == 1:
+                    self.fill_area(round(x), 0, 1, self.height, "#00FF00")
+
+
+
+            current_time += major_step
+            x += major_tick_step
+
+
+
+        #minor ticks
+        if self.minor_tick >= dt.timedelta(days = 28): # month
+            step_format = "%b"
+
+        elif self.minor_tick == dt.timedelta(days = 7): # week
+            step_format = "%a\n%d"
+        elif self.minor_tick == dt.timedelta(days = 1): # day
+            step_format = "%a\n%d"
+        else:        
             step_format = "%H:%M"
-        elif time_step <= dt.timedelta(days = 7):
-            step_format = "%a %d"
+
 
         x = 1
-        i = 1
-        current_time = self.start_date
-        while current_time <= self.end_date:
+        current_time = self.start_time
+        for current_time, total in self.tick_totals:
             self.set_color("#aaaaaa")
-            self.context.move_to(x, 30)
             self.layout.set_text(current_time.strftime(step_format))
+            self.context.move_to(x, self.height - self.layout.get_pixel_size()[1])
             self.context.show_layout(self.layout)
 
-            current_time += time_step
             x += bar_width
-            i +=1
 
         
         self.set_color("#aaaaaa")
@@ -178,32 +272,3 @@ class NewTimeLine(graphics.Area):
 
         self.context.show_layout(self.layout)
 
-
-    def figure_time_fraction(self):
-        bar_width = 70 # preferred bar width
-        bar_count = self.width / float(bar_width)
-        
-        minutes = stuff.duration_minutes(self.end_date - self.start_date)
-        minutes_in_unit = int(minutes / bar_count)
-
-        # now let's find closest human understandable fraction of time that we will be actually using
-        fractions = [60, # minutes
-                     60 * 24, # days
-                     60 * 24 * 7, 60 * 24 * 14, # weeks (1,2)
-                     60 * 24 * 30, 60 * 24 * 30 * 3, 60 * 24 * 30 * 4, # months (1, 3, 4)
-                     60 * 24 * 356] # years
-        
-        human_step = bisect(fractions, minutes_in_unit)
-        # go to the closest side
-        if human_step > 0 and abs(fractions[human_step] - minutes_in_unit) > abs(fractions[human_step - 1] - minutes_in_unit):
-            human_step -=1
-        
-        step_minutes = fractions[human_step]
-        
-        bar_count = minutes / step_minutes
-        bar_width = self.width / float(bar_count)
-        
-        time_step = dt.timedelta(days = step_minutes / (60 * 24),
-                                 seconds = (step_minutes % (60 * 24)) * 60)
-    
-        return bar_width, time_step
\ No newline at end of file



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]