meal plan WIP

This commit is contained in:
vabene1111
2020-01-17 16:02:14 +01:00
parent 2afec837a4
commit 7449380434
13 changed files with 226 additions and 20 deletions

View File

@ -1,16 +1,27 @@
from dal_select2.widgets import ModelSelect2
from django import forms
from django.forms import widgets
from django.forms import widgets, SelectDateWidget
from django.utils.translation import gettext as _
from emoji_picker.widgets import EmojiPickerTextInput
from .models import *
class SelectWidget(widgets.Select):
class Media:
js = ('custom/js/form_select.js',)
class MultiSelectWidget(widgets.SelectMultiple):
class Media:
js = ('custom/js/form_multiselect.js',)
# yes there are some stupid browsers that still dont support this but i dont support people using these browsers
class DateWidget(forms.DateInput):
input_type = 'date'
class ExternalRecipeForm(forms.ModelForm):
file_path = forms.CharField(disabled=True, required=False)
storage = forms.ModelChoiceField(queryset=Storage.objects.all(), disabled=True, required=False)
@ -87,12 +98,6 @@ class StorageForm(forms.ModelForm):
}
class RecipeBookForm(forms.ModelForm):
class Meta:
model = RecipeBook
fields = ('name',)
class RecipeBookEntryForm(forms.ModelForm):
prefix = 'bookmark'
@ -125,3 +130,17 @@ class ImportRecipeForm(forms.ModelForm):
'file_uid': _('File ID'),
}
widgets = {'keywords': MultiSelectWidget}
class RecipeBookForm(forms.ModelForm):
class Meta:
model = RecipeBook
fields = ('name',)
class MealPlanForm(forms.ModelForm):
class Meta:
model = MealPlan
fields = ('recipe', 'meal', 'note', 'date')
widgets = {'recipe': SelectWidget, 'date': DateWidget}

View File

@ -1,6 +1,6 @@
from dal import autocomplete
from cookbook.models import Keyword, RecipeIngredients
from cookbook.models import Keyword, RecipeIngredients, Recipe
class KeywordAutocomplete(autocomplete.Select2QuerySetView):
@ -27,3 +27,16 @@ class IngredientsAutocomplete(autocomplete.Select2QuerySetView):
qs = qs.filter(name__istartswith=self.q)
return qs
class RecipeAutocomplete(autocomplete.Select2QuerySetView):
def get_queryset(self):
if not self.request.user.is_authenticated:
return Recipe.objects.none()
qs = Recipe.objects.all()
if self.q:
qs = qs.filter(name__icontains=self.q)
return qs

View File

@ -0,0 +1,27 @@
# Generated by Django 3.0.2 on 2020-01-17 14:55
from django.conf import settings
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
('cookbook', '0007_auto_20191226_0852'),
]
operations = [
migrations.CreateModel(
name='MealPlan',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('meal', models.CharField(choices=[('BREAKFAST', 'Breakfast'), ('LUNCH', 'Lunch'), ('DINNER', 'Dinner'), ('OTHER', 'Other')], default='BREAKFAST', max_length=128)),
('note', models.TextField(blank=True)),
('date', models.DateField()),
('recipe', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='cookbook.Recipe')),
('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
],
),
]

View File

@ -1,5 +1,5 @@
from django.contrib.auth.models import User
from django.utils.translation import gettext as _
from django.db import models
@ -115,3 +115,17 @@ class RecipeBookEntry(models.Model):
def __str__(self):
return self.recipe.name
class MealPlan(models.Model):
BREAKFAST = 'BREAKFAST'
LUNCH = 'LUNCH'
DINNER = 'DINNER'
OTHER = 'OTHER'
MEAL_TYPES = ((BREAKFAST, _('Breakfast')), (LUNCH, _('Lunch')), (DINNER, _('Dinner')), (OTHER, _('Other')),)
user = models.ForeignKey(User, on_delete=models.CASCADE)
recipe = models.ForeignKey(Recipe, on_delete=models.CASCADE)
meal = models.CharField(choices=MEAL_TYPES, max_length=128, default=BREAKFAST)
note = models.TextField(blank=True)
date = models.DateField()

View File

@ -0,0 +1,3 @@
$(document).ready(function () {
$('.selectwidget').select2();
});

View File

@ -75,6 +75,9 @@
<li class="nav-item">
<a class="nav-link" href="{% url 'view_books' %}"><i class="fas fa-bookmark"></i> {% trans 'Books' %}</a>
</li>
<li class="nav-item">
<a class="nav-link" href="{% url 'view_plan' %}"><i class="fas fa-calendar"></i> {% trans 'Meal-Plan' %}</a>
</li>
<li class="nav-item dropdown">
<a class="nav-link dropdown-toggle" href="#" id="navbarDropdownMenuLink" data-toggle="dropdown"
aria-haspopup="true" aria-expanded="false">

