Skip to content

Commit

Permalink
[#299] Public actions view (#311)
Browse files Browse the repository at this point in the history
* [#299] Updating deps.

* [#299] Making to work API endpoint + tests.

* [#299] Adding a simple vue screen to display public actions (WIP).

* [#299] Creating infinite-scroll VUE component and making it to work with the API endpoint data.

* Using api service to get balance and fixing parse error on failed response.

* [#299] Making the public actions feed to work from within the app. It should be improved visually in another issue/PR!

* Adding model migration.

* Final adjustments.

* [#299] Removing description column.
  • Loading branch information
diegomanuel committed Apr 19, 2021
1 parent 7c19b90 commit 376f263
Show file tree
Hide file tree
Showing 19 changed files with 19,222 additions and 857 deletions.
21 changes: 21 additions & 0 deletions api/migrations/0014_auto_20210415_2026.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# Generated by Django 2.2.20 on 2021-04-15 20:26

from django.db import migrations


class Migration(migrations.Migration):

dependencies = [
('api', '0013_auto_20201001_1341'),
]

operations = [
migrations.AlterModelOptions(
name='partner',
options={'ordering': ['first_name', 'last_name'], 'verbose_name': 'partner'},
),
migrations.AlterModelOptions(
name='period',
options={'ordering': ['date_from'], 'verbose_name': 'period'},
),
]
9 changes: 5 additions & 4 deletions api/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -158,7 +158,8 @@ def get_current_actions(cls, cooperative, date_from, date_to, user_id = None):
return qs

@classmethod
def get_public_actions(cls, date_from, date_to):
qs = cls.objects.filter(principles__visible=True, date__gte=date_from, date__lte=date_to)
qs = qs.annotate(principles_num = Count('id'))
return qs
def get_public_actions(cls, more=0, limit=10):
start = more * limit
end = start + limit
query = cls.objects.filter(public=True).select_related('cooperative').order_by('-date', '-id')
return query[start:end]
1 change: 1 addition & 0 deletions api/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ class Meta:
fields = "__all__"

class ActionSerializer(serializers.ModelSerializer):
cooperative_name = serializers.CharField(source='cooperative', read_only=True)
principle_name_key = serializers.CharField(source='principle', read_only=True)
partners_involved = PartnerInvolvedSerializer(many=True)
principles = PrincipleSerializer(many=True)
Expand Down
69 changes: 62 additions & 7 deletions api/tests/test_views.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
import json
import unittest
import random
from datetime import datetime, date
from datetime import datetime, date, timedelta
from django.contrib.auth import get_user_model
from rest_framework import status
from rest_framework.test import APIClient
Expand Down Expand Up @@ -72,6 +72,7 @@ def test_create_invalid_user_unsuccessfull(self):
response = self.client.post(self.list_url, invalid_payload)
self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)


class PartnerTest(TestCase):
""" Test module for partners """
list_url = reverse('Partner-list')
Expand Down Expand Up @@ -136,6 +137,7 @@ class ActionTest(TestCase):
""" Test module for actions """

list_url = reverse('Action-list')
public_actions_url = reverse('public_actions')

@classmethod
def create_main_principles(cls):
Expand All @@ -150,26 +152,37 @@ def setUpTestData(cls):
cls.main_principles = cls.create_main_principles()
cls.principles = []
for main_pple in cls.main_principles:
cls.principles.append(Principle.objects.create(cooperative=cls.coop, main_principle=main_pple, description=f"principle {main_pple.id}", custom_description=f"principle {main_pple.id}"))
cls.principles.append(Principle.objects.create(cooperative=cls.coop, main_principle=main_pple, description=f"principle {main_pple.id}", custom_description=f"principle {main_pple.id}"))
#FIXME por qué NO puedo mover el resto de setUp acá?

def create_action(self, name, description=None, public=True, date=datetime.today(), coop=None):
return Action.objects.create(
cooperative=coop if coop != None else self.coop,
name=name,
description=description,
public=public,
date=date
)

def setUp(self):
self.client = APIClient()
self.user = get_user_model().objects.create_user("test_user@mail.com", "password")
self.user.cooperative = self.coop
self.client.force_authenticate(self.user)
self.action = Action.objects.create(name="test action", cooperative=self.coop)

def test_retrieve_actions_list(self):
self.create_action(name="action 1")
self.create_action(name="action 2")
response = self.client.get(self.list_url)
actions = Action.objects.all()
serializer = ActionSerializer(actions, many=True)
self.assertEqual(response.status_code, status.HTTP_200_OK)
self.assertEqual(len(response.data['results']), len(serializer.data))
self.assertEqual(len(response.data['results']), 2)
self.assertEqual(len(serializer.data), 2)

def test_retrieve_actions_list_with_filter_contents(self):
Action.objects.create(name="find my name", description="find my description", cooperative=self.coop)
Action.objects.create(name="don't find me", description="don't find me", cooperative=self.coop)
self.create_action(name="find my name", description="find my description")
self.create_action(name="don't find me", description="don't find me")
response = self.client.get(f"{self.list_url}?contents=my%20name")
self.assertEqual(response.status_code, status.HTTP_200_OK)
self.assertEqual(len(response.data['results']), 1)
Expand All @@ -182,9 +195,51 @@ def test_retrieve_actions_list_with_filter_contents(self):
response = self.client.get(f"{self.list_url}?contents=me")
self.assertEqual(response.status_code, status.HTTP_200_OK)
self.assertEqual(len(response.data['results']), 2)

def test_retrieve_public_actions_list(self):
for idx in range(1, 101):
date = datetime.today().date() - timedelta(days=idx)
self.create_action(name="Action {}!".format(idx), public=idx % 2 == 0, date=date)

response = self.client.get(self.public_actions_url)
self.assertEqual(response.status_code, status.HTTP_200_OK)
self.assertEqual(len(response.data["actions"]), 10)
self.assertEqual(response.data["actions"][0]["name"], "Action 2!")
self.assertEqual(response.data["actions"][9]["name"], "Action 20!")

response = self.client.get(f"{self.public_actions_url}?more=wrong&limit=wrong!")
self.assertEqual(response.status_code, status.HTTP_200_OK)
self.assertEqual(len(response.data["actions"]), 10)
self.assertEqual(response.data["actions"][0]["name"], "Action 2!")
self.assertEqual(response.data["actions"][9]["name"], "Action 20!")

response = self.client.get(f"{self.public_actions_url}?more=0&limit=5")
self.assertEqual(response.status_code, status.HTTP_200_OK)
self.assertEqual(len(response.data["actions"]), 5)
self.assertEqual(response.data["actions"][0]["name"], "Action 2!")
self.assertEqual(response.data["actions"][4]["name"], "Action 10!")

response = self.client.get(f"{self.public_actions_url}?more=1&limit=5")
self.assertEqual(response.status_code, status.HTTP_200_OK)
self.assertEqual(len(response.data["actions"]), 5)
self.assertEqual(response.data["actions"][0]["name"], "Action 12!")
self.assertEqual(response.data["actions"][4]["name"], "Action 20!")

response = self.client.get(f"{self.public_actions_url}?more=1&limit=20")
self.assertEqual(response.status_code, status.HTTP_200_OK)
self.assertEqual(len(response.data["actions"]), 20)
self.assertEqual(response.data["actions"][0]["name"], "Action 42!")
self.assertEqual(response.data["actions"][19]["name"], "Action 80!")

response = self.client.get(f"{self.public_actions_url}?more=2&limit=20")
self.assertEqual(response.status_code, status.HTTP_200_OK)
self.assertEqual(len(response.data["actions"]), 10)
self.assertEqual(response.data["actions"][0]["name"], "Action 82!")
self.assertEqual(response.data["actions"][9]["name"], "Action 100!")

def test_retrieve_action(self):
response = self.client.get(reverse('Action-detail', kwargs={"pk": self.action.pk}))
action = self.create_action(name="test action")
response = self.client.get(reverse('Action-detail', kwargs={"pk": action.pk}))
self.assertEqual(response.status_code, status.HTTP_200_OK)
self.assertEqual(response.data["name"], "test action")

Expand Down
29 changes: 10 additions & 19 deletions api/views/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -502,6 +502,7 @@ def list(self, request):
'total_invested': total_invested, 'totalHoursInvested': totalHoursInvested})


# @TODO This view might be removed if we don't plan to display an actions ranking in the future.
class ActionsRankingView(viewsets.ViewSet):
"""
list:
Expand Down Expand Up @@ -603,28 +604,18 @@ class PublicActionView(views.APIView):
Returns public actions for this year, all the principles and actions by principles.
"""
permission_classes = []
serializer_class = ActionSerializer

def get(self, request):
starting_day_of_current_year = datetime.now().date().replace(month=1, day=1)
ending_day_of_current_year = datetime.now().date().replace(month=12, day=31)
action_data = Action.get_public_actions(starting_day_of_current_year, ending_day_of_current_year).order_by('date')
action_serializer = ActionSerializer(action_data, many=True)

cooperative_data = Cooperative.objects.filter(is_active=True)
coopearive_serializer = CooperativeSerializer(cooperative_data, many=True)
principle_data = Principle.objects.filter(visible=True)
principle_serializer = PrincipleSerializer(principle_data, many=True)

actions_by_principles_data = Action.objects.filter(principles__visible=True).values('principles').annotate(total=Count('principles')).order_by()

try:
more = int(request.GET.get("more", 0))
limit = int(request.GET.get("limit", 10))
except ValueError:
more = 0
limit = 10
action_data = Action.get_public_actions(more, limit)
return Response({
'actions': action_serializer.data,
'principles': principle_serializer.data,
'actions_by_principles_data': actions_by_principles_data,
'cooperatives': coopearive_serializer.data
})
'actions': ActionSerializer(action_data, many=True).data
})

@receiver(reset_password_token_created)
def password_reset_token_created(sender, instance, reset_password_token, *args, **kwargs):
Expand Down
9 changes: 9 additions & 0 deletions frontend/assets/css/custom.css
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,15 @@ th.active .arrow {
opacity: 1;
}

.text-truncate-wrap {
white-space: normal;
}

.infinite-scroll table tfoot td {
font-style: italic;
font-size: smaller;
}

.arrow {
display: inline-block;
vertical-align: middle;
Expand Down
Loading

0 comments on commit 376f263

Please sign in to comment.