mirror of
https://github.com/TandoorRecipes/recipes.git
synced 2026-01-03 13:19:16 -05:00
Introduce ical action on MealPlanViewSet to expose ical format for listing meal plans.
This commit is contained in:
@@ -14,8 +14,11 @@ class QueryParam(object):
|
|||||||
|
|
||||||
|
|
||||||
class QueryParamAutoSchema(AutoSchema):
|
class QueryParamAutoSchema(AutoSchema):
|
||||||
|
def is_query(self, path, method):
|
||||||
|
return is_list_view(path, method, self.view)
|
||||||
|
|
||||||
def get_path_parameters(self, path, method):
|
def get_path_parameters(self, path, method):
|
||||||
if not is_list_view(path, method, self.view):
|
if not self.is_query(path, method):
|
||||||
return super().get_path_parameters(path, method)
|
return super().get_path_parameters(path, method)
|
||||||
parameters = super().get_path_parameters(path, method)
|
parameters = super().get_path_parameters(path, method)
|
||||||
for q in self.view.query_params:
|
for q in self.view.query_params:
|
||||||
|
|||||||
@@ -5,12 +5,14 @@ import pytest
|
|||||||
from django.contrib import auth
|
from django.contrib import auth
|
||||||
from django.urls import reverse
|
from django.urls import reverse
|
||||||
from django_scopes import scope, scopes_disabled
|
from django_scopes import scope, scopes_disabled
|
||||||
|
from icalendar import Calendar
|
||||||
|
|
||||||
from cookbook.models import MealPlan, MealType
|
from cookbook.models import MealPlan, MealType
|
||||||
from cookbook.tests.factories import RecipeFactory
|
from cookbook.tests.factories import RecipeFactory
|
||||||
|
|
||||||
LIST_URL = 'api:mealplan-list'
|
LIST_URL = 'api:mealplan-list'
|
||||||
DETAIL_URL = 'api:mealplan-detail'
|
DETAIL_URL = 'api:mealplan-detail'
|
||||||
|
ICAL_URL = 'api:mealplan-ical'
|
||||||
|
|
||||||
|
|
||||||
# NOTE: auto adding shopping list from meal plan is tested in test_shopping_recipe as tests are identical
|
# NOTE: auto adding shopping list from meal plan is tested in test_shopping_recipe as tests are identical
|
||||||
@@ -32,6 +34,11 @@ def obj_2(space_1, recipe_1_s1, meal_type, u1_s1):
|
|||||||
return MealPlan.objects.create(recipe=recipe_1_s1, space=space_1, meal_type=meal_type, from_date=datetime.now(), to_date=datetime.now(),
|
return MealPlan.objects.create(recipe=recipe_1_s1, space=space_1, meal_type=meal_type, from_date=datetime.now(), to_date=datetime.now(),
|
||||||
created_by=auth.get_user(u1_s1))
|
created_by=auth.get_user(u1_s1))
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def obj_3(space_1, recipe_1_s1, meal_type, u1_s1):
|
||||||
|
return MealPlan.objects.create(recipe=recipe_1_s1, space=space_1, meal_type=meal_type, from_date=datetime.now() - timedelta(days=30), to_date=datetime.now() - timedelta(days=1),
|
||||||
|
created_by=auth.get_user(u1_s1))
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize("arg", [
|
@pytest.mark.parametrize("arg", [
|
||||||
['a_u', 403],
|
['a_u', 403],
|
||||||
@@ -163,3 +170,32 @@ def test_add_with_shopping(u1_s1, meal_type):
|
|||||||
)
|
)
|
||||||
|
|
||||||
assert len(json.loads(u1_s1.get(reverse('api:shoppinglistentry-list')).content)) == 10
|
assert len(json.loads(u1_s1.get(reverse('api:shoppinglistentry-list')).content)) == 10
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize("arg", [
|
||||||
|
[f'', 2],
|
||||||
|
[f'?from_date={datetime.now().strftime("%Y-%m-%d")}', 1],
|
||||||
|
[f'?to_date={(datetime.now() - timedelta(days=1)).strftime("%Y-%m-%d")}', 1],
|
||||||
|
[f'?from_date={(datetime.now() + timedelta(days=2)).strftime("%Y-%m-%d")}&to_date={(datetime.now() + timedelta(days=2)).strftime("%Y-%m-%d")}', 0],
|
||||||
|
])
|
||||||
|
def test_ical(arg, request, obj_1, obj_3, u1_s1):
|
||||||
|
r = u1_s1.get(f'{reverse(ICAL_URL)}{arg[0]}')
|
||||||
|
assert r.status_code == 200
|
||||||
|
cal = Calendar.from_ical(r.getvalue().decode('UTF-8'))
|
||||||
|
events = cal.walk('VEVENT')
|
||||||
|
assert len(events) == arg[1]
|
||||||
|
|
||||||
|
|
||||||
|
def test_ical_event(obj_1, u1_s1):
|
||||||
|
r = u1_s1.get(f'{reverse(ICAL_URL)}')
|
||||||
|
|
||||||
|
cal = Calendar.from_ical(r.getvalue().decode('UTF-8'))
|
||||||
|
events = cal.walk('VEVENT')
|
||||||
|
assert len(events) == 1
|
||||||
|
|
||||||
|
event = events[0]
|
||||||
|
assert int(event['uid']) == obj_1.id
|
||||||
|
assert event['summary'] == f'{obj_1.meal_type.name}: {obj_1.get_label()}'
|
||||||
|
assert event['description'] == obj_1.note
|
||||||
|
assert event.decoded('dtstart') == datetime.now().date()
|
||||||
|
assert event.decoded('dtend') == datetime.now().date()
|
||||||
@@ -1,88 +1,55 @@
|
|||||||
# from datetime import datetime, timedelta
|
from datetime import datetime, timedelta
|
||||||
#
|
|
||||||
# import pytest
|
import pytest
|
||||||
# from django.contrib import auth
|
from django.contrib import auth
|
||||||
# from django.urls import reverse
|
from django.urls import reverse
|
||||||
# from icalendar import Calendar
|
from icalendar import Calendar
|
||||||
#
|
|
||||||
# from cookbook.models import MealPlan, MealType
|
from cookbook.models import MealPlan, MealType
|
||||||
#
|
|
||||||
# BOUND_URL = 'api_get_plan_ical'
|
BOUND_URL = 'api_get_plan_ical'
|
||||||
# FROM_URL = 'api_get_plan_ical'
|
|
||||||
# FUTURE_URL = 'api_get_plan_ical'
|
|
||||||
#
|
@pytest.fixture()
|
||||||
#
|
def meal_type(space_1, u1_s1):
|
||||||
# @pytest.fixture()
|
return MealType.objects.get_or_create(name='test', space=space_1, created_by=auth.get_user(u1_s1))[0]
|
||||||
# def meal_type(space_1, u1_s1):
|
|
||||||
# return MealType.objects.get_or_create(name='test', space=space_1, created_by=auth.get_user(u1_s1))[0]
|
|
||||||
#
|
@pytest.fixture()
|
||||||
#
|
def obj_1(space_1, recipe_1_s1, meal_type, u1_s1):
|
||||||
# @pytest.fixture()
|
return MealPlan.objects.create(recipe=recipe_1_s1, space=space_1, meal_type=meal_type, from_date=datetime.now(), to_date=datetime.now(),
|
||||||
# def obj_1(space_1, recipe_1_s1, meal_type, u1_s1):
|
created_by=auth.get_user(u1_s1))
|
||||||
# return MealPlan.objects.create(recipe=recipe_1_s1, space=space_1, meal_type=meal_type, from_date=datetime.now(), to_date=datetime.now(),
|
|
||||||
# created_by=auth.get_user(u1_s1))
|
|
||||||
#
|
@pytest.fixture
|
||||||
#
|
def obj_2(space_1, recipe_1_s1, meal_type, u1_s1):
|
||||||
# @pytest.fixture
|
return MealPlan.objects.create(recipe=recipe_1_s1, space=space_1, meal_type=meal_type, from_date=datetime.now()+timedelta(days=30), to_date=datetime.now()+timedelta(days=30),
|
||||||
# def obj_2(space_1, recipe_1_s1, meal_type, u1_s1):
|
created_by=auth.get_user(u1_s1))
|
||||||
# return MealPlan.objects.create(recipe=recipe_1_s1, space=space_1, meal_type=meal_type, from_date=datetime.now()+timedelta(days=30), to_date=datetime.now()+timedelta(days=30),
|
|
||||||
# created_by=auth.get_user(u1_s1))
|
@pytest.fixture
|
||||||
#
|
def obj_3(space_1, recipe_1_s1, meal_type, u1_s1):
|
||||||
# @pytest.fixture
|
return MealPlan.objects.create(recipe=recipe_1_s1, space=space_1, meal_type=meal_type, from_date=datetime.now()+timedelta(days=-30), to_date=datetime.now()+timedelta(days=-1),
|
||||||
# def obj_3(space_1, recipe_1_s1, meal_type, u1_s1):
|
created_by=auth.get_user(u1_s1))
|
||||||
# return MealPlan.objects.create(recipe=recipe_1_s1, space=space_1, meal_type=meal_type, from_date=datetime.now()+timedelta(days=-30), to_date=datetime.now()+timedelta(days=-1),
|
|
||||||
# created_by=auth.get_user(u1_s1))
|
|
||||||
#
|
@pytest.mark.parametrize("arg", [
|
||||||
#
|
['a_u', 403],
|
||||||
# @pytest.mark.parametrize("arg", [
|
['g1_s1', 403],
|
||||||
# ['a_u', 403],
|
['u1_s1', 200],
|
||||||
# ['g1_s1', 403],
|
['a1_s1', 200],
|
||||||
# ['u1_s1', 200],
|
])
|
||||||
# ['a1_s1', 200],
|
def test_permissions(arg, request):
|
||||||
# ])
|
c = request.getfixturevalue(arg[0])
|
||||||
# def test_permissions(arg, request):
|
from_date_slug = (datetime.now()+timedelta(days=-1)).strftime("%Y-%m-%d")
|
||||||
# c = request.getfixturevalue(arg[0])
|
to_date_slug = (datetime.now()+timedelta(days=1)).strftime("%Y-%m-%d")
|
||||||
# assert c.get(reverse(FUTURE_URL)).status_code == arg[1]
|
assert c.get(reverse(BOUND_URL, kwargs={'from_date': from_date_slug, 'to_date': to_date_slug})).status_code == arg[1]
|
||||||
#
|
|
||||||
# def test_future(obj_1, obj_2, obj_3, u1_s1):
|
def test_bound(obj_1, obj_2, obj_3, u1_s1):
|
||||||
# r = u1_s1.get(reverse(FUTURE_URL))
|
from_date_slug = (datetime.now()+timedelta(days=-1)).strftime("%Y-%m-%d")
|
||||||
# assert r.status_code == 200
|
to_date_slug = (datetime.now()+timedelta(days=1)).strftime("%Y-%m-%d")
|
||||||
#
|
r = u1_s1.get(reverse(BOUND_URL, kwargs={'from_date': from_date_slug, 'to_date': to_date_slug}))
|
||||||
# cal = Calendar.from_ical(r.getvalue().decode('UTF-8'))
|
assert r.status_code == 200
|
||||||
# events = cal.walk('VEVENT')
|
|
||||||
# assert len(events) == 2
|
cal = Calendar.from_ical(r.getvalue().decode('UTF-8'))
|
||||||
#
|
events = cal.walk('VEVENT')
|
||||||
# def test_from(obj_1, obj_2, obj_3, u1_s1):
|
assert len(events) == 1
|
||||||
# from_date_slug = (datetime.now()+timedelta(days=1)).strftime("%Y-%m-%d")
|
|
||||||
# r = u1_s1.get(reverse(FROM_URL, kwargs={'from_date': from_date_slug}))
|
|
||||||
# assert r.status_code == 200
|
|
||||||
#
|
|
||||||
# cal = Calendar.from_ical(r.getvalue().decode('UTF-8'))
|
|
||||||
# events = cal.walk('VEVENT')
|
|
||||||
# assert len(events) == 1
|
|
||||||
#
|
|
||||||
# def test_bound(obj_1, obj_2, obj_3, u1_s1):
|
|
||||||
# from_date_slug = (datetime.now()+timedelta(days=-1)).strftime("%Y-%m-%d")
|
|
||||||
# to_date_slug = (datetime.now()+timedelta(days=1)).strftime("%Y-%m-%d")
|
|
||||||
# r = u1_s1.get(reverse(BOUND_URL, kwargs={'from_date': from_date_slug, 'to_date': to_date_slug}))
|
|
||||||
# assert r.status_code == 200
|
|
||||||
#
|
|
||||||
# cal = Calendar.from_ical(r.getvalue().decode('UTF-8'))
|
|
||||||
# events = cal.walk('VEVENT')
|
|
||||||
# assert len(events) == 1
|
|
||||||
#
|
|
||||||
# def test_event(obj_1, u1_s1):
|
|
||||||
# from_date_slug = (datetime.now()+timedelta(days=-1)).strftime("%Y-%m-%d")
|
|
||||||
# to_date_slug = (datetime.now()+timedelta(days=1)).strftime("%Y-%m-%d")
|
|
||||||
# r = u1_s1.get(reverse(BOUND_URL, kwargs={'from_date': from_date_slug, 'to_date': to_date_slug}))
|
|
||||||
#
|
|
||||||
# cal = Calendar.from_ical(r.getvalue().decode('UTF-8'))
|
|
||||||
# events = cal.walk('VEVENT')
|
|
||||||
# assert len(events) == 1
|
|
||||||
#
|
|
||||||
# event = events[0]
|
|
||||||
# assert int(event['uid']) == obj_1.id
|
|
||||||
# assert event['summary'] == f'{obj_1.meal_type.name}: {obj_1.get_label()}'
|
|
||||||
# assert event['description'] == obj_1.note
|
|
||||||
# assert event.decoded('dtstart') == datetime.now().date()
|
|
||||||
# assert event.decoded('dtend') == datetime.now().date()
|
|
||||||
|
|||||||
@@ -41,7 +41,7 @@ from recipe_scrapers._exceptions import NoSchemaFoundInWildMode
|
|||||||
from requests.exceptions import MissingSchema
|
from requests.exceptions import MissingSchema
|
||||||
from rest_framework import decorators, status, viewsets
|
from rest_framework import decorators, status, viewsets
|
||||||
from rest_framework.authtoken.views import ObtainAuthToken
|
from rest_framework.authtoken.views import ObtainAuthToken
|
||||||
from rest_framework.decorators import api_view, permission_classes
|
from rest_framework.decorators import action, api_view, permission_classes
|
||||||
from rest_framework.exceptions import APIException, PermissionDenied
|
from rest_framework.exceptions import APIException, PermissionDenied
|
||||||
from rest_framework.pagination import PageNumberPagination
|
from rest_framework.pagination import PageNumberPagination
|
||||||
from rest_framework.parsers import MultiPartParser
|
from rest_framework.parsers import MultiPartParser
|
||||||
@@ -804,6 +804,13 @@ class MealPlanViewSet(viewsets.ModelViewSet):
|
|||||||
queryset = queryset.filter(meal_type__in=meal_type)
|
queryset = queryset.filter(meal_type__in=meal_type)
|
||||||
|
|
||||||
return queryset
|
return queryset
|
||||||
|
|
||||||
|
@action(detail=False)
|
||||||
|
def ical(self, request):
|
||||||
|
from_date = self.request.query_params.get('from_date', None)
|
||||||
|
to_date = self.request.query_params.get('to_date', None)
|
||||||
|
return meal_plans_to_ical(self.get_queryset(), f'meal_plan_{from_date}-{to_date}.ics')
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class AutoPlanViewSet(viewsets.ViewSet):
|
class AutoPlanViewSet(viewsets.ViewSet):
|
||||||
@@ -1786,6 +1793,9 @@ def get_plan_ical(request, from_date=datetime.date.today(), to_date=None):
|
|||||||
if to_date is not None:
|
if to_date is not None:
|
||||||
queryset = queryset.filter(to_date__lte=to_date)
|
queryset = queryset.filter(to_date__lte=to_date)
|
||||||
|
|
||||||
|
return meal_plans_to_ical(queryset, f'meal_plan_{from_date}-{to_date}.ics')
|
||||||
|
|
||||||
|
def meal_plans_to_ical(queryset, filename):
|
||||||
cal = Calendar()
|
cal = Calendar()
|
||||||
|
|
||||||
for p in queryset:
|
for p in queryset:
|
||||||
@@ -1801,7 +1811,7 @@ def get_plan_ical(request, from_date=datetime.date.today(), to_date=None):
|
|||||||
cal.add_component(event)
|
cal.add_component(event)
|
||||||
|
|
||||||
response = FileResponse(io.BytesIO(cal.to_ical()))
|
response = FileResponse(io.BytesIO(cal.to_ical()))
|
||||||
response["Content-Disposition"] = f'attachment; filename=meal_plan_{from_date}-{to_date}.ics' # noqa: E501
|
response["Content-Disposition"] = f'attachment; filename={filename}' # noqa: E501
|
||||||
|
|
||||||
return response
|
return response
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user