Skip to content

Commit 8e643fb

Browse files
authored
Merge pull request #6190 from stsewd/refactor-views-search-analytics
Refactor SearchAnalytics view
2 parents 0dc78b0 + da5fe54 commit 8e643fb

File tree

2 files changed

+73
-83
lines changed

2 files changed

+73
-83
lines changed

readthedocs/projects/urls/private.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434
ProjectUpdate,
3535
ProjectUsersCreateList,
3636
ProjectUsersDelete,
37+
SearchAnalytics,
3738
)
3839

3940
urlpatterns = [
@@ -123,7 +124,8 @@
123124
),
124125
url(
125126
r'^(?P<project_slug>[-\w]+)/search-analytics/$',
126-
private.search_analytics_view, name='projects_search_analytics',
127+
SearchAnalytics.as_view(),
128+
name='projects_search_analytics',
127129
),
128130
]
129131

readthedocs/projects/views/private.py

Lines changed: 70 additions & 82 deletions
Original file line numberDiff line numberDiff line change
@@ -932,98 +932,86 @@ class EnvironmentVariableDelete(EnvironmentVariableMixin, DeleteView):
932932
http_method_names = ['post']
933933

934934

935-
@login_required
936-
def search_analytics_view(request, project_slug):
937-
"""View for search analytics."""
938-
project = get_object_or_404(
939-
Project.objects.for_admin_user(request.user),
940-
slug=project_slug,
941-
)
935+
class SearchAnalytics(ProjectAdminMixin, PrivateViewMixin, TemplateView):
942936

943-
if not project.has_feature(Feature.SEARCH_ANALYTICS):
944-
return render(
945-
request,
946-
'projects/projects_search_analytics.html',
947-
{
948-
'project': project,
949-
'show_analytics': False,
950-
}
951-
)
952-
953-
download_data = request.GET.get('download', False)
937+
template_name = 'projects/projects_search_analytics.html'
938+
http_method_names = ['get']
954939

955-
# if the user has requested to download all data
956-
# return csv file in response.
957-
if download_data:
958-
return _search_analytics_csv_data(request, project_slug)
940+
def get(self, request, *args, **kwargs):
941+
download_data = request.GET.get('download', False)
942+
if download_data:
943+
return self._search_analytics_csv_data()
944+
return super().get(request, *args, **kwargs)
959945

960-
# data for plotting the line-chart
961-
query_count_of_1_month = SearchQuery.generate_queries_count_of_one_month(
962-
project_slug
963-
)
946+
def get_context_data(self, **kwargs):
947+
context = super().get_context_data(**kwargs)
948+
project = self.get_project()
964949

965-
queries = []
966-
qs = SearchQuery.objects.filter(project=project)
967-
if qs.exists():
968-
qs = (
969-
qs.values('query')
970-
.annotate(count=Count('id'))
971-
.order_by('-count', 'query')
972-
.values_list('query', 'count')
950+
context['show_analytics'] = project.has_feature(
951+
Feature.SEARCH_ANALYTICS,
973952
)
953+
if not context['show_analytics']:
954+
return context
974955

975-
# only show top 100 queries
976-
queries = qs[:100]
977-
978-
return render(
979-
request,
980-
'projects/projects_search_analytics.html',
981-
{
982-
'project': project,
983-
'queries': queries,
984-
'show_analytics': True,
985-
'query_count_of_1_month': query_count_of_1_month,
986-
}
987-
)
956+
# data for plotting the line-chart
957+
query_count_of_1_month = SearchQuery.generate_queries_count_of_one_month(
958+
project.slug,
959+
)
988960

961+
queries = []
962+
qs = SearchQuery.objects.filter(project=project)
963+
if qs.exists():
964+
qs = (
965+
qs.values('query')
966+
.annotate(count=Count('id'))
967+
.order_by('-count', 'query')
968+
.values_list('query', 'count')
969+
)
989970

990-
def _search_analytics_csv_data(request, project_slug):
991-
"""Generate raw csv data of search queries."""
992-
project = get_object_or_404(
993-
Project.objects.for_admin_user(request.user),
994-
slug=project_slug,
995-
)
971+
# only show top 100 queries
972+
queries = qs[:100]
996973

997-
now = timezone.now().date()
998-
last_3_month = now - timezone.timedelta(days=90)
974+
context.update(
975+
{
976+
'queries': queries,
977+
'query_count_of_1_month': query_count_of_1_month,
978+
},
979+
)
980+
return context
999981

1000-
data = (
1001-
SearchQuery.objects.filter(
1002-
project=project,
1003-
created__date__gte=last_3_month,
1004-
created__date__lte=now,
982+
def _search_analytics_csv_data(self):
983+
"""Generate raw csv data of search queries."""
984+
project = self.get_project()
985+
now = timezone.now().date()
986+
last_3_month = now - timezone.timedelta(days=90)
987+
988+
data = (
989+
SearchQuery.objects.filter(
990+
project=project,
991+
created__date__gte=last_3_month,
992+
created__date__lte=now,
993+
)
994+
.order_by('-created')
995+
.values_list('created', 'query')
1005996
)
1006-
.order_by('-created')
1007-
.values_list('created', 'query')
1008-
)
1009997

1010-
file_name = '{project_slug}_from_{start}_to_{end}.csv'.format(
1011-
project_slug=project_slug,
1012-
start=timezone.datetime.strftime(last_3_month, '%Y-%m-%d'),
1013-
end=timezone.datetime.strftime(now, '%Y-%m-%d'),
1014-
)
1015-
# remove any spaces in filename.
1016-
file_name = '-'.join([text for text in file_name.split() if text])
998+
file_name = '{project_slug}_from_{start}_to_{end}.csv'.format(
999+
project_slug=project.slug,
1000+
start=timezone.datetime.strftime(last_3_month, '%Y-%m-%d'),
1001+
end=timezone.datetime.strftime(now, '%Y-%m-%d'),
1002+
)
1003+
# remove any spaces in filename.
1004+
file_name = '-'.join([text for text in file_name.split() if text])
10171005

1018-
csv_data = (
1019-
[timezone.datetime.strftime(time, '%Y-%m-%d %H:%M:%S'), query]
1020-
for time, query in data
1021-
)
1022-
pseudo_buffer = Echo()
1023-
writer = csv.writer(pseudo_buffer)
1024-
response = StreamingHttpResponse(
1025-
(writer.writerow(row) for row in csv_data),
1026-
content_type="text/csv",
1027-
)
1028-
response['Content-Disposition'] = f'attachment; filename="{file_name}"'
1029-
return response
1006+
csv_data = (
1007+
[timezone.datetime.strftime(time, '%Y-%m-%d %H:%M:%S'), query]
1008+
for time, query in data
1009+
)
1010+
pseudo_buffer = Echo()
1011+
writer = csv.writer(pseudo_buffer)
1012+
response = StreamingHttpResponse(
1013+
(writer.writerow(row) for row in csv_data),
1014+
content_type="text/csv",
1015+
)
1016+
response['Content-Disposition'] = f'attachment; filename="{file_name}"'
1017+
return response

0 commit comments

Comments
 (0)