~ubuntu-branches/ubuntu/vivid/hamster-applet/vivid

« back to all changes in this revision

Viewing changes to hamster/stuff.py

  • Committer: Bazaar Package Importer
  • Author(s): Emilio Pozuelo Monfort
  • Date: 2009-10-22 22:01:54 UTC
  • mfrom: (1.2.4 upstream) (5.2.2 sid)
  • mto: This revision was merged to the branch mainline in revision 18.
  • Revision ID: james.westby@ubuntu.com-20091022220154-do4zoetlf35l36pe
Tags: 2.28.1-1
New upstream release.

Show diffs side-by-side

added added

removed removed

Lines of Context:
22
22
# cells, columns, trees and other
23
23
 
24
24
import gtk
25
 
from hamster import storage
26
25
import pango
27
26
from pango import ELLIPSIZE_END
28
27
 
 
28
from itertools import groupby
29
29
import datetime as dt
 
30
import time
 
31
import re
30
32
import locale
31
 
 
 
33
import os
 
34
 
 
35
def load_ui_file(name):
 
36
    from configuration import runtime
 
37
    ui = gtk.Builder()
 
38
    ui.add_from_file(os.path.join(runtime.data_dir, name))
 
39
    return ui 
 
40
 
 
41
def zero_hour(date):
 
42
    return dt.datetime.combine(date.date(), dt.time(0,0))
 
43
    
32
44
# it seems that python or something has bug of sorts, that breaks stuff for
33
45
# japanese locale, so we have this locale from and to ut8 magic in some places
34
46
# see bug 562298
46
58
        retval = locale_str
47
59
    return retval
48
60
 
 
61
def locale_first_weekday():
 
62
    """figure if week starts on monday or sunday"""
 
63
    first_weekday = 6 #by default settle on monday
 
64
 
 
65
    try:
 
66
        process = os.popen("locale first_weekday week-1stday")
 
67
        week_offset, week_start = process.read().split('\n')[:2]
 
68
        process.close()
 
69
        week_start = dt.date(*time.strptime(week_start, "%Y%m%d")[:3])
 
70
        week_offset = dt.timedelta(int(week_offset) - 1)
 
71
        beginning = week_start + week_offset
 
72
        first_weekday = int(beginning.strftime("%w"))
 
73
    except:
 
74
        print "WARNING - Failed to get first weekday from locale"
 
75
        pass
 
76
        
 
77
    return first_weekday
 
78
    
49
79
class CategoryCell(gtk.CellRendererText):
50
80
    def __init__(self):
51
81
        gtk.CellRendererText.__init__(self)        
56
86
        self.set_property('scale', pango.SCALE_SMALL)
57
87
        self.set_property('yalign', 0.0)
58
88
 
59
 
class ExpanderColumn(gtk.TreeViewColumn):
60
 
    def __init__(self, label, text):
61
 
        gtk.TreeViewColumn.__init__(self, label)
62
 
        
 
89
 
 
90
insensitive_color = gtk.Label().style.fg[gtk.STATE_INSENSITIVE].to_string()
 
91
def format_activity(name, category, description, pad_description = False):
 
92
    "returns pango markup for activity with category and description"
 
93
    text = name    
 
94
    if category and category != _("Unsorted"):
 
95
        text += """ - <span color="%s" size="x-small">%s</span>""" % (insensitive_color, category)
 
96
 
 
97
    if description:
 
98
        text+= "\n"
 
99
        if pad_description:
 
100
            text += "          "
 
101
 
 
102
        text += """<span style="italic" size="small">%s</span>""" % description
 
103
        
 
104
    return text
 
105
    
 
106
 
 
107
class ActivityColumn(gtk.TreeViewColumn):
 
108
    def activity_painter(self, column, cell, model, iter):
 
109
        activity_name = model.get_value(iter, self.name)
 
110
        description = model.get_value(iter, self.description)
 
111
        category = model.get_value(iter, self.category)
 
112
        
 
113
        markup = format_activity(activity_name, category, description)            
 
114
        cell.set_property('markup', markup)
 
115
        return
 
