Skip to content
This repository has been archived by the owner on Jan 28, 2020. It is now read-only.

Commit

Permalink
Merge pull request #540 from mitodl/feature/skm/512_search_empty_facet
Browse files Browse the repository at this point in the history
WIP Feature/skm/512 search empty facet
  • Loading branch information
ShawnMilo committed Aug 25, 2015
2 parents 0eeef62 + ee97126 commit 2ab9ad3
Show file tree
Hide file tree
Showing 19 changed files with 514 additions and 112 deletions.
79 changes: 79 additions & 0 deletions learningresources/migrations/0015_backfill_curator_vocabularies.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals

from django.db import models, migrations
from django.utils.text import slugify

from taxonomy.signals import create_default_vocabulary

# pylint: skip-file

VOCAB_NAME = "curation status"
EMPTY_VALUE = "--not set--"


def backfill_curator_vocabs(apps, schema_editor):
"""Backfill auto-generated vocabulary to existing repos."""
Repository = apps.get_model("learningresources", "Repository")
LearningResourceType = apps.get_model(
"learningresources", "LearningResourceType")
Vocabulary = apps.get_model("taxonomy", "Vocabulary")
learning_resource_types = LearningResourceType.objects.all()
for repo in Repository.objects.all().iterator():
if Vocabulary.objects.filter(
name=VOCAB_NAME, repository__id=repo.id).exists():
continue
vocab = Vocabulary.objects.create(
repository_id=repo.id,
name=VOCAB_NAME, description=VOCAB_NAME,
required=True, vocabulary_type='m',
weight=0, multi_terms=False,
slug=slugify("{0}_{1}_backfill".format(VOCAB_NAME, repo.id)),
)
for resource_type in learning_resource_types:
vocab.learning_resource_types.add(resource_type)
create_terms(apps, vocab.id)
backfill_resources(apps, repo.id)


def create_terms(apps, vocab_id):
"""Create the curation status terms for a newly-created Vocabulary."""
Term = apps.get_model("taxonomy", "Term")
labels = (
'ready to use', 'tagged', 'course information',
'discarded', 'hidden', EMPTY_VALUE,
)
for label in labels:
Term.objects.create(
vocabulary_id=vocab_id,
label=label,
slug=slugify("{0}_{1}_backfill".format(label, vocab_id)),
weight=0,
)


def backfill_resources(apps, repo_id):
"""Add terms to existing LearningResource instances."""
LearningResource = apps.get_model("learningresources", "LearningResource")
Term = apps.get_model("taxonomy", "Term")
empty_term = Term.objects.get(
vocabulary__repository__id=repo_id,
vocabulary__name=VOCAB_NAME,
label=EMPTY_VALUE,
)
resources = LearningResource.objects.filter(
course__repository__id=repo_id
)
for resource in resources.iterator():
resource.terms.add(empty_term)

class Migration(migrations.Migration):

dependencies = [
('learningresources', '0014_learning_resource_related_name'),
('taxonomy', '0007_vocabulary_multi_terms'),
]

operations = [
migrations.RunPython(backfill_curator_vocabs)
]
94 changes: 54 additions & 40 deletions learningresources/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@

from django.db import models
from django.db import transaction
from django.dispatch import Signal
from django.contrib.auth.models import User
from django.utils.text import slugify
from django.utils.encoding import python_2_unicode_compatible
Expand All @@ -27,6 +28,12 @@
STATIC_ASSET_PREFIX = 'assets'
STATIC_ASSET_BASEPATH = STATIC_ASSET_PREFIX + '/{org}/{course_number}/{run}/'

# Used to signify that a repository has been created
# and its initial permissions have been set.
# This allows the "curation status" vocabulary to be
# created automatically.
repo_created = Signal(providing_args=["repository"])


class FilePathLengthException(Exception):
"""Custom Exception to handle long file paths"""
Expand Down Expand Up @@ -136,45 +143,6 @@ class LearningResource(BaseModel):
xa_histogram_grade = models.FloatField(default=0)
url_name = models.TextField(null=True)

def get_preview_url(self, org=None, course_number=None, run=None):
"""
Create a preview URL. Accepts optional kwargs to prevent
database lookups, especially for during search engine indexing.
Args:
org (unicode): self.course.org
run (unicode): self.course.run
course_number (unicode): self.course.course_number
"""
if org is None:
org = self.course.org
if course_number is None:
course_number = self.course.course_number
if run is None:
run = self.course.run
key = "{org}/{course}/{run}".format(
org=org,
course=course_number,
run=run,
)

