From b6f95c71ce30f74f3b62ecbf39c26f14cc825143 Mon Sep 17 00:00:00 2001 From: Brian Wilson Date: Tue, 2 Apr 2013 16:20:46 -0400 Subject: [PATCH] change command to handle file input instead --- .../management/commands/remove_input_state.py | 84 ++++++++++++++----- 1 file changed, 62 insertions(+), 22 deletions(-) diff --git a/lms/djangoapps/courseware/management/commands/remove_input_state.py b/lms/djangoapps/courseware/management/commands/remove_input_state.py index 722fa90fdd..772758a33b 100644 --- a/lms/djangoapps/courseware/management/commands/remove_input_state.py +++ b/lms/djangoapps/courseware/management/commands/remove_input_state.py @@ -8,7 +8,7 @@ import json import logging from optparse import make_option -from django.core.management.base import BaseCommand +from django.core.management.base import BaseCommand, CommandError from django.db import transaction from courseware.models import StudentModule, StudentModuleHistory @@ -30,6 +30,17 @@ class Command(BaseCommand): modified > '2013-03-28 22:00:00' (the problem must have been visited after the bug was introduced on Prod and Edge) state like '%input_state%' (the problem must have "input_state" set). + + This filtering is done on the production database replica, so that the larger select queries don't lock + the real production database. The list of id values for Student Modules is written to a file, and the + file is passed into this command. The sql file passed to mysql contains: + + select sm.id from courseware_studentmodule sm + where sm.modified > "2013-03-28 22:00:00" + and sm.created < "2013-03-29 16:30:00" + and sm.state like "%input_state%" + and sm.module_type = 'problem'; + ''' num_visited = 0 @@ -42,26 +53,54 @@ class Command(BaseCommand): action='store_true', dest='save_changes', default=False, - help='Persist the changes that were encountered. If not set, no changes are saved.'), ) + help='Persist the changes that were encountered. If not set, no changes are saved.'), + make_option('--idlist', + action='store', + dest='idlist_path', + help='Path containing id values of StudentModule objects to clean (one per line).'), + ) - def fix_studentmodules(self, save_changes): - '''Identify the list of StudentModule objects that might need fixing, and then fix each one''' - modules = StudentModule.objects.filter(modified__gt='2013-03-28 22:00:00', - created__lt='2013-03-29 16:30:00', - state__contains='input_state') +# DON'T DO THIS: +# def fix_studentmodules(self, save_changes): +# '''Identify the list of StudentModule objects that might need fixing, and then fix each one''' +# modules = StudentModule.objects.filter(modified__gt='2013-03-28 22:00:00', +# created__lt='2013-03-29 16:30:00', +# state__contains='input_state') +# +# for module in modules: +# self.remove_studentmodule_input_state(module, save_changes) +# +# LOG.info("Finished student modules: updating {0} of {1} modules".format(self.num_changed, self.num_visited)) +# +# hist_modules = StudentModuleHistory.objects.filter(created__gt='2013-03-28 22:00:00', +# created__lt='2013-03-29 16:30:00', +# state__contains='input_state') +# +# for hist_module in hist_modules: +# self.remove_studentmodulehistory_input_state(hist_module, save_changes) +# +# LOG.info("Finished student history modules: updating {0} of {1} modules".format(self.num_hist_changed, self.num_hist_visited)) - for module in modules: + def fix_studentmodules_in_list(self, save_changes, idlist_path): + '''Read in the list of StudentModule objects that might need fixing, and then fix each one''' + + # open file and read id values from it: + for line in open(idlist_path, 'r'): + student_module_id = line.strip() + if student_module_id == 'id': + continue + try: + module = StudentModule.objects.get(id=student_module_id) + except: + LOG.error("Unable to find student module with id = {0}: skipping... ".format(student_module_id)) + continue self.remove_studentmodule_input_state(module, save_changes) + hist_modules = StudentModuleHistory.objects.filter(student_module_id = student_module_id) + for hist_module in hist_modules: + self.remove_studentmodulehistory_input_state(hist_module, save_changes) + LOG.info("Finished student modules: updating {0} of {1} modules".format(self.num_changed, self.num_visited)) - - hist_modules = StudentModuleHistory.objects.filter(created__gt='2013-03-28 22:00:00', - created__lt='2013-03-29 16:30:00', - state__contains='input_state') - - for hist_module in hist_modules: - self.remove_studentmodulehistory_input_state(hist_module, save_changes) - LOG.info("Finished student history modules: updating {0} of {1} modules".format(self.num_hist_changed, self.num_hist_visited)) @transaction.autocommit @@ -116,14 +155,15 @@ class Command(BaseCommand): # don't make the change, but increment the count indicating the change would be made self.num_hist_changed += 1 - def handle(self, **options): + def handle(self, *args, **options): '''Handle management command request''' - + if len(args) != 1: + raise CommandError("missing idlist file") + idlist_path = args[0] save_changes = options['save_changes'] + LOG.info("Starting run: reading from idlist file {0}; save_changes = {1}".format(idlist_path, save_changes)) - LOG.info("Starting run: save_changes = {0}".format(save_changes)) - - self.fix_studentmodules(save_changes) - + self.fix_studentmodules_in_list(save_changes, idlist_path) + LOG.info("Finished run: updating {0} of {1} student modules".format(self.num_changed, self.num_visited)) LOG.info("Finished run: updating {0} of {1} student history modules".format(self.num_hist_changed, self.num_hist_visited))