WIP: Services #25
|
@ -4,6 +4,7 @@ from django.contrib import admin
|
||||||
|
|
||||||
from .models import Membership
|
from .models import Membership
|
||||||
from .models import MembershipType
|
from .models import MembershipType
|
||||||
|
from .models import ServiceAccess
|
||||||
from .models import SubscriptionPeriod
|
from .models import SubscriptionPeriod
|
||||||
|
|
||||||
|
|
||||||
|
@ -20,3 +21,9 @@ class MembershipTypeAdmin(admin.ModelAdmin):
|
||||||
@admin.register(SubscriptionPeriod)
|
@admin.register(SubscriptionPeriod)
|
||||||
class SubscriptionPeriodAdmin(admin.ModelAdmin):
|
class SubscriptionPeriodAdmin(admin.ModelAdmin):
|
||||||
"""Admin for SubscriptionPeriod model."""
|
"""Admin for SubscriptionPeriod model."""
|
||||||
|
|
||||||
|
|
||||||
|
@admin.register(ServiceAccess)
|
||||||
|
class ServiceAccessAdmin(admin.ModelAdmin):
|
||||||
|
"""Admin for ServiceAccess model."""
|
||||||
|
pass
|
||||||
|
|
|
@ -0,0 +1,64 @@
|
||||||
|
# Generated by Django 5.0.1 on 2024-01-13 19:20
|
||||||
|
|
||||||
|
import django.db.models.deletion
|
||||||
|
import django_registries.registry
|
||||||
|
import services.registry
|
||||||
|
from django.conf import settings
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
dependencies = [
|
||||||
|
("membership", "0005_member"),
|
||||||
|
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.CreateModel(
|
||||||
|
name="ServiceAccess",
|
||||||
|
fields=[
|
||||||
|
(
|
||||||
|
"id",
|
||||||
|
models.AutoField(
|
||||||
|
auto_created=True,
|
||||||
|
primary_key=True,
|
||||||
|
serialize=False,
|
||||||
|
verbose_name="ID",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"modified",
|
||||||
|
models.DateTimeField(auto_now=True, verbose_name="modified"),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"created",
|
||||||
|
models.DateTimeField(auto_now_add=True, verbose_name="created"),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"service",
|
||||||
|
django_registries.registry.ChoicesField(
|
||||||
|
choices=[],
|
||||||
|
registry=services.registry.ServiceRegistry,
|
||||||
|
verbose_name="service",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"user",
|
||||||
|
models.ForeignKey(
|
||||||
|
on_delete=django.db.models.deletion.PROTECT,
|
||||||
|
to=settings.AUTH_USER_MODEL,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
options={
|
||||||
|
"verbose_name": "service access",
|
||||||
|
"verbose_name_plural": "service accesses",
|
||||||
|
},
|
||||||
|
),
|
||||||
|
migrations.AddConstraint(
|
||||||
|
model_name="serviceaccess",
|
||||||
|
constraint=models.UniqueConstraint(
|
||||||
|
fields=("user", "service"), name="unique_user_service"
|
||||||
|
),
|
||||||
|
),
|
||||||
|
]
|
|
@ -10,6 +10,8 @@ from django.contrib.postgres.fields import RangeOperators
|
||||||
from django.db import models
|
from django.db import models
|
||||||
from django.utils import timezone
|
from django.utils import timezone
|
||||||
from django.utils.translation import gettext as _
|
from django.utils.translation import gettext as _
|
||||||
|
|
||||||
|
from services.registry import ServiceRegistry
|
||||||
from utils.mixins import CreatedModifiedAbstract
|
from utils.mixins import CreatedModifiedAbstract
|
||||||
|
|
||||||
|
|
||||||
|
@ -139,3 +141,22 @@ class MembershipType(CreatedModifiedAbstract):
|
||||||
|
|
||||||
def __str__(self) -> str:
|
def __str__(self) -> str:
|
||||||
return self.name
|
return self.name
|
||||||
|
|
||||||
|
|
||||||
|
class ServiceAccess(CreatedModifiedAbstract):
|
||||||
|
class Meta:
|
||||||
|
verbose_name = _("service access")
|
||||||
|
verbose_name_plural = _("service accesses")
|
||||||
|
constraints = [
|
||||||
|
models.UniqueConstraint(
|
||||||
|
fields=["user", "service"],
|
||||||
|
name="unique_user_service",
|
||||||
|
),
|
||||||
|
]
|
||||||
|
|
||||||
|
user = models.ForeignKey("auth.User", on_delete=models.PROTECT)
|
||||||
|
|||||||
|
|
||||||
|
service = ServiceRegistry.choices_field(verbose_name=_("service"))
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return f"{self.user} - {self.service}"
|
||||||
|
|
|
@ -43,12 +43,14 @@ THIRD_PARTY_APPS = [
|
||||||
"allauth",
|
"allauth",
|
||||||
"allauth.account",
|
"allauth.account",
|
||||||
"django_view_decorator",
|
"django_view_decorator",
|
||||||
|
"django_registries",
|
||||||
]
|
]
|
||||||
|
|
||||||
LOCAL_APPS = [
|
LOCAL_APPS = [
|
||||||
"utils",
|
"utils",
|
||||||
"accounting",
|
"accounting",
|
||||||
"membership",
|
"membership",
|
||||||
|
"services",
|
||||||
]
|
]
|
||||||
|
|
||||||
INSTALLED_APPS = [
|
INSTALLED_APPS = [
|
||||||
|
|
0
src/services/__init__.py
Normal file
0
src/services/__init__.py
Normal file
1
src/services/admin.py
Normal file
1
src/services/admin.py
Normal file
|
@ -0,0 +1 @@
|
||||||
|
# Register your models here.
|
6
src/services/apps.py
Normal file
6
src/services/apps.py
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
from django.apps import AppConfig
|
||||||
|
|
||||||
|
|
||||||
|
class ServicesConfig(AppConfig):
|
||||||
|
default_auto_field = "django.db.models.BigAutoField"
|
||||||
|
name = "services"
|
0
src/services/migrations/__init__.py
Normal file
0
src/services/migrations/__init__.py
Normal file
1
src/services/models.py
Normal file
1
src/services/models.py
Normal file
|
@ -0,0 +1 @@
|
||||||
|
# Create your models here.
|
21
src/services/registry.py
Normal file
21
src/services/registry.py
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
from django_registries.registry import Interface
|
||||||
|
from django_registries.registry import Registry
|
||||||
|
|
||||||
|
|
||||||
|
class ServiceRegistry(Registry):
|
||||||
|
"""Registry for services"""
|
||||||
|
|
||||||
|
implementations_module = "services"
|
||||||
|
|
||||||
|
|
||||||
|
class ServiceInterface(Interface):
|
||||||
|
"""Interface for services"""
|
||||||
|
|
||||||
|
registry = ServiceRegistry
|
||||||
|
|
||||||
|
public: bool = False
|
||||||
|
|
||||||
|
# TODO: add a way to add a something which defines the required fields for a service
|
||||||
|
# - maybe a list of tuples with the field name and the type of the field
|
||||||
|
# this could be used to generate a form for the service, and also to validate
|
||||||
|
# the data saved in a JSONField on the ServiceAccess model
|
27
src/services/services.py
Normal file
27
src/services/services.py
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
from .registry import ServiceInterface
|
||||||
|
|
||||||
|
|
||||||
|
class MailService(ServiceInterface):
|
||||||
|
slug = "mail"
|
||||||
|
|
||||||
|
|
||||||
|
class MatrixService(ServiceInterface):
|
||||||
|
slug = "matrix"
|
||||||
|
|
||||||
|
|
||||||
|
class MastodonService(ServiceInterface):
|
||||||
|
slug = "mastodon"
|
||||||
|
|
||||||
|
|
||||||
|
class NextcloudService(ServiceInterface):
|
||||||
|
slug = "nextcloud"
|
||||||
|
|
||||||
|
|
||||||
|
class HedgeDocService(ServiceInterface):
|
||||||
|
slug = "hedgedoc"
|
||||||
|
public = True
|
||||||
|
|
||||||
|
|
||||||
|
class RalllyService(ServiceInterface):
|
||||||
|
slug = "rallly"
|
||||||
|
public = True
|
1
src/services/tests.py
Normal file
1
src/services/tests.py
Normal file
|
@ -0,0 +1 @@
|
||||||
|
# Create your tests here.
|
1
src/services/views.py
Normal file
1
src/services/views.py
Normal file
|
@ -0,0 +1 @@
|
||||||
|
# Create your views here.
|
Loading…
Reference in a new issue
This is correct! 💯
Notable because there is also a Membership model.. however, memberships are renewed every year, and a user should be able to use the same services across different memberships.
How we handle "eligibility to a service" can happen later..