예제 #1
0
    def get(self):
        '''
            List problems.

            Sample arguments:
            course="CompoundProblems",
            set_id="Assignment1"
            Response:

            '''
        course = self.get_argument('course')
        set_id = self.get_argument('set_id')

        query = '''
            SELECT p.problem_id, p.source_file, p.value,
            COUNT(a.answer_id) AS attempt_count
            FROM {0}_problem AS p
            LEFT OUTER JOIN {0}_past_answer AS a
            ON a.set_id = p.set_id AND a.problem_id = p.problem_id
            WHERE p.set_id = %s
            GROUP BY COALESCE(a.problem_id, p.problem_id)
            ORDER BY problem_id ASC; '''.format(course)
        result = conn.query(query, set_id)

        hints_query = ''' SELECT p.problem_id, COUNT(h.id) as hint_count
        FROM {0}_problem as p LEFT OUTER JOIN {0}_hint as h
        ON h.set_id = p.set_id AND h.problem_id = p.problem_id AND h.deleted = 0
        WHERE p.set_id = %s
        GROUP BY COALESCE(h.problem_id, p.problem_id)
        ORDER BY problem_id ASC;
        '''.format(course)
        hints = conn.query(hints_query, set_id)
        for i in range(len(result)):
            result[i]['hint_count'] = hints[i]['hint_count']
        self.write(json.dumps(result))
예제 #2
0
    def get(self):
        '''
            List problems.

            Sample arguments:
            course="CompoundProblems",
            set_id="Assignment1"
            Response:

            '''
        course = self.get_argument('course')
        set_id = self.get_argument('set_id')

        query = '''
            SELECT p.problem_id, p.source_file, p.value,
            COUNT(a.answer_id) AS attempt_count
            FROM {0}_problem AS p
            LEFT OUTER JOIN {0}_past_answer AS a
            ON a.set_id = p.set_id AND a.problem_id = p.problem_id
            WHERE p.set_id = %s
            GROUP BY COALESCE(a.problem_id, p.problem_id)
            ORDER BY problem_id ASC; '''.format(course)
        result = conn.query(query, set_id)

        hints_query = ''' SELECT p.problem_id, COUNT(h.id) as hint_count
        FROM {0}_problem as p LEFT OUTER JOIN {0}_hint as h
        ON h.set_id = p.set_id AND h.problem_id = p.problem_id AND h.deleted = 0
        WHERE p.set_id = %s
        GROUP BY COALESCE(h.problem_id, p.problem_id)
        ORDER BY problem_id ASC;
        '''.format(course)
        hints = conn.query(hints_query, set_id)
        for i in range(len(result)):
            result[i]['hint_count'] = hints[i]['hint_count']
        self.write(json.dumps(result))
예제 #3
0
    def get(self):
        '''
            List answers by part.

            Sample arguments:
            course="CompoundProblems",
            set_id="Assignment1",
            problem_id=1,
            user_id=iawwal

            Response:
                [{"answer_id": 123..., "answer_string":"42"}, ...]
            '''
        only_counts = self.get_argument('counts', False)
        course = self.get_argument('course')
        if not only_counts:
            query_parts = ['select *  from {0}_answers_by_part'.format(course)]
            query_parts.append(
                self.where_clause('set_id', 'problem_id', 'part_id',
                                  'user_id'))
            query_parts.append('ORDER BY timestamp ASC;')

        else:
            query_parts = [
                '''SELECT set_id, problem_id, part_id, count(id) as attempt_count
            FROM {course}_answers_by_part'''.format(course=course)
            ]
            query_parts.append(
                self.where_clause('set_id', 'problem_id', 'part_id',
                                  'user_id'))
            query_parts.append('GROUP BY set_id, problem_id, part_id;')
        query = ' '.join(query_parts)
        result = conn.query(query)
        self.write(json.dumps(result, default=serialize_datetime))
예제 #4
0
    def get(self):
        '''
            List counts of students attempted, completed for problem part.

            Sample arguments:
            course="CompoundProblems",
            set_id="Assignment1"
            problem_id=1
            Response:
            {"students_completed": 193.0, "students_attempted": 193}

            '''

        course = self.get_argument('course')
        set_id = self.get_argument('set_id')
        problem_id = self.get_argument('problem_id')
        part_id = self.get_argument('part_id')
        status_query = '''
        SELECT COUNT(user_id) as students_attempted, SUM(completed) as students_completed FROM
        (SELECT user_id, MAX(score) AS completed
        FROM {course}_answers_by_part
        WHERE set_id = '{set_id}' AND problem_id = {problem_id} AND part_id = {part_id}
        GROUP BY user_id) as stats;
        '''.format(course=course,
                   set_id=set_id,
                   problem_id=problem_id,
                   part_id=part_id)
        result = conn.query(status_query)
        out = result[0]
        self.write(json.dumps(out))
예제 #5
0
    def get(self):
        '''
            List answers by part.

            Sample arguments:
            course="CompoundProblems",
            set_id="Assignment1",
            problem_id=1,
            user_id=iawwal

            Response:
                [{"answer_id": 123..., "answer_string":"42"}, ...]
            '''
        only_counts = self.get_argument('counts', False)
        course = self.get_argument('course')
        if not only_counts:
            query_parts = ['select *  from {0}_answers_by_part'.format(
                course)]
            query_parts.append(self.where_clause('set_id', 'problem_id', 'part_id', 'user_id'))
            query_parts.append('ORDER BY timestamp ASC;')

        else:
            query_parts = ['''SELECT set_id, problem_id, part_id, count(id) as attempt_count
            FROM {course}_answers_by_part'''.format(course=course)]
            query_parts.append(self.where_clause('set_id', 'problem_id', 'part_id', 'user_id'))
            query_parts.append('GROUP BY set_id, problem_id, part_id;')
        query = ' '.join(query_parts)
        result = conn.query(query)
        self.write(json.dumps(result, default=serialize_datetime))
예제 #6
0
    def get(self):
        '''
            List counts of students attempted, completed for problem part.

            Sample arguments:
            course="CompoundProblems",
            set_id="Assignment1"
            problem_id=1
            Response:
            {"students_completed": 193.0, "students_attempted": 193}

            '''

        course = self.get_argument('course')
        set_id = self.get_argument('set_id')
        problem_id = self.get_argument('problem_id')
        part_id = self.get_argument('part_id')
        status_query = '''
        SELECT COUNT(user_id) as students_attempted, SUM(completed) as students_completed FROM
        (SELECT user_id, MAX(score) AS completed
        FROM {course}_answers_by_part
        WHERE set_id = '{set_id}' AND problem_id = {problem_id} AND part_id = {part_id}
        GROUP BY user_id) as stats;
        '''.format(course=course, set_id=set_id, problem_id=problem_id, part_id=part_id)
        result = conn.query(status_query)
        out = result[0]
        self.write(json.dumps(out))
예제 #7
0
def get_source(course, set_id, problem_id):
    source_file = conn.query('''select source_file from {course}_problem
            where problem_id={problem_id} and set_id="{set_id}";
        '''.format(course=course, set_id=set_id, problem_id=problem_id))[0]['source_file']
    pg_path = os.path.join(webwork_dir, 'courses', course, 'templates', source_file)
    with open(pg_path, 'r') as fin:
        pg_file = fin.read()
        return pg_file
    def get(self):
        '''
        Assigns a filter function to a given part and hint.
        '''
        query = '''SELECT * FROM assigned_filters {WHERE}'''.\
            format(self.where_clause('course', 'set_id', 'problem_id', 'part_id'))
        res = conn.query(query)

        self.write(json.dumps(res))
예제 #9
0
 def check_hint_assignment(self, rows):
     ''' Given the course, user_id, set_id, problem_id, and pg_id,
         return a pandas DataFrame containing the rows in the mysql
         realtime_past_answers table that match the given args
          '''
     # Convert the context of the student struggling on the particular part
     # to a pandas DataFrame
     df = pd.DataFrame(rows)
     # Go through each entry in the assigned_hint_filter table.
     # Find matches for
     query_template = ''' SELECT hint_filter.filter_name,
             assigned_hint_filter.hint_id,
             assigned_hint_filter.trigger_cond,
             hint.set_id, hint.problem_id
         FROM {{course}}_hint_filter AS hint_filter
         JOIN {{course}}_assigned_hint_filter as assigned_hint_filter
         ON assigned_hint_filter.hint_filter_id = hint_filter.id
         JOIN {{course}}_hint as hint ON assigned_hint_filter.hint_id = hint.id
         WHERE hint.set_id = '{{set_id}}' AND hint.problem_id = {{problem_id}}
     '''
     query_rendered = Template(query_template).generate(**self.args)
     hint_filters_dict = dict((f.__name__, f) for f in hint_filters)
     assigned_hint_filters = conn.query(query_rendered)
     hint_ids_to_assign = set([])
     for assigned_hint_filter in assigned_hint_filters:
         filter_name = assigned_hint_filter['filter_name']
         hint_id = assigned_hint_filter['hint_id']
         trigger_cond = assigned_hint_filter['trigger_cond']
         hint_filter = hint_filters_dict[filter_name]
         self.args['hint_id'] = hint_id
         # Get a list of other places the hint has been assigned
         query_template = '''
             select {{course}}_assigned_hint.*
             from {{course}}_hint, {{course}}_assigned_hint
             where {{course}}_assigned_hint.hint_id={{course}}_hint.id
                 and {{course}}_hint.id = {{hint_id}}
         '''
         query_rendered = Template(query_template).generate(**self.args)
         previous_hint_assignments = conn.query(query_rendered)
         if hint_filter(self.args, df, previous_hint_assignments,
                        trigger_cond):
             hint_ids_to_assign.add(hint_id)
     return list(hint_ids_to_assign)
예제 #10
0
def get_source(course, set_id, problem_id):
    source_file = conn.query('''select source_file from {course}_problem
            where problem_id={problem_id} and set_id="{set_id}";
        '''.format(course=course, set_id=set_id,
                   problem_id=problem_id))[0]['source_file']
    pg_path = os.path.join(webwork_dir, 'courses', course, 'templates',
                           source_file)
    with open(pg_path, 'r') as fin:
        pg_file = fin.read()
        return pg_file
예제 #11
0
 def add_problem_source(self, args):
     query_template = '''select source_file from {{course}}_problem
         where {{course}}_problem.set_id     = "{{set_id}}" AND
               {{course}}_problem.problem_id = {{problem_id}} '''
     query_rendered = Template(query_template).generate(**args)
     rows = conn.query(query_rendered)
     if len(rows) == 1:
         args['source_file'] = rows[0]['source_file']
     else:
         args['source_file'] = None
     return args
예제 #12
0
def get_user_vars(course, set_id, problem_id):
    '''
    Gets user specific PG variables from the webwork database
    '''
    user_variables = conn.query('''SELECT * from {course}_user_variables
    WHERE set_id="{set_id}" AND problem_id = {problem_id};
    '''.format(course=course, set_id=set_id, problem_id=problem_id))
    variables_df = pd.DataFrame(user_variables)
    if len(variables_df) == 0:
        logger.warn("No user variables saved for assignment %s, please run the save_answers script", set_id)
    return variables_df
예제 #13
0
 def add_problem_source(self, args):
     query_template = '''select source_file from {{course}}_problem
         where {{course}}_problem.set_id     = "{{set_id}}" AND
               {{course}}_problem.problem_id = {{problem_id}} '''
     query_rendered = Template(query_template).generate(**args)
     rows = conn.query(query_rendered)
     if len(rows) == 1:
         args['source_file'] = rows[0]['source_file']
     else:
         args['source_file'] = None
     return args
예제 #14
0
 def check_hint_assignment(self, rows):
     ''' Given the course, user_id, set_id, problem_id, and pg_id, 
         return a pandas DataFrame containing the rows in the mysql 
         realtime_past_answers table that match the given args
          '''
     # Convert the context of the student struggling on the particular part
     # to a pandas DataFrame
     df = pd.DataFrame(rows) 
     # Go through each entry in the assigned_hint_filter table.  
     # Find matches for 
     query_template = ''' select {{course}}_hint_filter.filter_name, 
             {{course}}_assigned_hint_filter.hint_id
         from {{course}}_hint_filter, {{course}}_assigned_hint_filter
         where
             {{course}}_assigned_hint_filter.hint_filter_id
                 = {{course}}_hint_filter.id
     ''' 
     query_rendered = Template(query_template).generate(**self.args)
     hint_filters_dict = dict( (f.__name__, f) for f in hint_filters)
     assigned_hint_filters = conn.query(query_rendered)
     hint_ids_to_assign = set([]) 
     for assigned_hint_filter in assigned_hint_filters:
         filter_name = assigned_hint_filter['filter_name']
         hint_id = assigned_hint_filter['hint_id']
         hint_filter = hint_filters_dict[filter_name]
         self.args['hint_id'] = hint_id
         # Get a list of other places the hint has been assigned
         query_template = '''
             select {{course}}_assigned_hint.*
             from {{course}}_hint, {{course}}_assigned_hint
             where {{course}}_assigned_hint.hint_id={{course}}_hint.id 
                 and {{course}}_hint.id = {{hint_id}}
         '''
         query_rendered = Template(query_template).generate(**self.args)
         previous_hint_assignments = conn.query(query_rendered)
         if hint_filter(self.args, df, previous_hint_assignments):
             hint_ids_to_assign.add(hint_id)
     return list(hint_ids_to_assign)
