@@ -85,11 +85,11 @@ class ContributionTypesStatsView(APIView):
8585 """
8686 Get time series data showing how many contribution types have been assigned on each date.
8787 """
88-
88+
8989 def get (self , request ):
9090 from django .db .models .functions import TruncDate
9191 from datetime import date , timedelta
92-
92+
9393 # Get contributions grouped by date and contribution type
9494 daily_contributions = (
9595 Contribution .objects
@@ -101,51 +101,119 @@ def get(self, request):
101101 )
102102 .order_by ('date' )
103103 )
104-
104+
105105 # Build cumulative data
106106 data = []
107107 cumulative_types = set ()
108-
108+
109109 # Get all contributions to track cumulative unique types
110110 all_contributions = (
111111 Contribution .objects
112112 .values ('contribution_date' , 'contribution_type' )
113113 .order_by ('contribution_date' )
114114 )
115-
115+
116116 # Group by date and count cumulative types
117117 from collections import defaultdict
118118 contributions_by_date = defaultdict (set )
119-
119+
120120 for contrib in all_contributions :
121121 date_key = contrib ['contribution_date' ].date ()
122122 contributions_by_date [date_key ].add (contrib ['contribution_type' ])
123-
123+
124124 if contributions_by_date :
125125 # Get date range
126126 start_date = min (contributions_by_date .keys ())
127127 end_date = max (contributions_by_date .keys ())
128-
128+
129129 # Extend to today if needed
130130 today = date .today ()
131131 if end_date < today :
132132 end_date = today
133-
133+
134134 # Build continuous time series with cumulative count
135135 current_date = start_date
136-
136+
137137 while current_date <= end_date :
138138 # Add new types for this date
139139 if current_date in contributions_by_date :
140140 cumulative_types .update (contributions_by_date [current_date ])
141-
141+
142142 data .append ({
143143 'date' : current_date .isoformat (),
144144 'count' : len (cumulative_types ),
145145 'new_types' : len (contributions_by_date .get (current_date , set ()))
146146 })
147-
147+
148148 # Move to next day
149149 current_date += timedelta (days = 1 )
150-
150+
151+ return Response ({'data' : data })
152+
153+
154+ class ParticipantsGrowthView (APIView ):
155+ """
156+ Get time series data for validators, waitlist users, and builders growth over time.
157+ """
158+
159+ def get (self , request ):
160+ from django .db .models .functions import TruncDate
161+ from datetime import date , timedelta
162+ from collections import defaultdict
163+ from validators .models import Validator
164+ from builders .models import Builder
165+
166+ # Get validators by creation date
167+ validators_by_date = defaultdict (int )
168+ for v in Validator .objects .all ():
169+ date_key = v .created_at .date ()
170+ validators_by_date [date_key ] += 1
171+
172+ # Get waitlist users by contribution date (users with validator-waitlist contribution)
173+ waitlist_by_date = defaultdict (int )
174+ try :
175+ waitlist_type = ContributionType .objects .get (slug = 'validator-waitlist' )
176+ for c in Contribution .objects .filter (contribution_type = waitlist_type ):
177+ date_key = c .contribution_date .date ()
178+ waitlist_by_date [date_key ] += 1
179+ except ContributionType .DoesNotExist :
180+ pass
181+
182+ # Get builders by creation date
183+ builders_by_date = defaultdict (int )
184+ for b in Builder .objects .all ():
185+ date_key = b .created_at .date ()
186+ builders_by_date [date_key ] += 1
187+
188+ # Find date range across all sources
189+ all_dates = set (validators_by_date .keys ()) | set (waitlist_by_date .keys ()) | set (builders_by_date .keys ())
190+
191+ if not all_dates :
192+ return Response ({'data' : []})
193+
194+ start_date = min (all_dates )
195+ end_date = max (max (all_dates ), date .today ())
196+
197+ # Build cumulative time series
198+ data = []
199+ cum_validators = 0
200+ cum_waitlist = 0
201+ cum_builders = 0
202+
203+ current_date = start_date
204+ while current_date <= end_date :
205+ cum_validators += validators_by_date .get (current_date , 0 )
206+ cum_waitlist += waitlist_by_date .get (current_date , 0 )
207+ cum_builders += builders_by_date .get (current_date , 0 )
208+
209+ data .append ({
210+ 'date' : current_date .isoformat (),
211+ 'validators' : cum_validators ,
212+ 'waitlist' : cum_waitlist ,
213+ 'builders' : cum_builders ,
214+ 'total' : cum_validators + cum_waitlist + cum_builders
215+ })
216+
217+ current_date += timedelta (days = 1 )
218+
151219 return Response ({'data' : data })
0 commit comments