Repository: oppia/oppia

Issue:
Update docstrings in the Python backend code to follow the style guide.
We've been a bit lax about adding docstrings to code in the backend. Instead, we should be following the style guide: https://google.github.io/styleguide/pyguide.html?showone=Comments#Comments -- docstrings should define args, return values, exceptions, etc. The aim of this issue is to ensure that the backend code is properly documented; we will tackle this on a file-by-file basis.

Note: if you're a new contributor, this issue is a particularly good introductory one to tackle, since it will require reading, understanding and documenting the purpose of a file in the codebase, and help you get acquainted with how the Oppia backend is structured. It's important that you're able to communicate and write clearly, though!

Some useful tips:
-  Please follow our [style guide](https://github.com/oppia/oppia/wiki/Coding-style-guide#general) (and also the [Google Python style guide](https://google.github.io/styleguide/pyguide.html?showone=Comments#Comments)) when writing docstrings. Make sure to follow the patterns established there as closely as possible, including spacing, capitalization and punctuation. Also, it's a good idea to look at some of the existing files (see below) to get a sense of the correct format -- we recommend following this closely in order to maintain uniformity across the codebase.
- If applicable, make sure that the docstrings for all functions correctly specify the args, return values, etc. If a function has no args, you can omit the Args section; if it has no return value, you can omit the Returns section.
- If a docstring is not present in the code, or the existing one is too vague for you to understand what's going on just by looking at it, then it should be rewritten to be more clear. You'll often need to read the code as well, and do a grep on the callsites to see how it's being used, in order to get a clear understanding of what it's doing. This is good practice for learning how to navigate the codebase, and is an intended part of this project.
- If you feel that parts of the codebase are unclear or incorrectly factored, please feel free to bring this up! The whole point of docstrings is to improve code health and accessibility for new developers, so if you spot other things that can be done in service of that aim, we should also look into doing that.

**Completed:**
- [x] core.controllers.base @shaz13 
- [x] core.controllers.cron @saeedjassani
- [x] core.controllers.reader @96mohitm
- [x] core.domain.activity_domain @andromfins
- [x] core.domain.activity_services @souravsingh
- [x] core.domain.classifier_services @andromfins 
- [x] core.domain.collection_domain @domevit
- [x] core.domain.collection_services @kahkhang 
- [x] core.domain.config_services @vibhor98 
- [x] core.domain.email_manager @Oishikatta 
- [x] core.domain.exp_domain @domevit
- [x] core.domain.exp_services @DubeySandeep
- [x] core.domain.feedback_domain @terrameijar 
- [x] core.domain.feedback_jobs_continuous
- [x] core.domain.feedback_services @andimeo
- [x] core.domain.fs_domain @kartik-25 
- [x] core.domain.html_cleaner @seanlip
- [x] core.domain.param_domain @rohan-gulati
- [x] core.domain.rating_services @BojOnTheBeat 
- [x] core.domain.rights_manager @ayejay
- [x] core.domain.stats_domain @Arunabh98
- [x] core.domain.stats_jobs_continuous @Arunabh98 
- [x] core.domain.stats_jobs_one_off @zpuller
- [x] core.domain.stats_services @manoj-makkuboy 
- [x] core.domain.subscription_services @vibhor98 
- [x] core.domain.summary_services @domevit 
- [x] core.domain.user_jobs_continuous @shubha1593 
- [x] core.domain.user_services @Nalinc 
- [x] core.domain.value_generators_domain @meetsha 
- [x] core.jobs @brianrodri
- [x] core.platform.app_identity.gae_app_identity_services @ubhisat
- [x] core.platform.models @pranavsid98 
- [x] core.platform.transactions.gae_transaction_services @ubhisat
- [x] core.storage.base_model.gae_models @vojtechjelinek
- [x] core.storage.collection.gae_models @kaggis
- [x] core.storage.email.gae_models @rajan-garg
- [x] core.storage.exploration.gae_models @kirankonduru
- [x] core.storage.feedback.gae_models @naveenshukla
- [x] core.storage.file.gae_models @Ian91 
- [x] core.storage.job.gae_models @sasacocic
- [x] core.storage.statistics.gae_models @abhishekarya286
- [x] core.storage.user.gae_models @Anthony-Alridge 
- [x] core.tests.test_util_jobs @seanlip
- [x] core.tests.test_utils @vibhor98
- [x] utils @yashLadha 


Discussion:
@seanlip I would like to work in the issue.

Hi @souravsingh, do you think you could finish up #2051 first, please? Thanks! 

@seanlip I would like to work on this issue.

Hi @andromfins -- now that you've had a go at writing docstrings for a single class, would you mind picking one or two other files and doing the same thing for those? Let me know which ones you pick, so that I can mark you off against them.

Thanks!

Hi, i'm working on **core.domain.classifier_services**.

OK, thanks @andromfins! 

@seanlip Can I take a stab at other file to add docstrings?

Yep, sounds good. Could you take core.domain.html_cleaner? Thanks!

@andromfins -- are you still working on this?

  @seanlip Yes!


Resolving patch:
diff --git a/core/domain/rating_services.py b/core/domain/rating_services.py
--- a/core/domain/rating_services.py
+++ b/core/domain/rating_services.py
@@ -33,9 +33,14 @@ def assign_rating_to_exploration(user_id, exploration_id, new_rating):
     """Records the rating awarded by the user to the exploration in both the
     user-specific data and exploration summary.
 
-    It validates the exploration id but not the user id.
-
-     - 'new_rating' should be an integer between 1 and 5
+    This function validates the exploration id but not the user id.
+
+    Args:
+        user_id: str. The id of the user assigning the rating.
+        exploration_id: str. The id of the exploration that is
+            assigned a rating.
+        new_rating: int. Value of assigned rating, should be between
+            1 and 5 inclusive.
     """
 
     if not isinstance(new_rating, int):
@@ -84,10 +89,16 @@ def _update_user_rating():
 
 
 def get_user_specific_rating_for_exploration(user_id, exploration_id):
-    """
+    """Fetches a rating for the specified exploration from the specified user
+    if one exists.
+
+    Args:
+        user_id: str. The id of the user.
+        exploration_id: str. The id of the exploration.
+
     Returns:
-        An integer 1-5, or None if there is no rating of this exploration by
-        this user.
+        int or None. An integer between 1 and 5 inclusive, or None if the user
+        has not previously rated the exploration.
     """
     exp_user_data_model = user_models.ExplorationUserDataModel.get(
         user_id, exploration_id)
@@ -95,11 +106,19 @@ def get_user_specific_rating_for_exploration(user_id, exploration_id):
 
 
 def get_when_exploration_rated(user_id, exploration_id):
-    """Returns the date-time the exploration was lasted rated by this user, or
-    None if no such rating has been awarded.
+    """Fetches the datetime the exploration was last rated by this user, or
+    None if no rating has been awarded.
+
+    Currently this function is only used for testing purposes.
+
+    Args:
+        user_id: str. The id of the user.
+        exploration_id: str. The id of the exploration.
 
-    Currently this function is only used for testing since the times ratings
-    were awarded are not used for anything.
+    Returns:
+        datetime.datetime or None. When the exploration was last
+        rated by the user, or None if the user has not previously
+        rated the exploration.
     """
     exp_user_data_model = user_models.ExplorationUserDataModel.get(
         user_id, exploration_id)
@@ -107,5 +126,15 @@ def get_when_exploration_rated(user_id, exploration_id):
 
 
 def get_overall_ratings_for_exploration(exploration_id):
+    """Fetches all ratings for an exploration.
+
+    Args:
+        exploration_id: str. The id of the exploration.
+
+    Returns:
+        a dict whose keys are '1', '2', '3', '4', '5' and whose
+        values are nonnegative integers representing the frequency counts
+        of each rating.
+    """
     exp_summary = exp_services.get_exploration_summary_by_id(exploration_id)
     return exp_summary.ratings


Test patch: