Skip to content

Commit

Permalink
Merge branch 'master' into fix-schema-tests
Browse files Browse the repository at this point in the history
  • Loading branch information
akiva10b committed Dec 10, 2024
2 parents 5a8108f + 587c797 commit 4588ef2
Show file tree
Hide file tree
Showing 40 changed files with 1,081 additions and 222 deletions.
8 changes: 8 additions & 0 deletions django_topics/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# Django Topics app

Django app that defines models and admin interfaces for editing certain aspects of topics that are unique to Sefaria's product and not needed for general usage of Sefaria's data.

Currently contains methods to:
- Edit which topics are in which pools
- Define topic of the day schedule
- Define seasonal topic schedule
Empty file added django_topics/__init__.py
Empty file.
229 changes: 229 additions & 0 deletions django_topics/admin.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,229 @@
from django.contrib import admin, messages
from django.utils.html import format_html
from django_topics.models import Topic, TopicPool, TopicOfTheDayEnglish, TopicOfTheDayHebrew, SeasonalTopicEnglish, SeasonalTopicHebrew
from django_topics.models.pool import PoolType


def create_add_to_pool_action(pool_name):
def add_to_pool(modeladmin, request, queryset):
try:
pool = TopicPool.objects.get(name=pool_name)
for topic in queryset:
topic.pools.add(pool)
modeladmin.message_user(request, f"Added {queryset.count()} topics to {pool.name}", messages.SUCCESS)

except TopicPool.DoesNotExist:
modeladmin.message_user(request, "The specified pool does not exist.", messages.ERROR)

add_to_pool.short_description = f"Add selected topics to '{pool_name}' pool"
add_to_pool.__name__ = f"add_to_specific_pool_{pool_name}"
return add_to_pool


def create_remove_from_pool_action(pool_name):
def remove_from_pool(modeladmin, request, queryset):
try:
pool = TopicPool.objects.get(name=pool_name)
for topic in queryset:
topic.pools.remove(pool)
modeladmin.message_user(request, f"Removed {queryset.count()} topics from {pool.name}", messages.SUCCESS)

except TopicPool.DoesNotExist:
modeladmin.message_user(request, "The specified pool does not exist.", messages.ERROR)

remove_from_pool.short_description = f"Remove selected topics from '{pool_name}' pool"
remove_from_pool.__name__ = f"remove_from_pool_{pool_name}"
return remove_from_pool


class PoolFilter(admin.SimpleListFilter):
title = 'Pool Filter'
parameter_name = 'pool'

def lookups(self, request, model_admin):
return [
('general_en', 'General Pool EN'),
('general_he', 'General Pool HE'),
(PoolType.TORAH_TAB.value, 'TorahTab Pool'),
]

def queryset(self, request, queryset):
pool_name = self.value()
if pool_name:
pool = TopicPool.objects.get(name=pool_name)
return queryset.filter(pools=pool)
return queryset


@admin.register(Topic)
class TopicAdmin(admin.ModelAdmin):
list_display = ('slug', 'en_title', 'he_title', 'is_in_pool_general_en', 'is_in_pool_general_he', 'is_in_pool_torah_tab', 'sefaria_link')
list_filter = (PoolFilter,)
filter_horizontal = ('pools',)
search_fields = ('slug', 'en_title', 'he_title')
readonly_fields = ('slug', 'en_title', 'he_title')
actions = [
create_add_to_pool_action('general_en'),
create_add_to_pool_action('general_he'),
create_add_to_pool_action(PoolType.TORAH_TAB.value),
create_remove_from_pool_action('general_en'),
create_remove_from_pool_action('general_he'),
create_remove_from_pool_action(PoolType.TORAH_TAB.value),
]

def has_add_permission(self, request):
return False

def has_delete_permission(self, request, obj=None):
return False

def get_queryset(self, request):
queryset = super().get_queryset(request)
return queryset.filter(pools__name=PoolType.LIBRARY.value)

def is_in_pool_general_en(self, obj):
return obj.pools.filter(name='general_en').exists()
is_in_pool_general_en.boolean = True
is_in_pool_general_en.short_description = "General Pool EN"

def is_in_pool_general_he(self, obj):
return obj.pools.filter(name='general_he').exists()
is_in_pool_general_he.boolean = True
is_in_pool_general_he.short_description = "General Pool HE"

def is_in_pool_torah_tab(self, obj):
return obj.pools.filter(name=PoolType.TORAH_TAB.value).exists()
is_in_pool_torah_tab.boolean = True
is_in_pool_torah_tab.short_description = "TorahTab Pool"

def sefaria_link(self, obj):
url = f"https://www.sefaria.org/topics/{obj.slug}"
return format_html('<a href="{}" target="_blank">{}</a>', url, obj.slug)
sefaria_link.short_description = "Sefaria Link"


class TopicOfTheDayAdmin(admin.ModelAdmin):
exclude = ("lang",) # not for manual editing
list_display = ('start_date', 'topic')
list_filter = ('start_date',)
raw_id_fields = ('topic',)
search_fields = ('topic__slug', 'topic__en_title', 'topic__he_title')
date_hierarchy = 'start_date'
ordering = ['-start_date']
fieldsets = (
(None, {
'fields': ('topic', 'start_date'),
}),
)

def formfield_for_foreignkey(self, db_field, request, **kwargs):
if db_field.name == "topic":
kwargs["label"] = "Topic slug"
kwargs["help_text"] = "Use the magnifying glass button to select a topic."
return super().formfield_for_foreignkey(db_field, request, **kwargs)


@admin.register(TopicOfTheDayEnglish)
class TopicOfTheDayAdminEnglish(TopicOfTheDayAdmin):

def get_queryset(self, request):
qs = super().get_queryset(request)
return qs.filter(lang="en")


@admin.register(TopicOfTheDayHebrew)
class TopicOfTheDayAdminHebrew(TopicOfTheDayAdmin):

def get_queryset(self, request):
qs = super().get_queryset(request)
return qs.filter(lang="he")


class SeasonalTopicAdmin(admin.ModelAdmin):
exclude = ("lang",) # not for manual editing
list_display = (
'start_date',
'topic',
'display_date_prefix',
'display_date_suffix',
'secondary_topic',
'display_start_date_israel',
'display_end_date_israel',
'display_start_date_diaspora',
'display_end_date_diaspora'
)
raw_id_fields = ('topic', 'secondary_topic')
list_filter = (
'start_date',
'display_start_date_israel',
'display_start_date_diaspora'
)
ordering = ['-start_date']
search_fields = ('topic__slug', 'topic__en_title', 'topic__he_title', 'secondary_topic__slug')
autocomplete_fields = ('topic', 'secondary_topic')
date_hierarchy = 'start_date'
fieldsets = (
(None, {
'fields': (
'topic',
'secondary_topic',
'start_date'
)
}),
('Display Date Prefix/Suffix', {
'fields': (
'display_date_prefix',
'display_date_suffix',
),
'description': 'Prefix/Suffix that will be displayed around the secondary topic.',
}),
('Israel Display Dates', {
'fields': (
'display_start_date_israel',
'display_end_date_israel'
),
'description': 'Dates to be displayed to the user of when this topic is "happening". '
'E.g. for a holiday, when the holiday occurs. '
'When the dates are the same for both Israel and Diaspora, only fill out Israeli dates. '
'Similarly, when the start and end dates are the same, only fill out start date.'
}),
('Diaspora Display Dates', {
'fields': (
'display_start_date_diaspora',
'display_end_date_diaspora'
),
'description': 'When the dates are the same for both Israel and Diaspora, only fill out Israeli dates. '
'Similarly, when the start and end dates are the same, only fill out start date.'

}),
)

def formfield_for_foreignkey(self, db_field, request, **kwargs):
if db_field.name == "topic":
kwargs["label"] = "Topic slug"
kwargs["help_text"] = "Use the magnifying glass button to select a topic."
if db_field.name == "secondary_topic":
kwargs["label"] = "Secondary topic slug"
return super().formfield_for_foreignkey(db_field, request, **kwargs)

def save_model(self, request, obj, form, change):
"""
Overriding the save_model to ensure the model's clean method is executed.
"""
obj.clean()
super().save_model(request, obj, form, change)


@admin.register(SeasonalTopicEnglish)
class SeasonalTopicAdminEnglish(SeasonalTopicAdmin):

def get_queryset(self, request):
qs = super().get_queryset(request)
return qs.filter(lang="en")


@admin.register(SeasonalTopicHebrew)
class SeasonalTopicAdminHebrew(SeasonalTopicAdmin):
def get_queryset(self, request):
qs = super().get_queryset(request)
return qs.filter(lang="he")
6 changes: 6 additions & 0 deletions django_topics/apps.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
from django.apps import AppConfig


class DjangoTopicsAppConfig(AppConfig):
name = "django_topics"
verbose_name = "Topics Management"
83 changes: 83 additions & 0 deletions django_topics/migrations/0001_initial.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11.29 on 2024-11-19 08:18
from __future__ import unicode_literals

from django.db import migrations, models
import django.db.models.deletion


class Migration(migrations.Migration):

initial = True

dependencies = [
]

operations = [
migrations.CreateModel(
name='SeasonalTopic',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('start_date', models.DateField()),
('display_start_date_israel', models.DateField(blank=True, null=True)),
('display_end_date_israel', models.DateField(blank=True, null=True)),
('display_start_date_diaspora', models.DateField(blank=True, null=True)),
('display_end_date_diaspora', models.DateField(blank=True, null=True)),
],
options={
'verbose_name': 'Seasonal Topic',
'verbose_name_plural': 'Seasonal Topics',
},
),
migrations.CreateModel(
name='Topic',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('slug', models.CharField(max_length=255, unique=True)),
('en_title', models.CharField(blank=True, default='', max_length=255)),
('he_title', models.CharField(blank=True, default='', max_length=255)),
],
),
migrations.CreateModel(
name='TopicOfTheDay',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('start_date', models.DateField()),
('topic', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='topic_of_the_day', to='django_topics.Topic')),
],
options={
'verbose_name': 'Topic of the Day',
'verbose_name_plural': 'Topics of the Day',
},
),
migrations.CreateModel(
name='TopicPool',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('name', models.CharField(max_length=255, unique=True)),
],
),
migrations.AddField(
model_name='topic',
name='pools',
field=models.ManyToManyField(blank=True, related_name='topics', to='django_topics.TopicPool'),
),
migrations.AddField(
model_name='seasonaltopic',
name='secondary_topic',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='seasonal_secondary_topic', to='django_topics.Topic'),
),
migrations.AddField(
model_name='seasonaltopic',
name='topic',
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='seasonal_topic', to='django_topics.Topic'),
),
migrations.AlterUniqueTogether(
name='topicoftheday',
unique_together=set([('topic', 'start_date')]),
),
migrations.AlterUniqueTogether(
name='seasonaltopic',
unique_together=set([('topic', 'start_date')]),
),
]
38 changes: 38 additions & 0 deletions django_topics/migrations/0002_auto_20241121_0617.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11.29 on 2024-11-21 10:17
from __future__ import unicode_literals

from django.db import migrations, models
import django.db.models.deletion


class Migration(migrations.Migration):

dependencies = [
('django_topics', '0001_initial'),
]

operations = [
migrations.AlterModelOptions(
name='seasonaltopic',
options={'verbose_name': 'Landing Page - Calendar', 'verbose_name_plural': 'Landing Page - Calendar'},
),
migrations.AlterModelOptions(
name='topic',
options={'verbose_name': 'Topic Pool Management', 'verbose_name_plural': 'Topic Pool Management'},
),
migrations.AlterModelOptions(
name='topicoftheday',
options={'verbose_name': 'Landing Page - Topic of the Day', 'verbose_name_plural': 'Landing Page - Topic of the Day'},
),
migrations.AlterField(
model_name='seasonaltopic',
name='secondary_topic',
field=models.ForeignKey(blank=True, help_text="Secondary topic which will be displayed alongside `topic`. E.g. `topic` is 'Teshuva' then secondary topic could be 'Yom Kippur'.", null=True, on_delete=django.db.models.deletion.CASCADE, related_name='seasonal_secondary_topic', to='django_topics.Topic'),
),
migrations.AlterField(
model_name='seasonaltopic',
name='start_date',
field=models.DateField(help_text='Start date of when this will appear. End date is implied by when the next Seasonal Topic is displayed.'),
),
]
24 changes: 24 additions & 0 deletions django_topics/migrations/0003_auto_20241121_0757.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11.29 on 2024-11-21 11:57
from __future__ import unicode_literals

from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
('django_topics', '0002_auto_20241121_0617'),
]

operations = [
migrations.RemoveField(
model_name='topic',
name='id',
),
migrations.AlterField(
model_name='topic',
name='slug',
field=models.CharField(max_length=255, primary_key=True, serialize=False),
),
]
Loading

0 comments on commit 4588ef2

Please sign in to comment.