From 6b815b2f79fab31b38180bfc35836c353a0c36ad Mon Sep 17 00:00:00 2001 From: Ihsan Ullah Date: Thu, 2 Apr 2026 08:29:36 +0500 Subject: [PATCH 1/3] quota cleanup updated to allow users to remove unused starting kits and competition bundles. Starting kit and public data are not included in unused datasets anymore --- src/apps/api/tests/test_cleanup.py | 47 +++++++++++++++++- src/apps/api/urls.py | 2 + src/apps/api/views/quota.py | 72 +++++++++++++++++++++++++-- src/static/js/ours/client.js | 6 +++ src/static/riot/quota_management.tag | 74 +++++++++++++++++++++++++++- 5 files changed, 195 insertions(+), 6 deletions(-) diff --git a/src/apps/api/tests/test_cleanup.py b/src/apps/api/tests/test_cleanup.py index a927800af..1dd67a2ce 100644 --- a/src/apps/api/tests/test_cleanup.py +++ b/src/apps/api/tests/test_cleanup.py @@ -56,8 +56,19 @@ def setUp(self): DataFactory(created_by=user, type=Data.INGESTION_PROGRAM), DataFactory(created_by=user, type=Data.SCORING_PROGRAM), DataFactory(created_by=user, type=Data.INPUT_DATA), - DataFactory(created_by=user, type=Data.REFERENCE_DATA), - DataFactory(created_by=user, type=Data.PUBLIC_DATA) + DataFactory(created_by=user, type=Data.REFERENCE_DATA) + ] + + # Create unused starting kits + self.unused_starting_kits = [ + DataFactory(created_by=user, type=Data.STARTING_KIT), + DataFactory(created_by=user, type=Data.STARTING_KIT) + ] + + # Create unused competition bundles + self.unused_competition_bundles = [ + DataFactory(created_by=user, type=Data.COMPETITION_BUNDLE), + DataFactory(created_by=user, type=Data.COMPETITION_BUNDLE) ] self.client.login(username='test_user', password='test_user') @@ -72,6 +83,8 @@ def test_cleanup_stats(self): assert content["unused_datasets_programs"] == len(self.unused_datasets_programs) assert content["unused_submissions"] == len(self.unused_submissions) assert content["failed_submissions"] == len(self.failed_submissions) + assert content["unused_starting_kits"] == len(self.unused_starting_kits) + assert content["unused_competition_bundles"] == len(self.unused_competition_bundles) def test_delete_unused_tasks(self): @@ -132,3 +145,33 @@ def test_delete_failed_submissions(self): assert resp.status_code == 200 content = json.loads(resp.content) assert content["failed_submissions"] == 0 + + def test_delete_unused_starting_kits(self): + + url = reverse('delete_unused_starting_kits') + resp = self.client.delete(url) + assert resp.status_code == 200 + content = json.loads(resp.content) + assert content["success"] + assert content["message"] == "Unused starting kits deleted successfully" + + url = reverse('user_quota_cleanup') + resp = self.client.get(url) + assert resp.status_code == 200 + content = json.loads(resp.content) + assert content["unused_starting_kits"] == 0 + + def test_delete_unused_competition_bundles(self): + + url = reverse('delete_unused_competition_bundles') + resp = self.client.delete(url) + assert resp.status_code == 200 + content = json.loads(resp.content) + assert content["success"] + assert content["message"] == "Unused competition bundles deleted successfully" + + url = reverse('user_quota_cleanup') + resp = self.client.get(url) + assert resp.status_code == 200 + content = json.loads(resp.content) + assert content["unused_competition_bundles"] == 0 diff --git a/src/apps/api/urls.py b/src/apps/api/urls.py index cad6fde62..640b8a954 100644 --- a/src/apps/api/urls.py +++ b/src/apps/api/urls.py @@ -54,6 +54,8 @@ path('delete_unused_datasets/', quota.delete_unused_datasets, name="delete_unused_datasets"), path('delete_unused_submissions/', quota.delete_unused_submissions, name="delete_unused_submissions"), path('delete_failed_submissions/', quota.delete_failed_submissions, name="delete_failed_submissions"), + path('delete_unused_starting_kits/', quota.delete_unused_starting_kits, name="delete_unused_starting_kits"), + path('delete_unused_competition_bundles/', quota.delete_unused_competition_bundles, name="delete_unused_competition_bundles"), # User account path('delete_account/', profiles.delete_account, name="delete_account"), diff --git a/src/apps/api/views/quota.py b/src/apps/api/views/quota.py index 0b99ff3a2..6cc927517 100644 --- a/src/apps/api/views/quota.py +++ b/src/apps/api/views/quota.py @@ -21,7 +21,9 @@ def user_quota_cleanup(request): unused_datasets_programs = Data.objects.filter( Q(created_by=request.user) & ~Q(type=Data.SUBMISSION) & - ~Q(type=Data.COMPETITION_BUNDLE) + ~Q(type=Data.COMPETITION_BUNDLE) & + ~Q(type=Data.PUBLIC_DATA) & + ~Q(type=Data.STARTING_KIT) ).exclude( Q(task_ingestion_programs__isnull=False) | Q(task_input_datas__isnull=False) | @@ -42,11 +44,29 @@ def user_quota_cleanup(request): Q(status=Submission.FAILED) ).count() + # Get unused starting kits count + unused_starting_kits = Data.objects.filter( + Q(created_by=request.user) & + Q(type=Data.STARTING_KIT) & + Q(competition__isnull=True) & + Q(phase_starting_kit__isnull=True) + ).count() + + # Get unused competition bundles + unused_competition_bundles = Data.objects.filter( + Q(created_by=request.user) & + Q(type=Data.COMPETITION_BUNDLE) & + Q(competition__isnull=True) & + Q(competition_bundles__isnull=True) + ).count() + return Response({ "unused_tasks": unused_tasks, "unused_datasets_programs": unused_datasets_programs, "unused_submissions": unused_submissions, - "failed_submissions": failed_submissions + "failed_submissions": failed_submissions, + "unused_starting_kits": unused_starting_kits, + "unused_competition_bundles": unused_competition_bundles }) @@ -84,7 +104,9 @@ def delete_unused_datasets(request): Data.objects.filter( Q(created_by=request.user) & ~Q(type=Data.SUBMISSION) & - ~Q(type=Data.COMPETITION_BUNDLE) + ~Q(type=Data.COMPETITION_BUNDLE) & + ~Q(type=Data.PUBLIC_DATA) & + ~Q(type=Data.STARTING_KIT) ).exclude( Q(task_ingestion_programs__isnull=False) | Q(task_input_datas__isnull=False) | @@ -144,3 +166,47 @@ def delete_failed_submissions(request): "success": False, "message": f"{e}" }) + + +@api_view(['DELETE']) +def delete_unused_starting_kits(request): + try: + Data.objects.filter( + Q(created_by=request.user) & + Q(type=Data.STARTING_KIT) & + Q(competition__isnull=True) & + Q(phase_starting_kit__isnull=True) + ).delete() + + return Response({ + "success": True, + "message": "Unused starting kits deleted successfully" + }) + except Exception as e: + logger.error(f"UNUSED STARTING KITS DELETION --- {e}") + return Response({ + "success": False, + "message": f"{e}" + }) + + +@api_view(['DELETE']) +def delete_unused_competition_bundles(request): + try: + Data.objects.filter( + Q(created_by=request.user) & + Q(type=Data.COMPETITION_BUNDLE) & + Q(competition__isnull=True) & + Q(competition_bundles__isnull=True) + ).delete() + + return Response({ + "success": True, + "message": "Unused competition bundles deleted successfully" + }) + except Exception as e: + logger.error(f"UNUSED COMPETITION BUNDLES DELETION --- {e}") + return Response({ + "success": False, + "message": f"{e}" + }) diff --git a/src/static/js/ours/client.js b/src/static/js/ours/client.js index decd1d6f4..fa169c5a8 100644 --- a/src/static/js/ours/client.js +++ b/src/static/js/ours/client.js @@ -392,6 +392,12 @@ CODALAB.api = { delete_failed_submissions: () => { return CODALAB.api.request('DELETE', `${URLS.API}delete_failed_submissions/`) }, + delete_unused_starting_kits: () => { + return CODALAB.api.request('DELETE', `${URLS.API}delete_unused_starting_kits/`) + }, + delete_unused_competition_bundles: () => { + return CODALAB.api.request('DELETE', `${URLS.API}delete_unused_competition_bundles/`) + }, /*--------------------------------------------------------------------- User Account ---------------------------------------------------------------------*/ diff --git a/src/static/riot/quota_management.tag b/src/static/riot/quota_management.tag index f4102e738..b97b82a45 100644 --- a/src/static/riot/quota_management.tag +++ b/src/static/riot/quota_management.tag @@ -54,6 +54,26 @@ + + + Unused Starting Kits ({unused_starting_kits}) + + + + + + + Unused Competition Bundles ({unused_competition_bundles}) + + + + @@ -66,6 +86,8 @@ self.unused_datasets_programs = 0 self.unused_submissions = 0 self.failed_submissions = 0 + self.unused_starting_kits = 0 + self.unused_competition_bundles = 0 self.quota = 0 self.storage_used = 0 @@ -84,6 +106,8 @@ self.unused_datasets_programs = data.unused_datasets_programs self.unused_submissions = data.unused_submissions self.failed_submissions = data.failed_submissions + self.unused_starting_kits = data.unused_starting_kits + self.unused_competition_bundles = data.unused_competition_bundles self.update() }) .fail(function (response) { @@ -119,7 +143,7 @@ self.update() CODALAB.events.trigger('reload_tasks') CODALAB.events.trigger('reload_datasets') - self.get_cleanup() + CODALAB.events.trigger('reload_quota_cleanup') }else{ toastr.error(data.message) } @@ -141,6 +165,7 @@ toastr.success(data.message) self.update() CODALAB.events.trigger('reload_datasets') + CODALAB.events.trigger('reload_quota_cleanup') }else{ toastr.error(data.message) } @@ -162,6 +187,7 @@ toastr.success(data.message) self.update() CODALAB.events.trigger('reload_submissions') + CODALAB.events.trigger('reload_quota_cleanup') }else{ toastr.error(data.message) } @@ -183,6 +209,7 @@ toastr.success(data.message) self.update() CODALAB.events.trigger('reload_submissions') + CODALAB.events.trigger('reload_quota_cleanup') }else{ toastr.error(data.message) } @@ -193,6 +220,51 @@ } } + // Delete unused starting kits + self.delete_unused_starting_kits = function(){ + if (confirm(`Are you sure you want to permanently delete all unused starting kits?`)) { + + CODALAB.api.delete_unused_starting_kits() + .done(function (data) { + if(data.success){ + self.unused_starting_kits = 0 + toastr.success(data.message) + self.update() + CODALAB.events.trigger('reload_datasets') + CODALAB.events.trigger('reload_quota_cleanup') + }else{ + toastr.error(data.message) + } + }) + .fail(function (response) { + toastr.error("Unused starting kits deletion failed!") + }) + } + } + + // Delete unused competition bundles + self.delete_unused_competition_bundles = function(){ + if (confirm(`Are you sure you want to permanently delete all unused competition bundles?`)) { + + CODALAB.api.delete_unused_competition_bundles() + .done(function (data) { + if(data.success){ + self.unused_competition_bundles = 0 + toastr.success(data.message) + self.update() + CODALAB.events.trigger('reload_competition_bundles') + CODALAB.events.trigger('reload_quota_cleanup') + }else{ + toastr.error(data.message) + } + }) + .fail(function (response) { + toastr.error("Unused starting kits deletion failed!") + }) + } + } + + CODALAB.events.on('reload_quota_cleanup', self.get_cleanup) From 250495c197d1d797f79ec78be45ff4886afd2086 Mon Sep 17 00:00:00 2001 From: didayolo Date: Thu, 9 Apr 2026 13:30:31 +0200 Subject: [PATCH 2/3] Merge conflicts --- src/static/riot/competitions/bundle_management.tag | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/static/riot/competitions/bundle_management.tag b/src/static/riot/competitions/bundle_management.tag index b7253dc5a..56641d67c 100644 --- a/src/static/riot/competitions/bundle_management.tag +++ b/src/static/riot/competitions/bundle_management.tag @@ -242,7 +242,11 @@ } // Update bundles on unused bundles delete +<<<<<<< HEAD CODALAB.events.on('reload_competition_bundles', self.update_competition_bundles) +======= + CODALAB.events.on('reload_competition_bundles', self.update_datasets) +>>>>>>> 801a7bd9 (quota cleanup updated to allow users to remove unused starting kits and competition bundles. Starting kit and public data are not included in unused datasets anymore) From 1be7e389845cbcd7f90c2800e203c55005118fff Mon Sep 17 00:00:00 2001 From: didayolo Date: Thu, 9 Apr 2026 13:32:36 +0200 Subject: [PATCH 3/3] Merge conflict --- src/static/riot/competitions/bundle_management.tag | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/static/riot/competitions/bundle_management.tag b/src/static/riot/competitions/bundle_management.tag index 56641d67c..b7253dc5a 100644 --- a/src/static/riot/competitions/bundle_management.tag +++ b/src/static/riot/competitions/bundle_management.tag @@ -242,11 +242,7 @@ } // Update bundles on unused bundles delete -<<<<<<< HEAD CODALAB.events.on('reload_competition_bundles', self.update_competition_bundles) -======= - CODALAB.events.on('reload_competition_bundles', self.update_datasets) ->>>>>>> 801a7bd9 (quota cleanup updated to allow users to remove unused starting kits and competition bundles. Starting kit and public data are not included in unused datasets anymore)