Skip to content

Commit

Permalink
added an ability to approve registration requests for approval via ad…
Browse files Browse the repository at this point in the history
…min app
  • Loading branch information
ihorsokhanexoft committed Feb 12, 2025
1 parent 1855d55 commit a1e906a
Show file tree
Hide file tree
Showing 7 changed files with 161 additions and 6 deletions.
2 changes: 2 additions & 0 deletions admin/nodes/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@
re_path(r'^known_spam$', views.NodeKnownSpamList.as_view(), name='known-spam'),
re_path(r'^known_ham$', views.NodeKnownHamList.as_view(), name='known-ham'),
re_path(r'^doi_backlog_list/$', views.DoiBacklogListView.as_view(), name='doi-backlog-list'),
re_path(r'^approval_backlog_list/$', views.ApprovalBacklogListView.as_view(), name='approval-backlog-list'),
re_path(r'^confirm_approve_backlog_list/$', views.ConfirmApproveBacklogView.as_view(), name='confirm-approve-backlog-list'),
re_path(r'^registration_list/$', views.RegistrationListView.as_view(), name='registrations'),
re_path(r'^stuck_registration_list/$', views.StuckRegistrationListView.as_view(), name='stuck-registrations'),
re_path(r'^ia_backlog_list/$', views.RegistrationBacklogListView.as_view(), name='ia-backlog-list'),
Expand Down
39 changes: 39 additions & 0 deletions admin/nodes/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
NodeLog,
AbstractNode,
Registration,
RegistrationApproval,
SpamStatus
)
from osf.models.admin_log_entry import (
Expand All @@ -47,12 +48,15 @@
)
from osf.utils.permissions import ADMIN

from scripts.approve_registrations import approve_past_pendings

from website import settings, search


class NodeMixin(PermissionRequiredMixin):

