Partners
authorRadek Czajka <rczajka@rczajka.pl>
Fri, 6 Mar 2026 13:53:44 +0000 (14:53 +0100)
committerRadek Czajka <rczajka@rczajka.pl>
Fri, 6 Mar 2026 13:53:44 +0000 (14:53 +0100)
12 files changed:
src/api/urls.py
src/partners/__init__.py [new file with mode: 0644]
src/partners/admin.py [new file with mode: 0644]
src/partners/api/urls.py [new file with mode: 0644]
src/partners/api/views.py [new file with mode: 0644]
src/partners/apps.py [new file with mode: 0644]
src/partners/migrations/0001_initial.py [new file with mode: 0644]
src/partners/migrations/__init__.py [new file with mode: 0644]
src/partners/models.py [new file with mode: 0644]
src/partners/tests.py [new file with mode: 0644]
src/partners/views.py [new file with mode: 0644]
src/wolnelektury/settings/apps.py

index 182e0dd..1a1b4f3 100644 (file)
@@ -26,6 +26,8 @@ urlpatterns1 = [
     path('', include('bookmarks.api.urls')),
     path('', include('search.api.urls')),
     path('', include('push.api.urls')),
+
+    path('partners/', include('partners.api.urls')),
 ]
 
 
diff --git a/src/partners/__init__.py b/src/partners/__init__.py
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/src/partners/admin.py b/src/partners/admin.py
new file mode 100644 (file)
index 0000000..3ad6775
--- /dev/null
@@ -0,0 +1,15 @@
+from django.contrib import admin
+from . import models
+
+
+class PriceLevelInline(admin.TabularInline):
+    model = models.PriceLevel
+    extra = 0
+
+
+@admin.register(models.Partner)
+class PartnerAdmin(admin.ModelAdmin):
+    inlines = [
+        PriceLevelInline,
+    ]
+
diff --git a/src/partners/api/urls.py b/src/partners/api/urls.py
new file mode 100644 (file)
index 0000000..df250fd
--- /dev/null
@@ -0,0 +1,11 @@
+# This file is part of Wolne Lektury, licensed under GNU Affero GPLv3 or later.
+# Copyright © Fundacja Wolne Lektury. See NOTICE for more information.
+#
+from django.urls import path
+from . import views
+
+
+urlpatterns = [
+    path('<slug:key>/books/',
+        views.PartnerBooksView.as_view()),
+]
diff --git a/src/partners/api/views.py b/src/partners/api/views.py
new file mode 100644 (file)
index 0000000..f8aaa05
--- /dev/null
@@ -0,0 +1,36 @@
+from django.utils.decorators import method_decorator
+from django.views.decorators.cache import never_cache
+from rest_framework.generics import (ListAPIView, get_object_or_404)
+from rest_framework import serializers
+from api.fields import AbsoluteURLField
+from catalogue.models import Book
+from partners import models
+
+
+
+
+class PartnerBookSerializer(serializers.ModelSerializer):
+    url = AbsoluteURLField(view_name='catalogue_api_book', view_args=['slug'])
+    price = serializers.SerializerMethodField()
+
+    class Meta:
+        model = Book
+        fields = ['url', 'epub_url', 'price']
+
+    def get_price(self, obj):
+        if obj.pages is None:
+            return None
+        return self.context['partner'].get_price(obj.pages)
+
+
+@method_decorator(never_cache, name='dispatch')
+class PartnerBooksView(ListAPIView):
+    serializer_class = PartnerBookSerializer
+
+    def get_serializer_context(self):
+        ctx = super().get_serializer_context()
+        ctx['partner'] = get_object_or_404(models.Partner, key=self.kwargs['key'])
+        return ctx
+
+    def get_queryset(self):
+        return Book.objects.filter(parent=None).filter(can_sell=True).exclude(pages=None)
diff --git a/src/partners/apps.py b/src/partners/apps.py
new file mode 100644 (file)
index 0000000..07969c7
--- /dev/null
@@ -0,0 +1,6 @@
+from django.apps import AppConfig
+
+
+class PartnersConfig(AppConfig):
+    default_auto_field = 'django.db.models.BigAutoField'
+    name = 'partners'
diff --git a/src/partners/migrations/0001_initial.py b/src/partners/migrations/0001_initial.py
new file mode 100644 (file)
index 0000000..10d88f7
--- /dev/null
@@ -0,0 +1,35 @@
+# Generated by Django 4.0.8 on 2026-03-06 13:32
+
+from django.db import migrations, models
+import django.db.models.deletion
+
+
+class Migration(migrations.Migration):
+
+    initial = True
+
+    dependencies = [
+    ]
+
+    operations = [
+        migrations.CreateModel(
+            name='Partner',
+            fields=[
+                ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+                ('name', models.CharField(max_length=255)),
+                ('key', models.CharField(max_length=255)),
+            ],
+        ),
+        migrations.CreateModel(
+            name='PriceLevel',
+            fields=[
+                ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+                ('min_pages', models.IntegerField(blank=True, null=True)),
+                ('price', models.IntegerField()),
+                ('partner', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='partners.partner')),
+            ],
+            options={
+                'ordering': ('price',),
+            },
+        ),
+    ]
diff --git a/src/partners/migrations/__init__.py b/src/partners/migrations/__init__.py
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/src/partners/models.py b/src/partners/models.py
new file mode 100644 (file)
index 0000000..9e58e2a
--- /dev/null
@@ -0,0 +1,26 @@
+from django.db import models
+
+
+class Partner(models.Model):
+    name = models.CharField(max_length=255)
+    key = models.CharField(max_length=255)
+
+    def __str__(self):
+        return self.name
+    
+    def get_price(self, pages):
+        price_obj = self.pricelevel_set.exclude(
+            min_pages__gt=pages
+        ).order_by('-price').first()
+        if price_obj is None:
+            return None
+        return price_obj.price
+
+
+class PriceLevel(models.Model):
+    partner = models.ForeignKey(Partner, models.CASCADE)
+    min_pages = models.IntegerField(null=True, blank=True)
+    price = models.IntegerField()
+
+    class Meta:
+        ordering = ('price',)
diff --git a/src/partners/tests.py b/src/partners/tests.py
new file mode 100644 (file)
index 0000000..7ce503c
--- /dev/null
@@ -0,0 +1,3 @@
+from django.test import TestCase
+
+# Create your tests here.
diff --git a/src/partners/views.py b/src/partners/views.py
new file mode 100644 (file)
index 0000000..91ea44a
--- /dev/null
@@ -0,0 +1,3 @@
+from django.shortcuts import render
+
+# Create your views here.
index b9d507e..c90152c 100644 (file)
@@ -41,6 +41,7 @@ INSTALLED_APPS_OUR = [
     'push',
     'club',
     'redirects',
+    'partners',
 ]
 
 INSTALLED_APPS_CONTRIB = [