Skip to content

Commit

Permalink
feat: Generic system defined object tags and Language object tag added
Browse files Browse the repository at this point in the history
  • Loading branch information
ChrisChV committed Jul 7, 2023
1 parent 0ffda05 commit c0a2aeb
Show file tree
Hide file tree
Showing 10 changed files with 390 additions and 359 deletions.
24 changes: 0 additions & 24 deletions openedx_tagging/core/tagging/migrations/0002_systemtaxonomy.py

This file was deleted.

47 changes: 8 additions & 39 deletions openedx_tagging/core/tagging/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,6 @@
# Will contain 0...TAXONOMY_MAX_DEPTH elements.
Lineage = List[str]

class SystemDefinedTaxonomyTagsType(Enum):
"""
Tag types of system-defined taxonomies
"""
closed = 'closed' # Tags are created by fixtures/migrations
free_form = 'free_form' # Tags are free form


class Tag(models.Model):
"""
Expand Down Expand Up @@ -497,6 +490,9 @@ def _check_object(self):
"""
# Must have a valid object id/type:
return self.object_id and self.object_type

def _check_tag(self):
return self.value

def is_valid(self, check_taxonomy=True, check_tag=True, check_object=True) -> bool:
"""
Expand All @@ -515,7 +511,7 @@ def is_valid(self, check_taxonomy=True, check_tag=True, check_object=True) -> bo
return False

# Open taxonomies don't need an associated tag, but we need a value.
if check_tag and not self.value:
if check_tag and not self._check_tag():
return False

if check_object and not self._check_object():
Expand All @@ -539,6 +535,9 @@ def valid_for(cls, taxonomy: Taxonomy = None, tag: Tag = None, **kwargs) -> bool
"""
return tag and taxonomy and not taxonomy.allow_free_text

def _check_tag(self):
return self.tag_id

def is_valid(self, check_taxonomy=True, check_tag=True, check_object=True) -> bool:
"""
Returns True if this ObjectTag is valid for use with a closed taxonomy.
Expand All @@ -554,7 +553,7 @@ def is_valid(self, check_taxonomy=True, check_tag=True, check_object=True) -> bo
return False

# Closed taxonomies require a Tag
if check_tag and not self.tag_id:
if check_tag and not self._check_tag():
return False

if check_tag and check_taxonomy and (self.tag.taxonomy != self.taxonomy):
Expand All @@ -571,33 +570,3 @@ def is_valid(self, check_taxonomy=True, check_tag=True, check_object=True) -> bo
register_object_tag_class(OpenObjectTag)
register_object_tag_class(ClosedObjectTag)

class SystemTaxonomy(Taxonomy):
"""
System-defined taxonomies are not editable by normal users; they're defined by fixtures/migrations, and may have
dynamically-determined Tags and ObjectTags.
"""

@property
def system_defined(self) -> bool:
"""
This is a system-defined taxonomy.
"""
return True

@property
def is_visible(self) -> bool:
"""
Controls the visibility of this taxonomy to content authors.
This value is static and must be implemented per each system-defined taxonomy.
"""
raise NotImplementedError

@property
def creation_type (self) -> SystemDefinedTaxonomyTagsType:
"""
Controls the behaviour of the tags on this taxonomy
This value is static and must be implemented per each system-defined taxonomy.
"""
raise NotImplementedError
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
- model: oel_tagging.taxonomy
pk: 1
fields:
name: Language
name: Languages
description: ISO 639-1 Languages
enabled: true
required: true
Expand Down
172 changes: 172 additions & 0 deletions openedx_tagging/core/tagging/system_defined_taxonomies/object_tags.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,172 @@
from typing import List

from django.conf import settings
from django.contrib.auth import get_user_model
from django.core.exceptions import FieldDoesNotExist
from django.db import models


from openedx_tagging.core.tagging.models import (
Tag,
Taxonomy,
OpenObjectTag,
ClosedObjectTag,
)
from openedx_tagging.core.tagging.registry import register_object_tag_class


class OpenSystemObjectTag(OpenObjectTag):
"""
Free-text object tag used on system-defined taxonomies
"""

class Meta:
proxy = True

@classmethod
def valid_for(cls, taxonomy: Taxonomy = None, **kwars):
"""
Returns True if the the taxonomy is system-defined
"""
return super().valid_for(taxonomy=taxonomy) and taxonomy.system_defined

class ClosedSystemObjectTag(ClosedObjectTag):
"""
Object tags linked to a closed system-taxonomy
"""

class Meta:
proxy = True

@classmethod
def valid_for(cls, taxonomy: Taxonomy = None, tag: Tag = None, **kargs):
"""
Returns True if the the taxonomy is system-defined
"""
return super().valid_for(taxonomy=taxonomy, tag=tag) and taxonomy.system_defined


class ModelObjectTag(OpenSystemObjectTag):
"""
Object tags used with tags that relate to the id of a model
This object tag class is not registered as it needs to have an associated model
"""

class Meta:
proxy = True

tag_class_model = None

@classmethod
def valid_for(cls, taxonomy: Taxonomy = None, **kwars):
"""
Validates if has an associated Django model that has an Id
"""
if not super().valid_for(taxonomy=taxonomy) or not cls.tag_class_model:
return False

# Verify that is a Django model
if not issubclass(cls.tag_class_model, models.Model):
return False

# Verify that the model has 'id' field
try:
cls.tag_class_model._meta.get_field('id')
except FieldDoesNotExist:
return False

return True

def _check_instance(self):
"""
Validates if the instance exists
"""
try:
intance_id = int(self.value)
except ValueError:
return False
return self.tag_class_model.objects.filter(id=intance_id).exists()

def _check_tag(self):
"""
Validates if the instance exists
"""
if not super()._check_tag():
return False

# Validates if the instance exists
if not self._check_instance():
return False

return True


class UserObjectTag(ModelObjectTag):
"""
Object tags used on taxonomies associated with user model
"""

class Meta:
proxy = True

tag_class_model = get_user_model()


class LanguageObjectTag(ClosedSystemObjectTag):
"""
Object tag for Languages
The tags are filtered and validated taking into account the
languages available in Django LANGUAGES settings var
"""

class Meta:
proxy = True

@classmethod
def get_tags(cls, taxonomy: Taxonomy) -> List[Tag]:
"""
Returns a list of tags of the available languages.
"""
# TODO we need to overweite this
# tags = super().get_tags()
tags = taxonomy.tag_set.objects().all()
result = []
available_langs = cls.get_available_languages()
for tag in tags:
if tag.external_id in available_langs:
result.append(tag)
return result

@classmethod
def _get_available_languages(cls) -> List[str]:
"""
Get the available languages from Django LANGUAGE.
"""
langs = set()
for django_lang in settings.LANGUAGES:
# Split to get the language part
langs.add(django_lang[0].split('-')[0])
return langs


def _check_tag(self):
"""
Validates if the language tag is on the available languages
"""
if not super()._check_tag():
return False

available_langs = self._get_available_languages()

# Must be linked to a tag and must be an available language
if not self.tag or not self.tag.external_id in available_langs:
return False

return True


# Register the object tag classes in reverse order for how we want them considered
register_object_tag_class(LanguageObjectTag)
register_object_tag_class(UserObjectTag)
Loading

0 comments on commit c0a2aeb

Please sign in to comment.