PageRenderTime 239ms CodeModel.GetById 28ms RepoModel.GetById 0ms app.codeStats 0ms

/addons/im_livechat/models/im_livechat_channel.py

https://gitlab.com/thanhchatvn/cloud-odoo
Python | 258 lines | 252 code | 3 blank | 3 comment | 3 complexity | 94bfed028f79d33b7fabe741c60034ab MD5 | raw file
  1. # -*- coding: utf-8 -*-
  2. import json
  3. import openerp
  4. import random
  5. import re
  6. from datetime import datetime, timedelta
  7. from openerp import api, fields, models
  8. from openerp import tools
  9. class ImLivechatChannel(models.Model):
  10. """ Livechat Channel
  11. Define a communication channel, which can be accessed with 'script_external' (script tag to put on
  12. external website), 'script_internal' (code to be integrated with odoo website) or via 'web_page' link.
  13. It provides rating tools, and access rules for anonymous people.
  14. """
  15. _name = 'im_livechat.channel'
  16. _description = 'Livechat Channel'
  17. def _default_image(self):
  18. image_path = openerp.modules.get_module_resource('im_livechat', 'static/src/img', 'default.png')
  19. return tools.image_resize_image_big(open(image_path, 'rb').read().encode('base64'))
  20. def _default_user_ids(self):
  21. return [(6, 0, [self._uid])]
  22. # attribute fields
  23. name = fields.Char('Name', required=True, help="The name of the channel")
  24. button_text = fields.Char('Text of the Button', default='Have a Question? Chat with us.',
  25. help="Default text displayed on the Livechat Support Button")
  26. default_message = fields.Char('Welcome Message', default='How may I help you?',
  27. help="This is an automated 'welcome' message that your visitor will see when they initiate a new conversation.")
  28. input_placeholder = fields.Char('Chat Input Placeholder')
  29. # computed fields
  30. web_page = fields.Char('Web Page', compute='_compute_web_page_link', store=False, readonly=True,
  31. help="URL to a static page where you client can discuss with the operator of the channel.")
  32. are_you_inside = fields.Boolean(string='Are you inside the matrix?',
  33. compute='_are_you_inside', store=False, readonly=True)
  34. script_external = fields.Text('Script (external)', compute='_compute_script_external', store=False, readonly=True)
  35. nbr_channel = fields.Integer('Number of conversation', compute='_compute_nbr_channel', store=False, readonly=True)
  36. rating_percentage_satisfaction = fields.Integer(
  37. '% Happy', compute='_compute_percentage_satisfaction', store=False, default=-1,
  38. help="Percentage of happy ratings over the past 7 days")
  39. # images fields
  40. image = fields.Binary('Image', default=_default_image, attachment=True,
  41. help="This field holds the image used as photo for the group, limited to 1024x1024px.")
  42. image_medium = fields.Binary('Medium', attachment=True,
  43. help="Medium-sized photo of the group. It is automatically "\
  44. "resized as a 128x128px image, with aspect ratio preserved. "\
  45. "Use this field in form views or some kanban views.")
  46. image_small = fields.Binary('Thumbnail', attachment=True,
  47. help="Small-sized photo of the group. It is automatically "\
  48. "resized as a 64x64px image, with aspect ratio preserved. "\
  49. "Use this field anywhere a small image is required.")
  50. # relationnal fields
  51. user_ids = fields.Many2many('res.users', 'im_livechat_channel_im_user', 'channel_id', 'user_id', string='Operators', default=_default_user_ids)
  52. channel_ids = fields.One2many('mail.channel', 'livechat_channel_id', 'Sessions')
  53. rule_ids = fields.One2many('im_livechat.channel.rule', 'channel_id', 'Rules')
  54. @api.one
  55. def _are_you_inside(self):
  56. self.are_you_inside = bool(self.env.uid in [u.id for u in self.user_ids])
  57. @api.multi
  58. def _compute_script_external(self):
  59. view = self.env['ir.model.data'].get_object('im_livechat', 'external_loader')
  60. values = {
  61. "url": self.env['ir.config_parameter'].sudo().get_param('web.base.url'),
  62. "dbname": self._cr.dbname,
  63. }
  64. for record in self:
  65. values["channel_id"] = record.id
  66. record.script_external = view.render(values)
  67. @api.multi
  68. def _compute_web_page_link(self):
  69. base_url = self.env['ir.config_parameter'].sudo().get_param('web.base.url')
  70. for record in self:
  71. record.web_page = "%s/im_livechat/support/%i" % (base_url, record.id)
  72. @api.multi
  73. @api.depends('channel_ids')
  74. def _compute_nbr_channel(self):
  75. for record in self:
  76. record.nbr_channel = len(record.channel_ids)
  77. @api.multi
  78. @api.depends('channel_ids.rating_ids')
  79. def _compute_percentage_satisfaction(self):
  80. for record in self:
  81. dt = fields.Datetime.to_string(datetime.utcnow() - timedelta(days=7))
  82. repartition = record.channel_ids.rating_get_grades([('create_date', '>=', dt)])
  83. total = sum(repartition.values())
  84. if total > 0:
  85. happy = repartition['great']
  86. record.rating_percentage_satisfaction = ((happy*100) / total) if happy > 0 else 0
  87. else:
  88. record.rating_percentage_satisfaction = -1
  89. @api.model
  90. def create(self, vals):
  91. tools.image_resize_images(vals)
  92. return super(ImLivechatChannel, self).create(vals)
  93. @api.multi
  94. def write(self, vals):
  95. tools.image_resize_images(vals)
  96. return super(ImLivechatChannel, self).write(vals)
  97. # --------------------------
  98. # Action Methods
  99. # --------------------------
  100. @api.multi
  101. def action_join(self):
  102. self.ensure_one()
  103. return self.write({'user_ids': [(4, self._uid)]})
  104. @api.multi
  105. def action_quit(self):
  106. self.ensure_one()
  107. return self.write({'user_ids': [(3, self._uid)]})
  108. @api.multi
  109. def action_view_rating(self):
  110. """ Action to display the rating relative to the channel, so all rating of the
  111. sessions of the current channel
  112. :returns : the ir.action 'action_view_rating' with the correct domain
  113. """
  114. self.ensure_one()
  115. action = self.env['ir.actions.act_window'].for_xml_id('rating', 'action_view_rating')
  116. action['domain'] = [('res_id', 'in', [s.id for s in self.channel_ids]), ('res_model', '=', 'mail.channel')]
  117. return action
  118. # --------------------------
  119. # Channel Methods
  120. # --------------------------
  121. @api.multi
  122. def get_available_users(self):
  123. """ get available user of a given channel
  124. :retuns : return the res.users having their im_status online
  125. """
  126. self.ensure_one()
  127. return self.sudo().user_ids.filtered(lambda user: user.im_status == 'online')
  128. @api.model
  129. def get_mail_channel(self, livechat_channel_id, anonymous_name):
  130. """ Return a mail.channel given a livechat channel. It creates one with a connected operator, or return false otherwise
  131. :param livechat_channel_id : the identifier if the im_livechat.channel
  132. :param anonymous_name : the name of the anonymous person of the channel
  133. :type livechat_channel_id : int
  134. :type anonymous_name : str
  135. :return : channel header
  136. :rtype : dict
  137. """
  138. # get the avalable user of the channel
  139. users = self.sudo().browse(livechat_channel_id).get_available_users()
  140. if len(users) == 0:
  141. return False
  142. # choose the res.users operator and get its partner id
  143. user = random.choice(users)
  144. operator_partner_id = user.partner_id.id
  145. # partner to add to the mail.channel
  146. channel_partner_to_add = [(4, operator_partner_id)]
  147. if self.env.uid: # if the user if logged (portal user), he can be identify
  148. channel_partner_to_add.append((4, self.env.user.partner_id.id))
  149. # create the session, and add the link with the given channel
  150. mail_channel = self.env["mail.channel"].with_context(mail_create_nosubscribe=False).sudo().create({
  151. 'channel_partner_ids': channel_partner_to_add,
  152. 'livechat_channel_id': livechat_channel_id,
  153. 'anonymous_name': anonymous_name,
  154. 'channel_type': 'livechat',
  155. 'name': ', '.join([anonymous_name, user.name]),
  156. 'public': 'private',
  157. 'email_send': False,
  158. })
  159. return mail_channel.sudo().with_context(im_livechat_operator_partner_id=operator_partner_id).channel_info()[0]
  160. @api.model
  161. def get_channel_infos(self, channel_id):
  162. channel = self.browse(channel_id)
  163. return {
  164. 'button_text': channel.button_text,
  165. 'input_placeholder': channel.input_placeholder,
  166. 'default_message': channel.default_message,
  167. "channel_name": channel.name,
  168. "channel_id": channel.id,
  169. }
  170. @api.model
  171. def get_livechat_info(self, channel_id, username='Visitor'):
  172. info = {}
  173. info['available'] = len(self.browse(channel_id).get_available_users()) > 0
  174. info['server_url'] = self.env['ir.config_parameter'].get_param('web.base.url')
  175. if info['available']:
  176. info['options'] = self.sudo().get_channel_infos(channel_id)
  177. info['options']["default_username"] = username
  178. return info
  179. # FIXME: to remove in master
  180. @api.model
  181. def match_rules(self, request, channel_id, username='Visitor'):
  182. pass
  183. class ImLivechatChannelRule(models.Model):
  184. """ Channel Rules
  185. Rules defining access to the channel (countries, and url matching). It also provide the 'auto pop'
  186. option to open automatically the conversation.
  187. """
  188. _name = 'im_livechat.channel.rule'
  189. _description = 'Channel Rules'
  190. _order = 'sequence asc'
  191. regex_url = fields.Char('URL Regex',
  192. help="Regular expression identifying the web page on which the rules will be applied.")
  193. action = fields.Selection([('display_button', 'Display the button'), ('auto_popup', 'Auto popup'), ('hide_button', 'Hide the button')],
  194. string='Action', required=True, default='display_button',
  195. help="* Select 'Display the button' to simply display the chat button on the pages.\n"\
  196. "* Select 'Auto popup' for to display the button, and automatically open the conversation window.\n"\
  197. "* Select 'Hide the button' to hide the chat button on the pages.")
  198. auto_popup_timer = fields.Integer('Auto popup timer', default=0,
  199. help="Delay (in seconds) to automatically open the converssation window. Note : the selected action must be 'Auto popup', otherwise this parameter will not be take into account.")
  200. channel_id = fields.Many2one('im_livechat.channel', 'Channel',
  201. help="The channel of the rule")
  202. country_ids = fields.Many2many('res.country', 'im_livechat_channel_country_rel', 'channel_id', 'country_id', 'Country',
  203. help="The actual rule will match only for this country. So if you set select 'Belgium' and 'France' and you set the action to 'Hide Buttun', this 2 country will not be see the support button for the specified URL. This feature requires GeoIP installed on your server.")
  204. sequence = fields.Integer('Matching order', default=10,
  205. help="Given the order to find a matching rule. If 2 rules are matching for the given url/country, the one with the lowest sequence will be chosen.")
  206. def match_rule(self, channel_id, url, country_id=False):
  207. """ determine if a rule of the given channel match with the given url
  208. :param channel_id : the identifier of the channel_id
  209. :param url : the url to match with a rule
  210. :param country_id : the identifier of the country
  211. :returns the rule that match the given condition. False otherwise.
  212. :rtype : im_livechat.channel.rule
  213. """
  214. def _match(rules):
  215. for rule in rules:
  216. if re.search(rule.regex_url, url):
  217. return rule
  218. return False
  219. # first, search the country specific rules (the first match is returned)
  220. if country_id: # don't include the country in the research if geoIP is not installed
  221. domain = [('country_ids', 'in', [country_id]), ('channel_id', '=', channel_id)]
  222. rule = _match(self.search(domain))
  223. if rule:
  224. return rule
  225. # second, fallback on the rules without country
  226. domain = [('country_ids', '=', False), ('channel_id', '=', channel_id)]
  227. return _match(self.search(domain))