Add a very basic /stats/ JSON view

Will be useful to feed munin for instance.
This commit is contained in:
David Larlet 2023-05-22 17:47:04 -04:00
parent 32873b7fe3
commit 9d752ea306
3 changed files with 83 additions and 42 deletions

View file

@ -1,4 +1,6 @@
import json
import socket
from datetime import date
import pytest
from django.conf import settings
@ -11,12 +13,12 @@ from umap.views import validate_url
def get(target="http://osm.org/georss.xml", verb="get", **kwargs):
defaults = {
'HTTP_X_REQUESTED_WITH': 'XMLHttpRequest',
'HTTP_REFERER': '%s/path/' % settings.SITE_URL
"HTTP_X_REQUESTED_WITH": "XMLHttpRequest",
"HTTP_REFERER": "%s/path/" % settings.SITE_URL,
}
defaults.update(kwargs)
func = getattr(RequestFactory(**defaults), verb)
return func('/', {'url': target})
return func("/", {"url": target})
def test_good_request_passes():
@ -70,67 +72,91 @@ def test_unkown_domain_raises():
def test_valid_proxy_request(client):
url = reverse('ajax-proxy')
params = {'url': 'http://example.org'}
url = reverse("ajax-proxy")
params = {"url": "http://example.org"}
headers = {
'HTTP_X_REQUESTED_WITH': 'XMLHttpRequest',
'HTTP_REFERER': settings.SITE_URL
"HTTP_X_REQUESTED_WITH": "XMLHttpRequest",
"HTTP_REFERER": settings.SITE_URL,
}
response = client.get(url, params, **headers)
assert response.status_code == 200
assert 'Example Domain' in response.content.decode()
assert 'Cookie' not in response['Vary']
assert "Example Domain" in response.content.decode()
assert "Cookie" not in response["Vary"]
def test_valid_proxy_request_with_ttl(client):
url = reverse('ajax-proxy')
params = {'url': 'http://example.org', 'ttl': 3600}
url = reverse("ajax-proxy")
params = {"url": "http://example.org", "ttl": 3600}
headers = {
'HTTP_X_REQUESTED_WITH': 'XMLHttpRequest',
'HTTP_REFERER': settings.SITE_URL
"HTTP_X_REQUESTED_WITH": "XMLHttpRequest",
"HTTP_REFERER": settings.SITE_URL,
}
response = client.get(url, params, **headers)
assert response.status_code == 200
assert 'Example Domain' in response.content.decode()
assert 'Cookie' not in response['Vary']
assert response['X-Accel-Expires'] == '3600'
assert "Example Domain" in response.content.decode()
assert "Cookie" not in response["Vary"]
assert response["X-Accel-Expires"] == "3600"
def test_valid_proxy_request_with_invalid_ttl(client):
url = reverse('ajax-proxy')
params = {'url': 'http://example.org', 'ttl': 'invalid'}
url = reverse("ajax-proxy")
params = {"url": "http://example.org", "ttl": "invalid"}
headers = {
'HTTP_X_REQUESTED_WITH': 'XMLHttpRequest',
'HTTP_REFERER': settings.SITE_URL
"HTTP_X_REQUESTED_WITH": "XMLHttpRequest",
"HTTP_REFERER": settings.SITE_URL,
}
response = client.get(url, params, **headers)
assert response.status_code == 200
assert 'Example Domain' in response.content.decode()
assert 'Cookie' not in response['Vary']
assert 'X-Accel-Expires' not in response
assert "Example Domain" in response.content.decode()
assert "Cookie" not in response["Vary"]
assert "X-Accel-Expires" not in response
@pytest.mark.django_db
def test_login_does_not_contain_form_if_not_enabled(client, settings):
settings.ENABLE_ACCOUNT_LOGIN = False
response = client.get(reverse('login'))
assert 'username' not in response.content.decode()
response = client.get(reverse("login"))
assert "username" not in response.content.decode()
@pytest.mark.django_db
def test_login_contains_form_if_enabled(client, settings):
settings.ENABLE_ACCOUNT_LOGIN = True
response = client.get(reverse('login'))
assert 'username' in response.content.decode()
response = client.get(reverse("login"))
assert "username" in response.content.decode()
@pytest.mark.django_db
def test_can_login_with_username_and_password_if_enabled(client, settings):
settings.ENABLE_ACCOUNT_LOGIN = True
User = get_user_model()
user = User.objects.create(username='test')
user.set_password('test')
user = User.objects.create(username="test")
user.set_password("test")
user.save()
client.post(reverse('login'), {'username': 'test', 'password': 'test'})
client.post(reverse("login"), {"username": "test", "password": "test"})
user = get_user(client)
assert user.is_authenticated
@pytest.mark.django_db
def test_stats_empty(client):
response = client.get(reverse("stats"))
assert json.loads(response.content.decode()) == {
"maps_active_last_week_count": 0,
"maps_count": 0,
"users_active_last_week_count": 0,
"users_count": 0,
}
@pytest.mark.django_db
def test_stats_basic(client, map, datalayer):
map.owner.last_login = date.today()
map.owner.save()
response = client.get(reverse("stats"))
assert json.loads(response.content.decode()) == {
"maps_active_last_week_count": 1,
"maps_count": 1,
"users_active_last_week_count": 1,
"users_count": 1,
}

View file

@ -1,5 +1,5 @@
from django.conf import settings
from django.urls import include, re_path
from django.urls import include, path, re_path
from django.conf.urls.i18n import i18n_patterns
from django.conf.urls.static import static
from django.contrib import admin
@ -41,9 +41,7 @@ urlpatterns = [
]
i18n_urls = [
re_path(
r"^login/$", jsonize_view(auth_views.LoginView.as_view()), name="login"
),
re_path(r"^login/$", jsonize_view(auth_views.LoginView.as_view()), name="login"),
re_path(
r"^login/popup/end/$", views.LoginPopupEnd.as_view(), name="login_popup_end"
),
@ -96,9 +94,9 @@ i18n_urls += decorated_patterns(
i18n_urls += decorated_patterns(
[login_required],
re_path(
r'^map/(?P<map_id>[\d]+)/star/$',
r"^map/(?P<map_id>[\d]+)/star/$",
views.ToggleMapStarStatus.as_view(),
name='map_star'
name="map_star",
),
)
i18n_urls += decorated_patterns(
@ -151,10 +149,11 @@ urlpatterns += i18n_patterns(
),
re_path(r"^search/$", views.search, name="search"),
re_path(r"^about/$", views.about, name="about"),
re_path(r"^user/(?P<username>.+)/stars/$", views.user_stars, name='user_stars'),
re_path(r"^user/(?P<username>.+)/stars/$", views.user_stars, name="user_stars"),
re_path(r"^user/(?P<username>.+)/$", views.user_maps, name="user_maps"),
re_path(r"", include(i18n_urls)),
)
urlpatterns += (path("stats/", views.stats, name="stats"),)
if settings.DEBUG and settings.MEDIA_ROOT:
urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)

View file

@ -3,6 +3,7 @@ import mimetypes
import os
import re
import socket
from datetime import date, timedelta
from pathlib import Path
from django.conf import settings
@ -10,7 +11,7 @@ from django.contrib import messages
from django.contrib.auth import logout as do_logout
from django.contrib.auth import get_user_model
from django.contrib.gis.measure import D
from django.contrib.postgres.search import SearchQuery, SearchRank, SearchVector
from django.contrib.postgres.search import SearchQuery, SearchVector
from django.core.paginator import EmptyPage, PageNotAnInteger, Paginator
from django.core.signing import BadSignature, Signer
from django.core.validators import URLValidator, ValidationError
@ -214,7 +215,7 @@ class Search(TemplateView, PaginatorMixin):
q, config=settings.UMAP_SEARCH_CONFIGURATION, search_type="websearch"
)
qs = Map.objects.annotate(search=vector).filter(search=query)
qs = qs.filter(share_status=Map.PUBLIC).order_by('-modified_at')
qs = qs.filter(share_status=Map.PUBLIC).order_by("-modified_at")
results = self.paginate(qs)
kwargs.update({"maps": results, "q": q})
return kwargs
@ -381,7 +382,7 @@ class MapDetailMixin:
"allowEdit": self.is_edit_allowed(),
"default_iconUrl": "%sumap/img/marker.png" % settings.STATIC_URL, # noqa
"umap_id": self.get_umap_id(),
'starred': self.is_starred(),
"starred": self.is_starred(),
"licences": dict((l.name, l.json) for l in Licence.objects.all()),
"edit_statuses": [(i, str(label)) for i, label in Map.EDIT_STATUS],
"share_statuses": [
@ -663,9 +664,8 @@ class MapClone(PermissionsMixin, View):
class ToggleMapStarStatus(View):
def post(self, *args, **kwargs):
map_inst = get_object_or_404(Map, pk=kwargs['map_id'])
map_inst = get_object_or_404(Map, pk=kwargs["map_id"])
qs = Star.objects.filter(map=map_inst, by=self.request.user)
if qs.exists():
qs.delete()
@ -852,6 +852,22 @@ class PictogramJSONList(ListView):
# ############## #
def stats(request):
last_week = date.today() - timedelta(days=7)
return simple_json_response(
**{
"maps_count": Map.objects.count(),
"maps_active_last_week_count": Map.objects.filter(
modified_at__gt=last_week
).count(),
"users_count": User.objects.count(),
"users_active_last_week_count": User.objects.filter(
last_login__gt=last_week
).count(),
}
)
def logout(request):
do_logout(request)
return simple_json_response(redirect="/")