diff --git a/src/snek/mapper/__init__.py b/src/snek/mapper/__init__.py index 5428f10..69901ec 100644 --- a/src/snek/mapper/__init__.py +++ b/src/snek/mapper/__init__.py @@ -10,10 +10,12 @@ from snek.mapper.user import UserMapper from snek.mapper.user_property import UserPropertyMapper from snek.mapper.repository import RepositoryMapper from snek.mapper.channel_attachment import ChannelAttachmentMapper +from snek.mapper.container import ContainerMapper from snek.system.object import Object @functools.cache + def get_mappers(app=None): return Object( **{ @@ -27,6 +29,7 @@ def get_mappers(app=None): "user_property": UserPropertyMapper(app=app), "repository": RepositoryMapper(app=app), "channel_attachment": ChannelAttachmentMapper(app=app), + "container": ContainerMapper(app=app), } ) diff --git a/src/snek/mapper/container.py b/src/snek/mapper/container.py new file mode 100644 index 0000000..f88f9f0 --- /dev/null +++ b/src/snek/mapper/container.py @@ -0,0 +1,6 @@ +from snek.model.container import Container +from snek.system.mapper import BaseMapper + +class ContainerMapper(BaseMapper): + model_class = Container + table_name = "container" \ No newline at end of file diff --git a/src/snek/model/__init__.py b/src/snek/model/__init__.py index 17832c6..dec05e8 100644 --- a/src/snek/model/__init__.py +++ b/src/snek/model/__init__.py @@ -12,6 +12,7 @@ from snek.model.user import UserModel from snek.model.user_property import UserPropertyModel from snek.model.repository import RepositoryModel from snek.model.channel_attachment import ChannelAttachmentModel +from snek.model.container import Container from snek.system.object import Object @@ -29,6 +30,7 @@ def get_models(): "user_property": UserPropertyModel, "repository": RepositoryModel, "channel_attachment": ChannelAttachmentModel, + "container": Container, } ) diff --git a/src/snek/model/container.py b/src/snek/model/container.py new file mode 100644 index 0000000..3947a8d --- /dev/null +++ b/src/snek/model/container.py @@ -0,0 +1,10 @@ +from snek.system.model import BaseModel, ModelField + +class Container(BaseModel): + id = ModelField(name="id", required=True, kind=str) + name = ModelField(name="name", required=True, kind=str) + status = ModelField(name="status", required=True, kind=str) + resources = ModelField(name="resources", required=False, kind=str) + user_uid = ModelField(name="user_uid", required=False, kind=str) + path = ModelField(name="path", required=False, kind=str) + readonly = ModelField(name="readonly", required=False, kind=bool, default=False) diff --git a/src/snek/service/__init__.py b/src/snek/service/__init__.py index dae9e09..5669bdb 100644 --- a/src/snek/service/__init__.py +++ b/src/snek/service/__init__.py @@ -13,6 +13,7 @@ from snek.service.user_property import UserPropertyService from snek.service.util import UtilService from snek.service.repository import RepositoryService from snek.service.channel_attachment import ChannelAttachmentService +from snek.service.container import ContainerService from snek.system.object import Object from snek.service.db import DBService @@ -34,6 +35,7 @@ def get_services(app): "repository": RepositoryService(app=app), "db": DBService(app=app), "channel_attachment": ChannelAttachmentService(app=app), + "container": ContainerService(app=app), } ) diff --git a/src/snek/service/container.py b/src/snek/service/container.py new file mode 100644 index 0000000..4025e9e --- /dev/null +++ b/src/snek/service/container.py @@ -0,0 +1,29 @@ +from snek.system.service import BaseService + +class ContainerService(BaseService): + mapper_name = "container" + + async def create(self, id, name, status, resources=None, user_uid=None, path=None, readonly=False): + model = await self.new() + model["id"] = id + model["name"] = name + model["status"] = status + if resources: + model["resources"] = resources + if user_uid: + model["user_uid"] = user_uid + if path: + model["path"] = path + model["readonly"] = readonly + if await super().save(model): + return model + raise Exception(f"Failed to create container: {model.errors}") + + async def get(self, id): + return await self.mapper.get(id) + + async def update(self, model): + return await self.mapper.update(model) + + async def delete(self, id): + return await self.mapper.delete(id) diff --git a/src/snek/templates/settings/containers/create.html b/src/snek/templates/settings/containers/create.html new file mode 100644 index 0000000..9b60f3e --- /dev/null +++ b/src/snek/templates/settings/containers/create.html @@ -0,0 +1,39 @@ +{% extends 'settings/index.html' %} + +{% block header_text %}

Create Container

