storage backend reworked
This commit is contained in:
parent
c5986c6e7f
commit
4871b6194c
@ -17,7 +17,7 @@ class EmojiWidget(forms.TextInput):
|
||||
class EditRecipeForm(forms.ModelForm):
|
||||
class Meta:
|
||||
model = Recipe
|
||||
fields = ('name', 'category', 'keywords', 'path')
|
||||
fields = ('name', 'category', 'keywords', 'path', 'storage')
|
||||
|
||||
labels = {
|
||||
'name': _('Name'),
|
||||
@ -42,8 +42,20 @@ class KeywordForm(forms.ModelForm):
|
||||
widgets = {'icon': EmojiWidget}
|
||||
|
||||
|
||||
class MonitorForm(forms.Form):
|
||||
path = forms.CharField(label=_('Path'))
|
||||
class StorageForm(forms.ModelForm):
|
||||
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)
|
||||
token = forms.CharField(widget=forms.TextInput(attrs={'autocomplete': 'new-password', 'type': 'password'}), required=False)
|
||||
|
||||
class Meta:
|
||||
model = Storage
|
||||
fields = ('name', 'method', 'username', 'password', 'token', 'url')
|
||||
|
||||
|
||||
class SyncForm(forms.ModelForm):
|
||||
class Meta:
|
||||
model = Sync
|
||||
fields = ('storage', 'path')
|
||||
|
||||
|
||||
class BatchEditForm(forms.Form):
|
||||
|
@ -8,22 +8,11 @@ from django.conf import settings
|
||||
from cookbook.models import Recipe, Sync, RecipeImport, SyncLog
|
||||
|
||||
|
||||
def sync_all():
|
||||
monitors = Sync.objects.all()
|
||||
|
||||
for monitor in monitors:
|
||||
ret = import_all(monitor)
|
||||
if not ret:
|
||||
return ret
|
||||
|
||||
return True
|
||||
|
||||
|
||||
def import_all(monitor):
|
||||
url = "https://api.dropboxapi.com/2/files/list_folder"
|
||||
|
||||
headers = {
|
||||
"Authorization": "Bearer " + settings.DROPBOX_API_KEY,
|
||||
"Authorization": "Bearer " + monitor.storage.token,
|
||||
"Content-Type": "application/json"
|
||||
}
|
||||
|
||||
@ -40,15 +29,15 @@ def import_all(monitor):
|
||||
return r
|
||||
|
||||
import_count = 0
|
||||
for recipe in recipes['entries']:
|
||||
for recipe in recipes['entries']: # TODO check if has_more is set and import that as well
|
||||
path = recipe['path_lower']
|
||||
if not Recipe.objects.filter(path=path).exists() and not RecipeImport.objects.filter(path=path).exists():
|
||||
name = os.path.splitext(recipe['name'])[0]
|
||||
new_recipe = RecipeImport(name=name, path=path)
|
||||
new_recipe = RecipeImport(name=name, path=path, storage=monitor.storage)
|
||||
new_recipe.save()
|
||||
import_count += 1
|
||||
|
||||
log_entry = SyncLog(status='SUCCESS', msg='Imported ' + str(import_count) + ' recipes', monitor=monitor)
|
||||
log_entry = SyncLog(status='SUCCESS', msg='Imported ' + str(import_count) + ' recipes', sync=monitor)
|
||||
log_entry.save()
|
||||
|
||||
monitor.last_checked = datetime.now()
|
||||
|
@ -1,4 +1,4 @@
|
||||
# Generated by Django 2.0.5 on 2018-05-14 08:50
|
||||
# Generated by Django 2.0.5 on 2018-05-25 13:11
|
||||
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
@ -17,6 +17,7 @@ class Migration(migrations.Migration):
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('name', models.CharField(max_length=64, unique=True)),
|
||||
('icon', models.CharField(blank=True, max_length=1, null=True)),
|
||||
('description', models.TextField(blank=True, default='')),
|
||||
('created_by', models.IntegerField(default=0)),
|
||||
('created_at', models.DateTimeField(auto_now_add=True)),
|
||||
@ -28,6 +29,7 @@ class Migration(migrations.Migration):
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('name', models.CharField(max_length=64, unique=True)),
|
||||
('icon', models.CharField(blank=True, max_length=1, null=True)),
|
||||
('description', models.TextField(blank=True, default='')),
|
||||
('created_by', models.IntegerField(default=0)),
|
||||
('created_at', models.DateTimeField(auto_now_add=True)),
|
||||
@ -44,7 +46,7 @@ class Migration(migrations.Migration):
|
||||
('created_by', models.IntegerField(default=0)),
|
||||
('created_at', models.DateTimeField(auto_now_add=True)),
|
||||
('updated_at', models.DateTimeField(auto_now=True)),
|
||||
('category', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, to='cookbook.Category')),
|
||||
('category', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='cookbook.Category')),
|
||||
('keywords', models.ManyToManyField(blank=True, to='cookbook.Keyword')),
|
||||
],
|
||||
),
|
||||
@ -57,6 +59,18 @@ class Migration(migrations.Migration):
|
||||
('created_at', models.DateTimeField(auto_now_add=True)),
|
||||
],
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='Storage',
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('name', models.CharField(max_length=128)),
|
||||
('method', models.CharField(choices=[('DB', 'Dropbox')], default='DB', max_length=128)),
|
||||
('username', models.CharField(blank=True, max_length=128, null=True)),
|
||||
('password', models.CharField(blank=True, max_length=128, null=True)),
|
||||
('token', models.CharField(blank=True, max_length=512, null=True)),
|
||||
('url', models.URLField(blank=True, null=True)),
|
||||
],
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='Sync',
|
||||
fields=[
|
||||
@ -65,6 +79,7 @@ class Migration(migrations.Migration):
|
||||
('last_checked', models.DateTimeField()),
|
||||
('created_at', models.DateTimeField(auto_now_add=True)),
|
||||
('updated_at', models.DateTimeField(auto_now=True)),
|
||||
('storage', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to='cookbook.Storage')),
|
||||
],
|
||||
),
|
||||
migrations.CreateModel(
|
||||
@ -77,4 +92,9 @@ class Migration(migrations.Migration):
|
||||
('monitor', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='cookbook.Sync')),
|
||||
],
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='recipe',
|
||||
name='storage',
|
||||
field=models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to='cookbook.Storage'),
|
||||
),
|
||||
]
|
||||
|
18
cookbook/migrations/0002_auto_20180525_1558.py
Normal file
18
cookbook/migrations/0002_auto_20180525_1558.py
Normal file
@ -0,0 +1,18 @@
|
||||
# Generated by Django 2.0.5 on 2018-05-25 13:58
|
||||
|
||||
from django.db import migrations
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('cookbook', '0001_initial'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.RenameField(
|
||||
model_name='synclog',
|
||||
old_name='monitor',
|
||||
new_name='sync',
|
||||
),
|
||||
]
|
@ -1,18 +0,0 @@
|
||||
# Generated by Django 2.0.5 on 2018-05-14 08:58
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('cookbook', '0001_initial'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='category',
|
||||
name='icon',
|
||||
field=models.CharField(blank=True, max_length=4, null=True),
|
||||
),
|
||||
]
|
@ -1,23 +0,0 @@
|
||||
# Generated by Django 2.0.5 on 2018-05-14 09:21
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('cookbook', '0002_category_icon'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='keyword',
|
||||
name='icon',
|
||||
field=models.CharField(blank=True, max_length=1, null=True),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='category',
|
||||
name='icon',
|
||||
field=models.CharField(blank=True, max_length=1, null=True),
|
||||
),
|
||||
]
|
20
cookbook/migrations/0003_recipeimport_storage.py
Normal file
20
cookbook/migrations/0003_recipeimport_storage.py
Normal file
@ -0,0 +1,20 @@
|
||||
# Generated by Django 2.0.5 on 2018-05-25 14:05
|
||||
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('cookbook', '0002_auto_20180525_1558'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='recipeimport',
|
||||
name='storage',
|
||||
field=models.ForeignKey(default=0, on_delete=django.db.models.deletion.PROTECT, to='cookbook.Storage'),
|
||||
preserve_default=False,
|
||||
),
|
||||
]
|
@ -1,19 +0,0 @@
|
||||
# Generated by Django 2.0.5 on 2018-05-15 18:35
|
||||
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('cookbook', '0003_auto_20180514_1121'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='recipe',
|
||||
name='category',
|
||||
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='cookbook.Category'),
|
||||
),
|
||||
]
|
@ -1,6 +1,36 @@
|
||||
from django.db import models
|
||||
|
||||
|
||||
class Storage(models.Model):
|
||||
DROPBOX = 'DB'
|
||||
STORAGE_TYPES = ((DROPBOX, 'Dropbox'),)
|
||||
|
||||
name = models.CharField(max_length=128)
|
||||
method = models.CharField(choices=STORAGE_TYPES, max_length=128, default=DROPBOX)
|
||||
username = models.CharField(max_length=128, blank=True, null=True)
|
||||
password = models.CharField(max_length=128, blank=True, null=True)
|
||||
token = models.CharField(max_length=512, blank=True, null=True)
|
||||
url = models.URLField(blank=True, null=True)
|
||||
|
||||
def __str__(self):
|
||||
return self.name
|
||||
|
||||
|
||||
class Sync(models.Model):
|
||||
storage = models.ForeignKey(Storage, on_delete=models.PROTECT)
|
||||
path = models.CharField(max_length=512, default="")
|
||||
last_checked = models.DateTimeField()
|
||||
created_at = models.DateTimeField(auto_now_add=True)
|
||||
updated_at = models.DateTimeField(auto_now=True)
|
||||
|
||||
|
||||
class SyncLog(models.Model):
|
||||
sync = models.ForeignKey(Sync, on_delete=models.CASCADE)
|
||||
status = models.CharField(max_length=32)
|
||||
msg = models.TextField(default="")
|
||||
created_at = models.DateTimeField(auto_now_add=True)
|
||||
|
||||
|
||||
class Keyword(models.Model):
|
||||
name = models.CharField(max_length=64, unique=True)
|
||||
icon = models.CharField(max_length=1, blank=True, null=True)
|
||||
@ -28,6 +58,7 @@ class Category(models.Model):
|
||||
class Recipe(models.Model):
|
||||
name = models.CharField(max_length=128)
|
||||
path = models.CharField(max_length=512, default="")
|
||||
storage = models.ForeignKey(Storage, on_delete=models.PROTECT)
|
||||
link = models.CharField(max_length=512, default="")
|
||||
category = models.ForeignKey(Category, blank=True, on_delete=models.SET_NULL, null=True)
|
||||
keywords = models.ManyToManyField(Keyword, blank=True)
|
||||
@ -45,19 +76,6 @@ class Recipe(models.Model):
|
||||
|
||||
class RecipeImport(models.Model):
|
||||
name = models.CharField(max_length=128)
|
||||
storage = models.ForeignKey(Storage, on_delete=models.PROTECT)
|
||||
path = models.CharField(max_length=512, default="")
|
||||
created_at = models.DateTimeField(auto_now_add=True)
|
||||
|
||||
|
||||
class Sync(models.Model):
|
||||
path = models.CharField(max_length=512, default="")
|
||||
last_checked = models.DateTimeField()
|
||||
created_at = models.DateTimeField(auto_now_add=True)
|
||||
updated_at = models.DateTimeField(auto_now=True)
|
||||
|
||||
|
||||
class SyncLog(models.Model):
|
||||
monitor = models.ForeignKey(Sync, on_delete=models.CASCADE)
|
||||
status = models.CharField(max_length=32)
|
||||
msg = models.TextField(default="")
|
||||
created_at = models.DateTimeField(auto_now_add=True)
|
||||
|
@ -39,8 +39,17 @@ class KeywordTable(tables.Table):
|
||||
fields = ('id', 'icon', 'name')
|
||||
|
||||
|
||||
class StorageTable(tables.Table):
|
||||
id = tables.LinkColumn('edit_storage', args=[A('id')])
|
||||
|
||||
class Meta:
|
||||
model = Storage
|
||||
template_name = 'generic/table_template.html'
|
||||
fields = ('id', 'name', 'method')
|
||||
|
||||
|
||||
class ImportLogTable(tables.Table):
|
||||
monitor_id = tables.LinkColumn('edit_monitor', args=[A('monitor_id')])
|
||||
sync_id = tables.LinkColumn('edit_sync', args=[A('sync_id')])
|
||||
|
||||
@staticmethod
|
||||
def render_status(value):
|
||||
@ -52,20 +61,24 @@ class ImportLogTable(tables.Table):
|
||||
class Meta:
|
||||
model = SyncLog
|
||||
template_name = 'generic/table_template.html'
|
||||
fields = ('status', 'msg', 'monitor_id', 'created_at')
|
||||
fields = ('status', 'msg', 'sync_id', 'created_at')
|
||||
|
||||
|
||||
class MonitoredPathTable(tables.Table):
|
||||
id = tables.LinkColumn('edit_monitor', args=[A('id')])
|
||||
class SyncTable(tables.Table):
|
||||
id = tables.LinkColumn('edit_sync', args=[A('id')])
|
||||
|
||||
@staticmethod
|
||||
def render_path(value):
|
||||
return format_html('<code>%s</code>' % value)
|
||||
|
||||
@staticmethod
|
||||
def render_storage(value):
|
||||
return format_html('<span class="badge badge-success">%s</span>' % value)
|
||||
|
||||
class Meta:
|
||||
model = Sync
|
||||
template_name = 'generic/table_template.html'
|
||||
fields = ('id', 'path', 'last_checked')
|
||||
fields = ('id', 'path', 'storage', 'last_checked')
|
||||
|
||||
|
||||
class RecipeImportTable(tables.Table):
|
||||
|
@ -65,6 +65,8 @@
|
||||
class="fas fa-archive"></i> {% trans 'Category' %}</a>
|
||||
<a class="dropdown-item" href="{% url 'new_keyword' %}"><i
|
||||
class="fas fa-tags"></i> {% trans 'Keyword' %}</a>
|
||||
<a class="dropdown-item" href="{% url 'new_storage' %}"><i
|
||||
class="fas fa-database"></i> {% trans 'Storage Backend' %}</a>
|
||||
</div>
|
||||
</li>
|
||||
<li class="nav-item dropdown">
|
||||
@ -81,6 +83,8 @@
|
||||
class="far fa-file-alt"></i> {% trans 'New Recipes' %}</a>
|
||||
<a class="dropdown-item" href="{% url 'list_import_log' %}"><i
|
||||
class="fas fa-history"></i> {% trans 'Import Log' %}</a>
|
||||
<a class="dropdown-item" href="{% url 'list_storage' %}"><i
|
||||
class="fas fa-database"></i> {% trans 'Storage Backends' %}</a>
|
||||
</div>
|
||||
</li>
|
||||
<li class="nav-item dropdown">
|
||||
|
@ -24,7 +24,7 @@
|
||||
</div>
|
||||
|
||||
<br/>
|
||||
<a href="{% url 'api_dropbox_sync' %}" class="btn btn-warning">{% trans 'Sync Now!' %}</a>
|
||||
<a href="{% url 'batch_sync_wait' %}" class="btn btn-warning">{% trans 'Sync Now!' %}</a>
|
||||
<br/><br/>
|
||||
|
||||
{% render_table monitored_paths %}
|
||||
|
26
cookbook/templates/batch/waiting.html
Normal file
26
cookbook/templates/batch/waiting.html
Normal file
@ -0,0 +1,26 @@
|
||||
{% extends "base.html" %}
|
||||
{% load i18n %}
|
||||
|
||||
{% block title %}{% trans 'Importing Recipes' %}{% endblock %}
|
||||
|
||||
|
||||
{% block content %}
|
||||
|
||||
<h3>
|
||||
{% trans 'Importing Recipes' %}
|
||||
</h3>
|
||||
<br/>
|
||||
<br/>
|
||||
<br/>
|
||||
<br/>
|
||||
<div class="text-center">
|
||||
<i class="fas fa-sync fa-spin fa-10x"></i>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
$(document).ready(function () {
|
||||
window.location.href = "http://192.168.178.27:8000/cookbook/api/sync_all"
|
||||
});
|
||||
</script>
|
||||
|
||||
{% endblock %}
|
@ -13,6 +13,17 @@
|
||||
|
||||
<h3>{% trans 'Edit' %} {{ title }}</h3>
|
||||
|
||||
{% if form.Meta.model|get_class == 'Storage' %} <!-- TODO make one include for this text block -->
|
||||
<div class="alert alert-danger" role="alert">
|
||||
<h4 class="alert-heading"><i class="far fa-exclamation-triangle"></i> {% trans 'Security Warning' %}</h4>
|
||||
<p>{% blocktrans %}
|
||||
The <b>Password and Token</b> field are stored as <b>plain text</b> inside the database.
|
||||
This is necessary because they are needed to make API requests, but it also increases the risk of someone stealing it. <br/>
|
||||
To limit the possible damage use read only tokens or accounts if available or create separate accounts with limited access (only to recipes).
|
||||
{% endblocktrans %}</p>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<form action="." method="post">
|
||||
{% csrf_token %}
|
||||
{{ form|crispy }}
|
||||
|
@ -13,6 +13,17 @@
|
||||
|
||||
<h3>{% trans 'New' %} {{ title }} </h3>
|
||||
|
||||
{% if form.Meta.model|get_class == 'Storage' %} <!-- TODO make one include for this text block -->
|
||||
<div class="alert alert-danger" role="alert">
|
||||
<h4 class="alert-heading"><i class="far fa-exclamation-triangle"></i> {% trans 'Security Warning' %}</h4>
|
||||
<p>{% blocktrans %}
|
||||
The <b>Password and Token</b> field are stored as <b>plain text</b> inside the database.
|
||||
This is necessary because they are needed to make API requests, but it also increases the risk of someone stealing it. <br/>
|
||||
To limit the possible damage use read only tokens or accounts if available or create separate accounts with limited access (only to recipes).
|
||||
{% endblocktrans %}</p>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<form action="." method="post">
|
||||
{% csrf_token %}
|
||||
{{ form|crispy }}
|
||||
|
@ -12,32 +12,37 @@ urlpatterns = [
|
||||
path('new/recipe_import/<int:import_id>/', new.create_new_recipe, name='new_recipe_import'),
|
||||
path('new/category/', new.CategoryCreate.as_view(), name='new_category'),
|
||||
path('new/keyword/', new.KeywordCreate.as_view(), name='new_keyword'),
|
||||
path('new/storage/', new.StorageCreate.as_view(), name='new_storage'),
|
||||
|
||||
path('list/keyword', lists.keyword, name='list_keyword'),
|
||||
path('list/category', lists.category, name='list_category'),
|
||||
path('list/import_log', lists.sync_log, name='list_import_log'),
|
||||
path('list/import', lists.recipe_import, name='list_import'),
|
||||
path('list/storage', lists.storage, name='list_storage'),
|
||||
|
||||
path('edit/recipe/<int:pk>/', edit.RecipeUpdate.as_view(), name='edit_recipe'),
|
||||
path('edit/keyword/<int:pk>/', edit.KeywordUpdate.as_view(), name='edit_keyword'),
|
||||
path('edit/category/<int:pk>/', edit.CategoryUpdate.as_view(), name='edit_category'),
|
||||
path('edit/monitor/<int:pk>/', edit.MonitorUpdate.as_view(), name='edit_monitor'),
|
||||
path('edit/sync/<int:pk>/', edit.SyncUpdate.as_view(), name='edit_sync'),
|
||||
path('edit/import/<int:pk>/', edit.ImportUpdate.as_view(), name='edit_import'),
|
||||
path('edit/storage/<int:pk>/', edit.StorageUpdate.as_view(), name='edit_storage'),
|
||||
|
||||
path('redirect/delete/<slug:name>/<int:pk>/', edit.delete_redirect, name='redirect_delete'),
|
||||
|
||||
path('delete/recipe/<int:pk>/', edit.RecipeDelete.as_view(), name='delete_recipe'),
|
||||
path('delete/keyword/<int:pk>/', edit.KeywordDelete.as_view(), name='delete_keyword'),
|
||||
path('delete/category/<int:pk>/', edit.CategoryDelete.as_view(), name='delete_category'),
|
||||
path('delete/monitor/<int:pk>/', edit.MonitorDelete.as_view(), name='delete_monitor'),
|
||||
path('delete/sync/<int:pk>/', edit.MonitorDelete.as_view(), name='delete_sync'),
|
||||
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('batch/monitor', batch.batch_monitor, name='batch_monitor'),
|
||||
path('batch/sync', batch.batch_monitor, name='batch_monitor'), # TODO move to generic "new" view
|
||||
path('batch/edit', batch.batch_edit, name='batch_edit'),
|
||||
path('batch/import/all', batch.batch_import_all, name='batch_import_all'),
|
||||
path('batch/sync/wait', batch.sync_wait, name='batch_sync_wait'),
|
||||
|
||||
path('api/get_file_link/<int:recipe_id>/', api.get_file_link, name='api_get_file_link'),
|
||||
path('api/sync_all/', api.dropbox_sync, name='api_dropbox_sync'),
|
||||
path('api/sync_all/', api.sync_all, name='api_sync'),
|
||||
|
||||
path('dal/keyword/', dal.KeywordAutocomplete.as_view(), name='dal_keyword'),
|
||||
]
|
||||
|
@ -4,13 +4,15 @@ from django.utils.translation import gettext as _
|
||||
from django.contrib.auth.decorators import login_required
|
||||
from django.shortcuts import redirect
|
||||
|
||||
from cookbook.models import Recipe
|
||||
from cookbook.models import Recipe, Sync, Storage
|
||||
from cookbook.helper import dropbox
|
||||
|
||||
|
||||
@login_required
|
||||
def get_file_link(request, recipe_id):
|
||||
recipe = Recipe.objects.get(id=recipe_id)
|
||||
|
||||
if recipe.storage.method == Storage.DROPBOX:
|
||||
if recipe.link == "":
|
||||
response = dropbox.get_share_link(recipe.path) # TODO response validation
|
||||
recipe.link = response['url']
|
||||
@ -20,9 +22,17 @@ def get_file_link(request, recipe_id):
|
||||
|
||||
|
||||
@login_required
|
||||
def dropbox_sync(request):
|
||||
ret = dropbox.sync_all()
|
||||
if ret:
|
||||
def sync_all(request):
|
||||
monitors = Sync.objects.all()
|
||||
|
||||
error = False
|
||||
for monitor in monitors:
|
||||
if monitor.storage.method == Storage.DROPBOX:
|
||||
ret = dropbox.import_all(monitor)
|
||||
if not ret:
|
||||
error = True
|
||||
|
||||
if not error:
|
||||
messages.add_message(request, messages.SUCCESS, _('Sync successful!'))
|
||||
return redirect('list_import')
|
||||
else:
|
||||
|
@ -5,36 +5,42 @@ from django.contrib.auth.decorators import login_required
|
||||
from django.shortcuts import redirect, render
|
||||
from django_tables2 import RequestConfig
|
||||
|
||||
from cookbook.forms import MonitorForm, BatchEditForm, RecipeImport
|
||||
from cookbook.forms import SyncForm, BatchEditForm, RecipeImport
|
||||
from cookbook.models import Recipe, Category, Sync
|
||||
from cookbook.tables import MonitoredPathTable
|
||||
from cookbook.tables import SyncTable
|
||||
from django.utils.translation import gettext as _, ngettext
|
||||
|
||||
|
||||
@login_required
|
||||
def batch_monitor(request):
|
||||
if request.method == "POST":
|
||||
form = MonitorForm(request.POST)
|
||||
form = SyncForm(request.POST)
|
||||
if form.is_valid():
|
||||
new_path = Sync()
|
||||
new_path.path = form.cleaned_data['path']
|
||||
new_path.storage = form.cleaned_data['storage']
|
||||
new_path.last_checked = datetime.now()
|
||||
new_path.save()
|
||||
return redirect('batch_monitor')
|
||||
else:
|
||||
form = MonitorForm()
|
||||
form = SyncForm()
|
||||
|
||||
monitored_paths = MonitoredPathTable(Sync.objects.all())
|
||||
monitored_paths = SyncTable(Sync.objects.all())
|
||||
RequestConfig(request, paginate={'per_page': 25}).configure(monitored_paths)
|
||||
|
||||
return render(request, 'batch/monitor.html', {'form': form, 'monitored_paths': monitored_paths})
|
||||
|
||||
|
||||
@login_required
|
||||
def sync_wait(request):
|
||||
return render(request, 'batch/waiting.html')
|
||||
|
||||
|
||||
@login_required
|
||||
def batch_import_all(request):
|
||||
imports = RecipeImport.objects.all()
|
||||
for new_recipe in imports:
|
||||
recipe = Recipe(name=new_recipe.name, path=new_recipe.path)
|
||||
recipe = Recipe(name=new_recipe.name, path=new_recipe.path, storage=new_recipe.storage)
|
||||
recipe.save()
|
||||
new_recipe.delete()
|
||||
|
||||
|
@ -5,23 +5,23 @@ 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 EditRecipeForm, CategoryForm, KeywordForm
|
||||
from cookbook.models import Recipe, Category, Sync, Keyword, RecipeImport
|
||||
from cookbook.forms import EditRecipeForm, CategoryForm, KeywordForm, StorageForm, SyncForm
|
||||
from cookbook.models import Recipe, Category, Sync, Keyword, RecipeImport, Storage
|
||||
|
||||
|
||||
class MonitorUpdate(LoginRequiredMixin, UpdateView):
|
||||
class SyncUpdate(LoginRequiredMixin, UpdateView):
|
||||
template_name = "generic\edit_template.html"
|
||||
model = Sync
|
||||
fields = ['path']
|
||||
form_class = SyncForm
|
||||
|
||||
# TODO add msg box
|
||||
|
||||
def get_success_url(self):
|
||||
return reverse('edit_monitor', kwargs={'pk': self.object.pk})
|
||||
return reverse('edit_sync', kwargs={'pk': self.object.pk})
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super(MonitorUpdate, self).get_context_data(**kwargs)
|
||||
context['title'] = _("Monitor")
|
||||
context = super(SyncUpdate, self).get_context_data(**kwargs)
|
||||
context['title'] = _("Sync")
|
||||
return context
|
||||
|
||||
|
||||
@ -57,6 +57,22 @@ class KeywordUpdate(LoginRequiredMixin, UpdateView):
|
||||
return context
|
||||
|
||||
|
||||
class StorageUpdate(LoginRequiredMixin, UpdateView):
|
||||
template_name = "generic\edit_template.html"
|
||||
model = Storage
|
||||
form_class = StorageForm
|
||||
|
||||
# TODO add msg box
|
||||
|
||||
def get_success_url(self):
|
||||
return reverse('edit_storage', kwargs={'pk': self.object.pk})
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super(StorageUpdate, self).get_context_data(**kwargs)
|
||||
context['title'] = _("Storage Backend")
|
||||
return context
|
||||
|
||||
|
||||
class ImportUpdate(LoginRequiredMixin, UpdateView):
|
||||
template_name = "generic\edit_template.html"
|
||||
model = RecipeImport
|
||||
@ -154,3 +170,14 @@ class KeywordDelete(LoginRequiredMixin, DeleteView):
|
||||
context = super(KeywordDelete, self).get_context_data(**kwargs)
|
||||
context['title'] = _("Keyword")
|
||||
return context
|
||||
|
||||
|
||||
class StorageDelete(LoginRequiredMixin, DeleteView):
|
||||
template_name = "generic\delete_template.html"
|
||||
model = Storage
|
||||
success_url = reverse_lazy('list_storage')
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super(StorageDelete, self).get_context_data(**kwargs)
|
||||
context['title'] = _("Storage Backend")
|
||||
return context
|
||||
|
@ -4,8 +4,8 @@ from django.shortcuts import render
|
||||
from django_tables2 import RequestConfig
|
||||
from django.utils.translation import gettext as _
|
||||
|
||||
from cookbook.models import Category, Keyword, SyncLog, RecipeImport
|
||||
from cookbook.tables import CategoryTable, KeywordTable, ImportLogTable, RecipeImportTable
|
||||
from cookbook.models import Category, Keyword, SyncLog, RecipeImport, Storage
|
||||
from cookbook.tables import CategoryTable, KeywordTable, ImportLogTable, RecipeImportTable, StorageTable
|
||||
|
||||
|
||||
@login_required
|
||||
@ -38,3 +38,11 @@ def recipe_import(request):
|
||||
RequestConfig(request, paginate={'per_page': 25}).configure(table)
|
||||
|
||||
return render(request, 'generic/list_template.html', {'title': _("Import"), 'table': table})
|
||||
|
||||
|
||||
@login_required
|
||||
def storage(request):
|
||||
table = StorageTable(Storage.objects.all())
|
||||
RequestConfig(request, paginate={'per_page': 25}).configure(table)
|
||||
|
||||
return render(request, 'generic/list_template.html', {'title': _("Storage Backend"), 'table': table})
|
||||
|
@ -6,7 +6,7 @@ from django.urls import reverse_lazy
|
||||
from django.utils.translation import gettext as _
|
||||
from django.views.generic import CreateView
|
||||
|
||||
from cookbook.forms import ImportRecipeForm, RecipeImport, CategoryForm, KeywordForm
|
||||
from cookbook.forms import ImportRecipeForm, RecipeImport, CategoryForm, KeywordForm, Storage, StorageForm
|
||||
from cookbook.models import Category, Keyword, Recipe
|
||||
|
||||
|
||||
@ -46,6 +46,18 @@ class KeywordCreate(LoginRequiredMixin, CreateView):
|
||||
return context
|
||||
|
||||
|
||||
class StorageCreate(LoginRequiredMixin, CreateView):
|
||||
template_name = "generic\\new_template.html"
|
||||
model = Storage
|
||||
form_class = StorageForm
|
||||
success_url = reverse_lazy('list_storage')
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super(StorageCreate, self).get_context_data(**kwargs)
|
||||
context['title'] = _("Storage Backend")
|
||||
return context
|
||||
|
||||
|
||||
@login_required
|
||||
def create_new_recipe(request, import_id):
|
||||
if request.method == "POST":
|
||||
|
Loading…
Reference in New Issue
Block a user