@@ -1914,20 +1914,16 @@ def calculate_retention_period_days(challenge_end_date, challenge=None):
19141914 # Default 30-day retention when host has consented
19151915 return 30
19161916
1917- # No host consent - use conservative default (longer retention )
1918- # Default retention calculation (90 days after challenge ends for safety)
1917+ # No host consent - use indefinite retention (no automatic cleanup )
1918+ # Without consent, data is retained indefinitely for safety
19191919 if challenge_end_date > now :
1920- # Challenge is still active, retain until end date + 90 days
1921- # Round up to the nearest day to avoid flakiness
1922- seconds_until_end = (challenge_end_date - now ).total_seconds ()
1923- days_until_end = math .ceil (seconds_until_end / (24 * 3600.0 ))
1924- return int (days_until_end ) + 90
1920+ # Challenge is still active, retain indefinitely
1921+ # Return a very large number to effectively make it indefinite
1922+ return 3653 # Maximum AWS CloudWatch retention period (10 years)
19251923 else :
1926- # Challenge has ended, retain for 90 more days
1927- # Round down to match original behavior of .days
1928- seconds_since_end = (now - challenge_end_date ).total_seconds ()
1929- days_since_end = math .floor (seconds_since_end / (24 * 3600.0 ))
1930- return max (90 - int (days_since_end ), 1 ) # At least 1 day
1924+ # Challenge has ended, retain indefinitely
1925+ # Return maximum retention period
1926+ return 3653 # Maximum AWS CloudWatch retention period (10 years)
19311927
19321928
19331929def map_retention_days_to_aws_values (days ):
@@ -1993,7 +1989,7 @@ def set_cloudwatch_log_retention(challenge_pk, retention_days=None):
19931989 return {
19941990 "error" : f"Challenge { challenge_pk } host has not consented to retention policy. "
19951991 "Please obtain consent before applying retention policies. "
1996- "Without consent, data is retained for 90 days for safety." ,
1992+ "Without consent, data is retained indefinitely for safety." ,
19971993 "requires_consent" : True ,
19981994 "challenge_id" : challenge_pk ,
19991995 }
@@ -2045,7 +2041,7 @@ def set_cloudwatch_log_retention(challenge_pk, retention_days=None):
20452041 "retention_days" : aws_retention_days ,
20462042 "log_group" : log_group_name ,
20472043 "message" : f"Retention policy set to { aws_retention_days } days "
2048- f"({ '30-day policy applied' if challenge_obj .retention_policy_consent else '90-day safety retention' } )" ,
2044+ f"({ '30-day policy applied' if challenge_obj .retention_policy_consent else 'indefinite retention (no consent) ' } )" ,
20492045 "host_consent" : challenge_obj .retention_policy_consent ,
20502046 }
20512047
@@ -2076,7 +2072,7 @@ def calculate_submission_retention_date(challenge_phase):
20762072 challenge_phase: ChallengePhase object
20772073
20782074 Returns:
2079- datetime: Date when submission artifacts can be deleted
2075+ datetime: Date when submission artifacts can be deleted, or None if indefinite retention
20802076 """
20812077 from datetime import timedelta
20822078
@@ -2092,17 +2088,14 @@ def calculate_submission_retention_date(challenge_phase):
20922088
20932089 # Check if challenge has host consent
20942090 if challenge .retention_policy_consent :
2095- # Use challenge-level retention policy
2091+ # Use challenge-level retention policy (30 days)
20962092 retention_days = calculate_retention_period_days (
20972093 challenge_phase .end_date , challenge
20982094 )
2095+ return challenge_phase .end_date + timedelta (days = retention_days )
20992096 else :
2100- # No host consent, use default retention period
2101- retention_days = calculate_retention_period_days (
2102- challenge_phase .end_date , challenge
2103- )
2104-
2105- return challenge_phase .end_date + timedelta (days = retention_days )
2097+ # No host consent - indefinite retention (no automatic cleanup)
2098+ return None
21062099
21072100
21082101def delete_submission_files_from_storage (submission ):
@@ -2209,7 +2202,9 @@ def cleanup_expired_submission_artifacts():
22092202 # Find submissions eligible for cleanup
22102203 now = timezone .now ()
22112204 eligible_submissions = Submission .objects .filter (
2212- retention_eligible_date__lte = now , is_artifact_deleted = False
2205+ retention_eligible_date__lte = now ,
2206+ retention_eligible_date__isnull = False , # Exclude indefinite retention
2207+ is_artifact_deleted = False ,
22132208 ).select_related ("challenge_phase__challenge" )
22142209
22152210 cleanup_stats = {
@@ -2307,36 +2302,26 @@ def update_submission_retention_dates():
23072302
23082303 for phase in ended_phases :
23092304 try :
2310- # Process submissions by type
2311- for submission_type in [
2312- "participant" ,
2313- "host" ,
2314- "baseline" ,
2315- "evaluation_output" ,
2316- ]:
2317- retention_date = calculate_submission_retention_date (
2318- phase , submission_type
2319- )
2320- if retention_date :
2321- # Update submissions for this phase and type
2322- submissions_updated = Submission .objects .filter (
2323- challenge_phase = phase ,
2324- submission_type = submission_type ,
2325- retention_eligible_date__isnull = True ,
2326- is_artifact_deleted = False ,
2327- ).update (retention_eligible_date = retention_date )
2328-
2329- updated_count += submissions_updated
2330-
2331- if submissions_updated > 0 :
2332- logger .info (
2333- f"Updated { submissions_updated } { submission_type } submissions for phase { phase .pk } "
2334- f"({ phase .challenge .title } ) with retention date { retention_date } "
2335- )
2336- else :
2337- logger .debug (
2338- f"No retention date calculated for phase { phase .pk } submission type { submission_type } - phase may still be public"
2305+ retention_date = calculate_submission_retention_date (phase )
2306+ if retention_date :
2307+ # Update submissions for this phase
2308+ submissions_updated = Submission .objects .filter (
2309+ challenge_phase = phase ,
2310+ retention_eligible_date__isnull = True ,
2311+ is_artifact_deleted = False ,
2312+ ).update (retention_eligible_date = retention_date )
2313+
2314+ updated_count += submissions_updated
2315+
2316+ if submissions_updated > 0 :
2317+ logger .info (
2318+ f"Updated { submissions_updated } submissions for phase { phase .pk } "
2319+ f"({ phase .challenge .title } ) with retention date { retention_date } "
23392320 )
2321+ else :
2322+ logger .debug (
2323+ f"No retention date calculated for phase { phase .pk } - phase may still be public or indefinite retention"
2324+ )
23402325
23412326 except Exception as e :
23422327 error_msg = f"Failed to update retention dates for phase { phase .pk } : { str (e )} "
@@ -2480,6 +2465,7 @@ def weekly_retention_notifications_and_consent_log():
24802465 warning_date = timezone .now () + timedelta (days = 14 )
24812466 warning_submissions = Submission .objects .filter (
24822467 retention_eligible_date__date = warning_date .date (),
2468+ retention_eligible_date__isnull = False , # Exclude indefinite retention
24832469 is_artifact_deleted = False ,
24842470 ).select_related ("challenge_phase__challenge__creator" )
24852471
0 commit comments