Skip to content

Commit c4f8494

Browse files
committed
Modify tests
1 parent 9b8dbfc commit c4f8494

File tree

8 files changed

+122
-199
lines changed

8 files changed

+122
-199
lines changed

apps/challenges/aws_utils.py

Lines changed: 38 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -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

19331929
def 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

21082101
def 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

apps/challenges/management/commands/manage_retention.py

Lines changed: 31 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -247,8 +247,6 @@ def add_arguments(self, parser):
247247
help="Limit number of results (default: 50)",
248248
)
249249

250-
251-
252250
# Check consent status
253251
subparsers.add_parser(
254252
"check-consent",
@@ -335,7 +333,9 @@ def handle_cleanup(self, options):
335333

336334
now = timezone.now()
337335
eligible_submissions = Submission.objects.filter(
338-
retention_eligible_date__lte=now, is_artifact_deleted=False
336+
retention_eligible_date__lte=now,
337+
retention_eligible_date__isnull=False, # Exclude indefinite retention
338+
is_artifact_deleted=False,
339339
).select_related("challenge_phase__challenge")
340340

341341
if not eligible_submissions.exists():
@@ -527,11 +527,11 @@ def show_challenge_status(self, challenge_id):
527527
else:
528528
self.stdout.write(
529529
self.style.WARNING(
530-
"❌ HOST HAS NOT CONSENTED - 90-DAY SAFETY RETENTION APPLIED"
530+
"❌ HOST HAS NOT CONSENTED - INDEFINITE RETENTION APPLIED"
531531
)
532532
)
533533
self.stdout.write(
534-
f" Retention policy: 90-day safety retention (default)"
534+
f" Retention policy: Indefinite retention (no automatic cleanup)"
535535
)
536536
self.stdout.write(
537537
f" Action needed: Host must provide consent for 30-day retention"
@@ -578,9 +578,18 @@ def show_challenge_status(self, challenge_id):
578578
f" Retention eligible date: {retention_date}"
579579
)
580580
else:
581-
self.stdout.write(
582-
" Retention not applicable (phase still public or no end date)"
583-
)
581+
if phase.is_public:
582+
self.stdout.write(
583+
" Retention not applicable (phase still public)"
584+
)
585+
elif not phase.end_date:
586+
self.stdout.write(
587+
" Retention not applicable (no end date)"
588+
)
589+
else:
590+
self.stdout.write(
591+
" Retention: Indefinite (no host consent)"
592+
)
584593

585594
submissions = Submission.objects.filter(challenge_phase=phase)
586595
total_submissions = submissions.count()
@@ -630,6 +639,7 @@ def show_overall_status(self):
630639
).count()
631640
eligible_submissions = Submission.objects.filter(
632641
retention_eligible_date__lte=timezone.now(),
642+
retention_eligible_date__isnull=False, # Exclude indefinite retention
633643
is_artifact_deleted=False,
634644
).count()
635645

@@ -650,7 +660,7 @@ def show_overall_status(self):
650660
f"With consent (30-day retention): {consented_challenges}"
651661
)
652662
self.stdout.write(
653-
f"Without consent (90-day retention): {non_consented_challenges}"
663+
f"Without consent (indefinite retention): {non_consented_challenges}"
654664
)
655665

656666
if non_consented_challenges > 0:
@@ -671,6 +681,7 @@ def show_overall_status(self):
671681
upcoming_submissions = Submission.objects.filter(
672682
retention_eligible_date__lte=upcoming_date,
673683
retention_eligible_date__gt=timezone.now(),
684+
retention_eligible_date__isnull=False, # Exclude indefinite retention
674685
is_artifact_deleted=False,
675686
).select_related("challenge_phase__challenge")
676687

@@ -694,7 +705,7 @@ def show_overall_status(self):
694705
consent_status = (
695706
"✅ 30-day"
696707
if challenge_data["has_consent"]
697-
else "❌ 90-day"
708+
else "❌ Indefinite"
698709
)
699710
self.stdout.write(
700711
f" - {challenge_data['name']}: {challenge_data['count']} submissions ({consent_status})"
@@ -902,7 +913,7 @@ def _build_retention_report(self, challenge_id=None):
902913
"retention_policy": (
903914
"30-day"
904915
if challenge.retention_policy_consent
905-
else "90-day safety"
916+
else "indefinite"
906917
),
907918
},
908919
"admin_override": {
@@ -964,6 +975,7 @@ def _build_retention_report(self, challenge_id=None):
964975
challenge_data["submissions"]["eligible"] = (
965976
challenge_submissions.filter(
966977
retention_eligible_date__lte=now,
978+
retention_eligible_date__isnull=False, # Exclude indefinite retention
967979
is_artifact_deleted=False,
968980
).count()
969981
)
@@ -1160,14 +1172,18 @@ def handle_check_health(self, options):
11601172
f"Found {orphaned_submissions} submissions without challenge phases"
11611173
)
11621174

