diff --git a/.docker_files/main/__manifest__.py b/.docker_files/main/__manifest__.py index 3ba7f6b..a5faf36 100644 --- a/.docker_files/main/__manifest__.py +++ b/.docker_files/main/__manifest__.py @@ -13,7 +13,8 @@ 'depends': [ 'base', 'github_event', - 'github_pull_request' + 'github_pull_request', + 'github_pull_request_project', ], 'installable': True, } diff --git a/Dockerfile b/Dockerfile index 373713c..1001dab 100644 --- a/Dockerfile +++ b/Dockerfile @@ -15,6 +15,7 @@ USER odoo COPY github_event /mnt/extra-addons/github_event COPY github_pull_request /mnt/extra-addons/github_pull_request +COPY github_pull_request_project /mnt/extra-addons/github_pull_request_project COPY .docker_files/main /mnt/extra-addons/main COPY .docker_files/odoo.conf /etc/odoo diff --git a/github_pull_request_project/README.rst b/github_pull_request_project/README.rst new file mode 100644 index 0000000..5eba95e --- /dev/null +++ b/github_pull_request_project/README.rst @@ -0,0 +1,19 @@ +Github Pull Request Project +=========================== + +This modules adds a relation between tasks and github pull request. + + +Tasks in Pull Request Form view +------------------------------- + +.. image:: static/description/pull_request_form_tasks.png + +Pull Requests in Tasks Form View +-------------------------------- + +.. image:: static/description/project_task_pull_requests.png + +Contributors +------------ +* Numigi (tm) and all its contributors (https://bit.ly/numigiens) diff --git a/github_pull_request_project/__init__.py b/github_pull_request_project/__init__.py new file mode 100644 index 0000000..ac4a686 --- /dev/null +++ b/github_pull_request_project/__init__.py @@ -0,0 +1,4 @@ +# Copyright 2023 - today Numigi (tm) and all its contributors (https://bit.ly/numigiens) +# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl). + +from . import models diff --git a/github_pull_request_project/__manifest__.py b/github_pull_request_project/__manifest__.py new file mode 100644 index 0000000..c7ee2a8 --- /dev/null +++ b/github_pull_request_project/__manifest__.py @@ -0,0 +1,23 @@ +# Copyright 2023 - Today Numigi (tm) and all its contributors (https://bit.ly/numigiens) +# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl). + +{ + 'name': 'Github Pull Request Project', + 'version': "16.0.1.0.0", + 'author': 'Numigi', + 'maintainer': 'Numigi', + 'website': 'https://bit.ly/numigi-com', + 'license': 'LGPL-3', + 'category': 'Project', + 'summary': 'Create a relation between tasks and github.pull_request models.', + 'depends': ['project', 'github_pull_request'], + 'data': [ + 'data/project_tags.xml', + 'views/ir_actions_act_window.xml', + 'views/ir_ui_menu.xml', + 'views/github_pull_request.xml', + 'views/project_task.xml', + ], + 'auto_install': True, + 'installable': True, +} diff --git a/github_pull_request_project/data/project_tags.xml b/github_pull_request_project/data/project_tags.xml new file mode 100644 index 0000000..e8a6d9b --- /dev/null +++ b/github_pull_request_project/data/project_tags.xml @@ -0,0 +1,19 @@ + + + + + PR: Open + + + + + PR: Merged + + + + + PR: Closed + + + + diff --git a/github_pull_request_project/i18n/fr.po b/github_pull_request_project/i18n/fr.po new file mode 100644 index 0000000..b7604dd --- /dev/null +++ b/github_pull_request_project/i18n/fr.po @@ -0,0 +1,50 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * github_pull_request_project +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 16.0\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2019-11-14 19:48+0000\n" +"PO-Revision-Date: 2019-11-14 19:48+0000\n" +"Last-Translator: <>\n" +"Language-Team: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: \n" +"Plural-Forms: \n" + +#. module: github_pull_request_project +#: model:ir.model,name:github_pull_request_project.model_github_pull_request +msgid "Github Pull Request" +msgstr "" + +#. module: github_pull_request_project +#: model:ir.model.fields,field_description:github_pull_request_project.field_project_task__no_pull_request_open +msgid "No Pull Request Open" +msgstr "Aucune pull request d'ouverte" + +#. module: github_pull_request_project +#: model:ir.model.fields,field_description:github_pull_request_project.field_project_task__pull_request_qty +msgid "Pull Request Qty" +msgstr "Quantité de pull request" + +#. module: github_pull_request_project +#: model:ir.actions.act_window,name:github_pull_request_project.pull_requests_action_window_context +#: model:ir.model.fields,field_description:github_pull_request_project.field_project_task__pull_request_ids +#: model:ir.ui.menu,name:github_pull_request_project.project_pull_request_menu +#: model_terms:ir.ui.view,arch_db:github_pull_request_project.project_task_form_pull_requests +msgid "Pull Requests" +msgstr "" + +#. module: github_pull_request_project +#: model:ir.model,name:github_pull_request_project.model_project_task +msgid "Task" +msgstr "Tâche" + +#. module: github_pull_request_project +#: model:ir.model.fields,field_description:github_pull_request_project.field_github_pull_request__task_ids +msgid "Tasks" +msgstr "Tâches" + diff --git a/github_pull_request_project/models/__init__.py b/github_pull_request_project/models/__init__.py new file mode 100644 index 0000000..753acce --- /dev/null +++ b/github_pull_request_project/models/__init__.py @@ -0,0 +1,4 @@ +# Copyright 2023 - today Numigi (tm) and all its contributors (https://bit.ly/numigiens) +# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl). + +from . import github_pull_request diff --git a/github_pull_request_project/models/github_pull_request.py b/github_pull_request_project/models/github_pull_request.py new file mode 100644 index 0000000..2a9d382 --- /dev/null +++ b/github_pull_request_project/models/github_pull_request.py @@ -0,0 +1,106 @@ +# Copyright 2023 - today Numigi (tm) and all its contributors (https://bit.ly/numigiens) +# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl). + +from odoo import fields, models, api + + +class GithubPullRequestTask(models.Model): + + _inherit = "github.pull_request" + + task_ids = fields.Many2many( + 'project.task', + 'pull_request_task_ref', + 'pull_request_id', + 'task_id', + string='Tasks', + ) + + @api.model + def create(self, vals): + pr = super().create(vals) + pr.task_ids._update_pull_request_tags() + return pr + + def write(self, vals): + must_update_tags_on_tasks = 'task_ids' in vals or 'state' in vals + + if must_update_tags_on_tasks: + tasks_to_update = self.mapped('task_ids') + + super().write(vals) + + if must_update_tags_on_tasks: + tasks_to_update |= self.mapped('task_ids') + tasks_to_update._update_pull_request_tags() + + return True + + +def has_pull_request_at_state(task: 'project.task', state: str) -> bool: + """Return True if the task has at least one PR at the given state.""" + return task.pull_request_ids.filtered(lambda pr: pr.state == state) + + +class ProjectTaskPullRequest(models.Model): + + _inherit = "project.task" + + pull_request_ids = fields.Many2many( + 'github.pull_request', + 'pull_request_task_ref', + 'task_id', + 'pull_request_id', + string='Pull Requests', + copy=False, + ) + + def _compute_pull_request_qty(self): + for record in self: + record.pull_request_qty = len(record.pull_request_ids) + + pull_request_qty = fields.Integer(compute="_compute_pull_request_qty") + + @api.model + def create(self, vals): + task = super().create(vals) + task._update_pull_request_tags() + return task + + def write(self, vals): + super().write(vals) + + if 'pull_request_ids' in vals: + self._update_pull_request_tags() + + return True + + def _update_pull_request_tags(self): + tag_open = self.env.ref('github_pull_request_project.tag_pull_request_open') + tag_merged = self.env.ref('github_pull_request_project.tag_pull_request_merged') + tag_closed = self.env.ref('github_pull_request_project.tag_pull_request_closed') + + for task in self: + show_open_tag = has_pull_request_at_state(task, 'open') + show_merged_tag = ( + has_pull_request_at_state(task, 'merged') and not show_open_tag + ) + show_closed_tag = ( + has_pull_request_at_state(task, 'closed') + and not show_open_tag + and not show_merged_tag + ) + + task.update( + { + 'tag_ids': [ + (4 if show_open_tag else 3, tag_open.id), + (4 if show_merged_tag else 3, tag_merged.id), + (4 if show_closed_tag else 3, tag_closed.id), + ] + } + ) + + @api.onchange('pull_request_ids') + def _onchange_pull_requests_update_tags(self): + self._update_pull_request_tags() diff --git a/github_pull_request_project/static/description/icon.png b/github_pull_request_project/static/description/icon.png new file mode 100644 index 0000000..92a86b1 Binary files /dev/null and b/github_pull_request_project/static/description/icon.png differ diff --git a/github_pull_request_project/static/description/project_task_pull_requests.png b/github_pull_request_project/static/description/project_task_pull_requests.png new file mode 100644 index 0000000..be72ddb Binary files /dev/null and b/github_pull_request_project/static/description/project_task_pull_requests.png differ diff --git a/github_pull_request_project/static/description/pull_request_form_tasks.png b/github_pull_request_project/static/description/pull_request_form_tasks.png new file mode 100644 index 0000000..c67b609 Binary files /dev/null and b/github_pull_request_project/static/description/pull_request_form_tasks.png differ diff --git a/github_pull_request_project/tests/__init__.py b/github_pull_request_project/tests/__init__.py new file mode 100644 index 0000000..cef8020 --- /dev/null +++ b/github_pull_request_project/tests/__init__.py @@ -0,0 +1,4 @@ +# Copyright 2023 - today Numigi (tm) and all its contributors (https://bit.ly/numigiens) +# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl). + +from . import test_project_task diff --git a/github_pull_request_project/tests/test_project_task.py b/github_pull_request_project/tests/test_project_task.py new file mode 100644 index 0000000..5b0da95 --- /dev/null +++ b/github_pull_request_project/tests/test_project_task.py @@ -0,0 +1,173 @@ +# Copyright 2023 - today Numigi (tm) and all its contributors (https://bit.ly/numigiens) +# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl). + + +from odoo.tests import common + + +class TestProjectTask(common.SavepointCase): + + @classmethod + def setUpClass(cls): + super().setUpClass() + cls.pull_request_open = cls.env["github.pull_request"].create( + { + "source": "https://github.com/Numigi/odoo-git-addons/pull/1", + "state": "open", + } + ) + cls.pull_request_closed = cls.env["github.pull_request"].create( + { + "source": "https://github.com/Numigi/odoo-git-addons/pull/2", + "state": "closed", + } + ) + cls.pull_request_merged = cls.env["github.pull_request"].create( + { + "source": "https://github.com/Numigi/odoo-git-addons/pull/3", + "state": "merged", + } + ) + + cls.tag_open = cls.env.ref('github_pull_request_project.tag_pull_request_open') + cls.tag_merged = cls.env.ref( + 'github_pull_request_project.tag_pull_request_merged' + ) + cls.tag_closed = cls.env.ref( + 'github_pull_request_project.tag_pull_request_closed' + ) + + # Simple cases + def test_whenNoPR_thenNoTag(self): + task = self.env["project.task"].create( + {"name": "ttask", "pull_request_ids": [(5, False, False)]} + ) + assert not task.tag_ids + + def test_whenPrAreMerged_thenTaskMerged(self): + task = self.env["project.task"].create( + { + "name": "ttask", + "pull_request_ids": [(6, False, (self.pull_request_merged.id,))], + } + ) + assert task.tag_ids == self.tag_merged + + def test_whenPrAreClosed_thenTaskClosed(self): + task = self.env["project.task"].create( + { + "name": "ttask", + "pull_request_ids": [(6, False, (self.pull_request_closed.id,))], + } + ) + assert task.tag_ids == self.tag_closed + + def test_whenPrAreOpen_thenTaskOpen(self): + task = self.env["project.task"].create( + { + "name": "ttask", + "pull_request_ids": [(6, False, (self.pull_request_open.id,))], + } + ) + assert task.tag_ids == self.tag_open + + # Mixing cases + def test_whenAllStates_thenTaskOpen(self): + task = self.env["project.task"].create( + { + "name": "ttask", + "pull_request_ids": [ + ( + 6, + False, + ( + self.pull_request_open.id, + self.pull_request_closed.id, + self.pull_request_merged.id, + ), + ) + ], + } + ) + assert task.tag_ids == self.tag_open + + def test_whenClosedAndMerged_thenTaskMerged(self): + task = self.env["project.task"].create( + { + "name": "ttask", + "pull_request_ids": [ + ( + 6, + False, + (self.pull_request_closed.id, self.pull_request_merged.id), + ) + ], + } + ) + assert task.tag_ids == self.tag_merged + + def test_whenMergedAndOpen_thenTaskOpen(self): + task = self.env["project.task"].create( + { + "name": "ttask", + "pull_request_ids": [ + (6, False, (self.pull_request_merged.id, self.pull_request_open.id)) + ], + } + ) + assert task.tag_ids == self.tag_open + + def test_whenClosedAndOpen_thenTaskOpen(self): + task = self.env["project.task"].create( + { + "name": "ttask", + "pull_request_ids": [ + (6, False, (self.pull_request_closed.id, self.pull_request_open.id)) + ], + } + ) + assert task.tag_ids == self.tag_open + + def test_onTaskWrite_tagsUpdated(self): + task = self.env["project.task"].create( + { + "name": "ttask", + } + ) + task.pull_request_ids = self.pull_request_open + assert task.tag_ids == self.tag_open + + def test_onPullRequestStateChange_tagsUpdated(self): + task = self.env["project.task"].create( + { + "name": "ttask", + "pull_request_ids": [(4, self.pull_request_open.id)], + } + ) + assert task.tag_ids == self.tag_open + self.pull_request_open.state = 'merged' + assert task.tag_ids == self.tag_merged + + def test_onPullRequestTaskIdsChange_tagsUpdated(self): + task = self.env["project.task"].create( + { + "name": "ttask", + } + ) + self.pull_request_open.task_ids = task + assert task.tag_ids == self.tag_open + + def test_onPullRequestCreate_tagsUpdated(self): + task = self.env["project.task"].create( + { + "name": "ttask", + } + ) + self.env["github.pull_request"].create( + { + "source": "https://github.com/Numigi/odoo-git-addons/pull/999", + "state": "open", + "task_ids": [(4, task.id)], + } + ) + assert task.tag_ids == self.tag_open diff --git a/github_pull_request_project/views/github_pull_request.xml b/github_pull_request_project/views/github_pull_request.xml new file mode 100644 index 0000000..7d03d4e --- /dev/null +++ b/github_pull_request_project/views/github_pull_request.xml @@ -0,0 +1,32 @@ + + + + + + + + + + + + + + github.pull_request + Github Pull Request: task ids + form + + + + + + + + + + + github.pull_request + Github Pull Request: task ids + 16 + search + + diff --git a/github_pull_request_project/views/ir_actions_act_window.xml b/github_pull_request_project/views/ir_actions_act_window.xml new file mode 100644 index 0000000..00a7120 --- /dev/null +++ b/github_pull_request_project/views/ir_actions_act_window.xml @@ -0,0 +1,13 @@ + + + + Pull Requests + github.pull_request + current + ir.actions.act_window + tree,form + { + 'search_default_task_ids': [active_id], + } + + diff --git a/github_pull_request_project/views/ir_ui_menu.xml b/github_pull_request_project/views/ir_ui_menu.xml new file mode 100644 index 0000000..cce2fff --- /dev/null +++ b/github_pull_request_project/views/ir_ui_menu.xml @@ -0,0 +1,9 @@ + + + + + Pull Requests + + 10 + + diff --git a/github_pull_request_project/views/project_task.xml b/github_pull_request_project/views/project_task.xml new file mode 100644 index 0000000..1d16560 --- /dev/null +++ b/github_pull_request_project/views/project_task.xml @@ -0,0 +1,25 @@ + + + + + + +
+ +
+ + + + + +
+ + project.task + Project Task: pull request fields + 99 +
+ +