Browse Source

references updated with editor field

master
Mohsen Taba 3 weeks ago
parent
commit
f28cb75dda
  1. 81
      apps/hadis/management/commands/seed_edition_editors.py
  2. 28
      apps/hadis/migrations/0033_bookeditor.py
  3. 37
      apps/hadis/models/reference.py
  4. 11
      apps/hadis/serializers/reference_v2.py
  5. 42
      apps/hadis/serializers/serializers_admin.py
  6. 2
      apps/hadis/views_admin.py

81
apps/hadis/management/commands/seed_edition_editors.py

@ -0,0 +1,81 @@
# backend/apps/hadis/management/commands/seed_edition_editors.py
import random
from django.core.management.base import BaseCommand
from django.db import transaction
from django.utils.text import slugify
from apps.hadis.models import BookEdition, BookAuthor, BookEditor
class Command(BaseCommand):
help = 'Assigns editors to each BookEdition: one random text-only and one linked to a random existing BookAuthor.'
def handle(self, *args, **options):
self.stdout.write(self.style.WARNING("Starting to seed edition editors..."))
editions = list(BookEdition.objects.all())
authors = list(BookAuthor.objects.all())
if not editions:
self.stdout.write(self.style.ERROR("No BookEditions found in the database."))
return
if not authors:
self.stdout.write(self.style.ERROR("No BookAuthors found. Please add some authors first."))
return
# 1. Clean existing editors
self.stdout.write("Deleting existing BookEditors...")
BookEditor.objects.all().delete()
random_editor_names = [
{"fa": "دکتر حسین نوری", "en": "Dr. Hossein Noori"},
{"fa": "استاد عباس محمودی", "en": "Professor Abbas Mahmoudi"},
{"fa": "دکتر زهرا طاهری", "en": "Dr. Zahra Taheri"},
{"fa": "مصحح مهدی حیدری", "en": "Editor Mehdi Heidari"},
{"fa": "دکتر کاظم مرادی", "en": "Dr. Kazem Moradi"},
{"fa": "استاد سارا ابراهیمی", "en": "Professor Sara Ebrahimi"},
{"fa": "دکتر قاسم باقری", "en": "Dr. Ghasem Bagheri"},
]
text_only_count = 0
linked_author_count = 0
with transaction.atomic():
for edition in editions:
# 1. First editor: Text-only (Random Name & Slug)
name_data = random.choice(random_editor_names)
name_field = [
{"language_code": "fa", "text": name_data["fa"]},
{"language_code": "en", "text": name_data["en"]},
]
# Ensure clean slug
base_slug = slugify(name_data["en"])
slug_val = base_slug
counter = 1
while BookEditor.objects.filter(slug=slug_val).exists():
slug_val = f"{base_slug}-{counter}"
counter += 1
BookEditor.objects.create(
book_edition=edition,
name=name_field,
slug=slug_val,
author=None
)
text_only_count += 1
# 2. Second editor: Linked to a random BookAuthor
random_author = random.choice(authors)
BookEditor.objects.create(
book_edition=edition,
author=random_author
)
linked_author_count += 1
self.stdout.write(
self.style.SUCCESS(
f"Successfully seeded editors for {len(editions)} BookEditions!\n"
f"- Created {text_only_count} text-only editors.\n"
f"- Created {linked_author_count} editors linked to existing authors."
)
)

28
apps/hadis/migrations/0033_bookeditor.py

@ -0,0 +1,28 @@
# Generated by Django 4.2.30 on 2026-08-01 10:51
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('hadis', '0032_alter_bookauthor_slug_alter_bookreference_slug_and_more'),
]
operations = [
migrations.CreateModel(
name='BookEditor',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('name', models.JSONField(blank=True, default=list, verbose_name='Name')),
('slug', models.SlugField(allow_unicode=True, blank=True, max_length=255, null=True, verbose_name='slug')),
('author', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='editor_profiles', to='hadis.bookauthor', verbose_name='Author')),
('book_edition', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='editors', to='hadis.bookedition', verbose_name='Book Edition')),
],
options={
'verbose_name': 'Book Editor',
'verbose_name_plural': 'Book Editors',
},
),
]

