Edit an experience entry by double-clicking on the tree
[crapvine.git] / experience.py
blob9e2f717400073719f78a66256f897a4e113d071e
1 ## This file is part of Crapvine.
2 ##
3 ## Copyright (C) 2007 Andrew Sayman <lorien420@myrealbox.com>
4 ##
5 ## Crapvine is free software; you can redistribute it and/or modify
6 ## it under the terms of the GNU General Public License as published by
7 ## the Free Software Foundation; either version 3 of the License, or
8 ## (at your option) any later version.
9 ##
10 ## Crapvine is distributed in the hope that it will be useful,
11 ## but WITHOUT ANY WARRANTY; without even the implied warranty of
12 ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 ## GNU General Public License for more details.
15 ## You should have received a copy of the GNU General Public License
16 ## along with this program. If not, see <http://www.gnu.org/licenses/>.
18 import operator
20 # PyGtk
21 import gobject
23 # Character Support
24 from grapevine_xml import Attributed, AttributedListModel
25 from attribute import AttributeBuilder
27 class Experience(AttributedListModel):
28 number_as_text_attrs = ['unspent', 'earned']
29 column_attrs = ['date', 'change', 'type', 'unspent', 'earned', 'reason']
30 column_attr_types = [ gobject.TYPE_STRING, gobject.TYPE_STRING, gobject.TYPE_STRING, gobject.TYPE_STRING, gobject.TYPE_STRING, gobject.TYPE_STRING ]
32 def __init__(self):
33 AttributedListModel.__init__(self)
34 self.list = []
35 self.entries = self.list
37 def add_entry(self, entry, calculate_expenditures = True):
38 print "Appending entry %s" % entry.get_xml()
39 self.entries.append(entry)
41 # Signal the addition
42 path = (len(self.list) - 1, )
43 self.row_inserted(path, self.get_iter(path))
45 # Sort the entries using date
46 old_list = []
47 old_list.extend(self.entries)
48 self.entries.sort(key=operator.attrgetter('date'))
50 # Signal the reordering
51 new_indices = []
52 for old_entry in old_list:
53 new_indices.append(self.entries.index(old_entry))
54 self.rows_reordered((), None, new_indices)
55 print "After-sort entry %s" % entry.get_xml()
57 # Calculate new earned/unspent totals
58 if calculate_expenditures:
59 idx = self.entries.index(entry)
60 if idx > 0:
61 entry.calculate_expenditures_from(self.entries[idx - 1])
62 else:
63 entry.calculate_expenditures()
64 self.row_changed((idx,), self.get_iter((idx,)))
65 print "After-calc entry %s" % entry.get_xml()
67 for cascade_idx in range(idx + 1, len(self.entries)):
68 self.entries[cascade_idx].calculate_expenditures_from(
69 self.entries[cascade_idx - 1])
70 path = (cascade_idx,)
71 self.row_changed(path, self.get_iter(path))
73 self.__update_earned_unspent()
75 def __update_earned_unspent(self):
76 if len(self.entries) == 0:
77 self.earned = '0'
78 self.unspent = '0'
79 return
81 last_entry = self.entries[-1]
82 self.earned = "%s" % last_entry.earned
83 self.unspent = "%s" % last_entry.unspent
85 def get_xml(self, indent=''):
86 end_tag = ">\n" if len(self.entries) > 0 else "/>"
87 ret = '%s<experience %s%s' % (indent, self.get_attrs_xml(), end_tag)
88 local_indent = '%s ' % (indent)
89 ret += "\n".join([entry.get_xml(local_indent) for entry in self.entries])
90 if len(self.entries) > 0:
91 ret += '%s%s</experience>' % ("\n", indent)
92 return ret
93 def __str__(self):
94 return self.get_xml()
96 def on_get_value(self, index, column):
97 """Override display value for type in the tree view"""
98 if self.column_attrs[column] == 'type':
99 if index > len(self.list):
100 return None
101 targ = self.list[index]
102 return ExperienceEntry.map_type_to_str(targ['type'])
103 else:
104 return super(Experience, self).on_get_value(index, column)
106 class ExperienceEntry(Attributed):
107 text_attrs = ['reason']
108 number_as_text_attrs = ['change', 'type', 'earned', 'unspent']
109 date_attrs = ['date']
111 def get_xml(self, indent=''):
112 return '%s<entry %s/>' % (indent, self.get_attrs_xml())
113 def __str__(self):
114 return self.get_xml()
116 def calculate_expenditures(self):
117 tmp_e = ExperienceEntry()
118 tmp_e.unspent = '0'
119 tmp_e.earned = '0'
120 self.calculate_expenditures_from(tmp_e)
121 def calculate_expenditures_from(self, next_entry):
122 t = int(self.type)
123 ne_u = float(next_entry.unspent)
124 ne_e = float(next_entry.earned)
125 s_c = float(self.change)
126 s_u = float(self.unspent)
127 s_e = float(self.earned)
128 if t == 3: # Spend
129 self.unspent = str(ne_u - s_c)
130 self.earned = "%s" % next_entry.earned
131 elif t == 0: # Earn
132 self.unspent = str(ne_u + s_c)
133 self.earned = str(ne_e + s_c)
134 elif t == 4: # Unspend
135 self.unspent = str(ne_u + s_c)
136 self.earned = "%s" % next_entry.earned
137 elif t == 1: # Lose
138 self.unspent = "%s" % next_entry.earned
139 self.earned = str(ne_e - s_c)
140 elif t == 2: # Set Earned To
141 self.unspent = "%s" % next_entry.unspent
142 self.earned = "%s" % self.change
143 elif t == 5: # Set Unspent To
144 self.unspent = "%s" % self.change
145 self.earned = "%s" % next_entry.earned
146 elif t == 6: # Comment
147 self.unspent = "%s" % next_entry.unspent
148 self.earned = "%s" % next_entry.earned
149 else:
150 raise ValueError("Type must be an integer between 0 and 6")
152 @staticmethod
153 def map_type_to_str(type_input):
154 type = str(type_input)
155 if type == '0':
156 return 'Earn'
157 elif type == '1':
158 return 'Lose'
159 elif type == '2':
160 return 'Set Earned To'
161 elif type == '3':
162 return 'Spend'
163 elif type == '4':
164 return 'Unspend'
165 elif type == '5':
166 return 'Set Unspent To'
167 elif type == '6':
168 return 'Comment'
169 else:
170 return type