1163-
# Check 3: Submissions with missing retention dates
1175+
# Check 3: Submissions with missing retention dates (excluding indefinite retention)
1176+
# Only count submissions that should have retention dates but don't
11641177
missing_retention_dates = Submission.objects.filter(
11651178
retention_eligible_date__isnull=True,
11661179
is_artifact_deleted=False,
1180+
challenge_phase__end_date__isnull=False, # Has end date
1181+
challenge_phase__is_public=False, # Phase is not public
1182+
challenge_phase__challenge__retention_policy_consent=True, # Has consent
11671183
).count()
11681184
if missing_retention_dates > 0:
11691185
health_status["warnings"].append(
1170-
f"Found {missing_retention_dates} submissions without retention dates"
1186+
f"Found {missing_retention_dates} submissions without retention dates (should have 30-day retention)"
11711187
)
11721188

11731189
# Check 4: Recent errors (if verbose)
@@ -1352,8 +1368,6 @@ def handle_find_submissions(self, options):
13521368

13531369
# NEW: Consent management methods
13541370

1355-
1356-
13571371
def handle_check_consent(self, options):
13581372
"""Handle checking consent status for challenges"""
13591373
self.stdout.write("Checking retention policy consent status:")
@@ -1369,7 +1383,7 @@ def handle_check_consent(self, options):
13691383
status = "✅ CONSENTED (30-day retention allowed)"
13701384
else:
13711385
consent_stats["without_consent"] += 1
1372-
status = "❌ NO CONSENT (90-day retention for safety)"
1386+
status = "❌ NO CONSENT (indefinite retention for safety)"
13731387

13741388
self.stdout.write(
13751389
f"Challenge {challenge.pk}: {challenge.title[:40]:<40} | {status}"
@@ -1383,7 +1397,7 @@ def handle_check_consent(self, options):
13831397
f"With consent (30-day retention allowed): {consent_stats['with_consent']}"
13841398
)
13851399
self.stdout.write(
1386-
f"Without consent (90-day retention for safety): {consent_stats['without_consent']}"
1400+
f"Without consent (indefinite retention for safety): {consent_stats['without_consent']}"
13871401
)
13881402

13891403
if consent_stats["without_consent"] > 0:

apps/challenges/migrations/0115_add_retention_consent_fields.py renamed to apps/challenges/migrations/0114_add_log_retention_and_consent_fields.py

Lines changed: 13 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,22 @@ class Migration(migrations.Migration):
99

1010
dependencies = [
1111
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
12-
("challenges", "0114_add_log_retention_override"),
12+
("challenges", "0113_add_github_branch_field_and_unique_constraint"),
1313
]
1414

1515
operations = [
16+
# Log retention override field (from 0114)
17+
migrations.AddField(
18+
model_name="challenge",
19+
name="log_retention_days_override",
20+
field=models.PositiveIntegerField(
21+
blank=True,
22+
default=None,
23+
help_text="Admin override for CloudWatch log retention period in days (defaults to 30 days when host has consented)",
24+
null=True,
25+
),
26+
),
27+
# Retention consent fields (from 0115)
1628
migrations.AddField(
1729
model_name="challenge",
1830
name="retention_policy_consent",
@@ -52,14 +64,4 @@ class Migration(migrations.Migration):
5264
null=True,
5365
),
5466
),
55-
migrations.AlterField(
56-
model_name="challenge",
57-
name="log_retention_days_override",
58-
field=models.PositiveIntegerField(
59-
blank=True,
60-
default=None,
61-
help_text="Admin override for CloudWatch log retention period in days (defaults to 30 days when host has consented)",
62-
null=True,
63-
),
64-
),
6567
]

apps/challenges/migrations/0114_add_log_retention_override.py

Lines changed: 0 additions & 21 deletions
This file was deleted.

0 commit comments

Comments
 (0)