예제 #15
0
 def get(self):
     '''
         Sample arguments:
         course="CompoundProblems",
         hint_id=10
         '''
     query = '''
         select hint_id, user_id
         from {course}_assigned_hint
         where
             hint_id={hint_id};'''.format(
                 course=self.get_argument('course'), hint_id=self.get_argument('hint_id'))
     rows = conn.query(query)
     self.write(json.dumps(rows, default=serialize_datetime))
예제 #16
0
 def get(self):
     '''
         Sample arguments:
         course="CompoundProblems",
         hint_id=10
         '''
     query = '''
         select hint_id, user_id
         from {course}_assigned_hint
         where
             hint_id={hint_id};'''.format(
         course=self.get_argument('course'),
         hint_id=self.get_argument('hint_id'))
     rows = conn.query(query)
     self.write(json.dumps(rows, default=serialize_datetime))
예제 #17
0
 def get(self):
     '''
         Sample arguments:
         course="CompoundProblems",
         problem_id = 1,
         part_id=1
     '''
     query = '''
         select pg_id, user_id, hint_id
         from {course}_assigned_hint
         where
             set_id='{set_id}' and problem_id={problem_id};'''.format(
                 course=self.get_argument('course'), set_id=self.get_argument('set_id'),
                 problem_id=self.get_argument('problem_id'))
     rows = conn.query(query)
     self.write(json.dumps(rows, default=serialize_datetime))
예제 #18
0
    def get(self):
        only_counts = self.get_argument('counts', False)
        course = self.get_argument('course')
        if not only_counts:
            query_parts = ['select *  from {0}_answers_by_part'.format(
                course)]
            query_parts.append(self.where_clause('set_id', 'problem_id', 'part_id'))
            query_parts.append('ORDER BY timestamp ASC;')

        else:
            query_parts = ['''SELECT set_id, problem_id, part_id, count(id) as attempt_count
            FROM {course}_answers_by_part'''.format(course=course)]
            query_parts.append(self.where_clause('set_id', 'problem_id', 'part_id'))
            query_parts.append('GROUP BY set_id, problem_id, part_id;')
        query = ' '.join(query_parts)
        result = conn.query(query)
        self.write(json.dumps(result, default=serialize_datetime))
예제 #19
0
 def get(self):
     '''
         Sample arguments:
         course="CompoundProblems",
         problem_id = 1,
         part_id=1
     '''
     query = '''
         select pg_id, user_id, hint_id
         from {course}_assigned_hint
         where
             set_id='{set_id}' and problem_id={problem_id};'''.format(
         course=self.get_argument('course'),
         set_id=self.get_argument('set_id'),
         problem_id=self.get_argument('problem_id'))
     rows = conn.query(query)
     self.write(json.dumps(rows, default=serialize_datetime))
예제 #20
0
    def get(self):
        '''
        Export all data about a problem
        '''
        course = self.get_argument('course')
        set_id = self.get_argument('set_id')
        problem_id = self.get_argument('problem_id')
        query = 'SELECT * from {0}_problem WHERE set_id = %s AND problem_id = %s'.format(
            course)
        result = conn.query(query, set_id, problem_id)[0]
        pg_path = result['source_file']
        full_path = os.path.join(webwork_dir, 'courses', course, 'templates',
                                 pg_path)
        with open(full_path, 'r') as f:
            pg_file_contents = f.read()

        out = {}
        out['pg_file'] = pg_file_contents
        out['filename'] = full_path
        past_answers = conn.query(
            'SELECT * from {0}_past_answer where set_id = %s AND problem_id = %s'
            .format(course), set_id, problem_id)

        out['past_answers'] = past_answers

        realtime_past_answers = conn.query(
            'SELECT * from {0}_realtime_past_answer where set_id = %s AND problem_id = %s'
            .format(course), set_id, problem_id)

        out['realtime_past_answers'] = realtime_past_answers

        hints = conn.query(
            'SELECT * from {0}_hint where set_id = %s AND problem_id = %s'.
            format(course), set_id, problem_id)
        out['hints'] = hints

        assigned_hints = conn.query(
            'SELECT * from {0}_assigned_hint where set_id = %s AND problem_id = %s'
            .format(course), set_id, problem_id)
        out['assigned_hints'] = assigned_hints

        hint_feedback = conn.query(
            'SELECT {0}_assigned_hint_feedback.* from {0}_assigned_hint_feedback INNER JOIN {0}_assigned_hint ON {0}_assigned_hint_feedback.assigned_hint_id = {0}_assigned_hint.id WHERE {0}_assigned_hint.set_id = %s AND {0}_assigned_hint.problem_id = %s'
            .format(course), set_id, problem_id)
        out['hint_feedback'] = hint_feedback
        self.write(json.dumps(out, default=serialize_datetime))
예제 #21
0
파일: auth.py 프로젝트: nimning/hintSystem
    def post(self):
        ''' For authenticating users against a Webwork course.

        Arguments:
            course="CompoundProblems",
            user_id="iawwal",
            password="******",

        Returns: {
                    "user_id": "iawwal",
                    "permission": 5
                }
        '''
        data = tornado.escape.json_decode(self.request.body)
        course = data.get("course")
        user_id = data.get("username")
        password = data.get("password")
        query = '''SELECT * from {0}_password as passwd
        JOIN {0}_permission as perm ON perm.user_id = passwd.user_id
        WHERE passwd.user_id=%s'''.format(course)
        result = conn.query(query, user_id)[0]
        salt = result['password']
        permission_level = result['permission']
        if crypt(password, salt) == salt and permission_level > 0:
            # Password is correct and has higher than student privileges
            userdata = {"user_id": user_id,
                        "permission": permission_level,
                    }
            jwt_string = jwt.encode(userdata, jwt_key)
            response = {"message": "Successfully logged in",
                        "token": jwt_string
                    }
        else:
            response = {"message": "Incorrect username or password"}
            self.set_status(401)
        self.write(json.dumps(response))
        self.flush()
        return
