[BACKPORT] publisher warranty stats
[odoo/odoo.git] / bin / addons / base / publisher_warranty / publisher_warranty.py
1 # -*- coding: utf-8 -*-
2 ##############################################################################
3 #
4 #    OpenERP, Open Source Management Solution
5 #    Copyright (C) 2004-2010 OpenERP S.A. (<http://www.openerp.com>).
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 Module to handle publisher warranty contracts as well as notifications from
23 OpenERP.
24 """
25
26 import datetime
27 import logging
28 import sys
29 import urllib
30 import urllib2
31
32 import pooler
33 import release
34 from osv import osv, fields
35 from tools.translate import _
36 from tools.safe_eval import safe_eval
37 from tools.config import config
38 from tools import misc
39
40 _logger = logging.getLogger(__name__)
41
42 """
43 Time interval that will be used to determine up to which date we will
44 check the logs to see if a message we just received was already logged.
45 @type: datetime.timedelta
46 """
47 _PREVIOUS_LOG_CHECK = datetime.timedelta(days=365)
48
49 class publisher_warranty_contract(osv.osv):
50     """
51     Osv representing a publisher warranty contract.
52     """
53     _name = "publisher_warranty.contract"
54
55     def _get_valid_contracts(self, cr, uid):
56         """
57         Return the list of the valid contracts encoded in the system.
58
59         @return: A list of contracts
60         @rtype: list of publisher_warranty.contract browse records
61         """
62         return [contract for contract in self.browse(cr, uid, self.search(cr, uid, []))
63                 if contract.state == 'valid']
64
65     def status(self, cr, uid):
66         """ Method called by the client to check availability of publisher warranty contract. """
67
68         contracts = self._get_valid_contracts(cr, uid)
69         return {
70             'status': "full" if contracts else "none" ,
71             'uncovered_modules': list(),
72         }
73
74     def send(self, cr, uid, tb, explanations, remarks=None, issue_name=None):
75         """ Method called by the client to send a problem to the publisher warranty server. """
76
77         if not remarks:
78             remarks = ""
79
80         valid_contracts = self._get_valid_contracts(cr, uid)
81         valid_contract = valid_contracts[0]
82
83         try:
84             origin = 'client'
85             dbuuid = self.pool.get('ir.config_parameter').get_param(cr, uid, 'database.uuid')
86             db_create_date = self.pool.get('ir.config_parameter').get_param(cr, uid, 'database.create_date')
87             user = self.pool.get("res.users").browse(cr, uid, uid)
88             user_name = user.name
89             email = user.email
90
91             msg = {'contract_name': valid_contract.name,
92                 'tb': tb,
93                 'explanations': explanations,
94                 'remarks': remarks,
95                 'origin': origin,
96                 'dbname': cr.dbname,
97                 'dbuuid': dbuuid,
98                 'db_create_date': db_create_date,
99                 'issue_name': issue_name,
100                 'email': email,
101                 'user_name': user_name,
102             }
103
104
105             add_arg = {"timeout":30} if sys.version_info >= (2,6) else {}
106             uo = urllib2.urlopen(config.get("publisher_warranty_url"),
107                                     urllib.urlencode({'arg0': msg, "action": "send",}),**add_arg)
108             try:
109                 submit_result = uo.read()
110             finally:
111                 uo.close()
112
113             result = safe_eval(submit_result)
114
115             crm_case_id = result
116
117             if not crm_case_id:
118                 return False
119
120         except osv.except_osv:
121             raise
122         except Exception:
123             _logger.warning("Error sending problem report", exc_info=1)
124             raise osv.except_osv(_("Error"),
125                                  _("Error during communication with the publisher warranty server."))
126
127         return True
128
129     def check_validity(self, cr, uid, ids, context=None):
130         """
131         Check the validity of a publisher warranty contract. This method just call get_logs() but checks
132         some more things, so it can be called from a user interface.
133         """
134         contract_id = ids[0]
135         contract = self.browse(cr, uid, contract_id)
136         state = contract.state
137         validated = state != "unvalidated"
138
139         self.get_logs(cr, uid, ids, cron_mode=False, context=context)
140
141         contract = self.browse(cr, uid, contract_id)
142         validated2 = contract.state != "unvalidated"
143         if not validated and not validated2:
144             raise osv.except_osv(_("Contract validation error"),
145                                  _("Please verify your publisher warranty serial number and validity."))
146         return True
147
148     def get_logs(self, cr, uid, ids, cron_mode=True, context=None):
149         """
150         Send a message to OpenERP's publisher warranty server to check the validity of
151         the contracts, get notifications, etc...
152
153         @param cron_mode: If true, catch all exceptions (appropriate for usage in a cron).
154         @type cron_mode: boolean
155         """
156         try:
157             try:
158                 result = get_sys_logs(cr, uid)
159             except Exception:
160                 if cron_mode: # we don't want to see any stack trace in cron
161                     return False
162                 _logger.debug("Exception while sending a get logs messages", exc_info=1)
163                 raise osv.except_osv(_("Error"), _("Error during communication with the publisher warranty server."))
164
165             contracts = result["contracts"]
166             for contract in contracts:
167                 c_id = self.search(cr, uid, [("name","=",contract)])[0]
168                 # for backward compatibility
169                 if type(contracts[contract]) == tuple:
170                     self.write(cr, uid, c_id, {
171                         "date_start": contracts[contract][0],
172                         "date_stop": contracts[contract][1],
173                         "state": contracts[contract][2],
174                         "check_support": False,
175                         "check_opw": False,
176                         "kind": "",
177                     })
178                 else:
179                     self.write(cr, uid, c_id, {
180                         "date_start": contracts[contract]["date_from"],
181                         "date_stop": contracts[contract]["date_to"],
182                         "state": contracts[contract]["state"],
183                         "check_support": contracts[contract]["check_support"],
184                         "check_opw": contracts[contract]["check_opw"],
185                         "kind": contracts[contract]["kind"],
186                     })
187
188
189             limit_date = (datetime.datetime.now() - _PREVIOUS_LOG_CHECK).strftime(misc.DEFAULT_SERVER_DATETIME_FORMAT)
190             for message in result["messages"]:
191                 ids = self.pool.get("res.log").search(cr, uid, [("res_model", "=", "publisher_warranty.contract"),
192                                                           ("create_date", ">=", limit_date),
193                                                           ("name", "=", message)])
194                 if ids:
195                     continue
196                 self.pool.get('res.log').create(cr, uid,
197                         {
198                             'name': message,
199                             'res_model': "publisher_warranty.contract",
200                             "read": True,
201                             "user_id": False,
202                         },
203                         context=context
204                 )
205         except Exception:
206             if cron_mode:
207                 return False # we don't want to see any stack trace in cron
208             else:
209                 raise
210         return True
211
212     def get_last_user_messages(self, cr, uid, limit, context=None):
213         """
214         Get the messages to be written in the web client.
215         @return: A list of html messages with ids, can be False or empty.
216         @rtype: list of tuples(int,string)
217         """
218         ids = self.pool.get('res.log').search(cr, uid, [("res_model", "=", "publisher_warranty.contract")]
219                                         , order="create_date desc", limit=limit)
220         if not ids:
221             return []
222         messages = [(x.id, x.name) for x in self.pool.get('res.log').browse(cr, uid, ids)]
223
224         return messages
225
226     def del_user_message(self, cr, uid, id, context=None):
227         """
228         Delete a message.
229         """
230         self.pool.get('res.log').unlink(cr, uid, [id])
231
232         return True
233
234     _columns = {
235         'name' : fields.char('Serial Key', size=384, required=True, help="Your OpenERP Publisher's Warranty Contract unique key, also called serial number."),
236         'date_start' : fields.date('Starting Date', readonly=True),
237         'date_stop' : fields.date('Ending Date', readonly=True),
238         'state' : fields.selection([('unvalidated', 'Unvalidated'), ('valid', 'Valid')
239                             , ('terminated', 'Terminated'), ('canceled', 'Canceled')], string="State", readonly=True),
240         'kind' : fields.char('Kind', size=64, readonly=True),
241         "check_support": fields.boolean("Support Level 1", readonly=True),
242         "check_opw": fields.boolean("OPW", readonly=True, help="Checked if this is an OpenERP Publisher's Warranty contract (versus older contract types"),
243     }
244
245     _defaults = {
246         'state': 'unvalidated',
247     }
248
249     _sql_constraints = [
250         ('uniq_name', 'unique(name)', "That contract is already registered in the system.")
251     ]
252
253 publisher_warranty_contract()
254
255 class maintenance_contract(osv.osv_memory):
256     """ Old osv we only keep for compatibility with the clients. """
257
258     _name = "maintenance.contract"
259
260     def status(self, cr, uid):
261         return self.pool.get("publisher_warranty.contract").status(cr, uid)
262
263     def send(self, cr, uid, tb, explanations, remarks=None, issue_name=None):
264         return self.pool.get("publisher_warranty.contract").send(cr, uid, tb,
265                         explanations, remarks, issue_name)
266
267 maintenance_contract()
268
269 class publisher_warranty_contract_wizard(osv.osv_memory):
270     """
271     A wizard osv to help people entering a publisher warranty contract.
272     """
273     _name = 'publisher_warranty.contract.wizard'
274     _inherit = "ir.wizard.screen"
275
276     _columns = {
277         'name' : fields.char('Serial Key', size=256, required=True, help="Your OpenERP Publisher's Warranty Contract unique key, also called serial number."),
278         'state' : fields.selection([("draft", "Draft"), ("finished", "Finished")])
279     }
280
281     _defaults = {
282         "state": "draft",
283     }
284
285     def action_validate(self, cr, uid, ids, context=None):
286         if not ids:
287             return False
288
289         wiz = self.browse(cr, uid, ids[0])
290         c_name = wiz.name
291
292         contract_osv = self.pool.get("publisher_warranty.contract")
293         contracts = contract_osv.search(cr, uid, [("name","=",c_name)])
294         if contracts:
295             raise osv.except_osv(_("Error"), _("That contract is already registered in the system."))
296
297         contract_id = contract_osv.create(cr, uid, {
298             "name": c_name,
299             "state": "unvalidated",
300         })
301
302         contract_osv.check_validity(cr, uid, [contract_id])
303
304         self.write(cr, uid, ids, {"state": "finished"})
305
306         # We should return an action ?
307         return True
308
309
310 publisher_warranty_contract_wizard()
311
312 def get_sys_logs(cr, uid):
313     """
314     Utility method to send a publisher warranty get logs messages.
315     """
316     pool = pooler.get_pool(cr.dbname)
317
318     dbuuid = pool.get('ir.config_parameter').get_param(cr, uid, 'database.uuid')
319     db_create_date = pool.get('ir.config_parameter').get_param(cr, uid, 'database.create_date')
320     limit_date = datetime.datetime.now()
321     limit_date = limit_date - datetime.timedelta(15)
322     limit_date_str = limit_date.strftime(misc.DEFAULT_SERVER_DATETIME_FORMAT)
323     nbr_users = pool.get("res.users").search(cr, uid, [], count=True)
324     nbr_active_users = pool.get("res.users").search(cr, uid, [("date", ">=", limit_date_str)], count=True)
325     nbr_share_users = False
326     nbr_active_share_users = False
327     if "share" in pool.get("res.users")._columns \
328             or "share" in pool.get("res.users")._inherit_fields:
329         nbr_share_users = pool.get("res.users").search(cr, uid, [("share", "=", True)], count=True)
330         nbr_active_share_users = pool.get("res.users").search(cr, uid, [("share", "=", True), ("date", ">=", limit_date_str)], count=True)
331     contractosv = pool.get('publisher_warranty.contract')
332     contracts = contractosv.browse(cr, uid, contractosv.search(cr, uid, []))
333     user = pool.get("res.users").browse(cr, uid, uid)
334     msg = {
335         "dbuuid": dbuuid,
336         "nbr_users": nbr_users,
337         "nbr_active_users": nbr_active_users,
338         "nbr_share_users": nbr_share_users,
339         "nbr_active_share_users": nbr_active_share_users,
340         "dbname": cr.dbname,
341         "db_create_date": db_create_date,
342         "version": release.version,
343         "contracts": [c.name for c in contracts],
344         "language": user.context_lang,
345     }
346
347     add_arg = {"timeout":30} if sys.version_info >= (2,6) else {}
348     arguments = {'arg0': msg, "action": "update",}
349     arguments_raw = urllib.urlencode(arguments)
350     url = config.get("publisher_warranty_url")
351     uo = urllib2.urlopen(url, arguments_raw, **add_arg)
352     try:
353         submit_result = uo.read()
354     finally:
355         uo.close()
356
357     result = safe_eval(submit_result) if submit_result else {}
358
359     return result
360
361 # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
362