Skip to content

Commit d8142a5

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 <aayush214.kumar@gmail.com>
1 parent 61bb26d commit d8142a5

3 files changed

Lines changed: 82 additions & 1 deletion

File tree

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
@@ -498,6 +498,69 @@ def get(self, request, *args, **kwargs):
498498
return response
499499

500500

501+
class ExportJSONMixin:
502+
"""
503+
Add the ability to export the current filtered QuerySet of a `FilterView`
504+
into JSON format.
505+
"""
506+
507+
export_json_query_param = "export_json"
508+
509+
def get_export_json_queryset(self):
510+
return self.filterset.qs
511+
512+
def get_export_json_filename(self):
513+
return f"{self.project.name}_{self.model._meta.model_name}.json"
514+
515+
def get_filtered_files(self, queryset):
516+
from scanpipe.api.serializers import CodebaseResourceSerializer
517+
518+
yield from self.encode_queryset(queryset, CodebaseResourceSerializer)
519+
520+
def export_json_file_response(self):
521+
queryset = self.get_export_json_queryset()
522+
523+
output_file = io.BytesIO()
524+
525+
for chunk in self.get_filtered_files(queryset):
526+
output_file.write(chunk.encode("utf-8"))
527+
output_file.seek(0)
528+
529+
return FileResponse(
530+
output_file,
531+
as_attachment=True,
532+
filename=self.get_export_json_filename(),
533+
content_type="application/json",
534+
)
535+
536+
def encode_queryset(self, queryset, serializer_class):
537+
for obj in queryset.iterator(chunk_size=2000):
538+
serialized_obj = serializer_class(obj)
539+
data = serialized_obj.data
540+
541+
for field in output.JSON_EXCLUDE_FIELDS:
542+
data.pop(field, None)
543+
544+
yield json.dumps(data, indent=2)
545+
546+
def get_context_data(self, **kwargs):
547+
context = super().get_context_data(**kwargs)
548+
549+
query_dict = self.request.GET.copy()
550+
query_dict[self.export_json_query_param] = True
551+
context["export_json_url_query"] = query_dict.urlencode()
552+
553+
return context
554+
555+
def get(self, request, *args, **kwargs):
556+
response = super().get(request, *args, **kwargs)
557+
558+
if request.GET.get(self.export_json_query_param):
559+
return self.export_json_file_response()
560+
561+
return response
562+
563+
501564
class FormAjaxMixin:
502565
def is_xhr(self):
503566
return self.request.META.get("HTTP_X_REQUESTED_WITH") == "XMLHttpRequest"
@@ -1542,6 +1605,7 @@ class CodebaseResourceListView(
15421605
ProjectRelatedViewMixin,
15431606
TableColumnsMixin,
15441607
ExportXLSXMixin,
1608+
ExportJSONMixin,
15451609
PaginatedFilterView,
15461610
):
15471611
model = CodebaseResource
@@ -1615,6 +1679,7 @@ class DiscoveredPackageListView(
16151679
ProjectRelatedViewMixin,
16161680
TableColumnsMixin,
16171681
ExportXLSXMixin,
1682+
ExportJSONMixin,
16181683
PaginatedFilterView,
16191684
):
16201685
model = DiscoveredPackage
@@ -1671,6 +1736,7 @@ class DiscoveredDependencyListView(
16711736
ProjectRelatedViewMixin,
16721737
TableColumnsMixin,
16731738
ExportXLSXMixin,
1739+
ExportJSONMixin,
16741740
PaginatedFilterView,
16751741
):
16761742
model = DiscoveredDependency
@@ -1740,6 +1806,7 @@ class ProjectMessageListView(
17401806
ProjectRelatedViewMixin,
17411807
TableColumnsMixin,
17421808
ExportXLSXMixin,
1809+
ExportJSONMixin,
17431810
FilterView,
17441811
):
17451812
model = ProjectMessage
@@ -1764,6 +1831,7 @@ class CodebaseRelationListView(
17641831
PrefetchRelatedViewMixin,
17651832
TableColumnsMixin,
17661833
ExportXLSXMixin,
1834+
ExportJSONMixin,
17671835
PaginatedFilterView,
17681836
):
17691837
model = CodebaseRelation

0 commit comments

Comments
 (0)