Commit 9f0899a3 by christopher lee Committed by Christopher Lee

Clean up Subject and SubjectTranslation models

parent 0a0d285b
......@@ -7,7 +7,7 @@ travis_start: ## Start containers stopped by `travis_stop`
docker-compose -f .travis/docker-compose-travis.yml start
travis_test: ## Run tests on Docker containers, as on Travis CI
docker exec -it course-discovery env TERM=$(TERM) /edx/app/discovery/discovery/.travis/run_tests.sh
docker exec -it discovery env TERM=$(TERM) /edx/app/discovery/discovery/.travis/run_tests.sh
travis_stop: ## Stop running containers created by `travis_up` without removing them
docker-compose -f .travis/docker-compose-travis.yml stop
......
......@@ -191,9 +191,6 @@ class FAQSerializer(serializers.ModelSerializer):
class SubjectSerializer(serializers.ModelSerializer):
"""Serializer for the ``Subject`` model."""
name = serializers.CharField(source='name_t')
subtitle = serializers.CharField(source='subtitle_t')
description = serializers.CharField(source='description_t')
@classmethod
def prefetch_queryset(cls):
......
......@@ -963,11 +963,11 @@ class SubjectSerializerTests(TestCase):
serializer = SubjectSerializer(subject)
expected = {
'name': subject.name_t,
'description': subject.description_t,
'name': subject.name,
'description': subject.description,
'banner_image_url': subject.banner_image_url,
'card_image_url': subject.card_image_url,
'subtitle': subject.subtitle_t,
'subtitle': subject.subtitle,
'slug': subject.slug,
'uuid': str(subject.uuid),
}
......
......@@ -224,12 +224,11 @@ class OrganizationAdmin(admin.ModelAdmin):
@admin.register(Subject)
class SubjectAdmin(TranslatableAdmin):
# These fields are excluded here because they will be removed in favor of the translated fields.
exclude = ('name', 'subtitle', 'description')
list_display = ('uuid', 'name_t', 'slug',)
exclude = ('name_t', 'subtitle_t', 'description_t') # TODO Remove in the clean up phase LEARNER-2617
list_display = ('uuid', 'name', 'slug',)
list_filter = ('partner',)
readonly_fields = ('uuid',)
search_fields = ('uuid', 'name_t', 'slug',)
search_fields = ('uuid', 'name', 'slug',)
@admin.register(Person)
......
......@@ -143,9 +143,9 @@ class SubjectMarketingSiteDataLoader(AbstractMarketingSiteDataLoader):
slug = data['field_subject_url_slug']
defaults = {
'uuid': data['uuid'],
'name_t': data['title'],
'description_t': self.clean_html(data['body']['value']),
'subtitle_t': self.clean_html(data['field_subject_subtitle']['value']),
'name': data['title'],
'description': self.clean_html(data['body']['value']),
'subtitle': self.clean_html(data['field_subject_subtitle']['value']),
'card_image_url': self._get_nested_url(data.get('field_subject_card_image')),
# NOTE (CCB): This is not a typo. Yes, the banner image for subjects is in a field with xseries in the name.
'banner_image_url': self._get_nested_url(data.get('field_xseries_banner_image'))
......
......@@ -146,9 +146,9 @@ class SubjectMarketingSiteDataLoaderTests(AbstractMarketingSiteDataLoaderTestMix
subject = Subject.objects.get(slug=slug, partner=self.partner)
expected_values = {
'uuid': UUID(data['uuid']),
'name_t': data['title'],
'description_t': self.loader.clean_html(data['body']['value']),
'subtitle_t': self.loader.clean_html(data['field_subject_subtitle']['value']),
'name': data['title'],
'description': self.loader.clean_html(data['body']['value']),
'subtitle': self.loader.clean_html(data['field_subject_subtitle']['value']),
'card_image_url': data['field_subject_card_image']['url'],
'banner_image_url': data['field_xseries_banner_image']['url'],
}
......@@ -157,7 +157,7 @@ class SubjectMarketingSiteDataLoaderTests(AbstractMarketingSiteDataLoaderTestMix
self.assertEqual(getattr(subject, field), value)
@responses.activate
def test_ingest(self):
def test_ingest_create(self):
self.mock_login_response()
api_data = self.mock_api()
......@@ -167,15 +167,15 @@ class SubjectMarketingSiteDataLoaderTests(AbstractMarketingSiteDataLoaderTestMix
self.assert_subject_loaded(datum)
@responses.activate
def test_ingest2(self):
def test_ingest_update(self):
self.mock_login_response()
api_data = self.mock_api()
for data in api_data:
subject_data = {
'uuid': UUID(data['uuid']),
'name_t': data['title'],
'description_t': self.loader.clean_html(data['body']['value']),
'subtitle_t': self.loader.clean_html(data['field_subject_subtitle']['value']),
'name': data['title'],
'description': self.loader.clean_html(data['body']['value']),
'subtitle': self.loader.clean_html(data['field_subject_subtitle']['value']),
'card_image_url': data['field_subject_card_image']['url'],
'banner_image_url': data['field_xseries_banner_image']['url'],
}
......
......@@ -31,7 +31,7 @@ def backwards_func(apps, schema_editor):
for subject in Subject.objects.all():
try:
translation = SubjectTranslation.objects.filter(master_id=subject.pk)
translation = SubjectTranslation.objects.get(master_id=subject.pk, language_code=settings.LANGUAGE_CODE)
subject.name = translation.name_t
subject.subtitle = translation.subtitle_t
subject.description = translation.description_t
......@@ -40,7 +40,6 @@ def backwards_func(apps, schema_editor):
# nothing to migrate
logger.exception('Migrating data from SubjectTranslation for master_id={} DoesNotExist'.format(subject.pk))
class Migration(migrations.Migration):
dependencies = [
('course_metadata', '0060_create_subjecttranslations_models'),
......
# -*- coding: utf-8 -*-
# Generated by Django 1.11.3 on 2017-10-05 19:31
from __future__ import unicode_literals
import django_extensions.db.fields
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('course_metadata', '0062_courserun_license'),
]
operations = [
migrations.RemoveField(
model_name='subject',
name='description',
),
migrations.RemoveField(
model_name='subject',
name='name',
),
migrations.RemoveField(
model_name='subject',
name='subtitle',
),
migrations.AddField(
model_name='subjecttranslation',
name='description',
field=models.TextField(blank=True, null=True),
),
migrations.AddField(
model_name='subjecttranslation',
name='name',
field=models.CharField(blank=True, max_length=255),
),
migrations.AddField(
model_name='subjecttranslation',
name='subtitle',
field=models.CharField(blank=True, max_length=255, null=True),
),
migrations.AlterField(
model_name='subject',
name='slug',
field=django_extensions.db.fields.AutoSlugField(blank=True, editable=False,
help_text='Leave this field blank to have the value generated automatically.',
populate_from='name'),
),
migrations.RunSQL(
["UPDATE course_metadata_subjecttranslation SET name = name_t",
"UPDATE course_metadata_subjecttranslation SET description = description_t",
"UPDATE course_metadata_subjecttranslation SET subtitle = subtitle_t"],
["UPDATE course_metadata_subjecttranslation SET name_t = name",
"UPDATE course_metadata_subjecttranslation SET description_t = description",
"UPDATE course_metadata_subjecttranslation SET subtitle_t = subtitle"],
),
migrations.AlterField(
model_name='subjecttranslation',
name='name',
field=models.CharField(max_length=255),
),
]
......@@ -116,13 +116,11 @@ class LevelType(AbstractNamedModel):
class Subject(TranslatableModel, TimeStampedModel):
""" Subject model. """
uuid = models.UUIDField(blank=False, null=False, default=uuid4, editable=False, verbose_name=_('UUID'))
name = models.CharField(max_length=255, blank=False, null=False)
subtitle = models.CharField(max_length=255, blank=True, null=True)
description = models.TextField(blank=True, null=True)
banner_image_url = models.URLField(blank=True, null=True)
card_image_url = models.URLField(blank=True, null=True)
slug = AutoSlugField(populate_from='name_t', editable=True, blank=True,
slug = AutoSlugField(populate_from='name', editable=True, blank=True,
help_text=_('Leave this field blank to have the value generated automatically.'))
partner = models.ForeignKey(Partner)
def __str__(self):
......@@ -137,20 +135,21 @@ class Subject(TranslatableModel, TimeStampedModel):
def validate_unique(self, *args, **kwargs):
super(Subject, self).validate_unique(*args, **kwargs)
qs = Subject.objects.filter(partner=self.partner_id)
if qs.filter(translations__name_t=self.name_t).exists():
raise ValidationError({'name_t': ['Subject with this Name and Partner already exists', ]})
if qs.filter(translations__name=self.name).exclude(pk=self.pk).exists():
raise ValidationError({'name': ['Subject with this Name and Partner already exists', ]})
class SubjectTranslation(TranslatedFieldsModel):
master = models.ForeignKey(Subject, related_name='translations', null=True)
name = models.CharField(max_length=255, blank=False, null=False)
subtitle = models.CharField(max_length=255, blank=True, null=True)
description = models.TextField(blank=True, null=True)
name_t = models.CharField(max_length=255, blank=False, null=False)
subtitle_t = models.CharField(max_length=255, blank=True, null=True)
description_t = models.TextField(blank=True, null=True)
def __str__(self):
return self.name_t
class Meta:
unique_together = ('language_code', 'master')
verbose_name = _('Subject model translations')
......
......@@ -41,8 +41,8 @@ class SubjectFactory(factory.DjangoModelFactory):
class Meta:
model = Subject
name_t = FuzzyText() # TODO Switch to 'name' when 'name_t' is renamed.
description_t = FuzzyText()
name = FuzzyText()
description = FuzzyText()
banner_image_url = FuzzyURL()
card_image_url = FuzzyURL()
partner = factory.SubFactory(PartnerFactory)
......
......@@ -996,26 +996,24 @@ class FAQTests(TestCase):
class SubjectTests(SiteMixin, TestCase):
""" Tests of the Multilingual Subject model. """
""" Tests of the Multilingual Subject (and SubjectTranslation) model. """
def test_partner_name_t_uniqueness(self):
dummy_url = "http://www.example.com"
Subject.objects.create(
def test_validate_unique(self):
subject = Subject.objects.create(
name="name1",
name_t="aaa",
partner_id=self.partner.id,
banner_image_url=dummy_url,
card_image_url=dummy_url)
banner_image_url="http://www.example.com",
card_image_url="http://www.example.com")
self.assertIsNone(subject.full_clean())
invalid_subject = Subject(
name="name2",
name_t="aaa",
duplicate_subject = Subject(
name="name1",
partner_id=self.partner.id,
banner_image_url=dummy_url,
card_image_url=dummy_url)
banner_image_url="http://www.example.com",
card_image_url="http://www.example.com")
with self.assertRaises(ValidationError) as validation_error:
invalid_subject.full_clean()
duplicate_subject.full_clean()
self.assertEqual(
str(validation_error.exception),
"{'name_t': ['Subject with this Name and Partner already exists']}")
"{'name': ['Subject with this Name and Partner already exists']}")
......@@ -103,7 +103,7 @@ class CourseForm(BaseForm):
label=_('Organization Course Admin'),
)
subjects = Subject.objects.all().order_by('name')
subjects = Subject.objects.all().order_by("translations__name")
primary_subject = forms.ModelChoiceField(
queryset=subjects,
label=_('Primary'),
......
......@@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2017-10-12 11:27+0500\n"
"POT-Creation-Date: 2017-10-16 17:29+0000\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
......
......@@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2017-10-12 11:27+0500\n"
"POT-Creation-Date: 2017-10-16 17:29+0000\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
......
......@@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2017-10-12 11:27+0500\n"
"POT-Creation-Date: 2017-10-16 17:29+0000\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
......
......@@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2017-10-12 11:27+0500\n"
"POT-Creation-Date: 2017-10-16 17:29+0000\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
......
......@@ -22,6 +22,17 @@ DEFAULT_PARTNER_ID = 1
COMPRESS_OFFLINE = False
COMPRESS_ENABLED = False
PARLER_LANGUAGES = {
1: (
{'code': LANGUAGE_CODE, },
{'code': 'es', },
),
'default': {
'fallbacks': [PARLER_DEFAULT_LANGUAGE_CODE],
'hide_untranslated': False,
}
}
#####################################################################
# Lastly, see if the developer has any local overrides.
if os.path.isfile(join(dirname(abspath(__file__)), 'private.py')):
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment