Skip to content
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

Fixes #2956: Images in explorations have descriptive alt attributes #2972

Merged
merged 21 commits into from
Feb 1, 2017

Conversation

kirankonduru
Copy link
Contributor

Fixes #2956 : Whenever images are uploaded in explorations the creator needs to specify the alt field which is a required field now.

@seanlip
Copy link
Member

seanlip commented Jan 25, 2017

@kirankonduru, please remove code in this PR that's related to the other one. The file diff should only reflect changes that are being made to the specific issue that's being fixed. Remember to base your branch off of develop at the outset, and not your other feature branch.

When you've done that, please assign this issue to @anthkris for review. Thanks!

@codecov-io
Copy link

codecov-io commented Jan 25, 2017

Codecov Report

Merging #2972 into develop will increase coverage by 0.08%.

@@             Coverage Diff             @@
##           develop    #2972      +/-   ##
===========================================
+ Coverage    46.24%   46.33%   +0.08%     
===========================================
  Files          227      228       +1     
  Lines        17762    17800      +38     
  Branches      2875     2871       -4     
===========================================
+ Hits          8214     8247      +33     
- Misses        9548     9553       +5
Impacted Files Coverage Δ
...d/domain/collection/CollectionNodeObjectFactory.js 91.42% <ø> (-2.52%)
...es/exploration_editor/EditorNavigationDirective.js 2.56% <ø> (-0.22%)
...ead/domain/exploration/ExplorationObjectFactory.js 1.72% <ø> (-0.07%)
...ev/head/pages/exploration_player/PlayerServices.js 1.7% <ø> (-0.07%)
...lates/dev/head/pages/library/SearchBarDirective.js 1.14% <ø> (-0.02%)
.../dev/head/domain/exploration/StateObjectFactory.js 100% <ø> (ø)
.../exploration_editor/ParamChangesEditorDirective.js 0.9% <ø> (ø)
...ges/exploration_editor/ParameterMetadataService.js 1.06% <ø> (ø)
.../exploration_player/AnswerClassificationService.js 97.14% <ø> (ø)
...es/exploration_editor/editor_tab/StateResponses.js 17.94% <ø> (ø)
... and 5 more

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update f16ebd7...eea71b8. Read the comment docs.

Copy link
Contributor

@anthkris anthkris left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As far as I can see, this looks great! The UX is on par with other areas of the editor where you cannot save your changes until you fill in the required information.

@anthkris
Copy link
Contributor

Hey @kirankonduru Please take a look at the failing checks. Take a look at this resource: https://github.com/oppia/oppia/wiki/If-your-build-fails...

@kirankonduru
Copy link
Contributor Author

@anthkris the test fails because a default value is needed in the description field (alt field) if we want to make it a required field. What do you suggest as the default value for this field?

@anthkris
Copy link
Contributor

@kirankonduru I'd suggest making the UI similar to the publish window.

screen shot 2017-01-26 at 6 02 21 pm

The default text should offer an exemplar. Perhaps something like "George Frideric Handel, 18th century baroque composer" or something relatively universal.

It may also be helpful to add a bit to the label for the field, the "Alternative text (for screen readers)" part to say "Alternative text (for screen readers): Describe the contents of this image"

@seanlip Any thoughts?

@seanlip
Copy link
Member

seanlip commented Jan 27, 2017

Hi @anthkris, no thoughts on my end -- I defer to you and @jaredsilver on this.

@jaredsilver
Copy link
Contributor

While we may understand what alt text and screen readers are, I have a feeling that most teachers and other exploration creators may not. Therefore, it may make sense to tell the user why instead of what. For example, the label might say "Briefly explain this image to a visually impaired learner." And then, the placeholder could offer an exemplar as @anthkris mentioned. As an exploration creator, this may lead me to write a better and more descriptive alternative text because now I know the importance of why I'm doing it, and I also have a good example of what it should look like. What do you think about that approach, @anthkris?

@anthkris
Copy link
Contributor

Thanks @jaredsilver That's exactly what I was thinking! I think people have a vague notion of what the alt attribute is but not how to write a good one or why it's really important. I like this approach!

@kirankonduru Please let me know if you have any other questions or concerns.

@kirankonduru
Copy link
Contributor Author

Here is the modified image upload section
screenshot from 2017-01-28 02 43 16

Now the alt field must have a string with length greater than zero if we want to make that field required. In the current schemas all the fields that are required (for example in the insert tabs section) the input fields have some descriptive text by default.

@anthkris
Copy link
Contributor

