From cccdc1f1f74b9de7f88045d8360dcc6c98d2099e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Krier?= Date: Tue, 16 Apr 2024 18:43:37 +0200 Subject: [PATCH] Add scheduled task to update cached feeds On large data set, generating the feed may take too long for an HTTP request. So in this case user may want to update the cache with a scheduled task so the requests are always answered directly using the cache. --- modules/web_shop_product_data_feed/CHANGELOG | 1 + .../web_shop_product_data_feed/__init__.py | 3 ++- .../web_shop_product_data_feed/doc/usage.rst | 10 +++++++ modules/web_shop_product_data_feed/ir.py | 15 +++++++++++ .../tests/test_module.py | 8 +++++- modules/web_shop_product_data_feed/web.py | 27 ++++++++++++++++--- 6 files changed, 59 insertions(+), 5 deletions(-) create mode 100644 modules/web_shop_product_data_feed/ir.py diff --git a/modules/web_shop_product_data_feed/CHANGELOG b/modules/web_shop_product_data_feed/CHANGELOG index 68b4ef56de..4f00d97968 100644 --- a/modules/web_shop_product_data_feed/CHANGELOG +++ b/modules/web_shop_product_data_feed/CHANGELOG @@ -1,3 +1,4 @@ +* Add scheduled task to update cached feeds Version 7.2.0 - 2024-04-29 -------------------------- diff --git a/modules/web_shop_product_data_feed/__init__.py b/modules/web_shop_product_data_feed/__init__.py index 4500ca3f19..4ec9c03aaf 100644 --- a/modules/web_shop_product_data_feed/__init__.py +++ b/modules/web_shop_product_data_feed/__init__.py @@ -3,13 +3,14 @@ from trytond.pool import Pool -from . import product, routes, web +from . import ir, product, routes, web __all__ = ['register', 'routes'] def register(): Pool.register( + ir.Cron, product.UoM, product.Template, product.Product, diff --git a/modules/web_shop_product_data_feed/doc/usage.rst b/modules/web_shop_product_data_feed/doc/usage.rst index 06ac749d9c..b09a5df9b9 100644 --- a/modules/web_shop_product_data_feed/doc/usage.rst +++ b/modules/web_shop_product_data_feed/doc/usage.rst @@ -2,6 +2,16 @@ Usage ***** +.. _Scheduling Product Data Feed updates: + +Scheduling Product Data Feed updates +==================================== + +You may want to define some "Scheduled tasks" :guilabel:`Update Web Shop +Product Data Feed` to keep the cache of the feeds up to date. +The task updates each feed already in the cache if their last modification was +before half of the `cache duration `. + .. _Fill Google Product Category: Fill Google Product Category diff --git a/modules/web_shop_product_data_feed/ir.py b/modules/web_shop_product_data_feed/ir.py new file mode 100644 index 0000000000..23c6c29214 --- /dev/null +++ b/modules/web_shop_product_data_feed/ir.py @@ -0,0 +1,15 @@ +# This file is part of Tryton. The COPYRIGHT file at the top level of +# this repository contains the full copyright notices and license terms. + +from trytond.pool import PoolMeta + + +class Cron(metaclass=PoolMeta): + __name__ = 'ir.cron' + + @classmethod + def __setup__(cls): + super().__setup__() + cls.method.selection.append( + ('web.shop|update_product_data_feed_csv', + "Update Web Shop Product Data Feed")) diff --git a/modules/web_shop_product_data_feed/tests/test_module.py b/modules/web_shop_product_data_feed/tests/test_module.py index 85d6dfcee9..8809cbb5c3 100644 --- a/modules/web_shop_product_data_feed/tests/test_module.py +++ b/modules/web_shop_product_data_feed/tests/test_module.py @@ -4,7 +4,7 @@ import os import tempfile from decimal import Decimal -from unittest.mock import Mock, patch +from unittest.mock import ANY, Mock, patch from trytond.modules.account.tests import create_chart from trytond.modules.company.tests import create_company, set_company @@ -133,6 +133,12 @@ class WebShopProductDataFeedTestCase(ModuleTestCase): os.path.dirname(__file__), 'google-products.csv')).read()) + with patch.object(Shop, 'product_data_feed_csv') as feed: + Shop.update_product_data_feed_csv() + + feed.assert_called_once_with( + 'google', language=None, duration=ANY) + class WebShopProductDataFeedRouteTestCase(RouteTestCase): "Test Web Shop Product Data Feed route" diff --git a/modules/web_shop_product_data_feed/web.py b/modules/web_shop_product_data_feed/web.py index cd7061b38b..533fee0550 100644 --- a/modules/web_shop_product_data_feed/web.py +++ b/modules/web_shop_product_data_feed/web.py @@ -2,6 +2,7 @@ # this repository contains the full copyright notices and license terms. import csv +import fnmatch import os import shutil import tempfile @@ -75,7 +76,25 @@ class Shop(metaclass=PoolMeta): context['language'] = language return context - def product_data_feed_csv(self, format, language=None): + @classmethod + def update_product_data_feed_csv(cls, shops=None): + if shops is None: + shops = cls.search([]) + + directory = _get_directory() + for shop in shops: + for name in fnmatch.filter( + os.listdir(directory), + f'{shop.name}-{shop.id}-*.csv'): + name = name[len(f'{shop.name}-{shop.id}-'):-len('.csv')] + if '-' in name: + format, language = name.rsplit('-', 1) + else: + format, language = name, None + shop.product_data_feed_csv( + format, language=language, duration=_duration / 2) + + def product_data_feed_csv(self, format, language=None, duration=_duration): transaction = Transaction() directory = _get_directory() if language: @@ -85,10 +104,12 @@ class Shop(metaclass=PoolMeta): filename = os.path.join(directory, filename) try: - if (time.time() - os.path.getmtime(filename)) < _duration: + if (time.time() - os.path.getmtime(filename)) < duration: return filename except OSError: - pass + # Create the file so cron will update it + # even if this generation fails + open(filename, 'a').close() with transaction.set_context(_product_data_language=language): products, prices, taxes = self.get_products()