SciPost Code Repository

Skip to content
Snippets Groups Projects
Commit cf13b185 authored by Jean-Sébastien Caux's avatar Jean-Sébastien Caux
Browse files

Improve Subsidy allocation logic

parent b8499bcf
No related branches found
No related tags found
No related merge requests found
......@@ -29,6 +29,18 @@ class SubsidyAttachmentInline(admin.TabularInline):
@admin.register(Subsidy)
class SubsidyAdmin(admin.ModelAdmin):
list_display = [
"organization_name_short",
"orgtype_display",
"amount",
"status",
"date_from",
"date_until",
"total_compensations",
]
list_filter = [
"organization__orgtype",
]
inlines = [
SubsidyPaymentInline,
SubsidyAttachmentInline,
......@@ -43,6 +55,14 @@ class SubsidyAdmin(admin.ModelAdmin):
"organization__acronym",
]
@admin.display(description="org name short")
def organization_name_short(self, obj):
return obj.organization.name[:40]
@admin.display(description='org type')
def orgtype_display(self, obj):
return obj.organization.get_orgtype_display()
@admin.register(SubsidyAttachment)
class SubsidyAttachmentAdmin(admin.ModelAdmin):
......
......@@ -3,6 +3,7 @@ __license__ = "AGPL v3"
from .models import Subsidy, PubFrac
from .managers import PubFracQuerySet
"""
......@@ -34,21 +35,26 @@ The algorithms are implemented in the following order,
"""
def compensate(subsidy: Subsidy, pubfracs: PubFracQuerySet):
"""
Allocate subsidy to unallocated pubfracs in queryset, up to depletion.
"""
for pubfrac in pubfracs.uncompensated():
if pubfrac.cf_value <= subsidy.remainder:
pubfrac.compensated_by = subsidy
pubfrac.save()
def allocate_to_any_aff(subsidy: Subsidy):
"""
Allocate to PubFracs with affiliation to Subsidy-giver.
"""
max_year = subsidy.date_until.year if subsidy.date_until else subsidy.date_from.year
uncompensated_pubfracs = PubFrac.objects.filter(
organization=subsidy.organization,
pubfracs = subsidy.organization.pubfracs.filter(
publication__publication_date__year__gte=subsidy.date_from.year,
publication__publication_date__year__lte=max_year,
compensated_by__isnull=True,
)
for pubfrac in uncompensated_pubfracs.all():
if pubfrac.cf_value <= subsidy.remainder:
pubfrac.compensated_by = subsidy
pubfrac.save()
compensate(subsidy, pubfracs)
def allocate_to_all_aff(subsidy: Subsidy):
......@@ -56,18 +62,13 @@ def allocate_to_all_aff(subsidy: Subsidy):
Allocate to all PubFracs of Publications with at least one aff to Subsidy-giver.
"""
max_year = subsidy.date_until.year if subsidy.date_until else subsidy.date_from.year
uncompensated_pubfracs = PubFrac.objects.filter(
organization=subsidy.organization,
pubfracs = subsidy.organization.pubfracs.filter(
publication__publication_date__year__gte=subsidy.date_from.year,
publication__publication_date__year__lte=max_year,
)
for pubfrac in uncompensated_pubfracs.all():
for pubfrac in pubfracs.all():
# retrieve all uncompensated PubFracs for the relevant Publication
pubfracs_for_pub = PubFrac.objects.filter(
publication__doi_label=pubfrac.publication.doi_label,
compensated_by__isnull=True,
)
for pf in pubfracs_for_pub.all():
if pf.cf_value <= subsidy.remainder:
pf.compensated_by = subsidy
pf.save()
compensate(subsidy, pubfracs_for_pub)
......@@ -32,3 +32,11 @@ class SubsidyAttachmentQuerySet(models.QuerySet):
def orphaned(self):
return self.filter(subsidy__isnull=True)
class PubFracQuerySet(models.QuerySet):
def uncompensated(self):
return self.filter(compensated_by__isnull=True)
def compensated(self):
return self.exclude(compensated_by__isnull=True)
......@@ -6,6 +6,8 @@ from decimal import Decimal
from django.db import models
from ..managers import PubFracQuerySet
class PubFrac(models.Model):
"""
......@@ -43,6 +45,8 @@ class PubFrac(models.Model):
max_digits=16, decimal_places=3, blank=True, null=True
)
objects = PubFracQuerySet.as_manager()
class Meta:
unique_together = (("organization", "publication"),)
verbose_name = "PubFrac"
......
......@@ -10,8 +10,13 @@ from django.db.models import Sum
from django.urls import reverse
from django.utils.html import format_html
from ..constants import SUBSIDY_TYPES, SUBSIDY_TYPE_SPONSORSHIPAGREEMENT, SUBSIDY_STATUS
from ..managers import SubsidyQuerySet
from ..constants import (
SUBSIDY_TYPES,
SUBSIDY_TYPE_SPONSORSHIPAGREEMENT,
SUBSIDY_STATUS,
SUBSIDY_WITHDRAWN,
)
from ..managers import SubsidyQuerySet, PubFracQuerySet
class Subsidy(models.Model):
......@@ -180,6 +185,18 @@ class Subsidy(models.Model):
"""
return self.amount == self.payments.aggregate(Sum("amount"))["amount__sum"]
@property
def allocatable(self):
"""
Determine whether this Subsidy can be allocated.
"""
implemented_algorithms = [self.ALGORITHM_ANY_AFF, self.ALGORITHM_ALL_AFF]
return (
self.status != SUBSIDY_WITHDRAWN
and self.algorithm != self.ALGORITHM_RESERVES
and self.algorithm in implemented_algorithms
)
def allocate(self):
"""
Allocate the funds according to the algorithm specific by the instance.
......
......@@ -116,8 +116,10 @@
<h3 class="highlight">Expenditures compensated by this Subsidy</h3>
{% if 'edadmin' in user_roles %}
<a class="btn btn-primary" href="{% url 'finances:allocate_subsidy' subsidy_id=subsidy.id %}">Allocate this Subsidy</a>
{% if 'finadmin' in user_roles %}
{% if subsidy.allocatable %}
<a class="btn btn-primary" href="{% url 'finances:allocate_subsidy' subsidy_id=subsidy.id %}">Allocate this Subsidy</a>
{% endif %}
{% endif %}
<div class="row">
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment