Exemple #1
0
 def load_file_submissions(self, scripts):
     # Get instructor control scripts
     all_scripts = []
     for script in scripts:
         script_file_name, script_file_extension = os.path.splitext(script)
         # Single Python file
         if script_file_extension in ('.py', ):
             with open(script, 'r') as scripts_file:
                 scripts_contents = scripts_file.read()
             all_scripts.append((script, scripts_contents))
     given_submissions = self.config.submissions
     # If submission is a directory, use it as a directory adjacent to each ics
     if os.path.isdir(given_submissions):
         for script, scripts_contents in all_scripts:
             directory_pattern = given_submissions
             submission_dir = normalize_path(directory_pattern, script)
             submission_files = [
                 os.path.join(submission_dir, sub)
                 for sub in os.listdir(submission_dir)
             ]
             subs = get_python_files(submission_files)
             for main_file, main_code in subs.items():
                 new_submission = Submission(main_file=main_file,
                                             main_code=main_code,
                                             instructor_file=script)
                 self.submissions.append(
                     Bundle(self.config, scripts_contents, new_submission))
     # Otherwise, if the submission is a single file:
     # Maybe it's a Progsnap DB file?
     elif given_submissions.endswith('.db'):
         for script, scripts_contents in all_scripts:
             self.load_progsnap(given_submissions,
                                instructor_code=scripts_contents)
     # Otherwise, must just be a single python file.
     else:
         main_file = given_submissions
         load_error = None
         try:
             with open(given_submissions, 'r') as single_submission_file:
                 main_code = single_submission_file.read()
         except OSError as e:
             # Okay, file does not exist. Load error gets triggered.
             main_code = None
             load_error = e
         for script, scripts_contents in all_scripts:
             new_submission = Submission(main_file=main_file,
                                         main_code=main_code,
                                         instructor_file=script,
                                         load_error=load_error)
             self.submissions.append(
                 Bundle(self.config, scripts_contents, new_submission))
Exemple #2
0
def contextualize_report(submission,
                         filename='answer.py',
                         clear=True,
                         report=MAIN_REPORT):
    """
    Updates the report with the submission. By default, clears out any old
    information in the report. You can pass in either an actual
    :py:class:`~pedal.core.submission.Submission` or a string representing
    the code of the submission.

    Args:
        submission (str or Submission):
        filename (str or None): If the `submission` was not a
            :py:class:`~pedal.core.submission.Submission`, then this will be
            used as the filename for the code given in ``submission``.
        clear (bool): Whether or not to clear the report before attaching
            the submission.
        report: The report to attach this feedback to (defaults to the
            :py:data:`~pedal.core.report.MAIN_REPORT`).
    """
    if not isinstance(submission, Submission):
        submission = Submission(files={filename: submission})
    if clear:
        report.clear()
    report.contextualize(submission)
Exemple #3
0
 def load_submissions(self):
     # Use first argument as student submissions
     given_script = self.config.instructor
     # ... either a single file
     if os.path.isfile(given_script):
         scripts = [given_script]
     # ... or a directory of files
     else:
         scripts = os.listdir(given_script)
     # Then create submission to run
     for script in scripts:
         script_file_name, script_file_extension = os.path.splitext(script)
         if script_file_extension in ('.py', ):
             load_error = None
             try:
                 with open(script, 'r') as scripts_file:
                     scripts_contents = scripts_file.read()
             except Exception as e:
                 load_error = e
             new_submission = Submission(main_file="answer.py",
                                         main_code=scripts_contents,
                                         instructor_file=script,
                                         load_error=load_error)
             self.submissions.append(
                 Bundle(self.config, self.ICS, new_submission))
