Re-enabling the cache
[doneit] / src / toolbox.py
1 import sys
2 import StringIO
3 import urllib
4 from xml.dom import minidom
5 import datetime
6
7
8 class Optional(object):
9         """
10         Taglines:
11         Even you don't have to worry about knowing when to perform None checks
12         When the NULL object pattern just isn't good enough
13
14         >>> a = Optional()
15         >>> a.is_good()
16         False
17         >>> a.get_nothrow("Blacksheep")
18         'Blacksheep'
19         >>> a.set("Lamb")
20         >>> a.is_good()
21         True
22         >>> a.get_nothrow("Blacksheep"), a.get(), a()
23         ('Lamb', 'Lamb', 'Lamb')
24         >>> a.clear()
25         >>> a.is_good()
26         False
27         >>> a.get_nothrow("Blacksheep")
28         'Blacksheep'
29         >>>
30         >>> b = Optional("Lamb")
31         >>> a.set("Lamb")
32         >>> a.is_good()
33         True
34         >>> a.get_nothrow("Blacksheep"), a.get(), a()
35         ('Lamb', 'Lamb', 'Lamb')
36         >>> a.clear()
37         >>> a.is_good()
38         False
39         >>> a.get_nothrow("Blacksheep")
40         'Blacksheep'
41         """
42
43         class NonExistent(object):
44                 pass
45
46         def __init__(self, value = NonExistent):
47                 self._value = value
48
49         def set(self, value):
50                 self._value = value
51
52         def clear(self):
53                 self._value = self.NonExistent
54
55         def is_good(self):
56                 return self._value is not self.NonExistent
57
58         def get_nothrow(self, default = None):
59                 return self._value if self.is_good() else default
60
61         def get(self):
62                 if not self.is_good():
63                         raise ReferenceError("Optional not initialized")
64                 return self._value
65
66         def __call__(self):
67                 # Implemented to imitate weakref
68                 return self.get()
69
70         def __str__(self):
71                 return str(self.get_nothrow(""))
72
73         def __repr__(self):
74                 return "<Optional at %x; to %r>" % (
75                         id(self), self.get_nothrow("Nothing")
76                 )
77
78         def __not__(self):
79                 return not self.is_good()
80
81         def __eq__(self, rhs):
82                 return self._value == rhs._value
83
84         def __ne__(self, rhs):
85                 return self._value != rhs._value
86
87         def __lt__(self, rhs):
88                 return self._value < rhs._value
89
90         def __le__(self, rhs):
91                 return self._value <= rhs._value
92
93         def __gt__(self, rhs):
94                 return self._value > rhs._value
95
96         def __ge__(self, rhs):
97                 return self._value >= rhs._value
98
99
100 def open_anything(source, alternative=None):
101         """URI, filename, or string --> stream
102
103         This function lets you define parsers that take any input source
104         (URL, pathname to local or network file, or actual data as a string)
105         and deal with it in a uniform manner.  Returned object is guaranteed
106         to have all the basic stdio read methods (read, readline, readlines).
107         Just .close() the object when you're done with it.
108         """
109         if hasattr(source, "read"):
110                 return source
111
112         if source == '-':
113                 return sys.stdin
114
115         # try to open with urllib (if source is http, ftp, or file URL)
116         try:
117                 return urllib.urlopen(source)
118         except (IOError, OSError):
119                 ##pass
120                 print "ERROR with URL ("+source+")!\n"
121
122         # try to open with native open function (if source is pathname)
123         try:
124                 return open(source)
125         except (IOError, OSError):
126                 ##pass
127                 print "ERROR with file!\n"
128
129         # treat source as string
130         if alternative == None:
131                 print 'LAST RESORT.  String is "'+source+'"\n'
132                 return StringIO.StringIO(str(source))
133         else:
134                 print 'LAST RESORT.  String is "'+alternative+'"\n'
135                 return StringIO.StringIO(str(alternative))
136
137
138 def load_xml(source, alternative=None):
139         """load XML input source, return parsed XML document
140
141         - a URL of a remote XML file ("http://diveintopython.org/kant.xml")
142         - a filename of a local XML file ("~/diveintopython/common/py/kant.xml")
143         - standard input ("-")
144         - the actual XML document, as a string
145         """
146         sock = open_anything(source, alternative)
147         try:
148                 xmldoc = minidom.parse(sock).documentElement
149         except (IOError, OSError):
150                 print "ERROR with data"
151                 sock.close()
152                 sock = open_anything('<response method="getProjects"><project projName="ERROR!"/></response>')
153                 xmldoc = minidom.parse(sock).documentElement
154         sock.close()
155         return xmldoc
156
157
158 def is_same_year(targetDate, todaysDate = datetime.datetime.today()):
159         return targetDate.year == todaysDate.year
160
161
162 def is_same_month(targetDate, todaysDate = datetime.datetime.today()):
163         return targetDate.month == todaysDate.month
164
165
166 def is_same_day(targetDate, todaysDate = datetime.datetime.today()):
167         return targetDate.day == todaysDate.day
168
169
170 def to_fuzzy_date(targetDate, todaysDate = datetime.datetime.today()):
171         """
172         Conert a date/time/datetime object to a fuzzy date
173
174         >>> todaysDate = datetime.date(2009, 4, 16)
175         >>> to_fuzzy_date(datetime.date(1, 4, 6), todaysDate)
176         'Forever ago'
177         >>> to_fuzzy_date(datetime.date(2008, 4, 13), todaysDate)
178         'Last year'
179         >>> to_fuzzy_date(datetime.date(2009, 4, 13), todaysDate)
180         'Last Monday'
181         >>> to_fuzzy_date(datetime.date(2009, 4, 20), todaysDate)
182         'This Monday'
183         >>> to_fuzzy_date(datetime.date(2010, 4, 13), todaysDate)
184         'Next year'
185         >>> to_fuzzy_date(datetime.date(2012, 12, 12), todaysDate)
186         'Forever from now'
187         """
188         delta = targetDate - todaysDate
189         days = abs(delta.days)
190         noDifference = datetime.timedelta()
191         isFuture = noDifference < delta
192         directionBy1 = "Next" if isFuture else "Last"
193         directionByN = "Later" if isFuture else "Earlier"
194
195         yearDelta = abs(targetDate.year - todaysDate.year)
196         if 1 < yearDelta:
197                 directionByInf = "from now" if isFuture else "ago"
198                 return "Forever %s" % directionByInf
199         elif 1 == yearDelta:
200                 return "%s year" % directionBy1
201
202         monthDelta = abs(targetDate.month - todaysDate.month)
203         if 1 < monthDelta:
204                 return "%s this year" % directionByN
205         elif 1 == monthDelta:
206                 return "%s month" % directionBy1
207
208         dayDelta = abs(targetDate.day - todaysDate.day)
209         if 14 < dayDelta:
210                 return "%s this month" % directionByN
211         elif 1 < dayDelta:
212                 directionInWeek = "This" if isFuture else "Last"
213                 days = ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"]
214                 return "%s %s" % (directionInWeek, days[targetDate.weekday()])
215         elif 1 == dayDelta:
216                 return "%s day" % directionBy1
217         else:
218                 return "Today"