Skip to content

Commit

Permalink
Merge to update production with latest version (#328)
Browse files Browse the repository at this point in the history
* Fix for NODE_ENV value that was failing on production npm build.

* [#299] Public actions view (#311)

* [#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.

* [313] Adding public actions feed to landing app (#314)

* [313] Adding public actions feed to landing app.

* [313] Parsing actions data on sroll (WIP).

* [#313] Done parsing actions data on scroll. WIP on modal+styles.

* [#313] Adding API endpoint for public action details.

* [#313] Adding styles and translations for principles and getting public action data from API (WIP details modal).

* [#313] Displaying public action details in a modal (WIP).

* [#313] Completing public action details in a modal.

* [#313] Removing partners involved from public actions (list+details).

* [#319] Fix to partner edit data fetch. (#321)

* [#206] Replacing the download/export balance button by print (#325)

Co-authored-by: Diego Calero <dcalero@fiqus.coop>
  • Loading branch information
nicolasdimarco and diegomanuel committed Aug 30, 2021
1 parent dd6026f commit 6ac4526
Show file tree
Hide file tree
Showing 30 changed files with 19,857 additions and 996 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'},
),
]
13 changes: 9 additions & 4 deletions api/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -158,7 +158,12 @@ 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]

@classmethod
def get_public_action(cls, id):
return cls.objects.filter(pk=id, public=True).select_related('cooperative')[0]
15 changes: 15 additions & 0 deletions api/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,9 @@ class Meta:
model = SDGObjective
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 Expand Up @@ -113,6 +115,19 @@ def update(self, instance, validated_data):
instance.save()
return instance

class PublicActionSerializer(serializers.ModelSerializer):
cooperative_name = serializers.CharField(source='cooperative', read_only=True)
principle_name_key = serializers.CharField(source='principle', read_only=True)
principles = PrincipleSerializer(many=True)
sustainable_development_goals = SustainableDevelopmentGoalSerializer(many=True)
invested_money = BlankableDecimalField(max_digits=19, decimal_places=2, required=False)
invested_hours = BlankableDecimalField(max_digits=19, decimal_places=2, required=False)

class Meta:
model = Action
ordering = ['-date']
exclude = ['partners_involved']


class PeriodSerializer(serializers.ModelSerializer):
class Meta:
Expand Down
82 changes: 75 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,64 @@ 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):
self.client.force_authenticate(None)
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_public_action_details(self):
self.client.force_authenticate(None)
public = self.create_action(name="test action PUBLIC", public=True)
private = self.create_action(name="test action PRIVATE", public=False)
response = self.client.get(reverse('public_action_detail', kwargs={"id": public.pk}))
self.assertEqual(response.status_code, status.HTTP_200_OK)
self.assertEqual(response.data["action"]["id"], public.pk)
self.assertEqual(response.data["action"]["name"], "test action PUBLIC")
response = self.client.get(reverse('public_action_detail', kwargs={"id": private.pk}))
self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND)
self.assertEqual(response.data, "PUBLIC_ACTION_NOT_FOUND")

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
66 changes: 32 additions & 34 deletions api/views/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
get_all_principles_data_for_current_partner
from api.models import Principle, Action, Period, Cooperative, Partner, MainPrinciple, \
SustainableDevelopmentGoal
from api.serializers import PrincipleSerializer, ActionSerializer, PeriodSerializer, CooperativeSerializer, \
from api.serializers import PrincipleSerializer, ActionSerializer, PublicActionSerializer, PeriodSerializer, CooperativeSerializer, \
PartnerSerializer, MyTokenObtainPairSerializer, ChangePasswordSerializer, MainPrincipleSerializer, \
ActionsByCoopSerializer
from django_filters import rest_framework as filters
Expand Down Expand Up @@ -319,12 +319,6 @@ def get_queryset(self):
queryset = Partner.objects.filter(cooperative=self.request.user.cooperative_id)
return queryset

def get_object(self, pk):
try:
return Partner.objects.get(pk=pk)
except Partner.DoesNotExist:
raise Http404

@transaction.atomic
def create(self, request):
data = request.data
Expand Down Expand Up @@ -374,11 +368,7 @@ def send_email():
return Response('Partner asked to be created', status=status.HTTP_200_OK)

def destroy(self, request, *args, **kwargs):
partner = self.get_object(self.kwargs.get("pk", None))
if not partner:
partner = self.get_object()
partner_serializer = PartnerSerializer(data=partner)

partner = self.get_object()
if partner.id == request.user.id or partner.cooperative.id != request.user.cooperative.id:
return Response(_("Current logged in user can not delete itself."), status=status.HTTP_404_NOT_FOUND)

Expand Down Expand Up @@ -502,6 +492,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 @@ -598,33 +589,40 @@ def list(self, request):


class PublicActionView(views.APIView):
# It's a public endpoint!
permission_classes = []

"""
get:
Returns public actions for this year, all the principles and actions by principles.
Returns public actions from all cooperatives for unauthenticated users.
If no action ID is given (to return its details), it will return a list following the parameters `more` and `limit`.
"""
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()

return Response({
'actions': action_serializer.data,
'principles': principle_serializer.data,
'actions_by_principles_data': actions_by_principles_data,
'cooperatives': coopearive_serializer.data
def get(self, request, id=0):
if id > 0:
return self._get_public_action_details(id)
return self._get_public_actions(request)

def _get_public_action_details(self, id):
try:
action = Action.get_public_action(id)
return Response({
'action': PublicActionSerializer(action, many=False).data
})
except:
return Response("PUBLIC_ACTION_NOT_FOUND", status=status.HTTP_404_NOT_FOUND)

def _get_public_actions(self, request):
try:
more = int(request.GET.get("more", 0))
limit = int(request.GET.get("limit", 10))
except ValueError:
more = 0
limit = 10
actions = Action.get_public_actions(more, limit)
return Response({
'actions': PublicActionSerializer(actions, many=True).data
})

@receiver(reset_password_token_created)
def password_reset_token_created(sender, instance, reset_password_token, *args, **kwargs):
Expand Down
3 changes: 2 additions & 1 deletion coobs/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,8 @@
path('docs/', include_docs_urls(title='COOBS API', permission_classes=[], public=False)),
path('admin/', admin.site.urls),
path('api/', include(router.urls)),
path('api/public-actions', PublicActionView.as_view(), name='public_actions'),
path('api/public-actions/', PublicActionView.as_view(), name='public_actions'),
path('api/public-actions/<int:id>', PublicActionView.as_view(), name='public_action_detail'),
path('api/token/', MyTokenObtainPairView.as_view(), name='token_obtain_pair'),
path('api/token/refresh/', TokenRefreshView.as_view(), name='token_refresh'),
path('api/token/verify/', TokenVerifyView.as_view(), name='token_verify'),
Expand Down
Loading

0 comments on commit 6ac4526

Please sign in to comment.