convert example & homeassistant specific configs to a generic with all optional fields
This commit is contained in:
parent
245787b89e
commit
409c0295ec
@ -16,7 +16,7 @@ from .models import (BookmarkletImport, Comment, CookLog, Food, ImportLog, Ingre
|
|||||||
ShoppingList, ShoppingListEntry, ShoppingListRecipe, Space, Step, Storage,
|
ShoppingList, ShoppingListEntry, ShoppingListRecipe, Space, Step, Storage,
|
||||||
Supermarket, SupermarketCategory, SupermarketCategoryRelation, Sync, SyncLog,
|
Supermarket, SupermarketCategory, SupermarketCategoryRelation, Sync, SyncLog,
|
||||||
TelegramBot, Unit, UnitConversion, UserFile, UserPreference, UserSpace,
|
TelegramBot, Unit, UnitConversion, UserFile, UserPreference, UserSpace,
|
||||||
ViewLog, HomeAssistantConfig, ExampleConfig)
|
ViewLog, ConnectorConfig)
|
||||||
|
|
||||||
|
|
||||||
class CustomUserAdmin(UserAdmin):
|
class CustomUserAdmin(UserAdmin):
|
||||||
@ -95,20 +95,12 @@ class StorageAdmin(admin.ModelAdmin):
|
|||||||
admin.site.register(Storage, StorageAdmin)
|
admin.site.register(Storage, StorageAdmin)
|
||||||
|
|
||||||
|
|
||||||
class HomeAssistantConfigAdmin(admin.ModelAdmin):
|
class ConnectorConfigAdmin(admin.ModelAdmin):
|
||||||
list_display = ('id', 'name', 'enabled', 'url')
|
list_display = ('id', 'name', 'type', 'enabled', 'url')
|
||||||
search_fields = ('name', 'url')
|
search_fields = ('name', 'url')
|
||||||
|
|
||||||
|
|
||||||
admin.site.register(HomeAssistantConfig, HomeAssistantConfigAdmin)
|
admin.site.register(ConnectorConfig, ConnectorConfigAdmin)
|
||||||
|
|
||||||
|
|
||||||
class ExampleConfigAdmin(admin.ModelAdmin):
|
|
||||||
list_display = ('id', 'name', 'enabled', 'feed_url')
|
|
||||||
search_fields = ('name',)
|
|
||||||
|
|
||||||
|
|
||||||
admin.site.register(ExampleConfig, ExampleConfigAdmin)
|
|
||||||
|
|
||||||
|
|
||||||
class SyncAdmin(admin.ModelAdmin):
|
class SyncAdmin(admin.ModelAdmin):
|
||||||
|
@ -1,9 +1,13 @@
|
|||||||
from abc import ABC, abstractmethod
|
from abc import ABC, abstractmethod
|
||||||
|
|
||||||
from cookbook.models import ShoppingListEntry, Space
|
from cookbook.models import ShoppingListEntry, Space, ConnectorConfig
|
||||||
|
|
||||||
|
|
||||||
class Connector(ABC):
|
class Connector(ABC):
|
||||||
|
@abstractmethod
|
||||||
|
def __init__(self, config: ConnectorConfig):
|
||||||
|
pass
|
||||||
|
|
||||||
@abstractmethod
|
@abstractmethod
|
||||||
async def on_shopping_list_entry_created(self, space: Space, instance: ShoppingListEntry) -> None:
|
async def on_shopping_list_entry_created(self, space: Space, instance: ShoppingListEntry) -> None:
|
||||||
pass
|
pass
|
||||||
@ -20,5 +24,4 @@ class Connector(ABC):
|
|||||||
async def close(self) -> None:
|
async def close(self) -> None:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
# TODO: Maybe add an 'IsEnabled(self) -> Bool' to here
|
|
||||||
# TODO: Add Recipes & possibly Meal Place listeners/hooks (And maybe more?)
|
# TODO: Add Recipes & possibly Meal Place listeners/hooks (And maybe more?)
|
||||||
|
@ -12,15 +12,13 @@ from typing import List, Any, Dict, Optional
|
|||||||
from django_scopes import scope
|
from django_scopes import scope
|
||||||
|
|
||||||
from cookbook.connectors.connector import Connector
|
from cookbook.connectors.connector import Connector
|
||||||
from cookbook.connectors.example import Example
|
|
||||||
from cookbook.connectors.homeassistant import HomeAssistant
|
from cookbook.connectors.homeassistant import HomeAssistant
|
||||||
from cookbook.models import ShoppingListEntry, Recipe, MealPlan, Space, HomeAssistantConfig, ExampleConfig
|
from cookbook.models import ShoppingListEntry, Recipe, MealPlan, Space, ConnectorConfig
|
||||||
|
|
||||||
multiprocessing.set_start_method('fork') # https://code.djangoproject.com/ticket/31169
|
multiprocessing.set_start_method('fork') # https://code.djangoproject.com/ticket/31169
|
||||||
|
|
||||||
QUEUE_MAX_SIZE = 25
|
QUEUE_MAX_SIZE = 25
|
||||||
REGISTERED_CLASSES: UnionType = ShoppingListEntry | Recipe | MealPlan
|
REGISTERED_CLASSES: UnionType = ShoppingListEntry | Recipe | MealPlan
|
||||||
CONNECTOR_UPDATE_CLASSES: UnionType = HomeAssistantConfig | ExampleConfig
|
|
||||||
|
|
||||||
|
|
||||||
class ActionType(Enum):
|
class ActionType(Enum):
|
||||||
@ -37,7 +35,7 @@ class Work:
|
|||||||
|
|
||||||
class ConnectorManager:
|
class ConnectorManager:
|
||||||
_queue: JoinableQueue
|
_queue: JoinableQueue
|
||||||
_listening_to_classes = REGISTERED_CLASSES | CONNECTOR_UPDATE_CLASSES
|
_listening_to_classes = REGISTERED_CLASSES | ConnectorConfig
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self._queue = multiprocessing.JoinableQueue(maxsize=QUEUE_MAX_SIZE)
|
self._queue = multiprocessing.JoinableQueue(maxsize=QUEUE_MAX_SIZE)
|
||||||
@ -88,7 +86,7 @@ class ConnectorManager:
|
|||||||
break
|
break
|
||||||
|
|
||||||
# If a Connector was changed/updated, refresh connector from the database for said space
|
# If a Connector was changed/updated, refresh connector from the database for said space
|
||||||
refresh_connector_cache = isinstance(item.instance, CONNECTOR_UPDATE_CLASSES)
|
refresh_connector_cache = isinstance(item.instance, ConnectorConfig)
|
||||||
|
|
||||||
space: Space = item.instance.space
|
space: Space = item.instance.space
|
||||||
connectors: Optional[List[Connector]] = _connectors.get(space.name)
|
connectors: Optional[List[Connector]] = _connectors.get(space.name)
|
||||||
@ -98,10 +96,11 @@ class ConnectorManager:
|
|||||||
loop.run_until_complete(close_connectors(connectors))
|
loop.run_until_complete(close_connectors(connectors))
|
||||||
|
|
||||||
with scope(space=space):
|
with scope(space=space):
|
||||||
connectors: List[Connector] = [
|
connectors: List[Connector] = list(
|
||||||
*(HomeAssistant(config) for config in space.homeassistantconfig_set.all() if config.enabled),
|
filter(
|
||||||
*(Example(config) for config in space.exampleconfig_set.all() if config.enabled)
|
lambda x: x is not None,
|
||||||
]
|
[ConnectorManager.get_connected_for_config(config) for config in space.connectorconfig_set.all() if config.enabled],
|
||||||
|
))
|
||||||
_connectors[space.name] = connectors
|
_connectors[space.name] = connectors
|
||||||
|
|
||||||
if len(connectors) == 0 or refresh_connector_cache:
|
if len(connectors) == 0 or refresh_connector_cache:
|
||||||
@ -113,6 +112,14 @@ class ConnectorManager:
|
|||||||
|
|
||||||
loop.close()
|
loop.close()
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def get_connected_for_config(config: ConnectorConfig) -> Optional[Connector]:
|
||||||
|
match config.type:
|
||||||
|
case ConnectorConfig.HOMEASSISTANT:
|
||||||
|
return HomeAssistant(config)
|
||||||
|
case _:
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
async def close_connectors(connectors: List[Connector]):
|
async def close_connectors(connectors: List[Connector]):
|
||||||
tasks: List[Task] = [asyncio.create_task(connector.close()) for connector in connectors]
|
tasks: List[Task] = [asyncio.create_task(connector.close()) for connector in connectors]
|
||||||
|
@ -1,27 +0,0 @@
|
|||||||
from cookbook.connectors.connector import Connector
|
|
||||||
from cookbook.models import ExampleConfig, Space, ShoppingListEntry
|
|
||||||
|
|
||||||
|
|
||||||
class Example(Connector):
|
|
||||||
_config: ExampleConfig
|
|
||||||
|
|
||||||
def __init__(self, config: ExampleConfig):
|
|
||||||
self._config = config
|
|
||||||
|
|
||||||
async def on_shopping_list_entry_created(self, space: Space, shopping_list_entry: ShoppingListEntry) -> None:
|
|
||||||
if not self._config.on_shopping_list_entry_created_enabled:
|
|
||||||
return
|
|
||||||
pass
|
|
||||||
|
|
||||||
async def on_shopping_list_entry_updated(self, space: Space, shopping_list_entry: ShoppingListEntry) -> None:
|
|
||||||
if not self._config.on_shopping_list_entry_updated_enabled:
|
|
||||||
return
|
|
||||||
pass
|
|
||||||
|
|
||||||
async def on_shopping_list_entry_deleted(self, space: Space, shopping_list_entry: ShoppingListEntry) -> None:
|
|
||||||
if not self._config.on_shopping_list_entry_deleted_enabled:
|
|
||||||
return
|
|
||||||
pass
|
|
||||||
|
|
||||||
async def close(self) -> None:
|
|
||||||
pass
|
|
@ -4,16 +4,19 @@ from logging import Logger
|
|||||||
from homeassistant_api import Client, HomeassistantAPIError, Domain
|
from homeassistant_api import Client, HomeassistantAPIError, Domain
|
||||||
|
|
||||||
from cookbook.connectors.connector import Connector
|
from cookbook.connectors.connector import Connector
|
||||||
from cookbook.models import ShoppingListEntry, HomeAssistantConfig, Space
|
from cookbook.models import ShoppingListEntry, ConnectorConfig, Space
|
||||||
|
|
||||||
|
|
||||||
class HomeAssistant(Connector):
|
class HomeAssistant(Connector):
|
||||||
_domains_cache: dict[str, Domain]
|
_domains_cache: dict[str, Domain]
|
||||||
_config: HomeAssistantConfig
|
_config: ConnectorConfig
|
||||||
_logger: Logger
|
_logger: Logger
|
||||||
_client: Client
|
_client: Client
|
||||||
|
|
||||||
def __init__(self, config: HomeAssistantConfig):
|
def __init__(self, config: ConnectorConfig):
|
||||||
|
if not config.token or not config.url or not config.todo_entity:
|
||||||
|
raise ValueError("config for HomeAssistantConnector in incomplete")
|
||||||
|
|
||||||
self._domains_cache = dict()
|
self._domains_cache = dict()
|
||||||
self._config = config
|
self._config = config
|
||||||
self._logger = logging.getLogger("connector.HomeAssistant")
|
self._logger = logging.getLogger("connector.HomeAssistant")
|
||||||
|
@ -10,7 +10,7 @@ from django_scopes.forms import SafeModelChoiceField, SafeModelMultipleChoiceFie
|
|||||||
from hcaptcha.fields import hCaptchaField
|
from hcaptcha.fields import hCaptchaField
|
||||||
|
|
||||||
from .models import (Comment, Food, InviteLink, Keyword, Recipe, RecipeBook, RecipeBookEntry,
|
from .models import (Comment, Food, InviteLink, Keyword, Recipe, RecipeBook, RecipeBookEntry,
|
||||||
SearchPreference, Space, Storage, Sync, User, UserPreference, HomeAssistantConfig, ExampleConfig)
|
SearchPreference, Space, Storage, Sync, User, UserPreference, ConnectorConfig)
|
||||||
|
|
||||||
|
|
||||||
class SelectWidget(widgets.Select):
|
class SelectWidget(widgets.Select):
|
||||||
@ -209,11 +209,6 @@ class ConnectorConfigForm(forms.ModelForm):
|
|||||||
required=False,
|
required=False,
|
||||||
)
|
)
|
||||||
|
|
||||||
class Meta:
|
|
||||||
fields = ('name', 'enabled', 'on_shopping_list_entry_created_enabled', 'on_shopping_list_entry_updated_enabled', 'on_shopping_list_entry_deleted_enabled')
|
|
||||||
|
|
||||||
|
|
||||||
class HomeAssistantConfigForm(ConnectorConfigForm):
|
|
||||||
update_token = forms.CharField(
|
update_token = forms.CharField(
|
||||||
widget=forms.TextInput(attrs={'autocomplete': 'update-token', 'type': 'password'}),
|
widget=forms.TextInput(attrs={'autocomplete': 'update-token', 'type': 'password'}),
|
||||||
required=False,
|
required=False,
|
||||||
@ -221,45 +216,23 @@ class HomeAssistantConfigForm(ConnectorConfigForm):
|
|||||||
)
|
)
|
||||||
|
|
||||||
url = forms.URLField(
|
url = forms.URLField(
|
||||||
required=True,
|
required=False,
|
||||||
help_text=_('Something like http://homeassistant.local:8123/api'),
|
help_text=_('Something like http://homeassistant.local:8123/api'),
|
||||||
)
|
)
|
||||||
|
|
||||||
on_shopping_list_entry_created_enabled = forms.BooleanField(
|
|
||||||
help_text="Enable syncing ShoppingListEntry to Homeassistant Todo List -- Warning: Might have negative performance impact",
|
|
||||||
required=False,
|
|
||||||
)
|
|
||||||
|
|
||||||
on_shopping_list_entry_updated_enabled = forms.BooleanField(
|
|
||||||
help_text="PLACEHOLDER",
|
|
||||||
required=False,
|
|
||||||
)
|
|
||||||
|
|
||||||
on_shopping_list_entry_deleted_enabled = forms.BooleanField(
|
|
||||||
help_text="Enable syncing ShoppingListEntry deletion to Homeassistant Todo List -- Warning: Might have negative performance impact",
|
|
||||||
required=False,
|
|
||||||
)
|
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = HomeAssistantConfig
|
model = ConnectorConfig
|
||||||
|
|
||||||
fields = ConnectorConfigForm.Meta.fields + ('url', 'todo_entity')
|
fields = (
|
||||||
|
'name', 'type', 'enabled', 'on_shopping_list_entry_created_enabled', 'on_shopping_list_entry_updated_enabled',
|
||||||
|
'on_shopping_list_entry_deleted_enabled', 'url', 'todo_entity',
|
||||||
|
)
|
||||||
|
|
||||||
help_texts = {
|
help_texts = {
|
||||||
'url': _('http://homeassistant.local:8123/api for example'),
|
'url': _('http://homeassistant.local:8123/api for example'),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
class ExampleConfigForm(ConnectorConfigForm):
|
|
||||||
feed_url = forms.URLField(
|
|
||||||
required=False,
|
|
||||||
)
|
|
||||||
|
|
||||||
class Meta:
|
|
||||||
model = ExampleConfig
|
|
||||||
fields = ConnectorConfigForm.Meta.fields + ('feed_url',)
|
|
||||||
|
|
||||||
|
|
||||||
# TODO: Deprecate
|
# TODO: Deprecate
|
||||||
class RecipeBookEntryForm(forms.ModelForm):
|
class RecipeBookEntryForm(forms.ModelForm):
|
||||||
prefix = 'bookmark'
|
prefix = 'bookmark'
|
||||||
|
36
cookbook/migrations/0208_connectorconfig.py
Normal file
36
cookbook/migrations/0208_connectorconfig.py
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
# Generated by Django 4.2.7 on 2024-01-17 21:12
|
||||||
|
|
||||||
|
import cookbook.models
|
||||||
|
from django.conf import settings
|
||||||
|
import django.core.validators
|
||||||
|
from django.db import migrations, models
|
||||||
|
import django.db.models.deletion
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||||
|
('cookbook', '0207_space_logo_color_128_space_logo_color_144_and_more'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='ConnectorConfig',
|
||||||
|
fields=[
|
||||||
|
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||||
|
('name', models.CharField(max_length=128, validators=[django.core.validators.MinLengthValidator(1)])),
|
||||||
|
('type', models.CharField(choices=[('HomeAssistant', 'HomeAssistant')], default='HomeAssistant', max_length=128)),
|
||||||
|
('enabled', models.BooleanField(default=True, help_text='Is Connector Enabled')),
|
||||||
|
('on_shopping_list_entry_created_enabled', models.BooleanField(default=False)),
|
||||||
|
('on_shopping_list_entry_updated_enabled', models.BooleanField(default=False)),
|
||||||
|
('on_shopping_list_entry_deleted_enabled', models.BooleanField(default=False)),
|
||||||
|
('url', models.URLField(blank=True, null=True)),
|
||||||
|
('token', models.CharField(blank=True, max_length=512, null=True)),
|
||||||
|
('todo_entity', models.CharField(blank=True, max_length=128, null=True)),
|
||||||
|
('created_by', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to=settings.AUTH_USER_MODEL)),
|
||||||
|
('space', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='cookbook.space')),
|
||||||
|
],
|
||||||
|
bases=(models.Model, cookbook.models.PermissionModelMixin),
|
||||||
|
),
|
||||||
|
]
|
@ -1,56 +0,0 @@
|
|||||||
# Generated by Django 4.2.7 on 2024-01-14 16:00
|
|
||||||
|
|
||||||
import cookbook.models
|
|
||||||
from django.conf import settings
|
|
||||||
import django.core.validators
|
|
||||||
from django.db import migrations, models
|
|
||||||
import django.db.models.deletion
|
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
|
||||||
|
|
||||||
dependencies = [
|
|
||||||
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
|
||||||
('cookbook', '0207_space_logo_color_128_space_logo_color_144_and_more'),
|
|
||||||
]
|
|
||||||
|
|
||||||
operations = [
|
|
||||||
migrations.CreateModel(
|
|
||||||
name='HomeAssistantConfig',
|
|
||||||
fields=[
|
|
||||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
|
||||||
('name', models.CharField(max_length=128, validators=[django.core.validators.MinLengthValidator(1)])),
|
|
||||||
('enabled', models.BooleanField(default=True, help_text='Is Connector Enabled')),
|
|
||||||
('on_shopping_list_entry_created_enabled', models.BooleanField(default=False)),
|
|
||||||
('on_shopping_list_entry_updated_enabled', models.BooleanField(default=False)),
|
|
||||||
('on_shopping_list_entry_deleted_enabled', models.BooleanField(default=False)),
|
|
||||||
('url', models.URLField(blank=True)),
|
|
||||||
('token', models.CharField(blank=True, max_length=512)),
|
|
||||||
('todo_entity', models.CharField(default='todo.shopping_list', max_length=128)),
|
|
||||||
('created_by', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to=settings.AUTH_USER_MODEL)),
|
|
||||||
('space', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='cookbook.space')),
|
|
||||||
],
|
|
||||||
options={
|
|
||||||
'abstract': False,
|
|
||||||
},
|
|
||||||
bases=(models.Model, cookbook.models.PermissionModelMixin),
|
|
||||||
),
|
|
||||||
migrations.CreateModel(
|
|
||||||
name='ExampleConfig',
|
|
||||||
fields=[
|
|
||||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
|
||||||
('name', models.CharField(max_length=128, validators=[django.core.validators.MinLengthValidator(1)])),
|
|
||||||
('enabled', models.BooleanField(default=True, help_text='Is Connector Enabled')),
|
|
||||||
('on_shopping_list_entry_created_enabled', models.BooleanField(default=False)),
|
|
||||||
('on_shopping_list_entry_updated_enabled', models.BooleanField(default=False)),
|
|
||||||
('on_shopping_list_entry_deleted_enabled', models.BooleanField(default=False)),
|
|
||||||
('feed_url', models.URLField(blank=True)),
|
|
||||||
('created_by', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to=settings.AUTH_USER_MODEL)),
|
|
||||||
('space', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='cookbook.space')),
|
|
||||||
],
|
|
||||||
options={
|
|
||||||
'abstract': False,
|
|
||||||
},
|
|
||||||
bases=(models.Model, cookbook.models.PermissionModelMixin),
|
|
||||||
),
|
|
||||||
]
|
|
@ -339,8 +339,7 @@ class Space(ExportModelOperationsMixin('space'), models.Model):
|
|||||||
SyncLog.objects.filter(sync__space=self).delete()
|
SyncLog.objects.filter(sync__space=self).delete()
|
||||||
Sync.objects.filter(space=self).delete()
|
Sync.objects.filter(space=self).delete()
|
||||||
Storage.objects.filter(space=self).delete()
|
Storage.objects.filter(space=self).delete()
|
||||||
HomeAssistantConfig.objects.filter(space=self).delete()
|
ConnectorConfig.objects.filter(space=self).delete()
|
||||||
ExampleConfig.objects.filter(space=self).delete()
|
|
||||||
|
|
||||||
ShoppingListEntry.objects.filter(shoppinglist__space=self).delete()
|
ShoppingListEntry.objects.filter(shoppinglist__space=self).delete()
|
||||||
ShoppingListRecipe.objects.filter(shoppinglist__space=self).delete()
|
ShoppingListRecipe.objects.filter(shoppinglist__space=self).delete()
|
||||||
@ -366,30 +365,28 @@ class Space(ExportModelOperationsMixin('space'), models.Model):
|
|||||||
|
|
||||||
|
|
||||||
class ConnectorConfig(models.Model, PermissionModelMixin):
|
class ConnectorConfig(models.Model, PermissionModelMixin):
|
||||||
|
HOMEASSISTANT = 'HomeAssistant'
|
||||||
|
CONNECTER_TYPE = ((HOMEASSISTANT, 'HomeAssistant'),)
|
||||||
|
|
||||||
name = models.CharField(max_length=128, validators=[MinLengthValidator(1)])
|
name = models.CharField(max_length=128, validators=[MinLengthValidator(1)])
|
||||||
|
type = models.CharField(
|
||||||
|
choices=CONNECTER_TYPE, max_length=128, default=HOMEASSISTANT
|
||||||
|
)
|
||||||
|
|
||||||
enabled = models.BooleanField(default=True, help_text="Is Connector Enabled")
|
enabled = models.BooleanField(default=True, help_text="Is Connector Enabled")
|
||||||
on_shopping_list_entry_created_enabled = models.BooleanField(default=False)
|
on_shopping_list_entry_created_enabled = models.BooleanField(default=False)
|
||||||
on_shopping_list_entry_updated_enabled = models.BooleanField(default=False)
|
on_shopping_list_entry_updated_enabled = models.BooleanField(default=False)
|
||||||
on_shopping_list_entry_deleted_enabled = models.BooleanField(default=False)
|
on_shopping_list_entry_deleted_enabled = models.BooleanField(default=False)
|
||||||
|
|
||||||
|
url = models.URLField(blank=True, null=True)
|
||||||
|
token = models.CharField(max_length=512, blank=True, null=True)
|
||||||
|
todo_entity = models.CharField(max_length=128, blank=True, null=True)
|
||||||
|
|
||||||
created_by = models.ForeignKey(User, on_delete=models.PROTECT)
|
created_by = models.ForeignKey(User, on_delete=models.PROTECT)
|
||||||
|
|
||||||
space = models.ForeignKey(Space, on_delete=models.CASCADE)
|
space = models.ForeignKey(Space, on_delete=models.CASCADE)
|
||||||
objects = ScopedManager(space='space')
|
objects = ScopedManager(space='space')
|
||||||
|
|
||||||
class Meta:
|
|
||||||
abstract = True
|
|
||||||
|
|
||||||
|
|
||||||
class HomeAssistantConfig(ConnectorConfig):
|
|
||||||
url = models.URLField(blank=True)
|
|
||||||
token = models.CharField(max_length=512, blank=True)
|
|
||||||
todo_entity = models.CharField(max_length=128, default='todo.shopping_list')
|
|
||||||
|
|
||||||
|
|
||||||
class ExampleConfig(ConnectorConfig):
|
|
||||||
feed_url = models.URLField(blank=True)
|
|
||||||
|
|
||||||
|
|
||||||
class UserPreference(models.Model, PermissionModelMixin):
|
class UserPreference(models.Model, PermissionModelMixin):
|
||||||
|
@ -34,7 +34,7 @@ from cookbook.models import (Automation, BookmarkletImport, Comment, CookLog, Cu
|
|||||||
ShareLink, ShoppingList, ShoppingListEntry, ShoppingListRecipe, Space,
|
ShareLink, ShoppingList, ShoppingListEntry, ShoppingListRecipe, Space,
|
||||||
Step, Storage, Supermarket, SupermarketCategory,
|
Step, Storage, Supermarket, SupermarketCategory,
|
||||||
SupermarketCategoryRelation, Sync, SyncLog, Unit, UnitConversion,
|
SupermarketCategoryRelation, Sync, SyncLog, Unit, UnitConversion,
|
||||||
UserFile, UserPreference, UserSpace, ViewLog, HomeAssistantConfig)
|
UserFile, UserPreference, UserSpace, ViewLog, ConnectorConfig)
|
||||||
from cookbook.templatetags.custom_tags import markdown
|
from cookbook.templatetags.custom_tags import markdown
|
||||||
from recipes.settings import AWS_ENABLED, MEDIA_URL
|
from recipes.settings import AWS_ENABLED, MEDIA_URL
|
||||||
|
|
||||||
@ -413,14 +413,14 @@ class StorageSerializer(SpacedModelSerializer):
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
class HomeAssistantConfigSerializer(SpacedModelSerializer):
|
class ConnectorConfigConfigSerializer(SpacedModelSerializer):
|
||||||
|
|
||||||
def create(self, validated_data):
|
def create(self, validated_data):
|
||||||
validated_data['created_by'] = self.context['request'].user
|
validated_data['created_by'] = self.context['request'].user
|
||||||
return super().create(validated_data)
|
return super().create(validated_data)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = HomeAssistantConfig
|
model = ConnectorConfig
|
||||||
fields = (
|
fields = (
|
||||||
'id', 'name', 'url', 'token', 'todo_entity', 'enabled',
|
'id', 'name', 'url', 'token', 'todo_entity', 'enabled',
|
||||||
'on_shopping_list_entry_created_enabled', 'on_shopping_list_entry_updated_enabled',
|
'on_shopping_list_entry_created_enabled', 'on_shopping_list_entry_updated_enabled',
|
||||||
|
@ -1,14 +1,9 @@
|
|||||||
from typing import Any, Dict
|
|
||||||
|
|
||||||
import django_tables2 as tables
|
import django_tables2 as tables
|
||||||
from django.utils.html import format_html
|
from django.utils.html import format_html
|
||||||
from django.utils.translation import gettext as _
|
from django.utils.translation import gettext as _
|
||||||
from django.views.generic import TemplateView
|
|
||||||
from django_tables2 import MultiTableMixin
|
|
||||||
from django_tables2.utils import A
|
from django_tables2.utils import A
|
||||||
|
|
||||||
from .helper.permission_helper import GroupRequiredMixin
|
from .models import CookLog, InviteLink, RecipeImport, Storage, Sync, SyncLog, ViewLog, ConnectorConfig
|
||||||
from .models import CookLog, InviteLink, RecipeImport, Storage, Sync, SyncLog, ViewLog, HomeAssistantConfig, ExampleConfig
|
|
||||||
|
|
||||||
|
|
||||||
class StorageTable(tables.Table):
|
class StorageTable(tables.Table):
|
||||||
@ -20,47 +15,13 @@ class StorageTable(tables.Table):
|
|||||||
fields = ('id', 'name', 'method')
|
fields = ('id', 'name', 'method')
|
||||||
|
|
||||||
|
|
||||||
class ExampleConfigTable(tables.Table):
|
class ConnectorConfigTable(tables.Table):
|
||||||
id = tables.LinkColumn('edit_example_config', args=[A('id')])
|
id = tables.LinkColumn('edit_connector_config', args=[A('id')])
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = ExampleConfig
|
model = ConnectorConfig
|
||||||
template_name = 'generic/table_template.html'
|
template_name = 'generic/table_template.html'
|
||||||
fields = ('id', 'name', 'enabled', 'feed_url')
|
fields = ('id', 'name', 'type', 'enabled')
|
||||||
attrs = {'table_name': "Example Configs", 'create_url': 'new_example_config'}
|
|
||||||
|
|
||||||
|
|
||||||
class HomeAssistantConfigTable(tables.Table):
|
|
||||||
id = tables.LinkColumn('edit_home_assistant_config', args=[A('id')])
|
|
||||||
|
|
||||||
class Meta:
|
|
||||||
model = HomeAssistantConfig
|
|
||||||
template_name = 'generic/table_template.html'
|
|
||||||
fields = ('id', 'name', 'enabled', 'url')
|
|
||||||
attrs = {'table_name': "HomeAssistant Configs", 'create_url': 'new_home_assistant_config'}
|
|
||||||
|
|
||||||
|
|
||||||
class ConnectorConfigTable(GroupRequiredMixin, MultiTableMixin, TemplateView):
|
|
||||||
groups_required = ['admin']
|
|
||||||
template_name = "list_connectors.html"
|
|
||||||
|
|
||||||
table_pagination = {
|
|
||||||
"per_page": 25
|
|
||||||
}
|
|
||||||
|
|
||||||
def get_context_data(self, **kwargs: Any) -> Dict[str, Any]:
|
|
||||||
kwargs = super().get_context_data(**kwargs)
|
|
||||||
kwargs['title'] = _("Connectors")
|
|
||||||
return kwargs
|
|
||||||
|
|
||||||
def get_tables(self):
|
|
||||||
example_configs = ExampleConfig.objects.filter(space=self.request.space).all()
|
|
||||||
home_assistant_configs = HomeAssistantConfig.objects.filter(space=self.request.space).all()
|
|
||||||
|
|
||||||
return [
|
|
||||||
ExampleConfigTable(example_configs),
|
|
||||||
HomeAssistantConfigTable(home_assistant_configs)
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
class ImportLogTable(tables.Table):
|
class ImportLogTable(tables.Table):
|
||||||
|
@ -336,7 +336,7 @@
|
|||||||
class="fas fa-server fa-fw"></i> {% trans 'Space Settings' %}</a>
|
class="fas fa-server fa-fw"></i> {% trans 'Space Settings' %}</a>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if request.user == request.space.created_by or user.is_superuser %}
|
{% if request.user == request.space.created_by or user.is_superuser %}
|
||||||
<a class="dropdown-item" href="{% url 'list_connectors' %}"><i
|
<a class="dropdown-item" href="{% url 'list_connector_config' %}"><i
|
||||||
class="fas fa-sync-alt fa-fw"></i> {% trans 'External Connectors' %}</a>
|
class="fas fa-sync-alt fa-fw"></i> {% trans 'External Connectors' %}</a>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if user.is_superuser %}
|
{% if user.is_superuser %}
|
||||||
|
@ -5,21 +5,21 @@ from django.contrib import auth
|
|||||||
from django.urls import reverse
|
from django.urls import reverse
|
||||||
from django_scopes import scopes_disabled
|
from django_scopes import scopes_disabled
|
||||||
|
|
||||||
from cookbook.models import HomeAssistantConfig
|
from cookbook.models import ConnectorConfig
|
||||||
|
|
||||||
LIST_URL = 'api:homeassistantconfig-list'
|
LIST_URL = 'api:connectorconfig-list'
|
||||||
DETAIL_URL = 'api:homeassistantconfig-detail'
|
DETAIL_URL = 'api:connectorconfig-detail'
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture()
|
@pytest.fixture()
|
||||||
def obj_1(space_1, u1_s1):
|
def obj_1(space_1, u1_s1):
|
||||||
return HomeAssistantConfig.objects.create(
|
return ConnectorConfig.objects.create(
|
||||||
name='HomeAssistant 1', token='token', url='url', todo_entity='todo.shopping_list', enabled=True, created_by=auth.get_user(u1_s1), space=space_1, )
|
name='HomeAssistant 1', token='token', url='url', todo_entity='todo.shopping_list', enabled=True, created_by=auth.get_user(u1_s1), space=space_1, )
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
def obj_2(space_1, u1_s1):
|
def obj_2(space_1, u1_s1):
|
||||||
return HomeAssistantConfig.objects.create(
|
return ConnectorConfig.objects.create(
|
||||||
name='HomeAssistant 2', token='token', url='url', todo_entity='todo.shopping_list', enabled=True, created_by=auth.get_user(u1_s1), space=space_1, )
|
name='HomeAssistant 2', token='token', url='url', todo_entity='todo.shopping_list', enabled=True, created_by=auth.get_user(u1_s1), space=space_1, )
|
||||||
|
|
||||||
|
|
||||||
@ -123,4 +123,4 @@ def test_delete(a1_s1, a1_s2, obj_1):
|
|||||||
|
|
||||||
assert r.status_code == 204
|
assert r.status_code == 204
|
||||||
with scopes_disabled():
|
with scopes_disabled():
|
||||||
assert HomeAssistantConfig.objects.count() == 0
|
assert ConnectorConfig.objects.count() == 0
|
||||||
|
@ -3,16 +3,18 @@ from django.contrib import auth
|
|||||||
from django.contrib import messages
|
from django.contrib import messages
|
||||||
from django.contrib.messages import get_messages
|
from django.contrib.messages import get_messages
|
||||||
from django.urls import reverse
|
from django.urls import reverse
|
||||||
|
from pytest_django.asserts import assertTemplateUsed
|
||||||
|
|
||||||
from cookbook.models import HomeAssistantConfig
|
from cookbook.models import ConnectorConfig
|
||||||
|
|
||||||
EDIT_VIEW_NAME = 'edit_home_assistant_config'
|
EDIT_VIEW_NAME = 'edit_connector_config'
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
def home_assistant_config_obj(a1_s1, space_1):
|
def home_assistant_config_obj(a1_s1, space_1):
|
||||||
return HomeAssistantConfig.objects.create(
|
return ConnectorConfig.objects.create(
|
||||||
name='HomeAssistant 1',
|
name='HomeAssistant 1',
|
||||||
|
type=ConnectorConfig.HOMEASSISTANT,
|
||||||
token='token',
|
token='token',
|
||||||
url='http://localhost:8123/api',
|
url='http://localhost:8123/api',
|
||||||
todo_entity='todo.shopping_list',
|
todo_entity='todo.shopping_list',
|
||||||
@ -22,20 +24,22 @@ def home_assistant_config_obj(a1_s1, space_1):
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def test_edit_home_assistant_config(home_assistant_config_obj: HomeAssistantConfig, a1_s1, a1_s2):
|
def test_edit_connector_config_homeassistant(home_assistant_config_obj: ConnectorConfig, a1_s1, a1_s2):
|
||||||
new_token = '1234_token'
|
new_token = '1234_token'
|
||||||
|
|
||||||
r = a1_s1.post(
|
r = a1_s1.post(
|
||||||
reverse(EDIT_VIEW_NAME, args={home_assistant_config_obj.pk}),
|
reverse(EDIT_VIEW_NAME, args={home_assistant_config_obj.pk}),
|
||||||
{
|
{
|
||||||
'name': home_assistant_config_obj.name,
|
'name': home_assistant_config_obj.name,
|
||||||
|
'type': home_assistant_config_obj.type,
|
||||||
'url': home_assistant_config_obj.url,
|
'url': home_assistant_config_obj.url,
|
||||||
'token': new_token,
|
'update_token': new_token,
|
||||||
'todo_entity': home_assistant_config_obj.todo_entity,
|
'todo_entity': home_assistant_config_obj.todo_entity,
|
||||||
'enabled': home_assistant_config_obj.enabled,
|
'enabled': home_assistant_config_obj.enabled,
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
assert r.status_code == 200
|
assert r.status_code == 302
|
||||||
|
|
||||||
r_messages = [m for m in get_messages(r.wsgi_request)]
|
r_messages = [m for m in get_messages(r.wsgi_request)]
|
||||||
assert not any(m.level > messages.SUCCESS for m in r_messages)
|
assert not any(m.level > messages.SUCCESS for m in r_messages)
|
||||||
|
|
||||||
@ -46,9 +50,10 @@ def test_edit_home_assistant_config(home_assistant_config_obj: HomeAssistantConf
|
|||||||
reverse(EDIT_VIEW_NAME, args={home_assistant_config_obj.pk}),
|
reverse(EDIT_VIEW_NAME, args={home_assistant_config_obj.pk}),
|
||||||
{
|
{
|
||||||
'name': home_assistant_config_obj.name,
|
'name': home_assistant_config_obj.name,
|
||||||
|
'type': home_assistant_config_obj.type,
|
||||||
'url': home_assistant_config_obj.url,
|
'url': home_assistant_config_obj.url,
|
||||||
'todo_entity': home_assistant_config_obj.todo_entity,
|
'todo_entity': home_assistant_config_obj.todo_entity,
|
||||||
'token': new_token,
|
'update_token': new_token,
|
||||||
'enabled': home_assistant_config_obj.enabled,
|
'enabled': home_assistant_config_obj.enabled,
|
||||||
}
|
}
|
||||||
)
|
)
|
@ -12,8 +12,7 @@ from recipes.settings import DEBUG, PLUGINS
|
|||||||
from .models import (Automation, Comment, CustomFilter, Food, InviteLink, Keyword, PropertyType,
|
from .models import (Automation, Comment, CustomFilter, Food, InviteLink, Keyword, PropertyType,
|
||||||
Recipe, RecipeBook, RecipeBookEntry, RecipeImport, ShoppingList, Space, Step,
|
Recipe, RecipeBook, RecipeBookEntry, RecipeImport, ShoppingList, Space, Step,
|
||||||
Storage, Supermarket, SupermarketCategory, Sync, SyncLog, Unit, UnitConversion,
|
Storage, Supermarket, SupermarketCategory, Sync, SyncLog, Unit, UnitConversion,
|
||||||
UserFile, UserSpace, get_model_name, HomeAssistantConfig, ExampleConfig)
|
UserFile, UserSpace, get_model_name, ConnectorConfig)
|
||||||
from .tables import ConnectorConfigTable
|
|
||||||
from .views import api, data, delete, edit, import_export, lists, new, telegram, views
|
from .views import api, data, delete, edit, import_export, lists, new, telegram, views
|
||||||
from .views.api import CustomAuthToken, ImportOpenData
|
from .views.api import CustomAuthToken, ImportOpenData
|
||||||
|
|
||||||
@ -52,7 +51,7 @@ router.register(r'shopping-list-recipe', api.ShoppingListRecipeViewSet)
|
|||||||
router.register(r'space', api.SpaceViewSet)
|
router.register(r'space', api.SpaceViewSet)
|
||||||
router.register(r'step', api.StepViewSet)
|
router.register(r'step', api.StepViewSet)
|
||||||
router.register(r'storage', api.StorageViewSet)
|
router.register(r'storage', api.StorageViewSet)
|
||||||
router.register(r'home-assistant-config', api.HomeAssistantConfigViewSet)
|
router.register(r'home-assistant-config', api.ConnectorConfigConfigViewSet)
|
||||||
router.register(r'supermarket', api.SupermarketViewSet)
|
router.register(r'supermarket', api.SupermarketViewSet)
|
||||||
router.register(r'supermarket-category', api.SupermarketCategoryViewSet)
|
router.register(r'supermarket-category', api.SupermarketCategoryViewSet)
|
||||||
router.register(r'supermarket-category-relation', api.SupermarketCategoryRelationViewSet)
|
router.register(r'supermarket-category-relation', api.SupermarketCategoryRelationViewSet)
|
||||||
@ -116,7 +115,6 @@ urlpatterns = [
|
|||||||
path('edit/recipe/convert/<int:pk>/', edit.convert_recipe, name='edit_convert_recipe'),
|
path('edit/recipe/convert/<int:pk>/', edit.convert_recipe, name='edit_convert_recipe'),
|
||||||
|
|
||||||
path('edit/storage/<int:pk>/', edit.edit_storage, name='edit_storage'),
|
path('edit/storage/<int:pk>/', edit.edit_storage, name='edit_storage'),
|
||||||
path('list/connectors', ConnectorConfigTable.as_view(), name='list_connectors'),
|
|
||||||
|
|
||||||
path('delete/recipe-source/<int:pk>/', delete.delete_recipe_source, name='delete_recipe_source'),
|
path('delete/recipe-source/<int:pk>/', delete.delete_recipe_source, name='delete_recipe_source'),
|
||||||
|
|
||||||
@ -169,7 +167,7 @@ urlpatterns = [
|
|||||||
]
|
]
|
||||||
|
|
||||||
generic_models = (
|
generic_models = (
|
||||||
Recipe, RecipeImport, Storage, HomeAssistantConfig, ExampleConfig, RecipeBook, SyncLog, Sync,
|
Recipe, RecipeImport, Storage, ConnectorConfig, RecipeBook, SyncLog, Sync,
|
||||||
Comment, RecipeBookEntry, ShoppingList, InviteLink, UserSpace, Space
|
Comment, RecipeBookEntry, ShoppingList, InviteLink, UserSpace, Space
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -76,7 +76,7 @@ from cookbook.models import (Automation, BookmarkletImport, CookLog, CustomFilte
|
|||||||
ShoppingListEntry, ShoppingListRecipe, Space, Step, Storage,
|
ShoppingListEntry, ShoppingListRecipe, Space, Step, Storage,
|
||||||
Supermarket, SupermarketCategory, SupermarketCategoryRelation, Sync,
|
Supermarket, SupermarketCategory, SupermarketCategoryRelation, Sync,
|
||||||
SyncLog, Unit, UnitConversion, UserFile, UserPreference, UserSpace,
|
SyncLog, Unit, UnitConversion, UserFile, UserPreference, UserSpace,
|
||||||
ViewLog, HomeAssistantConfig)
|
ViewLog, ConnectorConfig)
|
||||||
from cookbook.provider.dropbox import Dropbox
|
from cookbook.provider.dropbox import Dropbox
|
||||||
from cookbook.provider.local import Local
|
from cookbook.provider.local import Local
|
||||||
from cookbook.provider.nextcloud import Nextcloud
|
from cookbook.provider.nextcloud import Nextcloud
|
||||||
@ -102,7 +102,7 @@ from cookbook.serializer import (AccessTokenSerializer, AutomationSerializer,
|
|||||||
SupermarketCategorySerializer, SupermarketSerializer,
|
SupermarketCategorySerializer, SupermarketSerializer,
|
||||||
SyncLogSerializer, SyncSerializer, UnitConversionSerializer,
|
SyncLogSerializer, SyncSerializer, UnitConversionSerializer,
|
||||||
UnitSerializer, UserFileSerializer, UserPreferenceSerializer,
|
UnitSerializer, UserFileSerializer, UserPreferenceSerializer,
|
||||||
UserSerializer, UserSpaceSerializer, ViewLogSerializer, HomeAssistantConfigSerializer)
|
UserSerializer, UserSpaceSerializer, ViewLogSerializer, ConnectorConfigConfigSerializer)
|
||||||
from cookbook.views.import_export import get_integration
|
from cookbook.views.import_export import get_integration
|
||||||
from recipes import settings
|
from recipes import settings
|
||||||
from recipes.settings import FDC_API_KEY, DRF_THROTTLE_RECIPE_URL_IMPORT
|
from recipes.settings import FDC_API_KEY, DRF_THROTTLE_RECIPE_URL_IMPORT
|
||||||
@ -464,10 +464,9 @@ class StorageViewSet(viewsets.ModelViewSet):
|
|||||||
return self.queryset.filter(space=self.request.space)
|
return self.queryset.filter(space=self.request.space)
|
||||||
|
|
||||||
|
|
||||||
class HomeAssistantConfigViewSet(viewsets.ModelViewSet):
|
class ConnectorConfigConfigViewSet(viewsets.ModelViewSet):
|
||||||
# TODO handle delete protect error and adjust test
|
queryset = ConnectorConfig.objects
|
||||||
queryset = HomeAssistantConfig.objects
|
serializer_class = ConnectorConfigConfigSerializer
|
||||||
serializer_class = HomeAssistantConfigSerializer
|
|
||||||
permission_classes = [CustomIsAdmin & CustomTokenHasReadWriteScope]
|
permission_classes = [CustomIsAdmin & CustomTokenHasReadWriteScope]
|
||||||
|
|
||||||
def get_queryset(self):
|
def get_queryset(self):
|
||||||
|
@ -9,7 +9,7 @@ from django.views.generic import DeleteView
|
|||||||
|
|
||||||
from cookbook.helper.permission_helper import GroupRequiredMixin, OwnerRequiredMixin, group_required
|
from cookbook.helper.permission_helper import GroupRequiredMixin, OwnerRequiredMixin, group_required
|
||||||
from cookbook.models import (Comment, InviteLink, MealPlan, Recipe, RecipeBook, RecipeBookEntry,
|
from cookbook.models import (Comment, InviteLink, MealPlan, Recipe, RecipeBook, RecipeBookEntry,
|
||||||
RecipeImport, Space, Storage, Sync, UserSpace, HomeAssistantConfig, ExampleConfig)
|
RecipeImport, Space, Storage, Sync, UserSpace, ConnectorConfig)
|
||||||
from cookbook.provider.dropbox import Dropbox
|
from cookbook.provider.dropbox import Dropbox
|
||||||
from cookbook.provider.local import Local
|
from cookbook.provider.local import Local
|
||||||
from cookbook.provider.nextcloud import Nextcloud
|
from cookbook.provider.nextcloud import Nextcloud
|
||||||
@ -122,27 +122,15 @@ class StorageDelete(GroupRequiredMixin, DeleteView):
|
|||||||
return HttpResponseRedirect(reverse('list_storage'))
|
return HttpResponseRedirect(reverse('list_storage'))
|
||||||
|
|
||||||
|
|
||||||
class HomeAssistantConfigDelete(GroupRequiredMixin, DeleteView):
|
class ConnectorConfigDelete(GroupRequiredMixin, DeleteView):
|
||||||
groups_required = ['admin']
|
groups_required = ['admin']
|
||||||
template_name = "generic/delete_template.html"
|
template_name = "generic/delete_template.html"
|
||||||
model = HomeAssistantConfig
|
model = ConnectorConfig
|
||||||
success_url = reverse_lazy('list_connectors')
|
success_url = reverse_lazy('list_connector_config')
|
||||||
|
|
||||||
def get_context_data(self, **kwargs):
|
def get_context_data(self, **kwargs):
|
||||||
context = super().get_context_data(**kwargs)
|
context = super().get_context_data(**kwargs)
|
||||||
context['title'] = _("HomeAssistant Config Backend")
|
context['title'] = _("Connectors Config Backend")
|
||||||
return context
|
|
||||||
|
|
||||||
|
|
||||||
class ExampleConfigDelete(GroupRequiredMixin, DeleteView):
|
|
||||||
groups_required = ['admin']
|
|
||||||
template_name = "generic/delete_template.html"
|
|
||||||
model = ExampleConfig
|
|
||||||
success_url = reverse_lazy('list_connectors')
|
|
||||||
|
|
||||||
def get_context_data(self, **kwargs):
|
|
||||||
context = super().get_context_data(**kwargs)
|
|
||||||
context['title'] = _("Example Config Backend")
|
|
||||||
return context
|
return context
|
||||||
|
|
||||||
|
|
||||||
|
@ -9,10 +9,10 @@ from django.utils.translation import gettext as _
|
|||||||
from django.views.generic import UpdateView
|
from django.views.generic import UpdateView
|
||||||
from django.views.generic.edit import FormMixin
|
from django.views.generic.edit import FormMixin
|
||||||
|
|
||||||
from cookbook.forms import CommentForm, ExternalRecipeForm, StorageForm, SyncForm, HomeAssistantConfigForm, ExampleConfigForm
|
from cookbook.forms import CommentForm, ExternalRecipeForm, StorageForm, SyncForm, ConnectorConfigForm
|
||||||
from cookbook.helper.permission_helper import (GroupRequiredMixin, OwnerRequiredMixin,
|
from cookbook.helper.permission_helper import (GroupRequiredMixin, OwnerRequiredMixin,
|
||||||
above_space_limit, group_required)
|
above_space_limit, group_required)
|
||||||
from cookbook.models import Comment, Recipe, RecipeImport, Storage, Sync, HomeAssistantConfig, ExampleConfig
|
from cookbook.models import Comment, Recipe, RecipeImport, Storage, Sync, ConnectorConfig
|
||||||
from cookbook.provider.dropbox import Dropbox
|
from cookbook.provider.dropbox import Dropbox
|
||||||
from cookbook.provider.local import Local
|
from cookbook.provider.local import Local
|
||||||
from cookbook.provider.nextcloud import Nextcloud
|
from cookbook.provider.nextcloud import Nextcloud
|
||||||
@ -128,11 +128,11 @@ def edit_storage(request, pk):
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class HomeAssistantConfigUpdate(GroupRequiredMixin, UpdateView):
|
class ConnectorConfigUpdate(GroupRequiredMixin, UpdateView):
|
||||||
groups_required = ['admin']
|
groups_required = ['admin']
|
||||||
template_name = "generic/edit_template.html"
|
template_name = "generic/edit_template.html"
|
||||||
model = HomeAssistantConfig
|
model = ConnectorConfig
|
||||||
form_class = HomeAssistantConfigForm
|
form_class = ConnectorConfigForm
|
||||||
|
|
||||||
def get_form_kwargs(self):
|
def get_form_kwargs(self):
|
||||||
kwargs = super().get_form_kwargs()
|
kwargs = super().get_form_kwargs()
|
||||||
@ -143,33 +143,14 @@ class HomeAssistantConfigUpdate(GroupRequiredMixin, UpdateView):
|
|||||||
if form.cleaned_data['update_token'] != VALUE_NOT_CHANGED and form.cleaned_data['update_token'] != "":
|
if form.cleaned_data['update_token'] != VALUE_NOT_CHANGED and form.cleaned_data['update_token'] != "":
|
||||||
form.instance.token = form.cleaned_data['update_token']
|
form.instance.token = form.cleaned_data['update_token']
|
||||||
messages.add_message(self.request, messages.SUCCESS, _('Config saved!'))
|
messages.add_message(self.request, messages.SUCCESS, _('Config saved!'))
|
||||||
return super(HomeAssistantConfigUpdate, self).form_valid(form)
|
return super(ConnectorConfigUpdate, self).form_valid(form)
|
||||||
|
|
||||||
def get_success_url(self):
|
def get_success_url(self):
|
||||||
return reverse('edit_home_assistant_config', kwargs={'pk': self.object.pk})
|
return reverse('edit_connector_config', kwargs={'pk': self.object.pk})
|
||||||
|
|
||||||
def get_context_data(self, **kwargs):
|
def get_context_data(self, **kwargs):
|
||||||
context = super().get_context_data(**kwargs)
|
context = super().get_context_data(**kwargs)
|
||||||
context['title'] = _("HomeAssistantConfig")
|
context['title'] = _("ConnectorConfig")
|
||||||
return context
|
|
||||||
|
|
||||||
|
|
||||||
class ExampleConfigUpdate(GroupRequiredMixin, UpdateView):
|
|
||||||
groups_required = ['admin']
|
|
||||||
template_name = "generic/edit_template.html"
|
|
||||||
model = ExampleConfig
|
|
||||||
form_class = ExampleConfigForm
|
|
||||||
|
|
||||||
def form_valid(self, form):
|
|
||||||
messages.add_message(self.request, messages.SUCCESS, _('Config saved!'))
|
|
||||||
return super().form_valid(form)
|
|
||||||
|
|
||||||
def get_success_url(self):
|
|
||||||
return reverse('edit_example_config', kwargs={'pk': self.object.pk})
|
|
||||||
|
|
||||||
def get_context_data(self, **kwargs):
|
|
||||||
context = super().get_context_data(**kwargs)
|
|
||||||
context['title'] = _("ExampleConfig")
|
|
||||||
return context
|
return context
|
||||||
|
|
||||||
|
|
||||||
|
@ -6,8 +6,8 @@ from django.utils.translation import gettext as _
|
|||||||
from django_tables2 import RequestConfig
|
from django_tables2 import RequestConfig
|
||||||
|
|
||||||
from cookbook.helper.permission_helper import group_required
|
from cookbook.helper.permission_helper import group_required
|
||||||
from cookbook.models import InviteLink, RecipeImport, Storage, SyncLog, UserFile
|
from cookbook.models import InviteLink, RecipeImport, Storage, SyncLog, UserFile, ConnectorConfig
|
||||||
from cookbook.tables import ImportLogTable, InviteLinkTable, RecipeImportTable, StorageTable
|
from cookbook.tables import ImportLogTable, InviteLinkTable, RecipeImportTable, StorageTable, ConnectorConfigTable
|
||||||
|
|
||||||
|
|
||||||
@group_required('admin')
|
@group_required('admin')
|
||||||
@ -65,6 +65,22 @@ def storage(request):
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@group_required('admin')
|
||||||
|
def connector_config(request):
|
||||||
|
table = ConnectorConfigTable(ConnectorConfig.objects.filter(space=request.space).all())
|
||||||
|
RequestConfig(request, paginate={'per_page': 25}).configure(table)
|
||||||
|
|
||||||
|
return render(
|
||||||
|
request,
|
||||||
|
'generic/list_template.html',
|
||||||
|
{
|
||||||
|
'title': _("Connector Config Backend"),
|
||||||
|
'table': table,
|
||||||
|
'create_url': 'new_connector_config'
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
@group_required('admin')
|
@group_required('admin')
|
||||||
def invite_link(request):
|
def invite_link(request):
|
||||||
table = InviteLinkTable(
|
table = InviteLinkTable(
|
||||||
|
@ -5,9 +5,9 @@ from django.urls import reverse, reverse_lazy
|
|||||||
from django.utils.translation import gettext as _
|
from django.utils.translation import gettext as _
|
||||||
from django.views.generic import CreateView
|
from django.views.generic import CreateView
|
||||||
|
|
||||||
from cookbook.forms import ImportRecipeForm, Storage, StorageForm, HomeAssistantConfigForm, ExampleConfigForm
|
from cookbook.forms import ImportRecipeForm, Storage, StorageForm, ConnectorConfigForm
|
||||||
from cookbook.helper.permission_helper import GroupRequiredMixin, above_space_limit, group_required
|
from cookbook.helper.permission_helper import GroupRequiredMixin, above_space_limit, group_required
|
||||||
from cookbook.models import Recipe, RecipeImport, ShareLink, Step, HomeAssistantConfig, ExampleConfig
|
from cookbook.models import Recipe, RecipeImport, ShareLink, Step, ConnectorConfig
|
||||||
from recipes import settings
|
from recipes import settings
|
||||||
|
|
||||||
|
|
||||||
@ -70,21 +70,12 @@ class StorageCreate(GroupRequiredMixin, CreateView):
|
|||||||
return context
|
return context
|
||||||
|
|
||||||
|
|
||||||
class HomeAssistantConfigCreate(GroupRequiredMixin, CreateView):
|
class ConnectorConfigCreate(GroupRequiredMixin, CreateView):
|
||||||
groups_required = ['admin']
|
groups_required = ['admin']
|
||||||
template_name = "generic/new_template.html"
|
template_name = "generic/new_template.html"
|
||||||
model = HomeAssistantConfig
|
model = ConnectorConfig
|
||||||
form_class = HomeAssistantConfigForm
|
form_class = ConnectorConfigForm
|
||||||
success_url = reverse_lazy('list_home_assistant_config')
|
success_url = reverse_lazy('list_connector_config')
|
||||||
|
|
||||||
def get_form_class(self):
|
|
||||||
form_class = super().get_form_class()
|
|
||||||
|
|
||||||
if self.request.method == 'GET':
|
|
||||||
update_token_field = form_class.base_fields['update_token']
|
|
||||||
update_token_field.required = True
|
|
||||||
|
|
||||||
return form_class
|
|
||||||
|
|
||||||
def form_valid(self, form):
|
def form_valid(self, form):
|
||||||
if self.request.space.demo or settings.HOSTED:
|
if self.request.space.demo or settings.HOSTED:
|
||||||
@ -96,35 +87,11 @@ class HomeAssistantConfigCreate(GroupRequiredMixin, CreateView):
|
|||||||
obj.created_by = self.request.user
|
obj.created_by = self.request.user
|
||||||
obj.space = self.request.space
|
obj.space = self.request.space
|
||||||
obj.save()
|
obj.save()
|
||||||
return HttpResponseRedirect(reverse('edit_home_assistant_config', kwargs={'pk': obj.pk}))
|
return HttpResponseRedirect(reverse('edit_connector_config', kwargs={'pk': obj.pk}))
|
||||||
|
|
||||||
def get_context_data(self, **kwargs):
|
def get_context_data(self, **kwargs):
|
||||||
context = super().get_context_data(**kwargs)
|
context = super().get_context_data(**kwargs)
|
||||||
context['title'] = _("HomeAssistant Config Backend")
|
context['title'] = _("Connector Config Backend")
|
||||||
return context
|
|
||||||
|
|
||||||
|
|
||||||
class ExampleConfigCreate(GroupRequiredMixin, CreateView):
|
|
||||||
groups_required = ['admin']
|
|
||||||
template_name = "generic/new_template.html"
|
|
||||||
model = ExampleConfig
|
|
||||||
form_class = ExampleConfigForm
|
|
||||||
success_url = reverse_lazy('list_connectors')
|
|
||||||
|
|
||||||
def form_valid(self, form):
|
|
||||||
if self.request.space.demo or settings.HOSTED:
|
|
||||||
messages.add_message(self.request, messages.ERROR, _('This feature is not yet available in the hosted version of tandoor!'))
|
|
||||||
return redirect('index')
|
|
||||||
|
|
||||||
obj = form.save(commit=False)
|
|
||||||
obj.created_by = self.request.user
|
|
||||||
obj.space = self.request.space
|
|
||||||
obj.save()
|
|
||||||
return HttpResponseRedirect(reverse('edit_example_config', kwargs={'pk': obj.pk}))
|
|
||||||
|
|
||||||
def get_context_data(self, **kwargs):
|
|
||||||
context = super().get_context_data(**kwargs)
|
|
||||||
context['title'] = _("Example Config Backend")
|
|
||||||
return context
|
return context
|
||||||
|
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user