feat(dailyrecipe and week recipe): [A] 增加dailyrecipe 每日菜单,增加 weekrecipe 接口
[A] 增加dailyrecipe 每日菜单,增加 weekrecipe 接口 Signed-off-by: Ching <loooching@gmail.com>
This commit is contained in:
parent
b4fa5d9519
commit
1dd97b9e54
27
recipe/migrations/0002_auto_20211002_1926.py
Normal file
27
recipe/migrations/0002_auto_20211002_1926.py
Normal file
@ -0,0 +1,27 @@
|
||||
# Generated by Django 3.2.6 on 2021-10-02 11:26
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('recipe', '0001_initial'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='recipe',
|
||||
name='recipe_type',
|
||||
field=models.CharField(default='meat', max_length=32),
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='DailyRecipe',
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('date', models.DateField()),
|
||||
('meal_type', models.CharField(default='supper', max_length=32)),
|
||||
('recipes', models.ManyToManyField(to='recipe.Recipe')),
|
||||
],
|
||||
),
|
||||
]
|
||||
@ -1,9 +1,101 @@
|
||||
import random
|
||||
from django.db import models
|
||||
from django.utils.timezone import now
|
||||
from utils import const
|
||||
import utils
|
||||
|
||||
|
||||
|
||||
# Create your models here.
|
||||
class Recipe(models.Model):
|
||||
name = models.CharField(max_length=128)
|
||||
recipe_type = models.CharField(max_length=32)
|
||||
recipe_type = models.CharField(max_length=32,
|
||||
default=const.RECIPE_TYPE_MEAT)
|
||||
note = models.TextField(null=True)
|
||||
rate = models.IntegerField(default=0)
|
||||
difficulty = models.IntegerField(default=0)
|
||||
|
||||
def serialize(self, verbose=False):
|
||||
data = {
|
||||
'id': self.id,
|
||||
'name': self.name,
|
||||
'recipe_type': self.recipe_type,
|
||||
}
|
||||
if verbose:
|
||||
data.update({
|
||||
'difficulty': self.difficulty,
|
||||
'rate': self.rate,
|
||||
'note': self.note,
|
||||
})
|
||||
|
||||
return data
|
||||
|
||||
|
||||
class DailyRecipe(models.Model):
|
||||
recipes = models.ManyToManyField(Recipe)
|
||||
date = models.DateField()
|
||||
meal_type = models.CharField(
|
||||
max_length=32,
|
||||
default=const.DAILY_RECIPE_MEAL_TYPE_SUPPER,)
|
||||
|
||||
def generate_recipe(self, prev_recipes=None, ignore_prev=False):
|
||||
if not prev_recipes:
|
||||
prev_recipes = []
|
||||
if ignore_prev:
|
||||
prev_recipes = []
|
||||
|
||||
recipes = []
|
||||
retry = 5
|
||||
|
||||
# meat
|
||||
for x in range(0,2):
|
||||
while True:
|
||||
recipe = Recipe.objects.filter(
|
||||
recipe_type=const.RECIPE_TYPE_MEAT,
|
||||
).order_by('?').first()
|
||||
if recipe.id not in recipes and recipe.id not in prev_recipes:
|
||||
recipes.append(recipe.id)
|
||||
break
|
||||
if retry <= 0:
|
||||
retry = 5
|
||||
break
|
||||
|
||||
# vegetable
|
||||
for x in range(0, 1):
|
||||
while True:
|
||||
recipe = Recipe.objects.filter(
|
||||
recipe_type=const.RECIPE_TYPE_VEGETABLE,
|
||||
).order_by('?').first()
|
||||
if recipe.id not in recipes and recipe.id not in prev_recipes:
|
||||
recipes.append(recipe.id)
|
||||
break
|
||||
retry -= 1
|
||||
if retry <= 0:
|
||||
retry = 5
|
||||
break
|
||||
|
||||
# soup
|
||||
if random.randint(0,2):
|
||||
recipe = Recipe.objects.filter(
|
||||
recipe_type=const.RECIPE_TYPE_SOUP,
|
||||
).order_by('?').first()
|
||||
# if recipe not in recipes and recipe not in prev_recipes:
|
||||
if recipe:
|
||||
recipes.append(recipe.id)
|
||||
|
||||
self.recipes.set(Recipe.objects.filter(id__in=recipes))
|
||||
|
||||
def serialize(self):
|
||||
data = {
|
||||
const.RECIPE_TYPE_MEAT: [],
|
||||
const.RECIPE_TYPE_VEGETABLE: [],
|
||||
const.RECIPE_TYPE_SOUP: [],
|
||||
}
|
||||
for key_, value_ in data.items():
|
||||
for recipe in self.recipes.filter(
|
||||
recipe_type=key_).order_by('id'):
|
||||
value_.append(recipe.serialize())
|
||||
|
||||
date = now()
|
||||
date.replace(year=self.date.year, month=self.date.month, day=self.date.day,)
|
||||
data['date'] = utils.timestamp_of(utils.day_start(date))
|
||||
return data
|
||||
|
||||
@ -8,3 +8,10 @@ class RecipeSerializer(serializers.HyperlinkedModelSerializer):
|
||||
class Meta:
|
||||
model = recipe.models.Recipe
|
||||
fields = '__all__'
|
||||
|
||||
|
||||
class WeekRecipeSerializer(serializers.HyperlinkedModelSerializer):
|
||||
|
||||
class Meta:
|
||||
model = recipe.models.DailyRecipe
|
||||
fields = '__all__'
|
||||
|
||||
@ -10,4 +10,5 @@ from recipe import views
|
||||
urlpatterns = [
|
||||
url(r'^recipe/(?P<pk>\d+)$', views.RecipeAPI.as_view(), name='recipe-detail'),
|
||||
url(r'^recipe/$', views.RecipeListAPI.as_view()),
|
||||
url(r'^week-recipe/$', views.WeekRecipeListAPI.as_view()),
|
||||
]
|
||||
|
||||
@ -1,11 +1,7 @@
|
||||
import datetime
|
||||
|
||||
from django.shortcuts import render
|
||||
|
||||
# Create your views here.
|
||||
|
||||
import django.views.generic
|
||||
from django.http import JsonResponse
|
||||
from django.urls import reverse
|
||||
|
||||
from django.utils.timezone import now, localtime
|
||||
from rest_framework import authentication, permissions, status, viewsets
|
||||
import rest_framework.generics
|
||||
from rest_framework.response import Response
|
||||
@ -34,3 +30,33 @@ class RecipeListAPI(rest_framework.generics.ListAPIView,
|
||||
queryset = recipe.models.Recipe.objects.all()
|
||||
serializer_class = recipe.serializers.RecipeSerializer
|
||||
|
||||
|
||||
class WeekRecipeListAPI(rest_framework.generics.ListAPIView,
|
||||
rest_framework.generics.CreateAPIView):
|
||||
|
||||
queryset = recipe.models.DailyRecipe.objects.all()
|
||||
serializer_class = recipe.serializers.WeekRecipeSerializer
|
||||
def post(self, request, *args, **kwargs):
|
||||
# Monday == 0 ... Sunday == 6
|
||||
today = localtime()
|
||||
recipes = []
|
||||
for x in range(0, 7-today.weekday()):
|
||||
daily_recipe, __ = recipe.models.DailyRecipe.objects.get_or_create(
|
||||
date=today + datetime.timedelta(days=1)
|
||||
)
|
||||
daily_recipe.generate_recipe()
|
||||
recipes.append(daily_recipe.recipes.values_list('id', flat=True))
|
||||
return Response({}, status=status.HTTP_201_CREATED, headers={})
|
||||
|
||||
def get(self, request, *args, **kwargs):
|
||||
today = localtime()
|
||||
week_start = (today - datetime.timedelta(days=today.weekday())).date()
|
||||
week_end = week_start + datetime.timedelta(days=6)
|
||||
daily_recipes = recipe.models.DailyRecipe.objects.filter(
|
||||
date__gte=week_start,
|
||||
date__lte=week_end,
|
||||
).order_by('date')
|
||||
data = []
|
||||
for daily_recipe in daily_recipes:
|
||||
data.append(daily_recipe.serialize())
|
||||
return Response(data)
|
||||
|
||||
118
utils/__init__.py
Normal file
118
utils/__init__.py
Normal file
@ -0,0 +1,118 @@
|
||||
# -*- coding: UTF-8 -*-
|
||||
import calendar
|
||||
from datetime import timedelta
|
||||
|
||||
from django.utils import timezone
|
||||
|
||||
|
||||
def timestamp_of(d):
|
||||
if hasattr(d, 'isoformat'):
|
||||
return calendar.timegm(d.utctimetuple())
|
||||
return None
|
||||
|
||||
|
||||
def now():
|
||||
return timezone.localtime(timezone.now())
|
||||
|
||||
|
||||
def midnight():
|
||||
return now().replace(hour=0, minute=0, second=0, microsecond=0)
|
||||
|
||||
|
||||
def is_today(tme):
|
||||
if day_of(tme) != day_of(midnight()):
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
def current_time_n(n):
|
||||
return now() - timedelta(days=n)
|
||||
|
||||
|
||||
def day_n(n):
|
||||
"""Midnight of N days ago."""
|
||||
return midnight() - timedelta(days=n)
|
||||
|
||||
|
||||
def day_of(d):
|
||||
"""Returns date part."""
|
||||
return str(d.date())
|
||||
|
||||
|
||||
def midnight_of(d):
|
||||
"""
|
||||
Args:
|
||||
d : datetime object
|
||||
Return:
|
||||
Midnight of datetime.
|
||||
"""
|
||||
d = timezone.localtime(d)
|
||||
return d.replace(hour=0, minute=0, second=0, microsecond=0)
|
||||
|
||||
|
||||
def day_start(date):
|
||||
""" 返回 date 这天的开始时间
|
||||
|
||||
Args:
|
||||
date: `Datetime` 对象
|
||||
|
||||
Returns:
|
||||
返回 date 这天的开始时间(0 点),`Datetime` 对象
|
||||
"""
|
||||
return timezone.localtime(date).replace(hour=0, minute=0, second=0,
|
||||
microsecond=0)
|
||||
|
||||
|
||||
def week_start(date):
|
||||
""" 返回 date 这天所在周的开始时间
|
||||
|
||||
Args:
|
||||
date: `Datetime` 对象
|
||||
|
||||
Returns:
|
||||
返回 date 这天所在周开始时间(周一),`Datetime` 对象
|
||||
"""
|
||||
return timezone.localtime(date) + dateutil.relativedelta.relativedelta(
|
||||
weekday=dateutil.relativedelta.MO(-1), hour=0, minute=0, second=0,
|
||||
microsecond=0)
|
||||
|
||||
|
||||
def month_start(d):
|
||||
""" 返回 date 这天所在月的开始时间
|
||||
|
||||
Args:
|
||||
date: `Datetime` 对象
|
||||
|
||||
Returns:
|
||||
返回 date 这天所在月的开始时间(1号),`Datetime` 对象
|
||||
"""
|
||||
return timezone.localtime(d) + dateutil.relativedelta.relativedelta(
|
||||
day=1, hour=0, minute=0, second=0, microsecond=0)
|
||||
|
||||
|
||||
def month_end(d):
|
||||
"""返回 date 这天所在月的结束时间,即最后一天的 23:59:59
|
||||
|
||||
Args:
|
||||
data: `Datetime` 对象
|
||||
|
||||
Returns:
|
||||
返回 date 这天所在月的最后一天的 23:59:59 的时间
|
||||
"""
|
||||
start = month_start(d)
|
||||
month_days = calendar.monthrange(start.year, start.month)[1]
|
||||
return start + timedelta(days=month_days, seconds=-1)
|
||||
|
||||
|
||||
def year_start(d):
|
||||
""" 返回 date 这天所在年的开始时间
|
||||
|
||||
Args:
|
||||
date: `Datetime` 对象
|
||||
|
||||
Returns:
|
||||
返回 date 这天所在年的开始时间(1月 1号),`Datetime` 对象
|
||||
"""
|
||||
return timezone.localtime(d) + dateutil.relativedelta.relativedelta(
|
||||
month=1, day=1, hour=0, minute=0, second=0, microsecond=0)
|
||||
|
||||
18
utils/const.py
Normal file
18
utils/const.py
Normal file
@ -0,0 +1,18 @@
|
||||
DAILY_RECIPE_MEAL_TYPE_BREAKFAST = 'breakfast'
|
||||
DAILY_RECIPE_MEAL_TYPE_LUNCH = 'lunch'
|
||||
DAILY_RECIPE_MEAL_TYPE_SUPPER = 'supper'
|
||||
DAILY_RECIPE_MEAL_TYPE_CHOICE = [
|
||||
DAILY_RECIPE_MEAL_TYPE_BREAKFAST,
|
||||
DAILY_RECIPE_MEAL_TYPE_LUNCH,
|
||||
DAILY_RECIPE_MEAL_TYPE_SUPPER,
|
||||
]
|
||||
|
||||
RECIPE_TYPE_MEAT = 'meat'
|
||||
RECIPE_TYPE_VEGETABLE = 'vegetable'
|
||||
RECIPE_TYPE_SOUP = 'soup'
|
||||
|
||||
RECIPE_TYPE_CHOICE = [
|
||||
RECIPE_TYPE_MEAT,
|
||||
RECIPE_TYPE_VEGETABLE,
|
||||
RECIPE_TYPE_SOUP,
|
||||
]
|
||||
Loading…
x
Reference in New Issue
Block a user