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

Added BulkEditChange.edited_value #35806

Merged
merged 3 commits into from
Feb 20, 2025
Merged

Conversation

orangejenny
Copy link
Contributor

Technical Summary

Cherry-pick from the data cleaning backend branch.

Adds a BulkEditChange.edited_value to do the basic string transformations.

This does not handle the RESET action, that'll come later.

Safety Assurance

Safety story

Adds new code and tests, but nothing calls the new code.

Automated test coverage

In PR.

QA Plan

No.

Rollback instructions

  • This PR can be reverted after deploy with no further considerations

Labels & Review

  • Risk label is set correctly
  • The set of people pinged as reviewers is appropriate for the level of risk of the change

@orangejenny orangejenny added the product/invisible Change has no end-user visible impact label Feb 19, 2025

def edited_value(self, case):
simple_transformations = {
EditActionType.REPLACE: lambda x: self.replace_string,
Copy link
Contributor Author

Choose a reason for hiding this comment

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

@biyeun The spec says replace_all_string will be used with the REPLACE action, but it doesn't describe replace_string at all. What's the intended difference between replace_string and replace_all_string?

Copy link
Member

Choose a reason for hiding this comment

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

self.replace_all_string is what should be used here

Copy link
Contributor Author

@orangejenny orangejenny Feb 19, 2025

Choose a reason for hiding this comment

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

What's the purpose of replace_string?

Copy link
Member

Choose a reason for hiding this comment

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

self.replace_string is used in find and replace. I can combine the two if you feel bothered by it, but the two will be separate fields in the form

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes, I think they should be combined.

return old_value.replace(self.find_string, self.replace_string)

if self.action_type == EditActionType.RESET:
raise UnsupportedActionException(f"{EditActionType.RESET} is not applied by calling edited_value")
Copy link
Member

Choose a reason for hiding this comment

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

this should just return the value of the current case property value. I think this should be implemented.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

The intention is to reset all previous edits? Based on the spec, I thought it only reset the previous action.

Copy link
Member

Choose a reason for hiding this comment

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

no reset all edits, not the previous action. It's the equivalent of hitting the "x" in the column or "Undo all edits" in the menu for the action
Screenshot 2025-02-19 at 10 29 15 PM

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Then why would it go back to the original value of the case property, when it may have had additional edits?

I'm not addressing reset right now.

def edited_value(self, case):
simple_transformations = {
EditActionType.REPLACE: lambda x: self.replace_string,
EditActionType.FIND_REPLACE: lambda x: x.replace(self.find_string, self.replace_string),
Copy link
Member

@biyeun biyeun Feb 19, 2025

Choose a reason for hiding this comment

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

does this support regular expressions?
this was what I had to do to support a regex find and replace in the prototype

new_value = re.sub(
re.compile(find_string),
replace_string,
value
)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Copy link
Member

Choose a reason for hiding this comment

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

self.use_regex will note whether the self.find_string is a regular expression

Copy link
Member

Choose a reason for hiding this comment

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

@orangejenny regexes were out of scope for filters, but not find/replace

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I'll add it in a future PR. The purpose of this PR is to get enough functionality that backend tests can run.

Comment on lines 279 to 281
if self.action_type == EditActionType.COPY_REPLACE:
old_value = case.get_case_property(self.copy_from_property)
return old_value.replace(self.find_string, self.replace_string)
Copy link
Member

Choose a reason for hiding this comment

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

this is not the intention of COPY_REPLACE. This should return the value of case.get_case_property(self.copy_from_property) WITHOUT any find and replace action. It's just replacing the value of the current property with the value from the copy_from_property

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Comment on lines +44 to +51
def test_find_replace(self):
change = BulkEditChange(
property='favorite_beach',
action_type=EditActionType.FIND_REPLACE,
find_string='Playa',
replace_string='Punta',
)
self.assertEqual(change.edited_value(self.case), 'Punta Bastimento')
Copy link
Member

Choose a reason for hiding this comment

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

should also add a test for regular expressions

Copy link
Member

Choose a reason for hiding this comment

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

(a different test where use_regex=True)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Not implementing regular expressions in this PR.

'nearest_ocean': 'atlantic',
'town': 'Isabel Segunda',
'favorite_beach': 'Playa Bastimento',
'art': ' :) ',
Copy link
Member

Choose a reason for hiding this comment

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

there are many types of whitespaces. we should be sure to include \t and \n, as those are often the problematic ones (esp \n)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

change = BulkEditChange(
property='town',
action_type=EditActionType.REPLACE,
replace_string='Esperanza',
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
replace_string='Esperanza',
replace_all_string='Esperanza',

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Ignoring since these fields are being combined.

Comment on lines 65 to 66
find_string='Bastimento',
replace_string='Yallis',
Copy link
Member

Choose a reason for hiding this comment

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

not the intention. these values will never be present. the result should be the value of favorite_beach

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Resolved in b5dfc1f

Comment on lines +70 to +75
def test_title_case(self):
change = BulkEditChange(
property='nearest_ocean',
action_type=EditActionType.TITLE_CASE,
)
self.assertEqual(change.edited_value(self.case), 'Atlantic')
Copy link
Member

Choose a reason for hiding this comment

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

what about strings with multiple words and whitespace characters in between?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

It's directly calling a built-in python method. I'm not concerned about exhaustively testing that.

@@ -0,0 +1,103 @@
from django.test import TestCase
Copy link
Member

Choose a reason for hiding this comment

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

could this file be renamed to test_changes.py?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Why? It's testing model behavior.

Copy link
Member

Choose a reason for hiding this comment

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

i'm going to have other model tests in different files

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Definitely feel free to rename as the code evolves.

@@ -257,3 +259,28 @@ class BulkEditChange(models.Model):

class Meta:
ordering = ["created_on"]

def edited_value(self, case):
Copy link
Member

Choose a reason for hiding this comment

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

nit: I always prefer a verb for methods, especially if they return something. maybe get_edited_value?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I'm not a fan of prefixing methods with get_.

Copy link
Member

Choose a reason for hiding this comment

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

i think it's clearer because otherwise, someone could misread it as edit_value rather than retrieving the edited value

@orangejenny orangejenny merged commit 3b3405e into master Feb 20, 2025
14 checks passed
@orangejenny orangejenny deleted the jls/data-cleaning-edited-value branch February 20, 2025 12:38
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
product/invisible Change has no end-user visible impact
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants