Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fancy graph tests #591

Merged
merged 51 commits into from
Dec 12, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
51 commits
Select commit Hold shift + click to select a range
a69d84e
Added tests around DatapointView.get_datapoint
Theophile-Madet Dec 6, 2024
579d8ca
Added tests around DatapointView.get
Theophile-Madet Dec 6, 2024
c673abc
Added a shift_exemption_service.py and an annotation function to shif…
Theophile-Madet Dec 6, 2024
91d5cad
Removed get_shift_user_datas_of_working_members_annotated_with_attend…
Theophile-Madet Dec 6, 2024
36eb6fa
Added tests for ShiftExemptionService
Theophile-Madet Dec 6, 2024
ab15739
Added tests for ShiftExpectationService.is_member_expected_to_do_shifts
Theophile-Madet Dec 6, 2024
6d3c159
Added tests for ShiftExemptionService.get_credit_requirement_for_cycle
Theophile-Madet Dec 7, 2024
1741f0f
Added type hints for factories.
Theophile-Madet Dec 7, 2024
2bedbad
Fixed usages for transfer_attributes
Theophile-Madet Dec 7, 2024
21a09ed
Added tests for ShiftExpectationService.annotate_shift_user_data_quer…
Theophile-Madet Dec 7, 2024
2eac89b
Improved test_annotateShiftUserDataQuerysetWithIsFrozenAtDatetimeAfte…
Theophile-Madet Dec 7, 2024
f755427
Added tests for NumberOfAbcdMembersAtDateView
Theophile-Madet Dec 7, 2024
5f2f877
Added tests for NumberOfActiveMembersAtDateView
Theophile-Madet Dec 7, 2024
5612852
Translation file and fix for test_annotateQuerysetWithHasAbcdAttendan…
Theophile-Madet Dec 7, 2024
48291ea
Moved ShareOwner.can_shop to it's own service.
Theophile-Madet Dec 7, 2024
c19bc43
Fixed date range picked having to "date from" fields
Theophile-Madet Dec 10, 2024
6ca741b
Added options to the fancy graph:
Theophile-Madet Dec 10, 2024
77ca71f
Added a distinct() to most stat views.
Theophile-Madet Dec 10, 2024
e658726
Added description for data set long-term frozen members.
Theophile-Madet Dec 10, 2024
32e3206
Removed all references to the UpdateShiftUserDataLogEntry of the "old…
Theophile-Madet Dec 10, 2024
125155a
Added tests for MemberCanShopService
Theophile-Madet Dec 11, 2024
3b227a4
Added tests for ShiftCanShopService
Theophile-Madet Dec 11, 2024
b9a69dd
Added tests for NumberOfCoPurchasersAtDateView
Theophile-Madet Dec 11, 2024
7e00a69
Added tests for CoPurchaserHistoryService
Theophile-Madet Dec 11, 2024
9bf123c
Added tests for NumberOfCreatedResignationsInSameMonthView
Theophile-Madet Dec 11, 2024
e998e07
Added tests for NumberOfExemptedMembersAtDateView
Theophile-Madet Dec 11, 2024
6c28d50
Added tests for NumberOfFlyingMembersAtDateView
Theophile-Madet Dec 11, 2024
fa6f701
Translation file update, test for annotate_share_owner_queryset_with_…
Theophile-Madet Dec 11, 2024
b240836
Added tests for NumberOfFrozenMembersAtDateView
Theophile-Madet Dec 11, 2024
9c2976c
Translation file update
Theophile-Madet Dec 11, 2024
896ba37
Added tests for NumberOfInvestingMembersAtDateView
Theophile-Madet Dec 11, 2024
98b7f92
Added tests for NumberOfLongTermFrozenMembersAtDateView
Theophile-Madet Dec 11, 2024
a3d2ed1
Added tests for NumberOfMembersAtDateView
Theophile-Madet Dec 11, 2024
059cf58
Added tests for NumberOfPausedMembersAtDateView
Theophile-Madet Dec 11, 2024
4301b92
Removed old untested stats views.
Theophile-Madet Dec 11, 2024
7a41b95
Removed old untested stats views.
Theophile-Madet Dec 11, 2024
d6f1c5a
Added tests for NumberOfPendingResignationsAtDateView
Theophile-Madet Dec 11, 2024
9554d38
Added tests for NumberOfPurchasingMembersAtDateView
Theophile-Madet Dec 11, 2024
7daa2e1
Added tests for NumberOfShiftPartnersAtDateView
Theophile-Madet Dec 11, 2024
1204430
WIP ShiftPartnerHistoryService
Theophile-Madet Dec 11, 2024
653b432
Finished tests for ShiftPartnerHistoryService
Theophile-Madet Dec 12, 2024
cea7754
Added tests for NumberOfWorkingMembersAtDateView
Theophile-Madet Dec 12, 2024
fcb84c8
Removed unused function
Theophile-Madet Dec 12, 2024
4c77d88
Used test functions create_member_that_is_working and create_member_t…
Theophile-Madet Dec 12, 2024
2b4d998
Translation file update.
Theophile-Madet Dec 12, 2024
45be082
Merge branch 'master' into fancy_graph_tests
Theophile-Madet Dec 12, 2024
a2e1a98
Translation file update post-merge.
Theophile-Madet Dec 12, 2024
7e172bc
Fixed delete_transferred_share_ownerships
Theophile-Madet Dec 12, 2024
155aa31
Fixed test_deleteTransferredShareOwnerships_default_deletesAllOwnersh…
Theophile-Madet Dec 12, 2024
827fa6f
Ignore start date in delete_transferred_share_ownerships
Theophile-Madet Dec 12, 2024
363d1c3
Clear FancyGraphCache when resetting test data
Theophile-Madet Dec 12, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 3 additions & 22 deletions src/statistics/FancyGraphCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -68,27 +68,6 @@ const FancyGraphCard: React.FC = () => {
setDateFrom(getFirstOfMonth(dateFromOnPageLoad));
}, []);

useEffect(() => {
if (!dateFrom || !dateTo) return;

let currentDate = new Date(dateFrom);
const dates = [];
while (currentDate <= dateTo) {
dates.push(currentDate);
currentDate = new Date(currentDate);
currentDate.setDate(currentDate.getDate() + 32);
currentDate.setDate(1);
}
dates.push(currentDate);

const tomorrow = new Date();
tomorrow.setDate(tomorrow.getDate() + 1);
dates.push(tomorrow);

setDates(dates);
setGraphLabels(dates.map((date) => formatDate(date)));
}, [dateFrom, dateTo]);

useEffect(() => {
fillCachedData();
buildAndSetGraphData();
Expand Down Expand Up @@ -253,12 +232,14 @@ const FancyGraphCard: React.FC = () => {
<h5>
{gettext("Graph")} {fetching && <Spinner size={"sm"} />}
</h5>
<span className={"d-flex gap-2"}>
<span className={"d-flex gap-2 align-items-center"}>
<DateRangePicker
dateFrom={dateFrom}
setDateFrom={setDateFrom}
dateTo={dateTo}
setDateTo={setDateTo}
setDates={setDates}
setGraphLabels={setGraphLabels}
/>
<TapirButton
variant={"outline-secondary"}
Expand Down
103 changes: 88 additions & 15 deletions src/statistics/components/DateRangePicker.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import React from "react";
import React, { useEffect, useState } from "react";
import { FloatingLabel, Form } from "react-bootstrap";
import { getFirstOfMonth } from "../utils.tsx";
import { getFirstOfMonth, getLastOfMonth } from "../utils.tsx";
import { formatDate } from "../../utils/formatDate.ts";

declare let gettext: (english_text: string) => string;

Expand All @@ -9,42 +10,114 @@ interface DateRangePickerProps {
setDateFrom: (date: Date) => void;
dateTo: Date;
setDateTo: (date: Date) => void;
setDates: (dates: Date[]) => void;
setGraphLabels: (graphLabels: string[]) => void;
}

const DateRangePicker: React.FC<DateRangePickerProps> = ({
dateFrom,
setDateFrom,
dateTo,
setDateTo,
setDates,
setGraphLabels,
}) => {
const [includeToday, setIncludeToday] = useState(true);
const [startOfMonth, setStartOfMonth] = useState(true);

useEffect(() => {
if (!dateFrom || !dateTo) return;

let currentDate = new Date(dateFrom);
const dates = [];
while (currentDate <= dateTo) {
dates.push(currentDate);
currentDate = new Date(currentDate);
currentDate.setDate(currentDate.getDate() + (startOfMonth ? 32 : 1));
currentDate.setDate(1);
currentDate = adaptDate(currentDate);
}
dates.push(currentDate);

if (includeToday) {
const today = new Date();
let todayAlreadyInArray = false;
for (const date of dates) {
if (
date.getDate() === today.getDate() &&
date.getMonth() === today.getMonth() &&
date.getFullYear() === today.getFullYear()
) {
todayAlreadyInArray = true;
break;
}
}
if (!todayAlreadyInArray) {
dates.push(today);
}
}

dates.sort((date1, date2) => date1.getTime() - date2.getTime());

setDates(dates);
setGraphLabels(dates.map((date) => formatDate(date)));
}, [dateFrom, dateTo, includeToday, startOfMonth]);

function adaptDate(date: Date) {
let dateAdapter = getFirstOfMonth;
if (!startOfMonth) {
dateAdapter = getLastOfMonth;
}
return dateAdapter(date);
}

function getDateInputValue(date: Date) {
if (isNaN(date.getTime())) {
return undefined;
}
return date.toISOString().substring(0, 10);
}

return (
<>
<Form.Group>
<Form.Check
type={"switch"}
checked={includeToday}
onChange={(e) => {
setIncludeToday(e.target.checked);
}}
label={"Include today"}
/>
</Form.Group>
<Form.Group>
<Form.Select
onChange={(event) => {
setStartOfMonth(event.target.value === "startOfMonth");
}}
>
<option value={"startOfMonth"}>{gettext("First of month")}</option>
<option value={"lastOfMonth"}>{gettext("Last of month")}</option>
</Form.Select>
</Form.Group>
<Form.Group>
<FloatingLabel label={"Date from"}>
<Form.Control
type={"date"}
value={
!isNaN(dateFrom.getTime())
? dateFrom.toISOString().substring(0, 10)
: undefined
}
value={getDateInputValue(dateFrom)}
onChange={(event) => {
setDateFrom(getFirstOfMonth(new Date(event.target.value)));
setDateFrom(adaptDate(new Date(event.target.value)));
}}
/>
</FloatingLabel>
</Form.Group>
<Form.Group>
<FloatingLabel label={"Date from"}>
<FloatingLabel label={"Date to"}>
<Form.Control
type={"date"}
value={
!isNaN(dateTo.getTime())
? dateTo.toISOString().substring(0, 10)
: undefined
}
value={getDateInputValue(dateTo)}
onChange={(event) => {
setDateTo(getFirstOfMonth(new Date(event.target.value)));
setDateTo(adaptDate(new Date(event.target.value)));
}}
/>
</FloatingLabel>
Expand Down
3 changes: 3 additions & 0 deletions src/statistics/datasets.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,9 @@ export const datasets: { [key: string]: Dataset } = {
},
[datasetNumberOfLongTermFrozenMembers]: {
display_name: gettext("Long-term frozen members"),
description: gettext(
"Members that are frozen since more than 180 days (roughly 6 month)",
),
apiCall: api.statisticsNumberOfLongTermFrozenMembersAtDateRetrieve,
chart_type: "line",
relative: false,
Expand Down
4 changes: 4 additions & 0 deletions src/statistics/utils.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,7 @@ export function getFirstOfMonth(date: Date) {
date.setDate(1);
return date;
}

export function getLastOfMonth(date: Date) {
return new Date(date.getFullYear(), date.getMonth() + 1, 0);
}
51 changes: 24 additions & 27 deletions tapir/accounts/services/co_purchaser_history_service.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,17 @@

import datetime

from django.db.models import Value, OuterRef, Case, When, QuerySet, Q
from django.db.models import (
Value,
OuterRef,
Case,
When,
QuerySet,
Q,
CharField,
Subquery,
)
from django.db.models.functions import Coalesce
from django.utils import timezone

from tapir.accounts.models import TapirUser, UpdateTapirUserLogEntry
Expand All @@ -12,26 +22,6 @@ class CoPurchaserHistoryService:
ANNOTATION_HAS_CO_PURCHASER = "has_co_purchaser"
ANNOTATION_HAS_CO_PURCHASER_DATE_CHECK = "has_co_purchaser_date_check"

@classmethod
def has_co_purchaser(
cls, tapir_user: TapirUser, at_datetime: datetime.datetime = None
):
if at_datetime is None:
at_datetime = timezone.now()

if not hasattr(tapir_user, cls.ANNOTATION_HAS_CO_PURCHASER):
tapir_user = cls.annotate_tapir_user_queryset_with_has_co_purchaser_at_date(
TapirUser.objects.filter(id=tapir_user.id), at_datetime
).first()

annotated_date = getattr(tapir_user, cls.ANNOTATION_HAS_CO_PURCHASER_DATE_CHECK)
if annotated_date != at_datetime:
raise ValueError(
f"Trying to get 'has co purchaser' at date {at_datetime}, but the queryset has been "
f"annotated relative to {annotated_date}"
)
return getattr(tapir_user, cls.ANNOTATION_HAS_CO_PURCHASER)

@classmethod
def annotate_tapir_user_queryset_with_has_co_purchaser_at_date(
cls,
Expand All @@ -42,13 +32,20 @@ def annotate_tapir_user_queryset_with_has_co_purchaser_at_date(
at_datetime = timezone.now()

queryset = queryset.annotate(
co_purchaser_at_date=UpdateTapirUserLogEntry.objects.filter(
user_id=OuterRef("id"),
created_date__lte=at_datetime,
new_values__co_purchaser__isnull=False,
co_purchaser_from_log_entry=Subquery(
UpdateTapirUserLogEntry.objects.filter(
user_id=OuterRef("id"),
created_date__gte=at_datetime,
old_values__has_key="co_purchaser",
)
.order_by("created_date")
.values("old_values__co_purchaser")[:1],
output_field=CharField(),
)
.order_by("-created_date")
.values("new_values__co_purchaser")[:1]
)

queryset = queryset.annotate(
co_purchaser_at_date=Coalesce("co_purchaser_from_log_entry", "co_purchaser")
)

return queryset.annotate(
Expand Down
2 changes: 1 addition & 1 deletion tapir/accounts/tests/factories/factories.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
from tapir.utils.shortcuts import set_group_membership


class TapirUserFactory(UserDataFactory):
class TapirUserFactory(UserDataFactory[TapirUser]):
class Meta:
model = TapirUser
skip_postgeneration_save = True
Expand Down
6 changes: 5 additions & 1 deletion tapir/accounts/tests/factories/user_data_factory.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
from typing import TypeVar

import factory
import phonenumbers
from faker import Faker
Expand Down Expand Up @@ -27,8 +29,10 @@ def phone_number(self):
fake = Faker()
fake.add_provider(CustomPhoneProvider)

T = TypeVar("T")


class UserDataFactory(factory.django.DjangoModelFactory):
class UserDataFactory(factory.django.DjangoModelFactory[T]):
class Meta:
abstract = True
exclude = ("ATTRIBUTES",)
Expand Down
Loading
Loading