pykubegrader.build package#
Submodules#
pykubegrader.build.api_notebook_builder module#
- class pykubegrader.build.api_notebook_builder.FastAPINotebookBuilder(notebook_path: str, temp_notebook: str | None = None, assignment_tag: str = '', require_key: bool = False, verbose: bool = False)[source]#
Bases:
object
- static add_import_statements_to_tests(cell_source: list[str], require_key: bool = False, assignment_tag=None) list[str] [source]#
Adds the necessary import statements to the first cell of the notebook.
- static add_text_after_double_hash(markdown_source, insert_text, hash_prefix='## ')[source]#
Adds insert_text immediately after the first ‘##’ in the first line that starts with ‘##’.
Args: - markdown_source (list of str): The list of lines in the markdown cell. - insert_text (str): The text to be inserted.
Returns: - list of str: The modified markdown cell content.
- static conceal_tests(cell_source)[source]#
Takes a list of code lines, detects blocks between # BEGIN HIDE and # END HIDE, encodes them in Base64, and replaces them with an exec() statement.
Returns a new list of lines with the concealed blocks.
- extract_assertion_test_source(cell, source)[source]#
Extracts assertion test information from a given code cell source.
This method processes the source code of a Jupyter notebook cell to extract logging variables, assertions, comments, and point values associated with test configurations. It identifies and processes assertion statements, ensuring proper handling of multi-line assertions and comments.
- Parameters:
- Returns:
- A tuple containing:
logging_variables (list): A list of variables used for logging.
assertions (list): A list of assertion statements extracted from the source.
comments (list): A list of comments associated with the assertions.
points_value (float or None): The point value extracted from the source, or None if not found.
- Return type:
- Raises:
ValueError – If the points value cannot be converted to a float.
- static extract_log_variables(cell: dict) list[str] [source]#
Extracts log variables from the first cell.
- static extract_question_information(source: str) tuple[str, str, str] [source]#
Extracts question information from the given source string.
- find_first_markdown_cell_with(start_index: int, end_index: int = 0, code_to_find: str = '## ')[source]#
Finds the first markdown cell going backwards from the given start_index to the given end_index where the first line starts with ‘##’.
Args: - start_index (int): The index to start searching from. - end_index (int): The index to stop searching at.
Returns: - tuple: The index of the found markdown cell and its source content.
- static find_last_import_line(cell_source: list[str]) int [source]#
Finds the index of the last line with an import statement in a list of code lines, including multiline import statements.
- static get_question_points_by_part(question_dict: dict) dict [source]#
Get the points for each part of a question.
- static insert_list_at_index(original_list: list[str], insert_list: list[str], index: int, line_break: bool = True, inplace_line_break: bool = True) list[str] [source]#
Inserts a list into another list at a specific index.
- question_dict() dict [source]#
Builds a dictionary of question information from the notebook.
- Returns:
A dictionary containing question information.
- Return type:
- replace_cell_source(cell_index: int, new_source: str | list[str]) None [source]#
Replace the source code of a specific Jupyter notebook cell.
- static split_list_at_marker(input_list: list[str], marker: str = '# END TEST CONFIG') tuple[list[str], list[str]] [source]#
Splits a list into two parts at the specified marker string.
- Parameters:
- Returns:
- A tuple containing two lists. The first list contains the elements
before the marker, and the second list contains the elements after the marker (excluding the marker itself).
- Return type:
pykubegrader.build.build_folder module#
- class pykubegrader.build.build_folder.NotebookProcessor(root_folder: str, assignment_tag: str = '', verbose: bool = False, log: bool = True, require_key: bool = False, bonus_points: float = 0)[source]#
Bases:
object
A class for processing Jupyter notebooks in a directory and its subdirectories.
- add_final_submission_cells(notebook_path: str, output_path: str) None [source]#
Adds final submission cells to the end of a Jupyter notebook.
- static add_initialization_code(notebook_path, week, assignment_type, require_key=False, **kwargs)[source]#
- add_submission_cells(notebook_path: str, output_path: str) None [source]#
Adds submission cells to the end of a Jupyter notebook.
- static add_validate_block(notebook_path: str, require_key: bool, assignment_tag=None, **kwargs) None [source]#
Modifies the first code cell of a Jupyter notebook to add the validate_token call if require_key is True.
- static add_validate_token_cell(notebook_path: str, require_key: bool, **kwargs) None [source]#
Adds a new code cell at the top of a Jupyter notebook if require_key is True.
- build_payload(yaml_content)[source]#
Reads YAML content for an assignment and returns Python variables.
- extract_MCQ()[source]#
Extracts questions from markdown cells and organizes them as a nested dictionary, including subquestion numbers.
- static generate_solution_MCQ(data_list, output_file='output.py')[source]#
Generates a Python file with solutions and total points based on the input data. If the file already exists, it appends new solutions to the existing solution dictionary.
- static has_assignment(notebook_path, *tags)[source]#
Determines if a Jupyter notebook contains any of the specified configuration tags.
This method checks for the presence of specific content in a Jupyter notebook to identify whether it includes any of the required headings or tags.
- Parameters:
- Returns:
True if the notebook contains any of the specified tags, False otherwise.
- Return type:
- Dependencies:
The check_for_heading function must be implemented. It should search
for specific headings or content in a notebook file and return a boolean value indicating if any of the tags exist.
Example
- def check_for_heading(notebook_path, keywords):
# Mock implementation of content check with open(notebook_path, ‘r’) as file:
content = file.read()
return any(keyword in content for keyword in keywords)
notebook_path = “path/to/notebook.ipynb” # Check for default tags contains_config = has_assignment(notebook_path) self._print_and_log(f”Contains assignment config: {contains_config}”)
# Check for custom tags contains_custom = has_assignment(notebook_path, “# CUSTOM CONFIG”, “# ANOTHER CONFIG”) self._print_and_log(f”Contains custom config: {contains_custom}”)
- static merge_metadata(raw, data)[source]#
Merges raw metadata with extracted question data.
This method combines metadata from two sources: raw metadata and question data. It ensures that the points associated with each question are appropriately distributed and added to the final merged metadata.
- Parameters:
raw (list) – A list of dictionaries containing raw metadata. Each dictionary must have a ‘points’ key with a value that can be either a list of points or a string representing a single point value.
data (list) – A list of dictionaries containing extracted question data. Each dictionary represents a set of questions and their details.
- Returns:
- A list of dictionaries where each dictionary represents a question
with merged metadata and associated points.
- Return type:
- Raises:
KeyError – If ‘points’ is missing from any raw metadata entry.
IndexError – If the number of items in raw and data do not match.
Example
- raw = [
{“points”: [1.0, 2.0]}, {“points”: “3.0”}
] data = [
{“Q1”: {“question_text”: “What is 2+2?”}}, {“Q2”: {“question_text”: “What is 3+3?”}}
] merged = merge_metadata(raw, data) print(merged) # Output: # [ # {“Q1”: {“question_text”: “What is 2+2?”, “points”: 1.0}}, # {“Q2”: {“question_text”: “What is 3+3?”, “points”: 3.0}} # ]
- process_notebooks()[source]#
Recursively processes Jupyter notebooks in a given folder and its subfolders.
The function performs the following steps: 1. Iterates through all files within the root folder and subfolders. 2. Identifies Jupyter notebooks by checking file extensions (.ipynb). 3. Checks if each notebook contains assignment configuration metadata. 4. Processes notebooks that meet the criteria using otter assign or other defined steps.
- Prerequisites:
The has_assignment method should be implemented to check if a notebook
contains the required configuration for assignment processing. - The _process_single_notebook method should handle the specific processing of a single notebook, including moving it to a new folder or running additional tools like otter assign.
- Raises:
- OSError – If an issue occurs while accessing files or directories.
Example
- class NotebookProcessor:
- def __init__(self, root_folder):
self.root_folder = root_folder
- def has_assignment(self, notebook_path):
# Implementation to check for assignment configuration return True # Replace with actual check logic
- def _process_single_notebook(self, notebook_path):
# Implementation to process a single notebook self._print_and_log(f”Processing notebook: {notebook_path}”)
processor = NotebookProcessor(“/path/to/root/folder”) processor.process_notebooks()
- static remove_empty_cells(notebook_path, output_path=None)[source]#
Removes empty cells from a Jupyter Notebook and saves the updated notebook.
- static replace_temp_in_notebook(input_file, output_file)[source]#
Replaces occurrences of ‘_temp.ipynb’ with ‘.ipynb’ in a Jupyter Notebook.
Parameters: input_file (str): Path to the input Jupyter Notebook file. output_file (str): Path to the output Jupyter Notebook file.
Returns: None: Writes the modified notebook to the output file.
- pykubegrader.build.build_folder.check_for_heading(notebook_path, search_strings)[source]#
Checks if a Jupyter notebook contains a heading cell whose source matches any of the given strings.
- pykubegrader.build.build_folder.clean_notebook(notebook_path)[source]#
Removes specific cells and makes Markdown cells non-editable and non-deletable by updating their metadata.
- pykubegrader.build.build_folder.ensure_imports(output_file, header_lines)[source]#
Ensures specified header lines are present at the top of the file.
- pykubegrader.build.build_folder.extract_MCQ(ipynb_file)[source]#
Extracts multiple-choice questions from markdown cells within sections marked by # BEGIN MULTIPLE CHOICE and # END MULTIPLE CHOICE.
- Parameters:
ipynb_file (str) – Path to the .ipynb file.
- Returns:
- A list of dictionaries, where each dictionary corresponds to questions within
a section. Each dictionary contains parsed questions with details like ‘name’, ‘subquestion_number’, ‘question_text’, ‘OPTIONS’, and ‘solution’.
- Return type:
- pykubegrader.build.build_folder.extract_SELECT_MANY(ipynb_file)[source]#
Extracts questions marked by # BEGIN SELECT MANY and # END SELECT MANY in markdown cells, including all lines under the SOLUTION header until the first blank line or whitespace-only line.
- pykubegrader.build.build_folder.extract_TF(ipynb_file)[source]#
Extracts True False questions from markdown cells within sections marked by # BEGIN TF and # END TF.
- pykubegrader.build.build_folder.extract_config_from_notebook(notebook_path)[source]#
Extract configuration text from a Jupyter Notebook.
- pykubegrader.build.build_folder.extract_files(config_text)[source]#
Extract the list of files from the given configuration text, excluding .bin files.
- pykubegrader.build.build_folder.extract_raw_cells(ipynb_file, heading='# BEGIN MULTIPLE CHOICE')[source]#
Extracts all metadata from value cells in a Jupyter Notebook file for a specified heading.
- pykubegrader.build.build_folder.find_first_code_cell(notebook_path)[source]#
Finds the first Python code cell in a Jupyter notebook and its index.
- pykubegrader.build.build_folder.generate_mcq_file(data_dict, output_file='mc_questions.py')[source]#
Generates a Python file defining an MCQuestion class from a dictionary.
- pykubegrader.build.build_folder.generate_select_many_file(data_dict, output_file='select_many_questions.py')[source]#
Generates a Python file defining an MCQuestion class from a dictionary.
- pykubegrader.build.build_folder.generate_tf_file(data_dict, output_file='tf_questions.py')[source]#
Generates a Python file defining an MCQuestion class from a dictionary.
- pykubegrader.build.build_folder.replace_cell_source(notebook_path, cell_index, new_source)[source]#
Replace the source code of a specific Jupyter notebook cell.
- pykubegrader.build.build_folder.replace_cells_between_markers(data, markers, ipynb_file, output_file)[source]#
Replaces the cells between specified markers in a Jupyter Notebook (.ipynb file) with provided replacement cells and writes the result to the output file.
Parameters: data (list): A list of dictionaries with data for creating replacement cells. markers (tuple): A tuple containing two strings: the BEGIN and END markers. ipynb_file (str): Path to the input Jupyter Notebook file. output_file (str): Path to the output Jupyter Notebook file.
Returns: None: Writes the modified notebook to the output file.
- pykubegrader.build.build_folder.sanitize_string(input_string)[source]#
Converts a string into a valid Python variable name.
pykubegrader.build.clean_folder module#
pykubegrader.build.collate module#
- class pykubegrader.build.collate.QuestionCollator(root_folder: str, output_path: str)[source]#
Bases:
object
- collate_questions()[source]#
Collates questions from all solution folders and saves them to a new notebook.
- create_collated_notebook(questions)[source]#
Creates a new notebook with questions organized by type.
- Parameters:
questions (dict) – Dictionary of categorized questions.
- Returns:
The collated notebook.
- Return type:
Notebook
- extract_questions(folder_path)[source]#
Extracts questions from all notebooks in the solution folder.