116
        
 
117
    def __init__(self, name, description, category = None):
 
118
        gtk.TreeViewColumn.__init__(self)
 
119
        
 
120
        self.name, self.description, self.category = name, description, category
63
121
        self.set_expand(True)
64
122
        cell = gtk.CellRendererText()
65
 
        cell.set_property('ellipsize', ELLIPSIZE_END)
66
123
        self.pack_start(cell, True)
67
 
        self.set_attributes(cell, text=text)
68
 
 
69
 
 
70
 
def format_duration(minutes):
71
 
    if minutes:
72
 
        return "%02d:%02d" % (minutes / 60, minutes % 60)
 
124
        cell.set_property("ellipsize", pango.ELLIPSIZE_END)
 
125
        self.set_cell_data_func(cell, self.activity_painter)
 
126
 
 
127
def duration_minutes(duration):
 
128
    """returns minutes from duration, otherwise we keep bashing in same math"""
 
129
    return duration.seconds / 60 + duration.days * 24 * 60
 
130
    
 
131
def format_duration(minutes, human = True):
 
132
    """formats duration in a human readable format.
 
133
    accepts either minutes or timedelta"""
 
134
    
 
135
    if isinstance(minutes, dt.timedelta):
 
136
        minutes = duration_minutes(minutes)
 
137
        
 
138
    if not minutes:
 
139
        if human:
 
140
            return ""
 
141
        else:
 
142
            return "00:00"
 
143
    
 
144
    hours = minutes / 60
 
145
    minutes = minutes % 60
 
146
    formatted_duration = ""
 
147
    
 
148
    if human:
 
149
        if minutes % 60 == 0:
 
150
            # duration in round hours
 
151
            formatted_duration += _("%dh") % (hours)
 
152
        elif hours == 0:
 
153
            # duration less than hour
 
154
            formatted_duration += _("%dmin") % (minutes % 60.0)
 
155
        else:
 
156
            # x hours, y minutes
 
157
            formatted_duration += _("%dh %dmin") % (hours, minutes % 60)
73
158
    else:
74
 
        return None
75
 
 
76
 
def dateDict(date, prefix):
 
159
        formatted_duration += "%02d:%02d" % (hours, minutes)
 
160
    
 
161
    
 
162
    return formatted_duration
 
163
 
 
164
def totals(iter, keyfunc, sumfunc):
 
165
    """groups items by field described in keyfunc and counts totals using value
 
166
       from sumfunc
 
167
    """
 
168
    data = sorted(iter, key=keyfunc)
 
169
    res = {}
 
170
 
 
171
    for k, group in groupby(data, keyfunc):
 
172
        res[k] = sum([sumfunc(entry) for entry in group])
 
173
 
 
174
    return res
 
175
 
 
176
 
 
177
 
 
178
 
 
179
def dateDict(date, prefix = ""):
77
180
    """converts date into dictionary, having prefix for all the keys"""
78
181
    res = {}
79
182
    
113
216
    text = text.replace(">", "&gt;")
114
217
    return text
115
218
 
116
 
 
117
 
class DayStore(object):
118
 
    """A day view contains a treeview for facts of the day and another
119
 
       one for totals. It creates those widgets on init, use
120
 
       fill_view(store) to fill the tree and calculate totals """
121
 
 
122
 
    def __init__(self, date = None):
123
 
        date = date or dt.date.today()
124
 
        
125
 
        # ID, Time, Name, Duration, Date, Description
126
 
        self.fact_store = gtk.ListStore(int, str, str, str, str, str)
127
 
        
128
 
        self.facts = storage.get_facts(date)
129
 
        self.totals = {}
130
 
        
131
 
        for fact in self.facts:
132
 
            duration = None
133
 
            
134
 
            if fact["end_time"]: # not set if just started
135
 
                delta = fact["end_time"] - fact["start_time"]
136
 
                duration = 24 * delta.days + delta.seconds / 60
137
 
            elif fact["start_time"].date() == dt.date.today():  # give duration to today's last activity
138
 
                delta = dt.datetime.now() - fact["start_time"]
139
 
                duration = 24 * delta.days + delta.seconds / 60