37
apps/hadis/models/reference.py

@ -483,3 +483,40 @@ class BookResearcher(LowercaseSlugMixin, models.Model):
return self.name[0]['text'] return self.name[0]['text']
except: except:
return self.slug or f"Researcher {self.id}" return self.slug or f"Researcher {self.id}"
class BookEditor(LowercaseSlugMixin, models.Model):
slug_source_field = 'name'
book_edition = models.ForeignKey(
'BookEdition',
on_delete=models.CASCADE,
related_name='editors',
verbose_name=_('Book Edition')
)
author = models.ForeignKey(
'BookAuthor',
on_delete=models.SET_NULL,
null=True,
blank=True,
related_name='editor_profiles',
verbose_name=_('Author')
)
name = models.JSONField(default=list, blank=True, verbose_name=_('Name'))
slug = models.SlugField(max_length=255, verbose_name=_('slug'), null=True, blank=True, allow_unicode=True)
class Meta:
verbose_name = _('Book Editor')
verbose_name_plural = _('Book Editors')
def save(self, *args, **kwargs):
if self.author:
self.name = self.author.name
self.slug = self.author.slug
super().save(*args, **kwargs)
def __str__(self):
try:
return self.name[0]['text']
except:
return self.slug or f"Editor {self.id}"

11
apps/hadis/serializers/reference_v2.py

@ -1,6 +1,6 @@
from rest_framework import serializers from rest_framework import serializers
from rest_framework.pagination import PageNumberPagination from rest_framework.pagination import PageNumberPagination
from ..models import BookReference, BookAuthor, BookEdition, BookVolume, BookTag, BookResearcher
from ..models import BookReference, BookAuthor, BookEdition, BookVolume, BookTag, BookResearcher, BookEditor
from .category import get_localized_text from .category import get_localized_text
from apps.hadis.serializers import HadisListSerializer, HadisShortSerializer from apps.hadis.serializers import HadisListSerializer, HadisShortSerializer
from ..serializers.category import LocalizedField from ..serializers.category import LocalizedField
@ -24,6 +24,12 @@ class BookResearcherV2Serializer(serializers.ModelSerializer):
model = BookResearcher model = BookResearcher
fields = ['id', 'name', 'slug'] fields = ['id', 'name', 'slug']
class BookEditorV2Serializer(serializers.ModelSerializer):
name = LocalizedField()
class Meta:
model = BookEditor
fields = ['id', 'name', 'slug']
class BookReferenceV2ListSerializer(serializers.ModelSerializer): class BookReferenceV2ListSerializer(serializers.ModelSerializer):
title = LocalizedField() title = LocalizedField()
authors = serializers.SerializerMethodField() authors = serializers.SerializerMethodField()
@ -60,13 +66,14 @@ class BookEditionV2Serializer(serializers.ModelSerializer):
tags = LocalizedField() tags = LocalizedField()
publication_place = serializers.SerializerMethodField() publication_place = serializers.SerializerMethodField()
researchers = BookResearcherV2Serializer(many=True, read_only=True) researchers = BookResearcherV2Serializer(many=True, read_only=True)
editors = BookEditorV2Serializer(many=True, read_only=True)
class Meta: class Meta:
model = BookEdition model = BookEdition
fields = [ fields = [
'id', 'publisher', 'publication_place', 'edition_number', 'id', 'publisher', 'publication_place', 'edition_number',
'year_of_publication', 'number_of_volumes', 'notes', 'source_url', 'tags', 'year_of_publication', 'number_of_volumes', 'notes', 'source_url', 'tags',
'researchers'
'researchers', 'editors'
] ]
def get_publication_place(self, obj): def get_publication_place(self, obj):

42
apps/hadis/serializers/serializers_admin.py

