-
Notifications
You must be signed in to change notification settings - Fork 0
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
System defined taxonomies #3
System defined taxonomies #3
Conversation
9d07be9
to
a8566d3
Compare
openedx_tagging/core/tagging/system_defined_taxonomies/object_tags.py
Outdated
Show resolved
Hide resolved
c0a2aeb
to
8460459
Compare
openedx_tagging/core/tagging/system_defined_taxonomies/object_tags.py
Outdated
Show resolved
Hide resolved
669b847
to
e959287
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hi @ChrisChV, this is really interesting.. I couldn't see how the system taxonomies would fit together until I read your code! Very cool.
I've requested some changes, but: Feel free to wait until we've got approval at least in principle on openedx#62 before taking time to apply them, just in case that approach gets changed again.
Also, feel free to push back on anything that seems unreasonable?
openedx_tagging/core/tagging/system_defined_taxonomies/object_tags.py
Outdated
Show resolved
Hide resolved
openedx_tagging/core/tagging/system_defined_taxonomies/object_tags.py
Outdated
Show resolved
Hide resolved
openedx_tagging/core/tagging/system_defined_taxonomies/object_tags.py
Outdated
Show resolved
Hide resolved
openedx_tagging/core/tagging/system_defined_taxonomies/object_tags.py
Outdated
Show resolved
Hide resolved
openedx_tagging/core/tagging/system_defined_taxonomies/object_tags.py
Outdated
Show resolved
Hide resolved
result = object_tag.is_valid( | ||
check_taxonomy=True, | ||
check_object=True, | ||
check_tag=True, | ||
) | ||
|
||
self.assertEqual(result, assert_value) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's ok that all of these are explicitly laid out, but you can just say this if you want to:
result = object_tag.is_valid( | |
check_taxonomy=True, | |
check_object=True, | |
check_tag=True, | |
) | |
self.assertEqual(result, assert_value) | |
assert object_tag.is_valid() == assert_value |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
All test has been updated with this
@pomegranited In your experience, is there a difference in using one approach or another? For my tests I have always used self.assertEqual()
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@ChrisChV There's no functional difference.. I just think using plain python wherever possible looks cleaner. But I'm not that fussy about it -- we're using TestClass as a base, so all of its .assert*
methods are available regardless.
openedx_tagging/core/tagging/system_defined_taxonomies/object_tags.py
Outdated
Show resolved
Hide resolved
class Meta: | ||
proxy = True | ||
|
||
tag_class_model = get_user_model() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I really like how you used external_id
to store the language code for the language object tag , and then used the full language name as the value. So when we actually fetch an ObjectTag for display, we have a nice human-readable value to show.
I'd like to support something like that here with model tags. So that when we have a tag that references a User or Organization, we can show the User/Organization full name instead of the ID. Plus, that name will always be up-to-date, because we're fetching it from the source table.
So I'm wondering, can we do something like this:
class ObjectTag:
@property
def value_pretty(self):
"""
Returns a human-friendly version of the tag value stored here.
Subclasses should override this if they don't store human-readable values.
"""
return self.value
And then we can have here:
class ModelObjectTag(...):
@property
def tag_class_model_pretty_field(self):
"""
Name of the field to use on the tag_class_model instance to which stores the "pretty value" of the tag.
"""
raise NotImplementedError
@property
def value_pretty(self):
"""
Returns a human-friendly version of the tag value stored here.
Subclasses should override this if they don't store human-readable values.
"""
value = self.value
try:
instance = self.tag_class_model.objects.get(pk=value)
value = getattr(instance, self.tag_class_model_pretty_field, value)
except self.tag_class_model.DoesNotExist:
# Means the tag is invalid, but that's ok here
pass
return value
...
class UserObjectTag(ModelObjectTag):
@property
def tag_class_model_pretty_field(self):
"""
Name of the field to use on the tag_class_model instance to which stores the "pretty value" of the tag.
"""
return "full_name"
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Updated to use this approach #3 (comment)
fields: | ||
taxonomy: 1 | ||
parent: null | ||
value: German |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please make sure we're thinking about how to translate these before displaying them in the UI. For a German user, this language tag needs to appear as "Deutsch", not "German".
We should probably use a similar mechanism for users, storing their user ID as the tag value but displaying their username or their "full name (username)" in the UI.
You don't have to implement this in this PR (it's a bad idea to, actually) - just think about how it will work in the future.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We should probably use a similar mechanism for users, storing their user ID as the tag value but displaying their username or their "full name (username)" in the UI.
What do you think of this idea?
I think it could be adapted for translations too..
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think something like that would make sense, yes. Just not clear on how it would be adapted to the current approach. But let's not worry too much about that just yet; I don't want to muddy the waters any further here :)
a7661bb
to
0807949
Compare
) | ||
|
||
|
||
class ModelObjectTag(OpenSystemObjectTag): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@ChrisChV Since we've gone back to subclassing Taxonomy again, I wonder if we could create a ModelTaxonomy class, which can:
- Create Tag objects on the fly when new ObjectTags are added
- set external_id=model.pk
- set value = value we want to show to the world, e.g. User.username or Organization.name
- "Resync" could somehow re-fetch the related object and update this value? Or maybe we need another method.
- Can the ModelTaxonomy can be "closed" even though the Tag values don't exist (yet)?
- Can we return "autocomplete_tags" based on the values available in the linked model?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Create Tag objects on the fly when new ObjectTags are added
This has been one of the rejected options for creating Tags. But now that I see it, it's easier than it seemed and has many more advantages. Yes, I will build this type of taxonomy
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@pomegranited I updated the ADR e031a5e
e959287
to
8d84cd2
Compare
…vior (openedx#62) Adds support for custom Taxonomy subclasses to be stored against a Taxonomy, to be used by the python API when instantiating and returning Taxonomies. Also adds minimal support for ObjectTag subclasses. However, these are not stored against the ObjectTag instances; they can be instantiated by the Taxonomy subclasses if and when needed. Related: * docs: updates decisions to reflect this change * feat: adds api.get_taxonomy, which returns a Taxonomy cast to its subclass, when set * refactor: adds _check_taxonomy, _check_tag, and _check_object methods to the Taxonomy class, which can be overridden by subclasses when validating ObjectTags Added to support system-defined Taxonomies: * feat: adds un-editable Taxonomy.system_defined field so that system taxonomies can store this field and ensure no one edits them. * feat: adds Taxonomy.visible_to_authors, which is needed for fully automated tagging. Cleanup changes: * fix: updates Tag model to cascade delete if the Taxonomy or parent Tag is deleted. * style: adds missing type annotations to rules and python API
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hi @ChrisChV , I started suggesting things, but then decided it would be clearer to put the changes into a PR: #6
What do you think about that approach instead?
I'm also thinking we could rearrange the model code a bit to shorten the (now quite long) models.py
:
- move the base models (Tag, Taxonomy, ObjectTag) to
openedx_tagging/core/tagging/models/base.py
- move the system-defined models to
openedx_tagging/core/tagging/models/system_defined.py
- import the models we want people to use outside this library (Tag, Taxonomy, ObjectTag, LanguageTaxonomy, UserModelTaxonomy, etc.) in
openedx_tagging/core/tagging/models/__init__.py
If you agree, feel free to split out a test_system_defined_models.py
as well.
|
||
|
||
@ddt.ddt | ||
class TestSystemDefinedTaxonomy(TestTagTaxonomyMixin, TestCase): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If you have time, could you a test for a simple, fully dynamic SystemTaxonomy as @bradenmacdonald suggested here?
…x#66) AWS Aurora does not support COMPRESSED in its MySQL implementation: https://docs.aws.amazon.com/AmazonRDS/latest/AuroraUserGuide/AuroraMySQL.Migrating.RDSMySQL.Import.html
d4500df
to
e031a5e
Compare
@pomegranited Yes, I agree with that. Thanks. I will add it |
* Adds Taxonomy.object_tag_class property, which can be overridden by Taxonomy subclasses to provide their own ObjectTag subclasses. * Replaces the Taxonomy._set_tag method with an ObjectTag.tag_ref property setter, so that ObjectTag subclasses can dynamically create Tags as needed during tag_object() * Moves the tag_class_model and tag_class_value properties to a new ModelObjectTag abstract class, so the ModelSystemDefinedTaxonomy can be simpler. * Updates tests for coverage
removing the need for _get_tags_query_set
3d23976
to
0c74bb4
Compare
- Taxonomy.system_defined updated to be a hardcoded property. - TaxonomyAdmin updated to avoid edit and delete system taxonomies. - TagAdmin updated to avoid edit and delete tags from system taxonomies. - TagAmdin updated to avoid create Tags on system taxonomies.
Closed in favor of openedx#67 |
Description
Subclasses related to build System-defined taxonomies:
models.py
moved tomodels/base.py
tag_ref
setter to allow override how to set a tag on an object_tagobject_tag
added to TaxonomyTesting instructions
Manual testing is possible but not very useful, since we don't yet have views for these models and APIs.
But you can check that the added tests cover the expected polymorphism behavior supported by this change, and that tests are still covering 100% of the openedx_tagging module.
Testing admin:
Other information
Relates to openedx/modular-learning#77