Browse Source

authors library api update

master
Mohsen Taba 5 days ago
parent
commit
72beedbeb2
  1. 5
      apps/library/admin.py
  2. 24
      apps/library/doc.py
  3. 79
      apps/library/management/commands/populate_author_fields.py
  4. 73
      apps/library/migrations/0005_author_date_of_birth_author_date_of_death_and_more.py
  5. 18
      apps/library/migrations/0006_alter_author_number_of_volumes.py
  6. 18
      apps/library/models.py
  7. 34
      apps/library/serializers.py
  8. 15
      apps/library/serializers_admin.py
  9. 2
      apps/library/views.py
  10. 2
      entrypoint.sh

5
apps/library/admin.py

@ -253,7 +253,10 @@ class AuthorAdmin(ModelAdmin):
fieldsets = (
(None, {
'fields': ('full_name', 'slug', 'thumbnail', 'birth_date', 'death_date')
'fields': ('full_name', 'slug', 'thumbnail', 'image', 'birth_date', 'death_date', 'date_of_birth', 'date_of_death')
}),
(_('Publication Info'), {
'fields': ('researcher_editor', 'publisher', 'publication_place', 'edition_statement', 'year', 'number_of_volumes', 'online_source', 'tag', 'notes')
}),
(_('Timestamps'), {
'fields': ('created_at', 'updated_at'),

24
apps/library/doc.py

@ -57,6 +57,14 @@ search_param = openapi.Parameter(
required=False
)
author_search_param = openapi.Parameter(
'search',
openapi.IN_QUERY,
description="Search authors by full name",
type=openapi.TYPE_STRING,
required=False
)
thumbs_schema = openapi.Schema(
type=openapi.TYPE_OBJECT,
@ -75,6 +83,7 @@ author_summary_schema = openapi.Schema(
'full_name': openapi.Schema(type=openapi.TYPE_STRING),
'slug': openapi.Schema(type=openapi.TYPE_STRING),
'thumbnail': thumbs_schema,
'image': thumbs_schema,
}
)
@ -143,8 +152,20 @@ author_schema = openapi.Schema(
'full_name': openapi.Schema(type=openapi.TYPE_STRING, description="Full name of the author"),
'slug': openapi.Schema(type=openapi.TYPE_STRING, description="URL-friendly slug for the author"),
'thumbnail': thumbs_schema,
'image': thumbs_schema,
'birth_date': openapi.Schema(type=openapi.TYPE_STRING, format=openapi.FORMAT_DATE, nullable=True, description="Birth date"),
'death_date': openapi.Schema(type=openapi.TYPE_STRING, format=openapi.FORMAT_DATE, nullable=True, description="Death date"),
'date_of_birth': openapi.Schema(type=openapi.TYPE_STRING, nullable=True, description="Date of birth (text/historical format)"),
'date_of_death': openapi.Schema(type=openapi.TYPE_STRING, nullable=True, description="Date of death (text/historical format)"),
'researcher_editor': openapi.Schema(type=openapi.TYPE_STRING, nullable=True, description="Researcher / Editor"),
'publisher': openapi.Schema(type=openapi.TYPE_STRING, nullable=True, description="Publisher"),
'publication_place': openapi.Schema(type=openapi.TYPE_STRING, nullable=True, description="Publication Place"),
'edition_statement': openapi.Schema(type=openapi.TYPE_STRING, nullable=True, description="Edition Statement"),
'year': openapi.Schema(type=openapi.TYPE_STRING, nullable=True, description="Year"),
'number_of_volumes': openapi.Schema(type=openapi.TYPE_STRING, nullable=True, description="Number of Volumes"),
'notes': openapi.Schema(type=openapi.TYPE_STRING, nullable=True, description="Notes"),
'online_source': openapi.Schema(type=openapi.TYPE_STRING, format=openapi.FORMAT_URI, nullable=True, description="Online Source"),
'tag': openapi.Schema(type=openapi.TYPE_STRING, nullable=True, description="Tag"),
'books_count': openapi.Schema(type=openapi.TYPE_INTEGER, description="Number of active books"),
},
required=['id', 'full_name', 'slug', 'books_count']
@ -263,9 +284,12 @@ author_list_swagger = swagger_auto_schema(
Retrieve a paginated list of library authors.
Each author includes thumbnail, full name, and number of active books.
You can search authors by their full name using the `search` query parameter.
""",
operation_summary="List Authors",
tags=["Dobodbi - Library"],
manual_parameters=[author_search_param],
responses={
200: authors_response,
401: "Authentication credentials were not provided or are invalid.",

79
apps/library/management/commands/populate_author_fields.py

@ -0,0 +1,79 @@
from django.core.management.base import BaseCommand
from django.db import transaction
from apps.library.models import Author
class Command(BaseCommand):
help = 'Populates the newly added fields for all existing authors with sample English data'
def handle(self, *args, **options):
self.stdout.write(self.style.WARNING('--- Starting Author Fields Population ---'))
authors = Author.objects.all()
if not authors.exists():
self.stdout.write(self.style.ERROR('No authors found in the database.'))
return
self.stdout.write(self.style.SUCCESS(f'Found {authors.count()} authors. Commencing update...'))
updated_count = 0
with transaction.atomic():
for author in authors:
# 1. Researcher / Editor
if not author.researcher_editor:
author.researcher_editor = f"Edited and researched by {author.full_name}"
# 2. Publisher
if not author.publisher:
author.publisher = "Imam Javad Publishing Institute"
# 3. Publication Place
if not author.publication_place:
author.publication_place = "Qom, Iran"
# 4. Edition Statement
if not author.edition_statement:
author.edition_statement = "First Edition (Revised)"
# 5. Year
if not author.year:
author.year = "1448 AH"
# 6. Number of Volumes
if not author.number_of_volumes:
author.number_of_volumes = 5
# 7. Notes
if not author.notes:
author.notes = f"This edition contains annotations and key commentaries regarding the works of {author.full_name}."
# 8. Online Source
if not author.online_source:
author.online_source = "https://imamjavad.nwhco.ir"
# 9. Tag
if not author.tag:
author.tag = "Reference Books"
# 10. Copy thumbnail to image if thumbnail exists and image doesn't
if author.thumbnail and not author.image:
author.image = author.thumbnail
# 11. Date of Birth (text)
if not author.date_of_birth:
if author.birth_date:
author.date_of_birth = f"{author.birth_date.year} AD"
else:
author.date_of_birth = "Around 4th Century AH"
# 12. Date of Death (text)
if not author.date_of_death:
if author.death_date:
author.date_of_death = f"{author.death_date.year} AD"
else:
author.date_of_death = "Around 5th Century AH"
author.save()
updated_count += 1
self.stdout.write(f"Updated author: {author.full_name}")
self.stdout.write(self.style.SUCCESS(f'--- Finished! Successfully updated {updated_count} authors ---'))

73
apps/library/migrations/0005_author_date_of_birth_author_date_of_death_and_more.py

@ -0,0 +1,73 @@
# Generated by Django 4.2.30 on 2026-07-16 13:25
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('library', '0004_alter_author_slug_alter_book_slug_and_more'),
]
operations = [
migrations.AddField(
model_name='author',
name='date_of_birth',
field=models.CharField(blank=True, max_length=255, null=True, verbose_name='Date of Birth'),
),
migrations.AddField(
model_name='author',
name='date_of_death',
field=models.CharField(blank=True, max_length=255, null=True, verbose_name='Date of Death'),
),
migrations.AddField(
model_name='author',
name='edition_statement',
field=models.CharField(blank=True, max_length=255, null=True, verbose_name='Edition Statement'),
),
migrations.AddField(
model_name='author',
name='image',
field=models.ImageField(blank=True, help_text='image allowed', null=True, upload_to='author_images/', verbose_name='Image'),
),
migrations.AddField(
model_name='author',
name='notes',
field=models.TextField(blank=True, null=True, verbose_name='Notes'),
),
migrations.AddField(
model_name='author',
name='number_of_volumes',
field=models.CharField(blank=True, max_length=255, null=True, verbose_name='Number of Volumes'),
),
migrations.AddField(
model_name='author',
name='online_source',
field=models.URLField(blank=True, max_length=512, null=True, verbose_name='Online Source'),
),
migrations.AddField(
model_name='author',
name='publication_place',
field=models.CharField(blank=True, max_length=255, null=True, verbose_name='Publication Place'),
),
migrations.AddField(
model_name='author',
name='publisher',
field=models.CharField(blank=True, max_length=255, null=True, verbose_name='Publisher'),
),
migrations.AddField(
model_name='author',
name='researcher_editor',
field=models.CharField(blank=True, max_length=255, null=True, verbose_name='Researcher / Editor'),
),
migrations.AddField(
model_name='author',
name='tag',
field=models.CharField(blank=True, max_length=255, null=True, verbose_name='Tag'),
),
migrations.AddField(
model_name='author',
name='year',
field=models.CharField(blank=True, max_length=255, null=True, verbose_name='Year'),
),
]

18
apps/library/migrations/0006_alter_author_number_of_volumes.py

@ -0,0 +1,18 @@
# Generated by Django 4.2.30 on 2026-07-16 13:44
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('library', '0005_author_date_of_birth_author_date_of_death_and_more'),
]
operations = [
migrations.AlterField(
model_name='author',
name='number_of_volumes',
field=models.IntegerField(blank=True, null=True, verbose_name='Number of Volumes'),
),
]

18
apps/library/models.py

@ -97,6 +97,24 @@ class Author(LowercaseSlugMixin, models.Model):
)
birth_date = models.DateField(null=True, blank=True, verbose_name=_('Birth Date'))
death_date = models.DateField(null=True, blank=True, verbose_name=_('Death Date'))
researcher_editor = models.CharField(max_length=255, null=True, blank=True, verbose_name=_('Researcher / Editor'))
publisher = models.CharField(max_length=255, null=True, blank=True, verbose_name=_('Publisher'))
publication_place = models.CharField(max_length=255, null=True, blank=True, verbose_name=_('Publication Place'))
edition_statement = models.CharField(max_length=255, null=True, blank=True, verbose_name=_('Edition Statement'))
year = models.CharField(max_length=255, null=True, blank=True, verbose_name=_('Year'))
number_of_volumes = models.IntegerField(null=True, blank=True, verbose_name=_('Number of Volumes'))
notes = models.TextField(null=True, blank=True, verbose_name=_('Notes'))
online_source = models.URLField(max_length=512, null=True, blank=True, verbose_name=_('Online Source'))
tag = models.CharField(max_length=255, null=True, blank=True, verbose_name=_('Tag'))
image = models.ImageField(
upload_to='author_images/',
null=True,
blank=True,
help_text=_('image allowed'),
verbose_name=_('Image')
)
date_of_birth = models.CharField(max_length=255, null=True, blank=True, verbose_name=_('Date of Birth'))
date_of_death = models.CharField(max_length=255, null=True, blank=True, verbose_name=_('Date of Death'))
created_at = models.DateTimeField(auto_now_add=True, verbose_name=_('created at'))
updated_at = models.DateTimeField(auto_now=True, verbose_name=_('updated at'))

34
apps/library/serializers.py

@ -49,30 +49,47 @@ class PinnedBookCollectionSerializer(serializers.ModelSerializer):
class AuthorSummarySerializer(serializers.ModelSerializer):
thumbnail = serializers.SerializerMethodField()
image = serializers.SerializerMethodField()
class Meta:
model = Author
fields = ('id', 'full_name', 'slug', 'thumbnail')
fields = ('id', 'full_name', 'slug', 'thumbnail', 'image')
def get_thumbnail(self, obj):
if obj.thumbnail:
return get_thumbs(obj.thumbnail, self.context.get('request'))
return None
def get_image(self, obj):
if obj.image:
return get_thumbs(obj.image, self.context.get('request'))
return None
class AuthorListSerializer(serializers.ModelSerializer):
thumbnail = serializers.SerializerMethodField()
image = serializers.SerializerMethodField()
books_count = serializers.SerializerMethodField()
class Meta:
model = Author
fields = ('id', 'full_name', 'slug', 'thumbnail', 'books_count')
fields = (
'id', 'full_name', 'slug', 'thumbnail', 'image', 'books_count',
'birth_date', 'death_date', 'date_of_birth', 'date_of_death',
'researcher_editor', 'publisher', 'publication_place', 'edition_statement',
'year', 'number_of_volumes', 'notes', 'online_source', 'tag'
)
def get_thumbnail(self, obj):
if obj.thumbnail:
return get_thumbs(obj.thumbnail, self.context.get('request'))
return None
def get_image(self, obj):
if obj.image:
return get_thumbs(obj.image, self.context.get('request'))
return None
def get_books_count(self, obj):
if hasattr(obj, 'books_count_annotation'):
return obj.books_count_annotation
@ -81,17 +98,28 @@ class AuthorListSerializer(serializers.ModelSerializer):
class AuthorDetailSerializer(serializers.ModelSerializer):
thumbnail = serializers.SerializerMethodField()
image = serializers.SerializerMethodField()
books_count = serializers.SerializerMethodField()
class Meta:
model = Author
fields = ('id', 'full_name', 'slug', 'thumbnail', 'birth_date', 'death_date', 'books_count')
fields = (
'id', 'full_name', 'slug', 'thumbnail', 'image', 'birth_date', 'death_date',
'date_of_birth', 'date_of_death', 'researcher_editor', 'publisher',
'publication_place', 'edition_statement', 'year', 'number_of_volumes',
'notes', 'online_source', 'tag', 'books_count'
)
def get_thumbnail(self, obj):
if obj.thumbnail:
return get_thumbs(obj.thumbnail, self.context.get('request'))
return None
def get_image(self, obj):
if obj.image:
return get_thumbs(obj.image, self.context.get('request'))
return None
def get_books_count(self, obj):
if hasattr(obj, 'books_count_annotation'):
return obj.books_count_annotation

15
apps/library/serializers_admin.py

@ -63,12 +63,20 @@ class AdminLibraryCollectionSerializer(serializers.ModelSerializer):
class AdminAuthorSerializer(serializers.ModelSerializer):
thumbnail = AbsoluteImageField(required=False, allow_null=True)
image = AbsoluteImageField(required=False, allow_null=True)
books_count = serializers.SerializerMethodField()
remove_thumbnail = serializers.BooleanField(write_only=True, required=False, default=False)
remove_image = serializers.BooleanField(write_only=True, required=False, default=False)
class Meta:
model = Author
fields = ["id", "full_name", "slug", "thumbnail", "birth_date", "death_date", "books_count", "created_at", "updated_at", "remove_thumbnail"]
fields = [
"id", "full_name", "slug", "thumbnail", "image", "birth_date", "death_date",
"date_of_birth", "date_of_death", "researcher_editor", "publisher",
"publication_place", "edition_statement", "year", "number_of_volumes",
"notes", "online_source", "tag", "books_count", "created_at", "updated_at",
"remove_thumbnail", "remove_image"
]
read_only_fields = ["id", "created_at", "updated_at", "books_count"]
def get_books_count(self, obj):
@ -78,13 +86,18 @@ class AdminAuthorSerializer(serializers.ModelSerializer):
def create(self, validated_data):
validated_data.pop("remove_thumbnail", False)
validated_data.pop("remove_image", False)
return super().create(validated_data)
def update(self, instance, validated_data):
remove_thumbnail = validated_data.pop("remove_thumbnail", False)
remove_image = validated_data.pop("remove_image", False)
if remove_thumbnail and instance.thumbnail:
instance.thumbnail.delete(save=False)
instance.thumbnail = None
if remove_image and instance.image:
instance.image.delete(save=False)
instance.image = None
return super().update(instance, validated_data)

2
apps/library/views.py

@ -256,6 +256,8 @@ class AuthorListView(ListAPIView):
serializer_class = AuthorListSerializer
permission_classes = (IsAuthenticated,)
authentication_classes = [TokenAuthentication]
filter_backends = [SearchFilter]
search_fields = ['full_name']
pagination_class = StandardResultsSetPagination
@author_list_swagger

2
entrypoint.sh

@ -6,7 +6,7 @@ python manage.py migrate
# python manage.py compilemessages
python manage.py collectstatic --noinput
python manage.py assign_volume_images --force
python manage.py send_test_notifications
# python manage.py send_test_notifications
# python manage.py set_default_category_icons
# python manage.py set_hadiscorrection_fixed_text
# Seed Russian data (only runs once, skips if data exists)

Loading…
Cancel
Save