Exemple #4
0
 def load_progsnap(self, path, instructor_code=None):
     script_file_name, script_file_extension = os.path.splitext(path)
     if script_file_extension in ('.db', ):
         with SqlProgSnap2(path, cache=self.config.cache) as progsnap:
             if self.config.progsnap_profile:
                 progsnap.set_profile(self.config.progsnap_profile)
             link_filters = {}
             include_scripts = self.config.include_scripts
             if include_scripts:
                 link_filters['Assignment'] = {}
                 if include_scripts.startswith('name='):
                     link_filters['Assignment']['X-Name'] = include_scripts[
                         5:]
                 elif include_scripts.startswith('id='):
                     link_filters['Assignment'][
                         'AssignmentId'] = include_scripts[3:]
                 elif include_scripts.startswith('url='):
                     link_filters['Assignment']['X-URL'] = include_scripts[
                         4:]
                 else:
                     link_filters['Assignment']['X-URL'] = include_scripts
             event_type = self.progsnap_events_map[
                 self.config.progsnap_events]
             events = progsnap.get_events(
                 event_filter={'EventType': event_type},
                 link_filters=link_filters,
                 limit=self.config.limit)
         if self.config.progsnap_events == 'last':
             events = [
                 e for e in
                 {(event['student_email'], event['assignment_name']): event
                  for event in sorted(events, key=lambda e: e['event_id'])
                  }.values()
             ]
         for event in events:
             if instructor_code is None:
                 scripts_contents = event['on_run']
             new_submission = Submission(
                 main_file='answer.py',
                 main_code=event['submission_code'].decode('utf-8'),
                 instructor_file='instructor.py',
                 execution=dict(client_timestamp=event['client_timestamp'],
                                event_id=event['event_id']),
                 user=dict(email=event['student_email'],
                           first=event['student_first'],
                           last=event['student_last']),
                 assignment=dict(name=event['assignment_name'],
                                 url=event['assignment_url']),
             )
             self.submissions.append(
                 Bundle(self.config, instructor_code, new_submission))
         # raise ValueError("TODO: ProgSnap DB files not yet supported")
         # Progsnap Zip
     elif script_file_extension in ('.zip', ):
         raise ValueError("TODO: Zip files not yet supported")
Exemple #5
0
 def __init__(self,
              files=None,
              main_file='answer.py',
              main_code=None,
              user=None,
              assignment=None,
              course=None,
              execution=None,
              instructor_file='instructor.py',
              report=MAIN_REPORT):
     self.report = report
     report.clear()
     # Setup any code given as the submission.
     if isinstance(files, Submission):
         self.submission = files
     else:
         load_error = None
         if files is None:
             if main_code is None:
                 if main_file is None:
                     raise ValueError(
                         "files, main_code, and main_file cannot all be None."
                     )
                 else:
                     main_code = self.load_main(main_file)
                     if isinstance(main_code, Exception):
                         load_error = main_code
                         main_code = ""
             files = {main_file: main_code}
         else:
             if main_file is not None:
                 if main_code is None:
                     main_code = files[main_file]
                 if main_file not in files:
                     files[main_file] = main_code
         self.submission = Submission(files,
                                      main_file,
                                      main_code,
                                      user,
                                      assignment,
                                      course,
                                      execution,
                                      instructor_file,
                                      load_error=load_error)
     # Contextualize report
     self.report.contextualize(self.submission)
     self.fields = {}
Exemple #6
0
    def test_unittest(self):
        student_code = dedent('''
            x = 0
        ''')
        student = Sandbox()
        student.run(student_code)
        student.call('x')
        self.assertIsNotNone(student.exception)

        student_code = dedent('''
        class Fruit:
            def __init__(self, name, weight=0):
                self.name = name
                self.weight = weight
        def do_math(a, b):
            return a + b - 5
        def weigh_fruits(fruits):
            return sum(fruit.weight for fruit in fruits)    
        ''')
        student.report.contextualize(Submission(main_code=student_code))
        student = Sandbox()
        student.run()
        result = student.call('do_math', 15, 20)
        self.assertEqual(result, 30)
        self.assertEqual(['do_math(15, 20)'],
                         [context.code for context in student.get_context()])
        banana = student.call('Fruit', "Banana")
        self.assertIsInstance(banana, student.data['Fruit'])
        self.assertEqual(["Fruit('Banana')"],
                         [context.code for context in student.get_context()])
        student.start_grouping_context()
        student.run()
        orange = student.call("Fruit", "Orange", 30, target="orange")
        self.assertIsInstance(orange, student.data['Fruit'])
        student.call("Fruit", "Pineapple", 60, target="pineapple")
        student.run("fruits = [orange, pineapple]")
        total_weight = student.call('weigh_fruits', args_locals=["fruits"])
        self.assertEqual(total_weight, 90)
        self.assertEqual([
            student_code, "orange = Fruit('Orange', 30)",
            "pineapple = Fruit('Pineapple', 60)",
            "fruits = [orange, pineapple]", "weigh_fruits(fruits)"
        ], [context.code for context in student.get_context()])