if self.url_name is not None:
url_format = 'courses/{key}/jump_to_id/{preview_id}'
return LORE_PREVIEW_BASE_URL + urllib_parse.quote(
url_format.format(
base_url=LORE_PREVIEW_BASE_URL,
key=key,
preview_id=self.url_name,
)
)
else:
url_format = 'courses/{key}/courseware'
return LORE_PREVIEW_BASE_URL + urllib_parse.quote(
url_format.format(
base_url=LORE_PREVIEW_BASE_URL,
key=key,
)
)


@python_2_unicode_compatible
class LearningResourceType(BaseModel):
Expand All @@ -200,7 +168,13 @@ class Repository(BaseModel):

@transaction.atomic
def save(self, *args, **kwargs):
"""Handle slugs and groups"""
"""
Handle slugs and groups.
Note that creation of a new Repository also triggers a signal in
the taxonomy application to create the curation status vocabulary.
That code can't be put here because it would create a circular import.
"""
is_update = False
if self.id is None or self.name != get_object_or_404(
Repository, id=self.id).name:
Expand Down Expand Up @@ -228,3 +202,43 @@ class Meta: # pylint: disable=missing-docstring
RepoPermission.add_edit_metadata,
RepoPermission.manage_repo_users,
)


def get_preview_url(resource, org=None, course_number=None, run=None):
"""
Create a preview URL. Accepts optional kwargs to prevent
database lookups, especially for during search engine indexing.
Args:
resource (LearningResource): LearningResource
org (unicode): resource.course.org
run (unicode): resource.course.run
course_number (unicode): resource.course.course_number
Returns:
url (unicode): Preview URL for LearningResource.
"""
if org is None:
org = resource.course.org
if course_number is None:
course_number = resource.course.course_number
if run is None:
run = resource.course.run
key = "{org}/{course}/{run}".format(
org=org,
course=course_number,
run=run,
)

if resource.url_name is None:
path = "courseware"
preview_id = ""
else:
path = "jump_to_id"
preview_id = "/{0}".format(resource.url_name)

url_format = '{base_url}courses/{key}/{path}{preview_id}'
return url_format.format(
base_url=LORE_PREVIEW_BASE_URL,
path=urllib_parse.quote(path),
key=urllib_parse.quote(key),
preview_id=urllib_parse.quote(preview_id),
)
30 changes: 29 additions & 1 deletion learningresources/tests/test_models.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,17 +9,20 @@
from shutil import rmtree
from tempfile import mkdtemp

from django.conf import settings
from django.core.files import File
from mock import MagicMock

from .base import LoreTestCase
from learningresources.models import (
FILE_PATH_MAX_LENGTH,
LearningResource,
LearningResourceType,
Repository,
static_asset_basepath,
StaticAsset,
FilePathLengthException
FilePathLengthException,
get_preview_url,
)


Expand Down Expand Up @@ -162,3 +165,28 @@ def test_static_asset_filename_length(self):
asset=File(file_handle)
)
)

def test_preview_url(self):
"""
Test get_preview_url function. It returns different results depending
upon whether property url_name is None.
"""
base_url = "{0}courses/org/".format(settings.LORE_PREVIEW_BASE_URL)
resource = LearningResource()

kwargs = {
'resource': resource,
'org': "org",
'course_number': 'babelfish',
'run': 'gazelle',
}

tests = (
(None, '{0}babelfish/gazelle/courseware'.format(base_url)),
("WNYX", '{0}babelfish/gazelle/jump_to_id/WNYX'.format(base_url))
)

for url_name, wanted in tests:
resource.url_name = url_name
url = get_preview_url(**kwargs)
self.assertEqual(url, wanted)
5 changes: 3 additions & 2 deletions rest/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,8 @@
LearningResource,
StaticAsset,
LearningResourceType,
STATIC_ASSET_BASEPATH
STATIC_ASSET_BASEPATH,
get_preview_url as resource_preview_url,
)


