1+ #!/usr/bin/env python
2+ """
3+ Script to create a test challenge for [email protected] to test retention features. 4+ This script creates:
5+ 1. A test user ([email protected] ) 6+ 2. A challenge host team
7+ 3. A test challenge with phases
8+ 4. Some test submissions
9+ """
10+
11+ import os
12+ import sys
13+ import django
14+ from datetime import timedelta
15+
16+ # Add the project root to the Python path
17+ sys .path .append (os .path .dirname (os .path .dirname (os .path .abspath (__file__ ))))
18+
19+ # Set up Django
20+ os .environ .setdefault ('DJANGO_SETTINGS_MODULE' , 'settings.dev' )
21+ django .setup ()
22+
23+ from django .contrib .auth import get_user_model
24+ from django .utils import timezone
25+ from hosts .models import ChallengeHostTeam , ChallengeHost
26+ from challenges .models import Challenge , ChallengePhase
27+ from participants .models import ParticipantTeam , Participant
28+ from jobs .models import Submission
29+
30+ User = get_user_model ()
31+
32+ def create_test_challenge ():
33+ """Create a complete test challenge setup for retention testing."""
34+
35+ print ("🚀 Creating test challenge for retention features..." )
36+
37+ # 1. Get the existing test user
38+ print ("1. Getting existing test user..." )
39+ try :
40+ user = User .objects .get (username = 'host' )
41+ print (f" ✅ Found existing user: { user .username } (ID: { user .id } )" )
42+ except User .DoesNotExist :
43+ print (" ❌ User 'host' not found. Please create the user first." )
44+ return None
45+
46+ # 2. Create challenge host team
47+ print ("2. Creating challenge host team..." )
48+ host_team , created = ChallengeHostTeam .objects .get_or_create (
49+ team_name = 'Test Retention Host Team' ,
50+ created_by = user ,
51+ defaults = {
52+ 'team_url' : 'https://example.com/test-team' ,
53+ }
54+ )
55+ if created :
56+ print (f" ✅ Created host team: { host_team .team_name } (ID: { host_team .id } )" )
57+ else :
58+ print (f" ✅ Found existing host team: { host_team .team_name } (ID: { host_team .id } )" )
59+
60+ # 3. Add user to host team if not already a member
61+ host_member , created = ChallengeHost .objects .get_or_create (
62+ user = user ,
63+ team_name = host_team ,
64+ defaults = {'status' : ChallengeHost .ACCEPTED }
65+ )
66+ if created :
67+ print (f" ✅ Added user to host team" )
68+ else :
69+ print (f" ✅ User already in host team" )
70+
71+ # 4. Create the challenge
72+ print ("3. Creating test challenge..." )
73+ challenge , created = Challenge .objects .get_or_create (
74+ title = 'Test Retention Challenge' ,
75+ creator = host_team ,
76+ defaults = {
77+ 'description' : 'A test challenge for testing retention features including consent management, cleanup, and notifications.' ,
78+ 'start_date' : timezone .now () - timedelta (days = 5 ),
79+ 'end_date' : timezone .now () + timedelta (days = 10 ),
80+ 'approved_by_admin' : True ,
81+ 'is_docker_based' : False ,
82+ 'uses_ec2_worker' : False ,
83+ 'remote_evaluation' : False ,
84+ 'queue' : 'test_retention_queue' ,
85+ 'worker_cpu_cores' : 1024 ,
86+ 'worker_memory' : 2048 ,
87+ 'ephemeral_storage' : 20 ,
88+ 'inform_hosts' : True , # Enable host notifications
89+ }
90+ )
91+ if created :
92+ print (f" ✅ Created challenge: { challenge .title } (ID: { challenge .id } )" )
93+ else :
94+ print (f" ✅ Found existing challenge: { challenge .title } (ID: { challenge .id } )" )
95+
96+ # 5. Create challenge phases
97+ print ("4. Creating challenge phases..." )
98+
99+ # Phase 1: Active phase (still accepting submissions)
100+ phase1 , created = ChallengePhase .objects .get_or_create (
101+ name = 'Active Phase' ,
102+ challenge = challenge ,
103+ defaults = {
104+ 'description' : 'Phase that is still active and accepting submissions' ,
105+ 'start_date' : timezone .now () - timedelta (days = 3 ),
106+ 'end_date' : timezone .now () + timedelta (days = 7 ),
107+ 'is_public' : True ,
108+ 'max_submissions_per_day' : 10 ,
109+ 'max_submissions_per_month' : 100 ,
110+ 'codename' : 'active-phase' ,
111+ }
112+ )
113+ if created :
114+ print (f" ✅ Created active phase: { phase1 .name } (ID: { phase1 .id } )" )
115+ else :
116+ print (f" ✅ Found existing active phase: { phase1 .name } (ID: { phase1 .id } )" )
117+
118+ # Phase 2: Ended phase (for testing retention)
119+ phase2 , created = ChallengePhase .objects .get_or_create (
120+ name = 'Ended Phase' ,
121+ challenge = challenge ,
122+ defaults = {
123+ 'description' : 'Phase that has ended for testing retention cleanup' ,
124+ 'start_date' : timezone .now () - timedelta (days = 20 ),
125+ 'end_date' : timezone .now () - timedelta (days = 10 ),
126+ 'is_public' : False , # Not public anymore
127+ 'max_submissions_per_day' : 10 ,
128+ 'max_submissions_per_month' : 100 ,
129+ 'codename' : 'ended-phase' ,
130+ }
131+ )
132+ if created :
133+ print (f" ✅ Created ended phase: { phase2 .name } (ID: { phase2 .id } )" )
134+ else :
135+ print (f" ✅ Found existing ended phase: { phase2 .name } (ID: { phase2 .id } )" )
136+
137+ # 6. Create participant team and submissions
138+ print ("5. Creating participant team and submissions..." )
139+
140+ # Create participant team
141+ participant_team , created = ParticipantTeam .objects .get_or_create (
142+ team_name = 'Test Participant Team' ,
143+ created_by = user ,
144+ defaults = {
145+ 'team_url' : 'https://example.com/participant-team' ,
146+ }
147+ )
148+ if created :
149+ print (f" ✅ Created participant team: { participant_team .team_name } " )
150+ else :
151+ print (f" ✅ Found existing participant team: { participant_team .team_name } " )
152+
153+ # Add user to participant team
154+ participant_member , created = Participant .objects .get_or_create (
155+ user = user ,
156+ team = participant_team ,
157+ defaults = {'status' : Participant .ACCEPTED }
158+ )
159+ if created :
160+ print (f" ✅ Added user to participant team" )
161+ else :
162+ print (f" ✅ User already in participant team" )
163+
164+ # Create test submissions
165+ print ("6. Creating test submissions..." )
166+
167+ # Submission in active phase (should not be eligible for cleanup)
168+ submission1 , created = Submission .objects .get_or_create (
169+ participant_team = participant_team ,
170+ challenge_phase = phase1 ,
171+ created_by = user ,
172+ defaults = {
173+ 'status' : 'FINISHED' ,
174+ 'is_artifact_deleted' : False ,
175+ }
176+ )
177+ if created :
178+ print (f" ✅ Created submission in active phase: { submission1 .id } " )
179+ else :
180+ print (f" ✅ Found existing submission in active phase: { submission1 .id } " )
181+
182+ # Submission in ended phase (should be eligible for cleanup if consent given)
183+ submission2 , created = Submission .objects .get_or_create (
184+ participant_team = participant_team ,
185+ challenge_phase = phase2 ,
186+ created_by = user ,
187+ defaults = {
188+ 'status' : 'FINISHED' ,
189+ 'is_artifact_deleted' : False ,
190+ }
191+ )
192+ if created :
193+ print (f" ✅ Created submission in ended phase: { submission2 .id } " )
194+ else :
195+ print (f" ✅ Found existing submission in ended phase: { submission2 .id } " )
196+
197+ # 7. Show retention status
198+ print ("\n 7. Retention Status Summary:" )
199+ print ("=" * 50 )
200+
201+ print (f"Challenge: { challenge .title } (ID: { challenge .id } )" )
202+ print (f"Host consent: { '✅ YES' if challenge .retention_policy_consent else '❌ NO' } " )
203+ print (f"Retention policy: { '30-day' if challenge .retention_policy_consent else 'Indefinite' } " )
204+
205+ # Calculate retention dates
206+ from challenges .aws_utils import calculate_submission_retention_date
207+
208+ retention_date1 = calculate_submission_retention_date (phase1 )
209+ retention_date2 = calculate_submission_retention_date (phase2 )
210+
211+ print (f"\n Phase 1 ({ phase1 .name } ):" )
212+ print (f" End date: { phase1 .end_date } " )
213+ print (f" Is public: { phase1 .is_public } " )
214+ print (f" Retention date: { retention_date1 or 'Not applicable' } " )
215+
216+ print (f"\n Phase 2 ({ phase2 .name } ):" )
217+ print (f" End date: { phase2 .end_date } " )
218+ print (f" Is public: { phase2 .is_public } " )
219+ print (f" Retention date: { retention_date2 or 'Indefinite (no consent)' } " )
220+
221+ print (f"\n Submissions:" )
222+ print (f" Active phase: { Submission .objects .filter (challenge_phase = phase1 ).count ()} " )
223+ print (f" Ended phase: { Submission .objects .filter (challenge_phase = phase2 ).count ()} " )
224+
225+ # 8. Show next steps
226+ print ("\n 8. Next Steps for Testing:" )
227+ print ("=" * 50 )
228+ print ("1. Check retention status:" )
229+ print (f" python manage.py manage_retention status --challenge-id { challenge .id } " )
230+ print ("\n 2. Test consent recording (if needed):" )
231+ print (
f" python manage.py manage_retention record-consent { challenge .id } --username [email protected] " )
232+ print ("\n 3. Test log retention setting:" )
233+ print (f" python manage.py manage_retention set-log-retention { challenge .id } " )
234+ print ("\n 4. Test cleanup (dry run):" )
235+ print (" python manage.py manage_retention cleanup --dry-run" )
236+ print ("\n 5. Check Celery tasks:" )
237+ print (" docker-compose logs celery_worker" )
238+ print (" docker-compose logs celery_beat" )
239+
240+ print (f"\n 🎉 Test challenge setup complete!" )
241+ print (f"Challenge ID: { challenge .id } " )
242+ print (
f"Host user: [email protected] " )
243+ print (f"Host username: host" )
244+
245+ return challenge
246+
247+ if __name__ == '__main__' :
248+ try :
249+ challenge = create_test_challenge ()
250+ print (f"\n ✅ Successfully created test challenge: { challenge .title } " )
251+ except Exception as e :
252+ print (f"❌ Error creating test challenge: { e } " )
253+ import traceback
254+ traceback .print_exc ()
255+ sys .exit (1 )
0 commit comments