Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions CHANGELOG
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,13 @@

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

26.1.4 (2026-01-06)
===================

- Follow-up fix for preprint/registration withdrawal request
- Added a script/task to populate default notification subscriptions that were missing before and after NR
- Reworked PR template

26.1.3 (2026-01-05)
===================

Expand Down
64 changes: 40 additions & 24 deletions PULL_REQUEST_TEMPLATE.md
Original file line number Diff line number Diff line change
@@ -1,39 +1,55 @@
<!-- Before submit your Pull Request, make sure you picked
the right branch:
[//]: # (
* Before submitting your Pull Request, make sure you've picked the right branch and your code is up-to-date with it.
* For critical hotfixes, select "master" as the target branch.
* For bugfixes, improvements and new features, select "develop" as the target branch.
* If your PR is part of a project/team, select project/team's dedicated feature branch as the target.
* If you have a JIRA ticket, prefix the ticket number [ENG-*****] to the PR title.
)

- For hotfixes, select "master" as the target branch
- For new features, select "develop" as the target branch
- For release feature fixes, select the relevant release branch (release/X.Y.Z) as the target branch -->
## Ticket

[//]: # (Link to a JIRA ticket if available.)

* [ENG-*****]()

## Purpose

<!-- Describe the purpose of your changes -->
[//]: # (Briefly describe the purpose of this PR.)

## Changes

<!-- Briefly describe or list your changes -->
[//]: # (Briefly describe or list your changes.)

## QA Notes
## Side Effects

[//]: # (Any possible side effects?)

<!-- Please make verification statements inspired by your code and what your code touches.
- Verify
- Verify
What are the areas of risk?
Any concerns/considerations/questions that development raised?
-->
## QE Notes

## Documentation
[//]: # (
* Any QA testing notes for QE?
* Make verification statements inspired by your code and what your code touches.
* What are the areas of risk?
* Any concerns/considerations/questions that development raised?
* If you have a JIRA ticket, make sure the ticket also contains the QE notes.
)

<!-- Does any internal or external documentation need to be updated?
- If the API was versioned, update the developer.osf.io changelog.
- If changes were made to the API, link the developer.osf.io PR here.
-->
## CE Notes

## Side Effects

<!-- Any possible side effects? -->
[//]: # (
* Any server configuration and deployment notes for CE?
* Is model migration required? Is data migration/backfill/population required?
* If so, is it reversible? Is there a roll-back plan?
* Does server settings needs to be updated?
* If so, have you checked with CE on existing settings for affected servers?
* Are there any deployment dependencies to other services?
* If you have a JIRA ticket, make sure the ticket also contains the CE notes.
)

## Ticket
## Documentation

<!-- Link to Jira ticket, if applicable e.g. https://openscience.atlassian.net/browse/OSF-1234 -->
[//]: # (
* Does any internal or external documentation need to be updated?
* If the API was versioned, update the developer.osf.io changelog.
* If changes were made to the API, link the developer.osf.io PR here.
)
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "OSF",
"version": "26.1.3",
"version": "26.1.4",
"description": "Facilitating Open Science",
"repository": "https://github.com/CenterForOpenScience/osf.io",
"author": "Center for Open Science",
Expand Down
111 changes: 111 additions & 0 deletions scripts/populate_notification_subscriptions.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
import django
django.setup()

from website.app import init_app
init_app(routes=False)

from framework.celery_tasks import app as celery_app
from django.contrib.contenttypes.models import ContentType
from django.db.models import Count, F, OuterRef, Subquery, IntegerField, CharField
from django.db.models.functions import Cast
from osf.models import OSFUser, Node, NotificationSubscription, NotificationType


@celery_app.task(name='scripts.populate_notification_subscriptions')
def populate_notification_subscriptions():
created = 0
user_file_nt = NotificationType.Type.USER_FILE_UPDATED.instance
review_nt = NotificationType.Type.REVIEWS_SUBMISSION_STATUS.instance
node_file_nt = NotificationType.Type.NODE_FILE_UPDATED.instance

user_ct = ContentType.objects.get_for_model(OSFUser)
node_ct = ContentType.objects.get_for_model(Node)

reviews_qs = OSFUser.objects.exclude(subscriptions__notification_type__name=NotificationType.Type.REVIEWS_SUBMISSION_STATUS).distinct('id')
files_qs = OSFUser.objects.exclude(subscriptions__notification_type__name=NotificationType.Type.USER_FILE_UPDATED).distinct('id')

node_notifications_sq = (
NotificationSubscription.objects.filter(
content_type=node_ct,
notification_type=node_file_nt,
object_id=Cast(OuterRef('pk'), CharField()),
).values(
'object_id'
).annotate(
cnt=Count('id')
).values('cnt')[:1]
)

nodes_qs = (
Node.objects
.annotate(
contributors_count=Count('_contributors', distinct=True),
notifications_count=Subquery(
node_notifications_sq,
output_field=IntegerField(),
),
).exclude(contributors_count=F('notifications_count'))
)

print(f"Creating REVIEWS_SUBMISSION_STATUS subscriptions for {reviews_qs.count()} users.")
for id, user in enumerate(reviews_qs, 1):
print(f"Processing user {id} / {reviews_qs.count()}")
try:
_, is_created = NotificationSubscription.objects.get_or_create(
notification_type=review_nt,
user=user,
content_type=user_ct,
object_id=user.id,
defaults={
'message_frequency': 'none',
},
)
if is_created:
created += 1
except Exception as exeption:
print(exeption)
continue

print(f"Creating USER_FILE_UPDATED subscriptions for {files_qs.count()} users.")
for id, user in enumerate(files_qs, 1):
print(f"Processing user {id} / {files_qs.count()}")
try:
_, is_created = NotificationSubscription.objects.get_or_create(
notification_type=user_file_nt,
user=user,
content_type=user_ct,
object_id=user.id,
defaults={
'message_frequency': 'none',
},
)
if is_created:
created += 1
except Exception as exeption:
print(exeption)
continue

print(f"Creating NODE_FILE_UPDATED subscriptions for {nodes_qs.count()} nodes.")
for id, node in enumerate(nodes_qs, 1):
print(f"Processing node {id} / {nodes_qs.count()}")
for contributor in node.contributors.all():
try:
_, is_created = NotificationSubscription.objects.get_or_create(
notification_type=node_file_nt,
user=contributor,
content_type=node_ct,
object_id=node.id,
defaults={
'message_frequency': 'none',
},
)
if is_created:
created += 1
except Exception as exeption:
print(exeption)
continue

print(f"Created {created} subscriptions")

if __name__ == '__main__':
populate_notification_subscriptions.delay()
7 changes: 6 additions & 1 deletion website/reviews/listeners.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,9 @@ def reviews_notification(self, creator, template, context, action):

@reviews_signals.reviews_withdraw_requests_notification_moderators.connect
def reviews_withdraw_requests_notification_moderators(self, timestamp, context, user, resource):
context['referrer_fullname'] = user.fullname
from website.profile.utils import get_profile_image_url
context['requester_fullname'] = user.fullname
context['profile_image_url'] = get_profile_image_url(resource.creator)
provider = resource.provider
from osf.models import NotificationType

Expand All @@ -38,6 +39,7 @@ def reviews_withdraw_requests_notification_moderators(self, timestamp, context,
for recipient in provider.get_group(group_name).user_set.all():
context['user_fullname'] = recipient.fullname
context['recipient_fullname'] = recipient.fullname
context['localized_timestamp'] = str(timestamp)

NotificationType.Type.PROVIDER_NEW_PENDING_WITHDRAW_REQUESTS.instance.emit(
user=recipient,
Expand All @@ -48,7 +50,9 @@ def reviews_withdraw_requests_notification_moderators(self, timestamp, context,

@reviews_signals.reviews_email_withdrawal_requests.connect
def reviews_withdrawal_requests_notification(self, timestamp, context):
from website.profile.utils import get_profile_image_url
preprint = context.pop('reviewable')
context['profile_image_url'] = get_profile_image_url(preprint.creator)
context['reviewable_absolute_url'] = preprint.absolute_url
context['reviewable_title'] = preprint.title
context['reviewable__id'] = preprint._id
Expand All @@ -63,6 +67,7 @@ def reviews_withdrawal_requests_notification(self, timestamp, context):
for recipient in preprint.provider.get_group(group_name).user_set.all():
context['user_fullname'] = recipient.fullname
context['recipient_fullname'] = recipient.fullname
context['localized_timestamp'] = str(timestamp)

NotificationType.Type.PROVIDER_NEW_PENDING_WITHDRAW_REQUESTS.instance.emit(
user=recipient,
Expand Down
2 changes: 2 additions & 0 deletions website/settings/defaults.py
Original file line number Diff line number Diff line change
Expand Up @@ -450,6 +450,7 @@ class CeleryConfig:
'osf.management.commands.monthly_reporters_go',
'osf.management.commands.ingest_cedar_metadata_templates',
'osf.metrics.reporters',
'scripts.populate_notification_subscriptions',
}

med_pri_modules = {
Expand Down Expand Up @@ -578,6 +579,7 @@ class CeleryConfig:
'osf.management.commands.monthly_reporters_go',
'osf.external.spam.tasks',
'api.share.utils',
'scripts.populate_notification_subscriptions',
)

# Modules that need metrics and release requirements
Expand Down
Loading