Beispiel #1
0
def start():
    """Setup before initializing curses"""

    # Handle Signals
    signal.signal(signal.SIGINT, sigint_handler)
    signal.signal(signal.SIGTSTP, sigtstp_handler)
    signal.signal(signal.SIGHUP, sighup_handler)

    # Set a short ESC key delay (curses environment variable)
    os.environ.setdefault("ESCDELAY", "25")

    args = parse_args()

    # Check for updates
    if not args.no_update and not args.install_version:
        latest_version = updater.get_latest_version()
        if latest_version != SharedData.VERSION:
            updater.update_zygrader(latest_version)
            sys.exit()

    if args.install_version:
        updater.install_version(args.install_version)
        sys.exit()

    # Setup user configuration
    preferences.initialize()

    versioning.versioning_update_preferences()

    # Handle configuration based args after config has been initialized
    handle_args(args)

    # Check for shared data dir
    data_dir = preferences.get("data_dir")
    if not data_dir:
        print("You have not set the shared data directory")
        print("Please run with the flag --set-data-dir [path]")
        sys.exit()

    # Start application and setup data folders
    if not SharedData.initialize_shared_data(data_dir):
        sys.exit()

    # Load data for the current class
    data.get_students()
    data.get_labs()

    # Change directory to the default output dir
    os.chdir(os.path.expanduser(preferences.get("output_dir")))

    name = data.netid_to_name(getpass.getuser())

    # Create a zygrader window, callback to main function
    ui.Window(main, f"zygrader {SharedData.VERSION}", name, args)

    logger.log("zygrader exited normally")
Beispiel #2
0
def set_due_date(lab, row: ui.layers.Row):
    """Set a cutoff date for a lab

    When grading the submission before the cutoff date will be shown, but the
    in-grader submission picker allows to pick submissions after the cutoff date
    if needed
    """
    window = ui.get_window()

    labs = data.get_labs()

    old_date = lab.options.get("due", None)
    date_spinner = ui.layers.DatetimeSpinner("Due Date")
    date_spinner.set_optional(True)
    if old_date:
        date_spinner.set_initial_time(old_date)
    window.run_layer(date_spinner)
    if date_spinner.canceled:
        return

    due_date = date_spinner.get_time()

    # Clearing the due date
    if due_date == ui.components.DatetimeSpinner.NO_DATE:
        if "due" in lab.options:
            del lab.options["due"]
    else:
        # Remove time zone information
        lab.options["due"] = due_date.astimezone(tz=None)

    data.write_labs(labs)
    set_date_text(lab, row)
Beispiel #3
0
def lab_select_fn(selected_index, use_locks, student: model.Student = None):
    """Callback function that executes after selecting a lab"""
    lab = data.get_labs()[selected_index]

    # Skip selecting a student and go immediately to the grader
    if student:
        student_select_fn(student, lab, use_locks)
        return

    window = ui.get_window()
    students = data.get_students()

    student_list = ui.layers.ListLayer()
    student_list.set_searchable("Student")
    student_list.set_sortable()
    fill_student_list(student_list, students, lab, use_locks,
                      student_select_fn)

    # Register a watch function to watch the students
    watch_students(student_list, students, lab, use_locks)

    # # Remove the file watch handler when done choosing students
    student_list.set_destroy_fn(
        lambda: data.fs_watch.fs_watch_unregister("student_list_watch"))
    window.register_layer(student_list, lab.name)
Beispiel #4
0
def edit_labs():
    """Creates a list of labs to edit"""
    window = ui.get_window()
    labs = data.get_labs()

    lab_list = ui.layers.ListLayer()
    fill_lab_list(lab_list, labs)
    window.register_layer(lab_list)
Beispiel #5
0
def remove_fn(lab_list, window, lab) -> bool:
    """Remove a lab from the list"""
    msg = [f"Are you sure you want to remove {lab.name}?"]
    popup = ui.layers.BoolPopup("Confirm", msg)
    window.run_layer(popup)
    remove = popup.get_result()

    if remove:
        labs = data.get_labs()
        labs.remove(lab)
        data.write_labs(labs)

    labs = data.get_labs()
    fill_lab_list(lab_list, labs)
    events = ui.get_events()
    events.push_layer_close_event()
    return remove
Beispiel #6
0
def toggle_lab_option(lab, option):
    """Toggle a boolean lab option (T/F value)"""
    if option in lab.options:
        del lab.options[option]
    else:
        lab.options[option] = ""

    labs = data.get_labs()
    data.write_labs(labs)
Beispiel #7
0
def submission_search_init():
    """Get lab part and string from the user for searching"""
    window = ui.get_window()
    labs = data.get_labs()

    menu = ui.layers.ListLayer()
    menu.set_searchable("Assignment")
    for lab in labs:
        menu.add_row_text(str(lab))
    window.run_layer(menu, "Submissions Search")
    if menu.canceled:
        return

    assignment = labs[menu.selected_index()]

    # Select the lab part if needed
    if len(assignment.parts) > 1:
        popup = ui.layers.ListLayer("Select Part", popup=True)
        for part in assignment.parts:
            popup.add_row_text(part["name"])
        window.run_layer(popup, "Submissions Search")
        if popup.canceled:
            return

        part = assignment.parts[popup.selected_index()]
    else:
        part = assignment.parts[0]

    regex_input = ui.layers.BoolPopup("Use Regex")
    regex_input.set_message(["Would you like to use regex?"])
    window.run_layer(regex_input)
    if regex_input.canceled:
        return
    use_regex = regex_input.get_result()

    text_input = ui.layers.TextInputLayer("Search String")
    text_input.set_prompt(["Enter a search string"])
    window.run_layer(text_input, "Submissions Search")
    if text_input.canceled:
        return

    search_string = text_input.get_text()

    # Get a valid output path
    filename_input = ui.layers.PathInputLayer("Output File")
    filename_input.set_prompt(
        ["Enter the filename to save the search results"])
    filename_input.set_text(preferences.get("output_dir"))
    window.run_layer(filename_input, "Submissions Search")
    if filename_input.canceled:
        return

    logger = ui.layers.LoggerLayer()
    logger.set_log_fn(lambda: submission_search_fn(
        logger, part, search_string, filename_input.get_path(), use_regex))
    window.run_layer(logger, "Submission Search")
Beispiel #8
0
def rename_lab(lab_list: ui.layers.ListLayer, lab):
    """Rename a lab"""
    window = ui.get_window()
    labs = data.get_labs()

    text_input = ui.layers.TextInputLayer("Rename Lab")
    text_input.set_prompt(["Enter Lab's new name"])
    text_input.set_text(lab.name)
    window.run_layer(text_input)
    if not text_input.canceled:
        lab.name = text_input.get_text()
        data.write_labs(labs)
        fill_lab_list(lab_list, labs)
Beispiel #9
0
def move_lab(lab_list: ui.layers.ListLayer, lab, step):
    """Move a lab up or down the list of labs"""
    labs = data.get_labs()
    index = labs.index(lab)

    # Prevent moving out of bounds
    if index + step > len(labs) - 1 or index + step < 0:
        return

    labs[index] = labs[index + step]
    labs[index + step] = lab

    data.write_labs(labs)
    lab_list.component._selected_index += step
    fill_lab_list(lab_list, labs)
Beispiel #10
0
def grade(use_locks=True, student: model.Student = None):
    """Create the list of labs to pick one to grade"""
    window = ui.get_window()
    labs = data.get_labs()

    if not labs:
        popup = ui.layers.Popup("Error")
        popup.set_message(["No labs have been created yet"])
        window.run_layer(popup)
        return

    title = "Grader"
    if not use_locks:
        title = "Run for Fun"

    lab_list = ui.layers.ListLayer()
    lab_list.set_searchable("Lab")
    for index, lab in enumerate(labs):
        lab_list.add_row_text(str(lab), lab_select_fn, index, use_locks,
                              student)
    window.register_layer(lab_list, title)
Beispiel #11
0
def add_lab():
    """Add a lab to the current class"""
    window = ui.get_window()
    zy_api = Zybooks()

    text_input = ui.layers.TextInputLayer("Lab Name")
    text_input.set_prompt(["Enter the Lab Name"])
    window.run_layer(text_input)
    if text_input.canceled:
        return

    lab_name = text_input.get_text()

    # Get lab part(s)
    parts = []

    section_selector = ZybookSectionSelector(allow_optional_and_hidden=True)
    section_numbers = section_selector.select_zybook_sections(
        return_just_numbers=True)

    for chapter, section in section_numbers:
        part = {}
        response = zy_api.get_zybook_section(chapter, section)
        if not response.success:
            popup = ui.layers.Popup("Error", ["Invalid URL"])
            window.run_layer(popup)
        part["name"] = response.name
        part["id"] = response.id
        parts.append(part)

    new_lab = data.model.Lab(lab_name, parts, {})

    edit_lab_options(new_lab)

    all_labs = data.get_labs()
    all_labs.append(new_lab)

    data.write_labs(all_labs)