diff --git a/sync/README.rst b/sync/README.rst index 39d00a3b..28fb0c60 100644 --- a/sync/README.rst +++ b/sync/README.rst @@ -1,6 +1,6 @@ .. image:: https://itpp.dev/images/infinity-readme.png :alt: Tested and maintained by IT Projects Labs - :target: https://itpp.dev + :target: https://odoomagic.com .. image:: https://img.shields.io/badge/license-MIT-blue.svg :target: https://opensource.org/licenses/MIT diff --git a/sync/__manifest__.py b/sync/__manifest__.py index bf40e8c9..8fa5b8b9 100644 --- a/sync/__manifest__.py +++ b/sync/__manifest__.py @@ -7,13 +7,16 @@ "name": "Sync ๐Ÿชฌ Studio", "summary": """Join the Amazing ๐Ÿ˜ Community โคต๏ธ""", "category": "VooDoo โœจ Magic", - "version": "16.0.11.0.1", + "version": "16.0.13.0.0", "application": True, "author": "Ivan Kropotkin", "support": "info@odoomagic.com", "website": "https://sync_studio.t.me/", "license": "Other OSI approved licence", # MIT - "depends": ["base_automation", "mail", "queue_job"], + # The `partner_telegram` dependency is not directly needed, + # but it plays an important role in the **Sync ๐Ÿชฌ Studio** ecosystem + # and is added for the quick onboarding of new **Cyber โœจ Pirates**. + "depends": ["base_automation", "mail", "queue_job", "partner_telegram"], "external_dependencies": {"python": ["markdown", "pyyaml"], "bin": []}, "data": [ "security/sync_groups.xml", @@ -37,12 +40,6 @@ }, "demo": [ "data/sync_project_unittest_demo.xml", - # Obsolete - # "data/sync_project_context_demo.xml", - # "data/sync_project_telegram_demo.xml", - # "data/sync_project_odoo2odoo_demo.xml", - # "data/sync_project_trello_github_demo.xml", - # "data/sync_project_context_demo.xml", ], "qweb": [], "post_load": None, diff --git a/sync/doc/MAGIC.rst b/sync/doc/MAGIC.rst index 092fa23b..77855865 100644 --- a/sync/doc/MAGIC.rst +++ b/sync/doc/MAGIC.rst @@ -80,6 +80,7 @@ Tools * ``MAGIC.type2str``: get type of the given object * ``MAGIC.DEFAULT_SERVER_DATETIME_FORMAT`` * ``MAGIC.AttrDict``: Extended dictionary that allows for attribute-style access +* ``MAGIC.group_by_lang(partners, default_lang="en_US")``: yields `lang, partners` grouped by lang Exceptions ========== diff --git a/sync/doc/changelog.rst b/sync/doc/changelog.rst index 38053cc1..688a2064 100644 --- a/sync/doc/changelog.rst +++ b/sync/doc/changelog.rst @@ -1,3 +1,12 @@ +`13.0.0` +------- + +- **Fix:** use `__sync.` for xmlid namespace to avoid data lose on module update +- **New:** add *Sync Order* โ€” advanced manual trigger with blackjack, partners list, text input, etc. +- **New:** support `data.markdown` for custom documentation at the `DATA.๐Ÿซ` tab +- **New:** add `MAGIC.group_by_lang` to eval context +- **Improvement:** add `DATA.*` to the library eval context + `11.0.1` ------- diff --git a/sync/models/base.py b/sync/models/base.py index 788ebc37..27529f14 100644 --- a/sync/models/base.py +++ b/sync/models/base.py @@ -19,7 +19,7 @@ def search_links(self, relation_name, refs=None): ._search_links_odoo(self, relation_name, refs) ) - def _create_or_update_by_xmlid(self, vals, code, namespace="XXX", module="sync"): + def _create_or_update_by_xmlid(self, vals, code, namespace="XXX", module="__sync"): """ Create or update a record by a dynamically generated XML ID. Warning! The field `noupdate` is ignored, i.e. existing records are always updated. diff --git a/sync/models/sync_project.py b/sync/models/sync_project.py index 46b9e7e2..1775d206 100644 --- a/sync/models/sync_project.py +++ b/sync/models/sync_project.py @@ -7,6 +7,8 @@ import logging import os from datetime import datetime +from itertools import groupby +from operator import itemgetter import urllib3 from pytz import timezone @@ -110,6 +112,7 @@ class SyncProject(models.Model): link_ids = fields.One2many("sync.link", "project_id") link_count = fields.Integer(compute="_compute_link_count") data_ids = fields.One2many("sync.data", "project_id") + data_description = fields.Html(readonly=True) def copy(self, default=None): default = dict(default or {}) @@ -259,6 +262,24 @@ def record2image(record, fname="image_1920"): ) ) + def group_by_lang(partners, default_lang="en_US"): + """ + Yield groups of partners grouped by their language. + + :param partners: recordset of res.partner + :return: generator yielding tuples of (lang, partners) + """ + if not partners: + return + + # Sort the partners by 'lang' to ensure groupby works correctly + partners = partners.sorted(key=lambda p: p.lang) + + # Group the partners by 'lang' + for lang, group in groupby(partners, key=itemgetter("lang")): + partner_group = partners.browse([partner.id for partner in group]) + yield lang or default_lang, partner_group + context = dict(self.env.context, log_function=log, sync_project_id=self.id) env = self.env(context=context) link_functions = env["sync.link"]._get_eval_context() @@ -296,6 +317,7 @@ def record2image(record, fname="image_1920"): "b64decode": base64.b64decode, "type2str": type2str, "record2image": record2image, + "group_by_lang": group_by_lang, "DEFAULT_SERVER_DATETIME_FORMAT": DEFAULT_SERVER_DATETIME_FORMAT, "AttrDict": AttrDict, }, @@ -508,11 +530,15 @@ def magic_upgrade(self): vals[field_name] = gist_files[file_name] # [DATA] + file_content = gist_files.get("data.markdown") + if file_content: + vals["data_description"] = compile_markdown_to_html(file_content) + http = urllib3.PoolManager() for file_info in gist_content["files"].values(): # e.g. "data.emoji.csv" file_name = file_info["filename"] - if not file_name.startswith("data."): + if not (file_name.startswith("data.") and file_name != "data.markdown"): continue raw_url = file_info["raw_url"] response = http.request("GET", raw_url) diff --git a/sync/models/sync_trigger_mixin.py b/sync/models/sync_trigger_mixin.py index 0a675bd9..0422bd12 100644 --- a/sync/models/sync_trigger_mixin.py +++ b/sync/models/sync_trigger_mixin.py @@ -39,10 +39,11 @@ def write(self, vals): self._update_name(vals) return res - @api.model - def create(self, vals): - res = super().create(vals) - res._update_name(vals) + @api.model_create_multi + def create(self, vals_list): + for vals in vals_list: + res = super().create(vals) + res._update_name(vals) return res def default_get(self, fields): diff --git a/sync/views/sync_project_views.xml b/sync/views/sync_project_views.xml index 6a04c9f7..674cb482 100644 --- a/sync/views/sync_project_views.xml +++ b/sync/views/sync_project_views.xml @@ -254,13 +254,6 @@ - - - - - - -

Hint: @@ -272,6 +265,16 @@

+ + + + + + + +
+ +