mirror of
https://github.com/macports/macports-webapp.git
synced 2026-07-13 03:18:42 -07:00
260 lines
9.9 KiB
Python
260 lines
9.9 KiB
Python
import json
|
|
import datetime
|
|
|
|
from django.shortcuts import render
|
|
from django.http import HttpResponse
|
|
from django.views.decorators.csrf import csrf_exempt
|
|
from django.core.paginator import Paginator, PageNotAnInteger, EmptyPage
|
|
from django.db.models import Subquery, Count, Case, IntegerField, When
|
|
from django.db.models.functions import TruncMonth
|
|
from rest_framework.response import Response
|
|
from rest_framework.views import APIView
|
|
from django.views.decorators.cache import cache_page
|
|
from django.utils.decorators import method_decorator
|
|
|
|
from stats.models import Submission, PortInstallation
|
|
from stats.validators import validate_stats_days, validate_columns_port_installations, validate_unique_columns_port_installations, ALLOWED_DAYS_FOR_STATS
|
|
from stats.serializers import PortStatisticsSerializer, PortMonthlyInstallationsSerializer, GeneralStatisticsSerializer
|
|
from stats.utilities import dates
|
|
|
|
def stats(request):
|
|
days = request.GET.get('days', 30)
|
|
days_ago = request.GET.get('days_ago', 0)
|
|
|
|
# Validate days and days_ago
|
|
for value in days, days_ago:
|
|
check, message = validate_stats_days(value)
|
|
if check is False:
|
|
return HttpResponse(message)
|
|
|
|
days = int(days)
|
|
days_ago = int(days_ago)
|
|
|
|
end_date = datetime.datetime.now(tz=datetime.timezone.utc) - datetime.timedelta(days=days_ago)
|
|
start_date = end_date - datetime.timedelta(days=days)
|
|
|
|
current_week = datetime.datetime.today().isocalendar()[1]
|
|
all_submissions = Submission.objects.all().only('id')
|
|
total_unique_users = all_submissions.distinct('user').count()
|
|
current_week_unique = all_submissions.filter(timestamp__week=current_week).distinct('user').count()
|
|
last_week_unique = all_submissions.filter(timestamp__week=current_week - 1).distinct('user').count()
|
|
|
|
# Number of unique users vs month
|
|
users_by_month = Submission.objects.only('id').filter(timestamp__gte=dates.get_first_day_of_month_x_months_ago(12)).annotate(month=TruncMonth('timestamp')).values('month').annotate(num=Count('user_id', distinct=True))
|
|
|
|
submissions_last_x_days = Submission.objects.only('id').filter(timestamp__range=[start_date, end_date]).order_by('user', '-timestamp').distinct('user')
|
|
|
|
return render(request, 'stats/stats.html', {
|
|
'total_submissions': all_submissions.count(),
|
|
'unique_users': total_unique_users,
|
|
'current_week': current_week_unique,
|
|
'last_week': last_week_unique,
|
|
'users_by_month': users_by_month,
|
|
'days': days,
|
|
'days_ago': days_ago,
|
|
'start_date': start_date,
|
|
'end_date': end_date,
|
|
'users_count_in_duration': submissions_last_x_days.count(),
|
|
'allowed_days': ALLOWED_DAYS_FOR_STATS
|
|
})
|
|
|
|
|
|
def stats_port_installations(request):
|
|
days = request.GET.get('days', 30)
|
|
first = str(request.GET.get('first', '-total_count'))
|
|
second = str(request.GET.get('second', '-req_count'))
|
|
third = str(request.GET.get('third', 'port'))
|
|
columns = [first, second, third]
|
|
|
|
# Validate days
|
|
check, message = validate_stats_days(days)
|
|
if check is False:
|
|
return HttpResponse(message)
|
|
|
|
# Validate columns
|
|
check, message = validate_columns_port_installations(columns)
|
|
if check is False:
|
|
return HttpResponse(message)
|
|
|
|
# Validate unique columns
|
|
check, message = validate_unique_columns_port_installations(columns)
|
|
if check is False:
|
|
return HttpResponse(message)
|
|
|
|
return render(request, 'stats/stats_port_installations.html', {
|
|
'days': int(days),
|
|
'first': first,
|
|
'second': second,
|
|
'third': third,
|
|
'allowed_days': ALLOWED_DAYS_FOR_STATS
|
|
})
|
|
|
|
|
|
@cache_page(60 * 60 * 24)
|
|
def stats_port_installations_filter(request):
|
|
days = request.GET.get('days', 30)
|
|
order_by_1 = str(request.GET.get('order_by_1', '-total_count'))
|
|
order_by_2 = str(request.GET.get('order_by_2', '-req_count'))
|
|
order_by_3 = str(request.GET.get('order_by_3', 'port'))
|
|
search_by = str(request.GET.get('search_by', ''))
|
|
columns = [order_by_1, order_by_2, order_by_3]
|
|
|
|
# Validate days
|
|
check, message = validate_stats_days(days)
|
|
if check is False:
|
|
return HttpResponse(message)
|
|
|
|
# Validate columns
|
|
check, message = validate_columns_port_installations(columns)
|
|
if check is False:
|
|
return HttpResponse(message)
|
|
|
|
# Validate unique columns
|
|
check, message = validate_unique_columns_port_installations(columns)
|
|
if check is False:
|
|
return HttpResponse(message)
|
|
|
|
days = int(days)
|
|
|
|
submissions_unique = Submission.objects.filter(timestamp__gte=datetime.datetime.now(tz=datetime.timezone.utc) - datetime.timedelta(days=days)).order_by('user','-timestamp').distinct('user')
|
|
installations = PortInstallation.objects.order_by('port') \
|
|
.filter(submission_id__in=Subquery(submissions_unique.values('id'))) \
|
|
.values('port').annotate(total_count=Count('port')) \
|
|
.annotate(req_count=Count(Case(When(requested=True, then=1), output_field=IntegerField()))) \
|
|
.exclude(port__icontains='mpstats') \
|
|
.filter(port__icontains=search_by) \
|
|
.extra(select={'port': 'lower(port)'}) \
|
|
.order_by(order_by_1, order_by_2, order_by_3)
|
|
|
|
paginated_obj = Paginator(installations, 100)
|
|
page = request.GET.get('page', 1)
|
|
try:
|
|
installs = paginated_obj.get_page(page)
|
|
except PageNotAnInteger:
|
|
installs = paginated_obj.get_page(1)
|
|
except EmptyPage:
|
|
installs = paginated_obj.get_page(paginated_obj.num_pages)
|
|
|
|
return render(request, 'stats/port_installations_table.html', {
|
|
'installs': installs,
|
|
'search_by': search_by
|
|
})
|
|
|
|
|
|
def stats_faq(request):
|
|
return render(request, 'stats/stats_faq.html')
|
|
|
|
|
|
@csrf_exempt
|
|
def stats_submit(request):
|
|
if request.method == "POST":
|
|
try:
|
|
received_body = request.body.decode()
|
|
prefix = 'submission[data]='
|
|
if not received_body.startswith(prefix):
|
|
return HttpResponse("Invalid body of the request.")
|
|
|
|
received_json = json.loads(received_body[len(prefix):])
|
|
submission_id = Submission.populate(received_json, datetime.datetime.now(tz=datetime.timezone.utc))
|
|
PortInstallation.populate(received_json['active_ports'], submission_id)
|
|
|
|
return HttpResponse("Success")
|
|
|
|
except:
|
|
return HttpResponse("Something went wrong")
|
|
else:
|
|
return HttpResponse("Method Not Allowed")
|
|
|
|
|
|
class PortStatisticsAPIView(APIView):
|
|
@method_decorator(cache_page(60 * 60))
|
|
def get(self, request):
|
|
result = PortStatisticsSerializer(
|
|
PortInstallation.objects.all(),
|
|
context={
|
|
'name': request.GET.get('name'),
|
|
'days': request.GET.get('days', "30"),
|
|
'days_ago': request.GET.get('days_ago', "0"),
|
|
'property': request.GET.getlist('property'),
|
|
'sort_by': request.GET.get('sort_by')
|
|
}
|
|
)
|
|
return Response(result.data)
|
|
|
|
|
|
class PortMonthlyInstallationsAPIView(APIView):
|
|
@method_decorator(cache_page(60 * 60 * 24))
|
|
def get(self, request):
|
|
result = PortMonthlyInstallationsSerializer(
|
|
PortInstallation.objects.all(),
|
|
context={
|
|
'name': request.GET.get('name'),
|
|
'include_versions': request.GET.get('include_versions')
|
|
}
|
|
)
|
|
return Response(result.data)
|
|
|
|
|
|
class GeneralStatisticsAPIView(APIView):
|
|
def get(self, request):
|
|
result = GeneralStatisticsSerializer(
|
|
Submission.objects.all(),
|
|
context={
|
|
'days': request.GET.get('days', "30"),
|
|
'days_ago': request.GET.get('days_ago', "0"),
|
|
'property': request.GET.getlist('property'),
|
|
'sort_by': request.GET.get('sort_by')
|
|
}
|
|
)
|
|
return Response(result.data)
|
|
|
|
|
|
class PopularPortsAPIView(APIView):
|
|
@method_decorator(cache_page(60 * 60)) # Cache for 1 hour
|
|
def get(self, request):
|
|
days = int(request.GET.get('days', 30))
|
|
limit = int(request.GET.get('limit', 10))
|
|
|
|
# Get submissions from the last X days
|
|
end_date = datetime.datetime.now(tz=datetime.timezone.utc)
|
|
start_date = end_date - datetime.timedelta(days=days)
|
|
|
|
submissions_unique = Submission.objects.filter(
|
|
timestamp__range=[start_date, end_date]
|
|
).order_by('user', '-timestamp').distinct('user')
|
|
|
|
# Get popular ports based on installation counts
|
|
popular_ports = PortInstallation.objects.filter(
|
|
submission_id__in=Subquery(submissions_unique.values('id')),
|
|
requested=True
|
|
).exclude(
|
|
port__icontains='mpstats'
|
|
).values('port').annotate(
|
|
total_count=Count('port'),
|
|
req_count=Count(Case(When(requested=True, then=1), output_field=IntegerField()))
|
|
).order_by('-total_count')[:limit]
|
|
|
|
return Response(list(popular_ports))
|
|
|
|
|
|
class EnhancedStatisticsAPIView(APIView):
|
|
@method_decorator(cache_page(60 * 60)) # Cache for 1 hour
|
|
def get(self, request):
|
|
from port.models import Port
|
|
|
|
# Get basic statistics
|
|
current_week = datetime.datetime.today().isocalendar()[1]
|
|
all_submissions = Submission.objects.all().only('id')
|
|
total_unique_users = all_submissions.distinct('user').count()
|
|
current_week_unique = all_submissions.filter(timestamp__week=current_week).distinct('user').count()
|
|
last_week_unique = all_submissions.filter(timestamp__week=current_week - 1).distinct('user').count()
|
|
total_ports = Port.objects.filter(active=True).count()
|
|
|
|
return Response({
|
|
'current_week': current_week_unique,
|
|
'last_week': last_week_unique,
|
|
'total_submissions': all_submissions.count(),
|
|
'total_unique_users': total_unique_users,
|
|
'total_ports': total_ports,
|
|
})
|