diff --git a/poetry.lock b/poetry.lock index 5badaf308..0c5da61b2 100644 --- a/poetry.lock +++ b/poetry.lock @@ -4323,13 +4323,13 @@ files = [ [[package]] name = "tablib" -version = "3.7.0" +version = "3.8.0" description = "Format agnostic tabular data library (XLS, JSON, YAML, CSV, etc.)" optional = false python-versions = ">=3.9" files = [ - {file = "tablib-3.7.0-py3-none-any.whl", hash = "sha256:9a6930037cfe0f782377963ca3f2b1dae3fd4cdbf0883848f22f1447e7bb718b"}, - {file = "tablib-3.7.0.tar.gz", hash = "sha256:f9db84ed398df5109bd69c11d46613d16cc572fb9ad3213f10d95e2b5f12c18e"}, + {file = "tablib-3.8.0-py3-none-any.whl", hash = "sha256:35bdb9d4ec7052232f8803908f9c7a9c3c65807188b70618fa7a7d8ccd560b4d"}, + {file = "tablib-3.8.0.tar.gz", hash = "sha256:94d8bcdc65a715a0024a6d5b701a5f31e45bd159269e62c73731de79f048db2b"}, ] [package.extras] @@ -5021,4 +5021,4 @@ propcache = ">=0.2.0" [metadata] lock-version = "2.0" python-versions = "3.11.2" -content-hash = "7dcea59c7a5133a3d027748f6fd77ef6d250dbf6ba09ff82535ce7606d2b25f7" +content-hash = "6a485f0828d702e51ebfe96f113838203009e972b36edf24b4aef7264d036d02" diff --git a/pyproject.toml b/pyproject.toml index e6b12dbc5..3a4dd018d 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -43,7 +43,7 @@ pytz = "^2024.1" requests = "^2.32.3" requests-oauthlib = "^1.3.1" six = "^1.16.0" -tablib = "^3.2.0" +tablib = "^3.8.0" ua-parser = "^1.0.0" djangorestframework = "^3.15.2" cffi = "^1.17.1" diff --git a/website/views/project.py b/website/views/project.py index b8d6f2e28..263b680e3 100644 --- a/website/views/project.py +++ b/website/views/project.py @@ -1,5 +1,6 @@ import ipaddress import json +import logging import re import socket import time @@ -20,7 +21,8 @@ from django.core.exceptions import ValidationError from django.core.paginator import EmptyPage, PageNotAnInteger, Paginator from django.core.validators import URLValidator -from django.db.models import F, Q, Sum +from django.db.models import Count, F, Q, Sum +from django.db.models.functions import TruncDate from django.http import HttpResponse, JsonResponse from django.shortcuts import get_object_or_404, redirect, render from django.utils import timezone @@ -45,7 +47,7 @@ ) from website.utils import admin_required -# logging.getLogger("matplotlib").setLevel(logging.ERROR) +logging.getLogger("matplotlib").setLevel(logging.ERROR) def blt_tomato(request): @@ -96,69 +98,30 @@ def distribute_bacon(request, contribution_id): class ProjectBadgeView(APIView): - def get_client_ip(self, request): - # Check X-Forwarded-For header first - x_forwarded_for = request.META.get("HTTP_X_FORWARDED_FOR") - if x_forwarded_for: - # Return first IP in chain (real client IP) - ip = x_forwarded_for.split(",")[0].strip() - return ip - - # Try X-Real-IP header next - x_real_ip = request.META.get("HTTP_X_REAL_IP") - if x_real_ip: - return x_real_ip - - # Finally fall back to REMOTE_ADDR - remote_addr = request.META.get("REMOTE_ADDR") - return remote_addr - def get(self, request, slug): - # Get the project or return 404 + # Retrieve the project or return 404 project = get_object_or_404(Project, slug=slug) - # Get today's date - today = now().date() - - # Get the real client IP - user_ip = self.get_client_ip(request) - - # Continue with existing code but use the new user_ip - visited_data = IP.objects.filter( - address=user_ip, path=request.path, created__date=today - ).last() - - if visited_data: - # If the creation date is today - if visited_data.created.date() == today: - # If the visit count is 1, update the project visit count - if visited_data.count == 1: - project.project_visit_count = F("project_visit_count") + 1 - project.save() - else: - # If the creation date is not today, reset the creation date and count - visited_data.created = now() - visited_data.count = 1 - visited_data.save() - - # Increment the project visit count - project.project_visit_count = F("project_visit_count") + 1 - project.save() - else: - # If no record exists, create a new one - IP.objects.create(address=user_ip, path=request.path, created=now(), count=1) - - # Increment the project's visit count - project.project_visit_count = F("project_visit_count") + 1 - project.save() + # Get unique visits, grouped by date + visit_counts = ( + IP.objects.filter(path=request.path) + .annotate(date=TruncDate("created")) + .values("date") + .annotate(visit_count=Count("address")) + .order_by("date") # Order from oldest to newest + ) - # Refresh project to get the latest visit count - project.refresh_from_db() + # Update project visit count + project.repo_visit_count += 1 + project.save() - total_views = project.project_visit_count + # Extract dates and counts + dates = [entry["date"] for entry in visit_counts] + counts = [entry["visit_count"] for entry in visit_counts] + total_views = sum(counts) # Calculate total views fig = plt.figure(figsize=(4, 1)) - plt.bar(0, total_views, color="red", width=0.5) + plt.bar(dates, counts, width=0.5, color="red") plt.title( f"{total_views}", @@ -185,7 +148,6 @@ def get(self, request, slug): response["Cache-Control"] = "no-store, no-cache, must-revalidate, max-age=0" response["Pragma"] = "no-cache" response["Expires"] = "0" - return response