Exemple #7
0
def set_source(code,
               filename=DEFAULT_STUDENT_FILENAME,
               sections=False,
               independent=False,
               report=MAIN_REPORT):
    """
    Sets the contents of the Source to be the given code. Can also be
    optionally given a filename. If there is no existing Submission,
    this contextualizes the Report with a new Submission containing the given
    code. Otherwise, it creates a Substitution and temporarily replaces
    the Submission's current main code.

    Args:
        code (str): The contents of the source file.
        filename (str): The filename of the students' code. Defaults to
                        `answer.py`.
        sections (str or bool): Whether or not the file should be divided into
                                sections. If a str, then it should be a
                                Python regular expression for how the sections
                                are separated. If False, there will be no
                                sections. If True, then the default pattern
                                will be used: '^##### Part (\\d+)$'
        independent (bool): Whether the separate sections should be considered
            separate or all existing in an accumulating namespace.
        report (Report): The report object to store data and feedback in. If
                         left None, defaults to the global MAIN_REPORT.
    """
    if report.submission is None:
        report.contextualize(Submission({filename: code}, filename, code))
    else:
        backup = Substitution(report.submission.main_code,
                              report.submission.main_file)
        report[TOOL_NAME]['substitutions'].append(backup)
        report.submission.replace_main(code, filename)

    report[TOOL_NAME]['independent'] = independent
    report[TOOL_NAME]['success'] = True
    if not sections:
        report[TOOL_NAME]['sections'] = None
        report[TOOL_NAME]['section'] = None
        verify(code, report=report)
    else:
        separate_into_sections(report=report)
Exemple #8
0
 def load_file_submissions(self, scripts):
     # Get instructor control scripts
     all_scripts = []
     for script in scripts:
         script_file_name, script_file_extension = os.path.splitext(script)
         # Single Python file
         if script_file_extension in ('.py',):
             with open(script, 'r') as scripts_file:
                 scripts_contents = scripts_file.read()
             all_scripts.append((script, scripts_contents))
     given_submissions = self.config.submissions
     # If submission is a directory, use it as a directory adjacent to each ics
     if os.path.isdir(given_submissions):
         for script, scripts_contents in all_scripts:
             directory_pattern = given_submissions
             submission_dir = normalize_path(directory_pattern, script)
             submission_files = [
                 os.path.join(submission_dir, sub)
                 for sub in os.listdir(submission_dir)
             ]
             subs = get_python_files(submission_files)
             for main_file, main_code in subs.items():
                 new_submission = Submission(
                     main_file=main_file, main_code=main_code,
                     instructor_file=script
                 )
                 self.submissions.append(Bundle(self.config, scripts_contents, new_submission))
     # Otherwise, if the submission is a single file:
     # Maybe it's a Progsnap DB file?
     elif given_submissions.endswith('.db'):
         for script, scripts_contents in all_scripts:
             self.load_progsnap(given_submissions, instructor_code=scripts_contents)
     # Otherwise, must just be a single python file.
     else:
         main_file = given_submissions
         load_error, possible_load_error = None, None
         alternatives = [given_submissions]
         # if alternative filenames given, we'll queue them up
         if self.config.alternate_filenames:
             alternatives.extend(find_possible_filenames(self.config.alternate_filenames))
         # Run through all possible filenames
         for possible in alternatives:
             try:
                 with open(possible, 'r') as single_submission_file:
                     main_code = single_submission_file.read()
                     main_file = possible
                 break
             except OSError as e:
                 # Only capture the first possible load error
                 if possible_load_error is None:
                     possible_load_error = e
         else:
             # Okay, file does not exist. Load error gets triggered.
             main_code = None
             load_error = possible_load_error
         for script, scripts_contents in all_scripts:
             new_submission = Submission(
                 main_file=main_file, main_code=main_code,
                 instructor_file=script, load_error=load_error
             )
             self.submissions.append(Bundle(self.config, scripts_contents, new_submission))
         return load_error
Exemple #9
0
 def __init__(self, code):
     contextualize_report(Submission({'answer.py': code}))
     self.student = run()
Exemple #10
0
"""
This file is meant to be an idealized example of Pedal with a completely generic autograding environment that
wants to customize everything without actually changing anything.
"""
from pedal.core.submission import Submission
from pedal.environments.autograder import setup_pedal
from pedal.core.resolver import Resolver

autograder = setup_pedal()
student_submission = Submission(
    files={'answer.py': 'print("Hello world!")'},
    user={"name": "Ada Lovelace"},
    assignment={"name": "#24.3 List Indexing in Functions"},
    course={"name": "Introduction to Computer Science"})

contextualize_report(student_submission)

from pedal.source import validate
validate()

resolve()