Skip to content

Commit ed549ff

Browse files
authored
Merge pull request #632 from digitalfabrik/indicate_migrated_jobs_units_words
Indicate in API and CMS if a job, word or unit was migrated from the old data model
2 parents 6ec1566 + 48e3e28 commit ed549ff

8 files changed

Lines changed: 266 additions & 22 deletions

File tree

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
UNRELEASED
22
----------
33

4+
* [ [#632](https://github.com/digitalfabrik/lunes-cms/pull/632) ] Indicate in API and CMS if a job, unit or word was migrated from the old data model
5+
46

57
2025.11.0
68
---------

lunes_cms/api/v2/serializers/job_serializer.py

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
from drf_spectacular.types import OpenApiTypes
2+
from drf_spectacular.utils import extend_schema_field
13
from rest_framework import serializers
24

35
from ....cmsv2.models import Job
@@ -9,11 +11,17 @@ class JobSerializer(serializers.ModelSerializer):
911
"""
1012

1113
number_units = serializers.IntegerField()
14+
migrated = serializers.SerializerMethodField()
15+
16+
@extend_schema_field(OpenApiTypes.BOOL)
17+
def get_migrated(self, obj):
18+
"""Return True if the job was migrated from the old data model"""
19+
return obj.v1_id is not None
1220

1321
class Meta:
1422
"""
1523
Define model and the corresponding fields
1624
"""
1725

1826
model = Job
19-
fields = ("id", "name", "icon", "number_units")
27+
fields = ("id", "name", "icon", "number_units", "migrated")

lunes_cms/api/v2/serializers/unit_serializer.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
from drf_spectacular.types import OpenApiTypes
2+
from drf_spectacular.utils import extend_schema_field
13
from rest_framework import serializers
24

35
from ....cmsv2.models import Unit
@@ -9,6 +11,12 @@ class UnitSerializer(serializers.ModelSerializer):
911
"""
1012

1113
number_words = serializers.IntegerField()
14+
migrated = serializers.SerializerMethodField()
15+
16+
@extend_schema_field(OpenApiTypes.BOOL)
17+
def get_migrated(self, obj):
18+
"""Return True if the unit was migrated from the old data model."""
19+
return obj.v1_id is not None
1220

1321
class Meta:
1422
"""
@@ -22,4 +30,5 @@ class Meta:
2230
"description",
2331
"icon",
2432
"number_words",
33+
"migrated",
2534
)

lunes_cms/api/v2/serializers/word_serializer.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
from drf_spectacular.types import OpenApiTypes
2+
from drf_spectacular.utils import extend_schema_field
13
from rest_framework import serializers
24

35
from ....cmsv2.models import Word
@@ -13,11 +15,17 @@ class WordSerializer(serializers.ModelSerializer):
1315
child=serializers.ImageField(), source="images_for_api"
1416
)
1517
example_sentence = serializers.SerializerMethodField()
18+
migrated = serializers.SerializerMethodField()
1619

1720
def get_example_sentence(self, obj):
1821
"""Return None for empty example sentences instead of empty string."""
1922
return obj.example_sentence if obj.example_sentence else None
2023

24+
@extend_schema_field(OpenApiTypes.BOOL)
25+
def get_migrated(self, obj):
26+
"""Return True if the word was migrated from the old data model"""
27+
return obj.v1_id is not None
28+
2129
class Meta:
2230
"""
2331
Define model and the corresponding fields
@@ -32,4 +40,5 @@ class Meta:
3240
"audio",
3341
"example_sentence",
3442
"example_sentence_audio",
43+
"migrated",
3544
)

lunes_cms/cmsv2/admins/job_admin.py

Lines changed: 67 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
from __future__ import absolute_import, unicode_literals
22

33
from django.contrib import admin
4+
from django.utils.html import format_html
45
from django.utils.translation import gettext_lazy as _
56

67
from .base import BaseAdmin
@@ -18,6 +19,46 @@ class UnitInline(admin.TabularInline):
1819
extra = 1
1920

2021

22+
class MigratedFilter(admin.SimpleListFilter):
23+
"""
24+
Admin filter for migration status.
25+
26+
Allows filtering jobs by whether they were migrated from v1 or created in v2.
27+
"""
28+
29+
title = _("migration status")
30+
parameter_name = "migrated"
31+
32+
def lookups(self, request, model_admin):
33+
"""
34+
Return the filter options.
35+
36+
Returns:
37+
list: A list of tuples containing (value, label) pairs for the filter options
38+
"""
39+
return [
40+
("yes", _("Migrated from old data model")),
41+
("no", _("Not migrated from old data model")),
42+
]
43+
44+
def queryset(self, request, queryset):
45+
"""
46+
Filter the queryset based on the selected option.
47+
48+
Args:
49+
request: The HTTP request
50+
queryset: The queryset to filter
51+
52+
Returns:
53+
QuerySet: The filtered queryset
54+
"""
55+
if self.value() == "yes":
56+
return queryset.filter(v1_id__isnull=False)
57+
if self.value() == "no":
58+
return queryset.filter(v1_id__isnull=True)
59+
return queryset
60+
61+
2162
class JobAdmin(BaseAdmin):
2263
"""
2364
Admin interface for the Job model.
@@ -28,23 +69,25 @@ class JobAdmin(BaseAdmin):
2869

2970
fields = [
3071
"name",
72+
"migrated_status",
3173
"icon",
3274
"image_tag",
3375
"created_by",
3476
"released",
3577
]
36-
readonly_fields = ["created_by", "image_tag"]
78+
readonly_fields = ["created_by", "image_tag", "migrated_status"]
3779
inlines = [UnitInline]
3880
search_fields = ["name"]
3981
list_display = [
4082
"name",
83+
"migrated_status",
4184
"released",
4285
"list_icon",
4386
"created_by",
4487
"created_at_date",
4588
]
4689
list_display_links = ["name"]
47-
list_filter = ["released"]
90+
list_filter = ["released", MigratedFilter]
4891
list_per_page = 25
4992
ordering = ["name"]
5093

@@ -86,3 +129,25 @@ def created_at_date(self, obj):
86129
return obj.created_at.date()
87130

88131
created_at_date.short_description = _("created at")
132+
133+
def migrated_status(self, obj):
134+
"""
135+
Display a badge indicating whether the job was migrated from v1 or created in v2.
136+
137+
Args:
138+
obj: The job object
139+
140+
Returns:
141+
str: HTML formatted badge showing migration status
142+
"""
143+
if obj.v1_id is not None:
144+
return format_html(
145+
'<span style="background-color: #28a745; color: white; padding: 3px 8px; '
146+
'border-radius: 3px; font-size: 13px; font-weight: 500;">Migrated</span>'
147+
)
148+
return format_html(
149+
'<span style="background-color: #007bff; color: white; padding: 3px 8px; '
150+
'border-radius: 3px; font-size: 13px; font-weight: 500;">New</span>'
151+
)
152+
153+
migrated_status.short_description = _("migrated")

lunes_cms/cmsv2/admins/unit_admin.py

Lines changed: 67 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
from __future__ import absolute_import, unicode_literals
22

33
from django.contrib import admin
4+
from django.utils.html import format_html
45
from django.utils.translation import gettext_lazy as _
56

67
from lunes_cms.cmsv2.admins.base import BaseAdmin
@@ -31,6 +32,46 @@ def get_formset(self, request, obj=None, **kwargs):
3132
return formset
3233

3334

35+
class MigratedFilter(admin.SimpleListFilter):
36+
"""
37+
Admin filter for migration status.
38+
39+
Allows filtering units by whether they were migrated from v1 or created in v2.
40+
"""
41+
42+
title = _("migration status")
43+
parameter_name = "migrated"
44+
45+
def lookups(self, request, model_admin):
46+
"""
47+
Return the filter options.
48+
49+
Returns:
50+
list: A list of tuples containing (value, label) pairs for the filter options
51+
"""
52+
return [
53+
("yes", _("Migrated from old data model")),
54+
("no", _("Not migrated from old data model")),
55+
]
56+
57+
def queryset(self, request, queryset):
58+
"""
59+
Filter the queryset based on the selected option.
60+
61+
Args:
62+
request: The HTTP request
63+
queryset: The queryset to filter
64+
65+
Returns:
66+
QuerySet: The filtered queryset
67+
"""
68+
if self.value() == "yes":
69+
return queryset.filter(v1_id__isnull=False)
70+
if self.value() == "no":
71+
return queryset.filter(v1_id__isnull=True)
72+
return queryset
73+
74+
3475
class UnitAdmin(BaseAdmin):
3576
"""
3677
Admin interface for the Unit model.
@@ -41,26 +82,28 @@ class UnitAdmin(BaseAdmin):
4182

4283
fields = [
4384
"title",
85+
"migrated_status",
4486
"description",
4587
"icon",
4688
"image_tag",
4789
"jobs",
4890
"created_by",
4991
"released",
5092
]
51-
readonly_fields = ["created_by", "image_tag"]
93+
readonly_fields = ["created_by", "image_tag", "migrated_status"]
5294
inlines = [WordInline]
5395
search_fields = ["title"]
5496
list_display = [
5597
"title",
98+
"migrated_status",
5699
"released",
57100
"list_icon",
58101
"related_jobs",
59102
"creator_group",
60103
"created_at_date",
61104
]
62105
list_display_links = ["title"]
63-
list_filter = ["released"]
106+
list_filter = ["released", MigratedFilter]
64107
list_per_page = 25
65108

66109
class Media:
@@ -120,3 +163,25 @@ def created_at_date(self, obj):
120163
return obj.created_at.date()
121164

122165
created_at_date.short_description = _("created at")
166+
167+
def migrated_status(self, obj):
168+
"""
169+
Display a badge indicating whether the unit was migrated from v1 or created in v2.
170+
171+
Args:
172+
obj: The unit object
173+
174+
Returns:
175+
str: HTML formatted badge showing migration status
176+
"""
177+
if obj.v1_id is not None:
178+
return format_html(
179+
'<span style="background-color: #28a745; color: white; padding: 3px 8px; '
180+
'border-radius: 3px; font-size: 13px; font-weight: 500;">Migrated</span>'
181+
)
182+
return format_html(
183+
'<span style="background-color: #007bff; color: white; padding: 3px 8px; '
184+
'border-radius: 3px; font-size: 13px; font-weight: 500;">New</span>'
185+
)
186+
187+
migrated_status.short_description = _("migrated")

0 commit comments

Comments
 (0)