Skip to content

Report merge #9532

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 36 commits into
base: master
Choose a base branch
from
Open
Changes from 1 commit
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
a92a1e1
Report merge
tristanle22 Apr 18, 2025
bd57809
Merge remote-tracking branch 'origin/master' into report_merge
tristanle22 Apr 18, 2025
25eef24
Remove auto-generated file
tristanle22 Apr 18, 2025
00d80ba
Remove pre-commit file
tristanle22 Apr 18, 2025
1c43864
Revert "Remove pre-commit file"
tristanle22 Apr 18, 2025
b4ce88b
Update API version
tristanle22 Apr 18, 2025
3f22396
Merge remote-tracking branch 'origin/master' into report_merge
tristanle22 Apr 20, 2025
5f58cf5
Reduced duplicated logic
tristanle22 Apr 21, 2025
559396e
Merge branch 'master' into report_merge
matmair Apr 21, 2025
5fb3638
reset pre-commit config
matmair Apr 21, 2025
497706e
Added migration files
tristanle22 Apr 23, 2025
b5b9397
Merge branch 'report_merge' of https://github.com/tristanle22/InvenTr…
tristanle22 Apr 23, 2025
7ef03b0
Added unit test
tristanle22 Apr 24, 2025
02a9af8
Removed redundant migration
tristanle22 Apr 24, 2025
4299338
Updated migration file
tristanle22 Apr 30, 2025
aeda554
Added a default report template with merge enabled
tristanle22 Apr 30, 2025
e4eb544
Unit test to ensure a single page is generated
tristanle22 Apr 30, 2025
effd432
Added docs to support merge feature
tristanle22 May 1, 2025
ae979e1
Clean up
tristanle22 May 1, 2025
0c11cb7
Merge branch 'master' into report_merge
tristanle22 May 1, 2025
390f03e
Merge branch 'report_merge' of https://github.com/tristanle22/InvenTr…
tristanle22 May 1, 2025
33bbc8b
Clean up
tristanle22 May 1, 2025
579e627
Merge branch 'master' into report_merge
tristanle22 May 5, 2025
dcb1040
Fixed unresolved link
tristanle22 May 5, 2025
da5a113
Merge branch 'report_merge' of https://github.com/tristanle22/InvenTr…
tristanle22 May 5, 2025
b9bdcfe
Merge branch 'master' into report_merge
tristanle22 May 7, 2025
026d1d0
Updated API version
tristanle22 May 7, 2025
f441996
Merge branch 'master' into report_merge
tristanle22 May 9, 2025
c5eb102
Merge branch 'master' into report_merge
tristanle22 May 10, 2025
986407b
Fixed test report path issue
tristanle22 May 12, 2025
d65292b
Merge branch 'report_merge' of https://github.com/tristanle22/InvenTr…
tristanle22 May 12, 2025
306adf2
Merge branch 'master' into report_merge
tristanle22 May 12, 2025
d20cf41
Merge branch 'master' into report_merge
tristanle22 May 15, 2025
12a0405
Add plugin context for each instance
tristanle22 May 18, 2025
56fb14c
Merge branch 'report_merge' of https://github.com/tristanle22/InvenTr…
tristanle22 May 18, 2025
33af985
Merge branch 'master' into report_merge
tristanle22 May 19, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
93 changes: 48 additions & 45 deletions src/backend/InvenTree/report/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -233,31 +233,34 @@ def generate_filename(self, context, **kwargs):

return template_string.render(Context(context))

def render_as_string(self, instance, request=None, **kwargs) -> str:
def render_as_string(self, instance, request=None, context=None, **kwargs) -> str:
"""Render the report to a HTML string.

Arguments:
instance: The model instance to render against
request: A HTTPRequest object (optional)
context: DTL context (optional)

Returns:
str: HTML string
"""
context = self.get_context(instance, request, **kwargs)
if context is None:
context = self.get_context(instance, request, **kwargs)

return render_to_string(self.template_name, context, request)

def render(self, instance, request=None, **kwargs) -> bytes:
def render(self, instance, request=None, context=None, **kwargs) -> bytes:
"""Render the template to a PDF file.

Arguments:
instance: The model instance to render against
request: A HTTPRequest object (optional)
context: DTL context (optional)

Returns:
bytes: PDF data
"""
html = self.render_as_string(instance, request, **kwargs)
html = self.render_as_string(instance, request, context, **kwargs)
pdf = HTML(string=html).write_pdf()

return pdf
Expand Down Expand Up @@ -424,6 +427,27 @@ def get_context(self, instance, request=None, **kwargs):

return context

def handle_attachment(self, instance, report, report_name, request, debug_mode):
"""Attach the generated report to the model instance (if required)."""
if self.attach_to_model and not debug_mode:
instance.create_attachment(
attachment=ContentFile(report, report_name),
comment=_(f'Report generated from template {self.name}'),
upload_user=request.user
if request and request.user.is_authenticated
else None,
)

def notify_plugins(self, instance, report, request):
"""Provide generated report to any interested plugins."""
report_plugins = registry.with_mixin(PluginMixinEnum.REPORT)

for plugin in report_plugins:
try:
plugin.report_callback(self, instance, report, request)
except Exception:
InvenTree.exceptions.log_error(f'plugins.{plugin.slug}.report_callback')

def print(self, items: list, request=None, output=None, **kwargs) -> DataOutput:
"""Print reports for a list of items against this template.

Expand Down Expand Up @@ -455,7 +479,7 @@ def print(self, items: list, request=None, output=None, **kwargs) -> DataOutput:
# Start with a default report name
report_name = None

report_plugins = registry.with_mixin(PluginMixinEnum.REPORT)
# report_plugins = registry.with_mixin(PluginMixinEnum.REPORT)

# If a DataOutput object is not provided, create a new one
if not output:
Expand All @@ -482,6 +506,7 @@ def print(self, items: list, request=None, output=None, **kwargs) -> DataOutput:
item_contexts = []
for instance in items:
item_contexts.append(instance.report_context())

contexts = {
**base_context,
**report_context,
Expand All @@ -491,29 +516,21 @@ def print(self, items: list, request=None, output=None, **kwargs) -> DataOutput:
if report_name is None:
report_name = self.generate_filename(contexts)

html = render_to_string(self.template_name, contexts, request)
report = HTML(string=html).write_pdf()
try:
if debug_mode:
report = self.render_as_string(instance, request, contexts)
else:
report = self.render(instance, request, contexts)
except TemplateDoesNotExist as e:
t_name = str(e) or self.template
raise ValidationError(f'Template file {t_name} does not exist')

outputs.append(report)

# Attach the generated report to the model instance (if required)
if self.attach_to_model and not debug_mode:
instance.create_attachment(
attachment=ContentFile(report, report_name),
comment=_(f'Report generated from template {self.name}'),
upload_user=request.user
if request and request.user.is_authenticated
else None,
)

# Provide generated report to any interested plugins
for plugin in report_plugins:
try:
plugin.report_callback(self, instance, report, request)
except Exception:
InvenTree.exceptions.log_error(
f'plugins.{plugin.slug}.report_callback'
)
self.handle_attachment(
instance, report, report_name, request, debug_mode
)
self.notify_plugins(instance, report, request)

# Update the progress of the report generation
output.progress += 1
Expand All @@ -528,33 +545,19 @@ def print(self, items: list, request=None, output=None, **kwargs) -> DataOutput:
# Render the report output
try:
if debug_mode:
report = self.render_as_string(instance, request)
report = self.render_as_string(instance, request, None)
else:
report = self.render(instance, request)
report = self.render(instance, request, None)
except TemplateDoesNotExist as e:
t_name = str(e) or self.template
raise ValidationError(f'Template file {t_name} does not exist')

outputs.append(report)

# Attach the generated report to the model instance (if required)
if self.attach_to_model and not debug_mode:
instance.create_attachment(
attachment=ContentFile(report, report_name),
comment=_(f'Report generated from template {self.name}'),
upload_user=request.user
if request and request.user.is_authenticated
else None,
)

# Provide generated report to any interested plugins
for plugin in report_plugins:
try:
plugin.report_callback(self, instance, report, request)
except Exception:
InvenTree.exceptions.log_error(
f'plugins.{plugin.slug}.report_callback'
)
self.handle_attachment(
instance, report, report_name, request, debug_mode
)
self.notify_plugins(instance, report, request)

# Update the progress of the report generation
output.progress += 1
Expand Down