diff --git a/vcp_github/models/vcp_repository.py b/vcp_github/models/vcp_repository.py index 016e5e9..8f655d3 100644 --- a/vcp_github/models/vcp_repository.py +++ b/vcp_github/models/vcp_repository.py @@ -17,6 +17,12 @@ class VcpRepository(models.Model): _inherit = "vcp.repository" + def _get_repository_url(self): + result = super()._get_repository_url() + if not result and self.platform_id.host_id.type_id.code == "github": + return f"https://github.com/{self.platform_id.name}/{self.name}" + return result + def _update_branches_github(self): self.ensure_one() client = self.platform_id._get_github_clients()[0] diff --git a/vcp_github/models/vcp_user.py b/vcp_github/models/vcp_user.py index 7b85782..8f6d1ae 100644 --- a/vcp_github/models/vcp_user.py +++ b/vcp_github/models/vcp_user.py @@ -2,7 +2,15 @@ # License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). -from odoo import models +import logging +from datetime import datetime + +import github3 + +from odoo import fields, models +from odoo.exceptions import ValidationError + +_logger = logging.getLogger(__name__) class VcpUser(models.Model): @@ -13,3 +21,34 @@ def _get_contributor_url(self): if not result and self.host_id.type_id.code == "github": return f"https://github.com/{self.external_id}" return result + + def _prepare_user_vals(self, user): + return { + "name": user.name or user.login, + "email": user.email, + "avatar_url": user.avatar_url, + "company": user.company, + } + + def _update_information_github(self): + self.ensure_one() + # TODO maybe we should move the api key on the host ? + platform = self.env["vcp.platform"].search( + [("host_id", "=", self.host_id.id)], limit=1 + ) + client = platform._get_github_clients()[0] + try: + user = client.user(self.external_id) + self.write(self._prepare_user_vals(user)) + except github3.exceptions.ForbiddenError as e: + _logger.error(e) + rate = client.rate_limit() + reset = fields.Datetime.to_string( + datetime.utcfromtimestamp(rate["resources"]["core"]["reset"]) + ) + raise ValidationError(self.env._(f"Reset on {reset}")) from e + except github3.exceptions.NotFoundError: + _logger.warning( + "The user %s do not exist anymore, inactive it", self.external_id + ) + self.active = False diff --git a/vcp_management/data/ir_cron.xml b/vcp_management/data/ir_cron.xml index 7746dda..907d4a9 100644 --- a/vcp_management/data/ir_cron.xml +++ b/vcp_management/data/ir_cron.xml @@ -38,4 +38,13 @@ days False + + VCP: User Update + + code + model._cron_update_users(limit=100) + 1 + days + False + diff --git a/vcp_management/models/res_partner.py b/vcp_management/models/res_partner.py index 902e67b..3e3b7ba 100644 --- a/vcp_management/models/res_partner.py +++ b/vcp_management/models/res_partner.py @@ -2,7 +2,12 @@ # License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). +import base64 + +import requests + from odoo import api, fields, models +from odoo.exceptions import UserError class ResPartner(models.Model): @@ -32,6 +37,34 @@ class ResPartner(models.Model): "vcp.organization", inverse_name="partner_id", ) + image_1920 = fields.Image( + compute="_compute_image_1920", + store=True, + readonly=False, + ) + + @api.depends( + "vcp_user_ids.sync_image_to_partner", + "vcp_user_ids.avatar_url", + ) + def _compute_image_1920(self): + for record in self: + sync_user = record.vcp_user_ids.filtered("sync_image_to_partner") + if len(sync_user) > 1: + raise UserError( + self.env._( + "Only one Vcp User can be use for synchronising the main image" + ) + ) + elif sync_user.avatar_url: + try: + response = requests.get(sync_user.avatar_url, timeout=10) + response.raise_for_status() + except Exception as e: + raise UserError( + self.env._("Fail to download avatar, %s.please retry".format()) + ) from e + record.image_1920 = base64.b64encode(response.content).decode("utf-8") @api.depends() def _compute_vcp_contributions(self): diff --git a/vcp_management/models/vcp_repository_branch.py b/vcp_management/models/vcp_repository_branch.py index d769f82..23db0cb 100644 --- a/vcp_management/models/vcp_repository_branch.py +++ b/vcp_management/models/vcp_repository_branch.py @@ -2,6 +2,7 @@ # License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). import os import re +import traceback from odoo import _, api, fields, models from odoo.exceptions import ValidationError @@ -11,6 +12,7 @@ class VcpRepositoryBranch(models.Model): _name = "vcp.repository.branch" _inherit = ["vcp.rule.information.mixin"] _description = "Links Branches with Repositories" + _order = "rule_failure_msg, repository_id, branch_id" branch_id = fields.Many2one( "vcp.branch", @@ -39,9 +41,14 @@ class VcpRepositoryBranch(models.Model): default=fields.Datetime.now, required=True, ) + rule_failure_msg = fields.Text() def _cron_process_branch_rules(self, limit): - branches = self.search([], limit=limit, order="update_rule_processing_date asc") + branches = self.search( + [], + limit=limit, + order="update_rule_processing_date asc", + ) for branch in branches: branch.process_rules() @@ -65,12 +72,25 @@ def _get_local_path(self): def process_rules(self): for record in self: rules = record._get_rules() - # This parameters dict can be used to store parameters that will - # be used by other rules. - parameters = {} - for rule in rules: - if re.match(rule.branch_pattern, record.branch_id.name): - rule._process_rule(record, parameters) + try: + with self.env.cr.savepoint(): + # This parameters dict can be used to store parameters that will + # be used by other rules. + parameters = {} + for rule in rules: + if re.match(rule.branch_pattern, record.branch_id.name): + rule._process_rule(record, parameters) + except Exception as e: + record.rule_failure_msg = ( + f"error: {e}\n\n traceback: {traceback.format_exc()}" + ) + # we need to purge the cache as _get_odoo_module keep the module name + # in cache and if a module have been creating during the try + # as this have been rollbacked we need to purge it from the cache + self.env.registry.clear_cache() + else: + record.rule_failure_msg = False + record.update_rule_processing_date = fields.Datetime.now() def _download_code(self): result = super()._download_code() diff --git a/vcp_management/models/vcp_user.py b/vcp_management/models/vcp_user.py index 1230e92..d7fbc8a 100644 --- a/vcp_management/models/vcp_user.py +++ b/vcp_management/models/vcp_user.py @@ -26,6 +26,14 @@ class VcpUser(models.Model): partner_id = fields.Many2one( "res.partner", ) + sync_image_to_partner = fields.Boolean( + help="Use the image user as image on the partner" + ) + user_update_date = fields.Datetime(readonly=True, default=fields.Datetime.now) + avatar_url = fields.Char(readonly=True) + email = fields.Char(readonly=True) + company = fields.Char(readonly=True) + active = fields.Boolean(readonly=True, default=True) _sql_constraints = [ ( @@ -40,3 +48,18 @@ def _get_contributor_url(self): def _get_contributors_name(self, kind, **kwargs): return self.partner_id.name or self.name + + def update_information(self): + self.ensure_one() + now = fields.Datetime.now() + getattr(self, f"_update_information_{self.host_id.type_id.code}")() + self.user_update_date = now + + def _cron_update_users(self, limit): + users = self.search( + [], + limit=limit, + order="user_update_date ASC", + ) + for user in users: + user.update_information() diff --git a/vcp_management/views/vcp_platform.xml b/vcp_management/views/vcp_platform.xml index 398622b..4a2707a 100644 --- a/vcp_management/views/vcp_platform.xml +++ b/vcp_management/views/vcp_platform.xml @@ -19,7 +19,7 @@ + + + + + diff --git a/vcp_odoo/views/vcp_odoo_author.xml b/vcp_odoo/views/vcp_odoo_author.xml new file mode 100644 index 0000000..e4b5d8f --- /dev/null +++ b/vcp_odoo/views/vcp_odoo_author.xml @@ -0,0 +1,31 @@ + + + + vcp.odoo.author + + + + + + + + + + vcp.odoo.author + + + + + + + + + Author + ir.actions.act_window + vcp.odoo.author + list + + [] + {} + + diff --git a/vcp_odoo/views/vcp_odoo_module_version.xml b/vcp_odoo/views/vcp_odoo_module_version.xml index 3484034..d1bb9e8 100644 --- a/vcp_odoo/views/vcp_odoo_module_version.xml +++ b/vcp_odoo/views/vcp_odoo_module_version.xml @@ -8,6 +8,7 @@
+
+ + +