예제 #22
0
    def get(self):
        '''
        Export all data about a problem
        '''
        course = self.get_argument('course')
        set_id = self.get_argument('set_id')
        problem_id = self.get_argument('problem_id')
        query = 'SELECT * from {0}_problem WHERE set_id = %s AND problem_id = %s'.format(course)
        result = conn.query(query, set_id, problem_id)[0]
        pg_path = result['source_file']
        full_path = os.path.join(webwork_dir, 'courses', course, 'templates', pg_path)
        with open(full_path, 'r') as f:
            pg_file_contents = f.read()

        out = {}
        out['pg_file'] = pg_file_contents
        out['filename'] = full_path
        past_answers = conn.query(
            'SELECT * from {0}_past_answer where set_id = %s AND problem_id = %s'.format(course),
            set_id, problem_id)

        out['past_answers'] = past_answers

        realtime_past_answers = conn.query(
            'SELECT * from {0}_realtime_past_answer where set_id = %s AND problem_id = %s'.format(course),
            set_id, problem_id)

        out['realtime_past_answers'] = realtime_past_answers

        hints = conn.query(
            'SELECT * from {0}_hint where set_id = %s AND problem_id = %s'.format(course),
            set_id, problem_id)
        out['hints'] = hints

        assigned_hints = conn.query(
            'SELECT * from {0}_assigned_hint where set_id = %s AND problem_id = %s'.format(course),
            set_id, problem_id)
        out['assigned_hints'] = assigned_hints

        hint_feedback = conn.query(
            'SELECT {0}_assigned_hint_feedback.* from {0}_assigned_hint_feedback INNER JOIN {0}_assigned_hint ON {0}_assigned_hint_feedback.assigned_hint_id = {0}_assigned_hint.id WHERE {0}_assigned_hint.set_id = %s AND {0}_assigned_hint.problem_id = %s'.format(course),
            set_id, problem_id)
        out['hint_feedback'] = hint_feedback
        self.write(json.dumps(out, default=serialize_datetime))
예제 #23
0
    def get(self):
        '''
            List counts of students attempted, completed for problem.

            Sample arguments:
            course="CompoundProblems",
            set_id="Assignment1"
            problem_id=1
            Response:
            {"students": 350, "students_completed": 184, "students_attempted": 1

            '''
        course = self.get_argument('course')
        set_id = self.get_argument('set_id')
        problem_id = self.get_argument('problem_id')
        status_query = '''
        SELECT COUNT(*) as students, SUM(CASE WHEN status=1 then 1 else 0 end) AS students_completed,
        SUM(attempted) AS students_attempted
        FROM {course}_problem_user
        WHERE set_id = '{set_id}' AND problem_id = {problem_id};
        '''.format(course=course, set_id=set_id, problem_id=problem_id)
        result = conn.query(status_query)
        out = result[0]
        self.write(json.dumps(out))
예제 #24
0
    def post(self):
        ''' For authenticating users against a Webwork course.

            Sample arguments:
            course="CompoundProblems",
            user_id="iawwal", 
            password="******",

            Returning: [
                {
                    "hint_id": 3,"
                },
                ...
            ]
        '''
        data = tornado.escape.json_decode(self.request.body)
        course = data.get("course")
        user_id = data.get("username")
        password = data.get("password")
        query = 'SELECT * from {0}_password WHERE user_id=%s'.format(course)
        result = conn.query(query, user_id)[0]
        salt = result['password']
        if crypt(password, salt) == salt: # Password is correct
            # TODO: Send along access levels too
            userdata = {"user_id": user_id,
                    }
            jwt_string = jwt.encode(userdata, jwt_key)
            response = {"message": "Successfully logged in",
                        "token": jwt_string
                    }
        else:
            response = {"message": "Incorrect username or password"}
            self.set_status(401)
        self.write(json.dumps(response))
        self.flush()
        return
예제 #25
0
    def get(self):
        '''
            List counts of students attempted, completed for problem.

            Sample arguments:
            course="CompoundProblems",
            set_id="Assignment1"
            problem_id=1
            Response:
            {"students": 350, "students_completed": 184, "students_attempted": 1

            '''
        course = self.get_argument('course')
        set_id = self.get_argument('set_id')
        problem_id = self.get_argument('problem_id')
        status_query = '''
        SELECT COUNT(*) as students, SUM(CASE WHEN status=1 then 1 else 0 end) AS students_completed,
        SUM(attempted) AS students_attempted
        FROM {course}_problem_user
        WHERE set_id = '{set_id}' AND problem_id = {problem_id};
        '''.format(course=course, set_id=set_id, problem_id=problem_id)
        result = conn.query(status_query)
        out = result[0]
        self.write(json.dumps(out))
예제 #26
0
    def post(self):
        logger.debug('post starting')

        self.answer_exps = {}
        course = self.get_argument('course')
        set_id = self.get_argument('set_id')
        problem_id = int(self.get_argument('problem_id'))
        part_id = int(self.get_argument('part_id'))
        include_finished = (int(self.get_argument('include_finished', 1)) == 1)
        filter_function = self.get_argument('filter_function')

        pg_file = get_source(course, set_id, problem_id)
        # get the variables as set for this user. It seems that there is an alternative code for doing the same thing in the method vars_for_student
        user_variables = conn.query('''SELECT * from {course}_user_variables
        WHERE set_id="{set_id}" AND problem_id = {problem_id};
        '''.format(course=course, set_id=set_id, problem_id=problem_id))
        self.user_variables = {row['name']: row['value'] for row in user_variables}
        
        #logger.debug('computing user vars. user_variables=%s, self_variables_df=%s'%(str(user_variables),str(self.variables_df)))
        if len(user_variables) == 0:
            logger.warn("No user variables saved for assignment %s, please run the save_answers script", set_id)

        # Get the answer from pg file
        self.part_answer = get_part_answer(pg_file, part_id)

        # Get attempts by part
        if include_finished:
            query = '''SELECT * from {course}_answers_by_part
            WHERE set_id="{set_id}" AND problem_id = {problem_id}
            AND part_id={part_id};
            '''.format(course=course, set_id=set_id, problem_id=problem_id,
                       part_id=part_id)
        else:
            # This self join query idea comes from http://stackoverflow.com/a/4519302/90551
            # You can do this more clearly with subqueries but it's super slow
            query = '''SELECT abp.* FROM {course}_answers_by_part as abp
            LEFT JOIN {course}_answers_by_part AS abp2
            ON abp.problem_id = abp2.problem_id AND abp.set_id = abp2.set_id
            AND abp.part_id = abp2.part_id AND abp.user_id = abp2.user_id AND abp2.score = '1'
            WHERE abp.set_id='{set_id}' AND abp.problem_id={problem_id}
            AND abp.part_id={part_id} AND abp2.user_id IS NULL;
            '''.format(course=course, set_id=set_id, problem_id=problem_id,
                       part_id=part_id)

        logger.debug('before sending sql query')
        answers = conn.query(query)
        logger.debug('after sending sql query')

        self.load_filters()

        func_name = filter_function[4:filter_function.index('(')]
        status=self.filter_bank.add_filter(func_name,filter_function)
        #This is the case when a filter which is saved and run immediately
        searchString = "Filter named: " + func_name + " already exists, the author of the function has to replace it"
        if status != None and status.find(searchString) != -1:
            print "Filter " + func_name + " which is in filter bank, is invoked " + status
        elif status!=None:
            print "ERROR LOADING FUNCTION"+status
            return status

        _stdout=''
        _hints=[]

        logger.warn("Environment keys before parsers.py main loop " , self.filter_bank.get_env_keys())
        
        for a in answers:
            user_id = a['user_id']
            attempt=a['answer_string']
            etree = parse_and_eval(attempt)
            self.part_answer = replace_variables(self.user_variables, self.part_answer)
            
            # Get the correct answer and generate an etree for it.
            self.answer_etree = parse_and_eval(self.part_answer)
            #ans = self.answer_for_student(user_id)
            if etree:
                status,hint,output=self.filter_bank.exec_filter(func_name,{'attempt':attempt, 'att_tree':etree, 'answer': self.part_answer, 'ans_tree':self.answer_etree, 'variables':self.user_variables})
                if status:
                    logger.debug('exec_filter succeeded, attempt=%s,hint=%s,output=%s'%(attempt,hint,output))
                    _hints.append(hint)
                    _stdout += output
                else:
                    logger.debug('exec_filter failed attempt=%s,error=%s output=%s'%(attempt,hint,output))
            else:
                logger.debug('filed to parse attempt=%s, etree=%s'%(attempt,str(etree)))
                
        out = {
            'output':  _stdout,
            'matches': _hints
        }

        self.write(json.dumps(out))
        logger.debug('finished post')
