Skip to content

Commit 929269f

Browse files
committed
Merge tag '26.5.1' into develop
Post-NR hotfixes
2 parents 231bf1b + 3a512f9 commit 929269f

13 files changed

Lines changed: 86 additions & 464 deletions

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,7 @@ website/static/sitemaps/
122122
##############
123123

124124
api/static/vendor
125+
osf/features.override.yaml
125126

126127
# Local settings files
127128
local.py

CHANGELOG

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,15 @@
22

33
We follow the CalVer (https://calver.org/) versioning scheme: YY.MINOR.MICRO.
44

5+
26.5.1 (2026-03-25)
6+
===================
7+
8+
- Post-NR hotfixes
9+
- Rework notification history cleanup task
10+
- Update merge subscription provider task
11+
- Enable overriding features.yaml for local dev to use mailhog
12+
- Remove deprecated routes, script and commands
13+
514
26.5.0 (2026-03-13)
615
===================
716

README-docker-compose.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -294,6 +294,11 @@
294294
```bash
295295
docker compose run --rm web python3 manage.py reset_db --noinput
296296
```
297+
- OPTIONAL: Create a `features.yaml` override
298+
```bash
299+
cp osf/features.yaml osf/features.override.yaml
300+
```
301+
Note: Update the WAFFLE_VALUES_YAML variable to point to the override file (osf/features.override.yaml).
297302

298303
## Application Debugging
299304

notifications/tasks.py

Lines changed: 34 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -497,18 +497,42 @@ def send_no_addon_email(self, dry_run=False, **kwargs):
497497

498498
@celery_app.task(bind=True, name='notifications.tasks.notifications_cleanup_task')
499499
def notifications_cleanup_task(self, dry_run=False, **kwargs):
500-
"""Remove old notifications and email tasks from the database."""
501-
500+
"""Entry point for notifications and email tasks cleanup task."""
502501
cutoff_date = timezone.now() - settings.NOTIFICATIONS_CLEANUP_AGE
503-
old_notifications = Notification.objects.filter(sent__lt=cutoff_date)
504-
old_email_tasks = EmailTask.objects.filter(created_at__lt=cutoff_date)
505-
506502
if dry_run:
507-
notifications_count = old_notifications.count()
508-
email_tasks_count = old_email_tasks.count()
503+
notifications_count = Notification.objects.filter(sent__lt=cutoff_date).count()
504+
email_tasks_count = EmailTask.objects.filter(created_at__lt=cutoff_date).count()
509505
logger.info(f'[Dry Run] Notifications Cleanup Task: {notifications_count} notifications and {email_tasks_count} email tasks would be deleted.')
510506
return
511507

512-
deleted_notifications_count, _ = old_notifications.delete()
513-
deleted_email_tasks_count, _ = old_email_tasks.delete()
514-
logger.info(f'Notifications Cleanup Task: Deleted {deleted_notifications_count} notifications and {deleted_email_tasks_count} email tasks older than {cutoff_date}.')
508+
delete_batch.delay('osf', 'Notification', {'sent__lt': cutoff_date}, batch_size=settings.NOTIFICATIONS_CLEANUP_BATCH_SIZE)
509+
delete_batch.delay('osf', 'EmailTask', {'created_at__lt': cutoff_date}, batch_size=settings.NOTIFICATIONS_CLEANUP_BATCH_SIZE)
510+
511+
512+
@celery_app.task(bind=True, name='notifications.tasks.delete_batch')
513+
def delete_batch(
514+
self,
515+
app_label,
516+
model_name,
517+
filters,
518+
order_field='id',
519+
batch_size=10000
520+
):
521+
Model = apps.get_model(app_label, model_name)
522+
523+
ids = list(
524+
Model.objects
525+
.filter(**filters)
526+
.order_by(order_field)
527+
.values_list('id', flat=True)[:batch_size]
528+
)
529+
530+
if not ids:
531+
logger.info(f'{model_name} cleanup finished')
532+
return
533+
534+
deleted, _ = Model.objects.filter(id__in=ids).delete()
535+
536+
logger.info(f'Deleted {deleted} rows from {model_name}')
537+
538+
delete_batch.delay(app_label, model_name, filters, order_field, batch_size)

osf/management/commands/remove_duplicate_notification_subscriptions.py

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

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "OSF",
3-
"version": "26.5.0",
3+
"version": "26.5.1",
44
"description": "Facilitating Open Science",
55
"repository": "https://github.com/CenterForOpenScience/osf.io",
66
"author": "Center for Open Science",

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[tool.poetry]
22
name = "osf-io"
3-
version = "26.5.0"
3+
version = "26.5.1"
44
description = "The code for [https://osf.io](https://osf.io)."
55
authors = ["Your Name <you@example.com>"]
66
license = "Apache License 2.0"

scripts/remove_after_use/merge_notification_subscription_provider_ct.py

Lines changed: 33 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,11 @@
77
from django.contrib.contenttypes.models import ContentType
88
from framework.celery_tasks import app as celery_app
99
from osf.models import NotificationSubscription
10+
from django.db.models import Exists, OuterRef
11+
from celery.utils.log import get_task_logger
12+
13+
logger = get_task_logger(__name__)
14+
1015

1116

1217
@celery_app.task(name='scripts.remove_after_use.merge_notification_subscription_provider_ct')
@@ -19,13 +24,37 @@ def merge_notification_subscription_provider_ct():
1924
ContentType.objects.get_by_natural_key('osf', 'registrationprovider'),
2025
ContentType.objects.get_by_natural_key('osf', 'collectionprovider'),
2126
]
22-
subscriptions = NotificationSubscription.objects.filter(
23-
content_type__in=provider_ct_list
24-
)
25-
subscriptions.update(
27+
28+
provider_ct_ids = [ct.id for ct in provider_ct_list]
29+
30+
abstract_provider_ct_qs = NotificationSubscription.objects.filter(
2631
content_type=abstract_provider_ct
2732
)
2833

34+
duplicates = NotificationSubscription.objects.filter(
35+
content_type_id__in=provider_ct_ids
36+
).annotate(
37+
abstract_exists=Exists(
38+
abstract_provider_ct_qs.filter(
39+
notification_type_id=OuterRef('notification_type_id'),
40+
user_id=OuterRef('user_id'),
41+
object_id=OuterRef('object_id'),
42+
_is_digest=OuterRef('_is_digest'),
43+
)
44+
)
45+
).filter(abstract_exists=True)
46+
47+
# delete rows that would conflict
48+
logger.info(f'Deleted {duplicates.count()} duplicate NotificationSubscription rows with provider content types.')
49+
duplicates.delete()
50+
51+
# update remaining rows
52+
update_qs = NotificationSubscription.objects.filter(
53+
content_type_id__in=provider_ct_ids
54+
)
55+
logger.info(f'Updated {update_qs.count()} NotificationSubscription rows to use abstract provider content type.')
56+
update_qs.update(content_type=abstract_provider_ct)
57+
2958

3059
if __name__ == '__main__':
3160
merge_notification_subscription_provider_ct.delay()

scripts/remove_after_use/populate_notification_subscriptions_node_file_updated.py

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

0 commit comments

Comments
 (0)