{% endblock %} + +{% block main %} +{% include 'settings/containers/form.html' %} +
+
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ +
+ + +
+
+{% endblock %} diff --git a/src/snek/templates/settings/containers/delete.html b/src/snek/templates/settings/containers/delete.html new file mode 100644 index 0000000..a0fc249 --- /dev/null +++ b/src/snek/templates/settings/containers/delete.html @@ -0,0 +1,17 @@ +{% extends 'settings/index.html' %} + +{% block header_text %}

Delete Container

{% endblock %} + +{% block main %} +
+

Are you sure you want to delete the following container?

+
{{ container.name }}
+
+ +
+ + +
+
+
+{% endblock %} diff --git a/src/snek/templates/settings/containers/form.html b/src/snek/templates/settings/containers/form.html new file mode 100644 index 0000000..8785cc6 --- /dev/null +++ b/src/snek/templates/settings/containers/form.html @@ -0,0 +1,28 @@ + + + + diff --git a/src/snek/templates/settings/containers/index.html b/src/snek/templates/settings/containers/index.html new file mode 100644 index 0000000..a6855dd --- /dev/null +++ b/src/snek/templates/settings/containers/index.html @@ -0,0 +1,96 @@ +{% extends 'settings/index.html' %} + +{% block header_text %}

Containers

{% endblock %} + +{% block main %} + + + + + Containers - List + + + + +
+
+ + New Container + +
+
+ {% for container in containers %} +
+
+ {{ container.name }} + {{ container.status }} + + + {% if container.readonly %}Readonly{% else %}Writable{% endif %} + +
+ +
+ {% endfor %} +
+
+ + +{% endblock %} diff --git a/src/snek/templates/settings/containers/update.html b/src/snek/templates/settings/containers/update.html new file mode 100644 index 0000000..1c258cd --- /dev/null +++ b/src/snek/templates/settings/containers/update.html @@ -0,0 +1,40 @@ +{% extends "settings/index.html" %} + +{% block header_text %}

Update Container

{% endblock %} + +{% block main %} +{% include "settings/containers/form.html" %} +
+
+ +
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ +
+ + +
+
+{% endblock %} diff --git a/src/snek/view/settings/containers.py b/src/snek/view/settings/containers.py new file mode 100644 index 0000000..dae0237 --- /dev/null +++ b/src/snek/view/settings/containers.py @@ -0,0 +1,91 @@ +import asyncio +from aiohttp import web + +from snek.system.view import BaseFormView +import pathlib + +class ContainersIndexView(BaseFormView): + + login_required = True + + async def get(self): + + user_uid = self.session.get("uid") + + containers = [] + async for container in self.services.container.find(user_uid=user_uid): + containers.append(container.record) + + user = await self.services.user.get(uid=self.session.get("uid")) + + return await self.render_template("settings/containers/index.html", {"containers": containers, "user": user}) + +class ContainersCreateView(BaseFormView): + + login_required = True + + async def get(self): + + return await self.render_template("settings/containers/create.html") + + async def post(self): + data = await self.request.post() + container = await self.services.container.create( + user_uid=self.session.get("uid"), + name=data['name'], + status=data['status'], + resources=data.get('resources', ''), + path=data.get('path', ''), + readonly=bool(data.get('readonly', False)) + ) + return web.HTTPFound("/settings/containers/index.html") + +class ContainersUpdateView(BaseFormView): + + login_required = True + + async def get(self): + + container = await self.services.container.get( + user_uid=self.session.get("uid"), uid=self.request.match_info["uid"] + ) + if not container: + return web.HTTPNotFound() + return await self.render_template("settings/containers/update.html", {"container": container.record}) + + async def post(self): + data = await self.request.post() + container = await self.services.container.get( + user_uid=self.session.get("uid"), uid=self.request.match_info["uid"] + ) + container['status'] = data['status'] + container['resources'] = data.get('resources', '') + container['path'] = data.get('path', '') + container['readonly'] = bool(data.get('readonly', False)) + await self.services.container.save(container) + return web.HTTPFound("/settings/containers/index.html") + +class ContainersDeleteView(BaseFormView): + + login_required = True + + async def get(self): + + container = await self.services.container.get( + user_uid=self.session.get("uid"), uid=self.request.match_info["uid"] + ) + if not container: + return web.HTTPNotFound() + + return await self.render_template("settings/containers/delete.html", {"container": container.record}) + + async def post(self): + user_uid = self.session.get("uid") + uid = self.request.match_info["uid"] + container = await self.services.container.get( + user_uid=user_uid, uid=uid + ) + if not container: + return web.HTTPNotFound() + await self.services.container.delete(user_uid=user_uid, uid=uid) + return web.HTTPFound("/settings/containers/index.html")