Skip to content

Commit 169c023

Browse files
committed
Added the ability to export the current filtered QuerySet of a 'FilterView' into the JSON format, Solves issue #1319
Signed-off-by: Aayush Kumar <[email protected]>
1 parent d9f50b1 commit 169c023

File tree

3 files changed

+82
-1
lines changed

3 files changed

+82
-1
lines changed

scanpipe/pipes/output.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -280,6 +280,16 @@ def get_relations(self, project):
280280
)
281281

282282

283+
JSON_EXCLUDE_FIELDS = [
284+
"extra_data",
285+
"package_data",
286+
"license_detections",
287+
"other_license_detections",
288+
"license_clues",
289+
"affected_by_vulnerabilities",
290+
]
291+
292+
283293
def to_json(project):
284294
"""
285295
Generate output for the provided `project` in JSON format.

scanpipe/templates/scanpipe/dropdowns/list_actions_dropdown.html

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,10 @@
1010
<div class="dropdown-menu" id="dropdown-menu-action" role="menu">
1111
<div class="dropdown-content">
1212
<a href="?{{ export_xlsx_url_query }}" class="dropdown-item">
13-
<i class="fa-solid fa-download mr-2"></i>Export results as XLSX
13+
<i class="fa-solid fa-file-excel mr-2"></i>Export results as XLSX
14+
</a>
15+
<a href="?{{ export_json_url_query }}" class="dropdown-item">
16+
<i class="fa-solid fa-file-pdf mr-2"></i>Export results as JSON
1417
</a>
1518
</div>
1619
</div>

scanpipe/views.py

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -490,6 +490,69 @@ def get(self, request, *args, **kwargs):
490490
return response
491491

492492

493+
class ExportJSONMixin:
494+
"""
495+
Add the ability to export the current filtered QuerySet of a `FilterView`
496+
into JSON format.
497+
"""
498+
499+
export_json_query_param = "export_json"
500+
501+
def get_export_json_queryset(self):
502+
return self.filterset.qs
503+
504+
def get_export_json_filename(self):
505+
return f"{self.project.name}_{self.model._meta.model_name}.json"
506+
507+
def get_filtered_files(self, queryset):
508+
from scanpipe.api.serializers import CodebaseResourceSerializer
509+
yield from self.encode_queryset(queryset, CodebaseResourceSerializer)
510+
511+
def export_json_file_response(self):
512+
queryset = self.get_export_json_queryset()
513+
514+
output_file = io.BytesIO()
515+
516+
for chunk in self.get_filtered_files(queryset):
517+
output_file.write(chunk.encode("utf-8"))
518+
output_file.seek(0)
519+
520+
return FileResponse(
521+
output_file,
522+
as_attachment=True,
523+
filename=self.get_export_json_filename(),
524+
content_type="application/json",
525+
)
526+
527+
def encode_queryset(self, queryset, serializer_class):
528+
for obj in queryset.iterator(chunk_size=2000):
529+
530+
serialized_obj = serializer_class(obj)
531+
data = serialized_obj.data
532+
533+
for field in output.JSON_EXCLUDE_FIELDS:
534+
data.pop(field, None)
535+
536+
yield json.dumps(data, indent=2)
537+
538+
def get_context_data(self, **kwargs):
539+
context = super().get_context_data(**kwargs)
540+
541+
query_dict = self.request.GET.copy()
542+
query_dict[self.export_json_query_param] = True
543+
context["export_json_url_query"] = query_dict.urlencode()
544+
545+
return context
546+
547+
def get(self, request, *args, **kwargs):
548+
response = super().get(request, *args, **kwargs)
549+
550+
if request.GET.get(self.export_json_query_param):
551+
return self.export_json_file_response()
552+
553+
return response
554+
555+
493556
class FormAjaxMixin:
494557
def is_xhr(self):
495558
return self.request.META.get("HTTP_X_REQUESTED_WITH") == "XMLHttpRequest"
@@ -1511,6 +1574,7 @@ class CodebaseResourceListView(
15111574
ProjectRelatedViewMixin,
15121575
TableColumnsMixin,
15131576
ExportXLSXMixin,
1577+
ExportJSONMixin,
15141578
PaginatedFilterView,
15151579
):
15161580
model = CodebaseResource
@@ -1584,6 +1648,7 @@ class DiscoveredPackageListView(
15841648
ProjectRelatedViewMixin,
15851649
TableColumnsMixin,
15861650
ExportXLSXMixin,
1651+
ExportJSONMixin,
15871652
PaginatedFilterView,
15881653
):
15891654
model = DiscoveredPackage
@@ -1640,6 +1705,7 @@ class DiscoveredDependencyListView(
16401705
ProjectRelatedViewMixin,
16411706
TableColumnsMixin,
16421707
ExportXLSXMixin,
1708+
ExportJSONMixin,
16431709
PaginatedFilterView,
16441710
):
16451711
model = DiscoveredDependency
@@ -1709,6 +1775,7 @@ class ProjectMessageListView(
17091775
ProjectRelatedViewMixin,
17101776
TableColumnsMixin,
17111777
ExportXLSXMixin,
1778+
ExportJSONMixin,
17121779
FilterView,
17131780
):
17141781
model = ProjectMessage
@@ -1733,6 +1800,7 @@ class CodebaseRelationListView(
17331800
PrefetchRelatedViewMixin,
17341801
TableColumnsMixin,
17351802
ExportXLSXMixin,
1803+
ExportJSONMixin,
17361804
PaginatedFilterView,
17371805
):
17381806
model = CodebaseRelation

0 commit comments

Comments
 (0)