예제 #27
0
    def get(self):
        '''
            Parses all expressions for a given question

            Sample arguments:
            course='CSE103_Fall14',
            set_id='Week1',
            problem_id=1,
            part_id=1
            include_finished=0
            Response:
                ...
            '''
        # Get correct answers
        logger.debug('Starting get')
        self.answer_exps = {}
        course = self.get_argument('course')
        set_id = self.get_argument('set_id')
        problem_id = self.get_argument('problem_id')
        part_id = int(self.get_argument('part_id'))
        include_finished = (int(self.get_argument('include_finished', 1)) == 1)

        source_file = conn.query('''
            select source_file from {course}_problem
            where
                problem_id={problem_id} and
                set_id="{set_id}";
        '''.format(course=course, set_id=set_id, problem_id=problem_id))[0]['source_file']
        
        pg_path = os.path.join(webwork_dir, 'courses', course, 'templates', source_file)
        with open(pg_path, 'r') as fin:
            pg_file = fin.read()
        # name  problem_id set_id   user_id  value
        user_variables = conn.query('''SELECT * from {course}_user_variables
        WHERE set_id="{set_id}" AND problem_id = {problem_id};
        '''.format(course=course, set_id=set_id, problem_id=problem_id))
        self.variables_df = pd.DataFrame(user_variables)
        if len(self.variables_df) == 0:
            logger.warn("No user variables saved for assignment %s, please run the save_answers script", set_id)
            # raise tornado.web.HTTPError(500)
        answer_re = re.compile('\[__+\]{(?:Compute\(")?(.+?)(?:"\))?}')
        answer_boxes = answer_re.findall(pg_file)
        self.part_answer = answer_boxes[part_id-1]
        self.answer_ptree = parse_webwork(self.part_answer)

        # Get attempts by part
        if include_finished:
            query = '''SELECT * from {course}_answers_by_part
            WHERE set_id="{set_id}" AND problem_id = {problem_id}
            AND part_id={part_id};
            '''.format(course=course, set_id=set_id, problem_id=problem_id,
                       part_id=part_id)
        else:
            # This self join query idea comes from http://stackoverflow.com/a/4519302/90551
            # You can do this more clearly with subqueries but it's super slow
            query = '''SELECT abp.* FROM {course}_answers_by_part as abp
            LEFT JOIN {course}_answers_by_part AS abp2
            ON abp.problem_id = abp2.problem_id AND abp.set_id = abp2.set_id
            AND abp.part_id = abp2.part_id AND abp.user_id = abp2.user_id AND abp2.score = '1'
            WHERE abp.set_id='{set_id}' AND abp.problem_id={problem_id}
            AND abp.part_id={part_id} AND abp2.user_id IS NULL;
            '''.format(course=course, set_id=set_id, problem_id=problem_id,
                       part_id=part_id)

        logger.debug('Before sending SQL')
        answers = conn.query(query)
        logger.debug('After sending SQL')

        all_correct_terms = set()
        correct_terms_map = defaultdict(lambda: defaultdict(list))
        incorrect_terms_map = defaultdict(lambda: defaultdict(list))
        # Parse and evaluate all answers
        for a in answers:
            # {'user_id': u'acpatel', 'timestamp': datetime.datetime(2014, 10, 12, 2, 10, 19), 'id': 284488L, 'score': u'1', 'answer_string': u'C(55,6)', 'part_id': 2L, 'problem_id': 10L, 'set_id': u'Week2', 'answer_id': 199326L}
            user_id = a['user_id']
            ans = self.answer_for_student(user_id)

            if a['score'] != '1':
                etree, nums = parsed(a['answer_string'])
                if etree and nums:
                    correct_terms = self.correct_terms(nums, ans)
                    all_correct_terms |= set(correct_terms)
                    # logger.debug(set(correct_terms))
                    if a['user_id'] not in correct_terms_map[str(sorted(correct_terms))][a['answer_string']]:
                        correct_terms_map[str(sorted(correct_terms))][a['answer_string']].append(a['user_id'])

        out = {}
        out['correct'] = correct_terms_map
        out['correct_terms'] = sorted(all_correct_terms)
        out['incorrect'] = incorrect_terms_map
        out['answer_ptree'] = str(self.answer_ptree)
        self.write(json.dumps(out, default=serialize_datetime))
        logger.debug('Finished')
    def post(self):
        """
        Tests one student's answer against the filters defined for the problem part.
        For any filters which match, returns the matched hint(PGML) which should be inserted into the hint.
        """
        course = self.get_argument('course')
        set_id = self.get_argument('set_id')
        problem_id = self.get_argument('problem_id')
        part_id = int(self.get_argument('part_id'))
        user_id = self.get_argument('user_id')
        answer_string = self.get_argument('answer_string')
        pg_file = self.get_source()

        # re.compile(r'\[__+\]{(?:Compute\(")?(.+?)(?:"\))?}')
        answer_re = re.compile('\[__+\]{(?:(?:Compute\(")(.+?)(?:"\))(?:.*)|(.+?))}')
        answer_boxes = answer_re.findall(pg_file)
        part_answer = answer_boxes[part_id-1][0] or answer_boxes[part_id-1][1]
        
        # Get student's variables, parse their answer, their correct answer
        user_variables = conn.query('''SELECT * from {course}_user_variables
        WHERE set_id="{set_id}" AND problem_id = {problem_id} AND user_id = "{user_id}";
        '''.format(course=course, set_id=set_id, problem_id=problem_id, user_id=user_id))

        user_variables = {row['name']: row['value'] for row in user_variables}
        part_answer = replace_variables(user_variables, part_answer)
        
        answer_etree = parse_and_eval(part_answer)
        etree = parse_and_eval(answer_string)
        answer_data = {'attempt': answer_string, 'att_tree': etree, 'answer': part_answer,
                        'ans_tree': answer_etree, 'variables': user_variables}
        logger.debug(answer_data)
        if etree == None:
            logger.error("att_tree is None")
            return
        if answer_etree == None:
            logger.error("ans_tree is None")
            return


        # #get conditional filter functions
        # conditional_filter_funcs = conn.query('''SELECT ff.id, ff.code, af.hint_id, af.course, af.set_id,
        # af.problem_id, af.part_id FROM filter_functions as ff
        # JOIN assigned_filters as af ON af.filter_function_id = ff.id
        # WHERE af.course='{course}' AND af.set_id='{set_id}' AND af.problem_id={problem_id} AND af.part_id={part_id} AND af.function_type='C';'''.
        #                           format(course=course, set_id=set_id, problem_id=problem_id, part_id=part_id))
        # logger.info("Conditional Filter Functions:")
        # logger.info(conditional_filter_funcs)
        # #get universal filter functions
        # universal_filter_funcs = conn.query('''SELECT ff.id, ff.code, af.hint_id, af.course, af.set_id,
        # af.problem_id, af.part_id FROM filter_functions as ff
        # JOIN assigned_filters as af ON af.filter_function_id = ff.id
        # WHERE af.course='{course}' AND af.set_id='{set_id}' AND af.problem_id={problem_id} AND af.part_id={part_id} AND af.function_type='U';'''.
        #                           format(course=course, set_id=set_id, problem_id=problem_id, part_id=part_id))
        # logger.info("Universal Filter Functions:")
        # logger.info(universal_filter_funcs)
        #logger.debug('Filters: %s', filter_funcs)

        # load from folder
        con_filter_funcs, uni_filter_funcs, time_filter_funcs = load_filters_from_folder(self.filter_bank, self.filters_dir)

        txt = ""
        correct_set_problem_part = str(set_id) + "_" + str(problem_id) + "_" + str(part_id)

        logger.info(user_id)
        logger.info(correct_set_problem_part)
        success = False
        filter_function_name = ""

        for func in con_filter_funcs:
            #if func.hint_id in hints_assigned:
            #    continue
            logger.info("checking conditional")
            if correct_set_problem_part in func['name']:
                logger.info(func['name'])
                success,txt,_ = self.filter_bank.exec_filter(func['name'], answer_data) #self.exec_filter_func(func['code'], answer_data, user_variables)
                #TODO: remove the length check when things become reliable
                if txt != "" and success and len(txt) < 100:
                    filter_function_name = func['name']
                    break
                else:
                    success = False
        if not success:
            logger.info("checking universal")
            for func in uni_filter_funcs:
                #logger.info(func['name'])
                success,txt,_ = self.filter_bank.exec_filter(func['name'], answer_data)#self.exec_filter_func(func['code'], answer_data, user_variables)
                #TODO: remove the length check when things become reliable
                if txt != "" and success and len(txt) < 100:
                    filter_function_name = func['name']
                    break
                else:
                    success = False
        if not success:
            logger.info("checking timebased")
            # Only run filters if at least 3 answers and at least 3 minutes since first answer
            try:
                answer_count = conn.get('''SELECT COUNT(*) as count from {course}_answers_by_part {WHERE};'''
                                        .format(course=course, WHERE=self.where_clause('set_id', 'problem_id', 'part_id', 'user_id'))).get('count')
                first_answer = conn.get('''SELECT timestamp from {course}_answers_by_part {WHERE}
            ORDER BY timestamp ASC LIMIT 1;'''
                                        .format(course=course, WHERE=self.where_clause('set_id', 'problem_id', 'part_id', 'user_id'))).get('timestamp')
                last_answer = conn.get('''SELECT timestamp from {course}_answers_by_part {WHERE}
            ORDER BY timestamp DESC LIMIT 1;'''
                                       .format(course=course, WHERE=self.where_clause('set_id', 'problem_id', 'part_id', 'user_id'))).get('timestamp')
                diff = last_answer-first_answer
                logger.info('diff and count')
                logger.info(diff)
                logger.info(answer_count)
                if answer_count > 3 and diff > timedelta(minutes=3):
                    logger.info("sending time based")
                    for func in time_filter_funcs:
                        if correct_set_problem_part in func['name']:
                            logger.info(func['name'])
                            success,txt,_ = self.filter_bank.exec_filter(func['name'], answer_data)#self.exec_filter_func(func['code'], answer_data, user_variables)
                            #TODO: remove the length check when things become reliable
                            if txt != "" and success and len(txt) < 300:
                                filter_function_name = func['name']
                                break
                            else:
                                success = False
            except Exception, e:
                logger.warn('Error: ' + str(e))
                self.write(json.dumps({}))
                return
