Repository: oppia/oppia

Issue:
Fix `last_updated` attribute's Optional[datetime] type for all suggestion classes.
<!--
  - Thanks for taking the time to report a bug in the Oppia project.
  - Before filing a new issue, please do a quick search to check that it hasn't
  - already been filed on the [issue tracker](https://github.com/oppia/oppia/issues)._
  -->

**Describe the issue**
Currently, In all suggestion classes, we are providing None as a default value for `last_updated`, which causes an unusual typing pattern that does not match with the expected behavior ( ideally last_updated should not be None ).
https://github.com/oppia/oppia/blob/8e710ba0e6179e0cf5debc84c808a5cb40677a77/core/domain/suggestion_registry.py#L406
https://github.com/oppia/oppia/blob/8e710ba0e6179e0cf5debc84c808a5cb40677a77/core/domain/suggestion_registry.py#L627
https://github.com/oppia/oppia/blob/8e710ba0e6179e0cf5debc84c808a5cb40677a77/core/domain/suggestion_registry.py#L849

The expected cause for why `last_updated` has a None default value is that while creating suggestions we are never providing `last_updated`'s value and always using None value ( because it's a default value ).
https://github.com/oppia/oppia/blob/8e710ba0e6179e0cf5debc84c808a5cb40677a77/core/domain/suggestion_services.py#L185
https://github.com/oppia/oppia/blob/8e710ba0e6179e0cf5debc84c808a5cb40677a77/core/domain/suggestion_services.py#L207
https://github.com/oppia/oppia/blob/8e710ba0e6179e0cf5debc84c808a5cb40677a77/core/domain/suggestion_services.py#L229

**Expected behavior**
last_updated should only be of type datetime, not Optional[datetime]

