Skip to content

Commit

Permalink
added stats page and ENABLE_STATS_PAGE/STATS_MAX_DAYS config options
Browse files Browse the repository at this point in the history
  • Loading branch information
jabelone committed Jun 7, 2024
1 parent 631a7e6 commit 6b0616c
Show file tree
Hide file tree
Showing 14 changed files with 393 additions and 14 deletions.
1 change: 1 addition & 0 deletions memberportal/api_general/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ def get(self, request):
"senderId": config.SMS_SENDER_ID,
"footer": config.SMS_FOOTER,
},
"enableStatsPage": config.ENABLE_STATS_PAGE,
}

keys = {"stripePublishableKey": config.STRIPE_PUBLISHABLE_KEY}
Expand Down
27 changes: 22 additions & 5 deletions memberportal/api_metrics/metrics.py
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,10 @@ def calculate_member_count():
profile_states.append({"state": state["state"], "total": state["count"]})

Metric.objects.create(
name=Metric.MetricName.MEMBER_COUNT_TOTAL, data=profile_states
name=Metric.MetricName.MEMBER_COUNT_TOTAL,
data=(
profile_states if len(profile_states) else [{"state": "active", "total": 0}]
),
).full_clean()


Expand All @@ -74,7 +77,10 @@ def calculate_member_count_6_months():
profile_states.append({"state": state["state"], "total": state["count"]})

Metric.objects.create(
name=Metric.MetricName.MEMBER_COUNT_6_MONTHS, data=profile_states
name=Metric.MetricName.MEMBER_COUNT_6_MONTHS,
data=(
profile_states if len(profile_states) else [{"state": "active", "total": 0}]
),
).full_clean()


Expand All @@ -91,7 +97,10 @@ def calculate_member_count_12_months():
profile_states.append({"state": state["state"], "total": state["count"]})

Metric.objects.create(
name=Metric.MetricName.MEMBER_COUNT_12_MONTHS, data=profile_states
name=Metric.MetricName.MEMBER_COUNT_12_MONTHS,
data=(
profile_states if len(profile_states) else [{"state": "active", "total": 0}]
),
).full_clean()


Expand All @@ -109,7 +118,11 @@ def calculate_subscription_count():
)
Metric.objects.create(
name=Metric.MetricName.SUBSCRIPTION_COUNT_TOTAL,
data=subscription_states_data,
data=(
subscription_states_data
if len(subscription_states_data)
else [{"state": "inactive", "total": 0}]
),
).full_clean()


Expand Down Expand Up @@ -139,5 +152,9 @@ def calculate_memberbucks_transactions():
)
Metric.objects.create(
name=Metric.MetricName.MEMBERBUCKS_TRANSACTIONS_TOTAL,
data=transaction_data,
data=(
transaction_data
if len(transaction_data)
else [{"type": "stripe", "total": 0.0}]
),
).full_clean()
44 changes: 40 additions & 4 deletions memberportal/api_metrics/views.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
from django.db.models import Max
from django.utils import timezone

import api_metrics.metrics
from api_metrics.models import Metric
from api_general.models import SiteSession

from constance import config
from rest_framework.response import Response
from rest_framework.views import APIView
from rest_framework import permissions
Expand All @@ -16,13 +20,45 @@ class Statistics(APIView):
"""

def get(self, request):
statistics = {}

# On site members
on_site = {"members": [], "count": 0}
members = SiteSession.objects.filter(signout_date=None).order_by("-signin_date")
member_list = []
on_site["count"] = members.count()

for member in members:
member_list.append(member.user.profile.get_full_name())

statistics = {"onSite": {"members": member_list, "count": members.count()}}
on_site["members"].append(member.user.profile.get_full_name())

statistics["on_site"] = on_site

for metric_name in Metric.MetricName.values:
# Don't return any data from the API if the stats page isn't enabled
if config.ENABLE_STATS_PAGE or request.user.is_admin:
metric_data = []
one_year_before_today = timezone.now() - timezone.timedelta(
days=config.STATS_MAX_DAYS
)
last_stat_per_day = (
Metric.objects.filter(
name=metric_name, creation_date__gte=one_year_before_today
)
.extra(select={"the_date": "date(creation_date)"})
.values_list("the_date")
.order_by("-the_date")
.annotate(max_date=Max("creation_date"))
)
max_dates = [item[1] for item in last_stat_per_day]
metrics = Metric.objects.filter(
name=metric_name, creation_date__in=max_dates
).order_by("creation_date")
for metric in metrics:
metric_data.append(
{"date": metric.creation_date, "data": metric.data}
)
statistics[metric_name] = metric_data
else:
statistics[metric_name] = []

return Response(statistics)

Expand Down
9 changes: 9 additions & 0 deletions memberportal/membermatters/constance_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -334,6 +334,14 @@
3600,
"The interval in seconds to calculate and store application level metrics data like member count and door swipes.",
),
"ENABLE_STATS_PAGE": (
True,
"Enable the stats page that shows member counts and other metrics.",
),
"STATS_MAX_DAYS": (
365,
"The maximum number of days to show on the stats page.",
),
}

CONSTANCE_CONFIG_FIELDSETS = OrderedDict(
Expand Down Expand Up @@ -366,6 +374,7 @@
"ENABLE_DOOR_BUMP_API",
),
),
("Stats Settings", ("ENABLE_STATS_PAGE", "STATS_MAX_DAYS")),
(
"Sentry Error Reporting",
(
Expand Down
113 changes: 113 additions & 0 deletions src-frontend/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions src-frontend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
"@stripe/stripe-js": "^1.29.0",
"address": "^1.1.2",
"animate.css": "^4.1.1",
"apexcharts": "^3.49.1",
"axios": "^0.26.1",
"core-js": "^3.22.2",
"crypto-js": "^4.1.1",
Expand All @@ -56,6 +57,7 @@
"vue-i18n": "^9.2.0-beta.35",
"vue-router": "^4.0.14",
"vue2-transitions": "^0.3.0",
"vue3-apexcharts": "^1.5.3",
"vuex": "^4.0.2"
},
"devDependencies": {
Expand Down
2 changes: 1 addition & 1 deletion src-frontend/quasar.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ module.exports = configure(async function (ctx) {
// app boot file (/src/boot)
// --> boot files are part of "main.js"
// https://v2.quasar.dev/quasar-cli-vite/boot-files
boot: ['sentry', 'i18n', 'axios', 'routeGuards', 'capacitor'],
boot: ['sentry', 'i18n', 'axios', 'routeGuards', 'capacitor', 'apexcharts'],

// https://v2.quasar.dev/quasar-cli-vite/quasar-config-js#css
css: ['app.scss'],
Expand Down
6 changes: 6 additions & 0 deletions src-frontend/src/boot/apexcharts.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import { boot } from 'quasar/wrappers';
import VueApexCharts from 'vue3-apexcharts';

export default boot(({ app }) => {
app.use(VueApexCharts);
});
8 changes: 4 additions & 4 deletions src-frontend/src/components/MembersOnsiteCard.vue
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
<q-item-section>
<q-item-label>{{ $t('statistics.memberCount') }}</q-item-label>
<q-item-label caption>
{{ onsiteCount }} {{ $t('statistics.onSite') }}
{{ onsiteCount }} {{ $t('statistics.on_site') }}
</q-item-label>
</q-item-section>
</q-item>
Expand Down Expand Up @@ -63,11 +63,11 @@ export default {
return icons;
},
onsiteCount() {
return this.statistics?.onSite ? this.statistics?.onSite?.count : 0;
return this.statistics?.on_site ? this.statistics?.on_site?.count : 0;
},
onsiteMembers() {
return this.statistics?.onSite?.members
? this.statistics?.onSite?.members
return this.statistics?.on_site?.members
? this.statistics?.on_site?.members
: [];
},
},
Expand Down
Loading

0 comments on commit 6b0616c

Please sign in to comment.