예제 #29
0
    def post(self):
        logger.debug('post starting')

        self.answer_exps = {}
        course = self.get_argument('course')
        set_id = self.get_argument('set_id')
        problem_id = int(self.get_argument('problem_id'))
        part_id = int(self.get_argument('part_id'))
        include_finished = (int(self.get_argument('include_finished', 1)) == 1)
        filter_function = self.get_argument('filter_function')

        pg_file = get_source(course, set_id, problem_id)
        # get the variables as set for this user. It seems that there is an alternative code for doing the same thing in the method vars_for_student
        user_variables = conn.query('''SELECT * from {course}_user_variables
        WHERE set_id="{set_id}" AND problem_id = {problem_id};
        '''.format(course=course, set_id=set_id, problem_id=problem_id))
        self.variables_df = pd.DataFrame(user_variables)
        logger.debug(
            'computing user vars. user_variables=%s, self_variables_df=%s' %
            (str(user_variables), str(self.variables_df)))
        if len(self.variables_df) == 0:
            logger.warn(
                "No user variables saved for assignment %s, please run the save_answers script",
                set_id)

        # Get the answer from pg file
        self.part_answer = get_part_answer(pg_file, part_id)

        # Get attempts by part
        if include_finished:
            query = '''SELECT * from {course}_answers_by_part
            WHERE set_id="{set_id}" AND problem_id = {problem_id}
            AND part_id={part_id};
            '''.format(course=course,
                       set_id=set_id,
                       problem_id=problem_id,
                       part_id=part_id)
        else:
            # This self join query idea comes from http://stackoverflow.com/a/4519302/90551
            # You can do this more clearly with subqueries but it's super slow
            query = '''SELECT abp.* FROM {course}_answers_by_part as abp
            LEFT JOIN {course}_answers_by_part AS abp2
            ON abp.problem_id = abp2.problem_id AND abp.set_id = abp2.set_id
            AND abp.part_id = abp2.part_id AND abp.user_id = abp2.user_id AND abp2.score = '1'
            WHERE abp.set_id='{set_id}' AND abp.problem_id={problem_id}
            AND abp.part_id={part_id} AND abp2.user_id IS NULL;
            '''.format(course=course,
                       set_id=set_id,
                       problem_id=problem_id,
                       part_id=part_id)

        logger.debug('before sending sql query')
        answers = conn.query(query)
        logger.debug('after sending sql query')

        a_filter_bank = filter_bank()
        status = a_filter_bank.add_filter('answer_filter', filter_function)
        if status != None:
            print "ERROR LOADING FUNCTION" + status
            return status

        _stdout = ''
        _hints = []
        for a in answers:
            user_id = a['user_id']
            attempt = a['answer_string']
            ptree, etree = parse_eval(attempt)
            user_vars = self.variables_df
            if len(user_vars) > 0:
                student_vars = dict(
                    user_vars[user_vars['user_id'] == user_id][[
                        'name', 'value'
                    ]].values.tolist())
            else:
                student_vars = {}
            # Replace variable with values
            for key in student_vars:
                if key in self.part_answer:
                    self.part_answer = self.part_answer.replace(
                        key, str(student_vars[key]))
            # Get the correct answer and generate a ptree and an etree for it.
            self.answer_ptree, self.answer_etree = parse_eval(self.part_answer)
            ans = self.answer_for_student(user_id)
            if ptree and etree:
                status, hint, output = a_filter_bank.exec_filter(
                    'answer_filter',
                    (attempt, ptree, etree, self.part_answer,
                     self.answer_ptree, self.answer_etree, student_vars))
                if status:
                    logger.debug(
                        'exec_filter succeeded, attempt=%s,hint=%s,output=%s' %
                        (attempt, hint, output))
                    _hints.append(hint)
                    _stdout += output
                else:
                    logger.debug(
                        'exec_filter failed attempt=%s,error=%s output=%s' %
                        (attempt, hint, output))
            else:
                logger.debug('filed to parse attempt=%s, ptree=%s, etree=%s' %
                             (attempt, str(ptree), str(etree)))

        out = {'output': _stdout, 'matches': _hints}

        self.write(json.dumps(out))
        logger.debug('finished post')
