Skip to content

Commit 3ad0124

Browse files
authored
feat: Render usage report in sales dashboard usage directly, without sending email (#5407)
1 parent ff9b43a commit 3ad0124

File tree

11 files changed

+78
-94
lines changed

11 files changed

+78
-94
lines changed

api/sales_dashboard/forms.py

Lines changed: 0 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,5 @@
1-
import inspect
2-
from datetime import date
3-
41
from django import forms
5-
from django.conf import settings
6-
from django.core.mail import send_mail
72

8-
from environments.models import Environment
9-
from features.models import Feature
103
from organisations.models import (
114
Organisation,
125
OrganisationSubscriptionInformationCache,
@@ -17,8 +10,6 @@
1710
MAX_SEATS_IN_FREE_PLAN,
1811
TRIAL_SUBSCRIPTION_ID,
1912
)
20-
from projects.models import Project
21-
from users.models import FFAdminUser
2213

2314

2415
class MaxSeatsForm(forms.Form):
@@ -86,28 +77,3 @@ def save(self, organisation: Organisation, commit: bool = True) -> Organisation:
8677
osic.save()
8778

8879
return organisation
89-
90-
91-
class EmailUsageForm(forms.Form):
92-
email_address = forms.EmailField()
93-
94-
def save(self, commit=True): # type: ignore[no-untyped-def]
95-
message = inspect.cleandoc(
96-
f"""
97-
Current Flagsmith Stats on {date.today()}:
98-
99-
Total Organisations = {Organisation.objects.all().count()}
100-
Total Projects = {Project.objects.all().count()}
101-
Total Environments = {Environment.objects.all().count()}
102-
Total Features = {Feature.objects.all().count()}
103-
Total Seats = {FFAdminUser.objects.all().count()}
104-
"""
105-
)
106-
107-
send_mail(
108-
subject="Flagsmith Usage Report",
109-
message=message,
110-
from_email=settings.DEFAULT_FROM_EMAIL,
111-
recipient_list=[self.cleaned_data["email_address"]],
112-
fail_silently=True,
113-
)

api/sales_dashboard/templates/sales_dashboard/base.html

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,10 @@
2727
</li>
2828
</ul>
2929
</nav>
30+
<div class="py-3">
3031
{% block content %}
3132
{% endblock %}
33+
</div>
3234
<!-- Optional JavaScript -->
3335
<!-- jQuery first, then Popper.js, then Bootstrap JS -->
3436
<script src="https://code.jquery.com/jquery-3.5.1.slim.min.js" integrity="sha384-DfXdz2htPH0lsSSs5nCTpuj/zy4C+OGpamoFVy38MVBnE+IbbVYUew+OrCXaRkfj" crossorigin="anonymous"></script>

api/sales_dashboard/templates/sales_dashboard/email-usage.html

Lines changed: 0 additions & 17 deletions
This file was deleted.

api/sales_dashboard/templates/sales_dashboard/home.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88

99
<main role="main" class="col-md-9 ml-sm-auto col-lg-10 px-md-4">
1010
<div
11-
class="d-flex justify-content-between flex-wrap flex-md-nowrap align-items-center pt-3 pb-2 mb-3 border-bottom">
11+
class="d-flex justify-content-between flex-wrap flex-md-nowrap align-items-center border-bottom">
1212
<h1 class="h2">Organisations</h1>
1313
<div class="float-right">
1414
<div class='float-left'>

api/sales_dashboard/templates/sales_dashboard/nav.html

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,11 @@
99
</a>
1010
</li>
1111
<li class="nav-item">
12-
<a class="nav-link active" href="/sales-dashboard/email-usage/">
12+
<a class="nav-link active" href="/sales-dashboard/usage/">
1313
<span data-feather="home"></span>
14-
Email Usage
14+
Usage report
1515
</a>
1616
</li>
1717
</ul>
1818
</div>
19-
</nav>
19+
</nav>
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
{% extends "sales_dashboard/base.html" %}
2+
3+
{% block title %}
4+
Usage Report
5+
{% endblock %}
6+
7+
{% block content %}
8+
<div class="container-fluid">
9+
<div class="row">
10+
{% include "sales_dashboard/nav.html" %}
11+
<main role="main" class="col-md-9 ml-sm-auto col-lg-10 px-md-4">
12+
<h1 class="title">Flagsmith Usage Report</h1>
13+
<p>{{ stats.site_url }}</p>
14+
<p>{{ stats.report_date }} UTC</p>
15+
<ul>
16+
<li>Total Organisations: {{ stats.total_organisations }}</li>
17+
<li>Total Projects: {{ stats.total_projects }}</li>
18+
<li>Total Environments: {{ stats.total_environments }}</li>
19+
<li>Total Features: {{ stats.total_features }}</li>
20+
<li>Total User Seats: {{ stats.total_seats }}</li>
21+
</ul>
22+
</main>
23+
</div>
24+
</div>
25+
{% endblock %}

api/sales_dashboard/urls.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -38,9 +38,9 @@
3838
name="update_max_api_calls",
3939
),
4040
path(
41-
"email-usage/",
42-
views.EmailUsage.as_view(),
43-
name="email-usage",
41+
"usage/",
42+
views.UsageReport.as_view(),
43+
name="usage",
4444
),
4545
path(
4646
"organisations/<int:organisation_id>/download_org_data",

api/sales_dashboard/views.py

Lines changed: 22 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import json
22
from datetime import timedelta
33

4+
import pytz
45
import re2 as re # type: ignore[import-untyped]
56
from django.conf import settings
67
from django.contrib.admin.views.decorators import staff_member_required
@@ -15,19 +16,21 @@
1516
)
1617
from django.shortcuts import get_object_or_404
1718
from django.template import loader
18-
from django.urls import reverse, reverse_lazy
19+
from django.urls import reverse
1920
from django.utils import timezone
2021
from django.utils.decorators import method_decorator
2122
from django.utils.safestring import mark_safe
22-
from django.views.generic import ListView
23-
from django.views.generic.edit import FormView
23+
from django.views.generic import ListView, TemplateView
2424

