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

Fix part of #1366: Cron job to keep history of dashboard statistics #2173

Merged
merged 21 commits into from
Jul 7, 2016
Merged

Fix part of #1366: Cron job to keep history of dashboard statistics #2173

merged 21 commits into from
Jul 7, 2016

Conversation

526avijitgupta
Copy link
Contributor

@526avijitgupta 526avijitgupta commented Jun 26, 2016

Tasks to be completed as a part of this PR:

  • Create model property for storing weekly stats in UserStatsModel
  • Create a service method to update this model with the last week's stats.
  • Write getter to get value of weekly stats list accompanied by tests for this service method.
  • Create cron job that class this service method for all users, weekly.
  • Testing this job manually, as well as writing tests for this.

@526avijitgupta
Copy link
Contributor Author

526avijitgupta commented Jun 26, 2016

@wxyxinyu @seanlip @AllanYangZhou I have tried to implement a cron job that keeps a copy of weekly statistics of a creator. But I'm not sure how to test this (both manually + writing tests). Do you have any ideas about how could I proceed with testing ?
Also, can you please review to see if the approach is correct or not ?
Thanks!

UPDATE:
Tested this job manually, works fine. I'm still not sure how to proceed with writing tests for this though!

@codecov-io
Copy link

codecov-io commented Jun 26, 2016

Current coverage is 48.01%

Merging #2173 into develop will decrease coverage by 0.05%

@@            develop      #2173   diff @@
==========================================
  Files           185        185          
  Lines         15829      15857    +28   
  Methods        2717       2720     +3   
  Messages          0          0          
  Branches       2688       2692     +4   
==========================================
+ Hits           7609       7613     +4   
- Misses         8220       8244    +24   
  Partials          0          0          

Powered by Codecov. Last updated by 125bc0e...36a8e81

else:
return None

def save_last_dashboard_stats(user_id):
Copy link
Contributor

Choose a reason for hiding this comment

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

I think you can pass in the entire list of users, and then run the update over all of them, and do put_multi() to store all of them at once. That way you don't need to call get() and put() for every user (which will take a long time).

Copy link
Contributor

Choose a reason for hiding this comment

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

Actually, I think it makes more sense to create a mapreduce job to do this instead. (Do a one-off job, not a continuous job, and let the cron start the job). It might not be possible to actually load all the users to memory.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Created a one-off mapreduce job. But I couldn't find any classmethod which can be used to kick off this job from cron.py
[start_compurtation() fulfills the very same purpose, but only for continuous jobs]

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 now.

Copy link
Member

Choose a reason for hiding this comment

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

Nit: 2 lines above top-level function

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

@wxyxinyu
Copy link
Contributor

Would you also need to get the total number of plays in this cron job? (We were going to experiment with changing the change in rating to change in number of ratings.)

Also, how did you test this manually? Just checking, because I don't find any tests for cron jobs either. I think testing the get/set functions for the data should be fine, especially since your cron has very little logic inside the get().