예제 #30
0
    def get(self):
        '''
            Parses all expressions for a given question

            Sample arguments:
            course='CSE103_Fall14',
            set_id='Week1',
            problem_id=1,
            part_id=1
            include_finished=0
            Response:
                ...
            '''
        # Get correct answers
        logger.debug('Starting get')
        self.answer_exps = {}
        course = self.get_argument('course')
        set_id = self.get_argument('set_id')
        problem_id = self.get_argument('problem_id')
        part_id = int(self.get_argument('part_id'))
        include_finished = (int(self.get_argument('include_finished', 1)) == 1)

        source_file = conn.query('''
            select source_file from {course}_problem
            where
                problem_id={problem_id} and
                set_id="{set_id}";
        '''.format(course=course, set_id=set_id,
                   problem_id=problem_id))[0]['source_file']

        pg_path = os.path.join(webwork_dir, 'courses', course, 'templates',
                               source_file)
        with open(pg_path, 'r') as fin:
            pg_file = fin.read()
        # name  problem_id set_id   user_id  value
        user_variables = conn.query('''SELECT * from {course}_user_variables
        WHERE set_id="{set_id}" AND problem_id = {problem_id};
        '''.format(course=course, set_id=set_id, problem_id=problem_id))
        self.variables_df = pd.DataFrame(user_variables)
        if len(self.variables_df) == 0:
            logger.warn(
                "No user variables saved for assignment %s, please run the save_answers script",
                set_id)
            # raise tornado.web.HTTPError(500)
        answer_re = re.compile('\[__+\]{(?:Compute\(")?(.+?)(?:"\))?}')
        answer_boxes = answer_re.findall(pg_file)
        self.part_answer = answer_boxes[part_id - 1]
        self.answer_ptree = parse_webwork(self.part_answer)

        # Get attempts by part
        if include_finished:
            query = '''SELECT * from {course}_answers_by_part
            WHERE set_id="{set_id}" AND problem_id = {problem_id}
            AND part_id={part_id};
            '''.format(course=course,
                       set_id=set_id,
                       problem_id=problem_id,
                       part_id=part_id)
        else:
            # This self join query idea comes from http://stackoverflow.com/a/4519302/90551
            # You can do this more clearly with subqueries but it's super slow
            query = '''SELECT abp.* FROM {course}_answers_by_part as abp
            LEFT JOIN {course}_answers_by_part AS abp2
            ON abp.problem_id = abp2.problem_id AND abp.set_id = abp2.set_id
            AND abp.part_id = abp2.part_id AND abp.user_id = abp2.user_id AND abp2.score = '1'
            WHERE abp.set_id='{set_id}' AND abp.problem_id={problem_id}
            AND abp.part_id={part_id} AND abp2.user_id IS NULL;
            '''.format(course=course,
                       set_id=set_id,
                       problem_id=problem_id,
                       part_id=part_id)

        logger.debug('Before sending SQL')
        answers = conn.query(query)
        logger.debug('After sending SQL')

        all_correct_terms = set()
        correct_terms_map = defaultdict(lambda: defaultdict(list))
        incorrect_terms_map = defaultdict(lambda: defaultdict(list))
        # Parse and evaluate all answers
        for a in answers:
            # {'user_id': u'acpatel', 'timestamp': datetime.datetime(2014, 10, 12, 2, 10, 19), 'id': 284488L, 'score': u'1', 'answer_string': u'C(55,6)', 'part_id': 2L, 'problem_id': 10L, 'set_id': u'Week2', 'answer_id': 199326L}
            user_id = a['user_id']
            ans = self.answer_for_student(user_id)

            if a['score'] != '1':
                etree, nums = parsed(a['answer_string'])
                if etree and nums:
                    correct_terms = self.correct_terms(nums, ans)
                    all_correct_terms |= set(correct_terms)
                    # logger.debug(set(correct_terms))
                    if a['user_id'] not in correct_terms_map[str(
                            sorted(correct_terms))][a['answer_string']]:
                        correct_terms_map[str(
                            sorted(correct_terms))][a['answer_string']].append(
                                a['user_id'])

        out = {}
        out['correct'] = correct_terms_map
        out['correct_terms'] = sorted(all_correct_terms)
        out['incorrect'] = incorrect_terms_map
        out['answer_ptree'] = str(self.answer_ptree)
        self.write(json.dumps(out, default=serialize_datetime))
        logger.debug('Finished')
    def post(self):
        """
        Tests one student's answer against the filters defined for the problem part.
        For any filters which match, returns the matched hint(PGML) which should be inserted into the hint.
        """
        course = self.get_argument('course')
        set_id = self.get_argument('set_id')
        problem_id = self.get_argument('problem_id')
        part_id = int(self.get_argument('part_id'))
        user_id = self.get_argument('user_id')
        answer_string = self.get_argument('answer_string')
        pg_file = self.get_source()

        # re.compile(r'\[__+\]{(?:Compute\(")?(.+?)(?:"\))?}')
        answer_re = re.compile('\[__+\]{(?:(?:Compute\(")(.+?)(?:"\))(?:.*)|(.+?))}')
        answer_boxes = answer_re.findall(pg_file)
        part_answer = answer_boxes[part_id-1][0] or answer_boxes[part_id-1][1]
        
        # Get student's variables, parse their answer, their correct answer
        user_variables = conn.query('''SELECT * from {course}_user_variables
        WHERE set_id="{set_id}" AND problem_id = {problem_id} AND user_id = "{user_id}";
        '''.format(course=course, set_id=set_id, problem_id=problem_id, user_id=user_id))

        user_variables = {row['name']: row['value'] for row in user_variables}
        # Replace variable with values
        for var in user_variables:
            if var['name'] in part_answer:
                part_answer = part_answer.replace(var['name'], str(var['value']))
        
        answer_ptree, answer_etree = parse_and_eval(part_answer, user_variables)
        ptree, etree = parse_and_eval(answer_string)
        answer_data = {'attemp': answer_string, 'attemp_tree': ptree, 'answer': part_answer,
                        'answer_tree': answer_ptree, 'variables': user_variables}
        # #get conditional filter functions
        # conditional_filter_funcs = conn.query('''SELECT ff.id, ff.code, af.hint_id, af.course, af.set_id,
        # af.problem_id, af.part_id FROM filter_functions as ff
        # JOIN assigned_filters as af ON af.filter_function_id = ff.id
        # WHERE af.course='{course}' AND af.set_id='{set_id}' AND af.problem_id={problem_id} AND af.part_id={part_id} AND af.function_type='C';'''.
        #                           format(course=course, set_id=set_id, problem_id=problem_id, part_id=part_id))
        # logger.info("Conditional Filter Functions:")
        # logger.info(conditional_filter_funcs)
        # #get universal filter functions
        # universal_filter_funcs = conn.query('''SELECT ff.id, ff.code, af.hint_id, af.course, af.set_id,
        # af.problem_id, af.part_id FROM filter_functions as ff
        # JOIN assigned_filters as af ON af.filter_function_id = ff.id
        # WHERE af.course='{course}' AND af.set_id='{set_id}' AND af.problem_id={problem_id} AND af.part_id={part_id} AND af.function_type='U';'''.
        #                           format(course=course, set_id=set_id, problem_id=problem_id, part_id=part_id))
        # logger.info("Universal Filter Functions:")
        # logger.info(universal_filter_funcs)
        #logger.debug('Filters: %s', filter_funcs)

        # load from folder
        self.filter_bank = filter_bank()
        basepath = os.path.dirname(__file__)
        logger.info(basepath)
        filters_path = os.path.join(basepath, "../filters/")
        self.filter_bank.import_filters_from_files(filters_path)
        files = self.filter_bank.get_env_keys()
        con_filter_funcs = []
        uni_filter_funcs = []
        time_filter_funcs = []
        for f in files:
            if f[0] != '_':
                ### remove the doc string from code ###
                code = self.filter_bank.get_code(filters_path, f)
                while '\"\"\"' in code:
                    code = code[code.index('\"\"\"')+3:]
            if f[0] == "C":
                con_filter_funcs += [{'name': f, 'code': code, 'doc': self.filter_bank.get_docstring(f)}]
            elif f[0] == "U":
                uni_filter_funcs += [{'name': f, 'code': code, 'doc': self.filter_bank.get_docstring(f)}]
            elif f[0] == "T":
                time_filter_funcs += [{'name': f, 'code': code, 'doc': self.filter_bank.get_docstring(f)}]

        txt = None
        for func in con_filter_funcs:
            #if func.hint_id in hints_assigned:
            #    continue
            _,txt,_ = self.filter_bank.exec_filter(func['name'], answer_data) #self.exec_filter_func(func['code'], answer_data, user_variables)
            if txt:
                break
        if not txt:
            for func in uni_filter_funcs:
                _,txt,_ = self.filter_bank.exec_filter(func['name'], answer_data)#self.exec_filter_func(func['code'], answer_data, user_variables)
                if txt:
                    break
        if not txt:
            # Only run filters if at least 3 answers and at least 5 minutes since first answer
            try:
                answer_count = conn.get('''SELECT COUNT(*) as count from {course}_answers_by_part {WHERE};'''
                                        .format(course=course, WHERE=self.where_clause('set_id', 'problem_id', 'part_id', 'user_id'))).get('count')
                first_answer = conn.get('''SELECT timestamp from {course}_answers_by_part {WHERE}
            ORDER BY timestamp ASC LIMIT 1;'''
                                        .format(course=course, WHERE=self.where_clause('set_id', 'problem_id', 'part_id', 'user_id'))).get('timestamp')
                last_answer = conn.get('''SELECT timestamp from {course}_answers_by_part {WHERE}
            ORDER BY timestamp DESC LIMIT 1;'''
                                       .format(course=course, WHERE=self.where_clause('set_id', 'problem_id', 'part_id', 'user_id'))).get('timestamp')
                diff = last_answer-first_answer
                if answer_count > 3 and diff > timedelta(minutes=5):
                    for func in time_filter_funcs:
                        _,txt,_ = self.filter_bank.exec_filter(func['name'], answer_data)#self.exec_filter_func(func['code'], answer_data, user_variables)
                        if txt:
                            break
            except Exception, e:
                logger.warn('Error: ' + e)
                self.write(json.dumps({}))
                return