Skip to content

Commit 13c0c73

Browse files
authored
Create a json output version of the open calls on the front page. (#4554)
Fixes #4555 Useful if an organisation wants to list the open calls on their web site.
1 parent deb7834 commit 13c0c73

File tree

8 files changed

+95
-2
lines changed

8 files changed

+95
-2
lines changed
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
# Open calls API endpoint
2+
3+
At `/api/v2/open-calls.json` that same lists and order of open calls that appear on the front page are available in JSON format.
4+
5+
```json
6+
[
7+
{
8+
"description": "The application description if any.",
9+
"image": "/path/to/application/image.jpeg",
10+
"next_deadline": "1970-01-01",
11+
"title": "Title of the application",
12+
"weight": 1
13+
}
14+
]
15+
```
16+
17+
This should make it easy to add a list of open calls to the organisations main web site.

hypha/api/__init__.py

Whitespace-only changes.

hypha/api/urls.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
from django.urls import include, path
2+
3+
from .v2 import urls as v2_urls
4+
5+
app_name = "api"
6+
7+
urlpatterns = [
8+
path("v2/", include(v2_urls)),
9+
]

hypha/api/v2/__init__.py

Whitespace-only changes.

hypha/api/v2/urls.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
from django.urls import path
2+
3+
from .views import open_calls_json
4+
5+
app_name = "v2"
6+
7+
urlpatterns = [
8+
path("open-calls.json", open_calls_json),
9+
]

hypha/api/v2/views.py

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
from django.conf import settings
2+
from django.http import JsonResponse
3+
from django_ratelimit.decorators import ratelimit
4+
5+
from hypha.apply.funds.models import ApplicationBase, LabBase
6+
7+
8+
@ratelimit(key="ip", rate=settings.DEFAULT_RATE_LIMIT)
9+
def open_calls_json(request):
10+
"""Open calls in JSON format.
11+
12+
List open calls in JSON format, useful when you want to list open calls on an external site.
13+
"""
14+
rounds = ApplicationBase.objects.order_by_end_date().specific()
15+
labs = LabBase.objects.public().live().specific()
16+
all_funds = list(rounds) + list(labs)
17+
18+
# Only pass rounds/labs that are open & visible for the front page
19+
data = [
20+
fund.as_json
21+
for fund in all_funds
22+
if fund.open_round and fund.list_on_front_page
23+
]
24+
return JsonResponse(data, safe=False)

hypha/apply/funds/models/applications.py

Lines changed: 33 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
from datetime import date
22
from typing import Optional
33

4+
import nh3
45
from django import forms
56
from django.conf import settings
67
from django.contrib.postgres.fields import ArrayField
@@ -73,10 +74,40 @@ def order_by_end_date(self):
7374
return qs.order_by("end_date")
7475

7576

77+
class AsJsonMixin:
78+
@cached_property
79+
def as_json(self):
80+
# Clean the strings in title and description.
81+
title = nh3.clean(self.title, tags=set())
82+
description = nh3.clean(self.description, tags=set())
83+
# If image exist scale it down and convert to webp.
84+
image = (
85+
self.image.get_rendition("max-1200x1200|format-webp|webpquality-60").url
86+
if self.image
87+
else ""
88+
)
89+
# Make sure weight is an int.
90+
weight = int(self.weight)
91+
# If next deadline exist and is set to show, format it as standard iso date.
92+
try:
93+
next_deadline = (
94+
self.next_deadline().isoformat() if self.show_deadline else ""
95+
)
96+
except AttributeError:
97+
next_deadline = ""
98+
return {
99+
"title": title,
100+
"description": description,
101+
"image": image,
102+
"weight": weight,
103+
"next_deadline": next_deadline,
104+
}
105+
106+
76107
@method_decorator(
77108
ratelimit(key="ip", rate=settings.DEFAULT_RATE_LIMIT, method="POST"), name="serve"
78109
)
79-
class ApplicationBase(EmailForm, WorkflowStreamForm): # type: ignore
110+
class ApplicationBase(EmailForm, WorkflowStreamForm, AsJsonMixin): # type: ignore
80111
is_creatable = False
81112

82113
# Adds validation around forms & workflows. Isn't on Workflow class due to not displaying workflow field on Round
@@ -555,7 +586,7 @@ def serve(self, request, *args, **kwargs):
555586
@method_decorator(
556587
ratelimit(key="ip", rate=settings.DEFAULT_RATE_LIMIT, method="POST"), name="serve"
557588
)
558-
class LabBase(EmailForm, WorkflowStreamForm, SubmittableStreamForm): # type: ignore
589+
class LabBase(EmailForm, WorkflowStreamForm, SubmittableStreamForm, AsJsonMixin): # type: ignore
559590
is_creatable = False
560591
submission_class = ApplicationSubmission
561592

hypha/urls.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,13 @@
1717
from hypha.apply.utils.views import custom_wagtail_page_delete
1818
from hypha.home.views import home
1919

20+
from .api import urls as api_urls
21+
2022
urlpatterns = [
2123
path("", home, name="home"),
2224
path("apply/", include("hypha.apply.funds.urls", "apply")),
2325
path("activity/", include("hypha.apply.activity.urls", "activity")),
26+
path("api/", include(api_urls)),
2427
path("todo/", include("hypha.apply.todo.urls", "todo")),
2528
path("django-admin/", admin.site.urls),
2629
path(

0 commit comments

Comments
 (0)