Skip to content

Commit

Permalink
Merge pull request #156 from UBC-DSCI/control-registration
Browse files Browse the repository at this point in the history
Control registration
  • Loading branch information
Lourenzutti authored Dec 31, 2021
2 parents 1443929 + 76a10bd commit 2e55537
Show file tree
Hide file tree
Showing 3 changed files with 59 additions and 40 deletions.
71 changes: 43 additions & 28 deletions rudaux/flows.py
Original file line number Diff line number Diff line change
@@ -1,17 +1,17 @@
import sys, os
import sys
import os
import prefect
from prefect import Flow, unmapped, task, flatten
from prefect import Flow, unmapped, task
from prefect.engine import signals
from prefect.schedules import IntervalSchedule
from prefect.executors import LocalExecutor, DaskExecutor, LocalDaskExecutor
from prefect.executors import LocalExecutor
from prefect.backend import FlowView, FlowRunView
from prefect.tasks.control_flow.filter import FilterTask
from traitlets.config import Config
from traitlets.config.loader import PyFileConfigLoader
import pendulum as plm
from requests.exceptions import ConnectionError
import logging
from subprocess import check_output, STDOUT, CalledProcessError
from subprocess import check_output, CalledProcessError

import threading

Expand All @@ -28,16 +28,17 @@

__PROJECT_NAME = "rudaux"


def _build_flows(args):
print("Loading the rudaux_config.py file...")
if not os.path.exists(os.path.join(args.directory, 'rudaux_config.py')):
sys.exit(
f"""
There is no rudaux_config.py in the directory {args.directory},
and no course directory was specified on the command line. Please
specify a directory with a valid rudaux_config.py file.
"""
)
sys.exit(
f"""
There is no rudaux_config.py in the directory {args.directory},
and no course directory was specified on the command line. Please
specify a directory with a valid rudaux_config.py file.
"""
)
config = Config()
config.merge(PyFileConfigLoader('rudaux_config.py', path=args.directory).load_config())

Expand Down Expand Up @@ -70,12 +71,12 @@ def _build_flows(args):
flows = []
for build_func, flow_name, interval, minute in flow_builders:
print(f"Building/registering the {flow_name} flow...")
_flows = build_func(config, args)
_flows = build_func(config)
for flow in _flows:
flow.executor = executor
if not config.debug:
flow.schedule = IntervalSchedule(start_date = plm.now('UTC').set(minute=minute),
interval = plm.duration(minutes=interval))
flow.schedule = IntervalSchedule(start_date=plm.now('UTC').set(minute=minute),
interval=plm.duration(minutes=interval))
flows.append(flow)
return flows

Expand All @@ -100,14 +101,13 @@ def register(args):
except ConnectionError as e:
print(e)
sys.exit(
f"""
Could not connect to the prefect server. Is the server running?
Make sure to start the server before trying to register flows.
To start the prefect server, run the command:
prefect server start
"""
Could not connect to the prefect server. Is the server running?
Make sure to start the server before trying to register flows.
To start the prefect server, run the command:
"""
prefect server start
"""
)
flows = _build_flows(args)
for flow in flows:
Expand Down Expand Up @@ -135,7 +135,7 @@ def run(args):

return

def build_snapshot_flows(config, args):
def build_snapshot_flows(config):
flows = []
for group in config.course_groups:
for course_id in config.course_groups[group]:
Expand All @@ -155,16 +155,31 @@ def build_snapshot_flows(config, args):
flows.append(flow)
return flows


@task(checkpoint=False)
def combine_dictionaries(dicts):
return {k : v for d in dicts for k, v in d.items()}
return {k: v for d in dicts for k, v in d.items()}

def build_autoext_flows(config, args):

def build_autoext_flows(config):
"""
Build the flow for the auto-extension of assignments for students
who register late.
Params
------
config: traitlets.config.loader.Config
a dictionary-like object containing the configurations
from rudaux_config.py
"""
flows = []
for group in config.course_groups:
for course_id in config.course_groups[group]:
with Flow(config.course_names[course_id]+"-autoext", terminal_state_handler = fail_handler_gen(config)) as flow:
with Flow(config.course_names[course_id] + "-autoext",
terminal_state_handler=fail_handler_gen(config)) as flow:

assignment_names = list(config.assignments[group].keys())

# Obtain course/student/assignment/etc info from the course API
course_info = api.get_course_info(config, course_id)
assignments = api.get_assignments(config, course_id, assignment_names)
Expand All @@ -178,7 +193,7 @@ def build_autoext_flows(config, args):
submission_sets = subm.build_submission_set.map(unmapped(config), submission_sets)

# Compute override updates
overrides = subm.get_latereg_overrides.map(unmapped(config.latereg_extension_days[group]), submission_sets)
overrides = subm.get_latereg_overrides.map(unmapped(config.latereg_extension_days[group]), submission_sets, unmapped(config))

# TODO: we would ideally do flatten(overrides) and then
# api.update_override.map(unmapped(config), unmapped(course_id), flatten(overrides))
Expand All @@ -196,7 +211,7 @@ def build_autoext_flows(config, args):
# rather dynamically from LMS; there we dont know what
# assignments there are until runtime. So doing it by group is the
# right strategy.
def build_grading_flows(config, args):
def build_grading_flows(config):
try:
check_output(['sudo', '-n', 'true'])
except CalledProcessError as e:
Expand Down
4 changes: 2 additions & 2 deletions rudaux/submission.py
Original file line number Diff line number Diff line change
Expand Up @@ -212,7 +212,7 @@ def generate_latereg_overrides_name(extension_days, subm_set, **kwargs):
return 'lateregs-'+subm_set['__name__']

@task(checkpoint=False,task_run_name=generate_latereg_overrides_name)
def get_latereg_overrides(extension_days, subm_set):
def get_latereg_overrides(extension_days, subm_set, config):
logger = get_logger()
fmt = 'ddd YYYY-MM-DD HH:mm:ss'
overrides = []
Expand All @@ -234,7 +234,7 @@ def get_latereg_overrides(extension_days, subm_set):

to_remove = None
to_create = None
if regdate > assignment['unlock_at']:
if regdate > assignment['unlock_at'] and assignment['unlock_at'] <= plm.from_format(config.registration_deadline, f'YYYY-MM-DD', tz=config.notify_timezone):
#the late registration due date
latereg_date = regdate.add(days=extension_days).in_timezone(tz).end_of('day').set(microsecond=0)
if latereg_date > subm['due_at']:
Expand Down
24 changes: 14 additions & 10 deletions scripts/rudaux_config_template.py
Original file line number Diff line number Diff line change
@@ -1,30 +1,34 @@
import rudaux


# the base domain for canvas at your institution
# e.g. at UBC, this is https://canvas.ubc.ca
c.canvas_domain = 'https://canvas.your-domain.com'

# tells rudaux which courses are part of which groups
#group_name is a simple name for the group of canvas courses.
#e.g.
# group_name is a simple name for the group of canvas courses.
# e.g.
# "dsci100" : ["12345", "678910"]
# "stat201" : ["54323"]
c.course_groups = {
'group_name' : ['canvas_id_1', 'canvas_id_2']
'group_name': ['canvas_id_1', 'canvas_id_2']
}

# tells rudaux what to call each course when printing to logs
# canvas_id_1/2/etc are the same as above
# human_readable_name_1/2/etc is just a human-readable name for each section,
# human_readable_name_1/2/etc is just a human-readable name for each section
# (e.g., human_readable_name_1 and 2 would be dsci100-001 and dsci100-004 for
# the two dsci100 sections)
# make sure you include names for every canvas ID above
# e.g.
# e.g.
# {"12345" : "dsci100-001", "678910" : "dsci100-004"}
c.course_names = {
'canvas_id_1' : 'human_readable_name_1', #e.g. human_readable_name_1 and 2 would be dsci100-001 and dsci100-004 (for the two dsci100 sections)
'canvas_id_2' : 'human_readable_name_2',
'canvas_id_1': 'human_readable_name_1',
'canvas_id_2': 'human_readable_name_2',
}

# tells rudaux the last day students have to add a course.
# at UBC we can check it here:
# https://students.ubc.ca/enrolment/registration/course-change-dates
c.registration_deadline = '2022-01-21'

# gives rudaux the ability to read/write to canvas page
# canvas_id_1/2/etc are same as above
# instructor_token_1/2/etc are the Canvas API tokens for the instructor of each section
Expand Down

0 comments on commit 2e55537

Please sign in to comment.