From f30845d70ac350b7513e80c3b6cfd8843a7e0072 Mon Sep 17 00:00:00 2001 From: MohmdFo Date: Wed, 16 Oct 2024 13:39:16 +0330 Subject: [PATCH 1/2] fix(template-discovery): correct template discovery to match files with additional characters after prefix - Updated `_find_templates_in_directory` method to ensure it matches template filenames that start with the `SAGE_MODEL_PREFIX` and allow additional characters (e.g., `mohammad_2.jinja2` now matches `mohammad` prefix). - Implemented case-insensitive prefix matching to ensure consistency regardless of the case of the prefix. - Ensured the discovery mechanism works for both default and custom templates located in user-defined directories. - This fix addresses an issue where only exact matches of the `SAGE_MODEL_PREFIX` were detected, excluding valid templates with suffixes. --- sage_invoice/service/discovery.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/sage_invoice/service/discovery.py b/sage_invoice/service/discovery.py index d7aed9b..cb9991b 100644 --- a/sage_invoice/service/discovery.py +++ b/sage_invoice/service/discovery.py @@ -49,8 +49,8 @@ def _find_templates_in_directory(self, directory, prefix=None): templates = [] for filename in os.listdir(directory): - if filename.endswith(".jinja2") and ( - not prefix or filename.startswith(prefix) - ): - templates.append(filename) + if filename.endswith(".jinja2"): + # Match filenames that start with the prefix (case-insensitive) + if prefix is None or filename.lower().startswith(prefix.lower()): + templates.append(filename) return templates From d7dfe43a9b16230df132cf8db57aefe803cf204f Mon Sep 17 00:00:00 2001 From: MohmdFo Date: Thu, 17 Oct 2024 13:32:12 +0330 Subject: [PATCH 2/2] fix(templates): correct file naming issue, template paths, and method naming - Fixed an issue where `quotation.html` files could not be found by renaming templates: - `quotation1.html` -> `quotation_1.html` - `quotation2.html` -> `quotation_2.html` - `quotation3.html` -> `quotation_3.html` - `quotation4.html` -> `quotation_4.html` - Refactored variable names in the invoice creation service: - Renamed `custom_template_dir` to `sage_template_dir` - Renamed `custom_template_prefix` to `sage_template_prefix` - Updated method name in `invoice_create.py`: - Renamed `render_contax` to `render_context` - Modified the migration file (`0002_alter_invoice_template_choice.py`) to accommodate template changes. - Updated related test cases in `test_service.py` to reflect the renaming. - Made necessary adjustments in `views/invoice.py` to align with the template changes. --- .../0002_alter_invoice_template_choice.py | 32 +++++++++++++++++++ sage_invoice/service/discovery.py | 22 +++++++------ sage_invoice/service/invoice_create.py | 12 +++---- .../{quotation1.html => quotation_1.html} | 0 .../{quotation2.html => quotation_2.html} | 0 .../{quotation3.html => quotation_3.html} | 0 .../{quotation4.html => quotation_4.html} | 0 sage_invoice/tests/test_service.py | 6 ++-- sage_invoice/views/invoice.py | 8 ++--- 9 files changed, 56 insertions(+), 24 deletions(-) create mode 100644 sage_invoice/migrations/0002_alter_invoice_template_choice.py rename sage_invoice/templates/{quotation1.html => quotation_1.html} (100%) rename sage_invoice/templates/{quotation2.html => quotation_2.html} (100%) rename sage_invoice/templates/{quotation3.html => quotation_3.html} (100%) rename sage_invoice/templates/{quotation4.html => quotation_4.html} (100%) diff --git a/sage_invoice/migrations/0002_alter_invoice_template_choice.py b/sage_invoice/migrations/0002_alter_invoice_template_choice.py new file mode 100644 index 0000000..609c0e0 --- /dev/null +++ b/sage_invoice/migrations/0002_alter_invoice_template_choice.py @@ -0,0 +1,32 @@ +# Generated by Django 5.1.2 on 2024-10-17 09:47 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ("sage_invoice", "0001_initial"), + ] + + operations = [ + migrations.AlterField( + model_name="invoice", + name="template_choice", + field=models.CharField( + choices=[ + ("quotation_1", "quotation_1"), + ("quotation_2", "quotation_2"), + ("quotation_3", "quotation_3"), + ("quotation_4", "quotation_4"), + ("receipt1", "receipt1"), + ("receipt2", "receipt2"), + ("receipt3", "receipt3"), + ], + db_comment="Template choice for the invoice", + help_text="The template you want for your invoice", + max_length=20, + verbose_name="Template choice", + ), + ), + ] diff --git a/sage_invoice/service/discovery.py b/sage_invoice/service/discovery.py index cb9991b..84feb63 100644 --- a/sage_invoice/service/discovery.py +++ b/sage_invoice/service/discovery.py @@ -7,10 +7,10 @@ class JinjaTemplateDiscovery: def __init__(self): self.default_template_dir = "default_invoices" # inside package - self.custom_template_dir = ( + self.sage_template_dir = ( settings.SAGE_MODEL_TEMPLATE ) # custom templates folder name - self.custom_template_prefix = ( + self.sage_template_prefix = ( settings.SAGE_MODEL_PREFIX ) # custom template prefix @@ -29,12 +29,12 @@ def get_custom_templates(self): template_choices = [] for app_config in apps.get_app_configs(): template_dir = os.path.join( - app_config.path, "templates", self.custom_template_dir + app_config.path, "templates", self.sage_template_dir ) if os.path.exists(template_dir): template_choices.extend( self._find_templates_in_directory( - template_dir, self.custom_template_prefix + template_dir, self.sage_template_prefix ) ) return template_choices @@ -42,15 +42,17 @@ def get_custom_templates(self): def _find_templates_in_directory(self, directory, prefix=None): """ Helper method to find .jinja2 files in a directory, optionally filtering by - prefix. + prefix, and return the filenames without the .jinja2 extension. """ if not os.path.exists(directory): return [] templates = [] for filename in os.listdir(directory): - if filename.endswith(".jinja2"): - # Match filenames that start with the prefix (case-insensitive) - if prefix is None or filename.lower().startswith(prefix.lower()): - templates.append(filename) - return templates + if filename.endswith(".jinja2") and (not prefix or filename.startswith(prefix)): + templates.append(filename) + + # Remove the .jinja2 extension from the filenames + filenames = list(map(lambda x: x.replace('.jinja2', ''), templates)) + + return filenames diff --git a/sage_invoice/service/invoice_create.py b/sage_invoice/service/invoice_create.py index 4ccac28..4eff297 100644 --- a/sage_invoice/service/invoice_create.py +++ b/sage_invoice/service/invoice_create.py @@ -22,18 +22,16 @@ def __init__(self) -> None: Jinja2. """ logger.info("Initializing QuotationService") - self.template_discovery = JinjaTemplateDiscovery( - models_dir=getattr(settings, "SAGE_MODEL_TEMPLATE", "default_invoices") - ) + self.template_discovery = JinjaTemplateDiscovery() self.env = Environment( - loader=FileSystemLoader(self.template_discovery.models_dir), + loader=FileSystemLoader(self.template_discovery.sage_template_dir), autoescape=select_autoescape( ["html", "xml"] ), # Enable autoescape for HTML and XML templates ) logger.info( "Template discovery set to directory: %s", - self.template_discovery.models_dir, + self.template_discovery.sage_template_dir, ) def render_quotation(self, queryset: QuerySet) -> str: @@ -50,7 +48,7 @@ def render_quotation(self, queryset: QuerySet) -> str: """ logger.info("Rendering quotation") invoice = queryset.first() - context = self.render_contax(queryset) + context = self.render_context(queryset) is_receipt = invoice.receipt template_number = "".join(filter(str.isdigit, invoice.template_choice)) logger.info("Selected template number: %s", template_number) @@ -65,7 +63,7 @@ def render_quotation(self, queryset: QuerySet) -> str: return template.render(context) - def render_contax(self, queryset: QuerySet) -> Dict[str, Any]: + def render_context(self, queryset: QuerySet) -> Dict[str, Any]: """Prepare the context data for rendering a quotation. Args: diff --git a/sage_invoice/templates/quotation1.html b/sage_invoice/templates/quotation_1.html similarity index 100% rename from sage_invoice/templates/quotation1.html rename to sage_invoice/templates/quotation_1.html diff --git a/sage_invoice/templates/quotation2.html b/sage_invoice/templates/quotation_2.html similarity index 100% rename from sage_invoice/templates/quotation2.html rename to sage_invoice/templates/quotation_2.html diff --git a/sage_invoice/templates/quotation3.html b/sage_invoice/templates/quotation_3.html similarity index 100% rename from sage_invoice/templates/quotation3.html rename to sage_invoice/templates/quotation_3.html diff --git a/sage_invoice/templates/quotation4.html b/sage_invoice/templates/quotation_4.html similarity index 100% rename from sage_invoice/templates/quotation4.html rename to sage_invoice/templates/quotation_4.html diff --git a/sage_invoice/tests/test_service.py b/sage_invoice/tests/test_service.py index 064ee3e..6c91d8e 100644 --- a/sage_invoice/tests/test_service.py +++ b/sage_invoice/tests/test_service.py @@ -19,7 +19,7 @@ def test_init(self, mock_template_discovery): assert service.template_discovery == mock_template_discovery.return_value assert service.env is not None mock_template_discovery.assert_called_once_with( - models_dir="sage_invoice" # Default directory + sage_template_dir="sage_invoice" # Default directory ) @pytest.fixture @@ -90,13 +90,13 @@ def test_render_quotation_template_not_found(self, mock_get_template_path, invoi mock_get_template_path.assert_called_once() - def test_render_contax(self, invoice): + def test_render_context(self, invoice): # Test context generation for the invoice service = QuotationService() # Ensure contacts and other fields are handled correctly invoice.contacts = ["you@example.com", "1234567890"] - context = service.render_contax(invoice) + context = service.render_context(invoice) assert context["title"] == invoice.title assert context["tracking_code"] == invoice.tracking_code diff --git a/sage_invoice/views/invoice.py b/sage_invoice/views/invoice.py index 287fe51..6914501 100644 --- a/sage_invoice/views/invoice.py +++ b/sage_invoice/views/invoice.py @@ -32,14 +32,14 @@ def get_context_data(self, **kwargs): invoice_slug = self.kwargs.get("slug") invoice = Invoice.objects.filter(slug=invoice_slug).first() service = QuotationService() - rendered_content = service.render_contax(invoice) + rendered_content = service.render_context(invoice) context.update(rendered_content) # Dynamically choose the template based on invoice type and choice if invoice.receipt: - self.template_name = f"receipt{invoice.template_choice}.html" + self.template_name = f"{invoice.template_choice}.html" else: - self.template_name = f"quotation{invoice.template_choice}.html" + self.template_name = f"{invoice.template_choice}.html" return context @@ -79,7 +79,7 @@ def get(self, request, *args, **kwargs): continue service = QuotationService() - rendered_content = service.render_contax(invoice) + rendered_content = service.render_context(invoice) # Determine the template based on invoice type if invoice.receipt: