Skip to content
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

[B5] Support for migrating report views to Bootstrap 5 #35648

Merged
merged 29 commits into from
Jan 29, 2025
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
92dad95
add utils for bootstrap 5 paths
biyeun May 13, 2024
1b593e9
add ability to set a report to use_bootstrap5
biyeun May 13, 2024
cef9291
pass use_bootstrap5 status to filters so the right template is chosen
biyeun May 13, 2024
eeb8ca5
create utility to get filter class individually
biyeun Jan 21, 2025
850fa06
B5: add report debug tools and way to track completion
biyeun May 13, 2024
9cca827
update bootstrap 5 migration guide for migrating report views
biyeun May 13, 2024
bb6a172
datatables_config (B5) - update how datatables gets imported
biyeun Jan 20, 2025
74f5f6d
update datatables_config (B5) to work with datatables 1.10+
biyeun Jan 20, 2025
a357483
loading template no longer supported in datatables 1.10+
biyeun Jan 20, 2025
b57aa60
add todo about custom sort for future investigation
biyeun Jan 20, 2025
a859093
update how reports handles datatables server side params after 1.10
biyeun Jan 8, 2025
3ff68d0
update the data returned by reports view to datatables
biyeun May 13, 2024
6997dfc
update how sorting block in generated for datatables > 1.10
biyeun May 13, 2024
2196bc7
update request parameter fetching for reports using Datatables 1.10+
biyeun Jan 13, 2025
34b094d
Merge branch 'master' of github.com:dimagi/commcare-hq into bmb/b5-re…
biyeun Jan 28, 2025
691d460
update method name to be clearer
biyeun Jan 28, 2025
b564d74
use get_bootstrap5_path on class variables that were missed
biyeun Jan 28, 2025
8070d78
fix typo: progres > progress
biyeun Jan 28, 2025
51da542
fix typo: list OF filters
biyeun Jan 28, 2025
836c7e6
removing client-side custom sort overrides because datatables 1.10+ s…
biyeun Jan 28, 2025
4ce3d85
add todo note aobut request_params versus request.GET
biyeun Jan 28, 2025
dd18ef4
split 'reports/async/basic.html' to bootstrap 3 and bootstrap 5 versions
biyeun Jan 28, 2025
4820131
update bootstrap3 path to bootstrap 5 in reports/async/bootstrap5/bas…
biyeun Jan 28, 2025
b356d1e
switch between default_path templates for bootstrap 3 and 5 reports
biyeun Jan 28, 2025
079d6a1
make sure DataTablesColumn has a bootstrap 5 version
biyeun Jan 28, 2025
3362e88
migrate bootstrap 5 template for DataTablesColumn
biyeun Jan 28, 2025
73ef95a
update sorting block to reference DataTablesColumn instance, as original
biyeun Jan 28, 2025
ca9c100
add column and sort checks to debug and completion tool
biyeun Jan 28, 2025
7cca4df
"Bootstrap 5 Migration - Rebuilt diffs"
biyeun Jan 28, 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
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
get_all_javascript_paths_for_app,
get_split_paths,
get_short_path,
get_bootstrap5_path,
)
from corehq.apps.hqwebapp.utils.management_commands import (
get_confirmation,
Expand Down Expand Up @@ -142,7 +143,7 @@ def sanitize_bootstrap3_from_filename(self, filename):
f"You specified '{filename}', which appears to be a Bootstrap 3 path!\n"
f"This file cannot be marked as complete with this tool.\n\n"
))
filename = filename.replace('/bootstrap3/', '/bootstrap5/')
filename = get_bootstrap5_path(filename)
confirm = get_confirmation(f"Did you mean '{filename}'?")
if not confirm:
self.stdout.write("Ok, aborting operation.\n\n")
Expand Down
25 changes: 24 additions & 1 deletion corehq/apps/hqwebapp/tests/utils/test_bootstrap_paths.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
from testil import eq
from corehq.apps.hqwebapp.utils.bootstrap.paths import is_ignored_path
from corehq.apps.hqwebapp.utils.bootstrap.paths import (
is_ignored_path,
get_bootstrap5_path,
is_bootstrap3_path,
)


def test_is_ignored_path_true():
Expand All @@ -12,3 +16,22 @@ def test_is_ignored_path_false():
path = "/path/to/corehq/apps/builds/templates/builds/base_builds.html"
app_name = "builds"
eq(is_ignored_path(app_name, path), False)


def test_get_bootstrap5_path():
bootstrap3_path = "reports/bootstrap3/base_template.html"
bootstrap5_path = "reports/bootstrap5/base_template.html"
eq(get_bootstrap5_path(bootstrap3_path), bootstrap5_path)


def test_get_bootstrap5_path_none():
eq(get_bootstrap5_path(None), None)


def test_is_bootstrap3_path():
bootstrap3_path = "reports/bootstrap3/base_template.html"
eq(is_bootstrap3_path(bootstrap3_path), True)


def test_is_bootstrap3_path_false_with_none():
eq(is_bootstrap3_path(None), False)
12 changes: 12 additions & 0 deletions corehq/apps/hqwebapp/utils/bootstrap/paths.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,12 @@ def is_bootstrap5_path(path):
return '/bootstrap5/' in str(path)


def is_bootstrap3_path(path):
if path is None:
return False
return '/bootstrap3/' in path


def is_mocha_path(path):
return str(path).endswith('mocha.html')

Expand Down Expand Up @@ -124,3 +130,9 @@ def get_split_folders(paths, include_root=False):
path.replace(str(COREHQ_BASE_DIR), '') for path in split_folders
}
return split_folders


def get_bootstrap5_path(bootstrap3_path):
if bootstrap3_path is None:
return None
return bootstrap3_path.replace('/bootstrap3/', '/bootstrap5/')
3 changes: 3 additions & 0 deletions corehq/apps/reports/dispatcher.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
from corehq.util.quickcache import quickcache

from .lookup import ReportLookup
from ..hqwebapp.utils.bootstrap import set_bootstrap_version5

datespan_default = datespan_in_request(
from_param="startdate",
Expand Down Expand Up @@ -151,6 +152,8 @@ def dispatch(self, request, domain=None, report_slug=None, render_as=None,
report.decorator_dispatcher(
request, domain=domain, report_slug=report_slug, *args, **kwargs
)
if report.use_bootstrap5:
set_bootstrap_version5()
return getattr(report, '%s_response' % render_as)
except BadRequestError as e:
return HttpResponseBadRequest(e)
Expand Down
8 changes: 6 additions & 2 deletions corehq/apps/reports/filters/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
from memoized import memoized

from corehq.apps.hqwebapp.crispy import CSS_FIELD_CLASS, CSS_LABEL_CLASS
from corehq.apps.hqwebapp.utils.bootstrap.paths import get_bootstrap5_path


class BaseReportFilter(object):
Expand All @@ -24,8 +25,9 @@ class BaseReportFilter(object):
is_cacheable = False
help_style_bubble = False

def __init__(self, request, domain=None, timezone=pytz.utc, parent_report=None,
css_label=None, css_field=None):
def __init__(self, request, domain=None, timezone=pytz.utc,
parent_report=None, css_label=None, css_field=None,
use_bootstrap5=False):
self.domain = domain
if self.slug is None:
raise NotImplementedError("slug is required")
Expand All @@ -39,6 +41,8 @@ def __init__(self, request, domain=None, timezone=pytz.utc, parent_report=None,
self.css_label = css_label or (CSS_LABEL_CLASS + ' control-label')
self.css_field = css_field or CSS_FIELD_CLASS
self.context = {}
if use_bootstrap5:
self.template = get_bootstrap5_path(self.template)
nospame marked this conversation as resolved.
Show resolved Hide resolved

@property
def is_disabled(self):
Expand Down
32 changes: 26 additions & 6 deletions corehq/apps/reports/generic.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
from celery.utils.log import get_task_logger
from memoized import memoized

from corehq.apps.hqwebapp.utils.bootstrap.paths import get_bootstrap5_path
from corehq.util.timezones.utils import get_timezone
from couchexport.export import export_from_tables, get_writer
from couchexport.shortcuts import export_response
Expand Down Expand Up @@ -64,7 +65,7 @@ def _sanitize_col(col):
return col


def get_filter_classes(fields, request, domain, timezone):
def get_filter_classes(fields, request, domain, timezone, use_bootstrap5=False):
filters = []
fields = fields
for field in fields or []:
Expand All @@ -73,7 +74,7 @@ def get_filter_classes(fields, request, domain, timezone):
else:
klass = field
filters.append(
klass(request, domain, timezone)
klass(request, domain, timezone, use_bootstrap5=use_bootstrap5)
)
return filters

Expand Down Expand Up @@ -175,6 +176,9 @@ class GenericReportView(object):
deprecation_email_message = gettext("This report has been deprecated.")
deprecation_message = gettext("This report has been deprecated.")

# use for bootstrap5 migration
use_bootstrap5 = False

def __init__(self, request, base_context=None, domain=None, **kwargs):
if not self.name or not self.section_name or self.slug is None or not self.dispatcher:
raise NotImplementedError(
Expand Down Expand Up @@ -294,19 +298,29 @@ def timezone(self):
@property
@memoized
def template_base(self):
if self.use_bootstrap5:
return get_bootstrap5_path(self.base_template)
return self.base_template

@property
@memoized
def template_async_base(self):
if self.use_bootstrap5:
default_path = "reports/async/bootstrap5/default.html"
else:
default_path = "reports/async/bootstrap3/default.html"
if self.asynchronous:
return self.base_template_async or "reports/async/bootstrap3/default.html"
return self.base_template_async or default_path
biyeun marked this conversation as resolved.
Show resolved Hide resolved
return self.template_base

@property
@memoized
def template_report(self):
original_template = self.report_template_path or "reports/async/basic.html"
if self.use_bootstrap5:
template_path = get_bootstrap5_path(self.report_template_path)
else:
template_path = self.report_template_path
original_template = template_path or "reports/async/basic.html"
biyeun marked this conversation as resolved.
Show resolved Hide resolved
if self.is_rendered_as_email:
self.context.update(original_template=original_template)
return self.override_template
Expand All @@ -315,12 +329,18 @@ def template_report(self):
@property
@memoized
def template_report_partial(self):
if self.use_bootstrap5:
return get_bootstrap5_path(self.report_partial_path)
return self.report_partial_path

@property
@memoized
def template_filters(self):
return self.base_template_filters or "reports/async/bootstrap3/filters.html"
if self.use_bootstrap5:
default_path = "reports/async/bootstrap5/filters.html"
else:
default_path = "reports/async/bootstrap3/filters.html"
return self.base_template_filters or default_path
biyeun marked this conversation as resolved.
Show resolved Hide resolved

@property
@memoized
Expand All @@ -330,7 +350,7 @@ def rendered_report_title(self):
@property
@memoized
def filter_classes(self):
return get_filter_classes(self.fields, self.request, self.domain, self.timezone)
return get_filter_classes(self.fields, self.request, self.domain, self.timezone, self.use_bootstrap5)

@property
@memoized
Expand Down