Skip to content

Commit 9d9905b

Browse files
committed
(PC-32268)[API] feat: Return individual or collective statistics only if offerer is concerned
1 parent 8e347d1 commit 9d9905b

File tree

5 files changed

+135
-13
lines changed

5 files changed

+135
-13
lines changed

api/src/pcapi/connectors/clickhouse/queries/yearly_revenue.py

Lines changed: 36 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,25 @@ class Config:
1616
extra = "forbid"
1717

1818

19+
class IndividualOnlyRevenue(pydantic_v1.BaseModel):
20+
total: Decimal
21+
individual: Decimal
22+
23+
class Config:
24+
extra = "forbid"
25+
26+
27+
class CollectiveOnlyRevenue(pydantic_v1.BaseModel):
28+
total: Decimal
29+
collective: Decimal
30+
31+
class Config:
32+
extra = "forbid"
33+
34+
1935
class AggregatedRevenue(pydantic_v1.BaseModel):
20-
revenue: Revenue
21-
expected_revenue: Revenue
36+
revenue: Revenue | IndividualOnlyRevenue | CollectiveOnlyRevenue
37+
expected_revenue: Revenue | IndividualOnlyRevenue | CollectiveOnlyRevenue
2238

2339
class Config:
2440
extra = "forbid"
@@ -34,16 +50,25 @@ class Config:
3450

3551

3652
class YearlyAggregatedRevenueQuery(BaseQuery[YearlyAggregatedRevenueModel]):
53+
def __init__(self, venues_have_individual: bool, venues_have_collective: bool) -> None:
54+
self.venues_have_individual = venues_have_individual
55+
self.venues_have_collective = venues_have_collective
56+
super().__init__()
57+
3758
def _format_result(self, results: list) -> dict:
38-
return {
39-
"incomeByYear": {
40-
result.year: {
41-
"revenue": json.loads(result.revenue),
42-
"expectedRevenue": json.loads(result.expected_revenue),
43-
}
44-
for result in results
45-
}
46-
}
59+
income_by_year = {}
60+
for result in results:
61+
revenue = json.loads(result.revenue)
62+
expected_revenue = json.loads(result.expected_revenue)
63+
if not self.venues_have_individual:
64+
revenue.pop("individual", None)
65+
expected_revenue.pop("individual", None)
66+
if not self.venues_have_collective:
67+
revenue.pop("collective", None)
68+
expected_revenue.pop("collective", None)
69+
income_by_year.update({result.year: {"revenue": revenue, "expectedRevenue": expected_revenue}})
70+
71+
return {"incomeByYear": income_by_year}
4772

4873
@property
4974
def raw_query(self) -> str:

api/src/pcapi/core/offers/repository.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1326,3 +1326,14 @@ def merge_products(to_keep: models.Product, to_delete: models.Product) -> models
13261326
db.session.delete(to_delete)
13271327

13281328
return to_keep
1329+
1330+
1331+
def venues_have_individual_and_collective_offers(venue_ids: list[int]) -> tuple[bool, bool]:
1332+
return (
1333+
db.session.query(offers_model.Offer.query.filter(offers_model.Offer.venueId.in_(venue_ids)).exists()).scalar(),
1334+
db.session.query(
1335+
educational_models.CollectiveOffer.query.filter(
1336+
educational_models.CollectiveOffer.venueId.in_(venue_ids)
1337+
).exists()
1338+
).scalar(),
1339+
)

api/src/pcapi/routes/pro/statistics.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
from flask_login import login_required
33

44
from pcapi.connectors.clickhouse import queries as clickhouse_queries
5+
from pcapi.core.offers.repository import venues_have_individual_and_collective_offers
56
from pcapi.models.api_errors import ApiErrors
67
from pcapi.routes.apis import private_api
78
from pcapi.routes.serialization.statistics_serialize import StatisticsModel
@@ -26,5 +27,8 @@ def get_statistics(query: StatisticsQueryModel) -> StatisticsModel:
2627
status_code=422,
2728
)
2829
check_user_has_access_to_venues(current_user, venue_ids)
29-
result = clickhouse_queries.YearlyAggregatedRevenueQuery().execute(tuple(venue_ids))
30+
venues_have_individual, venues_have_collective = venues_have_individual_and_collective_offers(venue_ids)
31+
result = clickhouse_queries.YearlyAggregatedRevenueQuery(venues_have_individual, venues_have_collective).execute(
32+
tuple(venue_ids)
33+
)
3034
return StatisticsModel.from_query(income_by_year=result.income_by_year)

api/tests/connectors/clickhouse/clickhouse_test.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ def test_get_yearly_revenue(self):
1414

1515
with mock.patch("pcapi.connectors.clickhouse.testing_backend.TestingBackend.run_query") as mock_run_query:
1616
mock_run_query.return_value = fixtures.YEARLY_AGGREGATED_VENUE_REVENUE
17-
result = clickhouse_queries.YearlyAggregatedRevenueQuery().execute(venue_ids)
17+
result = clickhouse_queries.YearlyAggregatedRevenueQuery(True, True).execute(venue_ids)
1818

1919
assert result.income_by_year["2024"].revenue.total == Decimal("24.24")
2020
assert result.income_by_year["2024"].revenue.individual == Decimal("12.12")

api/tests/routes/pro/get_statistics_test.py

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,9 @@
33
import pytest
44

55
from pcapi.core import testing
6+
import pcapi.core.educational.factories as educational_factories
67
import pcapi.core.offerers.factories as offerers_factories
8+
import pcapi.core.offers.factories as offers_factories
79
import pcapi.core.users.factories as users_factories
810

911
from tests.connectors.clickhouse import fixtures
@@ -18,10 +20,14 @@ def test_get_statistics_from_one_venue(self, run_query, client):
1820
offerers_factories.UserOffererFactory(user=user, offerer=offerer)
1921
venue = offerers_factories.VenueFactory(managingOfferer=offerer)
2022
venue_id = venue.id
23+
educational_factories.CollectiveOfferFactory(venue=venue)
24+
offers_factories.OfferFactory(venue=venue)
2125

2226
test_client = client.with_session_auth(email=user.email)
2327
num_queries = testing.AUTHENTICATION_QUERIES
2428
num_queries += 1 # select Offerer
29+
num_queries += 1 # select Offer
30+
num_queries += 1 # select CollectiveOffer
2531
with testing.assert_num_queries(num_queries):
2632
run_query.return_value = fixtures.YEARLY_AGGREGATED_VENUE_REVENUE
2733
response = test_client.get(f"/get-statistics/?venue_ids={venue_id}")
@@ -44,10 +50,14 @@ def test_get_statistics_from_multiple_venues(self, run_query, client):
4450
venue2 = offerers_factories.VenueFactory(managingOfferer=offerer)
4551
venue_id = venue.id
4652
venue2_id = venue2.id
53+
educational_factories.CollectiveOfferFactory(venue=venue)
54+
offers_factories.OfferFactory(venue=venue)
4755

4856
test_client = client.with_session_auth(email=user.email)
4957
num_queries = testing.AUTHENTICATION_QUERIES
5058
num_queries += 1 # select Offerer
59+
num_queries += 1 # select Offer
60+
num_queries += 1 # select CollectiveOffer
5161
with testing.assert_num_queries(num_queries):
5262
run_query.return_value = fixtures.YEARLY_AGGREGATED_VENUE_REVENUE
5363
response = test_client.get(f"/get-statistics/?venue_ids={venue_id}&venue_ids={venue2_id}")
@@ -70,10 +80,14 @@ def test_get_statistics_multiple_years(self, run_query, client):
7080
venue2 = offerers_factories.VenueFactory(managingOfferer=offerer)
7181
venue_id = venue.id
7282
venue2_id = venue2.id
83+
educational_factories.CollectiveOfferFactory(venue=venue)
84+
offers_factories.OfferFactory(venue=venue)
7385

7486
test_client = client.with_session_auth(email=user.email)
7587
num_queries = testing.AUTHENTICATION_QUERIES
7688
num_queries += 1 # select Offerer
89+
num_queries += 1 # select Offer
90+
num_queries += 1 # select CollectiveOffer
7791
with testing.assert_num_queries(num_queries):
7892
run_query.return_value = fixtures.YEARLY_AGGREGATED_VENUE_REVENUE_MULTIPLE_YEARS
7993
response = test_client.get(f"/get-statistics/?venue_ids={venue_id}&venue_ids={venue2_id}")
@@ -92,16 +106,84 @@ def test_get_statistics_multiple_years(self, run_query, client):
92106
}
93107
}
94108

109+
@patch("pcapi.connectors.clickhouse.testing_backend.TestingBackend.run_query")
110+
def test_get_statistics_only_collective(self, run_query, client):
111+
user = users_factories.UserFactory()
112+
offerer = offerers_factories.OffererFactory()
113+
offerers_factories.UserOffererFactory(user=user, offerer=offerer)
114+
venue = offerers_factories.VenueFactory(managingOfferer=offerer)
115+
venue_id = venue.id
116+
educational_factories.CollectiveOfferFactory(venue=venue)
117+
118+
test_client = client.with_session_auth(email=user.email)
119+
num_queries = testing.AUTHENTICATION_QUERIES
120+
num_queries += 1 # select Offerer
121+
num_queries += 1 # select Offer
122+
num_queries += 1 # select CollectiveOffer
123+
with testing.assert_num_queries(num_queries):
124+
run_query.return_value = fixtures.YEARLY_AGGREGATED_VENUE_REVENUE_MULTIPLE_YEARS
125+
response = test_client.get(f"/get-statistics/?venue_ids={venue_id}")
126+
assert response.status_code == 200
127+
assert response.json == {
128+
"incomeByYear": {
129+
"2022": {
130+
"expectedRevenue": {"collective": 22.12, "total": 44.24},
131+
"revenue": {"collective": 22.12, "total": 44.24},
132+
},
133+
"2023": {},
134+
"2024": {
135+
"expectedRevenue": {"collective": 13.12, "total": 26.24},
136+
"revenue": {"collective": 12.12, "total": 24.24},
137+
},
138+
}
139+
}
140+
141+
@patch("pcapi.connectors.clickhouse.testing_backend.TestingBackend.run_query")
142+
def test_get_statistics_only_individual(self, run_query, client):
143+
user = users_factories.UserFactory()
144+
offerer = offerers_factories.OffererFactory()
145+
offerers_factories.UserOffererFactory(user=user, offerer=offerer)
146+
venue = offerers_factories.VenueFactory(managingOfferer=offerer)
147+
venue_id = venue.id
148+
offers_factories.OfferFactory(venue=venue)
149+
150+
test_client = client.with_session_auth(email=user.email)
151+
num_queries = testing.AUTHENTICATION_QUERIES
152+
num_queries += 1 # select Offerer
153+
num_queries += 1 # select Offer
154+
num_queries += 1 # select CollectiveOffer
155+
with testing.assert_num_queries(num_queries):
156+
run_query.return_value = fixtures.YEARLY_AGGREGATED_VENUE_REVENUE_MULTIPLE_YEARS
157+
response = test_client.get(f"/get-statistics/?venue_ids={venue_id}")
158+
assert response.status_code == 200
159+
assert response.json == {
160+
"incomeByYear": {
161+
"2022": {
162+
"expectedRevenue": {"individual": 22.12, "total": 44.24},
163+
"revenue": {"individual": 22.12, "total": 44.24},
164+
},
165+
"2023": {},
166+
"2024": {
167+
"expectedRevenue": {"individual": 13.12, "total": 26.24},
168+
"revenue": {"individual": 12.12, "total": 24.24},
169+
},
170+
}
171+
}
172+
95173
def test_get_statistics_empty_result(self, client):
96174
user = users_factories.UserFactory()
97175
offerer = offerers_factories.OffererFactory()
98176
offerers_factories.UserOffererFactory(user=user, offerer=offerer)
99177
venue = offerers_factories.VenueFactory(managingOfferer=offerer)
100178
venue_id = venue.id
179+
educational_factories.CollectiveOfferFactory(venue=venue)
180+
offers_factories.OfferFactory(venue=venue)
101181

102182
test_client = client.with_session_auth(email=user.email)
103183
num_queries = testing.AUTHENTICATION_QUERIES
104184
num_queries += 1 # select Offerer
185+
num_queries += 1 # select Offer
186+
num_queries += 1 # select CollectiveOffer
105187
with testing.assert_num_queries(num_queries):
106188
response = test_client.get(f"/get-statistics/?venue_ids={venue_id}")
107189
assert response.status_code == 200

0 commit comments

Comments
 (0)