[IMP]: Added log message on create
[odoo/odoo.git] / addons / idea / idea.py
1 # -*- coding: utf-8 -*-
2 ##############################################################################
3 #
4 #    OpenERP, Open Source Management Solution
5 #    Copyright (C) 2004-2010 Tiny SPRL (<http://tiny.be>).
6 #
7 #    This program is free software: you can redistribute it and/or modify
8 #    it under the terms of the GNU Affero General Public License as
9 #    published by the Free Software Foundation, either version 3 of the
10 #    License, or (at your option) any later version.
11 #
12 #    This program is distributed in the hope that it will be useful,
13 #    but WITHOUT ANY WARRANTY; without even the implied warranty of
14 #    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 #    GNU Affero General Public License for more details.
16 #
17 #    You should have received a copy of the GNU Affero General Public License
18 #    along with this program.  If not, see <http://www.gnu.org/licenses/>.
19 #
20 ##############################################################################
21
22 from osv import osv
23 from osv import fields
24 from tools.translate import _
25 import time
26
27 VoteValues = [('-1', 'Not Voted'), ('0', 'Very Bad'), ('25', 'Bad'), \
28               ('50', 'Normal'), ('75', 'Good'), ('100', 'Very Good') ]
29 DefaultVoteValue = '50'
30
31 class idea_category(osv.osv):
32     """ Category of Idea """
33
34     _name = "idea.category"
35     _description = "Idea Category"
36     _log_create=True
37
38     _columns = {
39         'name': fields.char('Category', size=64, required=True),
40         'summary': fields.text('Summary'),
41         'parent_id': fields.many2one('idea.category', 'Parent Categories', ondelete='set null'),
42         'child_ids': fields.one2many('idea.category', 'parent_id', 'Child Categories'),
43         'visibility':fields.boolean('Open Idea?', required=False, help="If True creator of the idea will be visible to others"),
44     }
45     _sql_constraints = [
46         ('name', 'unique(parent_id,name)', 'The name of the category must be unique' )
47     ]
48     _order = 'parent_id,name asc'
49
50 idea_category()
51
52 class idea_idea(osv.osv):
53     """ Idea """
54     _name = 'idea.idea'
55     _rec_name = 'title'
56     _log_create=True
57
58     def _vote_avg_compute(self, cr, uid, ids, name, arg, context = None):
59
60         """ compute average for voting
61          @param cr: the current row, from the database cursor,
62          @param uid: the current user’s ID for security checks,
63          @param ids: List of voting’s IDs
64          @return: dictionay of Idea """
65
66         if not ids:
67             return {}
68
69         sql = """SELECT i.id, avg(v.score::integer)
70            FROM idea_idea i LEFT OUTER JOIN idea_vote v ON i.id = v.idea_id
71             WHERE i.id = ANY(%s)
72             GROUP BY i.id
73         """
74
75         cr.execute(sql, (ids,))
76         return dict(cr.fetchall())
77
78     def _vote_count(self, cr, uid, ids, name, arg, context=None):
79
80         """ count number of vote
81          @param cr: the current row, from the database cursor,
82          @param uid: the current user’s ID for security checks,
83          @param ids: List of voting count’s IDs
84          @return: dictionay of Idea """
85
86         if not ids:
87             return {}
88
89         sql = """SELECT i.id, COUNT(1)
90            FROM idea_idea i LEFT OUTER JOIN idea_vote v ON i.id = v.idea_id
91             WHERE i.id = ANY(%s)
92             GROUP BY i.id
93         """
94
95         cr.execute(sql, (ids,))
96         return dict(cr.fetchall())
97
98     def _comment_count(self, cr, uid, ids, name, arg, context=None):
99
100         """ count number of comment
101         @param cr: the current row, from the database cursor,
102         @param uid: the current user’s ID for security checks,
103         @param ids: List of comment’s IDs
104         @return: dictionay of Idea """
105
106         if not ids:
107             return {}
108
109         sql = """SELECT i.id, COUNT(1)
110            FROM idea_idea i LEFT OUTER JOIN idea_comment c ON i.id = c.idea_id
111             WHERE i.id = ANY(%s)
112             GROUP BY i.id
113         """
114
115         cr.execute(sql, (ids,))
116         return dict(cr.fetchall())
117
118     def _vote_read(self, cr, uid, ids, name, arg, context = None):
119
120         """ Read Vote
121         @param cr: the current row, from the database cursor,
122         @param uid: the current user’s ID for security checks,
123         @param ids: List of vote read’s IDs """
124
125         res = {}
126         for id in ids:
127             res[id] = '-1'
128         vote_obj = self.pool.get('idea.vote')
129         votes_ids = vote_obj.search(cr, uid, [('idea_id', 'in', ids), ('user_id', '=', uid)])
130         vote_obj_id = vote_obj.browse(cr, uid, votes_ids, context)
131
132         for vote in vote_obj_id:
133             res[vote.idea_id.id] = vote.score
134         return res
135
136     def _vote_save(self, cr, uid, id, field_name, field_value, arg, context = None):
137
138         """ save Vote
139         @param cr: the current row, from the database cursor,
140         @param uid: the current user’s ID for security checks,
141         @param ids: List of vote save’s IDs """
142
143         vote_obj = self.pool.get('idea.vote')
144         vote = vote_obj.search(cr, uid, [('idea_id', '=', id), ('user_id', '=', uid)])
145         textual_value = str(field_value)
146
147         if vote:
148             if int(field_value) >= 0:
149                 vote_obj.write(cr, uid, vote, {'score': textual_value })
150             else:
151                 vote_obj.unlink(cr, uid, vote)
152         else:
153             if int(field_value) >= 0:
154                 vote_obj.create(cr, uid, {'idea_id': id, 'user_id': uid, 'score': textual_value })
155
156     _columns = {
157         'user_id': fields.many2one('res.users', 'Creator', required=True, readonly=True),
158         'title': fields.char('Idea Summary', size=64, required=True, readonly=True, states={'draft':[('readonly',False)]}),
159         'description': fields.text('Description', help='Content of the idea', readonly=True, states={'draft':[('readonly',False)]}),
160         'comment_ids': fields.one2many('idea.comment', 'idea_id', 'Comments'),
161         'created_date': fields.datetime('Creation date', readonly=True),
162         'vote_ids': fields.one2many('idea.vote', 'idea_id', 'Vote'),
163         'my_vote': fields.function(_vote_read, fnct_inv = _vote_save, string="My Vote", method=True, type="selection", selection=VoteValues),
164         'vote_avg': fields.function(_vote_avg_compute, method=True, string="Average Score", type="float"),
165         'count_votes': fields.function(_vote_count, method=True, string="Count of votes", type="integer"),
166         'count_comments': fields.function(_comment_count, method=True, string="Count of comments", type="integer"),
167         'category_id': fields.many2one('idea.category', 'Category', required=True, readonly=True, states={'draft':[('readonly',False)]}),
168         'state': fields.selection([('draft', 'Draft'),
169             ('open', 'Opened'),
170             ('close', 'Accepted'),
171             ('cancel', 'Cancelled')],
172             'State', readonly=True,
173             help='When the Idea is created the state is \'Draft\'.\n It is \
174             opened by the user, the state is \'Opened\'.\
175             \nIf the idea is accepted, the state is \'Accepted\'.'
176         ),
177         'visibility':fields.boolean('Open Idea?', required=False),
178         'stat_vote_ids': fields.one2many('idea.vote.stat', 'idea_id', 'Statistics', readonly=True),
179         'vote_limit': fields.integer('Maximum Vote per User',
180                      help="Set to one if  you require only one Vote per user"),
181     }
182
183     _defaults = {
184         'user_id': lambda self,cr,uid,context: uid,
185         'my_vote': lambda *a: '-1',
186         'state': lambda *a: 'draft',
187         'vote_limit': lambda * a: 1,
188         'created_date': lambda *a: time.strftime('%Y-%m-%d %H:%M:%S'),
189         'visibility': lambda *a: True,
190     }
191     _order = 'id desc'
192
193     def create(self, cr, user, vals, context={}):
194         """
195         Create a new record for a model idea_idea
196         @param cr: A database cursor
197         @param user: ID of the user currently logged in
198         @param vals: provides data for new record
199         @param context: context arguments, like lang, time zone
200
201         @return: Returns an id of the new record
202         """
203         visibility = False
204
205         if vals.get('category_id', False):
206             category_pool = self.pool.get('idea.category')
207             category = category_pool.browse(cr, user, vals.get('category_id'), context)
208             visibility = category.visibility
209
210         vals.update({
211             'visibility':visibility
212         })
213
214         res_id = super(idea_idea, self).create(cr, user, vals, context)
215         return res_id
216
217     def copy(self, cr, uid, id, default={}, context={}):
218         """
219         Create the new record in idea_idea model from existing one
220         @param cr: A database cursor
221         @param user: ID of the user currently logged in
222         @param id: list of record ids on which copy method executes
223         @param default: dict type contains the values to be overridden during copy of object
224         @param context: context arguments, like lang, time zone
225
226         @return: Returns the id of the new record
227         """
228
229         default.update({
230             'comment_ids':False,
231             'vote_ids':False,
232             'stat_vote_ids':False
233
234         })
235         res_id = super(idea_idea, self).copy(cr, uid, id, default, context)
236         return res_id
237
238     def write(self, cr, user, ids, vals, context=None):
239         """
240         Update redord(s) exist in {ids}, with new value provided in {vals}
241
242         @param cr: A database cursor
243         @param user: ID of the user currently logged in
244         @param ids: list of record ids to update
245         @param vals: dict of new values to be set
246         @param context: context arguments, like lang, time zone
247
248         @return: Returns True on success, False otherwise
249         """
250
251         state = self.browse(cr, user, ids[0]).state
252
253         if vals.get('my_vote', False):
254             if vals.get('state', state) != 'open':
255                 raise osv.except_osv(_("Warning !"), _("Draft/Accepted/Cancelled ideas Could not be voted"))
256
257         res = super(idea_idea, self).write(cr, user, ids, vals, context)
258         return res
259
260     def idea_cancel(self, cr, uid, ids):
261         self.write(cr, uid, ids, { 'state': 'cancel' })
262         return True
263
264     def idea_open(self, cr, uid, ids):
265         self.write(cr, uid, ids, { 'state': 'open' })
266         return True
267
268     def idea_close(self, cr, uid, ids):
269         self.write(cr, uid, ids, { 'state': 'close' })
270         return True
271
272     def idea_draft(self, cr, uid, ids):
273         self.write(cr, uid, ids, { 'state': 'draft' })
274         return True
275 idea_idea()
276
277
278 class idea_comment(osv.osv):
279     """ Apply Idea for Comment """
280
281     _name = 'idea.comment'
282     _description = 'Comment'
283     _rec_name = 'content'
284
285     _columns = {
286         'idea_id': fields.many2one('idea.idea', 'Idea', required=True, ondelete='cascade'),
287         'user_id': fields.many2one('res.users', 'User', required=True),
288         'content': fields.text('Comment', required=True),
289         'create_date': fields.datetime('Creation date', readonly=True),
290     }
291
292     _defaults = {
293         'user_id': lambda self, cr, uid, context: uid
294     }
295
296     _order = 'id desc'
297
298 idea_comment()
299
300
301 class idea_vote(osv.osv):
302     """ Apply Idea for Vote """
303
304     _name = 'idea.vote'
305     _description = 'Idea Vote'
306     _rec_name = 'score'
307     _log_create = True
308
309     _columns = {
310         'user_id': fields.many2one('res.users', 'By user', readonly="True"),
311         'idea_id': fields.many2one('idea.idea', 'Idea', readonly="True", ondelete='cascade'),
312         'score': fields.selection(VoteValues, 'Vote Status', readonly="True"),
313         'date': fields.datetime('Date', readonly="True"),
314         'comment': fields.text('Comment', readonly="True"),
315     }
316     _defaults = {
317         'score': lambda *a: DefaultVoteValue,
318         'date': lambda *a: time.strftime('%Y-%m-%d %H:%M:%S'),
319     }
320
321 idea_vote()
322
323 class idea_vote_stat(osv.osv):
324     """ Idea votes Statistics """
325
326     _name = 'idea.vote.stat'
327     _description = 'Idea Votes Statistics'
328     _auto = False
329     _rec_name = 'idea_id'
330
331     _columns = {
332         'idea_id': fields.many2one('idea.idea', 'Idea', readonly=True),
333         'score': fields.selection(VoteValues, 'Score', readonly=True),
334         'nbr': fields.integer('Number of Votes', readonly=True),
335     }
336
337     def init(self, cr):
338         """
339         initialize the sql view for the stats
340
341         cr -- the cursor
342         """
343         cr.execute("""
344             CREATE OR REPLACE VIEW idea_vote_stat AS (
345                 SELECT
346                     MIN(v.id) AS id,
347                     i.id AS idea_id,
348                     v.score,
349                     COUNT(1) AS nbr
350                 FROM
351                     idea_vote v
352                     LEFT JOIN idea_idea i ON (v.idea_id = i.id)
353                 GROUP BY
354                     i.id, v.score, i.id )
355         """)
356
357 idea_vote_stat()
358
359 # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
360