2525
from app_analytics.influxdb_wrapper import (
2626
get_event_list_for_organisation,
2727
get_events_for_organisation,
2828
)
29+
from core.helpers import get_current_site_url
2930
from environments.dynamodb.migrator import IdentityMigrator
3031
from environments.identities.models import Identity
32+
from environments.models import Environment
33+
from features.models import Feature
3134
from import_export.export import full_export
3235
from organisations.chargebee.tasks import update_chargebee_cache
3336
from organisations.models import (
@@ -39,10 +42,10 @@
3942
update_organisation_subscription_information_cache,
4043
update_organisation_subscription_information_influx_cache,
4144
)
45+
from projects.models import Project
4246
from users.models import FFAdminUser
4347

4448
from .forms import (
45-
EmailUsageForm,
4649
EndTrialForm,
4750
MaxAPICallsForm,
4851
MaxSeatsForm,
@@ -295,18 +298,21 @@ def migrate_identities_to_edge(request, project_id): # type: ignore[no-untyped-
295298
name="get",
296299
decorator=staff_member_required(),
297300
)
298-
@method_decorator(
299-
name="post",
300-
decorator=staff_member_required(),
301-
)
302-
class EmailUsage(FormView): # type: ignore[type-arg]
303-
form_class = EmailUsageForm
304-
template_name = "sales_dashboard/email-usage.html"
305-
success_url = reverse_lazy("sales_dashboard:index")
306-
307-
def form_valid(self, form): # type: ignore[no-untyped-def]
308-
form.save()
309-
return super().form_valid(form)
301+
class UsageReport(TemplateView): # pragma: no cover
302+
template_name = "sales_dashboard/usage.html"
303+
304+
def get_context_data(self, **kwargs): # type: ignore[no-untyped-def]
305+
context = super().get_context_data(**kwargs)
306+
context["stats"] = {
307+
"site_url": get_current_site_url(),
308+
"total_organisations": Organisation.objects.all().count(),
309+
"total_projects": Project.objects.all().count(),
310+
"total_environments": Environment.objects.all().count(),
311+
"total_features": Feature.objects.all().count(),
312+
"total_seats": FFAdminUser.objects.all().count(),
313+
"report_date": timezone.now().astimezone(pytz.UTC),
314+
}
315+
return context
310316

311317

312318
@staff_member_required() # type: ignore[misc]

api/tests/unit/sales_dashboard/test_unit_sales_dashboard_views.py

Lines changed: 2 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -219,32 +219,14 @@ def test_get_email_usage_fails_if_not_staff(
219219
user = FFAdminUser.objects.create(email="notastaffuser@example.com")
220220
client.force_login(user)
221221

222-
url = reverse("sales_dashboard:email-usage")
222+
url = reverse("sales_dashboard:usage")
223223

224224
# When
225225
response = client.get(url)
226226

227227
# Then
228228
assert response.status_code == 302
229-
assert response.url == "/admin/login/?next=/sales-dashboard/email-usage/" # type: ignore[attr-defined]
230-
231-
232-
def test_post_email_usage_fails_if_not_staff(
233-
organisation: Organisation,
234-
client: Client,
235-
) -> None:
236-
# Given
237-
user = FFAdminUser.objects.create(email="notastaffuser@example.com")
238-
client.force_login(user)
239-
240-
url = reverse("sales_dashboard:email-usage")
241-
242-
# When
243-
response = client.post(url)
244-
245-
# Then
246-
assert response.status_code == 302
247-
assert response.url == "/admin/login/?next=/sales-dashboard/email-usage/" # type: ignore[attr-defined]
229+
assert response.url == "/admin/login/?next=/sales-dashboard/usage/" # type: ignore[attr-defined]
248230

249231

250232
def test_start_trial(
72.2 KB
Loading

0 commit comments

Comments
 (0)