From: Radek Czajka Date: Fri, 6 Mar 2026 13:53:44 +0000 (+0100) Subject: Partners X-Git-Url: https://git.mdrn.pl/wolnelektury.git/commitdiff_plain/68be98c55bfd8af8acbf9490d7db5f3f0c528d60?ds=inline Partners --- diff --git a/src/api/urls.py b/src/api/urls.py index 182e0dd6f..1a1b4f337 100644 --- a/src/api/urls.py +++ b/src/api/urls.py @@ -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 index 000000000..e69de29bb diff --git a/src/partners/admin.py b/src/partners/admin.py new file mode 100644 index 000000000..3ad6775ce --- /dev/null +++ b/src/partners/admin.py @@ -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 index 000000000..df250fdd6 --- /dev/null +++ b/src/partners/api/urls.py @@ -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('/books/', + views.PartnerBooksView.as_view()), +] diff --git a/src/partners/api/views.py b/src/partners/api/views.py new file mode 100644 index 000000000..f8aaa0568 --- /dev/null +++ b/src/partners/api/views.py @@ -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 index 000000000..07969c76d --- /dev/null +++ b/src/partners/apps.py @@ -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 index 000000000..10d88f75e --- /dev/null +++ b/src/partners/migrations/0001_initial.py @@ -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 index 000000000..e69de29bb diff --git a/src/partners/models.py b/src/partners/models.py new file mode 100644 index 000000000..9e58e2a88 --- /dev/null +++ b/src/partners/models.py @@ -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 index 000000000..7ce503c2d --- /dev/null +++ b/src/partners/tests.py @@ -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 index 000000000..91ea44a21 --- /dev/null +++ b/src/partners/views.py @@ -0,0 +1,3 @@ +from django.shortcuts import render + +# Create your views here. diff --git a/src/wolnelektury/settings/apps.py b/src/wolnelektury/settings/apps.py index b9d507e86..c90152cde 100644 --- a/src/wolnelektury/settings/apps.py +++ b/src/wolnelektury/settings/apps.py @@ -41,6 +41,7 @@ INSTALLED_APPS_OUR = [ 'push', 'club', 'redirects', + 'partners', ] INSTALLED_APPS_CONTRIB = [