@ -4,7 +4,7 @@ from django.core.files.uploadedfile import SimpleUploadedFile
from utils.image_compression import maybe_compress_uploaded_file from utils.image_compression import maybe_compress_uploaded_file
from ..models import ( from ..models import (
Hadis, HadisCategory, HadisStatus, HadisTag, HadisCorrection, HadisInterpretation, HadisCollection, HadisSect, HadisInCollection, HadisTransmitter, Hadis, HadisCategory, HadisStatus, HadisTag, HadisCorrection, HadisInterpretation, HadisCollection, HadisSect, HadisInCollection, HadisTransmitter,
BookReference, BookReferenceImage, BookReferenceDocument, BookAuthor, BookAttribute, BookSubjectArea, BookType, BookEdition, BookVolume, BookResearcher,
BookReference, BookReferenceImage, BookReferenceDocument, BookAuthor, BookAttribute, BookSubjectArea, BookType, BookEdition, BookVolume, BookResearcher, BookEditor,
Transmitters, TransmitterReliability, NarratorLayer, OpinionStatus, TransmitterOpinion, TransmitterOriginalText, Transmitters, TransmitterReliability, NarratorLayer, OpinionStatus, TransmitterOpinion, TransmitterOriginalText,
CorrectionReference, CorrectionReferenceImage, InterpretationReference, InterpretationReferenceImage, CorrectionReference, CorrectionReferenceImage, InterpretationReference, InterpretationReferenceImage,
OriginalTextReference, OriginalTextReferenceImage OriginalTextReference, OriginalTextReferenceImage
@ -577,6 +577,7 @@ class AdminHadisCategoryArchiveSerializer(serializers.ModelSerializer):
description = serializers.JSONField() description = serializers.JSONField()
xmind_file = AbsoluteFileField(required=False, allow_null=True) xmind_file = AbsoluteFileField(required=False, allow_null=True)
remove_xmind_file = serializers.BooleanField(write_only=True, required=False, default=False) remove_xmind_file = serializers.BooleanField(write_only=True, required=False, default=False)
has_hadith = serializers.SerializerMethodField()
parent = serializers.PrimaryKeyRelatedField( parent = serializers.PrimaryKeyRelatedField(
queryset=HadisCategory.objects.all(), queryset=HadisCategory.objects.all(),
@ -588,6 +589,9 @@ class AdminHadisCategoryArchiveSerializer(serializers.ModelSerializer):
required=True, required=True,
) )
def get_has_hadith(self, obj):
return obj.hadis_set.exists()
class Meta: class Meta:
model = HadisCategory model = HadisCategory
fields = [ fields = [
@ -605,6 +609,7 @@ class AdminHadisCategoryArchiveSerializer(serializers.ModelSerializer):
"slug", "slug",
"share_link", "share_link",
"xmind_share_link", "xmind_share_link",
"has_hadith",
] ]
read_only_fields = ["id", "share_link", "xmind_share_link"] read_only_fields = ["id", "share_link", "xmind_share_link"]
@ -1224,6 +1229,15 @@ class AdminBookResearcherSerializer(serializers.ModelSerializer):
fields = ["id", "name", "slug", "author"] fields = ["id", "name", "slug", "author"]
class AdminBookEditorSerializer(serializers.ModelSerializer):
id = serializers.IntegerField(required=False)
name = serializers.JSONField(required=False, default=list)
class Meta:
model = BookEditor
fields = ["id", "name", "slug", "author"]
class AdminBookEditionSerializer(serializers.ModelSerializer): class AdminBookEditionSerializer(serializers.ModelSerializer):
publisher = serializers.JSONField(required=False, default=list) publisher = serializers.JSONField(required=False, default=list)
city_of_publication = serializers.JSONField(required=False, default=list) city_of_publication = serializers.JSONField(required=False, default=list)
@ -1233,6 +1247,7 @@ class AdminBookEditionSerializer(serializers.ModelSerializer):
volumes_detail = AdminBookVolumeSerializer(source="volumes", many=True, read_only=True) volumes_detail = AdminBookVolumeSerializer(source="volumes", many=True, read_only=True)
number_of_volumes = serializers.SerializerMethodField() number_of_volumes = serializers.SerializerMethodField()
researchers = AdminBookResearcherSerializer(many=True, required=False) researchers = AdminBookResearcherSerializer(many=True, required=False)
editors = AdminBookEditorSerializer(many=True, required=False)
class Meta: class Meta:
model = BookEdition model = BookEdition
@ -1251,6 +1266,7 @@ class AdminBookEditionSerializer(serializers.ModelSerializer):
"tags", "tags",
"volumes_detail", "volumes_detail",
"researchers", "researchers",
"editors",
"created_at", "created_at",
"updated_at", "updated_at",
] ]
@ -1260,13 +1276,17 @@ class AdminBookEditionSerializer(serializers.ModelSerializer):
def create(self, validated_data): def create(self, validated_data):
researchers_data = validated_data.pop("researchers", []) researchers_data = validated_data.pop("researchers", [])
editors_data = validated_data.pop("editors", [])
edition = super().create(validated_data) edition = super().create(validated_data)
for res_data in researchers_data: for res_data in researchers_data:
BookResearcher.objects.create(book_edition=edition, **res_data) BookResearcher.objects.create(book_edition=edition, **res_data)
for ed_data in editors_data:
BookEditor.objects.create(book_edition=edition, **ed_data)
return edition return edition
def update(self, instance, validated_data): def update(self, instance, validated_data):
researchers_data = validated_data.pop("researchers", None) researchers_data = validated_data.pop("researchers", None)
editors_data = validated_data.pop("editors", None)
edition = super().update(instance, validated_data) edition = super().update(instance, validated_data)
if researchers_data is not None: if researchers_data is not None:
# Delete researchers no longer in the request payload # Delete researchers no longer in the request payload
@ -1277,13 +1297,29 @@ class AdminBookEditionSerializer(serializers.ModelSerializer):
if res_id: if res_id:
# Update existing researcher # Update existing researcher
res_data_clean = {k: v for k, v in res_data.items() if k != "id"} res_data_clean = {k: v for k, v in res_data.items() if k != "id"}
# Filter and update
BookResearcher.objects.filter(id=res_id, book_edition=instance).update(**res_data_clean) BookResearcher.objects.filter(id=res_id, book_edition=instance).update(**res_data_clean)
# Trigger save to execute save() hook (for author update propagation if author is changed)
res_obj = BookResearcher.objects.filter(id=res_id, book_edition=instance).first() res_obj = BookResearcher.objects.filter(id=res_id, book_edition=instance).first()
if res_obj: if res_obj:
res_obj.save() res_obj.save()
else: else:
# Create new researcher # Create new researcher
BookResearcher.objects.create(book_edition=instance, **res_data) BookResearcher.objects.create(book_edition=instance, **res_data)
if editors_data is not None:
# Delete editors no longer in the request payload
keep_ids = [e.get("id") for e in editors_data if e.get("id")]
instance.editors.exclude(id__in=keep_ids).delete()
for ed_data in editors_data:
ed_id = ed_data.get("id")
if ed_id:
# Update existing editor
ed_data_clean = {k: v for k, v in ed_data.items() if k != "id"}
BookEditor.objects.filter(id=ed_id, book_edition=instance).update(**ed_data_clean)
ed_obj = BookEditor.objects.filter(id=ed_id, book_edition=instance).first()
if ed_obj:
ed_obj.save()
else:
# Create new editor
BookEditor.objects.create(book_edition=instance, **ed_data)
return edition return edition

2
apps/hadis/views_admin.py

@ -100,7 +100,7 @@ class DovodiHadisCategoryViewSet(ModelViewSet):
source_type = self.request.query_params.get("source_type") source_type = self.request.query_params.get("source_type")
if source_type: if source_type:
queryset = queryset.filter(source_type=source_type) queryset = queryset.filter(source_type=source_type)
return queryset.order_by("tree_id", "lft", "order", "id")
return queryset.order_by("-id")
class AdminHadisStatusViewSet(ModelViewSet): class AdminHadisStatusViewSet(ModelViewSet):

Loading…
Cancel
Save