I think that it may be better to remove the first part of the label, "Alternative text (for screen readers)" and just stick with the why explanation (the second part). As for the example, it's considered bad practice to write "image of" or "picture of" in the alt text. It's redundant.

@kirankonduru
Copy link
Contributor Author

kirankonduru commented Jan 27, 2017

So any other suggestions for default text in the alt field?
If we add this "George Frideric Handel, 18th century baroque composer" and the user does not edit it then the alt for image becomes this string.

@anthkris
Copy link
Contributor

I can understand the problem; it's difficult to come up with something universal, but we do want it to fit the guidelines for a good alt description. So we don't want them to think that they can simply complete the phrase "Image of..."
Also, I would say that the desired behavior is that a user should be forced to edit this field before the window will close and save their changes. In other words, the default text should not remain. Is that not what is happening now?

@kirankonduru
Copy link
Contributor Author

In the current schemas whenever we make a field required we need to provide a default value. The forms are generic and are used in insert tabs and others too. We need to modify the current schemas if we want to allow the default value to be an empty string and in that case we can force the user to fill in the data.

@seanlip
Copy link
Member

seanlip commented Jan 27, 2017

Note that, if there's confirmation that the right thing to do here (from a user perspective) is to have a default value that is the empty string, then it should be possible to remove the general requirement that the default value must be nonempty for required fields.

@kirankonduru -- what other fields make use of this requirement? Just want to make sure that this infrastructural change doesn't break anything.

@kirankonduru
Copy link
Contributor Author

  1. extensions/gadgets/AdviceBar/AdviceBar.py
  2. extensions/gadgets/TestGadget/TestGadget.py
  3. extensions/gadgets/base_test.py
  4. schema_utils_test.py

All these files have the same condition that is needed to make a field required
i.e. ,
'validators': [{
'id': 'is_nonempty',
}],

and whenever we have an empty string as a defualt value the tests fail.

@seanlip
Copy link
Member

seanlip commented Jan 27, 2017

OK. If that's a comprehensive list of the affected fields, I am fine with removing that restriction.

@kirankonduru
Copy link
Contributor Author

  1. Modified the unicode schema which adds a placeholder to this schema.
  2. Now the required fields can be empty but they must be filled before the form can be submitted.

schema_utils.py Outdated
@@ -236,8 +236,10 @@ def has_length_at_most(obj, max_value):

@staticmethod
def is_nonempty(obj):
"""Returns True iff the given object (a string) is nonempty."""
return bool(obj)
"""Always returns True so that an input field can be made required
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This isn't the correct place to do this. It results in the method not saying what it does on the tin, and that's a programming antipattern.

Where exactly is the default value being tested? We need to modify that test somehow, I think.

Copy link
Contributor Author

@kirankonduru kirankonduru Jan 28, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This function checks for the default value and for the new implementation we need the function to return true even for strings of zero length right?

The test fails here in line 170 of schema_utils_test.py if we don't provide a default.
assert set(ui_config.keys()) <= set(reference_dict.keys())

The is_nonempty method returns a null value and give an assertion error.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, the function should return True if it's nonempty and False otherwise. We need to preserve the behaviour in order to ensure that user-submitted values are valid.

I don't think the line you're pointing to is the right one -- that just checks the keys, and doesn't apply any validators. Is it schema_utils.normalize_against_schema() that's the issue here? As part of the function, validation is applied.

If so, my question is: what is the code that's responsible for passing the image UI config to be validated here? Can you post the full traceback of the error?

schema_utils.py Outdated
@@ -236,8 +236,10 @@ def has_length_at_most(obj, max_value):

@staticmethod
def is_nonempty(obj):
"""Returns True iff the given object (a string) is nonempty."""
return bool(obj)
"""Always returns True so that an input field can be made required
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, the function should return True if it's nonempty and False otherwise. We need to preserve the behaviour in order to ensure that user-submitted values are valid.

I don't think the line you're pointing to is the right one -- that just checks the keys, and doesn't apply any validators. Is it schema_utils.normalize_against_schema() that's the issue here? As part of the function, validation is applied.

If so, my question is: what is the code that's responsible for passing the image UI config to be validated here? Can you post the full traceback of the error?

@kirankonduru
Copy link
Contributor Author

This is the traceback

