Maintainers
+Maintainers
This module is maintained by the OCA.
@@ -521,6 +516,5 @@ diff --git a/vcp_management/README.rst b/vcp_management/README.rst
index 47e3b4f..6e30f6c 100644
--- a/vcp_management/README.rst
+++ b/vcp_management/README.rst
@@ -1,7 +1,3 @@
-.. image:: https://odoo-community.org/readme-banner-image
- :target: https://odoo-community.org/get-involved?utm_source=readme
- :alt: Odoo Community Association
-
==============
VCP Management
==============
@@ -17,7 +13,7 @@ VCP Management
.. |badge1| image:: https://img.shields.io/badge/maturity-Beta-yellow.png
:target: https://odoo-community.org/page/development-status
:alt: Beta
-.. |badge2| image:: https://img.shields.io/badge/license-AGPL--3-blue.png
+.. |badge2| image:: https://img.shields.io/badge/licence-AGPL--3-blue.png
:target: http://www.gnu.org/licenses/agpl-3.0-standalone.html
:alt: License: AGPL-3
.. |badge3| image:: https://img.shields.io/badge/github-OCA%2Fversion--control--platform-lightgray.png?logo=github
diff --git a/vcp_management/__init__.py b/vcp_management/__init__.py
index 0650744..55ec7fc 100644
--- a/vcp_management/__init__.py
+++ b/vcp_management/__init__.py
@@ -1 +1,2 @@
from . import models
+from . import reports
diff --git a/vcp_management/__manifest__.py b/vcp_management/__manifest__.py
index c6b4a20..a1604f4 100644
--- a/vcp_management/__manifest__.py
+++ b/vcp_management/__manifest__.py
@@ -14,6 +14,7 @@
"security/ir.model.access.csv",
"data/ir_cron.xml",
"templates/templates.xml",
+ "reports/vcp_management_report.xml",
"views/vcp_comment.xml",
"views/vcp_review.xml",
"views/vcp_request.xml",
diff --git a/vcp_management/models/vcp_comment.py b/vcp_management/models/vcp_comment.py
index 938e701..d5ab148 100644
--- a/vcp_management/models/vcp_comment.py
+++ b/vcp_management/models/vcp_comment.py
@@ -32,7 +32,7 @@ class VcpComment(models.Model):
readonly=True,
store=True,
)
- created_at = fields.Datetime(readonly=True)
+ created_at = fields.Datetime(readonly=True, index=True)
updated_at = fields.Datetime(readonly=True)
request_id = fields.Many2one(
comodel_name="vcp.request",
diff --git a/vcp_management/models/vcp_request.py b/vcp_management/models/vcp_request.py
index c75c473..cd4a58b 100644
--- a/vcp_management/models/vcp_request.py
+++ b/vcp_management/models/vcp_request.py
@@ -69,9 +69,9 @@ class VcpRequest(models.Model):
)
is_merged = fields.Boolean(readonly=True)
is_draft = fields.Boolean(readonly=True)
- created_at = fields.Datetime(readonly=True)
+ created_at = fields.Datetime(readonly=True, index=True)
updated_at = fields.Datetime(readonly=True)
- closed_at = fields.Datetime(readonly=True)
+ closed_at = fields.Datetime(readonly=True, index=True)
number = fields.Integer(readonly=True)
label_ids = fields.Many2many(
comodel_name="vcp.request.label",
diff --git a/vcp_management/models/vcp_review.py b/vcp_management/models/vcp_review.py
index 86371dd..fd5ac3b 100644
--- a/vcp_management/models/vcp_review.py
+++ b/vcp_management/models/vcp_review.py
@@ -20,7 +20,7 @@ class VcpReview(models.Model):
partner_id = fields.Many2one(
related="user_id.partner_id",
)
- submitted_at = fields.Datetime(readonly=True)
+ submitted_at = fields.Datetime(readonly=True, index=True)
repository_id = fields.Many2one(
related="request_id.repository_id",
readonly=True,
diff --git a/vcp_management/reports/__init__.py b/vcp_management/reports/__init__.py
new file mode 100644
index 0000000..1613e55
--- /dev/null
+++ b/vcp_management/reports/__init__.py
@@ -0,0 +1 @@
+from . import vcp_management_report
diff --git a/vcp_management/reports/vcp_management_report.py b/vcp_management/reports/vcp_management_report.py
new file mode 100644
index 0000000..059f654
--- /dev/null
+++ b/vcp_management/reports/vcp_management_report.py
@@ -0,0 +1,101 @@
+# Copyright 2026 Dixmit
+# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
+from odoo import fields, models
+from odoo.tools import SQL
+
+
+class VCPManagementReport(models.Model):
+ _name = "vcp.management.report"
+ _description = "VCP Statistics"
+ _auto = False
+ _log_access = False
+ _rec_name = "user_id"
+ _order = "date desc"
+
+ platform_id = fields.Many2one("vcp.platform")
+ user_id = fields.Many2one("vcp.user")
+ organization_id = fields.Many2one("vcp.organization")
+ repository_id = fields.Many2one("vcp.repository")
+ date = fields.Datetime()
+ merged_pr_count = fields.Integer()
+ created_pr_count = fields.Integer()
+ review_count = fields.Integer()
+ comment_count = fields.Integer()
+
+ def _get_table_definition(self):
+ return {
+ "created_prs": {
+ "table": "vcp_request",
+ "date": "t.created_at",
+ "created_pr_count": "1",
+ },
+ "merged_prs": {
+ "table": "vcp_request",
+ "date": "t.closed_at",
+ "merged_pr_count": "1",
+ "filter": "t.is_merged = True",
+ },
+ "reviews": {
+ "table": "vcp_review",
+ "date": "t.submitted_at",
+ "review_count": "1",
+ },
+ "comments": {
+ "table": "vcp_comment",
+ "date": "t.created_at",
+ "comment_count": "1",
+ },
+ }
+
+ @property
+ def _table_query(self) -> SQL:
+ return SQL(self._get_sql_definition())
+
+ def _get_sql_definition(self):
+ definition = self._get_table_definition().items()
+ union_queries = []
+ element_number = 0
+ for table_name, table_def in definition:
+ union_queries.append(
+ self._get_table_query(
+ table_name, table_def, element_number, len(definition)
+ )
+ )
+ element_number += 1
+ return " UNION ALL ".join(union_queries)
+
+ def _get_table_query(self, table_name, table_def, element_number, total_elements):
+ from_definition = self._get_from_definition(table_name, table_def)
+ select_fields = self._get_select_fields(
+ table_def, element_number, total_elements
+ )
+ select_fields_str = ", ".join(select_fields)
+ filter_condition = (
+ f"WHERE {table_def['filter']}" if "filter" in table_def else ""
+ )
+ return f"""
+ SELECT
+ {select_fields_str}
+ FROM {from_definition}
+ {filter_condition}
+ """
+
+ def _get_from_definition(self, table_name, table_def):
+ table = table_def.get("table", table_name)
+ repository_id = table_def.get("repository_id", "t.repository_id")
+ return f"{table} AS t LEFT JOIN vcp_repository AS r ON {repository_id} = r.id"
+
+ def _get_select_fields(self, table_def, element_number, total_elements):
+ organization = table_def.get("organization_id", "t.organization_id")
+ return [
+ f"{element_number}+{total_elements}*t.id AS id",
+ f"{table_def.get('platform_id', 'r.platform_id')} AS platform_id",
+ f"{organization} AS organization_id",
+ f"{table_def.get('user_id', 't.user_id')} AS user_id",
+ f"{table_def.get('repository_id', 't.repository_id')} AS repository_id",
+ f"{table_def.get('date', 't.create_date')} AS date",
+ f"{table_def.get('merged_pr_count', 0)} AS merged_pr_count",
+ f"{table_def.get('created_pr_count', 0)} AS created_pr_count",
+ f"{table_def.get('review_count', 0)} AS review_count",
+ f"{table_def.get('comment_count', 0)} AS comment_count",
+ ]
diff --git a/vcp_management/reports/vcp_management_report.xml b/vcp_management/reports/vcp_management_report.xml
new file mode 100644
index 0000000..0733837
--- /dev/null
+++ b/vcp_management/reports/vcp_management_report.xml
@@ -0,0 +1,37 @@
+
+
+
Creates a set of modules used for handling a version control patform.
Table of contents
The aim of this module is to allow any community to import data from a version control system.
The system should be done in a way that is agnostic to the system and the connections are handled directly by specific modules.
Hosts are the origin of data. Each host has a type that helps us know how to integrate with the system. For example, on Github there is only one host (github.com). However, in Gitlab there could be one for each instance that we are integrating too.
We understand that a platform is an entity that can provide code and information to our Version Control Platform (VCP). A platform could be an organization on Github (like OCA) or Gitlab for example.
A repository is an origin of code. For example, this repository could be a VCP repository.
First step is to create a Platform. In the platform we need to set the host (you might need to create it on gitlab) and add some Platform Keys.
This keys will allow us to integrate with the origin system.
By default, the system provides some refresh rules for platforms and repositories, however we can deactivate or activate it manualy.
One of the capabilities of this module is the generation of rules.
This rules allow us to know some information of the repository.
By default, the system adds some rules aligned with odoo to make it @@ -479,7 +474,7 @@
Bugs are tracked on GitHub Issues. In case of trouble, please check there if your issue has already been reported. If you spotted it first, help us to smash it by providing a detailed and welcomed @@ -487,15 +482,15 @@
Do not contact contributors directly about support or help with technical issues.