diff --git a/cookbook/forms.py b/cookbook/forms.py index 0c85bab3..a57c6b8c 100644 --- a/cookbook/forms.py +++ b/cookbook/forms.py @@ -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): diff --git a/cookbook/helper/dropbox.py b/cookbook/helper/dropbox.py index c8aed082..672af489 100644 --- a/cookbook/helper/dropbox.py +++ b/cookbook/helper/dropbox.py @@ -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() diff --git a/cookbook/migrations/0001_initial.py b/cookbook/migrations/0001_initial.py index 05e513cc..0ee34575 100644 --- a/cookbook/migrations/0001_initial.py +++ b/cookbook/migrations/0001_initial.py @@ -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'), + ), ] diff --git a/cookbook/migrations/0002_auto_20180525_1558.py b/cookbook/migrations/0002_auto_20180525_1558.py new file mode 100644 index 00000000..2d78e127 --- /dev/null +++ b/cookbook/migrations/0002_auto_20180525_1558.py @@ -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', + ), + ] diff --git a/cookbook/migrations/0002_category_icon.py b/cookbook/migrations/0002_category_icon.py deleted file mode 100644 index 315045aa..00000000 --- a/cookbook/migrations/0002_category_icon.py +++ /dev/null @@ -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), - ), - ] diff --git a/cookbook/migrations/0003_auto_20180514_1121.py b/cookbook/migrations/0003_auto_20180514_1121.py deleted file mode 100644 index d24492fc..00000000 --- a/cookbook/migrations/0003_auto_20180514_1121.py +++ /dev/null @@ -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), - ), - ] diff --git a/cookbook/migrations/0003_recipeimport_storage.py b/cookbook/migrations/0003_recipeimport_storage.py new file mode 100644 index 00000000..4c2d523d --- /dev/null +++ b/cookbook/migrations/0003_recipeimport_storage.py @@ -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, + ), + ] diff --git a/cookbook/migrations/0004_auto_20180515_2035.py b/cookbook/migrations/0004_auto_20180515_2035.py deleted file mode 100644 index 3696d4c8..00000000 --- a/cookbook/migrations/0004_auto_20180515_2035.py +++ /dev/null @@ -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'), - ), - ] diff --git a/cookbook/models.py b/cookbook/models.py index 4f9fa6a4..35bfa27f 100644 --- a/cookbook/models.py +++ b/cookbook/models.py @@ -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) diff --git a/cookbook/tables.py b/cookbook/tables.py index bc1979b4..89bbdffb 100644 --- a/cookbook/tables.py +++ b/cookbook/tables.py @@ -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('%s' % value) + @staticmethod + def render_storage(value): + return format_html('%s' % 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): diff --git a/cookbook/templates/base.html b/cookbook/templates/base.html index 8e5944ad..9f238472 100644 --- a/cookbook/templates/base.html +++ b/cookbook/templates/base.html @@ -65,6 +65,8 @@ class="fas fa-archive"> {% trans 'Category' %} {% trans 'Keyword' %} + {% trans 'Storage Backend' %}