improvements and fixes
This commit is contained in:
@ -5,3 +5,8 @@ from .models import *
|
|||||||
admin.site.register(Recipe)
|
admin.site.register(Recipe)
|
||||||
admin.site.register(Keyword)
|
admin.site.register(Keyword)
|
||||||
admin.site.register(Category)
|
admin.site.register(Category)
|
||||||
|
|
||||||
|
admin.site.register(Sync)
|
||||||
|
admin.site.register(SyncLog)
|
||||||
|
admin.site.register(RecipeImport)
|
||||||
|
admin.site.register(Storage)
|
||||||
|
@ -6,7 +6,8 @@ from cookbook.models import Recipe, Keyword
|
|||||||
|
|
||||||
class RecipeFilter(django_filters.FilterSet):
|
class RecipeFilter(django_filters.FilterSet):
|
||||||
name = django_filters.CharFilter(lookup_expr='contains')
|
name = django_filters.CharFilter(lookup_expr='contains')
|
||||||
keywords = django_filters.ModelMultipleChoiceFilter(queryset=Keyword.objects.all(), widget=MultiSelectWidget, method='filter_keywords')
|
keywords = django_filters.ModelMultipleChoiceFilter(queryset=Keyword.objects.all(), widget=MultiSelectWidget,
|
||||||
|
method='filter_keywords')
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def filter_keywords(queryset, name, value):
|
def filter_keywords(queryset, name, value):
|
||||||
@ -19,3 +20,14 @@ class RecipeFilter(django_filters.FilterSet):
|
|||||||
class Meta:
|
class Meta:
|
||||||
model = Recipe
|
model = Recipe
|
||||||
fields = ['name', 'category', 'keywords']
|
fields = ['name', 'category', 'keywords']
|
||||||
|
|
||||||
|
|
||||||
|
class QuickRecipeFilter(django_filters.FilterSet):
|
||||||
|
name = django_filters.CharFilter(lookup_expr='contains')
|
||||||
|
category = django_filters.CharFilter(lookup_expr='contains')
|
||||||
|
keywords = django_filters.ModelMultipleChoiceFilter(queryset=Keyword.objects.all(), widget=MultiSelectWidget,
|
||||||
|
method='filter_keywords')
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
model = Recipe
|
||||||
|
fields = ['name', 'category', 'keywords']
|
||||||
|
@ -1,7 +1,8 @@
|
|||||||
from django.utils.translation import gettext as _
|
|
||||||
from django import forms
|
from django import forms
|
||||||
from .models import *
|
|
||||||
from django.forms import widgets
|
from django.forms import widgets
|
||||||
|
from django.utils.translation import gettext as _
|
||||||
|
|
||||||
|
from .models import *
|
||||||
|
|
||||||
|
|
||||||
class MultiSelectWidget(widgets.SelectMultiple):
|
class MultiSelectWidget(widgets.SelectMultiple):
|
||||||
@ -44,8 +45,10 @@ class KeywordForm(forms.ModelForm):
|
|||||||
|
|
||||||
class StorageForm(forms.ModelForm):
|
class StorageForm(forms.ModelForm):
|
||||||
username = forms.CharField(widget=forms.TextInput(attrs={'autocomplete': 'new-password'}), required=False)
|
username = forms.CharField(widget=forms.TextInput(attrs={'autocomplete': 'new-password'}), required=False)
|
||||||
password = forms.CharField(widget=forms.TextInput(attrs={'autocomplete': 'new-password', 'type': 'password'}), required=False)
|
password = forms.CharField(widget=forms.TextInput(attrs={'autocomplete': 'new-password', 'type': 'password'}),
|
||||||
token = forms.CharField(widget=forms.TextInput(attrs={'autocomplete': 'new-password', 'type': 'password'}), required=False)
|
required=False)
|
||||||
|
token = forms.CharField(widget=forms.TextInput(attrs={'autocomplete': 'new-password', 'type': 'password'}),
|
||||||
|
required=False)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Storage
|
model = Storage
|
||||||
@ -61,10 +64,8 @@ class SyncForm(forms.ModelForm):
|
|||||||
class BatchEditForm(forms.Form):
|
class BatchEditForm(forms.Form):
|
||||||
search = forms.CharField(label=_('Search String'))
|
search = forms.CharField(label=_('Search String'))
|
||||||
category = forms.ModelChoiceField(queryset=Category.objects.all().order_by('id'), required=False)
|
category = forms.ModelChoiceField(queryset=Category.objects.all().order_by('id'), required=False)
|
||||||
keywords = forms.ModelMultipleChoiceField(queryset=Keyword.objects.all().order_by('id'), required=False)
|
keywords = forms.ModelMultipleChoiceField(queryset=Keyword.objects.all().order_by('id'), required=False,
|
||||||
|
widget=MultiSelectWidget)
|
||||||
class Media:
|
|
||||||
js = ('custom/js/form_multiselect.js',)
|
|
||||||
|
|
||||||
|
|
||||||
class ImportRecipeForm(forms.ModelForm):
|
class ImportRecipeForm(forms.ModelForm):
|
||||||
|
@ -23,6 +23,9 @@ class Sync(models.Model):
|
|||||||
created_at = models.DateTimeField(auto_now_add=True)
|
created_at = models.DateTimeField(auto_now_add=True)
|
||||||
updated_at = models.DateTimeField(auto_now=True)
|
updated_at = models.DateTimeField(auto_now=True)
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return self.path
|
||||||
|
|
||||||
|
|
||||||
class SyncLog(models.Model):
|
class SyncLog(models.Model):
|
||||||
sync = models.ForeignKey(Sync, on_delete=models.CASCADE)
|
sync = models.ForeignKey(Sync, on_delete=models.CASCADE)
|
||||||
@ -79,3 +82,6 @@ class RecipeImport(models.Model):
|
|||||||
storage = models.ForeignKey(Storage, on_delete=models.PROTECT)
|
storage = models.ForeignKey(Storage, on_delete=models.PROTECT)
|
||||||
path = models.CharField(max_length=512, default="")
|
path = models.CharField(max_length=512, default="")
|
||||||
created_at = models.DateTimeField(auto_now_add=True)
|
created_at = models.DateTimeField(auto_now_add=True)
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return self.name
|
@ -93,9 +93,9 @@
|
|||||||
{% trans 'Manage Data' %}
|
{% trans 'Manage Data' %}
|
||||||
</a>
|
</a>
|
||||||
<div class="dropdown-menu" aria-labelledby="navbarDropdownMenuLink">
|
<div class="dropdown-menu" aria-labelledby="navbarDropdownMenuLink">
|
||||||
<a class="dropdown-item" href="{% url 'batch_monitor' %}"><i
|
<a class="dropdown-item" href="{% url 'data_sync' %}"><i
|
||||||
class="fas fa-sync-alt"></i> {% trans 'Configure Sync' %}</a>
|
class="fas fa-sync-alt"></i> {% trans 'Configure Sync' %}</a>
|
||||||
<a class="dropdown-item" href="{% url 'batch_edit' %}"><i
|
<a class="dropdown-item" href="{% url 'data_batch_edit' %}"><i
|
||||||
class="fas fa-edit"></i> {% trans 'Batch Edit' %}</a>
|
class="fas fa-edit"></i> {% trans 'Batch Edit' %}</a>
|
||||||
</div>
|
</div>
|
||||||
</li>
|
</li>
|
||||||
|
@ -24,7 +24,7 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<br/>
|
<br/>
|
||||||
<a href="{% url 'batch_sync_wait' %}" class="btn btn-warning">{% trans 'Sync Now!' %}</a>
|
<a href="{% url 'data_sync_wait' %}" class="btn btn-warning">{% trans 'Sync Now!' %}</a>
|
||||||
<br/><br/>
|
<br/><br/>
|
||||||
|
|
||||||
{% render_table monitored_paths %}
|
{% render_table monitored_paths %}
|
||||||
|
@ -15,11 +15,18 @@
|
|||||||
<br/>
|
<br/>
|
||||||
<div class="text-center">
|
<div class="text-center">
|
||||||
<i class="fas fa-sync fa-spin fa-10x"></i>
|
<i class="fas fa-sync fa-spin fa-10x"></i>
|
||||||
|
|
||||||
|
<br/>
|
||||||
|
<br/>
|
||||||
|
<br/>
|
||||||
|
<div class="alert alert-success" role="alert">
|
||||||
|
{% trans 'This can take a few minutes, depending on the number of recipes in sync, please wait.' %}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
$(document).ready(function () {
|
$(document).ready(function () {
|
||||||
window.location.href = "http://192.168.178.27:8000/cookbook/api/sync_all"
|
window.location.href = "{% url 'api_sync' %}"
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
@ -18,7 +18,7 @@
|
|||||||
<br/>
|
<br/>
|
||||||
|
|
||||||
{% if table.Meta.model|get_class == 'RecipeImport' %}
|
{% if table.Meta.model|get_class == 'RecipeImport' %}
|
||||||
<a href="{% url 'batch_import_all' %}" class="btn btn-warning">{% trans 'Import all' %}</a>
|
<a href="{% url 'data_batch_import' %}" class="btn btn-warning">{% trans 'Import all' %}</a>
|
||||||
<br/>
|
<br/>
|
||||||
<br/>
|
<br/>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
@ -16,10 +16,8 @@
|
|||||||
{% if filter %}
|
{% if filter %}
|
||||||
<div class="card">
|
<div class="card">
|
||||||
<div class="card-header">
|
<div class="card-header">
|
||||||
<i class="fas fa-search"></i> {% trans "Search" %} <input type="text" class="form-control">
|
<i class="fas fa-search"></i> {% trans "Search" %}
|
||||||
<a data-toggle="collapse" href="#collapse_search" role="button" aria-expanded="false" aria-controls="collapse_search">{% trans 'Advanced Search' %}</a>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="collapse" id="collapse_search">
|
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
<form action="" method="get" id="search_form">
|
<form action="" method="get" id="search_form">
|
||||||
{{ filter.form|crispy }}
|
{{ filter.form|crispy }}
|
||||||
@ -27,7 +25,7 @@
|
|||||||
<a href="#" onclick="window.location = window.location.pathname;"
|
<a href="#" onclick="window.location = window.location.pathname;"
|
||||||
class="btn btn-warning">{% trans 'Reset' %}</a>
|
class="btn btn-warning">{% trans 'Reset' %}</a>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
14
cookbook/templates/stats.html
Normal file
14
cookbook/templates/stats.html
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
{% extends "base.html" %}
|
||||||
|
{% load i18n %}
|
||||||
|
|
||||||
|
{% block title %}{% trans 'Stats' %}{% endblock %}
|
||||||
|
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
|
||||||
|
<h3>
|
||||||
|
{% trans 'Statistics' %}
|
||||||
|
</h3>
|
||||||
|
|
||||||
|
|
||||||
|
{% endblock %}
|
@ -36,10 +36,11 @@ urlpatterns = [
|
|||||||
path('delete/import/<int:pk>/', edit.ImportDelete.as_view(), name='delete_import'),
|
path('delete/import/<int:pk>/', edit.ImportDelete.as_view(), name='delete_import'),
|
||||||
path('delete/storage/<int:pk>/', edit.StorageDelete.as_view(), name='delete_storage'),
|
path('delete/storage/<int:pk>/', edit.StorageDelete.as_view(), name='delete_storage'),
|
||||||
|
|
||||||
path('batch/sync', batch.batch_monitor, name='batch_monitor'), # TODO move to generic "new" view
|
path('data/sync', data.sync, name='data_sync'), # TODO move to generic "new" view
|
||||||
path('batch/edit', batch.batch_edit, name='batch_edit'),
|
path('data/batch/edit', data.batch_edit, name='data_batch_edit'),
|
||||||
path('batch/import/all', batch.batch_import_all, name='batch_import_all'),
|
path('data/batch/import', data.batch_import, name='data_batch_import'),
|
||||||
path('batch/sync/wait', batch.sync_wait, name='batch_sync_wait'),
|
path('data/sync/wait', data.sync_wait, name='data_sync_wait'),
|
||||||
|
path('data/statistics', data.statistics, name='data_stats'),
|
||||||
|
|
||||||
path('api/get_file_link/<int:recipe_id>/', api.get_file_link, name='api_get_file_link'),
|
path('api/get_file_link/<int:recipe_id>/', api.get_file_link, name='api_get_file_link'),
|
||||||
path('api/sync_all/', api.sync_all, name='api_sync'),
|
path('api/sync_all/', api.sync_all, name='api_sync'),
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
from cookbook.views.views import *
|
from cookbook.views.views import *
|
||||||
from cookbook.views.api import *
|
from cookbook.views.api import *
|
||||||
from cookbook.views.batch import *
|
from cookbook.views.data import *
|
||||||
from cookbook.views.edit import *
|
from cookbook.views.edit import *
|
||||||
from cookbook.views.new import *
|
from cookbook.views.new import *
|
||||||
from cookbook.views.lists import *
|
from cookbook.views.lists import *
|
@ -3,16 +3,16 @@ from datetime import datetime
|
|||||||
from django.contrib import messages
|
from django.contrib import messages
|
||||||
from django.contrib.auth.decorators import login_required
|
from django.contrib.auth.decorators import login_required
|
||||||
from django.shortcuts import redirect, render
|
from django.shortcuts import redirect, render
|
||||||
|
from django.utils.translation import ngettext
|
||||||
from django_tables2 import RequestConfig
|
from django_tables2 import RequestConfig
|
||||||
|
|
||||||
from cookbook.forms import SyncForm, BatchEditForm, RecipeImport
|
from cookbook.forms import SyncForm, BatchEditForm, RecipeImport
|
||||||
from cookbook.models import Recipe, Category, Sync
|
from cookbook.models import Recipe, Sync
|
||||||
from cookbook.tables import SyncTable
|
from cookbook.tables import SyncTable
|
||||||
from django.utils.translation import gettext as _, ngettext
|
|
||||||
|
|
||||||
|
|
||||||
@login_required
|
@login_required
|
||||||
def batch_monitor(request):
|
def sync(request):
|
||||||
if request.method == "POST":
|
if request.method == "POST":
|
||||||
form = SyncForm(request.POST)
|
form = SyncForm(request.POST)
|
||||||
if form.is_valid():
|
if form.is_valid():
|
||||||
@ -21,7 +21,7 @@ def batch_monitor(request):
|
|||||||
new_path.storage = form.cleaned_data['storage']
|
new_path.storage = form.cleaned_data['storage']
|
||||||
new_path.last_checked = datetime.now()
|
new_path.last_checked = datetime.now()
|
||||||
new_path.save()
|
new_path.save()
|
||||||
return redirect('batch_monitor')
|
return redirect('data_sync')
|
||||||
else:
|
else:
|
||||||
form = SyncForm()
|
form = SyncForm()
|
||||||
|
|
||||||
@ -37,7 +37,7 @@ def sync_wait(request):
|
|||||||
|
|
||||||
|
|
||||||
@login_required
|
@login_required
|
||||||
def batch_import_all(request):
|
def batch_import(request):
|
||||||
imports = RecipeImport.objects.all()
|
imports = RecipeImport.objects.all()
|
||||||
for new_recipe in imports:
|
for new_recipe in imports:
|
||||||
recipe = Recipe(name=new_recipe.name, path=new_recipe.path, storage=new_recipe.storage)
|
recipe = Recipe(name=new_recipe.name, path=new_recipe.path, storage=new_recipe.storage)
|
||||||
@ -54,17 +54,17 @@ def batch_edit(request):
|
|||||||
if form.is_valid():
|
if form.is_valid():
|
||||||
word = form.cleaned_data['search']
|
word = form.cleaned_data['search']
|
||||||
category = form.cleaned_data['category']
|
category = form.cleaned_data['category']
|
||||||
keyword = form.cleaned_data['keyword']
|
keywords = form.cleaned_data['keywords']
|
||||||
|
|
||||||
recipes = Recipe.objects.filter(name__contains=word)
|
recipes = Recipe.objects.filter(name__contains=word)
|
||||||
count = 0
|
count = 0
|
||||||
for recipe in recipes:
|
for recipe in recipes:
|
||||||
edit = False
|
edit = False
|
||||||
if category is not None:
|
if category is not None:
|
||||||
recipe.category = Category.objects.get(name=category)
|
recipe.category = category
|
||||||
edit = True
|
edit = True
|
||||||
if keyword.__sizeof__() > 0:
|
if keywords.__sizeof__() > 0:
|
||||||
recipe.keywords.add(*list(keyword))
|
recipe.keywords.add(*list(keywords))
|
||||||
edit = True
|
edit = True
|
||||||
if edit:
|
if edit:
|
||||||
count = count + 1
|
count = count + 1
|
||||||
@ -79,8 +79,13 @@ def batch_edit(request):
|
|||||||
}
|
}
|
||||||
messages.add_message(request, messages.SUCCESS, msg)
|
messages.add_message(request, messages.SUCCESS, msg)
|
||||||
|
|
||||||
return redirect('batch_edit')
|
return redirect('data_batch_edit')
|
||||||
else:
|
else:
|
||||||
form = BatchEditForm()
|
form = BatchEditForm()
|
||||||
|
|
||||||
return render(request, 'batch/edit.html', {'form': form})
|
return render(request, 'batch/edit.html', {'form': form})
|
||||||
|
|
||||||
|
|
||||||
|
@login_required
|
||||||
|
def statistics(request):
|
||||||
|
return render(request, 'index.html')
|
@ -142,7 +142,7 @@ class ImportDelete(LoginRequiredMixin, DeleteView):
|
|||||||
class MonitorDelete(LoginRequiredMixin, DeleteView):
|
class MonitorDelete(LoginRequiredMixin, DeleteView):
|
||||||
template_name = "generic\delete_template.html"
|
template_name = "generic\delete_template.html"
|
||||||
model = Sync
|
model = Sync
|
||||||
success_url = reverse_lazy('batch_monitor')
|
success_url = reverse_lazy('data_sync')
|
||||||
|
|
||||||
def get_context_data(self, **kwargs):
|
def get_context_data(self, **kwargs):
|
||||||
context = super(MonitorDelete, self).get_context_data(**kwargs)
|
context = super(MonitorDelete, self).get_context_data(**kwargs)
|
||||||
|
Reference in New Issue
Block a user