View File

@ -0,0 +1,76 @@
{% extends "base.html" %}
{% load i18n %}
{% block title %}{% trans 'Meal-Plan' %}{% endblock %}
{% block extra_head %}
{{ form.media }}
{% endblock %}
{% block content %}
<h3>
{% trans 'Meal-Plan' %} <a href="{% url 'new_plan' %}"><i class="fas fa-plus-circle"></i></a>
</h3>
<div class="row">
<div class="col-md-12">
<form>
<label>{% trans 'Week' %}
<input id="id_week" class="form-control" type="week" onchange="document.forms[0].submit()">
</label>
</form>
</div>
</div>
<br/>
<div class="row">
<div class="col-md-12 table-responsive">
<table class="table table-bordered">
<tr style="text-align: center">
<th id="th_day_1">?</th>
<th id="th_day_2">?</th>
<th id="th_day_3">?</th>
<th id="th_day_4">?</th>
<th id="th_day_5">?</th>
<th id="th_day_6">?</th>
<th id="th_day_7">?</th>
</tr>
<tr>
<td colspan="7" style="text-align: center"><h5>{% trans 'Breakfast' %}</h5></td>
</tr>
<tr>
<td id="td_breakfast_1"></td>
<td id="td_breakfast_2"></td>
<td id="td_breakfast_3"></td>
<td id="td_breakfast_4"></td>
<td id="td_breakfast_5"></td>
<td id="td_breakfast_6"></td>
<td id="td_breakfast_7"></td>
</tr>
<tr>
<td colspan="7" style="text-align: center"><h5>{% trans 'Lunch' %}</h5></td>
</tr>
<tr>
</tr>
<tr>
<td colspan="7" style="text-align: center"><h5>{% trans 'Dinner' %}</h5></td>
</tr>
<tr>
</tr>
<tr>
<td colspan="7" style="text-align: center"><h5>{% trans 'Other' %}</h5></td>
</tr>
<tr>
</tr>
</table>
</div>
</div>
{% endblock %}

View File

@ -7,6 +7,7 @@ from cookbook.helper import dal
urlpatterns = [
path('', views.index, name='index'),
path('books', views.books, name='view_books'),
path('plan', views.meal_plan, name='view_plan'),
path('view/recipe/<int:pk>', views.recipe_view, name='view_recipe'),
@ -15,6 +16,7 @@ urlpatterns = [
path('new/keyword/', new.KeywordCreate.as_view(), name='new_keyword'),
path('new/storage/', new.StorageCreate.as_view(), name='new_storage'),
path('new/book/', new.RecipeBookCreate.as_view(), name='new_book'),
path('new/plan/', new.MealPlanCreate.as_view(), name='new_plan'),
path('list/keyword', lists.keyword, name='list_keyword'),
path('list/import_log', lists.sync_log, name='list_import_log'),
@ -34,6 +36,7 @@ urlpatterns = [
path('edit/storage/<int:pk>/', edit.edit_storage, name='edit_storage'),
path('edit/comment/<int:pk>/', edit.CommentUpdate.as_view(), name='edit_comment'),
path('edit/recipe-book/<int:pk>/', edit.RecipeBookUpdate.as_view(), name='edit_recipe_book'),
path('edit/plan/<int:pk>/', edit.MealPlanUpdate.as_view(), name='edit_plan'),
path('redirect/delete/<slug:name>/<int:pk>/', edit.delete_redirect, name='redirect_delete'),
@ -46,6 +49,7 @@ urlpatterns = [
path('delete/comment/<int:pk>/', edit.CommentDelete.as_view(), name='delete_comment'),
path('delete/recipe-book/<int:pk>/', edit.RecipeBookDelete.as_view(), name='delete_recipe_book'),
path('delete/recipe-book-entry/<int:pk>/', edit.RecipeBookEntryDelete.as_view(), name='delete_recipe_book_entry'),
path('delete/plan/<int:pk>/', edit.MealPlanDelete.as_view(), name='delete_plan'),
path('data/sync', data.sync, name='data_sync'), # TODO move to generic "new" view
path('data/batch/edit', data.batch_edit, name='data_batch_edit'),
@ -56,7 +60,6 @@ urlpatterns = [
path('api/get_file_link/<int:recipe_id>/', api.get_file_link, name='api_get_file_link'),
path('api/get_external_file_link/<int:recipe_id>/', api.get_external_file_link, name='api_get_external_file_link'),
path('api/sync_all/', api.sync_all, name='api_sync'),
path('dal/keyword/', dal.KeywordAutocomplete.as_view(), name='dal_keyword'),

View File

@ -13,9 +13,9 @@ from django.urls import reverse_lazy, reverse
from django.utils.translation import gettext as _
from django.views.generic import UpdateView, DeleteView
from cookbook.forms import ExternalRecipeForm, KeywordForm, StorageForm, SyncForm, InternalRecipeForm, CommentForm
from cookbook.forms import ExternalRecipeForm, KeywordForm, StorageForm, SyncForm, InternalRecipeForm, CommentForm, MealPlanForm
from cookbook.models import Recipe, Sync, Keyword, RecipeImport, Storage, Comment, RecipeIngredients, RecipeBook, \
RecipeBookEntry
RecipeBookEntry, MealPlan
from cookbook.provider.dropbox import Dropbox
from cookbook.provider.nextcloud import Nextcloud
@ -225,6 +225,22 @@ class RecipeBookUpdate(LoginRequiredMixin, UpdateView):
return context
class MealPlanUpdate(LoginRequiredMixin, UpdateView):
template_name = "generic/edit_template.html"
model = MealPlan
form_class = MealPlanForm
# TODO add msg box
def get_success_url(self):
return reverse('view_plan')
def get_context_data(self, **kwargs):
context = super(MealPlanUpdate, self).get_context_data(**kwargs)
context['title'] = _("Meal-Plan")
return context
class RecipeUpdate(LoginRequiredMixin, UpdateView):
model = Recipe
form_class = ExternalRecipeForm
@ -374,3 +390,14 @@ class RecipeBookEntryDelete(LoginRequiredMixin, DeleteView):
context = super(RecipeBookEntryDelete, self).get_context_data(**kwargs)
context['title'] = _("Bookmarks")
return context
class MealPlanDelete(LoginRequiredMixin, DeleteView):
template_name = "generic/delete_template.html"
model = MealPlan
success_url = reverse_lazy('view_plan')
def get_context_data(self, **kwargs):
context = super(MealPlanDelete, self).get_context_data(**kwargs)
context['title'] = _("Meal-Plan")
return context

View File

@ -8,8 +8,8 @@ from django.utils.translation import gettext as _
from django.views.generic import CreateView
from cookbook.forms import ImportRecipeForm, RecipeImport, KeywordForm, Storage, StorageForm, InternalRecipeForm, \
RecipeBookForm
from cookbook.models import Keyword, Recipe, RecipeBook
RecipeBookForm, MealPlanForm
from cookbook.models import Keyword, Recipe, RecipeBook, MealPlan
class RecipeCreate(LoginRequiredMixin, CreateView):
@ -109,3 +109,21 @@ class RecipeBookCreate(LoginRequiredMixin, CreateView):
context = super(RecipeBookCreate, self).get_context_data(**kwargs)
context['title'] = _("Recipe Book")
return context
class MealPlanCreate(LoginRequiredMixin, CreateView):
template_name = "generic/new_template.html"
model = MealPlan
form_class = MealPlanForm
success_url = reverse_lazy('view_plan')
def form_valid(self, form):
obj = form.save(commit=False)
obj.user = self.request.user
obj.save()
return HttpResponseRedirect(self.get_success_url())
def get_context_data(self, **kwargs):
context = super(MealPlanCreate, self).get_context_data(**kwargs)
context['title'] = _("Meal-Plan")
return context

View File

@ -1,8 +1,6 @@
from django.contrib import messages
from django.contrib.auth.decorators import login_required
from django.http import HttpResponseRedirect
from django.shortcuts import render, get_object_or_404
from django.urls import reverse
from django_tables2 import RequestConfig
from django.utils.translation import gettext as _
@ -69,3 +67,9 @@ def books(request):
book_list.append({'book': b, 'recipes': RecipeBookEntry.objects.filter(book=b).all()})
return render(request, 'books.html', {'book_list': book_list})
@login_required()
def meal_plan(request):
form = MealPlanForm()
return render(request, 'meal_plan.html', {'form': form})

View File

@ -72,8 +72,7 @@ ROOT_URLCONF = 'recipes.urls'
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [os.path.join(BASE_DIR, 'templates')]
,
'DIRS': [os.path.join(BASE_DIR, 'templates')],
'APP_DIRS': True,
'OPTIONS': {
'context_processors': [

View File

@ -13,6 +13,6 @@ markdown
simplejson
lxml
webdavclient3
python-dotenv==0.10.3
python-dotenv
psycopg2-binary
gunicorn==19.7.1
gunicorn