"""Save statistics for creator dashboard of a user by appending to a list
keyed by datetime.utcnow().date().

This method is called through a cron job which is run weekly.
Copy link
Member

Choose a reason for hiding this comment

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

This function should know (and assume) nothing about the cron job.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Removed reference about cron job.

@526avijitgupta
Copy link
Contributor Author

526avijitgupta commented Jun 28, 2016

@wxyxinyu I can add tests for number of plays as well. Just to confirm - it will require that I stop the job and re-start it, right ?
Open admin panel at localhost:8000/ . There's a "Cron Jobs" link in left menu.

UPDATE:
Added tests which include both - ratings as well as num plays: https://github.com/oppia/oppia/pull/2173/files#diff-f034774bfff184b81c6e784b604288b2R496

def get(self):
"""Handles GET requests."""
user_jobs_one_off_test.DashboardStatsOneOffJobTests.run_one_off_job()

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@seanlip I don't think this would work. Couldn't find any classmethod that can be used here to kick off this one-off job.
[start_computation fulfills the same purpose, but only for continuous jobs!]

Any suggestions ?

Copy link
Member

Choose a reason for hiding this comment

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

We have existing one-off jobs in the codebase. How do they work?

Copy link
Member

Choose a reason for hiding this comment

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

(Note that these are started by pressing a button on the admin page.)

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 knew that these can be manually tested through the admin panel. But my attempts of testing this manually falied. It kept throwing a wierd error -

No module named webtest

Not sure why this error was being thrown. I tried restarting the server a few times but it did not seem to go away. So I decided to commit without testing this line, and thus added a line comment here!

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Ah, got it

        user_jobs_one_off.DashboardStatsOneOffJob.enqueue(
            user_jobs_one_off.DashboardStatsOneOffJob.create_new())

works!

@@ -86,6 +87,15 @@ def get(self):
email_services.send_mail_to_admin(email_subject, email_message)


class AppendLastDashboardStatsToList(base.BaseHandler):
Copy link
Member

Choose a reason for hiding this comment

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

CronDashboardStatsHandler? (Follow existing naming conventions, unless there's a good reason not to.)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

okay. Renamed

average_ratings=average_ratings).put()
is_average_ratings = True
else:
is_average_ratings = False
Copy link
Member

Choose a reason for hiding this comment

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

What if you set average_ratings as None here? Then you don't need the is_average_ratings variable or the branching below in line 440.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

(This thought had crossed my mind during writing this code. But I went for creating a separate flag for this instead)
Done.

@526avijitgupta
Copy link
Contributor Author

PTAL

@526avijitgupta
Copy link
Contributor Author

Please review.

average_ratings = None

if not mr_model:
if average_ratings:
Copy link
Member

Choose a reason for hiding this comment

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

Why do you still have the branch?

Copy link
Contributor

@wxyxinyu wxyxinyu Jul 6, 2016

Choose a reason for hiding this comment

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

I think a cleaner way to do this is to follow the get_or_create() pattern in other models (for example, core.storage.activity.ActivityReferencesModel). Then, you just call get_or_create, and update the model you get, so you don't need to branch on the if not mr_model.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Used get_or_create

@seanlip
Copy link
Member

seanlip commented Jul 5, 2016

Hi @526avijitgupta, did a full review -- PTAL. Thanks!

@526avijitgupta
Copy link
Contributor Author

@wxyxinyu @seanlip Please review.

@seanlip
Copy link
Member

seanlip commented Jul 6, 2016

LGTM. Thanks @526avijitgupta!

@wxyxinyu do you have any other comments? If not I think this is mergeable once the travis checks pass.

"""Save statistics for creator dashboard of a user by appending to a list
keyed by a datetime string.
"""
model = user_models.UserStatsModel.get(user_id, strict=False)
Copy link
Contributor

Choose a reason for hiding this comment

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

You could use get_or_create() here too.

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

@wxyxinyu
Copy link
Contributor

wxyxinyu commented Jul 7, 2016

I just have one really small thing, and this will be good to merge.

@526avijitgupta
Copy link
Contributor Author

@wxyxinyu Addressed the review comment. Should be ready to merge now I think!

@wxyxinyu wxyxinyu merged commit e26ee9a into oppia:develop Jul 7, 2016
@526avijitgupta 526avijitgupta deleted the creator-dashboard-stats-cron-job branch July 7, 2016 18:50
wxyxinyu pushed a commit that referenced this pull request Aug 8, 2016
…2173)

* Cron job for recording creator stats

* Fixes

* Write getter and tests for weekly dasboard stats

* Minor changes to model

* Fix initial round of review comments

* Fix call to one-off job through cron

* Removed extra whiteline

* Fix accidental line deletion

* Partially addressed review comments

* Add sample test for multiple weeks

* Add test to check format of datetime string

* Add schema version to user dashboard stats

* Lint fix

* Fix brackets around conditional

* Fix continuous job and modify tests accordingly

* Address review comments

* Create new stats model if not already present

* Address review comments

* Use get_or_create for model

* Reused get_or_create in method
ctao5660 pushed a commit to ctao5660/oppia that referenced this pull request Nov 23, 2018
…ics (oppia#2173)

* Cron job for recording creator stats

* Fixes

* Write getter and tests for weekly dasboard stats

* Minor changes to model

* Fix initial round of review comments

* Fix call to one-off job through cron

* Removed extra whiteline

* Fix accidental line deletion

* Partially addressed review comments

* Add sample test for multiple weeks

* Add test to check format of datetime string

* Add schema version to user dashboard stats

* Lint fix

* Fix brackets around conditional

* Fix continuous job and modify tests accordingly

* Address review comments

* Create new stats model if not already present

* Address review comments

* Use get_or_create for model

* Reused get_or_create in method
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.

6 participants