Source code for pedal.core.commands

"""
Imperative style commands for constructing feedback in a convenient way.
Uses a global report object (MAIN_REPORT).
"""

__all__ = ['feedback', 'set_success', 'set_correct', 'compliment', 'give_partial', 'explain',
           'gently', 'hide_correctness', 'suppress', 'log', 'debug',
           'system_error', 'clear_report', 'get_all_feedback', 'guidance',
           'contextualize_report', 'Feedback', 'get_submission', 'set_formatter']

from pedal.core.feedback import (Feedback, FeedbackKind, FeedbackCategory,
                                 FeedbackResponse)
from pedal.core.report import MAIN_REPORT

from pedal.core.submission import Submission

#: Lowercase "function" version that works like other Core Feedback Functions.
feedback = Feedback


# TODO: force_success function, which ignores errors to give the points.

[docs] class set_correct(FeedbackResponse): """ **(Feedback Function)** Creates Successful feedback for the user, indicating that the entire assignment is done. """ title = "Complete" message_template = "Great work!" score = 1 correct = True category = FeedbackCategory.COMPLETE kind = FeedbackKind.RESULT valence = Feedback.POSITIVE_VALENCE
set_success = set_correct
[docs] class compliment(FeedbackResponse): """ Create a positive feedback for the user, potentially on a specific line of code. """ category = FeedbackCategory.INSTRUCTOR kind = FeedbackKind.COMPLIMENT valence = Feedback.POSITIVE_VALENCE correct = True def __init__(self, message=None, title=None, message_template=None, **kwargs): if title is None: title = message if message is None and message_template is None: raise ValueError("compliment requires at least either message or message_template") super().__init__(message=message, title=title, message_template=message_template, **kwargs)
[docs] class give_partial(FeedbackResponse): """ Increases the user's current score by the `score`. """ title = "Partial Credit" message_template = "Partial credit" category = FeedbackCategory.INSTRUCTOR kind = FeedbackKind.RESULT valence = Feedback.POSITIVE_VALENCE correct = None muted = True def __init__(self, value, **kwargs): super().__init__(score=value, **kwargs)
[docs] class explain(FeedbackResponse): """ Give a high-priority piece of negative feedback to the student. """ title = "Instructor Feedback" muted = False category = Feedback.CATEGORIES.INSTRUCTOR kind = FeedbackKind.MISTAKE valence = Feedback.NEGATIVE_VALENCE def __init__(self, message=None, message_template=None, **kwargs): if message is None and message_template is None: raise ValueError("explain requires at least either message or message_template") super().__init__(message=message, message_template=message_template, **kwargs)
[docs] class gently(FeedbackResponse): """ Give a low-priority piece of negative feedback to the student. Args: message (str): The feedback message to show to the student. """ title = "Instructor Feedback" priority = Feedback.CATEGORIES.STUDENT muted = False category = Feedback.CATEGORIES.INSTRUCTOR kind = FeedbackKind.MISTAKE valence = Feedback.NEGATIVE_VALENCE def __init__(self, message=None, message_template=None, **kwargs): if message is None and message_template is None: raise ValueError("gently requires at least either message or message_template") super().__init__(message=message, message_template=message_template, **kwargs)
[docs] class guidance(FeedbackResponse): """ Give instructions about a question. """ title = "Instructor Guidance" category = Feedback.CATEGORIES.INSTRUCTIONS kind = FeedbackKind.INSTRUCTIONAL valence = Feedback.NEUTRAL_VALENCE def __init__(self, message=None, message_template=None, **kwargs): if message is None and message_template is None: raise ValueError("compliment requires at least either message or message_template") super().__init__(message=message, message_template=message_template, **kwargs)
[docs] def hide_correctness(report=MAIN_REPORT): """ Force the report to not indicate score/correctness. Args: report (:py:class:`pedal.core.report.Report`): The report object to hide correctness on. """ report.hide_correctness()
[docs] def suppress(category=None, label=True, fields=None, report=MAIN_REPORT): """ Hides a given category or label within a category from being considered by the resolver. Args: category (str): The general feedback category to suppress within. Should be a member of :py:class:`pedal.core.feedback_category.FeedbackCategory`. label (str or bool): The specific feedback label to suppress, or True if all the labels within this category should be suppressed. fields (dict): The fields that will be exactly matched to suppress a given feedback. The keys should be strings. report (:py:class:`~pedal.core.report.Report`): The report object to suppress information within. """ report.suppress(category, label, fields)
[docs] def log(*items, sep=" ", **kwargs): """ Attach logging information to the Report as a piece of feedback. Args: sep: The separator to use between items (defaults to space). items (Any): Any set of values to log information about. Will be converted to strings using `str` if not already strings. Returns: """ kwargs.setdefault('category', Feedback.CATEGORIES.SYSTEM) kwargs.setdefault('muted', True) kwargs.setdefault('valence', Feedback.NEUTRAL_VALENCE) message = sep.join(item if isinstance(item, str) else str(item) for item in items) feedback(message=message, label="log", **kwargs)
[docs] def debug(*items, **kwargs): """ Attach logging information to the Report as a piece of feedback. Works at a higher priority than :py:func:`~pedal.core.commands.log` and does not attempt to convert to strings. TODO: Consider updating to match `log` Args: items (Any): Any set of values to log information about. Will be converted to strings using `str` if not already strings. Returns: """ kwargs.setdefault('category', Feedback.CATEGORIES.SYSTEM) kwargs.setdefault('muted', True) kwargs.setdefault('priority', 'high') kwargs.setdefault('valence', Feedback.NEGATIVE_VALENCE) for item in items: item = item cloned_kwargs = kwargs.copy() cloned_kwargs.setdefault('message', item) feedback(label="debug", **kwargs)
[docs] def clear_report(report=MAIN_REPORT): """ Removes all existing data from the report, including any submissions, suppressions, feedback, and Tool data. Args: report: The report to clear (defaults to the :py:data:`pedal.core.report.MAIN_REPORT`). """ report.clear()
[docs] 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)
[docs] def get_submission(report=MAIN_REPORT) -> Submission: """ Get the current submission from the given report, or the default MAIN_REPORT. Args: report: The report to attach this feedback to (defaults to the :py:data:`~pedal.core.report.MAIN_REPORT`). Returns: Submission: The current submission """ return report.submission
[docs] def get_all_feedback(report=MAIN_REPORT): """ Gives access to the list of feedback from the report. Usually, you won't need this; but if you want to build on the results of earlier tools, it can be a useful mechanism. TODO: Provide mechanisms for conveniently searching feedback Args: report (:py:class:`~pedal.core.report.Report`): The report to attach this feedback to (defaults to the :py:data:`~pedal.core.report.MAIN_REPORT`). Returns: List[:py:class:`~pedal.core.feedback.Feedback`]: A list of feedback objects from the report. """ return report.feedback
[docs] class system_error(FeedbackResponse): """ Call this function to indicate that something has gone wrong at the system level with Pedal. Ideally, this doesn't happen, but sometimes errors cascade and its polite for tools to suggest that they are not working correctly. These will not usually be reported to the student. """ title = "System Error" category = Feedback.CATEGORIES.SYSTEM kind = FeedbackKind.META valence = Feedback.NEUTRAL_VALENCE muted = True
# TODO: set_line_offset(offset, filename='answer.py')
[docs] def set_formatter(formatter, report=MAIN_REPORT): """ Set the formatter for the given report. Args: formatter (Formatter): The formatter class to use. If you wish to use an instance instead, you'll need to call `set_formatter` on the report instance instead. report (:py:class:`~pedal.core.report.Report`): The report to attach this feedback to (defaults to the :py:data:`~pedal.core.report.MAIN_REPORT`). """ report.set_formatter(formatter(report))
def set_pools(pools, report=MAIN_REPORT): """ Ability to have A/B testing on a per-feedback basis. Args: pools (int or list[str]): Either the number of pools or the names of the pools. If a number is given, the pools are given the identifiers A/B/C/etc. """ report.set_pools(pools)