140
 
            
141
 
            fact_category = fact['category']
142
 
            
143
 
            if fact_category not in self.totals:
144
 
                self.totals[fact_category] = 0
145
 
 
146
 
            if duration:
147
 
                self.totals[fact_category] += duration
148
 
 
149
 
            current_duration = format_duration(duration)
150
 
 
151
 
            self.fact_store.append([fact['id'], escape_pango(fact['name']), 
152
 
                                    fact["start_time"].strftime("%H:%M"), 
153
 
                                    current_duration,
154
 
                                    fact["start_time"].strftime("%Y%m%d"),
155
 
                                    escape_pango(fact["description"])])
156
 
 
 
219
def figure_time(str_time):
 
220
    if not str_time:
 
221
        return None
 
222
    
 
223
    # strip everything non-numeric and consider hours to be first number
 
224
    # and minutes - second number
 
225
    numbers = re.split("\D", str_time)
 
226
    numbers = filter(lambda x: x!="", numbers)
 
227
    
 
228
    hours, minutes = None, None
 
229
    
 
230
    if len(numbers) == 1 and len(numbers[0]) == 4:
 
231
        hours, minutes = int(numbers[0][:2]), int(numbers[0][2:])
 
232
    else:
 
233
        if len(numbers) >= 1:
 
234
            hours = int(numbers[0])
 
235
        if len(numbers) >= 2:
 
236
            minutes = int(numbers[1])
 
237
        
 
238
    if (hours is None or minutes is None) or hours > 24 or minutes > 60:
 
239
        return None #no can do
 
240
 
 
241
    return dt.datetime.now().replace(hour = hours, minute = minutes,
 
242
                                     second = 0, microsecond = 0)
 
243
 
 
244
def parse_activity_input(text):
 
245
    """Currently pretty braindead function that tries to parse arbitrary input
 
246
    into a activity"""
 
247
    class InputParseResult(object):
 
248
        def __init__(self):
 
249
            self.activity_name = None
 
250
            self.category_name = None
 
251
            self.start_time = None
 
252
            self.end_time = None
 
253
            self.description = None
 
254
            self.tags = []
 
255
            
 
256
    
 
257
    res = InputParseResult()
 
258
    
 
259
    input_parts = text.split(" ")
 
260
    if len(input_parts) > 1 and re.match('^-?\d', input_parts[0]): #look for time only if there is more
 
261
        potential_time = text.split(" ")[0]
 
262
        potential_end_time = None
 
263
        if len(potential_time) > 1 and  potential_time.startswith("-"):
 
264
            #if starts with minus, treat as minus delta minutes
 
265
            res.start_time = dt.datetime.now() + dt.timedelta(minutes =
 
266
                                                                int(potential_time))
 
267
    
 
268
        else:
 
269
            if potential_time.find("-") > 0:
 
270
                potential_time, potential_end_time = potential_time.split("-", 2)
 
271
                res.end_time = figure_time(potential_end_time)
 
272
                
 
273
            res.start_time = figure_time(potential_time)
 
274
        
 
275
        #remove parts that worked
 
276
        if res.start_time and potential_end_time and not res.end_time:
 
277
            res.start_time = None #scramble
 
278
        elif res.start_time:
 
279
            text = text[text.find(" ")+1:]
 
280
    
 
281
    #see if we have description of activity somewhere here (delimited by comma)
 
282
    if text.find(",") > 0:
 
283
        text, res.description = text.split(",", 1)
 
284
        res.description = res.description.strip()
 
285
 
 
286
    if text.find("@") > 0:
 
287
        text, res.category_name = text.split("@", 1)
 
288
        res.category_name = res.category_name.strip()
 
289
 
 
290
    #only thing left now is the activity name itself
 
291
    res.activity_name = text
 
292
 
 
293
    #this is most essential
 
294
    if (text.find("bbq") > -1 or text.find("barbeque") > -1
 
295
        or text.find("barbecue") > -1)  and text.find("omg") > -1:
 
296
        res.description = "[ponies = 1], [rainbows = 0]"
 
297
 
 
298
    return res