**Additional context**
A small discussion regarding this issue can be found [HERE](https://github.com/oppia/oppia/pull/15905#discussion_r963233691)

**Steps for new contributors**
1. Remove the `last_updated` field from the `SuggestionEditStateContent`, `SuggestionTranslateContent`, and `SuggestionAddQuestion` classes defined in core/domain/suggestion_registry.py, e.g. [here](https://github.com/oppia/oppia/blob/8e710ba0e6179e0cf5debc84c808a5cb40677a77/core/domain/suggestion_registry.py#L627). This field is not actually used in the domain object and should be removed.
2. Update all the instantiators of the above classes to stop passing in the `last_updated` field, including tests, e.g. in core/domain/suggestion_registry_test.py.
Fix `last_updated` attribute's Optional[datetime] type for all suggestion classes.
<!--
  - Thanks for taking the time to report a bug in the Oppia project.
  - Before filing a new issue, please do a quick search to check that it hasn't
  - already been filed on the [issue tracker](https://github.com/oppia/oppia/issues)._
  -->

**Describe the issue**
Currently, In all suggestion classes, we are providing None as a default value for `last_updated`, which causes an unusual typing pattern that does not match with the expected behavior ( ideally last_updated should not be None ).
https://github.com/oppia/oppia/blob/8e710ba0e6179e0cf5debc84c808a5cb40677a77/core/domain/suggestion_registry.py#L406
https://github.com/oppia/oppia/blob/8e710ba0e6179e0cf5debc84c808a5cb40677a77/core/domain/suggestion_registry.py#L627
https://github.com/oppia/oppia/blob/8e710ba0e6179e0cf5debc84c808a5cb40677a77/core/domain/suggestion_registry.py#L849

The expected cause for why `last_updated` has a None default value is that while creating suggestions we are never providing `last_updated`'s value and always using None value ( because it's a default value ).
https://github.com/oppia/oppia/blob/8e710ba0e6179e0cf5debc84c808a5cb40677a77/core/domain/suggestion_services.py#L185
https://github.com/oppia/oppia/blob/8e710ba0e6179e0cf5debc84c808a5cb40677a77/core/domain/suggestion_services.py#L207
https://github.com/oppia/oppia/blob/8e710ba0e6179e0cf5debc84c808a5cb40677a77/core/domain/suggestion_services.py#L229

**Expected behavior**
last_updated should only be of type datetime, not Optional[datetime]

**Additional context**
A small discussion regarding this issue can be found [HERE](https://github.com/oppia/oppia/pull/15905#discussion_r963233691)

**Steps for new contributors**
1. Remove the `last_updated` field from the `SuggestionEditStateContent`, `SuggestionTranslateContent`, and `SuggestionAddQuestion` classes defined in core/domain/suggestion_registry.py, e.g. [here](https://github.com/oppia/oppia/blob/8e710ba0e6179e0cf5debc84c808a5cb40677a77/core/domain/suggestion_registry.py#L627). This field is not actually used in the domain object and should be removed.
2. Update all the instantiators of the above classes to stop passing in the `last_updated` field, including tests, e.g. in core/domain/suggestion_registry_test.py.


Discussion:
Shouldn't this be a CD team issue? Also this issue arises when adding a created_on for suggestion classes.

Cc/ @sagangwee @seanlip 
@chris7716 Yup I think you're right, I moved it to CD. Thanks!
Hi @sagangwee, thanks for proposing this as a good first issue. I am removing the label for now and looping in @DubeySandeep to approve the label. It will be added back if approved. Thanks!
@seanlip   @Broppia    @sagangwee   @DubeySandeep 
I would like to work on this issue. 

As stated in the issue I would do the same. 
I would remove the `last_updated` from the classes and also update the instantiator methods as well as test files accordingly.

Also besides this shouldn't we do the similar thing for `created_on`.

@amaanlari Re created_on -- is that used in the domain layer and above by anything (e.g. is it passed to the frontend)? If so then you can't really drop it from the domain layer I think.

Can you show one code example of each of the points mentioned in the original issue, and show a video testing the flow so that we can verify that you know how to test your changes from an end-user perspective? Thanks!
@seanlip 
Sorry for late reply.

Here's the changes to be made [click here](https://github.com/amaanlari/oppia/commit/025a3e8086ab5cc2a26896749e0f3338af85f895)

I wasn't able to find out if the created_on is used in the frontend layer.
@amaanlari Thanks for the reply. I think you'll need to address the question of created_on (try tracing the calls from backend to frontend and vice versa, search for usages of created_on and createdOn in the codebase) before we can make progress here. This page might be helpful, too: https://github.com/oppia/oppia/wiki/Find-the-right-code-to-change

If you find this tricky, it's fine to drop this and pick up a different issue instead.
Shouldn't this be a CD team issue? Also this issue arises when adding a created_on for suggestion classes.

Cc/ @sagangwee @seanlip 
@chris7716 Yup I think you're right, I moved it to CD. Thanks!
Hi @sagangwee, thanks for proposing this as a good first issue. I am removing the label for now and looping in @DubeySandeep to approve the label. It will be added back if approved. Thanks!
@seanlip   @Broppia    @sagangwee   @DubeySandeep 
I would like to work on this issue. 

As stated in the issue I would do the same. 
I would remove the `last_updated` from the classes and also update the instantiator methods as well as test files accordingly.

Also besides this shouldn't we do the similar thing for `created_on`.

@amaanlari Re created_on -- is that used in the domain layer and above by anything (e.g. is it passed to the frontend)? If so then you can't really drop it from the domain layer I think.

Can you show one code example of each of the points mentioned in the original issue, and show a video testing the flow so that we can verify that you know how to test your changes from an end-user perspective? Thanks!
@seanlip 
Sorry for late reply.

Here's the changes to be made [click here](https://github.com/amaanlari/oppia/commit/025a3e8086ab5cc2a26896749e0f3338af85f895)

I wasn't able to find out if the created_on is used in the frontend layer.
@amaanlari Thanks for the reply. I think you'll need to address the question of created_on (try tracing the calls from backend to frontend and vice versa, search for usages of created_on and createdOn in the codebase) before we can make progress here. This page might be helpful, too: https://github.com/oppia/oppia/wiki/Find-the-right-code-to-change

If you find this tricky, it's fine to drop this and pick up a different issue instead.

Resolving patch:
diff --git a/core/domain/suggestion_registry.py b/core/domain/suggestion_registry.py
--- a/core/domain/suggestion_registry.py
+++ b/core/domain/suggestion_registry.py
@@ -407,8 +407,8 @@ def __init__(
         score_category: str,
         language_code: Optional[str],
         edited_by_reviewer: bool,
-        last_updated: Optional[datetime.datetime] = None,
-        created_on: Optional[datetime.datetime] = None
+        last_updated: datetime.datetime,
+        created_on: datetime.datetime
     ) -> None:
         """Initializes an object of type SuggestionEditStateContent
         corresponding to the SUGGESTION_TYPE_EDIT_STATE_CONTENT choice.
@@ -432,22 +432,8 @@ def __init__(
         # well. So, due to this conflict in types MyPy throws an `Incompatible
         # types in assignment` error. Thus to avoid the error, we used ignore.
         self.language_code = language_code  # type: ignore[assignment]
-        # TODO(#16048): Here we use MyPy ignore because in BaseSuggestion,
-        # last_updated is defined with only datetime type but here
-        # last_updated is of Optional[datetime] type because while creating
-        # 'SuggestionEditStateContent' through create_suggestion() method, we
-        # are not providing 'last_updated' and just using None default value.
-        # So, once this suggestion_services.create_suggestion() method is
-        # fixed, we can remove both todo and MyPy ignore from here.
-        self.last_updated = last_updated  # type: ignore[assignment]
-        # TODO(#16048): Here we use MyPy ignore because in BaseSuggestion,
-        # created_on is defined with only datetime type but here
-        # created_on is of Optional[datetime] type because while creating
-        # 'SuggestionEditStateContent' through create_suggestion() method, we
-        # are not providing 'created_on' and just using None default value.
-        # So, once this suggestion_services.create_suggestion() method is
-        # fixed, we can remove both todo and MyPy ignore from here.
-        self.created_on = created_on  # type: ignore[assignment]
+        self.last_updated = last_updated
+        self.created_on = created_on
         self.edited_by_reviewer = edited_by_reviewer
         # Here we use MyPy ignore because in BaseSuggestion, image_context
         # is defined as string type attribute but currently, we don't
@@ -643,8 +629,8 @@ def __init__(
         score_category: str,
         language_code: str,
         edited_by_reviewer: bool,
-        last_updated: Optional[datetime.datetime] = None,
-        created_on: Optional[datetime.datetime] = None
+        last_updated: datetime.datetime,
+        created_on: datetime.datetime
     ) -> None:
         """Initializes an object of type SuggestionTranslateContent
         corresponding to the SUGGESTION_TYPE_TRANSLATE_CONTENT choice.
@@ -663,22 +649,8 @@ def __init__(
         )
         self.score_category = score_category
         self.language_code = language_code
-        # TODO(#16048): Here we use MyPy ignore because in BaseSuggestion,
-        # last_updated is defined with only datetime type but here
-        # last_updated is of Optional[datetime] type because while creating
-        # 'SuggestionTranslateContent' through create_suggestion() method, we
-        # are not providing 'last_updated' and just using None default value.
-        # So, once this suggestion_services.create_suggestion() method is
-        # fixed, we can remove both todo and MyPy ignore from here.
-        self.last_updated = last_updated  # type: ignore[assignment]
-        # TODO(#16048): Here we use MyPy ignore because in BaseSuggestion,
-        # created_on is defined with only datetime type but here
-        # created_on is of Optional[datetime] type because while creating
-        # 'SuggestionTranslateContent' through create_suggestion() method, we
-        # are not providing 'created_on' and just using None default value.
-        # So, once this suggestion_services.create_suggestion() method is
-        # fixed, we can remove both todo and MyPy ignore from here.
-        self.created_on = created_on  # type: ignore[assignment]
+        self.last_updated = last_updated
+        self.created_on = created_on
         self.edited_by_reviewer = edited_by_reviewer
         self.image_context = feconf.IMAGE_CONTEXT_EXPLORATION_SUGGESTIONS
 
@@ -895,8 +867,8 @@ def __init__(
         score_category: str,
         language_code: str,
         edited_by_reviewer: bool,
-        last_updated: Optional[datetime.datetime] = None,
-        created_on: Optional[datetime.datetime] = None
+        last_updated: datetime.datetime,
+        created_on: datetime.datetime
     ) -> None:
         """Initializes an object of type SuggestionAddQuestion
         corresponding to the SUGGESTION_TYPE_ADD_QUESTION choice.
@@ -915,22 +887,8 @@ def __init__(
         )
         self.score_category = score_category
         self.language_code = language_code
-        # TODO(#16048): Here we use MyPy ignore because in BaseSuggestion,
-        # last_updated is defined with only datetime type but here
-        # last_updated is of Optional[datetime] type because while creating
-        # 'SuggestionAddQuestion' through create_suggestion() method, we
-        # are not providing 'last_updated' and just using None default value.
-        # So, once this suggestion_services.create_suggestion() method is
-        # fixed, we can remove both todo and MyPy ignore from here.
-        self.last_updated = last_updated  # type: ignore[assignment]
-        # TODO(#16048): Here we use MyPy ignore because in BaseSuggestion,
-        # created_on is defined with only datetime type but here
-        # created_on is of Optional[datetime] type because while creating
-        # 'SuggestionAddQuestion' through create_suggestion() method, we
-        # are not providing 'created_on' and just using None default value.
-        # So, once this suggestion_services.create_suggestion() method is
-        # fixed, we can remove both todo and MyPy ignore from here.
-        self.created_on = created_on  # type: ignore[assignment]
+        self.last_updated = last_updated
+        self.created_on = created_on
         self.image_context = feconf.IMAGE_CONTEXT_QUESTION_SUGGESTIONS
         self._update_change_to_latest_state_schema_version()
         self.edited_by_reviewer = edited_by_reviewer
diff --git a/core/domain/suggestion_services.py b/core/domain/suggestion_services.py
--- a/core/domain/suggestion_services.py
+++ b/core/domain/suggestion_services.py
@@ -199,7 +199,8 @@ def create_suggestion(
             suggestion_registry.SuggestionEditStateContent(
                 thread_id, target_id, target_version_at_submission, status,
                 author_id, None, change_cmd, score_category, language_code,
-                False
+                False, datetime.datetime.utcnow(),
+                datetime.datetime.utcnow()
             )
         )
     elif suggestion_type == feconf.SUGGESTION_TYPE_TRANSLATE_CONTENT:
@@ -221,8 +222,8 @@ def create_suggestion(
                 'was submitted.')
         suggestion = suggestion_registry.SuggestionTranslateContent(
             thread_id, target_id, target_version_at_submission, status,
-            author_id, None, change_cmd, score_category, language_code, False
-        )
+            author_id, None, change_cmd, score_category, language_code, False,
+            datetime.datetime.utcnow(), datetime.datetime.utcnow())
     elif suggestion_type == feconf.SUGGESTION_TYPE_ADD_QUESTION:
         score_category = (
             suggestion_models.SCORE_TYPE_QUESTION +
@@ -245,7 +246,8 @@ def create_suggestion(
         suggestion = suggestion_registry.SuggestionAddQuestion(
             thread_id, target_id, target_version_at_submission, status,
             author_id, None, change_cmd, score_category,
-            add_question_language_code, False
+            add_question_language_code, False,
+            datetime.datetime.utcnow(), datetime.datetime.utcnow()
         )
     else:
         raise Exception('Invalid suggestion type %s' % suggestion_type)


Test patch:
diff --git a/core/controllers/feedback_updates_test.py b/core/controllers/feedback_updates_test.py
--- a/core/controllers/feedback_updates_test.py
+++ b/core/controllers/feedback_updates_test.py
@@ -257,7 +257,8 @@ def test_raises_error_if_wrong_type_of_suggestion_provided(self) -> None:
             'exploration.exp1.thread1', 'exp1',
             1, suggestion_models.STATUS_ACCEPTED, 'author',
             'review_id', change_dict, 'translation.Algebra',
-            'en', False, datetime.datetime(2016, 4, 10, 0, 0, 0, 0)
+            'en', False, datetime.datetime(2016, 4, 10, 0, 0, 0, 0),
+            datetime.datetime(2016, 4, 10, 0, 0, 0, 0)
         )
 
         response_dict = self.get_json(
diff --git a/core/domain/suggestion_registry_test.py b/core/domain/suggestion_registry_test.py
--- a/core/domain/suggestion_registry_test.py
+++ b/core/domain/suggestion_registry_test.py
@@ -211,7 +211,8 @@ def test_validate_suggestion_edit_state_content(self) -> None:
             expected_suggestion_dict['status'], self.author_id,
             self.reviewer_id, expected_suggestion_dict['change_cmd'],
             expected_suggestion_dict['score_category'],
-            expected_suggestion_dict['language_code'], False, self.fake_date)
+            expected_suggestion_dict['language_code'], False, self.fake_date,
+            self.fake_date)
 
         suggestion.validate()
 
@@ -225,7 +226,8 @@ def test_get_score_part_helper_methods(self) -> None:
             expected_suggestion_dict['status'], self.author_id,
             self.reviewer_id, expected_suggestion_dict['change_cmd'],
             expected_suggestion_dict['score_category'],
-            expected_suggestion_dict['language_code'], False, self.fake_date)
+            expected_suggestion_dict['language_code'], False, self.fake_date,
+            self.fake_date)
 
         self.assertEqual(suggestion.get_score_type(), 'content')
         self.assertEqual(suggestion.get_score_sub_type(), 'Algebra')
@@ -239,7 +241,8 @@ def test_validate_suggestion_type(self) -> None:
             expected_suggestion_dict['status'], self.author_id,
             self.reviewer_id, expected_suggestion_dict['change_cmd'],
             expected_suggestion_dict['score_category'],
-            expected_suggestion_dict['language_code'], False, self.fake_date)
+            expected_suggestion_dict['language_code'], False, self.fake_date,
+            self.fake_date)
 
         suggestion.validate()
 
@@ -259,7 +262,8 @@ def test_validate_target_type(self) -> None:
             expected_suggestion_dict['status'], self.author_id,
             self.reviewer_id, expected_suggestion_dict['change_cmd'],
             expected_suggestion_dict['score_category'],
-            expected_suggestion_dict['language_code'], False, self.fake_date)
+            expected_suggestion_dict['language_code'], False, self.fake_date,
+            self.fake_date)
 
         suggestion.validate()
 
@@ -279,7 +283,8 @@ def test_validate_target_id(self) -> None:
             expected_suggestion_dict['status'], self.author_id,
             self.reviewer_id, expected_suggestion_dict['change_cmd'],
             expected_suggestion_dict['score_category'],
-            expected_suggestion_dict['language_code'], False, self.fake_date)
+            expected_suggestion_dict['language_code'], False, self.fake_date,
+            self.fake_date)
 
         suggestion.validate()
 
@@ -301,7 +306,8 @@ def test_validate_target_version_at_submission(self) -> None:
             expected_suggestion_dict['status'], self.author_id,
             self.reviewer_id, expected_suggestion_dict['change_cmd'],
             expected_suggestion_dict['score_category'],
-            expected_suggestion_dict['language_code'], False, self.fake_date)
+            expected_suggestion_dict['language_code'], False, self.fake_date,
+            self.fake_date)
 
         suggestion.validate()
 
@@ -324,7 +330,8 @@ def test_validate_status(self) -> None:
             expected_suggestion_dict['status'], self.author_id,
             self.reviewer_id, expected_suggestion_dict['change_cmd'],
             expected_suggestion_dict['score_category'],
-            expected_suggestion_dict['language_code'], False, self.fake_date)
+            expected_suggestion_dict['language_code'], False, self.fake_date,
+            self.fake_date)
 
         suggestion.validate()
 
@@ -343,7 +350,8 @@ def test_validate_author_id(self) -> None:
             expected_suggestion_dict['status'], self.author_id,
             self.reviewer_id, expected_suggestion_dict['change_cmd'],
             expected_suggestion_dict['score_category'],
-            expected_suggestion_dict['language_code'], False, self.fake_date)
+            expected_suggestion_dict['language_code'], False, self.fake_date,
+            self.fake_date)
 
         suggestion.validate()
 
@@ -365,7 +373,8 @@ def test_validate_author_id_format(self) -> None:
             expected_suggestion_dict['status'], self.author_id,
             self.reviewer_id, expected_suggestion_dict['change_cmd'],
             expected_suggestion_dict['score_category'],
-            expected_suggestion_dict['language_code'], False, self.fake_date)
+            expected_suggestion_dict['language_code'], False, self.fake_date,
+            self.fake_date)
 
         suggestion.validate()
 
@@ -388,7 +397,8 @@ def test_validate_final_reviewer_id(self) -> None:
             expected_suggestion_dict['status'], self.author_id,
             self.reviewer_id, expected_suggestion_dict['change_cmd'],
             expected_suggestion_dict['score_category'],
-            expected_suggestion_dict['language_code'], False, self.fake_date)
+            expected_suggestion_dict['language_code'], False, self.fake_date,
+            self.fake_date)
 
         suggestion.validate()
 
@@ -410,7 +420,8 @@ def test_validate_final_reviewer_id_format(self) -> None:
             expected_suggestion_dict['status'], self.author_id,
             self.reviewer_id, expected_suggestion_dict['change_cmd'],
             expected_suggestion_dict['score_category'],
-            expected_suggestion_dict['language_code'], False, self.fake_date)
+            expected_suggestion_dict['language_code'], False, self.fake_date,
+            self.fake_date)
 
         suggestion.validate()
 
@@ -433,7 +444,8 @@ def test_validate_score_category(self) -> None:
             expected_suggestion_dict['status'], self.author_id,
             self.reviewer_id, expected_suggestion_dict['change_cmd'],
             expected_suggestion_dict['score_category'],
-            expected_suggestion_dict['language_code'], False, self.fake_date)
+            expected_suggestion_dict['language_code'], False, self.fake_date,
+            self.fake_date)
 
         suggestion.validate()
 
@@ -455,7 +467,8 @@ def test_validate_score_category_format(self) -> None:
             expected_suggestion_dict['status'], self.author_id,
             self.reviewer_id, expected_suggestion_dict['change_cmd'],
             expected_suggestion_dict['score_category'],
-            expected_suggestion_dict['language_code'], False, self.fake_date)
+            expected_suggestion_dict['language_code'], False, self.fake_date,
+            self.fake_date)
 
         suggestion.validate()
 
@@ -484,7 +497,8 @@ def test_validate_score_type(self) -> None:
             expected_suggestion_dict['status'], self.author_id,
             self.reviewer_id, expected_suggestion_dict['change_cmd'],
             expected_suggestion_dict['score_category'],
-            expected_suggestion_dict['language_code'], False, self.fake_date)
+            expected_suggestion_dict['language_code'], False, self.fake_date,
+            self.fake_date)
 
         suggestion.validate()
 
@@ -505,7 +519,8 @@ def test_validate_change(self) -> None:
             expected_suggestion_dict['status'], self.author_id,
             self.reviewer_id, expected_suggestion_dict['change_cmd'],
             expected_suggestion_dict['score_category'],
-            expected_suggestion_dict['language_code'], False, self.fake_date)
+            expected_suggestion_dict['language_code'], False, self.fake_date,
+            self.fake_date)
 
         suggestion.validate()
 
@@ -528,7 +543,8 @@ def test_validate_score_type_content(self) -> None:
             expected_suggestion_dict['status'], self.author_id,
             self.reviewer_id, expected_suggestion_dict['change_cmd'],
             expected_suggestion_dict['score_category'],
-            expected_suggestion_dict['language_code'], False, self.fake_date)
+            expected_suggestion_dict['language_code'], False, self.fake_date,
+            self.fake_date)
 
         suggestion.validate()
 
@@ -548,7 +564,8 @@ def test_validate_change_cmd(self) -> None:
             expected_suggestion_dict['status'], self.author_id,
             self.reviewer_id, expected_suggestion_dict['change_cmd'],
             expected_suggestion_dict['score_category'],
-            expected_suggestion_dict['language_code'], False, self.fake_date)
+            expected_suggestion_dict['language_code'], False, self.fake_date,
+            self.fake_date)
         suggestion.validate()
 
         suggestion.change_cmd.cmd = 'invalid_cmd'
@@ -566,7 +583,8 @@ def test_validate_change_property_name(self) -> None:
             expected_suggestion_dict['status'], self.author_id,
             self.reviewer_id, expected_suggestion_dict['change_cmd'],
             expected_suggestion_dict['score_category'],
-            expected_suggestion_dict['language_code'], False, self.fake_date)
+            expected_suggestion_dict['language_code'], False, self.fake_date,
+            self.fake_date)
 
         suggestion.validate()
 
@@ -591,7 +609,8 @@ def test_validate_language_code_fails_when_language_codes_do_not_match(
             expected_suggestion_dict['status'], self.author_id,
             self.reviewer_id, expected_suggestion_dict['change_cmd'],
             expected_suggestion_dict['score_category'],
-            expected_suggestion_dict['language_code'], False, self.fake_date)
+            expected_suggestion_dict['language_code'], False, self.fake_date,
+            self.fake_date)
         suggestion.validate()
 
         suggestion.language_code = 'wrong_language_code'
@@ -612,7 +631,8 @@ def test_pre_accept_validate_state_name(self) -> None:
             expected_suggestion_dict['status'], self.author_id,
             self.reviewer_id, expected_suggestion_dict['change_cmd'],
             expected_suggestion_dict['score_category'],
-            expected_suggestion_dict['language_code'], False, self.fake_date)
+            expected_suggestion_dict['language_code'], False, self.fake_date,
+            self.fake_date)
 
         suggestion.change_cmd.state_name = 'Introduction'
 
@@ -635,7 +655,8 @@ def test_populate_old_value_of_change_with_invalid_state(self) -> None:
             expected_suggestion_dict['status'], self.author_id,
             self.reviewer_id, expected_suggestion_dict['change_cmd'],
             expected_suggestion_dict['score_category'],
-            expected_suggestion_dict['language_code'], False, self.fake_date)
+            expected_suggestion_dict['language_code'], False, self.fake_date,
+            self.fake_date)
 
         suggestion.change_cmd.state_name = 'invalid_state_name'
 
@@ -654,7 +675,8 @@ def test_pre_update_validate_change_cmd(self) -> None:
             expected_suggestion_dict['status'], self.author_id,
             self.reviewer_id, expected_suggestion_dict['change_cmd'],
             expected_suggestion_dict['score_category'],
-            expected_suggestion_dict['language_code'], False, self.fake_date)
+            expected_suggestion_dict['language_code'], False, self.fake_date,
+            self.fake_date)
 
         change_cmd = {
             'cmd': exp_domain.CMD_ADD_STATE,
@@ -681,7 +703,8 @@ def test_pre_update_validate_change_property_name(self) -> None:
             expected_suggestion_dict['status'], self.author_id,
             self.reviewer_id, expected_suggestion_dict['change_cmd'],
             expected_suggestion_dict['score_category'],
-            expected_suggestion_dict['language_code'], False, self.fake_date)
+            expected_suggestion_dict['language_code'], False, self.fake_date,
+            self.fake_date)
 
         change_cmd = {
             'cmd': exp_domain.CMD_EDIT_STATE_PROPERTY,
@@ -707,7 +730,8 @@ def test_pre_update_validate_change_state_name(self) -> None:
             expected_suggestion_dict['status'], self.author_id,
             self.reviewer_id, expected_suggestion_dict['change_cmd'],
             expected_suggestion_dict['score_category'],
-            expected_suggestion_dict['language_code'], False, self.fake_date)
+            expected_suggestion_dict['language_code'], False, self.fake_date,
+            self.fake_date)
 
         change_cmd = {
             'cmd': exp_domain.CMD_EDIT_STATE_PROPERTY,
@@ -733,7 +757,8 @@ def test_pre_update_validate_change_new_value(self) -> None:
             expected_suggestion_dict['status'], self.author_id,
             self.reviewer_id, expected_suggestion_dict['change_cmd'],
             expected_suggestion_dict['score_category'],
-            expected_suggestion_dict['language_code'], False, self.fake_date)
+            expected_suggestion_dict['language_code'], False, self.fake_date,
+            self.fake_date)
         new_content = state_domain.SubtitledHtml(
             'content', '<p>new suggestion html</p>').to_dict()
 
@@ -764,7 +789,8 @@ def test_pre_update_validate_non_equal_change_cmd(self) -> None:
             expected_suggestion_dict['status'], self.author_id,
             self.reviewer_id, expected_suggestion_dict['change_cmd'],
             expected_suggestion_dict['score_category'],
-            expected_suggestion_dict['language_code'], False, self.fake_date)
+            expected_suggestion_dict['language_code'], False, self.fake_date,
+            self.fake_date)
 
         with self.assertRaisesRegex(
             utils.ValidationError,
@@ -796,7 +822,8 @@ def test_get_all_html_content_strings(self) -> None:
             self.suggestion_dict['status'], self.author_id,
             self.reviewer_id, change_dict,
             self.suggestion_dict['score_category'],
-            self.suggestion_dict['language_code'], False, self.fake_date)
+            self.suggestion_dict['language_code'], False, self.fake_date,
+            self.fake_date)
 
         actual_outcome_list = suggestion.get_all_html_content_strings()
         expected_outcome_list = [u'new suggestion content']
@@ -832,7 +859,8 @@ def test_convert_html_in_suggestion_change(self) -> None:
             self.suggestion_dict['status'], self.author_id,
             self.reviewer_id, change_cmd,
             self.suggestion_dict['score_category'],
-            self.suggestion_dict['language_code'], False, self.fake_date)
+            self.suggestion_dict['language_code'], False, self.fake_date,
+            self.fake_date)
 
         suggestion.convert_html_in_suggestion_change(
             html_validation_service.
@@ -865,7 +893,8 @@ def test_get_target_entity_html_strings_returns_expected_strings(
             self.suggestion_dict['status'], self.author_id,
             self.reviewer_id, change_dict,
             self.suggestion_dict['score_category'],
-            self.suggestion_dict['language_code'], False, self.fake_date)
+            self.suggestion_dict['language_code'], False, self.fake_date,
+            self.fake_date)
 
         actual_outcome_list = suggestion.get_target_entity_html_strings()
         expected_outcome_list = [u'Old content.']
@@ -889,7 +918,8 @@ def test_get_target_entity_html_with_none_old_value(self) -> None:
             self.suggestion_dict['status'], self.author_id,
             self.reviewer_id, change_dict,
             self.suggestion_dict['score_category'],
-            self.suggestion_dict['language_code'], False, self.fake_date)
+            self.suggestion_dict['language_code'], False, self.fake_date,
+            self.fake_date)
 
         actual_outcome_list = suggestion.get_target_entity_html_strings()
         self.assertEqual(actual_outcome_list, [])
@@ -962,7 +992,7 @@ def test_pre_update_validate_fails_for_invalid_change_cmd(self) -> None:
             self.reviewer_id, expected_suggestion_dict['change_cmd'],
             expected_suggestion_dict['score_category'],
             expected_suggestion_dict['language_code'], False,
-            self.fake_date)
+            self.fake_date, self.fake_date)
 
         change = {
             'cmd': exp_domain.CMD_DELETE_STATE,
@@ -985,7 +1015,7 @@ def test_pre_update_validate_change_state_name(self) -> None:
             self.reviewer_id, expected_suggestion_dict['change_cmd'],
             expected_suggestion_dict['score_category'],
             expected_suggestion_dict['language_code'], False,
-            self.fake_date)
+            self.fake_date, self.fake_date)
         change = {
             'cmd': exp_domain.CMD_ADD_WRITTEN_TRANSLATION,
             'state_name': 'State 1',
@@ -1011,7 +1041,7 @@ def test_pre_update_validate_change_language_code(self) -> None:
             self.reviewer_id, expected_suggestion_dict['change_cmd'],
             expected_suggestion_dict['score_category'],
             expected_suggestion_dict['language_code'], False,
-            self.fake_date)
+            self.fake_date, self.fake_date)
         change = {
             'cmd': exp_domain.CMD_ADD_WRITTEN_TRANSLATION,
             'state_name': 'Introduction',
@@ -1037,7 +1067,7 @@ def test_pre_update_validate_change_content_html(self) -> None:
             self.reviewer_id, expected_suggestion_dict['change_cmd'],
             expected_suggestion_dict['score_category'],
             expected_suggestion_dict['language_code'], False,
-            self.fake_date)
+            self.fake_date, self.fake_date)
         change = {
             'cmd': exp_domain.CMD_ADD_WRITTEN_TRANSLATION,
             'state_name': 'Introduction',
@@ -1082,7 +1112,8 @@ def test_validate_suggestion_add_translation(self) -> None:
             expected_suggestion_dict['status'], self.author_id,
             self.reviewer_id, expected_suggestion_dict['change_cmd'],
             expected_suggestion_dict['score_category'],
-            expected_suggestion_dict['language_code'], False, self.fake_date)
+            expected_suggestion_dict['language_code'], False, self.fake_date,
+            self.fake_date)
 
         suggestion.validate()
 
@@ -1096,7 +1127,8 @@ def test_get_score_part_helper_methods(self) -> None:
             expected_suggestion_dict['status'], self.author_id,
             self.reviewer_id, expected_suggestion_dict['change_cmd'],
             expected_suggestion_dict['score_category'],
-            expected_suggestion_dict['language_code'], False, self.fake_date)
+            expected_suggestion_dict['language_code'], False, self.fake_date,
+            self.fake_date)
 
         self.assertEqual(suggestion.get_score_type(), 'translation')
         self.assertEqual(suggestion.get_score_sub_type(), 'Algebra')
@@ -1110,7 +1142,8 @@ def test_validate_suggestion_type(self) -> None:
             expected_suggestion_dict['status'], self.author_id,
             self.reviewer_id, expected_suggestion_dict['change_cmd'],
             expected_suggestion_dict['score_category'],
-            expected_suggestion_dict['language_code'], False, self.fake_date)
+            expected_suggestion_dict['language_code'], False, self.fake_date,
+            self.fake_date)
 
         suggestion.validate()
 
@@ -1130,7 +1163,8 @@ def test_validate_target_type(self) -> None:
             expected_suggestion_dict['status'], self.author_id,
             self.reviewer_id, expected_suggestion_dict['change_cmd'],
             expected_suggestion_dict['score_category'],
-            expected_suggestion_dict['language_code'], False, self.fake_date)
+            expected_suggestion_dict['language_code'], False, self.fake_date,
+            self.fake_date)
 
         suggestion.validate()
 
@@ -1150,7 +1184,8 @@ def test_validate_target_id(self) -> None:
             expected_suggestion_dict['status'], self.author_id,
             self.reviewer_id, expected_suggestion_dict['change_cmd'],
             expected_suggestion_dict['score_category'],
-            expected_suggestion_dict['language_code'], False, self.fake_date)
+            expected_suggestion_dict['language_code'], False, self.fake_date,
+            self.fake_date)
 
         suggestion.validate()
 
@@ -1172,7 +1207,8 @@ def test_validate_target_version_at_submission(self) -> None:
             expected_suggestion_dict['status'], self.author_id,
             self.reviewer_id, expected_suggestion_dict['change_cmd'],
             expected_suggestion_dict['score_category'],
-            expected_suggestion_dict['language_code'], False, self.fake_date)
+            expected_suggestion_dict['language_code'], False, self.fake_date,
+            self.fake_date)
 
         suggestion.validate()
 
@@ -1195,7 +1231,8 @@ def test_validate_status(self) -> None:
             expected_suggestion_dict['status'], self.author_id,
             self.reviewer_id, expected_suggestion_dict['change_cmd'],
             expected_suggestion_dict['score_category'],
-            expected_suggestion_dict['language_code'], False, self.fake_date)
+            expected_suggestion_dict['language_code'], False, self.fake_date,
+            self.fake_date)
 
         suggestion.validate()
 
@@ -1214,7 +1251,8 @@ def test_validate_author_id(self) -> None:
             expected_suggestion_dict['status'], self.author_id,
             self.reviewer_id, expected_suggestion_dict['change_cmd'],
             expected_suggestion_dict['score_category'],
-            expected_suggestion_dict['language_code'], False, self.fake_date)
+            expected_suggestion_dict['language_code'], False, self.fake_date,
+            self.fake_date)
 
         suggestion.validate()
 
@@ -1236,7 +1274,8 @@ def test_validate_author_id_format(self) -> None:
             expected_suggestion_dict['status'], self.author_id,
             self.reviewer_id, expected_suggestion_dict['change_cmd'],
             expected_suggestion_dict['score_category'],
-            expected_suggestion_dict['language_code'], False, self.fake_date)
+            expected_suggestion_dict['language_code'], False, self.fake_date,
+            self.fake_date)
 
         suggestion.validate()
 
@@ -1256,7 +1295,8 @@ def test_validate_final_reviewer_id(self) -> None:
             expected_suggestion_dict['status'], self.author_id,
             self.reviewer_id, expected_suggestion_dict['change_cmd'],
             expected_suggestion_dict['score_category'],
-            expected_suggestion_dict['language_code'], False, self.fake_date)
+            expected_suggestion_dict['language_code'], False, self.fake_date,
+            self.fake_date)
 
         suggestion.validate()
 
@@ -1278,7 +1318,8 @@ def test_validate_final_reviewer_id_format(self) -> None:
             expected_suggestion_dict['status'], self.author_id,
             self.reviewer_id, expected_suggestion_dict['change_cmd'],
             expected_suggestion_dict['score_category'],
-            expected_suggestion_dict['language_code'], False, self.fake_date)
+            expected_suggestion_dict['language_code'], False, self.fake_date,
+            self.fake_date)
 
         suggestion.validate()
 
@@ -1298,7 +1339,8 @@ def test_validate_score_category(self) -> None:
             expected_suggestion_dict['status'], self.author_id,
             self.reviewer_id, expected_suggestion_dict['change_cmd'],
             expected_suggestion_dict['score_category'],
-            expected_suggestion_dict['language_code'], False, self.fake_date)
+            expected_suggestion_dict['language_code'], False, self.fake_date,
+            self.fake_date)
 
         suggestion.validate()
 
@@ -1320,7 +1362,8 @@ def test_validate_score_category_format(self) -> None:
             expected_suggestion_dict['status'], self.author_id,
             self.reviewer_id, expected_suggestion_dict['change_cmd'],
             expected_suggestion_dict['score_category'],
-            expected_suggestion_dict['language_code'], False, self.fake_date)
+            expected_suggestion_dict['language_code'], False, self.fake_date,
+            self.fake_date)
 
         suggestion.validate()
 
@@ -1349,7 +1392,8 @@ def test_validate_score_type(self) -> None:
             expected_suggestion_dict['status'], self.author_id,
             self.reviewer_id, expected_suggestion_dict['change_cmd'],
             expected_suggestion_dict['score_category'],
-            expected_suggestion_dict['language_code'], False, self.fake_date)
+            expected_suggestion_dict['language_code'], False, self.fake_date,
+            self.fake_date)
 
         suggestion.validate()
 
@@ -1370,7 +1414,8 @@ def test_validate_change(self) -> None:
             expected_suggestion_dict['status'], self.author_id,
             self.reviewer_id, expected_suggestion_dict['change_cmd'],
             expected_suggestion_dict['score_category'],
-            expected_suggestion_dict['language_code'], False, self.fake_date)
+            expected_suggestion_dict['language_code'], False, self.fake_date,
+            self.fake_date)
 
         suggestion.validate()
 
@@ -1393,7 +1438,8 @@ def test_validate_score_type_translation(self) -> None:
             expected_suggestion_dict['status'], self.author_id,
             self.reviewer_id, expected_suggestion_dict['change_cmd'],
             expected_suggestion_dict['score_category'],
-            expected_suggestion_dict['language_code'], False, self.fake_date)
+            expected_suggestion_dict['language_code'], False, self.fake_date,
+            self.fake_date)
 
         suggestion.validate()
 
@@ -1413,7 +1459,8 @@ def test_validate_change_cmd(self) -> None:
             expected_suggestion_dict['status'], self.author_id,
             self.reviewer_id, expected_suggestion_dict['change_cmd'],
             expected_suggestion_dict['score_category'],
-            expected_suggestion_dict['language_code'], False, self.fake_date)
+            expected_suggestion_dict['language_code'], False, self.fake_date,
+            self.fake_date)
 
         suggestion.validate()
 
@@ -1432,7 +1479,8 @@ def test_validate_translation_html_rte_tags(self) -> None:
             expected_suggestion_dict['status'], self.author_id,
             self.reviewer_id, expected_suggestion_dict['change_cmd'],
             expected_suggestion_dict['score_category'],
-            expected_suggestion_dict['language_code'], False, self.fake_date)
+            expected_suggestion_dict['language_code'], False, self.fake_date,
+            self.fake_date)
 
         suggestion.validate()
 
@@ -1456,7 +1504,8 @@ def test_validate_language_code_fails_when_language_codes_do_not_match(
             expected_suggestion_dict['status'], self.author_id,
             self.reviewer_id, expected_suggestion_dict['change_cmd'],
             expected_suggestion_dict['score_category'],
-            expected_suggestion_dict['language_code'], False, self.fake_date)
+            expected_suggestion_dict['language_code'], False, self.fake_date,
+            self.fake_date)
         expected_language_code = (
             expected_suggestion_dict['change_cmd']['language_code']
         )
@@ -1482,7 +1531,8 @@ def test_validate_language_code_fails_when_language_code_is_set_to_none(
             expected_suggestion_dict['status'], self.author_id,
             self.reviewer_id, expected_suggestion_dict['change_cmd'],
             expected_suggestion_dict['score_category'],
-            expected_suggestion_dict['language_code'], False, self.fake_date)
+            expected_suggestion_dict['language_code'], False, self.fake_date,
+            self.fake_date)
         suggestion.validate()
 
         # TODO(#13059): Here we use MyPy ignore because after we fully type the
@@ -1506,7 +1556,8 @@ def test_validate_change_with_invalid_language_code_fails_validation(
             expected_suggestion_dict['status'], self.author_id,
             self.reviewer_id, expected_suggestion_dict['change_cmd'],
             expected_suggestion_dict['score_category'],
-            expected_suggestion_dict['language_code'], False, self.fake_date)
+            expected_suggestion_dict['language_code'], False, self.fake_date,
+            self.fake_date)
 
         suggestion.validate()
 
@@ -1526,7 +1577,8 @@ def test_pre_accept_validate_state_name(self) -> None:
             expected_suggestion_dict['status'], self.author_id,
             self.reviewer_id, expected_suggestion_dict['change_cmd'],
             expected_suggestion_dict['score_category'],
-            expected_suggestion_dict['language_code'], False, self.fake_date)
+            expected_suggestion_dict['language_code'], False, self.fake_date,
+            self.fake_date)
 
         exp_services.update_exploration(
             self.author_id, 'exp1', [
@@ -1566,7 +1618,8 @@ def test_accept_suggestion_adds_translation_in_exploration(self) -> None:
             self.suggestion_dict['status'], self.author_id,
             self.reviewer_id, self.suggestion_dict['change_cmd'],
             self.suggestion_dict['score_category'],
-            self.suggestion_dict['language_code'], False, self.fake_date)
+            self.suggestion_dict['language_code'], False, self.fake_date,
+            self.fake_date)
 
         suggestion.accept(
             'Accepted suggestion by translator: Add translation change.')
@@ -1604,7 +1657,8 @@ def test_accept_suggestion_with_set_of_string_adds_translation(
                 'data_format': 'set_of_normalized_string'
             },
             self.suggestion_dict['score_category'],
-            self.suggestion_dict['language_code'], False, self.fake_date)
+            self.suggestion_dict['language_code'], False, self.fake_date,
+            self.fake_date)
 
         suggestion.accept(
             'Accepted suggestion by translator: Add translation change.')
@@ -1636,7 +1690,8 @@ def test_accept_suggestion_with_psedonymous_author_adds_translation(
             expected_suggestion_dict['status'], self.PSEUDONYMOUS_ID,
             self.reviewer_id, expected_suggestion_dict['change_cmd'],
             expected_suggestion_dict['score_category'],
-            expected_suggestion_dict['language_code'], False, self.fake_date)
+            expected_suggestion_dict['language_code'], False, self.fake_date,
+            self.fake_date)
 
         suggestion.accept(
             'Accepted suggestion by translator: Add translation change.')
@@ -1658,7 +1713,8 @@ def test_get_all_html_content_strings(self) -> None:
             self.suggestion_dict['status'], self.author_id,
             self.reviewer_id, self.suggestion_dict['change_cmd'],
             self.suggestion_dict['score_category'],
-            self.suggestion_dict['language_code'], False, self.fake_date)
+            self.suggestion_dict['language_code'], False, self.fake_date,
+            self.fake_date)
 
         actual_outcome_list = suggestion.get_all_html_content_strings()
 
@@ -1683,7 +1739,8 @@ def test_get_all_html_content_strings_for_content_lists(self) -> None:
                 'data_format': 'set_of_normalized_string'
             },
             self.suggestion_dict['score_category'],
-            self.suggestion_dict['language_code'], False, self.fake_date)
+            self.suggestion_dict['language_code'], False, self.fake_date,
+            self.fake_date)
 
         actual_outcome_list = suggestion.get_all_html_content_strings()
 
@@ -1701,7 +1758,8 @@ def test_get_target_entity_html_strings_returns_expected_strings(
             self.suggestion_dict['status'], self.author_id,
             self.reviewer_id, self.suggestion_dict['change_cmd'],
             self.suggestion_dict['score_category'],
-            self.suggestion_dict['language_code'], False, self.fake_date)
+            self.suggestion_dict['language_code'], False, self.fake_date,
+            self.fake_date)
 
         actual_outcome_list = suggestion.get_target_entity_html_strings()
         expected_outcome_list = (
@@ -1733,7 +1791,8 @@ def test_convert_html_in_suggestion_change(self) -> None:
             self.suggestion_dict['status'], self.author_id,
             self.reviewer_id, change_dict,
             self.suggestion_dict['score_category'],
-            self.suggestion_dict['language_code'], False, self.fake_date)
+            self.suggestion_dict['language_code'], False, self.fake_date,
+            self.fake_date)
         suggestion.convert_html_in_suggestion_change(
             html_validation_service.add_math_content_to_math_rte_components)
         self.assertEqual(
@@ -1825,7 +1884,8 @@ def test_validate_suggestion_edit_state_content(self) -> None:
             expected_suggestion_dict['status'], self.author_id,
             self.reviewer_id, expected_suggestion_dict['change_cmd'],
             expected_suggestion_dict['score_category'],
-            expected_suggestion_dict['language_code'], False, self.fake_date)
+            expected_suggestion_dict['language_code'], False, self.fake_date,
+            self.fake_date)
 
         suggestion.validate()
 
@@ -1839,7 +1899,8 @@ def test_get_score_part_helper_methods(self) -> None:
             expected_suggestion_dict['status'], self.author_id,
             self.reviewer_id, expected_suggestion_dict['change_cmd'],
             expected_suggestion_dict['score_category'],
-            expected_suggestion_dict['language_code'], False, self.fake_date)
+            expected_suggestion_dict['language_code'], False, self.fake_date,
+            self.fake_date)
 
         self.assertEqual(suggestion.get_score_type(), 'question')
         self.assertEqual(suggestion.get_score_sub_type(), 'topic_1')
@@ -1854,7 +1915,8 @@ def test_validate_score_type(self) -> None:
             expected_suggestion_dict['status'], self.author_id,
             self.reviewer_id, expected_suggestion_dict['change_cmd'],
             expected_suggestion_dict['score_category'],
-            expected_suggestion_dict['language_code'], False, self.fake_date)
+            expected_suggestion_dict['language_code'], False, self.fake_date,
+            self.fake_date)
 
         suggestion.validate()
 
@@ -1876,7 +1938,8 @@ def test_validate_change_type(self) -> None:
             expected_suggestion_dict['status'], self.author_id,
             self.reviewer_id, expected_suggestion_dict['change_cmd'],
             expected_suggestion_dict['score_category'],
-            expected_suggestion_dict['language_code'], False, self.fake_date)
+            expected_suggestion_dict['language_code'], False, self.fake_date,
+            self.fake_date)
 
         suggestion.validate()
 
@@ -1902,7 +1965,8 @@ def test_validate_change_cmd(self) -> None:
             expected_suggestion_dict['status'], self.author_id,
             self.reviewer_id, expected_suggestion_dict['change_cmd'],
             expected_suggestion_dict['score_category'],
-            expected_suggestion_dict['language_code'], False, self.fake_date)
+            expected_suggestion_dict['language_code'], False, self.fake_date,
+            self.fake_date)
 
         suggestion.validate()
 
@@ -1923,7 +1987,8 @@ def test_validate_change_cmd_type(self) -> None:
             expected_suggestion_dict['status'], self.author_id,
             self.reviewer_id, expected_suggestion_dict['change_cmd'],
             expected_suggestion_dict['score_category'],
-            expected_suggestion_dict['language_code'], False, self.fake_date)
+            expected_suggestion_dict['language_code'], False, self.fake_date,
+            self.fake_date)
 
         suggestion.validate()
 
@@ -1945,7 +2010,8 @@ def test_validate_change_question_dict(self) -> None:
             expected_suggestion_dict['status'], self.author_id,
             self.reviewer_id, expected_suggestion_dict['change_cmd'],
             expected_suggestion_dict['score_category'],
-            expected_suggestion_dict['language_code'], False, self.fake_date)
+            expected_suggestion_dict['language_code'], False, self.fake_date,
+            self.fake_date)
 
         suggestion.validate()
 
@@ -1970,7 +2036,8 @@ def test_validate_change_question_state_data_schema_version(self) -> None:
             expected_suggestion_dict['status'], self.author_id,
             self.reviewer_id, expected_suggestion_dict['change_cmd'],
             expected_suggestion_dict['score_category'],
-            expected_suggestion_dict['language_code'], False, self.fake_date)
+            expected_suggestion_dict['language_code'], False, self.fake_date,
+            self.fake_date)
 
         suggestion.validate()
 
@@ -2001,7 +2068,8 @@ def test_validate_change_skill_difficulty_none(self) -> None:
             expected_suggestion_dict['status'], self.author_id,
             self.reviewer_id, expected_suggestion_dict['change_cmd'],
             expected_suggestion_dict['score_category'],
-            expected_suggestion_dict['language_code'], False, self.fake_date)
+            expected_suggestion_dict['language_code'], False, self.fake_date,
+            self.fake_date)
         suggestion.validate()
 
         # TODO(#13059): Here we use MyPy ignore because after we fully type the
@@ -2024,7 +2092,8 @@ def test_validate_change_skill_difficulty_invalid_value(self) -> None:
             expected_suggestion_dict['status'], self.author_id,
             self.reviewer_id, expected_suggestion_dict['change_cmd'],
             expected_suggestion_dict['score_category'],
-            expected_suggestion_dict['language_code'], False, self.fake_date)
+            expected_suggestion_dict['language_code'], False, self.fake_date,
+            self.fake_date)
         suggestion.validate()
 
         suggestion.change_cmd.skill_difficulty = 0.4
@@ -2045,7 +2114,8 @@ def test_pre_accept_validate_change_skill_id(self) -> None:
             expected_suggestion_dict['status'], self.author_id,
             self.reviewer_id, expected_suggestion_dict['change_cmd'],
             expected_suggestion_dict['score_category'],
-            expected_suggestion_dict['language_code'], False, self.fake_date)
+            expected_suggestion_dict['language_code'], False, self.fake_date,
+            self.fake_date)
 
         skill_id = skill_services.get_new_skill_id()
         self.save_new_skill(skill_id, self.author_id, description='description')
@@ -2073,7 +2143,8 @@ def test_pre_accept_validate_change_invalid_skill_id(self) -> None:
             expected_suggestion_dict['status'], self.author_id,
             self.reviewer_id, expected_suggestion_dict['change_cmd'],
             expected_suggestion_dict['score_category'],
-            expected_suggestion_dict['language_code'], False, self.fake_date)
+            expected_suggestion_dict['language_code'], False, self.fake_date,
+            self.fake_date)
 
         skill_id = skill_services.get_new_skill_id()
         self.save_new_skill(skill_id, self.author_id, description='description')
@@ -2098,7 +2169,8 @@ def test_populate_old_value_of_change(self) -> None:
             expected_suggestion_dict['status'], self.author_id,
             self.reviewer_id, expected_suggestion_dict['change_cmd'],
             expected_suggestion_dict['score_category'],
-            expected_suggestion_dict['language_code'], False, self.fake_date)
+            expected_suggestion_dict['language_code'], False, self.fake_date,
+            self.fake_date)
 
         # Here we use MyPy ignore because method `populate_old_value_of_change`
         # does not return any value but for testing purpose we are still
@@ -2116,7 +2188,8 @@ def test_cannot_accept_suggestion_with_invalid_skill_id(self) -> None:
             expected_suggestion_dict['status'], self.author_id,
             self.reviewer_id, expected_suggestion_dict['change_cmd'],
             expected_suggestion_dict['score_category'],
-            expected_suggestion_dict['language_code'], False, self.fake_date)
+            expected_suggestion_dict['language_code'], False, self.fake_date,
+            self.fake_date)
 
         suggestion.change_cmd.skill_id = skill_services.get_new_skill_id()
 
@@ -2136,7 +2209,8 @@ def test_pre_update_validate_change_cmd(self) -> None:
             expected_suggestion_dict['status'], self.author_id,
             self.reviewer_id, expected_suggestion_dict['change_cmd'],
             expected_suggestion_dict['score_category'],
-            expected_suggestion_dict['language_code'], False, self.fake_date)
+            expected_suggestion_dict['language_code'], False, self.fake_date,
+            self.fake_date)
 
         change = {
             'cmd': question_domain.CMD_UPDATE_QUESTION_PROPERTY,
@@ -2165,7 +2239,8 @@ def test_pre_update_validate_change_skill_id(self) -> None:
             expected_suggestion_dict['status'], self.author_id,
             self.reviewer_id, expected_suggestion_dict['change_cmd'],
             expected_suggestion_dict['score_category'],
-            expected_suggestion_dict['language_code'], False, self.fake_date)
+            expected_suggestion_dict['language_code'], False, self.fake_date,
+            self.fake_date)
 
         content_id_generator = translation_domain.ContentIdGenerator()
         change: ChangeType = {
@@ -2213,7 +2288,7 @@ def test_pre_update_validate_complains_if_nothing_changed(self) -> None:
             'exploration.exp1.thread1', 'exp1', 1,
             suggestion_models.STATUS_ACCEPTED, self.author_id,
             self.reviewer_id, change,
-            'question.topic_1', 'en', False, self.fake_date)
+            'question.topic_1', 'en', False, self.fake_date, self.fake_date)
 
         content_id_generator = translation_domain.ContentIdGenerator()
         new_change: ChangeType = {
@@ -2264,7 +2339,7 @@ def test_pre_update_validate_accepts_a_change_in_skill_difficulty_only(
             'exploration.exp1.thread1', 'exp1', 1,
             suggestion_models.STATUS_ACCEPTED, self.author_id,
             self.reviewer_id, change,
-            'question.topic_1', 'en', False, self.fake_date)
+            'question.topic_1', 'en', False, self.fake_date, self.fake_date)
 
         content_id_generator = translation_domain.ContentIdGenerator()
         new_change: ChangeType = {
@@ -2318,7 +2393,7 @@ def test_pre_update_validate_accepts_a_change_in_state_data_only(
             'exploration.exp1.thread1', 'exp1', 1,
             suggestion_models.STATUS_ACCEPTED, self.author_id,
             self.reviewer_id, change,
-            'question.topic_1', 'en', False, self.fake_date)
+            'question.topic_1', 'en', False, self.fake_date, self.fake_date)
 
         content_id_generator = translation_domain.ContentIdGenerator()
         new_change: ChangeType = {
@@ -2358,7 +2433,8 @@ def test_validate_author_id(self) -> None:
             expected_suggestion_dict['status'], self.author_id,
             self.reviewer_id, expected_suggestion_dict['change_cmd'],
             expected_suggestion_dict['score_category'],
-            expected_suggestion_dict['language_code'], False, self.fake_date)
+            expected_suggestion_dict['language_code'], False, self.fake_date,
+            self.fake_date)
 
         suggestion.validate()
 
@@ -2379,7 +2455,8 @@ def test_validate_author_id_format(self) -> None:
             expected_suggestion_dict['status'], self.author_id,
             self.reviewer_id, expected_suggestion_dict['change_cmd'],
             expected_suggestion_dict['score_category'],
-            expected_suggestion_dict['language_code'], False, self.fake_date)
+            expected_suggestion_dict['language_code'], False, self.fake_date,
+            self.fake_date)
 
         suggestion.validate()
 
@@ -2398,7 +2475,8 @@ def test_validate_final_reviewer_id(self) -> None:
             expected_suggestion_dict['status'], self.author_id,
             self.reviewer_id, expected_suggestion_dict['change_cmd'],
             expected_suggestion_dict['score_category'],
-            expected_suggestion_dict['language_code'], False, self.fake_date)
+            expected_suggestion_dict['language_code'], False, self.fake_date,
+            self.fake_date)
 
         suggestion.validate()
 
@@ -2419,7 +2497,8 @@ def test_validate_final_reviewer_id_format(self) -> None:
             expected_suggestion_dict['status'], self.author_id,
             self.reviewer_id, expected_suggestion_dict['change_cmd'],
             expected_suggestion_dict['score_category'],
-            expected_suggestion_dict['language_code'], False, self.fake_date)
+            expected_suggestion_dict['language_code'], False, self.fake_date,
+            self.fake_date)
 
         suggestion.validate()
 
@@ -2440,7 +2519,8 @@ def test_validate_language_code_fails_when_language_codes_do_not_match(
             expected_suggestion_dict['status'], self.author_id,
             self.reviewer_id, expected_suggestion_dict['change_cmd'],
             expected_suggestion_dict['score_category'],
-            expected_suggestion_dict['language_code'], False, self.fake_date)
+            expected_suggestion_dict['language_code'], False, self.fake_date,
+            self.fake_date)
         # Here we use cast because the value of `question_dict` key is a
         # Union of all allowed change dict types. So, to narrow down the type
         # to QuestionDict, we used assert here.
@@ -2474,7 +2554,8 @@ def test_validate_language_code_fails_when_language_code_is_set_to_none(
             expected_suggestion_dict['status'], self.author_id,
             self.reviewer_id, expected_suggestion_dict['change_cmd'],
             expected_suggestion_dict['score_category'],
-            expected_suggestion_dict['language_code'], False, self.fake_date)
+            expected_suggestion_dict['language_code'], False, self.fake_date,
+            self.fake_date)
         suggestion.validate()
 
         # TODO(#13059): Here we use MyPy ignore because after we fully type the
@@ -2495,7 +2576,8 @@ def test_get_all_html_content_strings(self) -> None:
             self.suggestion_dict['status'], self.author_id,
             self.reviewer_id, self.suggestion_dict['change_cmd'],
             self.suggestion_dict['score_category'],
-            self.suggestion_dict['language_code'], False, self.fake_date)
+            self.suggestion_dict['language_code'], False, self.fake_date,
+            self.fake_date)
 
         actual_outcome_list = suggestion.get_all_html_content_strings()
         expected_outcome_list = [
@@ -2628,7 +2710,8 @@ def test_convert_html_in_suggestion_change(self) -> None:
             suggestion_dict['target_version_at_submission'],
             suggestion_dict['status'], self.author_id, self.reviewer_id,
             suggestion_dict['change_cmd'], suggestion_dict['score_category'],
-            suggestion_dict['language_code'], False, self.fake_date)
+            suggestion_dict['language_code'], False, self.fake_date,
+            self.fake_date)
         suggestion.convert_html_in_suggestion_change(
             html_validation_service.add_math_content_to_math_rte_components)
         # Ruling out the possibility of any other type for mypy type checking.
@@ -2695,7 +2778,8 @@ def test_accept_suggestion_with_images(self) -> None:
             suggestion_dict['target_version_at_submission'],
             suggestion_dict['status'], self.author_id, self.reviewer_id,
             suggestion_dict['change_cmd'], suggestion_dict['score_category'],
-            suggestion_dict['language_code'], False, self.fake_date)
+            suggestion_dict['language_code'], False, self.fake_date,
+            self.fake_date)
         suggestion.accept('commit_message')
 
     def test_accept_suggestion_with_image_region_interactions(self) -> None:
@@ -2844,7 +2928,8 @@ def test_accept_suggestion_with_image_region_interactions(self) -> None:
             suggestion_dict['target_version_at_submission'],
             suggestion_dict['status'], self.author_id, self.reviewer_id,
             suggestion_dict['change_cmd'], suggestion_dict['score_category'],
-            suggestion_dict['language_code'], False, self.fake_date)
+            suggestion_dict['language_code'], False, self.fake_date,
+            self.fake_date)
 
         suggestion.accept('commit_message')
 
@@ -2883,7 +2968,7 @@ def test_contructor_updates_state_shema_in_change_cmd(self) -> None:
         suggestion = suggestion_registry.SuggestionAddQuestion(
             'suggestionId', 'target_id', 1, suggestion_models.STATUS_IN_REVIEW,
             self.author_id, 'test_reviewer', change, score_category, 'en',
-            False, self.fake_date)
+            False, self.fake_date, self.fake_date)
         # Ruling out the possibility of any other type for mypy type checking.
         assert isinstance(suggestion.change_cmd.question_dict, dict)
         self.assertEqual(
@@ -2924,7 +3009,7 @@ def test_contructor_raise_exception_for_invalid_state_shema_version(
                 'suggestionId', 'target_id', 1,
                 suggestion_models.STATUS_IN_REVIEW, self.author_id,
                 'test_reviewer', change, score_category, 'en', False,
-                self.fake_date)
+                self.fake_date, self.fake_date)
 
 
 class CommunityContributionStatsUnitTests(test_utils.GenericTestBase):