Expand Down Expand Up @@ -244,7 +245,7 @@ def validate_terms(self, terms):
@staticmethod
def get_preview_url(obj):
"""Construct preview URL for LearningResource."""
return obj.get_preview_url()
return resource_preview_url(obj)


class StaticAssetSerializer(ModelSerializer):
Expand Down
6 changes: 5 additions & 1 deletion rest/tests/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

from __future__ import unicode_literals
import json
import logging

from rest_framework.status import (
HTTP_200_OK,
Expand All @@ -24,6 +25,8 @@
API_BASE = '/api/v1/'
REPO_BASE = '/api/v1/repositories/'

log = logging.getLogger(__name__)


def as_json(resp):
"""Get JSON from response."""
Expand Down Expand Up @@ -199,6 +202,7 @@ def create_vocabulary(self, repo_slug, vocab_dict=DEFAULT_VOCAB_DICT,
repo_base=REPO_BASE,
), vocab_dict)
self.assertEqual(expected_status, resp.status_code)

if resp.status_code == HTTP_201_CREATED:
result_dict = as_json(resp)
if not skip_assert:
Expand Down Expand Up @@ -528,7 +532,7 @@ def patch_learning_resource(
result_dict = as_json(resp)
if not skip_assert:
for key, value in lr_dict.items():
self.assertEqual(value, result_dict[key])
self.assertEqual(sorted(value), sorted(result_dict[key]))
return result_dict

def put_learning_resource(
Expand Down
15 changes: 10 additions & 5 deletions rest/tests/test_learning_resource.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
from __future__ import unicode_literals
import os

from django.utils.text import slugify

from rest_framework.status import (
HTTP_200_OK,
HTTP_400_BAD_REQUEST,
Expand All @@ -25,9 +27,10 @@
LearningResource,
LearningResourceType,
Repository,
get_preview_url,
)
from importer.tasks import import_file
from taxonomy.models import Vocabulary
from taxonomy.models import Vocabulary, Term
from roles.permissions import GroupTypes
from roles.api import assign_user_to_repo_group

Expand Down Expand Up @@ -166,8 +169,10 @@ def test_add_term_to_learning_resource(self):
unsupported_term_slug = self.create_term(
self.repo.slug, vocab2_slug)['slug']

self.assertEqual([], self.get_learning_resource(
self.repo.slug, lr_id)['terms'])
self.assertEqual(
[slugify(Term.EMPTY_VALUE)],
self.get_learning_resource(self.repo.slug, lr_id)['terms']
)

self.patch_learning_resource(
self.repo.slug, lr_id, {"terms": [supported_term_slug]})
Expand Down Expand Up @@ -263,7 +268,7 @@ def test_preview_url(self):
)
self.assertEqual(
expected_jump_to_id_url,
learning_resource.get_preview_url()
get_preview_url(learning_resource)
)

resource_dict = self.get_learning_resource(
Expand All @@ -275,7 +280,7 @@ def test_preview_url(self):
self.assertEqual(
"https://www.sandbox.edx.org/courses/"
"test-org/infinity/Febtober/courseware",
learning_resource.get_preview_url()
get_preview_url(learning_resource)
)

def test_learning_resource_exports_invalid_methods(self):
Expand Down
9 changes: 6 additions & 3 deletions rest/tests/test_repository.py
Original file line number Diff line number Diff line change
Expand Up @@ -83,17 +83,20 @@ def test_repositories(self):
repositories = self.get_repositories()
self.assertEqual(2, repositories['count'])

original_count = self.get_vocabularies(self.repo.slug)['count']
vocab_slug = self.create_vocabulary(self.repo.slug)['slug']
vocabularies = self.get_vocabularies(self.repo.slug)
self.assertEqual(1, vocabularies['count'])
self.assertEqual(
original_count + 1,
self.get_vocabularies(self.repo.slug)['count']
)

self.delete_repository(self.repo.slug,
expected_status=HTTP_405_METHOD_NOT_ALLOWED)

self.delete_vocabulary(self.repo.slug, vocab_slug)

vocabularies = self.get_vocabularies(self.repo.slug)
self.assertEqual(0, vocabularies['count'])
self.assertEqual(original_count, vocabularies['count'])

self.delete_repository(self.repo.slug,
expected_status=HTTP_405_METHOD_NOT_ALLOWED)
Expand Down
Loading

0 comments on commit 2ab9ad3

Please sign in to comment.