def get_object(self):
breakpoint()
return AbstractNode.objects.filter(
guids___id=self.kwargs['guid']
).annotate(
Expand Down Expand Up @@ -335,6 +339,41 @@ def get_queryset(self):
return Registration.find_doi_backlog().annotate(guid=F('guids___id'))


class ApprovalBacklogListView(RegistrationListView):
""" Allows authorized users to view a list of registrations that have not yet been approved.
"""
template_name = 'nodes/registration_approval_list.html'
permission_required = 'osf.view_registrationapproval'

def get_queryset(self):
# Django template does not like attributes with underscores for some reason, so we annotate.
return RegistrationApproval.find_approval_backlog()

def get_context_data(self, **kwargs):
queryset = self.get_queryset()
page_size = self.get_paginate_by(queryset)
paginator, page, queryset, is_paginated = self.paginate_queryset(queryset, page_size)
return {
'queryset': queryset,
'page': page,
}


class ConfirmApproveBacklogView(RegistrationListView):
template_name = 'nodes/registration_approval_list.html'
permission_required = 'osf.view_registrationapproval'

def get_success_url(self):
return reverse('nodes:approval-backlog-list')

def post(self, request, *args, **kwargs):
data = dict(request.POST)
data.pop('csrfmiddlewaretoken', None)
approvals = RegistrationApproval.objects.filter(_id__in=list(data.keys()))
approve_past_pendings(approvals, dry_run=False)
return redirect(self.get_success_url())


class RegistrationUpdateEmbargoView(NodeMixin, View):
""" Allows authorized users to update the embargo of a registration.
"""
Expand Down
1 change: 1 addition & 0 deletions admin/templates/base.html
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,7 @@
<li><a href="{% url 'nodes:stuck-registrations' %}"><i class='fa fa-link'></i> <span>Stuck Registrations</span></a></li>
<li><a href="{% url 'nodes:ia-backlog-list' %}"><i class='fa fa-link'></i> <span>IA Backlog</span></a></li>
<li><a href="{% url 'nodes:doi-backlog-list' %}"><i class='fa fa-link'></i> <span>DOI Backlog</span></a></li>
<li><a href="{% url 'nodes:approval-backlog-list' %}"><i class='fa fa-link'></i> <span>Approval Backlog</span></a></li>
</ul>
</div>
{% endif %}
Expand Down
22 changes: 22 additions & 0 deletions admin/templates/nodes/approve_modal.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
<a data-toggle="modal" data-target="#confirmApproveRegistrationListModal" class="btn btn-success">
Approve
</a>
<div class="modal" id="confirmApproveRegistrationListModal">
<div class="modal-dialog">
<div class="modal-content">
<form class="well" method="post" action="{% url 'nodes:confirm-approve-backlog-list' %}">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal">x</button>
<h3>Are you sure you want to approve these registrations?</h3>
</div>
{% csrf_token %}
<div class="modal-footer">
<input class="btn btn-success" type="submit" value="Confirm" />
<button type="button" class="btn btn-default" data-dismiss="modal">
Cancel
</button>
</div>
</form>
</div>
</div>
</div>
76 changes: 76 additions & 0 deletions admin/templates/nodes/registration_approval_list.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
{% extends "base.html" %}
{% load render_bundle from webpack_loader %}
{% load node_extras %}

{% load static %}
{% block title %}
<title>Registrations to be approved</title>
{% endblock title %}
{% block content %}
<h2>List of registration requests that are waiting for approval</h2>
{% if perms.osf.change_registrationapproval %}
<form action="{% url 'nodes:confirm-approve-backlog-list' %}" method="post">
{% csrf_token %}
{% endif %}
{% include "util/pagination.html" with items=page status=status %}
<table class="table table-striped table-hover table-responsive">
<thead>
<tr>
<th>
<input type="checkbox" onclick="toggle(this)">
<script language="javascript">
function toggle(source) {
var checkboxes = document.getElementsByClassName('selection');
for (var i in checkboxes) {
checkboxes[i].checked = source.checked;
}
}
</script>
</th>
<th>ID</th>
<th>Title</th>
<th>Date created</th>
<th>Initiated By</th>
<th>State</th>
<th>Initiation Date</th>
<th>End Date</th>
</tr>
</thead>
<tbody>
{% for approval in queryset %}
<tr>
{% if perms.osf.change_registrationapproval %}
<td>
<input name="{{approval.guid}}" class="selection" type="checkbox"/>
</td>
{% endif %}
<td>
{{ approval.guid }}
</td>
<td>
{{ approval.registrations.first.title }}
</td>
<td>
{{ approval.created| date }}
</td>
<td>
{{ approval.initiated_by }}
</td>
<td>
{{ approval.state }}
</td>
<td>
{{ approval.initiation_date }}
</td>
<td>
{{ approval.end_date }}
</td>
</tr>
{% endfor %}
</tbody>
</table>
{% if perms.osf.change_registrationapproval %}
{% include 'nodes/approve_modal.html'%}
</form>
{% endif %}
{% endblock content %}
12 changes: 12 additions & 0 deletions osf/models/sanctions.py
Original file line number Diff line number Diff line change
Expand Up @@ -769,6 +769,18 @@ class RegistrationApproval(SanctionCallbackMixin, EmailApprovableSanction):

initiated_by = models.ForeignKey(settings.AUTH_USER_MODEL, null=True, blank=True, on_delete=models.CASCADE)

@staticmethod
def find_approval_backlog():
"""
These are registration requests that are waiting for approval within REGISTRATION_APPROVAL_TIME time
"""
return RegistrationApproval.objects.filter(
state=RegistrationApproval.UNAPPROVED,
end_date__gte=timezone.now()
).annotate(
guid=models.F('_id')
).order_by('-initiation_date')

def _get_registration(self):
return self.registrations.first()

Expand Down
15 changes: 9 additions & 6 deletions scripts/approve_registrations.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,12 +27,7 @@
logging.basicConfig(level=logging.INFO)


def main(dry_run=True):
approvals_past_pending = models.RegistrationApproval.objects.filter(
state=models.RegistrationApproval.UNAPPROVED,
initiation_date__lt=timezone.now() - settings.REGISTRATION_APPROVAL_TIME
)

def approve_past_pendings(approvals_past_pending, dry_run=True):
for registration_approval in approvals_past_pending:
if dry_run:
logger.warning('Dry run mode')
Expand Down Expand Up @@ -73,6 +68,14 @@ def main(dry_run=True):
transaction.savepoint_rollback(sid)


def main(dry_run=True):
approvals_past_pending = models.RegistrationApproval.objects.filter(
state=models.RegistrationApproval.UNAPPROVED,
initiation_date__lt=timezone.now() - settings.REGISTRATION_APPROVAL_TIME
)
approve_past_pendings(approvals_past_pending, dry_run)


@celery_app.task(name='scripts.approve_registrations')
def run_main(dry_run=True):
if not dry_run:
Expand Down

0 comments on commit a1e906a

Please sign in to comment.