This commit is contained in:
Iliyan Angelov
2025-10-09 00:44:15 +03:00
parent 18ae8b9f88
commit dd8eb1c7aa
44 changed files with 3240 additions and 137 deletions

View File

View File

@@ -0,0 +1,38 @@
from django.contrib import admin
from .models import Policy, PolicySection
class PolicySectionInline(admin.TabularInline):
model = PolicySection
extra = 1
fields = ['heading', 'content', 'order', 'is_active']
@admin.register(Policy)
class PolicyAdmin(admin.ModelAdmin):
list_display = ['title', 'type', 'version', 'last_updated', 'effective_date', 'is_active']
list_filter = ['type', 'is_active', 'last_updated']
search_fields = ['title', 'type', 'description']
prepopulated_fields = {'slug': ('type',)}
inlines = [PolicySectionInline]
fieldsets = (
('Basic Information', {
'fields': ('type', 'title', 'slug', 'description')
}),
('Version & Dates', {
'fields': ('version', 'effective_date', 'last_updated')
}),
('Status', {
'fields': ('is_active',)
}),
)
@admin.register(PolicySection)
class PolicySectionAdmin(admin.ModelAdmin):
list_display = ['policy', 'heading', 'order', 'is_active']
list_filter = ['policy__type', 'is_active']
search_fields = ['heading', 'content']
list_editable = ['order', 'is_active']

View File

@@ -0,0 +1,8 @@
from django.apps import AppConfig
class PoliciesConfig(AppConfig):
default_auto_field = 'django.db.models.BigAutoField'
name = 'policies'
verbose_name = 'Policies Management'

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,54 @@
# Generated by Django 4.2.7 on 2025-10-08 13:54
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
initial = True
dependencies = [
]
operations = [
migrations.CreateModel(
name='Policy',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('type', models.CharField(choices=[('privacy', 'Privacy Policy'), ('terms', 'Terms of Use'), ('support', 'Support Policy')], help_text='Type of policy document', max_length=50, unique=True)),
('title', models.CharField(help_text='Title of the policy', max_length=200)),
('slug', models.SlugField(blank=True, max_length=100, unique=True)),
('description', models.TextField(blank=True, help_text='Brief description of the policy')),
('last_updated', models.DateField(auto_now=True, help_text='Last update date')),
('version', models.CharField(default='1.0', help_text='Policy version number', max_length=20)),
('is_active', models.BooleanField(default=True, help_text='Whether this policy is currently active')),
('effective_date', models.DateField(help_text='Date when this policy becomes effective')),
('created_at', models.DateTimeField(auto_now_add=True)),
('updated_at', models.DateTimeField(auto_now=True)),
],
options={
'verbose_name': 'Policy',
'verbose_name_plural': 'Policies',
'ordering': ['type'],
},
),
migrations.CreateModel(
name='PolicySection',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('heading', models.CharField(help_text='Section heading', max_length=300)),
('content', models.TextField(help_text='Section content')),
('order', models.IntegerField(default=0, help_text='Display order of sections')),
('is_active', models.BooleanField(default=True, help_text='Whether this section is currently active')),
('created_at', models.DateTimeField(auto_now_add=True)),
('updated_at', models.DateTimeField(auto_now=True)),
('policy', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='sections', to='policies.policy')),
],
options={
'verbose_name': 'Policy Section',
'verbose_name_plural': 'Policy Sections',
'ordering': ['policy', 'order'],
},
),
]

View File

@@ -0,0 +1,100 @@
from django.db import models
from django.utils.text import slugify
class Policy(models.Model):
"""
Model to store various policy documents (Privacy, Terms, Support, etc.)
"""
POLICY_TYPES = [
('privacy', 'Privacy Policy'),
('terms', 'Terms of Use'),
('support', 'Support Policy'),
]
type = models.CharField(
max_length=50,
choices=POLICY_TYPES,
unique=True,
help_text="Type of policy document"
)
title = models.CharField(
max_length=200,
help_text="Title of the policy"
)
slug = models.SlugField(
max_length=100,
unique=True,
blank=True
)
description = models.TextField(
blank=True,
help_text="Brief description of the policy"
)
last_updated = models.DateField(
auto_now=True,
help_text="Last update date"
)
version = models.CharField(
max_length=20,
default="1.0",
help_text="Policy version number"
)
is_active = models.BooleanField(
default=True,
help_text="Whether this policy is currently active"
)
effective_date = models.DateField(
help_text="Date when this policy becomes effective"
)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
class Meta:
verbose_name = "Policy"
verbose_name_plural = "Policies"
ordering = ['type']
def save(self, *args, **kwargs):
if not self.slug:
self.slug = slugify(self.type)
super().save(*args, **kwargs)
def __str__(self):
return f"{self.get_type_display()} (v{self.version})"
class PolicySection(models.Model):
"""
Individual sections within a policy document
"""
policy = models.ForeignKey(
Policy,
on_delete=models.CASCADE,
related_name='sections'
)
heading = models.CharField(
max_length=300,
help_text="Section heading"
)
content = models.TextField(
help_text="Section content"
)
order = models.IntegerField(
default=0,
help_text="Display order of sections"
)
is_active = models.BooleanField(
default=True,
help_text="Whether this section is currently active"
)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
class Meta:
verbose_name = "Policy Section"
verbose_name_plural = "Policy Sections"
ordering = ['policy', 'order']
def __str__(self):
return f"{self.policy.type} - {self.heading}"

View File

@@ -0,0 +1,47 @@
from rest_framework import serializers
from .models import Policy, PolicySection
class PolicySectionSerializer(serializers.ModelSerializer):
"""Serializer for policy sections"""
class Meta:
model = PolicySection
fields = ['id', 'heading', 'content', 'order']
class PolicySerializer(serializers.ModelSerializer):
"""Serializer for policies with their sections"""
sections = PolicySectionSerializer(many=True, read_only=True)
class Meta:
model = Policy
fields = [
'id',
'type',
'title',
'slug',
'description',
'last_updated',
'version',
'effective_date',
'sections'
]
class PolicyListSerializer(serializers.ModelSerializer):
"""Simplified serializer for policy listing"""
class Meta:
model = Policy
fields = [
'id',
'type',
'title',
'slug',
'description',
'last_updated',
'version'
]

View File

@@ -0,0 +1,4 @@
from django.test import TestCase
# Create your tests here.

View File

@@ -0,0 +1,11 @@
from django.urls import path, include
from rest_framework.routers import DefaultRouter
from .views import PolicyViewSet
router = DefaultRouter()
router.register(r'', PolicyViewSet, basename='policy')
urlpatterns = [
path('', include(router.urls)),
]

View File

@@ -0,0 +1,52 @@
from rest_framework import viewsets, status
from rest_framework.decorators import action
from rest_framework.response import Response
from django.shortcuts import get_object_or_404
from .models import Policy, PolicySection
from .serializers import PolicySerializer, PolicyListSerializer
class PolicyViewSet(viewsets.ReadOnlyModelViewSet):
"""
ViewSet for viewing policies.
Provides list and retrieve actions.
"""
queryset = Policy.objects.filter(is_active=True)
def get_serializer_class(self):
if self.action == 'list':
return PolicyListSerializer
return PolicySerializer
def get_queryset(self):
queryset = Policy.objects.filter(is_active=True)
policy_type = self.request.query_params.get('type', None)
if policy_type:
queryset = queryset.filter(type=policy_type)
return queryset
def retrieve(self, request, pk=None):
"""
Retrieve a policy by ID or type
"""
# Try to get by ID first
if pk.isdigit():
policy = get_object_or_404(Policy, pk=pk, is_active=True)
else:
# Otherwise try by type (slug)
policy = get_object_or_404(Policy, type=pk, is_active=True)
serializer = self.get_serializer(policy)
return Response(serializer.data)
@action(detail=False, methods=['get'], url_path='by-type/(?P<policy_type>[^/.]+)')
def by_type(self, request, policy_type=None):
"""
Get a specific policy by its type
"""
policy = get_object_or_404(Policy, type=policy_type, is_active=True)
serializer = PolicySerializer(policy)
return Response(serializer.data)