{"action":"created","comment":{"url":"https://api.github.com/repos/odoo/odoo/pulls/comments/43097306","id":43097306,"diff_hunk":"@@ -0,0 +1,326 @@\n+# -*- coding: utf-8 -*-\n+# Part of Odoo. See LICENSE file for full copyright and licensing details.\n+\n+from odoo import api, fields, models, tools, _\n+from odoo.exceptions import AccessError\n+\n+\n+class ProjectIssue(models.Model):\n+    _name = \"project.issue\"\n+    _description = \"Project Issue\"\n+    _inherit = ['mail.thread', 'ir.needaction_mixin']\n+    _order = \"priority desc, create_date desc\"\n+    _mail_post_access = 'read'\n+\n+    def _get_default_partner(self):\n+        if 'default_project_id' in self.env.context:\n+            return self.env['project.project'].browse(self.env.context['default_project_id']).partner_id.id\n+\n+    name = fields.Char('Issue', required=True)\n+    active = fields.Boolean(default=True)\n+    create_date = fields.Datetime('Creation Date', readonly=True, index=True)\n+    write_date = fields.Datetime('Update Date', readonly=True)\n+    days_since_creation = fields.Integer(compute='_compute_inactivity_days', string='Days since creation date',\n+                                         help=\"Difference in days between creation date and current date\")\n+\n+    date_deadline = fields.Date('Deadline')\n+    team_id = fields.Many2one('crm.team', 'Sales Team', oldname='section_id', index=True, help=\"\"\"Sales team to which Case belongs to.\n+                              Define Responsible user and Email account for mail gateway.\"\"\",\n+                              default=lambda self: self.env['crm.team']._get_default_team_id())\n+    partner_id = fields.Many2one('res.partner', 'Contact', index=True)\n+    company_id = fields.Many2one('res.company', 'Company', default=lambda self: self.env.user.company_id)\n+    description = fields.Text('Private Note')\n+    kanban_state = fields.Selection([('normal', 'Normal'), ('blocked', 'Blocked'), ('done', 'Ready for next stage')], 'Kanban State',\n+                                    track_visibility='onchange', required=True, default='normal',\n+                                    help=\"\"\"A Issue's kanban state indicates special situations affecting it:\\n\n+                                           * Normal is the default situation\\n\n+                                           * Blocked indicates something is preventing the progress of this issue\\n\n+                                           * Ready for next stage indicates the issue is ready to be pulled to the next stage\"\"\")\n+    email_from = fields.Char('Email', help=\"These people will receive email.\", index=True)\n+    email_cc = fields.Char('Watchers Emails', help=\"\"\"These email addresses will be added to the CC field of all inbound\n+        and outbound emails for this record before being sent. Separate multiple email addresses with a comma\"\"\")\n+    date_open = fields.Datetime('Assigned', readonly=True, index=True)\n+    date_closed = fields.Datetime('Closed', readonly=True, index=True)\n+    date = fields.Datetime()\n+    date_last_stage_update = fields.Datetime('Last Stage Update', index=True, default=fields.Datetime.now)\n+    channel = fields.Char(help=\"Communication channel.\")\n+    tag_ids = fields.Many2many('project.tags', string='Tags')\n+    priority = fields.Selection([('0', 'Low'), ('1', 'Normal'), ('2', 'High')], 'Priority', index=True, default='0')\n+    stage_id = fields.Many2one('project.task.type', 'Stage', track_visibility='onchange', index=True,\n+                               domain=\"[('project_ids', '=', project_id)]\", copy=False,\n+                               default=lambda self: self.stage_find(self.env.context.get('default_project_id'), [('fold', '=', False)]))\n+    project_id = fields.Many2one('project.project', 'Project', track_visibility='onchange', index=True)\n+    duration = fields.Float()\n+    task_id = fields.Many2one('project.task', 'Task', domain=\"[('project_id','=',project_id)]\",\n+                              help=\"You can link this issue to an existing task or directly create a new one from here\")\n+    day_open = fields.Float(compute='_compute_day', string='Days to Assign', store=True)\n+    day_close = fields.Float(compute='_compute_day', string='Days to Close', store=True)\n+\n+    user_id = fields.Many2one('res.users', 'Assigned to', index=True, track_visibility='onchange', default=lambda self: self.env.uid)\n+    working_hours_open = fields.Float(compute='_compute_day', string='Working Hours to assign the Issue', store=True)\n+    working_hours_close = fields.Float(compute='_compute_day', string='Working Hours to close the Issue', store=True)\n+    inactivity_days = fields.Integer(compute='_compute_inactivity_days', string='Days since last action',\n+                                     help=\"Difference in days between last action and current date\")\n+    color = fields.Integer('Color Index')\n+    user_email = fields.Char(related='user_id.email', string='User Email', readonly=True)\n+    date_action_last = fields.Datetime('Last Action', readonly=True)\n+    date_action_next = fields.Datetime('Next Action', readonly=True)\n+    legend_blocked = fields.Char(related=\"stage_id.legend_blocked\", string='Kanban Blocked Explanation')\n+    legend_done = fields.Char(related=\"stage_id.legend_done\", string='Kanban Valid Explanation')\n+    legend_normal = fields.Char(related=\"stage_id.legend_normal\", string='Kanban Ongoing Explanation')\n+\n+    @api.multi\n+    def _read_group_stage_ids(self, domain, read_group_order=None, access_rights_uid=None):\n+        access_rights_uid = access_rights_uid or self.env.uid\n+        ProjectTaskType = self.env['project.task.type']\n+        order = ProjectTaskType._order\n+        if read_group_order == 'stage_id desc':\n+            order = \"%s desc\" % order\n+        search_domain = []\n+        if 'default_project_id' in self.env.context:\n+            search_domain = ['|', ('project_ids', '=', self.env.context['default_project_id']), ('id', 'in', self.ids)]\n+        else:\n+            search_domain = [('id', 'in', self.ids)]\n+        project_task_types = ProjectTaskType.sudo(access_rights_uid).search(search_domain, order=order)\n+        result = project_task_types.sudo(access_rights_uid).name_get()\n+        fold = {project_task_type.id: project_task_type.fold for project_task_type in project_task_types}\n+        return result, fold\n+\n+    _group_by_full = {\n+        'stage_id': _read_group_stage_ids\n+    }\n+\n+    @api.depends('create_date', 'date_closed', 'date_open')\n+    def _compute_day(self):\n+        Calendar = self.env['resource.calendar']\n+        for issue in self:\n+            calendar_id = issue.project_id.resource_calendar_id.id or None\n+\n+            dt_create_date = fields.Datetime.from_string(issue.create_date)\n+            if issue.date_open:\n+                dt_date_open = fields.Datetime.from_string(issue.date_open)\n+                issue.day_open = (dt_date_open - dt_create_date).total_seconds() / (24.0 * 3600)\n+                issue.working_hours_open = Calendar._model.get_working_hours(self.env.cr, self.env.uid, calendar_id, dt_create_date, dt_date_open,\n+                                                                             compute_leaves=True, resource_id=False, default_interval=(8, 16),\n+                                                                             context=self.env.context)\n+\n+            if issue.date_closed:\n+                dt_date_closed = fields.Datetime.from_string(issue.date_closed)\n+                issue.day_close = (dt_date_closed - dt_create_date).total_seconds() / (24.0 * 3600)\n+                issue.working_hours_close = Calendar._model.get_working_hours(self.env.cr, self.env.uid, calendar_id, dt_create_date, dt_date_closed,\n+                                                                              compute_leaves=True, resource_id=False, default_interval=(8, 16),\n+                                                                              context=self.env.context)\n+\n+    @api.depends('create_date', 'date_action_last', 'date_last_stage_update')\n+    def _compute_inactivity_days(self):\n+        current_datetime = fields.Datetime.from_string(fields.Datetime.now())\n+        for issue in self:\n+            dt_create_date = fields.Datetime.from_string(issue.create_date)\n+            issue.days_since_creation = (current_datetime - dt_create_date).days\n+\n+            if issue.date_action_last:\n+                issue.inactivity_days = (current_datetime - fields.Datetime.from_string(issue.date_action_last)).days\n+            elif issue.date_last_stage_update:\n+                issue.inactivity_days = (current_datetime - fields.Datetime.from_string(issue.date_last_stage_update)).days\n+            else:\n+                issue.inactivity_days = (current_datetime - dt_create_date).days\n+\n+    @api.onchange('partner_id')\n+    def onchange_partner_id(self):\n+        \"\"\" This function sets partner email address based on partner\n+        \"\"\"\n+        self.email_from = self.partner_id.email\n+\n+    @api.multi\n+    def on_change_project(self, project_id):\n+        if project_id:\n+            project = self.env['project.project'].browse(project_id)\n+            return {'value': {'partner_id': project.partner_id.id}}\n+\n+    @api.onchange('task_id')\n+    def onchange_task_id(self):\n+        self.user_id = self.task_id.user_id.id\n+\n+    @api.multi\n+    def copy_data(self, default={}):\n+        self.ensure_one()\n+        default.update(name=_('%s (copy)') % (self.name or \"\"))\n+        return super(ProjectIssue, self).copy_data(default)[0]\n+\n+    @api.model\n+    def create(self, vals):\n+        context = dict(self.env.context or {})\n+        if vals.get('project_id') and not self.env.context.get('default_project_id'):\n+            context['default_project_id'] = vals.get('project_id')\n+        if vals.get('user_id'):\n+            vals['date_open'] = fields.Datetime.now()\n+        if 'stage_id' in vals:\n+            vals.update(self.update_date_closed(vals['stage_id']))\n+\n+        # context: no_log, because subtype already handle this\n+        context['mail_create_nolog'] = True\n+        return super(ProjectIssue, self.with_context(context)).create(vals)\n+\n+    @api.multi\n+    def write(self, vals):\n+        # stage change: update date_last_stage_update\n+        if 'stage_id' in vals:\n+            vals.update(self.update_date_closed(vals['stage_id']))\n+            vals['date_last_stage_update'] = fields.Datetime.now()\n+            if 'kanban_state' not in vals:\n+                vals['kanban_state'] = 'normal'\n+        # user_id change: update date_start\n+        if vals.get('user_id'):\n+            vals['date_open'] = fields.Datetime.now()\n+        return super(ProjectIssue, self).write(vals)\n+\n+    @api.model\n+    def get_empty_list_help(self, help):\n+        return super(ProjectIssue, self.with_context(empty_list_help_model='project.project',\n+                                                     empty_list_help_id=self.env.context.get('default_project_id'),\n+                                                     empty_list_help_document_name=_(\"issues\"))).get_empty_list_help(help)\n+\n+    # -------------------------------------------------------\n+    # Stage management\n+    # -------------------------------------------------------\n+\n+    def update_date_closed(self, stage_id):\n+        project_task_type = self.env['project.task.type'].browse(stage_id)\n+        if project_task_type.fold:\n+            return {'date_closed': fields.Datetime.now()}\n+        return {'date_closed': False}\n+\n+    def stage_find(self, project_id, domain=None, order='sequence'):\n+        \"\"\" Override of the base.stage method\n+            Parameter of the stage search taken from the issue:\n+            - project_id: if set, stages must belong to this project or\n+              be a default case\n+        \"\"\"\n+        search_domain = []\n+        if project_id:\n+            search_domain = [('project_ids', '=', project_id)]\n+        if domain:\n+            search_domain += list(domain)\n+        project_task_type = self.env['project.task.type'].search(search_domain, order=order, limit=1)\n+        return project_task_type.id\n+\n+    # -------------------------------------------------------\n+    # Mail gateway\n+    # -------------------------------------------------------\n+\n+    @api.multi\n+    def _track_subtype(self, init_values):\n+        self.ensure_one()\n+        if 'kanban_state' in init_values and self.kanban_state == 'blocked':\n+            return 'project_issue.mt_issue_blocked'\n+        elif 'kanban_state' in init_values and self.kanban_state == 'done':\n+            return 'project_issue.mt_issue_ready'\n+        elif 'user_id' in init_values and self.user_id:  # assigned -> new\n+            return 'project_issue.mt_issue_new'\n+        elif 'stage_id' in init_values and self.stage_id and self.stage_id.sequence <= 1:  # start stage -> new\n+            return 'project_issue.mt_issue_new'\n+        elif 'stage_id' in init_values:\n+            return 'project_issue.mt_issue_stage'\n+        return super(ProjectIssue, self)._track_subtype(init_values)\n+\n+    @api.multi\n+    def _notification_group_recipients(self, message, recipients, done_ids, group_data):\n+        \"\"\" Override the mail.thread method to handle project users and officers\n+        recipients. Indeed those will have specific action in their notification\n+        emails: creating tasks, assigning it. \"\"\"\n+        group_project_user = self.env.ref('project.group_project_user')\n+        for recipient in recipients:\n+            if recipient.id in done_ids:\n+                continue\n+            if recipient.user_ids and group_project_user.id in recipient.user_ids[0].groups_id.ids:\n+                group_data['group_project_user'] |= recipient\n+                done_ids.add(recipient.id)\n+        return super(ProjectIssue, self)._notification_group_recipients(message, recipients, done_ids, group_data)\n+\n+    @api.multi\n+    def _notification_get_recipient_groups(self, message, recipients):\n+        self.ensure_one()\n+        res = super(ProjectIssue, self)._notification_get_recipient_groups(message, recipients)\n+\n+        actions = []\n+        if not self.user_id:\n+            take_action = self._notification_link_helper('assign')\n+            actions.append({'url': take_action, 'title': _('I take it')})\n+        else:\n+            new_action_id = self.pool['ir.model.data'].xmlid_to_res_id('project_issue.project_issue_categ_act0')\n+            new_action = self._notification_link_helper('new', action_id=new_action_id)\n+            actions.append({'url': new_action, 'title': _('New Issue')})\n+\n+        res['group_project_user'] = {\n+            'actions': actions\n+        }\n+        return res\n+\n+    @api.model\n+    def message_get_reply_to(self, res_ids, default=None):\n+        \"\"\" Override to get the reply_to of the parent project. \"\"\"\n+        issues = self.browse(res_ids)\n+        project_ids = set(map(lambda issue: issue.project_id.id, issues.filtered('project_id')))\n+        aliases = self.env['project.project'].message_get_reply_to(list(project_ids), default=default)\n+        return dict((issue.id, aliases.get(issue.project_id and issue.project_id.id or 0, False)) for issue in issues)\n+\n+    @api.multi\n+    def message_get_suggested_recipients(self):\n+        recipients = super(ProjectIssue, self).message_get_suggested_recipients()\n+        try:\n+            for issue in self:\n+                if issue.partner_id:\n+                    issue._message_add_suggested_recipient(recipients, partner=issue.partner_id, reason=_('Customer'))\n+                elif issue.email_from:\n+                    issue._message_add_suggested_recipient(recipients, email=issue.email_from, reason=_('Customer Email'))\n+        except AccessError:  # no read access rights -> just ignore suggested recipients because this imply modifying followers\n+            pass\n+        return recipients\n+\n+    @api.multi\n+    def email_split(self, msg):\n+        email_list = tools.email_split((msg.get('to') or '') + ',' + (msg.get('cc') or ''))\n+        aliases = [issue.project_id.alias_name for issue in self.filtered('project_id')]\n+        return filter(lambda x: x.split('@')[0] not in aliases, email_list)\n+\n+    @api.model\n+    def message_new(self, msg, custom_values=None):\n+        \"\"\" Overrides mail_thread message_new that is called by the mailgateway\n+            through message_process.\n+            This override updates the document according to the email.\n+        \"\"\"\n+        defaults = {\n+            'name':  msg.get('subject') or _(\"No Subject\"),\n+            'email_from': msg.get('from'),\n+            'email_cc': msg.get('cc'),\n+            'partner_id': msg.get('author_id', False),\n+            'user_id': False,\n+        }\n+        if custom_values:\n+            defaults.update(custom_values)\n+\n+        res_id = super(ProjectIssue, self.with_context(state_to='draft')).message_new(msg, custom_values=defaults)\n+        issue = self.browse(res_id)\n+        email_list = issue.email_split(msg)\n+        partner_ids = issue._find_partner_from_emails(email_list, force_create=True)\n+        issue.message_subscribe(partner_ids)\n+        return res_id\n+\n+    @api.multi\n+    def message_update(self, msg, update_vals=None):\n+        \"\"\" Override to update the issue according to the email. \"\"\"\n+        email_list = self.email_split(msg)\n+        partner_ids = self._find_partner_from_emails(email_list, force_create=True)\n+        self.message_subscribe(partner_ids)\n+        return super(ProjectIssue, self).message_update(msg, update_vals=update_vals)\n+\n+    @api.multi\n+    def message_post(self, subtype=None, **kwargs):\n+        \"\"\" Overrides mail_thread message_post so that we can set the date of last action field when\n+            a new message is posted on the issue.\n+        \"\"\"\n+        self.ensure_one()\n+        mail_message = super(ProjectIssue, self).message_post(subtype=subtype, **kwargs)\n+        if self.id and subtype:\n+            self.write({'date_action_last': fields.Datetime.now()})","path":"addons/project_issue/models/project_issue.py","position":325,"original_position":325,"commit_id":"eb92a23c510e9658c1442adcea08d07ef91451a8","original_commit_id":"eb92a23c510e9658c1442adcea08d07ef91451a8","user":{"login":"mdi-odoo","id":7599791,"avatar_url":"https://avatars.githubusercontent.com/u/7599791?v=3","gravatar_id":"","url":"https://api.github.com/users/mdi-odoo","html_url":"https://github.com/mdi-odoo","followers_url":"https://api.github.com/users/mdi-odoo/followers","following_url":"https://api.github.com/users/mdi-odoo/following{/other_user}","gists_url":"https://api.github.com/users/mdi-odoo/gists{/gist_id}","starred_url":"https://api.github.com/users/mdi-odoo/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/mdi-odoo/subscriptions","organizations_url":"https://api.github.com/users/mdi-odoo/orgs","repos_url":"https://api.github.com/users/mdi-odoo/repos","events_url":"https://api.github.com/users/mdi-odoo/events{/privacy}","received_events_url":"https://api.github.com/users/mdi-odoo/received_events","type":"User","site_admin":false},"body":"No need to check self.id .","created_at":"2015-10-27T09:14:50Z","updated_at":"2015-10-27T09:14:50Z","html_url":"https://github.com/odoo/odoo/pull/7787#discussion_r43097306","pull_request_url":"https://api.github.com/repos/odoo/odoo/pulls/7787","_links":{"self":{"href":"https://api.github.com/repos/odoo/odoo/pulls/comments/43097306"},"html":{"href":"https://github.com/odoo/odoo/pull/7787#discussion_r43097306"},"pull_request":{"href":"https://api.github.com/repos/odoo/odoo/pulls/7787"}}},"pull_request":{"url":"https://api.github.com/repos/odoo/odoo/pulls/7787","id":41120043,"html_url":"https://github.com/odoo/odoo/pull/7787","diff_url":"https://github.com/odoo/odoo/pull/7787.diff","patch_url":"https://github.com/odoo/odoo/pull/7787.patch","issue_url":"https://api.github.com/repos/odoo/odoo/issues/7787","number":7787,"state":"open","locked":false,"title":"[MIGRATE]Project_issue: Master project issue newapi","user":{"login":"atp-odoo","id":7599410,"avatar_url":"https://avatars.githubusercontent.com/u/7599410?v=3","gravatar_id":"","url":"https://api.github.com/users/atp-odoo","html_url":"https://github.com/atp-odoo","followers_url":"https://api.github.com/users/atp-odoo/followers","following_url":"https://api.github.com/users/atp-odoo/following{/other_user}","gists_url":"https://api.github.com/users/atp-odoo/gists{/gist_id}","starred_url":"https://api.github.com/users/atp-odoo/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/atp-odoo/subscriptions","organizations_url":"https://api.github.com/users/atp-odoo/orgs","repos_url":"https://api.github.com/users/atp-odoo/repos","events_url":"https://api.github.com/users/atp-odoo/events{/privacy}","received_events_url":"https://api.github.com/users/atp-odoo/received_events","type":"User","site_admin":false},"body":"Task: https://www.odoo.com/web?#id=13729&view_type=form&model=project.task&action=327\r\nPad: https://pad.odoo.com/p/r.xV4Xz2Agsil6yYs9","created_at":"2015-07-29T13:01:38Z","updated_at":"2015-10-27T09:14:50Z","closed_at":null,"merged_at":null,"merge_commit_sha":"4507119e40276213969178100cd191438aa3fcb0","assignee":null,"milestone":null,"commits_url":"https://api.github.com/repos/odoo/odoo/pulls/7787/commits","review_comments_url":"https://api.github.com/repos/odoo/odoo/pulls/7787/comments","review_comment_url":"https://api.github.com/repos/odoo/odoo/pulls/comments{/number}","comments_url":"https://api.github.com/repos/odoo/odoo/issues/7787/comments","statuses_url":"https://api.github.com/repos/odoo/odoo/statuses/eb92a23c510e9658c1442adcea08d07ef91451a8","head":{"label":"odoo-dev:master-project-issue-newapi-vbh","ref":"master-project-issue-newapi-vbh","sha":"eb92a23c510e9658c1442adcea08d07ef91451a8","user":{"login":"odoo-dev","id":7466976,"avatar_url":"https://avatars.githubusercontent.com/u/7466976?v=3","gravatar_id":"","url":"https://api.github.com/users/odoo-dev","html_url":"https://github.com/odoo-dev","followers_url":"https://api.github.com/users/odoo-dev/followers","following_url":"https://api.github.com/users/odoo-dev/following{/other_user}","gists_url":"https://api.github.com/users/odoo-dev/gists{/gist_id}","starred_url":"https://api.github.com/users/odoo-dev/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/odoo-dev/subscriptions","organizations_url":"https://api.github.com/users/odoo-dev/orgs","repos_url":"https://api.github.com/users/odoo-dev/repos","events_url":"https://api.github.com/users/odoo-dev/events{/privacy}","received_events_url":"https://api.github.com/users/odoo-dev/received_events","type":"Organization","site_admin":false},"repo":{"id":19853934,"name":"odoo","full_name":"odoo-dev/odoo","owner":{"login":"odoo-dev","id":7466976,"avatar_url":"https://avatars.githubusercontent.com/u/7466976?v=3","gravatar_id":"","url":"https://api.github.com/users/odoo-dev","html_url":"https://github.com/odoo-dev","followers_url":"https://api.github.com/users/odoo-dev/followers","following_url":"https://api.github.com/users/odoo-dev/following{/other_user}","gists_url":"https://api.github.com/users/odoo-dev/gists{/gist_id}","starred_url":"https://api.github.com/users/odoo-dev/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/odoo-dev/subscriptions","organizations_url":"https://api.github.com/users/odoo-dev/orgs","repos_url":"https://api.github.com/users/odoo-dev/repos","events_url":"https://api.github.com/users/odoo-dev/events{/privacy}","received_events_url":"https://api.github.com/users/odoo-dev/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/odoo-dev/odoo","description":"RD Dev Repository","fork":true,"url":"https://api.github.com/repos/odoo-dev/odoo","forks_url":"https://api.github.com/repos/odoo-dev/odoo/forks","keys_url":"https://api.github.com/repos/odoo-dev/odoo/keys{/key_id}","collaborators_url":"https://api.github.com/repos/odoo-dev/odoo/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/odoo-dev/odoo/teams","hooks_url":"https://api.github.com/repos/odoo-dev/odoo/hooks","issue_events_url":"https://api.github.com/repos/odoo-dev/odoo/issues/events{/number}","events_url":"https://api.github.com/repos/odoo-dev/odoo/events","assignees_url":"https://api.github.com/repos/odoo-dev/odoo/assignees{/user}","branches_url":"https://api.github.com/repos/odoo-dev/odoo/branches{/branch}","tags_url":"https://api.github.com/repos/odoo-dev/odoo/tags","blobs_url":"https://api.github.com/repos/odoo-dev/odoo/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/odoo-dev/odoo/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/odoo-dev/odoo/git/refs{/sha}","trees_url":"https://api.github.com/repos/odoo-dev/odoo/git/trees{/sha}","statuses_url":"https://api.github.com/repos/odoo-dev/odoo/statuses/{sha}","languages_url":"https://api.github.com/repos/odoo-dev/odoo/languages","stargazers_url":"https://api.github.com/repos/odoo-dev/odoo/stargazers","contributors_url":"https://api.github.com/repos/odoo-dev/odoo/contributors","subscribers_url":"https://api.github.com/repos/odoo-dev/odoo/subscribers","subscription_url":"https://api.github.com/repos/odoo-dev/odoo/subscription","commits_url":"https://api.github.com/repos/odoo-dev/odoo/commits{/sha}","git_commits_url":"https://api.github.com/repos/odoo-dev/odoo/git/commits{/sha}","comments_url":"https://api.github.com/repos/odoo-dev/odoo/comments{/number}","issue_comment_url":"https://api.github.com/repos/odoo-dev/odoo/issues/comments{/number}","contents_url":"https://api.github.com/repos/odoo-dev/odoo/contents/{+path}","compare_url":"https://api.github.com/repos/odoo-dev/odoo/compare/{base}...{head}","merges_url":"https://api.github.com/repos/odoo-dev/odoo/merges","archive_url":"https://api.github.com/repos/odoo-dev/odoo/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/odoo-dev/odoo/downloads","issues_url":"https://api.github.com/repos/odoo-dev/odoo/issues{/number}","pulls_url":"https://api.github.com/repos/odoo-dev/odoo/pulls{/number}","milestones_url":"https://api.github.com/repos/odoo-dev/odoo/milestones{/number}","notifications_url":"https://api.github.com/repos/odoo-dev/odoo/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/odoo-dev/odoo/labels{/name}","releases_url":"https://api.github.com/repos/odoo-dev/odoo/releases{/id}","created_at":"2014-05-16T11:16:19Z","updated_at":"2015-10-13T05:17:28Z","pushed_at":"2015-10-27T09:13:58Z","git_url":"git://github.com/odoo-dev/odoo.git","ssh_url":"git@github.com:odoo-dev/odoo.git","clone_url":"https://github.com/odoo-dev/odoo.git","svn_url":"https://github.com/odoo-dev/odoo","homepage":"https://www.odoo.com","size":1450781,"stargazers_count":62,"watchers_count":62,"language":null,"has_issues":false,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":24,"mirror_url":null,"open_issues_count":0,"forks":24,"open_issues":0,"watchers":62,"default_branch":"master"}},"base":{"label":"odoo:master","ref":"master","sha":"e217663023041d467611ded5776e7205855b5c59","user":{"login":"odoo","id":6368483,"avatar_url":"https://avatars.githubusercontent.com/u/6368483?v=3","gravatar_id":"","url":"https://api.github.com/users/odoo","html_url":"https://github.com/odoo","followers_url":"https://api.github.com/users/odoo/followers","following_url":"https://api.github.com/users/odoo/following{/other_user}","gists_url":"https://api.github.com/users/odoo/gists{/gist_id}","starred_url":"https://api.github.com/users/odoo/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/odoo/subscriptions","organizations_url":"https://api.github.com/users/odoo/orgs","repos_url":"https://api.github.com/users/odoo/repos","events_url":"https://api.github.com/users/odoo/events{/privacy}","received_events_url":"https://api.github.com/users/odoo/received_events","type":"Organization","site_admin":false},"repo":{"id":19745004,"name":"odoo","full_name":"odoo/odoo","owner":{"login":"odoo","id":6368483,"avatar_url":"https://avatars.githubusercontent.com/u/6368483?v=3","gravatar_id":"","url":"https://api.github.com/users/odoo","html_url":"https://github.com/odoo","followers_url":"https://api.github.com/users/odoo/followers","following_url":"https://api.github.com/users/odoo/following{/other_user}","gists_url":"https://api.github.com/users/odoo/gists{/gist_id}","starred_url":"https://api.github.com/users/odoo/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/odoo/subscriptions","organizations_url":"https://api.github.com/users/odoo/orgs","repos_url":"https://api.github.com/users/odoo/repos","events_url":"https://api.github.com/users/odoo/events{/privacy}","received_events_url":"https://api.github.com/users/odoo/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/odoo/odoo","description":"Odoo (formerly OpenERP). Open Source Apps To Grow Your Business.","fork":false,"url":"https://api.github.com/repos/odoo/odoo","forks_url":"https://api.github.com/repos/odoo/odoo/forks","keys_url":"https://api.github.com/repos/odoo/odoo/keys{/key_id}","collaborators_url":"https://api.github.com/repos/odoo/odoo/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/odoo/odoo/teams","hooks_url":"https://api.github.com/repos/odoo/odoo/hooks","issue_events_url":"https://api.github.com/repos/odoo/odoo/issues/events{/number}","events_url":"https://api.github.com/repos/odoo/odoo/events","assignees_url":"https://api.github.com/repos/odoo/odoo/assignees{/user}","branches_url":"https://api.github.com/repos/odoo/odoo/branches{/branch}","tags_url":"https://api.github.com/repos/odoo/odoo/tags","blobs_url":"https://api.github.com/repos/odoo/odoo/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/odoo/odoo/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/odoo/odoo/git/refs{/sha}","trees_url":"https://api.github.com/repos/odoo/odoo/git/trees{/sha}","statuses_url":"https://api.github.com/repos/odoo/odoo/statuses/{sha}","languages_url":"https://api.github.com/repos/odoo/odoo/languages","stargazers_url":"https://api.github.com/repos/odoo/odoo/stargazers","contributors_url":"https://api.github.com/repos/odoo/odoo/contributors","subscribers_url":"https://api.github.com/repos/odoo/odoo/subscribers","subscription_url":"https://api.github.com/repos/odoo/odoo/subscription","commits_url":"https://api.github.com/repos/odoo/odoo/commits{/sha}","git_commits_url":"https://api.github.com/repos/odoo/odoo/git/commits{/sha}","comments_url":"https://api.github.com/repos/odoo/odoo/comments{/number}","issue_comment_url":"https://api.github.com/repos/odoo/odoo/issues/comments{/number}","contents_url":"https://api.github.com/repos/odoo/odoo/contents/{+path}","compare_url":"https://api.github.com/repos/odoo/odoo/compare/{base}...{head}","merges_url":"https://api.github.com/repos/odoo/odoo/merges","archive_url":"https://api.github.com/repos/odoo/odoo/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/odoo/odoo/downloads","issues_url":"https://api.github.com/repos/odoo/odoo/issues{/number}","pulls_url":"https://api.github.com/repos/odoo/odoo/pulls{/number}","milestones_url":"https://api.github.com/repos/odoo/odoo/milestones{/number}","notifications_url":"https://api.github.com/repos/odoo/odoo/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/odoo/odoo/labels{/name}","releases_url":"https://api.github.com/repos/odoo/odoo/releases{/id}","created_at":"2014-05-13T15:38:58Z","updated_at":"2015-10-27T03:59:15Z","pushed_at":"2015-10-27T09:14:00Z","git_url":"git://github.com/odoo/odoo.git","ssh_url":"git@github.com:odoo/odoo.git","clone_url":"https://github.com/odoo/odoo.git","svn_url":"https://github.com/odoo/odoo","homepage":"https://www.odoo.com","size":1606998,"stargazers_count":3296,"watchers_count":3296,"language":"Python","has_issues":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":2923,"mirror_url":null,"open_issues_count":2697,"forks":2923,"open_issues":2697,"watchers":3296,"default_branch":"9.0"}},"_links":{"self":{"href":"https://api.github.com/repos/odoo/odoo/pulls/7787"},"html":{"href":"https://github.com/odoo/odoo/pull/7787"},"issue":{"href":"https://api.github.com/repos/odoo/odoo/issues/7787"},"comments":{"href":"https://api.github.com/repos/odoo/odoo/issues/7787/comments"},"review_comments":{"href":"https://api.github.com/repos/odoo/odoo/pulls/7787/comments"},"review_comment":{"href":"https://api.github.com/repos/odoo/odoo/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/odoo/odoo/pulls/7787/commits"},"statuses":{"href":"https://api.github.com/repos/odoo/odoo/statuses/eb92a23c510e9658c1442adcea08d07ef91451a8"}}}}