Traceback (most recent call last):
File "/home/kiran/Desktop/oppia/oppia/core/domain/rte_component_registry_test.py", line 171, in test_default_rte_components_are_valid
component._customization_arg_specs) # pylint: disable=protected-access
File "/home/kiran/Desktop/oppia/oppia/core/domain/rte_component_registry_test.py", line 66, in _validate_customization_arg_specs
ca_spec['default_value'], ca_spec['schema']))
File "/home/kiran/Desktop/oppia/oppia/schema_utils.py", line 145, in normalize_against_schema
validator['id'], kwargs, normalized_obj))
AssertionError: Validation failed: is_nonempty ({}) for object

Sorry the mistake was on my part. In a hurry I modified the wrong part and hence was getting the wrong traceback.
Here this _Validators.get(validator['id'])(normalized_obj, **kwargs) becomes null when we have empty value and hence the assertion error.
I guess we need to add some condition which will accept the is_nonempty as an empty string in this test. What do you suggest?

@seanlip
Copy link
Member

seanlip commented Jan 28, 2017

OK, here's what to do:

  • Give normalize_against_schema an additional arg apply_custom_validators which defaults to True. If the arg value is True, apply the block of code at the end that checks validation (the one starting with "Validate the normalized object"), otherwise don't do those checks.
  • In rte_component_registry_test.py, pass in False for this additional arg, and add a comment explaining that this is because the Image component has a required field whose default value is non-empty.

Note that this preserves the conceptual clarity, which is important when writing code. Things need to do what they say on the tin, otherwise developers will have trouble figuring out what's going on and modifying existing code safely.

@anthkris -- to confirm, we do want the default value for that text box to be empty, right? (I personally don't know the best thing to do here from a UI/UX perspective, so I'd defer to you here.)

@anthkris
Copy link
Contributor

@seanlip It would be ideal to have an exemplar there, as we do in the publish modal. However, there may be some difficulty in creating something universal enough that everyone looking at it could understand. So, in this case, empty string may be the best case scenario

Copy link
Member

@seanlip seanlip left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, @kirankonduru! Couple of things, and I also had a question for @anthkris.

schema_utils.py Outdated
@@ -237,7 +239,7 @@ def has_length_at_most(obj, max_value):
@staticmethod
def is_nonempty(obj):
"""Returns True iff the given object (a string) is nonempty."""
return bool(obj)
return obj
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please return this to how it was before you touched it.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

}],
'ui_config': {
'placeholder': (
'Description of Image (Example : "George Frideric, '
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@anthkris could you please check if you're happy with this (punctuation, spacing, capitalization, etc.)?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@kirankonduru I really like what you did with the descriptive text! I think that makes the example very clear. The only nit I would mention is that the composer's name is George Frideric Handel, so just add Handel to the end of his name.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@kirankonduru Been mulling it over all day, and I'd also recommend removing the quotation marks from around the example. They may give the wrong impression that the description should be in quotes.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@anthkris I removed the Handel from George Frideric Handel because that was overflowing the input field

screenshot from 2017-02-01 17 53 35

Can we remove Description of image and just keep this part
Example : George Frideric Handel, 18th century baroque composer

schema_utils_test.validate_schema(ca_spec['schema'])
self.assertEqual(
ca_spec['default_value'],
schema_utils.normalize_against_schema(
ca_spec['default_value'], ca_spec['schema']))
ca_spec['default_value'], ca_spec['schema'], False))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When passing in args that have default values, include the name of the arg. I.e. apply_custom_validators=False rather than just False.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

@@ -17,6 +17,7 @@
from extensions.rich_text_components import base



Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Drop extra newline. Classes should have exactly 2 blank lines above them.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

@@ -59,11 +59,14 @@ def _validate_customization_arg_specs(self, customization_arg_specs):
self.assertTrue(isinstance(ca_spec['description'], basestring))
self.assertGreater(len(ca_spec['description']), 0)

#Image component has a required field whose default value is
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add space after each '#'.

Also, I think the docstring is slightly incorrect (the default value is empty, not non-empty). Here's a rewrite to try and convey its meaning better:

The default value might not pass validation checks (e.g. the Image component has a required field whose default value is empty). Thus, when checking the default value schema, we don't apply the custom validators.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

@seanlip
Copy link
Member

seanlip commented Jan 30, 2017

Code LGTM, apart from the question I had for @anthkris. Thanks @kirankonduru!

@anthkris
Copy link
Contributor

anthkris commented Feb 1, 2017 via email

@anthkris
Copy link
Contributor

anthkris commented Feb 1, 2017

@kirankonduru @seanlip LGTM!

Copy link
Member

@seanlip seanlip left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@seanlip seanlip merged commit 9b28f46 into oppia:develop Feb 1, 2017
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants