diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index d5871a132d789c5828b0de1ad95d09248f01c8f4..5aa7f869ebe792ab2a04ec7a08b8b1fa98685511 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -34,7 +34,9 @@ jobs:
run: poetry install
if: steps.cache.outputs.cache-hit != 'true'
- name: "Flake8"
- run: poetry run flake8 relate course accounts tests
+ run: |
+ poetry run flake8 relate course accounts
+ poetry run flake8 --extend-ignore Q000 tests
- name: "Mypy"
run: poetry run mypy relate course
- name: "Safety"
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index daa2241ec97f597a764c7cf6bc17b950433a5495..a12a7a38cbc752300b5a80cf34905658c674973f 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -53,7 +53,9 @@ setup:
flake8:
<<: *quality
- script: poetry run flake8 relate course accounts tests
+ script: |
+ poetry run flake8 relate course accounts
+ poetry run flake8 --extend-ignore Q000 tests
mypy:
<<: *quality
diff --git a/accounts/__init__.py b/accounts/__init__.py
index 8319823191b94c101df51a5f8af026671f85f567..bc2bb4f01ef75893501a791beae6b1080bfa28d4 100644
--- a/accounts/__init__.py
+++ b/accounts/__init__.py
@@ -1 +1 @@
-default_app_config = 'accounts.apps.AccountsConfig'
+default_app_config = "accounts.apps.AccountsConfig"
diff --git a/accounts/apps.py b/accounts/apps.py
index 951b6f6d7c32cf4ec682383c106a3726bbc45fa4..173b9392b95e3209da7ce8df3fe6d1d053f0b604 100644
--- a/accounts/apps.py
+++ b/accounts/apps.py
@@ -3,6 +3,6 @@ from django.utils.translation import gettext_lazy as _
class AccountsConfig(AppConfig):
- name = 'accounts'
+ name = "accounts"
# for translation of the name of "Accounts" app displayed in admin.
verbose_name = _("Accounts")
diff --git a/accounts/models.py b/accounts/models.py
index 1476fcef548e474c72bf28fbd9f579c8a6ac01b9..e46c6397c16bd806bd9d0018834d68d6cf11a738 100644
--- a/accounts/models.py
+++ b/accounts/models.py
@@ -39,73 +39,73 @@ from course.constants import USER_STATUS_CHOICES
class User(AbstractBaseUser, PermissionsMixin):
username = models.CharField(
- _('username'),
+ _("username"),
max_length=30,
unique=True,
help_text=_(
- 'Required. 30 characters or fewer. Letters, digits and @/./+/-/_ only.'),
+ "Required. 30 characters or fewer. Letters, digits and @/./+/-/_ only."),
validators=[ASCIIUsernameValidator()],
error_messages={
- 'unique': _("A user with that username already exists."),
+ "unique": _("A user with that username already exists."),
},
)
- first_name = models.CharField(_('first name'), max_length=100, blank=True)
- last_name = models.CharField(_('last name'), max_length=100, blank=True)
- email = models.EmailField(_('email address'), blank=True,
+ first_name = models.CharField(_("first name"), max_length=100, blank=True)
+ last_name = models.CharField(_("last name"), max_length=100, blank=True)
+ email = models.EmailField(_("email address"), blank=True,
max_length=100)
name_verified = models.BooleanField(
- _('Name verified'),
+ _("Name verified"),
default=False,
help_text=_(
- 'Indicates that this user\'s name has been verified '
- 'as being associated with the individual able to sign '
- 'in to this account.'
+ "Indicates that this user's name has been verified "
+ "as being associated with the individual able to sign "
+ "in to this account."
),
)
is_active = models.BooleanField(
pgettext_lazy("User status", "active"),
default=True,
help_text=_(
- 'Designates whether this user should be treated as active. '
- 'Unselect this instead of deleting accounts.'
+ "Designates whether this user should be treated as active. "
+ "Unselect this instead of deleting accounts."
),
)
- date_joined = models.DateTimeField(_('date joined'), default=timezone.now)
+ date_joined = models.DateTimeField(_("date joined"), default=timezone.now)
objects = UserManager()
is_staff = models.BooleanField(
- _('staff status'),
+ _("staff status"),
default=False,
- help_text=_('Designates whether the user can log into this admin site.'),
+ help_text=_("Designates whether the user can log into this admin site."),
)
institutional_id = models.CharField(max_length=100,
- verbose_name=_('Institutional ID'),
+ verbose_name=_("Institutional ID"),
blank=True, null=True, unique=True, db_index=True)
institutional_id_verified = models.BooleanField(
- _('Institutional ID verified'),
+ _("Institutional ID verified"),
default=False,
help_text=_(
- 'Indicates that this user\'s institutional ID has been verified '
- 'as being associated with the individual able to log '
- 'in to this account.'
+ "Indicates that this user's institutional ID has been verified "
+ "as being associated with the individual able to log "
+ "in to this account."
),
)
status = models.CharField(max_length=50,
choices=USER_STATUS_CHOICES,
- verbose_name=_('User status'),
+ verbose_name=_("User status"),
null=True)
sign_in_key = models.CharField(max_length=50,
help_text=_("The sign in token sent out in email."),
null=True, unique=True, db_index=True, blank=True,
# Translators: the sign in token of the user.
- verbose_name=_('Sign in key'))
+ verbose_name=_("Sign in key"))
key_time = models.DateTimeField(default=None,
null=True, blank=True,
help_text=_("The time stamp of the sign in token."),
# Translators: the time when the token is sent out.
- verbose_name=_('Key time'))
+ verbose_name=_("Key time"))
editor_mode = models.CharField(max_length=20,
help_text=_("Which key bindings you prefer when editing "
@@ -122,12 +122,12 @@ class User(AbstractBaseUser, PermissionsMixin):
# Translators: the text editor used by participants
verbose_name=_("Editor mode"))
- USERNAME_FIELD = 'username'
- REQUIRED_FIELDS = ['email']
+ USERNAME_FIELD = "username"
+ REQUIRED_FIELDS = ["email"]
class Meta:
- verbose_name = _('user')
- verbose_name_plural = _('users')
+ verbose_name = _("user")
+ verbose_name_plural = _("users")
def get_full_name(self, allow_blank=True, force_verbose_blank=False):
if (not allow_blank
@@ -147,7 +147,7 @@ class User(AbstractBaseUser, PermissionsMixin):
Returns the first_name plus the last_name, with a space in
between.
"""
- return '%s %s' % (
+ return "%s %s" % (
verbose_blank(first_name), verbose_blank(last_name))
from accounts.utils import relate_user_method_settings
@@ -189,11 +189,11 @@ class User(AbstractBaseUser, PermissionsMixin):
return result
def get_short_name(self):
- "Returns the short name for the user."
+ """Returns the short name for the user."""
return self.first_name
def get_email_appellation(self):
- "Return the appellation of the receiver in email."
+ """Return the appellation of the receiver in email."""
from accounts.utils import relate_user_method_settings
priority_list = (
diff --git a/accounts/utils.py b/accounts/utils.py
index c796140f3e999d96350edfd8670072681c3c08f9..56515c494bc0839fa2843e645eef599f3f31ca91 100644
--- a/accounts/utils.py
+++ b/accounts/utils.py
@@ -248,7 +248,7 @@ class RelateUserMethodSettingsInitializer(object):
"method": relate_user_full_name_format_method,
"err_type": type(e).__name__,
"err_str": str(e),
- 'format_exc': format_exc()}
+ "format_exc": format_exc()}
),
id="relate_user_full_name_format_method.W003"
))
diff --git a/course/__init__.py b/course/__init__.py
index 10308e50109453da49fbc2072499f878365a0e17..0db938c12993c08b0b7a4f81ea76f2e845c382ec 100644
--- a/course/__init__.py
+++ b/course/__init__.py
@@ -1 +1 @@
-default_app_config = 'course.apps.CourseConfig'
+default_app_config = "course.apps.CourseConfig"
diff --git a/course/admin.py b/course/admin.py
index 608f5f7bf5d8418d70ab8f061f93a9eec9ade6b5..28d0ca0afa92c7623c29aab84191cdf7dea4f43d 100644
--- a/course/admin.py
+++ b/course/admin.py
@@ -96,7 +96,7 @@ def _filter_related_only(filter_arg):
class UnsafePasswordInput(forms.TextInput):
# This sends passwords back to the user--not ideal, but OK for the XMPP
# password.
- input_type = 'password'
+ input_type = "password"
class CourseAdminForm(forms.ModelForm):
@@ -553,14 +553,14 @@ class FlowPageVisitGradeInline(admin.TabularInline):
class HasAnswerListFilter(admin.SimpleListFilter):
- title = 'has answer'
+ title = "has answer"
- parameter_name = 'has_answer'
+ parameter_name = "has_answer"
def lookups(self, request, model_admin):
return (
- ('y', _('Yes')),
- ('n', _('No')),
+ ("y", _("Yes")),
+ ("n", _("No")),
)
def queryset(self, request, queryset):
diff --git a/course/apps.py b/course/apps.py
index 07c4f578ec9856f36082cd4fdae368ded10fda35..9e065efe9eeff1cc1f0590cc496ad46c49ad6c6e 100644
--- a/course/apps.py
+++ b/course/apps.py
@@ -4,7 +4,7 @@ from relate.checks import register_startup_checks_extra, register_startup_checks
class CourseConfig(AppConfig):
- name = 'course'
+ name = "course"
# for translation of the name of "Course" app displayed in admin.
verbose_name = _("Course module")
diff --git a/course/auth.py b/course/auth.py
index 1cadbe278380401589c2057dce0872ee94813086..4f2d04bfcf013c851f67c0af152dcfdf9a3de7f2 100644
--- a/course/auth.py
+++ b/course/auth.py
@@ -127,8 +127,8 @@ class ImpersonateMiddleware(object):
self.get_response = get_response
def __call__(self, request):
- if 'impersonate_id' in request.session:
- imp_id = request.session['impersonate_id']
+ if "impersonate_id" in request.session:
+ imp_id = request.session["impersonate_id"]
impersonee = None
try:
@@ -159,10 +159,10 @@ class ImpersonateMiddleware(object):
class UserSearchWidget(ModelSelect2Widget):
model = User
search_fields = [
- 'username__icontains',
- 'email__icontains',
- 'first_name__icontains',
- 'last_name__icontains',
+ "username__icontains",
+ "email__icontains",
+ "first_name__icontains",
+ "last_name__icontains",
]
def label_from_instance(self, u):
@@ -230,13 +230,13 @@ def impersonate(request):
qset = (User.objects
.filter(pk__in=impersonable_user_qset.values_list("pk", flat=True))
.order_by("last_name", "first_name", "username"))
- if request.method == 'POST':
+ if request.method == "POST":
form = ImpersonateForm(request.POST, impersonable_qset=qset)
if form.is_valid():
impersonee = form.cleaned_data["user"]
- request.session['impersonate_id'] = impersonee.id
- request.session['relate_impersonation_header'] = form.cleaned_data[
+ request.session["impersonate_id"] = impersonee.id
+ request.session["relate_impersonation_header"] = form.cleaned_data[
"add_impersonation_header"]
# Because we'll likely no longer have access to this page.
@@ -285,7 +285,7 @@ def stop_impersonating(request):
_("Not currently impersonating anyone."))
return http.JsonResponse({})
- del request.session['impersonate_id']
+ del request.session["impersonate_id"]
messages.add_message(request, messages.INFO,
_("No longer impersonating anyone."))
return http.JsonResponse({"result": "success"})
@@ -317,7 +317,7 @@ def make_sign_in_key(user):
def logout_confirmation_required(
func=None, redirect_field_name=REDIRECT_FIELD_NAME,
- logout_confirmation_url='relate-logout-confirmation'):
+ logout_confirmation_url="relate-logout-confirmation"):
"""
Decorator for views that checks that no user is logged in.
If a user is currently logged in, redirect him/her to the logout
@@ -362,7 +362,7 @@ class EmailedTokenBackend(object):
@logout_confirmation_required
def sign_in_choice(request, redirect_field_name=REDIRECT_FIELD_NAME):
redirect_to = request.POST.get(redirect_field_name,
- request.GET.get(redirect_field_name, ''))
+ request.GET.get(redirect_field_name, ""))
next_uri = ""
if redirect_to:
next_uri = "?%s=%s" % (redirect_field_name, redirect_to)
@@ -400,7 +400,7 @@ def sign_in_by_user_pw(request, redirect_field_name=REDIRECT_FIELD_NAME):
return redirect("relate-sign_in_choice")
redirect_to = request.POST.get(redirect_field_name,
- request.GET.get(redirect_field_name, ''))
+ request.GET.get(redirect_field_name, ""))
if request.method == "POST":
form = LoginForm(request, data=request.POST)
@@ -427,9 +427,9 @@ def sign_in_by_user_pw(request, redirect_field_name=REDIRECT_FIELD_NAME):
next_uri = "?%s=%s" % (redirect_field_name, redirect_to)
context = {
- 'form': form,
+ "form": form,
redirect_field_name: redirect_to,
- 'next_uri': next_uri,
+ "next_uri": next_uri,
}
return TemplateResponse(request, "course/login.html", context)
@@ -459,7 +459,7 @@ def sign_up(request):
raise SuspiciousOperation(
_("self-registration is not enabled"))
- if request.method == 'POST':
+ if request.method == "POST":
form = SignUpForm(request.POST)
if form.is_valid():
if get_user_model().objects.filter(
@@ -533,7 +533,7 @@ def sign_up(request):
class ResetPasswordFormByEmail(StyledForm):
email = forms.EmailField(required=True, label=_("Email"),
- max_length=User._meta.get_field('email').max_length)
+ max_length=User._meta.get_field("email").max_length)
def __init__(self, *args, **kwargs):
super(ResetPasswordFormByEmail, self).__init__(*args, **kwargs)
@@ -556,7 +556,7 @@ class ResetPasswordFormByInstid(StyledForm):
def masked_email(email):
# return a masked email address
- at = email.find('@')
+ at = email.find("@")
return email[:2] + "*" * (len(email[3:at])-1) + email[at-1:]
@@ -568,7 +568,7 @@ def reset_password(request, field="email"):
# return form class by string of class name
ResetPasswordForm = globals()["ResetPasswordFormBy" + field.title()] # noqa
- if request.method == 'POST':
+ if request.method == "POST":
form = ResetPasswordForm(request.POST)
user = None
if form.is_valid():
@@ -709,7 +709,7 @@ def reset_password_stage2(request, user_id, sign_in_key):
messages.add_message(request, messages.ERROR, _("Account does not exist."))
raise PermissionDenied(_("invalid sign-in token"))
- if request.method == 'POST':
+ if request.method == "POST":
form = ResetPasswordStage2Form(request.POST)
if form.is_valid():
from django.contrib.auth import authenticate, login
@@ -761,7 +761,7 @@ def reset_password_stage2(request, user_id, sign_in_key):
class SignInByEmailForm(StyledForm):
email = forms.EmailField(required=True, label=_("Email"),
# For now, until we upgrade to a custom user model.
- max_length=User._meta.get_field('email').max_length)
+ max_length=User._meta.get_field("email").max_length)
def __init__(self, *args, **kwargs):
super(SignInByEmailForm, self).__init__(*args, **kwargs)
@@ -777,7 +777,7 @@ def sign_in_by_email(request):
_("Email-based sign-in is not being used"))
return redirect("relate-sign_in_choice")
- if request.method == 'POST':
+ if request.method == "POST":
form = SignInByEmailForm(request.POST)
if form.is_valid():
email = form.cleaned_data["email"]
@@ -892,7 +892,7 @@ class UserForm(StyledModelForm):
"editor_mode")
def __init__(self, *args, **kwargs):
- self.is_inst_id_locked = kwargs.pop('is_inst_id_locked')
+ self.is_inst_id_locked = kwargs.pop("is_inst_id_locked")
super(UserForm, self).__init__(*args, **kwargs)
if self.instance.name_verified:
@@ -1072,7 +1072,7 @@ def sign_out_confirmation(request, redirect_field_name=REDIRECT_FIELD_NAME):
return redirect("relate-home")
redirect_to = request.POST.get(redirect_field_name,
- request.GET.get(redirect_field_name, ''))
+ request.GET.get(redirect_field_name, ""))
next_uri = ""
if redirect_to:
@@ -1090,7 +1090,7 @@ def sign_out(request, redirect_field_name=REDIRECT_FIELD_NAME):
return redirect("relate-home")
redirect_to = request.POST.get(redirect_field_name,
- request.GET.get(redirect_field_name, ''))
+ request.GET.get(redirect_field_name, ""))
response = None
if settings.RELATE_SIGN_IN_BY_SAML2_ENABLED:
@@ -1266,7 +1266,7 @@ def auth_course_with_token(method, func, request,
realm = _("Relate direct git access for {}".format(course_identifier))
response = http.HttpResponse("Forbidden: " + str(e),
content_type="text/plain")
- response['WWW-Authenticate'] = 'Basic realm="%s"' % (realm)
+ response["WWW-Authenticate"] = 'Basic realm="%s"' % (realm)
response.status_code = 401
return response
@@ -1352,7 +1352,7 @@ def manage_authentication_tokens(pctx):
from course.views import get_now_or_fake_time
now_datetime = get_now_or_fake_time(request)
- if request.method == 'POST':
+ if request.method == "POST":
form = AuthenticationTokenForm(pctx.participation, request.POST)
revoke_prefix = "revoke_"
diff --git a/course/calendar.py b/course/calendar.py
index 0de574f1670eb13b326f7e8ade8b0bfdf5cc24e8..edf2c03dd863413dca0e196f8b442a017d7dddb8 100644
--- a/course/calendar.py
+++ b/course/calendar.py
@@ -52,7 +52,7 @@ class ListTextWidget(forms.TextInput):
super(ListTextWidget, self).__init__(*args, **kwargs)
self._name = name
self._list = data_list
- self.attrs.update({'list': 'list__%s' % self._name})
+ self.attrs.update({"list": "list__%s" % self._name})
def render(self, name, value, attrs=None, renderer=None):
text_html = super(ListTextWidget, self).render(
@@ -60,7 +60,7 @@ class ListTextWidget(forms.TextInput):
data_list = ''
+ data_list += ""
return (text_html + data_list)
@@ -88,7 +88,7 @@ class RecurringEventForm(StyledForm):
shown_in_calendar = forms.BooleanField(
required=False,
initial=True,
- label=_('Shown in calendar'))
+ label=_("Shown in calendar"))
interval = forms.ChoiceField(required=True,
choices=(
("weekly", _("Weekly")),
@@ -110,7 +110,7 @@ class RecurringEventForm(StyledForm):
Event.objects.filter(
course__identifier=course_identifier)
.values_list("kind", flat=True))]
- self.fields['kind'].widget = ListTextWidget(data_list=exist_event_choices,
+ self.fields["kind"].widget = ListTextWidget(data_list=exist_event_choices,
name="event_choices")
self.helper.add_input(
@@ -145,7 +145,7 @@ def _create_recurring_events_backend(course, time, kind, starting_ordinal, inter
if Event.objects.filter(course=course, kind=kind, ordinal=ordinal).count():
raise EventAlreadyExists(
_("'%(exist_event)s' already exists")
- % {'exist_event': evt})
+ % {"exist_event": evt})
evt.save()
@@ -272,7 +272,7 @@ class RenumberEventsForm(StyledForm):
renumberable_event_kinds = set(Event.objects.filter(
course__identifier=self.course_identifier,
ordinal__isnull=False).values_list("kind", flat=True))
- self.fields['kind'].choices = tuple(
+ self.fields["kind"].choices = tuple(
(kind, kind) for kind in renumberable_event_kinds)
self.helper.add_input(
diff --git a/course/content.py b/course/content.py
index f6c18c203535e531df4addc690ac8e1f030b4697..25eb5748cc65acb306faeeeb44ebf07bad3c7626 100644
--- a/course/content.py
+++ b/course/content.py
@@ -227,7 +227,7 @@ def get_repo_blob(repo, full_name, commit_sha, allow_tree=True):
names = os.path.normpath(full_name).split(os.sep)
# Allow non-ASCII file name
- full_name_bytes = full_name.encode('utf-8')
+ full_name_bytes = full_name.encode("utf-8")
try:
tree_sha = dul_repo[commit_sha].tree
@@ -469,7 +469,7 @@ class GitTemplateLoader(BaseTemplateLoader):
except ObjectDoesNotExist:
raise TemplateNotFound(template)
- source = data.decode('utf-8')
+ source = data.decode("utf-8")
def is_up_to_date():
# There's not much point to caching here, because we create
@@ -647,10 +647,10 @@ def get_yaml_from_repo(repo, full_name, commit_sha, cached=True):
def _attr_to_string(key, val):
if val is None:
return key
- elif "\"" in val:
+ elif '"' in val:
return "%s='%s'" % (key, val)
else:
- return "%s=\"%s\"" % (key, val)
+ return '%s="%s"' % (key, val)
class TagProcessingHTMLParser(html_parser.HTMLParser):
@@ -1417,9 +1417,9 @@ def get_flow_page_desc(flow_id, flow_desc, group_id, page_id):
raise ObjectDoesNotExist(
_("page '%(group_id)s/%(page_id)s' in flow '%(flow_id)s'") % {
- 'group_id': group_id,
- 'page_id': page_id,
- 'flow_id': flow_id
+ "group_id": group_id,
+ "page_id": page_id,
+ "flow_id": flow_id
})
# }}}
@@ -1433,7 +1433,7 @@ class ClassNotFoundError(RuntimeError):
def import_class(name):
# type: (Text) -> type
- components = name.split('.')
+ components = name.split(".")
if len(components) < 2:
# need at least one module plus class name
@@ -1487,7 +1487,7 @@ def get_flow_page_class(repo, typename, commit_sha):
module_dict = {} # type: Dict
- exec(compile(module_code, module_name, 'exec'), module_dict)
+ exec(compile(module_code, module_name, "exec"), module_dict)
try:
return module_dict[classname]
diff --git a/course/enrollment.py b/course/enrollment.py
index 9d419fe052b2990b44e7be7b149ba32ef655ef71..8c7e27c9226467bde2d738c3f4a88c681cb54309 100644
--- a/course/enrollment.py
+++ b/course/enrollment.py
@@ -456,7 +456,7 @@ class BulkPreapprovalsForm(StyledForm):
self.fields["preapproval_data"] = forms.CharField(
required=True, widget=forms.Textarea,
help_text=_("Enter fully qualified data according to the "
- "\"Preapproval type\" you selected, one per line."),
+ "'Preapproval type' you selected, one per line."),
label=_("Preapproval data"))
self.helper.add_input(
@@ -541,9 +541,9 @@ def create_preapprovals(pctx):
"%(n_exist)d already existed, "
"%(n_requested_approved)d pending requests approved.")
% {
- 'n_created': created_count,
- 'n_exist': exist_count,
- 'n_requested_approved': pending_approved_count
+ "n_created": created_count,
+ "n_exist": exist_count,
+ "n_requested_approved": pending_approved_count
})
return redirect("relate-course_page", pctx.course.identifier)
@@ -1045,7 +1045,7 @@ def edit_participation(pctx, participation_id):
if participation.course.id != pctx.course.id:
raise SuspiciousOperation("may not edit participation in different course")
- if request.method == 'POST':
+ if request.method == "POST":
form = EditParticipationForm(
add_new, pctx, request.POST, instance=participation)
reset_form = False
diff --git a/course/exam.py b/course/exam.py
index cbc55980f8c5f088a6fb59db2f8c1f9883b155fa..f1708c6e0df5d155a98e36efd672dc2e78508eab 100644
--- a/course/exam.py
+++ b/course/exam.py
@@ -280,8 +280,8 @@ class BatchIssueTicketsForm(StyledForm):
dependencies=("xml",),
interaction_mode=editor_mode)
- help_text = (gettext("Enter "
+ help_text = (gettext('Enter '
"RELATE markup containing Django template statements to render "
"your exam tickets. tickets contains a list of "
"data structures "
diff --git a/course/flow.py b/course/flow.py
index dc8c54d7db1a1942f46933599a3aa66c1e66139c..8d923ea9435578c6a85343667a9ac6ba0c5cfa37 100644
--- a/course/flow.py
+++ b/course/flow.py
@@ -1075,8 +1075,8 @@ def expire_flow_session(
raise ValueError(
_("invalid expiration mode '%(mode)s' on flow session ID "
"%(session_id)d") % {
- 'mode': flow_session.expiration_mode,
- 'session_id': flow_session.id})
+ "mode": flow_session.expiration_mode,
+ "session_id": flow_session.id})
def get_flow_session_attempt_id(flow_session):
@@ -1111,8 +1111,8 @@ def grade_flow_session(
comment = (
# Translators: grade flow: calculating grade.
_("Counted at %(percent).1f%% of %(point).1f points") % {
- 'percent': grading_rule.credit_percent,
- 'point': points})
+ "percent": grading_rule.credit_percent,
+ "point": points})
points = points * grading_rule.credit_percent / 100
flow_session.points = points
@@ -1214,8 +1214,8 @@ def reopen_session(
session.append_comment(
_("Session reopened at %(now)s, previous completion time "
"was '%(complete_time)s'.") % {
- 'now': format_datetime_local(now_datetime),
- 'complete_time': format_datetime_local(
+ "now": format_datetime_local(now_datetime),
+ "complete_time": format_datetime_local(
as_local_time(session.completion_time))
})
@@ -1316,7 +1316,7 @@ def regrade_session(
with transaction.atomic():
session.append_comment(
_("Session regraded at %(time)s.") % {
- 'time': format_datetime_local(now_datetime)
+ "time": format_datetime_local(now_datetime)
})
session.save()
@@ -1346,7 +1346,7 @@ def recalculate_session_grade(repo, course, session):
now_datetime = local_now()
session.append_comment(
_("Session grade recomputed at %(time)s.") % {
- 'time': format_datetime_local(now_datetime)
+ "time": format_datetime_local(now_datetime)
})
session.save()
@@ -1752,7 +1752,7 @@ def create_flow_page_visit(request, flow_session, page_data):
visit = FlowPageVisit(
flow_session=flow_session,
page_data=page_data,
- remote_address=request.META['REMOTE_ADDR'],
+ remote_address=request.META["REMOTE_ADDR"],
user=user,
is_submitted_answer=None)
@@ -2079,8 +2079,8 @@ def view_flow_page(pctx, flow_session_id, page_ordinal):
# Wrappers used by JavaScript template (tmpl) so as not to
# conflict with Django template's tag wrapper
- "JQ_OPEN": '{%',
- 'JQ_CLOSE': '%}',
+ "JQ_OPEN": "{%",
+ "JQ_CLOSE": "%}",
}
if fpctx.page.expects_answer() and fpctx.page.is_answer_gradable():
@@ -2199,7 +2199,7 @@ def post_flow_page(
answer_visit = FlowPageVisit()
answer_visit.flow_session = flow_session
answer_visit.page_data = fpctx.page_data
- answer_visit.remote_address = request.META['REMOTE_ADDR']
+ answer_visit.remote_address = request.META["REMOTE_ADDR"]
answer_data = answer_visit.answer = fpctx.page.answer_data(
page_context, fpctx.page_data.data,
@@ -2337,9 +2337,9 @@ def send_email_about_flow_page(pctx, flow_session_id, page_ordinal):
review_url = reverse(
"relate-view_flow_page",
- kwargs={'course_identifier': pctx.course.identifier,
- 'flow_session_id': flow_session_id,
- 'page_ordinal': page_ordinal
+ kwargs={"course_identifier": pctx.course.identifier,
+ "flow_session_id": flow_session_id,
+ "page_ordinal": page_ordinal
}
)
@@ -2408,10 +2408,10 @@ def send_email_about_flow_page(pctx, flow_session_id, page_ordinal):
"[%(identifier)s:%(flow_id)s--%(page_id)s] ",
_("Interaction request from %(username)s"))
% {
- 'identifier': pctx.course_identifier,
- 'flow_id': flow_session_id,
- 'page_id': page_id,
- 'username': username
+ "identifier": pctx.course_identifier,
+ "flow_id": flow_session_id,
+ "page_id": page_id,
+ "username": username
},
body=message,
from_email=from_email,
@@ -2680,9 +2680,9 @@ def finish_flow_session_view(pctx, flow_session_id):
msg = EmailMessage(
string_concat("[%(identifier)s:%(flow_id)s] ",
_("Submission by %(participation_desc)s"))
- % {'participation_desc': participation_desc,
- 'identifier': fctx.course.identifier,
- 'flow_id': flow_session.flow_id},
+ % {"participation_desc": participation_desc,
+ "identifier": fctx.course.identifier,
+ "flow_id": flow_session.flow_id},
message,
getattr(settings, "NOTIFICATION_EMAIL_FROM",
settings.ROBOT_EMAIL_FROM),
@@ -2882,7 +2882,7 @@ def view_unsubmit_flow_page(pctx, flow_session_id, page_ordinal):
return redirect("relate-view_flow_page",
pctx.course.identifier, flow_session_id, page_ordinal)
- if request.method == 'POST':
+ if request.method == "POST":
form = UnsubmitFlowPageForm(request.POST)
if form.is_valid():
if "submit" in request.POST:
@@ -2940,7 +2940,7 @@ def purge_page_view_data(request):
purgeable_courses = get_pv_purgeable_courses_for_user_qs(request.user)
if not purgeable_courses.count():
raise PermissionDenied()
- if request.method == 'POST':
+ if request.method == "POST":
form = PurgePageViewData(request.user, request.POST)
if form.is_valid():
if "submit" in request.POST:
diff --git a/course/grades.py b/course/grades.py
index 6364d2a0043ea23e1fe05a3b47328fdc991801ce..b4df32b5b96f797f6677177ca2eb5466a06a4bd0 100644
--- a/course/grades.py
+++ b/course/grades.py
@@ -312,7 +312,7 @@ def export_gradebook_csv(pctx):
import csv
- fieldnames = ['user_name', 'last_name', 'first_name'] + [
+ fieldnames = ["user_name", "last_name", "first_name"] + [
gopp.identifier for gopp in grading_opps]
writer = csv.writer(csvfile)
@@ -330,7 +330,7 @@ def export_gradebook_csv(pctx):
response = http.HttpResponse(
csvfile.getvalue().encode("utf-8"),
content_type="text/plain; charset=utf-8")
- response['Content-Disposition'] = (
+ response["Content-Disposition"] = (
'attachment; filename="grades-%s.csv"'
% pctx.course.identifier)
return response
@@ -1144,7 +1144,7 @@ class ImportGradesForm(StyledForm):
header_count)
if not importable:
- self.add_error('file', err_msg)
+ self.add_error("file", err_msg)
class ParticipantNotFound(ValueError):
@@ -1324,8 +1324,8 @@ def csv_to_grade_changes(
"%(participation)s: %(updated)s ",
_("updated")
) % {
- 'participation': gchange.participation,
- 'updated': ", ".join(updated)})
+ "participation": gchange.participation,
+ "updated": ", ".join(updated)})
result.append(gchange)
else:
@@ -1388,8 +1388,8 @@ def import_grades(pctx):
if total_count != len(grade_changes):
messages.add_message(pctx.request, messages.INFO,
_("%(total)d grades found, %(unchaged)d unchanged.")
- % {'total': total_count,
- 'unchaged': total_count - len(grade_changes)})
+ % {"total": total_count,
+ "unchaged": total_count - len(grade_changes)})
from django.template.loader import render_to_string
@@ -1616,7 +1616,7 @@ def download_all_submissions(pctx, flow_id):
response = http.HttpResponse(
bio.getvalue(),
content_type="application/zip")
- response['Content-Disposition'] = (
+ response["Content-Disposition"] = (
'attachment; filename="submissions_%s_%s_%s_%s_%s.zip"'
% (pctx.course.identifier, flow_id, group_id, page_id,
now().date().strftime("%Y-%m-%d")))
@@ -1684,7 +1684,7 @@ def edit_grading_opportunity(pctx, opportunity_id):
raise SuspiciousOperation(
"may not edit grading opportunity in different course")
- if request.method == 'POST':
+ if request.method == "POST":
form = EditGradingOpportunityForm(add_new, request.POST, instance=gopp)
if form.is_valid():
diff --git a/course/grading.py b/course/grading.py
index 20d65693ebbbf01ed58eca84dae8d7d1ff8ff438..0286776c74b0d88d156469e86e0109c6f1cb1f24 100644
--- a/course/grading.py
+++ b/course/grading.py
@@ -396,8 +396,8 @@ def grade_flow_page(pctx, flow_session_id, page_ordinal):
# Wrappers used by JavaScript template (tmpl) so as not to
# conflict with Django template's tag wrapper
- "JQ_OPEN": '{%',
- 'JQ_CLOSE': '%}',
+ "JQ_OPEN": "{%",
+ "JQ_CLOSE": "%}",
})
diff --git a/course/im.py b/course/im.py
index 6b9856e0410065b528ca8cf147f5af9949a8e772..da47f7cae7c0ffefb216f5ca6055d9ab47b6ef6e 100644
--- a/course/im.py
+++ b/course/im.py
@@ -107,7 +107,7 @@ class CourseXMPP(sleekxmpp.ClientXMPP):
"""
Track how many roster entries have received presence updates.
"""
- self.received.add(pres['from'].bare)
+ self.received.add(pres["from"].bare)
if len(self.received) >= len(self.client_roster.keys()):
self.presences_received.set()
else:
@@ -196,7 +196,7 @@ def send_instant_message(pctx):
xmpp.send_message(
mto=course.recipient_xmpp_id,
mbody=form.cleaned_data["message"],
- mtype='chat')
+ mtype="chat")
except Exception:
from traceback import print_exc
diff --git a/course/management/commands/test.py b/course/management/commands/test.py
index 2e2d9e15bcea85beabf8d6f6549dd624155f3abc..e26ccbac302a98f7f850966a7a385d7dbeb84adf 100644
--- a/course/management/commands/test.py
+++ b/course/management/commands/test.py
@@ -5,15 +5,15 @@ class Command(DjangoTestCommand):
def add_arguments(self, parser):
super(Command, self).add_arguments(parser)
parser.add_argument(
- '--local_test_settings', action='store',
- dest='local_test_settings',
- help=('Overrides the default local test setting file path. '
- 'The default value is "local_settings_example.py" in '
- 'project root. Note that local settings for production '
+ "--local_test_settings", action="store",
+ dest="local_test_settings",
+ help=("Overrides the default local test setting file path. "
+ "The default value is 'local_settings_example.py' in "
+ "project root. Note that local settings for production "
'("local_settings.py") is not allowed to be used '
- 'for unit tests for security reason.')
+ "for unit tests for security reason.")
)
def handle(self, *test_labels, **options):
- del options['local_test_settings']
+ del options["local_test_settings"]
super(Command, self).handle(*test_labels, **options)
diff --git a/course/models.py b/course/models.py
index 722352685dfd7f20ffde637c7e75ecfe3a72dcf3..d8b386dec9d0650aa1b874089afa982d2880bc08 100644
--- a/course/models.py
+++ b/course/models.py
@@ -91,7 +91,7 @@ class Course(models.Model):
"on your file system where the course's git repository lives. "
"This should not be changed after the course has been created "
"without also moving the course's git on the server."),
- verbose_name=_('Course identifier'),
+ verbose_name=_("Course identifier"),
db_index=True,
validators=[
RegexValidator(
@@ -104,110 +104,110 @@ class Course(models.Model):
name = models.CharField(
null=True, blank=False,
max_length=200,
- verbose_name=_('Course name'),
+ verbose_name=_("Course name"),
help_text=_("A human-readable name for the course. "
"(e.g. 'Numerical Methods')"))
number = models.CharField(
null=True, blank=False,
max_length=200,
- verbose_name=_('Course number'),
+ verbose_name=_("Course number"),
help_text=_("A human-readable course number/ID "
"for the course (e.g. 'CS123')"))
time_period = models.CharField(
null=True, blank=False,
max_length=200,
- verbose_name=_('Time Period'),
+ verbose_name=_("Time Period"),
help_text=_("A human-readable description of the "
"time period for the course (e.g. 'Fall 2014')"))
start_date = models.DateField(
- verbose_name=_('Start date'),
+ verbose_name=_("Start date"),
null=True, blank=True)
end_date = models.DateField(
- verbose_name=_('End date'),
+ verbose_name=_("End date"),
null=True, blank=True)
hidden = models.BooleanField(
default=True,
help_text=_("Is the course only accessible to course staff?"),
- verbose_name=_('Only visible to course staff'))
+ verbose_name=_("Only visible to course staff"))
listed = models.BooleanField(
default=True,
help_text=_("Should the course be listed on the main page?"),
- verbose_name=_('Listed on main page'))
+ verbose_name=_("Listed on main page"))
accepts_enrollment = models.BooleanField(
default=True,
- verbose_name=_('Accepts enrollment'))
+ verbose_name=_("Accepts enrollment"))
git_source = models.CharField(max_length=200, blank=False,
help_text=_("A Git URL from which to pull course updates. "
"If you're just starting out, enter "
"git://github.com/inducer/relate-sample "
"to get some sample content."),
- verbose_name=_('git source'))
+ verbose_name=_("git source"))
ssh_private_key = models.TextField(blank=True,
help_text=_("An SSH private key to use for Git authentication. "
"Not needed for the sample URL above."
"You may use this tool to generate "
"a key pair."),
- verbose_name=_('SSH private key'))
+ verbose_name=_("SSH private key"))
course_root_path = models.CharField(max_length=200, blank=True,
help_text=_(
- 'Subdirectory within the git repository to use as '
- 'course root directory. Not required, and usually blank. '
- 'Use only if your course content lives in a subdirectory '
- 'of your git repository. '
- 'Should not include trailing slash.'),
- verbose_name=_('Course root in repository'))
+ "Subdirectory within the git repository to use as "
+ "course root directory. Not required, and usually blank. "
+ "Use only if your course content lives in a subdirectory "
+ "of your git repository. "
+ "Should not include trailing slash."),
+ verbose_name=_("Course root in repository"))
course_file = models.CharField(max_length=200,
default="course.yml",
help_text=_("Name of a YAML file in the git repository that "
"contains the root course descriptor."),
- verbose_name=_('Course file'))
+ verbose_name=_("Course file"))
events_file = models.CharField(max_length=200,
default="events.yml",
help_text=_("Name of a YAML file in the git repository that "
"contains calendar information."),
- verbose_name=_('Events file'))
+ verbose_name=_("Events file"))
enrollment_approval_required = models.BooleanField(
default=False,
help_text=_("If set, each enrolling student must be "
"individually approved."),
- verbose_name=_('Enrollment approval required'))
+ verbose_name=_("Enrollment approval required"))
preapproval_require_verified_inst_id = models.BooleanField(
default=True,
help_text=_("If set, students cannot get participation "
"preapproval using institutional ID if "
"the institutional ID they provided is not "
"verified."),
- verbose_name=_('Prevent preapproval by institutional ID if not '
- 'verified?'))
+ verbose_name=_("Prevent preapproval by institutional ID if not "
+ "verified?"))
enrollment_required_email_suffix = models.CharField(
max_length=200, blank=True, null=True,
help_text=_("Enrollee's email addresses must end in the "
"specified suffix, such as '@illinois.edu'."),
- verbose_name=_('Enrollment required email suffix'))
+ verbose_name=_("Enrollment required email suffix"))
from_email = models.EmailField(
# Translators: replace "RELATE" with the brand name of your
# website if necessary.
help_text=_("This email address will be used in the 'From' line "
"of automated emails sent by RELATE."),
- verbose_name=_('From email'))
+ verbose_name=_("From email"))
notify_email = models.EmailField(
help_text=_("This email address will receive "
"notifications about the course."),
- verbose_name=_('Notify email'))
+ verbose_name=_("Notify email"))
force_lang = models.CharField(max_length=200, blank=True, null=True,
default="",
validators=[validate_course_specific_language],
help_text=_(
"Which language is forced to be used for this course."),
- verbose_name=_('Course language forcibly used'))
+ verbose_name=_("Course language forcibly used"))
# {{{ XMPP
@@ -215,25 +215,25 @@ class Course(models.Model):
help_text=_("(Required only if the instant message feature is "
"desired.) The Jabber/XMPP ID (JID) the course will use to sign "
"in to an XMPP server."),
- verbose_name=_('Course xmpp ID'))
+ verbose_name=_("Course xmpp ID"))
course_xmpp_password = models.CharField(max_length=200, blank=True, null=True,
help_text=_("(Required only if the instant message feature is "
"desired.) The password to go with the JID above."),
- verbose_name=_('Course xmpp password'))
+ verbose_name=_("Course xmpp password"))
recipient_xmpp_id = models.CharField(max_length=200, blank=True, null=True,
help_text=_("(Required only if the instant message feature is "
"desired.) The JID to which instant messages will be sent."),
- verbose_name=_('Recipient xmpp ID'))
+ verbose_name=_("Recipient xmpp ID"))
# }}}
active_git_commit_sha = models.CharField(max_length=200, null=False,
blank=False,
- verbose_name=_('Active git commit SHA'))
+ verbose_name=_("Active git commit SHA"))
participants = models.ManyToManyField(settings.AUTH_USER_MODEL,
- through='Participation')
+ through="Participation")
class Meta:
verbose_name = _("Course")
@@ -282,12 +282,12 @@ class Event(models.Model):
"""
course = models.ForeignKey(Course,
- verbose_name=_('Course'), on_delete=models.CASCADE)
+ verbose_name=_("Course"), on_delete=models.CASCADE)
kind = models.CharField(max_length=50,
# Translators: format of event kind in Event model
help_text=_("Should be lower_case_with_underscores, no spaces "
"allowed."),
- verbose_name=_('Kind of event'),
+ verbose_name=_("Kind of event"),
validators=[
RegexValidator(
"^" + EVENT_KIND_REGEX + "$",
@@ -297,20 +297,20 @@ class Event(models.Model):
)
ordinal = models.IntegerField(blank=True, null=True,
# Translators: ordinal of event of the same kind
- verbose_name=_('Ordinal of event'))
+ verbose_name=_("Ordinal of event"))
- time = models.DateTimeField(verbose_name=_('Start time'))
+ time = models.DateTimeField(verbose_name=_("Start time"))
end_time = models.DateTimeField(null=True, blank=True,
- verbose_name=_('End time'))
+ verbose_name=_("End time"))
all_day = models.BooleanField(default=False,
# Translators: for when the due time is "All day", how the webpage
# of a event is displayed.
help_text=_("Only affects the rendering in the class calendar, "
"in that a start time is not shown"),
- verbose_name=_('All day'))
+ verbose_name=_("All day"))
shown_in_calendar = models.BooleanField(default=True,
- verbose_name=_('Shown in calendar'))
+ verbose_name=_("Shown in calendar"))
class Meta:
verbose_name = _("Event")
@@ -363,14 +363,14 @@ class Event(models.Model):
class ParticipationTag(models.Model):
course = models.ForeignKey(Course,
- verbose_name=_('Course'), on_delete=models.CASCADE)
+ verbose_name=_("Course"), on_delete=models.CASCADE)
name = models.CharField(max_length=100,
# Translators: name format of ParticipationTag
help_text=_("Format is lower-case-with-hyphens. "
"Do not use spaces."),
- verbose_name=_('Name of participation tag'))
+ verbose_name=_("Name of participation tag"))
shown_to_participant = models.BooleanField(default=False,
- verbose_name=_('Shown to pariticpant'))
+ verbose_name=_("Shown to pariticpant"))
def clean(self):
super(ParticipationTag, self).clean()
@@ -398,22 +398,22 @@ class ParticipationTag(models.Model):
class ParticipationRole(models.Model):
course = models.ForeignKey(Course,
- verbose_name=_('Course'), on_delete=models.CASCADE)
+ verbose_name=_("Course"), on_delete=models.CASCADE)
identifier = models.CharField(
max_length=100, blank=False, null=False,
help_text=_("A symbolic name for this role, used in course code. "
"lower_case_with_underscores, no spaces. May be any string. The "
"name 'unenrolled' is special and refers to anyone not enrolled "
"in the course."),
- verbose_name=_('Role identifier'))
+ verbose_name=_("Role identifier"))
name = models.CharField(max_length=200, blank=False, null=False,
help_text=_("A human-readable description of this role."),
- verbose_name=_('Role name'))
+ verbose_name=_("Role name"))
is_default_for_new_participants = models.BooleanField(default=False,
- verbose_name=_('Is default role for new participants'))
+ verbose_name=_("Is default role for new participants"))
is_default_for_unenrolled = models.BooleanField(default=False,
- verbose_name=_('Is default role for unenrolled users'))
+ verbose_name=_("Is default role for unenrolled users"))
def clean(self):
super(ParticipationRole, self).clean()
@@ -470,10 +470,10 @@ class ParticipationRole(models.Model):
class ParticipationPermissionBase(models.Model):
permission = models.CharField(max_length=200, blank=False, null=False,
choices=PARTICIPATION_PERMISSION_CHOICES,
- verbose_name=_('Permission'),
+ verbose_name=_("Permission"),
db_index=True)
argument = models.CharField(max_length=200, blank=True, null=True,
- verbose_name=_('Argument'))
+ verbose_name=_("Argument"))
class Meta:
abstract = True
@@ -489,7 +489,7 @@ class ParticipationPermissionBase(models.Model):
class ParticipationRolePermission(ParticipationPermissionBase):
role = models.ForeignKey(ParticipationRole,
- verbose_name=_('Role'), on_delete=models.CASCADE,
+ verbose_name=_("Role"), on_delete=models.CASCADE,
related_name="permissions")
def __unicode__(self):
@@ -508,13 +508,13 @@ class ParticipationRolePermission(ParticipationPermissionBase):
class Participation(models.Model):
user = models.ForeignKey(settings.AUTH_USER_MODEL,
- verbose_name=_('User ID'), on_delete=models.CASCADE,
+ verbose_name=_("User ID"), on_delete=models.CASCADE,
related_name="participations")
course = models.ForeignKey(Course, related_name="participations",
- verbose_name=_('Course'), on_delete=models.CASCADE)
+ verbose_name=_("Course"), on_delete=models.CASCADE)
enroll_time = models.DateTimeField(default=now,
- verbose_name=_('Enroll time'))
+ verbose_name=_("Enroll time"))
role = models.CharField(max_length=50,
verbose_name=_("Role (unused)"),)
roles = models.ManyToManyField(ParticipationRole, blank=True,
@@ -522,23 +522,23 @@ class Participation(models.Model):
status = models.CharField(max_length=50,
choices=PARTICIPATION_STATUS_CHOICES,
- verbose_name=_('Participation status'))
+ verbose_name=_("Participation status"))
time_factor = models.DecimalField(
max_digits=10, decimal_places=2,
default=1,
help_text=_("Multiplier for time available on time-limited "
"flows"),
- verbose_name=_('Time factor'))
+ verbose_name=_("Time factor"))
preview_git_commit_sha = models.CharField(max_length=200, null=True,
blank=True,
- verbose_name=_('Preview git commit SHA'))
+ verbose_name=_("Preview git commit SHA"))
tags = models.ManyToManyField(ParticipationTag, blank=True,
- verbose_name=_('Tags'))
+ verbose_name=_("Tags"))
notes = models.TextField(blank=True, null=True,
- verbose_name=_('Notes'))
+ verbose_name=_("Notes"))
def __unicode__(self):
# Translators: displayed format of Participation: some user in some
@@ -598,7 +598,7 @@ class Participation(models.Model):
class ParticipationPermission(ParticipationPermissionBase):
participation = models.ForeignKey(Participation,
- verbose_name=_('Participation'), on_delete=models.CASCADE,
+ verbose_name=_("Participation"), on_delete=models.CASCADE,
related_name="individual_permissions")
class Meta:
@@ -609,20 +609,20 @@ class ParticipationPermission(ParticipationPermissionBase):
class ParticipationPreapproval(models.Model):
email = models.EmailField(max_length=254, null=True, blank=True,
- verbose_name=_('Email'))
+ verbose_name=_("Email"))
institutional_id = models.CharField(max_length=254, null=True, blank=True,
- verbose_name=_('Institutional ID'))
+ verbose_name=_("Institutional ID"))
course = models.ForeignKey(Course,
- verbose_name=_('Course'), on_delete=models.CASCADE)
+ verbose_name=_("Course"), on_delete=models.CASCADE)
role = models.CharField(max_length=50,
verbose_name=_("Role (unused)"),)
roles = models.ManyToManyField(ParticipationRole, blank=True,
verbose_name=_("Roles"), related_name="+")
creator = models.ForeignKey(settings.AUTH_USER_MODEL, null=True,
- verbose_name=_('Creator'), on_delete=models.SET_NULL)
+ verbose_name=_("Creator"), on_delete=models.SET_NULL)
creation_time = models.DateTimeField(default=now, db_index=True,
- verbose_name=_('Creation time'))
+ verbose_name=_("Creation time"))
def __unicode__(self):
if self.email:
@@ -780,35 +780,35 @@ def _set_up_course_permissions(sender, instance, created, raw, using, update_fie
class AuthenticationToken(models.Model):
user = models.ForeignKey(settings.AUTH_USER_MODEL,
- verbose_name=_('User ID'), on_delete=models.CASCADE)
+ verbose_name=_("User ID"), on_delete=models.CASCADE)
participation = models.ForeignKey(Participation,
- verbose_name=_('Participation'), on_delete=models.CASCADE)
+ verbose_name=_("Participation"), on_delete=models.CASCADE)
restrict_to_participation_role = models.ForeignKey(ParticipationRole,
- verbose_name=_('Restrict to role'), on_delete=models.CASCADE,
+ verbose_name=_("Restrict to role"), on_delete=models.CASCADE,
blank=True, null=True)
description = models.CharField(max_length=200,
- verbose_name=_('Description'))
+ verbose_name=_("Description"))
creation_time = models.DateTimeField(
- default=now, verbose_name=_('Creation time'))
+ default=now, verbose_name=_("Creation time"))
last_use_time = models.DateTimeField(
- verbose_name=_('Last use time'),
+ verbose_name=_("Last use time"),
blank=True, null=True)
valid_until = models.DateTimeField(
- default=None, verbose_name=_('Valid until'),
+ default=None, verbose_name=_("Valid until"),
blank=True, null=True)
revocation_time = models.DateTimeField(
- default=None, verbose_name=_('Revocation time'),
+ default=None, verbose_name=_("Revocation time"),
blank=True, null=True)
token_hash = models.CharField(max_length=200,
help_text=_("A hash of the authentication token to be "
"used for direct git access."),
null=True, blank=True, unique=True,
- verbose_name=_('Hash of git authentication token'))
+ verbose_name=_("Hash of git authentication token"))
def __unicode__(self):
return _("Token %(id)d for %(participation)s: %(description)s") % {
@@ -830,15 +830,15 @@ class AuthenticationToken(models.Model):
class InstantFlowRequest(models.Model):
course = models.ForeignKey(Course,
- verbose_name=_('Course'), on_delete=models.CASCADE)
+ verbose_name=_("Course"), on_delete=models.CASCADE)
flow_id = models.CharField(max_length=200,
- verbose_name=_('Flow ID'))
+ verbose_name=_("Flow ID"))
start_time = models.DateTimeField(default=now,
- verbose_name=_('Start time'))
+ verbose_name=_("Start time"))
end_time = models.DateTimeField(
- verbose_name=_('End time'))
+ verbose_name=_("End time"))
cancelled = models.BooleanField(default=False,
- verbose_name=_('Cancelled'))
+ verbose_name=_("Cancelled"))
class Meta:
verbose_name = _("Instant flow request")
@@ -864,28 +864,28 @@ class FlowSession(models.Model):
# This looks like it's redundant with 'participation', below--but it's not.
# 'participation' is nullable.
course = models.ForeignKey(Course,
- verbose_name=_('Course'), on_delete=models.CASCADE)
+ verbose_name=_("Course"), on_delete=models.CASCADE)
participation = models.ForeignKey(Participation, null=True, blank=True,
db_index=True, related_name="flow_sessions",
- verbose_name=_('Participation'), on_delete=models.CASCADE)
+ verbose_name=_("Participation"), on_delete=models.CASCADE)
# This looks like it's redundant with participation, above--but it's not.
# Again--'participation' is nullable, and it is useful to be able to
# remember what user a session belongs to, even if they're not enrolled.
user = models.ForeignKey(settings.AUTH_USER_MODEL, null=True, blank=True,
- verbose_name=_('User'), on_delete=models.SET_NULL)
+ verbose_name=_("User"), on_delete=models.SET_NULL)
active_git_commit_sha = models.CharField(max_length=200,
- verbose_name=_('Active git commit SHA'))
+ verbose_name=_("Active git commit SHA"))
flow_id = models.CharField(max_length=200, db_index=True,
- verbose_name=_('Flow ID'))
+ verbose_name=_("Flow ID"))
start_time = models.DateTimeField(default=now,
- verbose_name=_('Start time'))
+ verbose_name=_("Start time"))
completion_time = models.DateTimeField(null=True, blank=True,
- verbose_name=_('Completion time'))
+ verbose_name=_("Completion time"))
page_count = models.IntegerField(null=True, blank=True,
- verbose_name=_('Page count'))
+ verbose_name=_("Page count"))
# This field allows avoiding redundant checks for whether the
# page data is in line with the course material and the current
@@ -893,20 +893,20 @@ class FlowSession(models.Model):
# See course.flow.adjust_flow_session_page_data.
page_data_at_revision_key = models.CharField(
max_length=200, null=True, blank=True,
- verbose_name=_('Page data at course revision'),
+ verbose_name=_("Page data at course revision"),
help_text=_(
"Page set-up data is up-to date for this revision of the "
"course material"))
in_progress = models.BooleanField(default=None,
- verbose_name=_('In progress'))
+ verbose_name=_("In progress"))
access_rules_tag = models.CharField(max_length=200, null=True,
blank=True,
- verbose_name=_('Access rules tag'))
+ verbose_name=_("Access rules tag"))
expiration_mode = models.CharField(max_length=20, null=True,
default=flow_session_expiration_mode.end,
choices=FLOW_SESSION_EXPIRATION_MODE_CHOICES,
- verbose_name=_('Expiration mode'))
+ verbose_name=_("Expiration mode"))
# Non-normal: These fields can be recomputed, albeit at great expense.
#
@@ -915,12 +915,12 @@ class FlowSession(models.Model):
points = models.DecimalField(max_digits=10, decimal_places=2,
blank=True, null=True,
- verbose_name=_('Points'))
+ verbose_name=_("Points"))
max_points = models.DecimalField(max_digits=10, decimal_places=2,
blank=True, null=True,
- verbose_name=_('Max point'))
+ verbose_name=_("Max point"))
result_comment = models.TextField(blank=True, null=True,
- verbose_name=_('Result comment'))
+ verbose_name=_("Result comment"))
class Meta:
verbose_name = _("Flow session")
@@ -930,13 +930,13 @@ class FlowSession(models.Model):
def __unicode__(self):
if self.participation is None:
return _("anonymous session %(session_id)d on '%(flow_id)s'") % {
- 'session_id': self.id,
- 'flow_id': self.flow_id}
+ "session_id": self.id,
+ "flow_id": self.flow_id}
else:
return _("%(user)s's session %(session_id)d on '%(flow_id)s'") % {
- 'user': self.participation.user,
- 'session_id': self.id,
- 'flow_id': self.flow_id}
+ "user": self.participation.user,
+ "session_id": self.id,
+ "flow_id": self.flow_id}
__str__ = __unicode__
@@ -987,32 +987,32 @@ class FlowSession(models.Model):
class FlowPageData(models.Model):
flow_session = models.ForeignKey(FlowSession, related_name="page_data",
- verbose_name=_('Flow session'), on_delete=models.CASCADE)
+ verbose_name=_("Flow session"), on_delete=models.CASCADE)
page_ordinal = models.IntegerField(null=True, blank=True,
- verbose_name=_('Page ordinal'))
+ verbose_name=_("Page ordinal"))
# This exists to catch changing page types in course content,
# which will generally lead to an inconsistency disaster.
page_type = models.CharField(max_length=200,
- verbose_name=_('Page type as indicated in course content'),
+ verbose_name=_("Page type as indicated in course content"),
null=True, blank=True)
group_id = models.CharField(max_length=200,
- verbose_name=_('Group ID'))
+ verbose_name=_("Group ID"))
page_id = models.CharField(max_length=200,
- verbose_name=_('Page ID'))
+ verbose_name=_("Page ID"))
data = JSONField(null=True, blank=True,
# Show correct characters in admin for non ascii languages.
- dump_kwargs={'ensure_ascii': False},
- verbose_name=_('Data'))
+ dump_kwargs={"ensure_ascii": False},
+ verbose_name=_("Data"))
title = models.CharField(max_length=1000,
- verbose_name=_('Page Title'), null=True, blank=True)
+ verbose_name=_("Page Title"), null=True, blank=True)
bookmarked = models.BooleanField(default=False,
help_text=_("A user-facing 'marking' feature to allow participants to "
"easily return to pages that still need their attention."),
- verbose_name=_('Bookmarked'))
+ verbose_name=_("Bookmarked"))
class Meta:
verbose_name = _("Flow page data")
@@ -1022,10 +1022,10 @@ class FlowPageData(models.Model):
# flow page data
return (_("Data for page '%(group_id)s/%(page_id)s' "
"(page ordinal %(page_ordinal)s) in %(flow_session)s") % {
- 'group_id': self.group_id,
- 'page_id': self.page_id,
- 'page_ordinal': self.page_ordinal,
- 'flow_session': self.flow_session})
+ "group_id": self.group_id,
+ "page_id": self.page_id,
+ "page_ordinal": self.page_ordinal,
+ "flow_session": self.flow_session})
__str__ = __unicode__
@@ -1049,34 +1049,34 @@ class FlowPageVisit(models.Model):
# page_data), but it helps the admin site understand the link
# and provide editing.
flow_session = models.ForeignKey(FlowSession, db_index=True,
- verbose_name=_('Flow session'), on_delete=models.CASCADE)
+ verbose_name=_("Flow session"), on_delete=models.CASCADE)
page_data = models.ForeignKey(FlowPageData, db_index=True,
- verbose_name=_('Page data'), on_delete=models.CASCADE)
+ verbose_name=_("Page data"), on_delete=models.CASCADE)
visit_time = models.DateTimeField(default=now, db_index=True,
- verbose_name=_('Visit time'))
+ verbose_name=_("Visit time"))
remote_address = models.GenericIPAddressField(null=True, blank=True,
- verbose_name=_('Remote address'))
+ verbose_name=_("Remote address"))
user = models.ForeignKey(settings.AUTH_USER_MODEL, null=True,
blank=True, related_name="visitor",
- verbose_name=_('User'), on_delete=models.SET_NULL)
+ verbose_name=_("User"), on_delete=models.SET_NULL)
impersonated_by = models.ForeignKey(settings.AUTH_USER_MODEL,
null=True, blank=True, related_name="impersonator",
- verbose_name=_('Impersonated by'), on_delete=models.SET_NULL)
+ verbose_name=_("Impersonated by"), on_delete=models.SET_NULL)
is_synthetic = models.BooleanField(default=False,
help_text=_("Synthetic flow page visits are generated for "
"unvisited pages once a flow is finished. This is needed "
"since grade information is attached to a visit, and it "
"needs a place to go."),
- verbose_name=_('Is synthetic'))
+ verbose_name=_("Is synthetic"))
answer = JSONField(null=True, blank=True,
# Show correct characters in admin for non ascii languages.
- dump_kwargs={'ensure_ascii': False},
+ dump_kwargs={"ensure_ascii": False},
# Translators: "Answer" is a Noun.
- verbose_name=_('Answer'))
+ verbose_name=_("Answer"))
# is_submitted_answer may seem redundant with answers being
# non-NULL, but it isn't. This supports saved (but as
@@ -1089,7 +1089,7 @@ class FlowPageVisit(models.Model):
is_submitted_answer = models.NullBooleanField(
# Translators: determine whether the answer is a final,
# submitted answer fit for grading.
- verbose_name=_('Is submitted answer'))
+ verbose_name=_("Is submitted answer"))
def __unicode__(self):
result = (
@@ -1147,22 +1147,22 @@ class FlowPageVisit(models.Model):
class FlowPageVisitGrade(models.Model):
visit = models.ForeignKey(FlowPageVisit, related_name="grades",
- verbose_name=_('Visit'), on_delete=models.CASCADE)
+ verbose_name=_("Visit"), on_delete=models.CASCADE)
- # NULL means 'autograded'
+ # NULL means "autograded"
grader = models.ForeignKey(settings.AUTH_USER_MODEL, null=True, blank=True,
- verbose_name=_('Grader'), on_delete=models.SET_NULL)
+ verbose_name=_("Grader"), on_delete=models.SET_NULL)
grade_time = models.DateTimeField(db_index=True, default=now,
- verbose_name=_('Grade time'))
+ verbose_name=_("Grade time"))
graded_at_git_commit_sha = models.CharField(
max_length=200, null=True, blank=True,
- verbose_name=_('Graded at git commit SHA'))
+ verbose_name=_("Graded at git commit SHA"))
grade_data = JSONField(null=True, blank=True,
# Show correct characters in admin for non ascii languages.
- dump_kwargs={'ensure_ascii': False},
- verbose_name=_('Grade data'))
+ dump_kwargs={"ensure_ascii": False},
+ verbose_name=_("Grade data"))
# This data should be recomputable, but we'll cache it here,
# because it might be very expensive (container-launch expensive
@@ -1172,12 +1172,12 @@ class FlowPageVisitGrade(models.Model):
# Translators: max point in grade
help_text=_("Point value of this question when receiving "
"full credit."),
- verbose_name=_('Max points'))
+ verbose_name=_("Max points"))
correctness = models.FloatField(null=True, blank=True,
# Translators: correctness in grade
help_text=_("Real number between zero and one (inclusively) "
"indicating the degree of correctness of the answer."),
- verbose_name=_('Correctness'))
+ verbose_name=_("Correctness"))
# This JSON object has fields corresponding to
# :class:`course.page.AnswerFeedback`, except for
@@ -1186,9 +1186,9 @@ class FlowPageVisitGrade(models.Model):
feedback = JSONField(null=True, blank=True,
# Show correct characters in admin for non ascii languages.
- dump_kwargs={'ensure_ascii': False},
+ dump_kwargs={"ensure_ascii": False},
# Translators: "Feedback" stands for the feedback of answers.
- verbose_name=_('Feedback'))
+ verbose_name=_("Feedback"))
def percentage(self):
if self.correctness is not None:
@@ -1224,14 +1224,14 @@ class FlowPageBulkFeedback(models.Model):
# We're only storing one of these per page, because
# they're 'bulk' (i.e. big, like plots or program output)
page_data = models.OneToOneField(FlowPageData,
- verbose_name=_('Page data'), on_delete=models.CASCADE)
+ verbose_name=_("Page data"), on_delete=models.CASCADE)
grade = models.ForeignKey(FlowPageVisitGrade,
- verbose_name=_('Grade'), on_delete=models.CASCADE)
+ verbose_name=_("Grade"), on_delete=models.CASCADE)
bulk_feedback = JSONField(null=True, blank=True,
# Show correct characters in admin for non ascii languages.
- dump_kwargs={'ensure_ascii': False},
- verbose_name=_('Bulk feedback'))
+ dump_kwargs={"ensure_ascii": False},
+ verbose_name=_("Bulk feedback"))
def update_bulk_feedback(page_data, grade, bulk_feedback_json):
@@ -1293,11 +1293,11 @@ class FlowAccessException(models.Model): # pragma: no cover (deprecated and not
# deprecated
participation = models.ForeignKey(Participation, db_index=True,
- verbose_name=_('Participation'), on_delete=models.CASCADE)
+ verbose_name=_("Participation"), on_delete=models.CASCADE)
flow_id = models.CharField(max_length=200, blank=False, null=False,
- verbose_name=_('Flow ID'))
+ verbose_name=_("Flow ID"))
expiration = models.DateTimeField(blank=True, null=True,
- verbose_name=_('Expiration'))
+ verbose_name=_("Expiration"))
stipulations = JSONField(blank=True, null=True,
# Translators: help text for stipulations in FlowAccessException
@@ -1307,13 +1307,13 @@ class FlowAccessException(models.Model): # pragma: no cover (deprecated and not
"credit_percent. If not specified here, values will default "
"to the stipulations in the course content."),
validators=[validate_stipulations],
- dump_kwargs={'ensure_ascii': False},
- verbose_name=_('Stipulations'))
+ dump_kwargs={"ensure_ascii": False},
+ verbose_name=_("Stipulations"))
creator = models.ForeignKey(settings.AUTH_USER_MODEL, null=True,
- verbose_name=_('Creator'), on_delete=models.SET_NULL)
+ verbose_name=_("Creator"), on_delete=models.SET_NULL)
creation_time = models.DateTimeField(default=now, db_index=True,
- verbose_name=_('Creation time'))
+ verbose_name=_("Creation time"))
is_sticky = models.BooleanField(
default=False,
@@ -1322,10 +1322,10 @@ class FlowAccessException(models.Model): # pragma: no cover (deprecated and not
"exception rule set should stay "
"under this rule set until it is expired."),
# Translators: deprecated
- verbose_name=_('Is sticky'))
+ verbose_name=_("Is sticky"))
comment = models.TextField(blank=True, null=True,
- verbose_name=_('Comment'))
+ verbose_name=_("Comment"))
def __unicode__(self):
return (
@@ -1346,10 +1346,10 @@ class FlowAccessExceptionEntry(models.Model): # pragma: no cover (deprecated an
exception = models.ForeignKey(FlowAccessException,
related_name="entries",
- verbose_name=_('Exception'), on_delete=models.CASCADE)
+ verbose_name=_("Exception"), on_delete=models.CASCADE)
permission = models.CharField(max_length=50,
choices=FLOW_PERMISSION_CHOICES,
- verbose_name=_('Permission'))
+ verbose_name=_("Permission"))
class Meta:
# Translators: FlowAccessExceptionEntry (deprecated)
@@ -1365,25 +1365,25 @@ class FlowAccessExceptionEntry(models.Model): # pragma: no cover (deprecated an
class FlowRuleException(models.Model):
flow_id = models.CharField(max_length=200, blank=False, null=False,
- verbose_name=_('Flow ID'))
+ verbose_name=_("Flow ID"))
participation = models.ForeignKey(Participation, db_index=True,
- verbose_name=_('Participation'), on_delete=models.CASCADE)
+ verbose_name=_("Participation"), on_delete=models.CASCADE)
expiration = models.DateTimeField(blank=True, null=True,
- verbose_name=_('Expiration'))
+ verbose_name=_("Expiration"))
creator = models.ForeignKey(settings.AUTH_USER_MODEL, null=True,
- verbose_name=_('Creator'), on_delete=models.SET_NULL)
+ verbose_name=_("Creator"), on_delete=models.SET_NULL)
creation_time = models.DateTimeField(default=now, db_index=True,
- verbose_name=_('Creation time'))
+ verbose_name=_("Creation time"))
comment = models.TextField(blank=True, null=True,
- verbose_name=_('Comment'))
+ verbose_name=_("Comment"))
kind = models.CharField(max_length=50, blank=False, null=False,
choices=FLOW_RULE_KIND_CHOICES,
- verbose_name=_('Kind'))
+ verbose_name=_("Kind"))
rule = YAMLField(blank=False, null=False,
- verbose_name=_('Rule'))
+ verbose_name=_("Rule"))
active = models.BooleanField(default=True,
verbose_name=pgettext_lazy(
"Is the flow rule exception activated?", "Active"))
@@ -1474,13 +1474,13 @@ class FlowRuleException(models.Model):
class GradingOpportunity(models.Model):
course = models.ForeignKey(Course,
- verbose_name=_('Course'), on_delete=models.CASCADE)
+ verbose_name=_("Course"), on_delete=models.CASCADE)
identifier = models.CharField(max_length=200, blank=False, null=False,
# Translators: format of identifier for GradingOpportunity
help_text=_("A symbolic name for this grade. "
"lower_case_with_underscores, no spaces."),
- verbose_name=_('Grading opportunity ID'),
+ verbose_name=_("Grading opportunity ID"),
validators=[
RegexValidator(
"^"+GRADING_OPP_ID_REGEX+"$",
@@ -1491,25 +1491,25 @@ class GradingOpportunity(models.Model):
name = models.CharField(max_length=200, blank=False, null=False,
# Translators: name for GradingOpportunity
help_text=_("A human-readable identifier for the grade."),
- verbose_name=_('Grading opportunity name'))
+ verbose_name=_("Grading opportunity name"))
flow_id = models.CharField(max_length=200, blank=True, null=True,
help_text=_("Flow identifier that this grading opportunity "
"is linked to, if any"),
- verbose_name=_('Flow ID'))
+ verbose_name=_("Flow ID"))
aggregation_strategy = models.CharField(max_length=20,
choices=GRADE_AGGREGATION_STRATEGY_CHOICES,
# Translators: strategy on how the grading of mutiple sessioins
# are aggregated.
- verbose_name=_('Aggregation strategy'))
+ verbose_name=_("Aggregation strategy"))
due_time = models.DateTimeField(default=None, blank=True, null=True,
- verbose_name=_('Due time'))
+ verbose_name=_("Due time"))
creation_time = models.DateTimeField(default=now,
- verbose_name=_('Creation time'))
+ verbose_name=_("Creation time"))
shown_in_grade_book = models.BooleanField(default=True,
- verbose_name=_('Shown in grade book'))
+ verbose_name=_("Shown in grade book"))
shown_in_participant_grade_book = models.BooleanField(default=True,
verbose_name=_("Shown in student grade book"))
result_shown_in_participant_grade_book = models.BooleanField(default=True,
@@ -1519,16 +1519,16 @@ class GradingOpportunity(models.Model):
verbose_name=_("Scores for individual pages are shown "
"in the participants' grade book"))
hide_superseded_grade_history_before = models.DateTimeField(
- verbose_name=_('Hide superseded grade history before'),
+ verbose_name=_("Hide superseded grade history before"),
blank=True, null=True,
help_text=_(
- 'Grade changes dated before this date that are '
- 'superseded by later grade changes will not be shown to '
- 'participants. '
- 'This can help avoid discussions about pre-release grading '
- 'adjustments. '
- 'May be blank. In that case, the entire grade history is '
- 'shown.'))
+ "Grade changes dated before this date that are "
+ "superseded by later grade changes will not be shown to "
+ "participants. "
+ "This can help avoid discussions about pre-release grading "
+ "adjustments. "
+ "May be blank. In that case, the entire grade history is "
+ "shown."))
class Meta:
verbose_name = _("Grading opportunity")
@@ -1561,15 +1561,15 @@ class GradeChange(models.Model):
ones.
"""
opportunity = models.ForeignKey(GradingOpportunity,
- verbose_name=_('Grading opportunity'), on_delete=models.CASCADE)
+ verbose_name=_("Grading opportunity"), on_delete=models.CASCADE)
participation = models.ForeignKey(Participation,
- verbose_name=_('Participation'), on_delete=models.CASCADE)
+ verbose_name=_("Participation"), on_delete=models.CASCADE)
state = models.CharField(max_length=50,
choices=GRADE_STATE_CHANGE_CHOICES,
# Translators: something like 'status'.
- verbose_name=_('State'))
+ verbose_name=_("State"))
attempt_id = models.CharField(max_length=50, null=True, blank=True,
default="main",
@@ -1577,28 +1577,28 @@ class GradeChange(models.Model):
help_text=_("Grade changes are grouped by their 'attempt ID' "
"where later grades with the same attempt ID supersede earlier "
"ones."),
- verbose_name=_('Attempt ID'))
+ verbose_name=_("Attempt ID"))
points = models.DecimalField(max_digits=10, decimal_places=2,
blank=True, null=True,
- verbose_name=_('Points'))
+ verbose_name=_("Points"))
max_points = models.DecimalField(max_digits=10, decimal_places=2,
- verbose_name=_('Max points'))
+ verbose_name=_("Max points"))
comment = models.TextField(blank=True, null=True,
- verbose_name=_('Comment'))
+ verbose_name=_("Comment"))
due_time = models.DateTimeField(default=None, blank=True, null=True,
- verbose_name=_('Due time'))
+ verbose_name=_("Due time"))
creator = models.ForeignKey(settings.AUTH_USER_MODEL, null=True,
- verbose_name=_('Creator'), on_delete=models.SET_NULL)
+ verbose_name=_("Creator"), on_delete=models.SET_NULL)
grade_time = models.DateTimeField(default=now, db_index=True,
- verbose_name=_('Grade time'))
+ verbose_name=_("Grade time"))
flow_session = models.ForeignKey(FlowSession, null=True, blank=True,
related_name="grade_changes",
- verbose_name=_('Flow session'), on_delete=models.SET_NULL)
+ verbose_name=_("Flow session"), on_delete=models.SET_NULL)
class Meta:
verbose_name = _("Grade change")
@@ -1608,9 +1608,9 @@ class GradeChange(models.Model):
def __unicode__(self):
# Translators: information for GradeChange
return _("%(participation)s %(state)s on %(opportunityname)s") % {
- 'participation': self.participation,
- 'state': self.state,
- 'opportunityname': self.opportunity.name}
+ "participation": self.participation,
+ "state": self.state,
+ "opportunityname": self.opportunity.name}
__str__ = __unicode__
@@ -1852,11 +1852,11 @@ def get_flow_grading_opportunity(course, flow_id, flow_desc,
class InstantMessage(models.Model):
participation = models.ForeignKey(Participation,
- verbose_name=_('Participation'), on_delete=models.CASCADE)
+ verbose_name=_("Participation"), on_delete=models.CASCADE)
text = models.CharField(max_length=200,
- verbose_name=_('Text'))
+ verbose_name=_("Text"))
time = models.DateTimeField(default=now,
- verbose_name=_('Time'))
+ verbose_name=_("Time"))
class Meta:
verbose_name = _("Instant message")
@@ -1875,27 +1875,27 @@ class InstantMessage(models.Model):
class Exam(models.Model):
course = models.ForeignKey(Course,
- verbose_name=_('Course'), on_delete=models.CASCADE)
+ verbose_name=_("Course"), on_delete=models.CASCADE)
description = models.CharField(max_length=200,
- verbose_name=_('Description'))
+ verbose_name=_("Description"))
flow_id = models.CharField(max_length=200,
- verbose_name=_('Flow ID'))
+ verbose_name=_("Flow ID"))
active = models.BooleanField(
default=True,
verbose_name=_("Active"),
help_text=_(
- 'Currently active, i.e. may be used to log in '
- 'via an exam ticket'))
+ "Currently active, i.e. may be used to log in "
+ "via an exam ticket"))
listed = models.BooleanField(
verbose_name=_("Listed"),
default=True,
- help_text=_('Shown in the list of current exams'))
+ help_text=_("Shown in the list of current exams"))
no_exams_before = models.DateTimeField(
- verbose_name=_('No exams before'))
+ verbose_name=_("No exams before"))
no_exams_after = models.DateTimeField(
null=True, blank=True,
- verbose_name=_('No exams after'))
+ verbose_name=_("No exams after"))
class Meta:
verbose_name = _("Exam")
@@ -1904,8 +1904,8 @@ class Exam(models.Model):
def __unicode__(self):
return _("Exam %(description)s in %(course)s") % {
- 'description': self.description,
- 'course': self.course,
+ "description": self.description,
+ "course": self.course,
}
__str__ = __unicode__
@@ -1913,35 +1913,35 @@ class Exam(models.Model):
class ExamTicket(models.Model):
exam = models.ForeignKey(Exam,
- verbose_name=_('Exam'), on_delete=models.CASCADE)
+ verbose_name=_("Exam"), on_delete=models.CASCADE)
participation = models.ForeignKey(Participation, db_index=True,
- verbose_name=_('Participation'), on_delete=models.CASCADE)
+ verbose_name=_("Participation"), on_delete=models.CASCADE)
creator = models.ForeignKey(settings.AUTH_USER_MODEL, null=True,
- verbose_name=_('Creator'), on_delete=models.SET_NULL)
+ verbose_name=_("Creator"), on_delete=models.SET_NULL)
creation_time = models.DateTimeField(default=now,
- verbose_name=_('Creation time'))
+ verbose_name=_("Creation time"))
usage_time = models.DateTimeField(
verbose_name=_("Usage time"),
- help_text=_('Date and time of first usage of ticket'),
+ help_text=_("Date and time of first usage of ticket"),
null=True, blank=True)
state = models.CharField(max_length=50,
choices=EXAM_TICKET_STATE_CHOICES,
- verbose_name=_('Exam ticket state'))
+ verbose_name=_("Exam ticket state"))
code = models.CharField(max_length=50, db_index=True, unique=True)
valid_start_time = models.DateTimeField(
verbose_name=_("End valid period"),
- help_text=_('If not blank, date and time at which this exam ticket '
- 'starts being valid/usable'),
+ help_text=_("If not blank, date and time at which this exam ticket "
+ "starts being valid/usable"),
null=True, blank=True)
valid_end_time = models.DateTimeField(
verbose_name=_("End valid period"),
- help_text=_('If not blank, date and time at which this exam ticket '
- 'stops being valid/usable'),
+ help_text=_("If not blank, date and time at which this exam ticket "
+ "stops being valid/usable"),
null=True, blank=True)
restrict_to_facility = models.CharField(max_length=200, blank=True, null=True,
verbose_name=_("Restrict to facility"),
@@ -1958,8 +1958,8 @@ class ExamTicket(models.Model):
def __unicode__(self):
return _("Exam ticket for %(participation)s in %(exam)s") % {
- 'participation': self.participation,
- 'exam': self.exam,
+ "participation": self.participation,
+ "exam": self.exam,
}
__str__ = __unicode__
diff --git a/course/page/base.py b/course/page/base.py
index 3f320f5a1f47489518a88b96d0b1ae585fc2bb53..319d2183f5ecf68e3663348385a4612b81b8b154 100644
--- a/course/page/base.py
+++ b/course/page/base.py
@@ -1104,8 +1104,8 @@ class PageBaseWithHumanTextFeedback(PageBase):
msg = EmailMessage(
string_concat("[%(identifier)s:%(flow_id)s] ",
_("New notification"))
- % {'identifier': page_context.course.identifier,
- 'flow_id': page_context.flow_session.flow_id},
+ % {"identifier": page_context.course.identifier,
+ "flow_id": page_context.flow_session.flow_id},
message,
getattr(settings, "GRADER_FEEDBACK_EMAIL_FROM",
page_context.course.get_from_email()),
@@ -1152,9 +1152,9 @@ class PageBaseWithHumanTextFeedback(PageBase):
msg = EmailMessage(
string_concat("[%(identifier)s:%(flow_id)s] ",
_("Grading notes from %(ta)s"))
- % {'identifier': page_context.course.identifier,
- 'flow_id': page_context.flow_session.flow_id,
- 'ta': request.user.get_full_name()
+ % {"identifier": page_context.course.identifier,
+ "flow_id": page_context.flow_session.flow_id,
+ "ta": request.user.get_full_name()
},
message,
getattr(settings, "GRADER_FEEDBACK_EMAIL_FROM",
diff --git a/course/page/choice.py b/course/page/choice.py
index 897924b2fec8e9cdda27ee0a2f8ab378830c6cb1..b25fc3d65d1430dffd093cbbfe9a86900ecef41c 100644
--- a/course/page/choice.py
+++ b/course/page/choice.py
@@ -92,7 +92,7 @@ class ChoiceInfo(object):
except Exception:
raise ValidationError(
_("%(location)s: unable to convert to string")
- % {'location': location})
+ % {"location": location})
tag_mode_dict = {
cls.CORRECT_TAG: ChoiceModes.CORRECT,
@@ -118,8 +118,8 @@ class ChoiceInfo(object):
raise ValidationError(
_("%(location)s: more than one choice modes "
"set: '%(modes)s'")
- % {'location': location,
- 'modes':
+ % {"location": location,
+ "modes":
"".join([find_tag_by_mode(item_mode[0]),
prefix])
})
@@ -310,8 +310,8 @@ class ChoiceQuestion(ChoiceQuestionBase):
_("one or more correct answer(s) "
"expected, %(n_correct)d found"))
% {
- 'location': location,
- 'n_correct': self.correct_choice_count})
+ "location": location,
+ "n_correct": self.correct_choice_count})
if self.disregard_choice_count:
raise ValidationError(
@@ -319,7 +319,7 @@ class ChoiceQuestion(ChoiceQuestionBase):
"%(location)s: ",
_("ChoiceQuestion does not allow any choices "
"marked 'disregard'"))
- % {'location': location})
+ % {"location": location})
if self.always_correct_choice_count:
raise ValidationError(
@@ -327,7 +327,7 @@ class ChoiceQuestion(ChoiceQuestionBase):
"%(location)s: ",
_("ChoiceQuestion does not allow any choices "
"marked 'always_correct'"))
- % {'location': location})
+ % {"location": location})
def allowed_attrs(self):
return super(ChoiceQuestion, self).allowed_attrs() + (
@@ -351,7 +351,7 @@ class ChoiceQuestion(ChoiceQuestionBase):
*args, **kwargs)
if not page_behavior.may_change_answer:
- form.fields['choice'].widget.attrs['disabled'] = True
+ form.fields["choice"].widget.attrs["disabled"] = True
return form
@@ -510,7 +510,7 @@ class MultipleChoiceQuestion(ChoiceQuestionBase):
_("'allow_partial_credit' or "
"'allow_partial_credit_subset_only' may not be specified"
"at the same time as 'credit_mode'"))
- % {'location': location})
+ % {"location": location})
else:
@@ -531,7 +531,7 @@ class MultipleChoiceQuestion(ChoiceQuestionBase):
_("'allow_partial_credit' and "
"'allow_partial_credit_subset_only' are not allowed to "
"coexist when both attribute are 'True'"))
- % {'location': location})
+ % {"location": location})
if credit_mode not in [
"exact",
@@ -541,7 +541,7 @@ class MultipleChoiceQuestion(ChoiceQuestionBase):
string_concat(
"%(location)s: ",
_("unrecognized credit_mode '%(credit_mode)s'"))
- % {'location': location, "credit_mode": credit_mode})
+ % {"location": location, "credit_mode": credit_mode})
if vctx is not None and not hasattr(pd, "credit_mode"):
vctx.add_warning(location,
@@ -578,7 +578,7 @@ class MultipleChoiceQuestion(ChoiceQuestionBase):
*args, **kwargs)
if not page_behavior.may_change_answer:
- form.fields['choice'].widget.attrs['disabled'] = True
+ form.fields["choice"].widget.attrs["disabled"] = True
return form
@@ -801,7 +801,7 @@ class SurveyChoiceQuestion(PageBaseWithTitle):
*args, **kwargs)
if not page_behavior.may_change_answer:
- form.fields['choice'].widget.attrs['disabled'] = True
+ form.fields["choice"].widget.attrs["disabled"] = True
return form
diff --git a/course/page/code.py b/course/page/code.py
index 7787640e9aac8cab535af102c1e8728f95d2ae82..19566a3aa45fd80afb6d574f787cf0a6e4b02d58 100644
--- a/course/page/code.py
+++ b/course/page/code.py
@@ -200,12 +200,12 @@ def request_run(run_req, run_timeout, image=None):
def debug_print(s):
pass
- command_path = '/opt/runcode/runcode'
- user = 'runcode'
+ command_path = "/opt/runcode/runcode"
+ user = "runcode"
# The following is necessary because tests don't arise from a CodeQuestion
# object, so we provide a fallback.
- debug_print('Image is %s.' % repr(image))
+ debug_print("Image is %s." % repr(image))
if image is None:
image = settings.RELATE_DOCKER_RUNPY_IMAGE
@@ -238,7 +238,7 @@ def request_run(run_req, run_timeout, image=None):
else:
container_id = None
- connect_host_ip = 'localhost'
+ connect_host_ip = "localhost"
try:
# FIXME: Prohibit networking
@@ -282,7 +282,7 @@ def request_run(run_req, run_timeout, image=None):
try:
connection = http_client.HTTPConnection(connect_host_ip, port)
- connection.request('GET', '/ping')
+ connection.request("GET", "/ping")
response = connection.getresponse()
response_data = response.read().decode()
@@ -315,7 +315,7 @@ def request_run(run_req, run_timeout, image=None):
connection = http_client.HTTPConnection(connect_host_ip, port,
timeout=1 + run_timeout)
- headers = {'Content-type': 'application/json'}
+ headers = {"Content-type": "application/json"}
json_run_req = json.dumps(run_req).encode("utf-8")
@@ -323,7 +323,7 @@ def request_run(run_req, run_timeout, image=None):
start_time = time()
debug_print("BEFPOST")
- connection.request('POST', '/run-python', json_run_req, headers)
+ connection.request("POST", "/run-python", json_run_req, headers)
debug_print("AFTPOST")
http_response = connection.getresponse()
@@ -1199,7 +1199,7 @@ class PythonCodeQuestion(CodeQuestion):
@property
def language_mode(self):
- return 'python'
+ return "python"
@property
def container_image(self):
@@ -1207,9 +1207,9 @@ class PythonCodeQuestion(CodeQuestion):
@property
def suffix(self):
- return '.py'
+ return ".py"
- def __init__(self, vctx, location, page_desc, language_mode='python'):
+ def __init__(self, vctx, location, page_desc, language_mode="python"):
super(PythonCodeQuestion, self).__init__(vctx, location, page_desc,
language_mode)
@@ -1279,7 +1279,7 @@ class PythonCodeQuestionWithHumanTextFeedback(
_("'human_feedback_value' and "
"'human_feedback_percentage' are not "
"allowed to coexist"))
- % {'location': location}
+ % {"location": location}
)
if not (hasattr(self.page_desc, "human_feedback_value")
or hasattr(self.page_desc, "human_feedback_percentage")):
@@ -1288,7 +1288,7 @@ class PythonCodeQuestionWithHumanTextFeedback(
"%(location)s: ",
_("expecting either 'human_feedback_value' "
"or 'human_feedback_percentage', found neither."))
- % {'location': location}
+ % {"location": location}
)
if hasattr(self.page_desc, "human_feedback_value"):
vctx.add_warning(
diff --git a/course/page/code_run_backend.py b/course/page/code_run_backend.py
index fee8718c4e82447c8213723e9f314a81c3dd325a..93f3da39af3d1b30fddc80863fe44f969ca9d7ee 100644
--- a/course/page/code_run_backend.py
+++ b/course/page/code_run_backend.py
@@ -177,7 +177,7 @@ def run_code(result, run_req):
if getattr(run_req, "setup_code", None):
try:
setup_code = compile(
- run_req.setup_code, "[setup code]", 'exec')
+ run_req.setup_code, "[setup code]", "exec")
except Exception:
package_exception(result, "setup_compile_error")
return
@@ -186,7 +186,7 @@ def run_code(result, run_req):
try:
user_code = compile(
- run_req.user_code, "[user code]", 'exec')
+ run_req.user_code, "[user code]", "exec")
except Exception:
package_exception(result, "user_compile_error")
return
@@ -194,7 +194,7 @@ def run_code(result, run_req):
if getattr(run_req, "test_code", None):
try:
test_code = compile(
- run_req.test_code, "[test code]", 'exec')
+ run_req.test_code, "[test code]", "exec")
except Exception:
package_exception(result, "test_compile_error")
return
diff --git a/course/page/inline.py b/course/page/inline.py
index 9f33ed25a5f5ec48931ccbaf61aa1c4cd372c920..0b2a16de14b95a0d897eaa753be9ca735dcb8e97 100644
--- a/course/page/inline.py
+++ b/course/page/inline.py
@@ -97,11 +97,11 @@ class InlineMultiQuestionForm(StyledInlineForm):
if isinstance(self.fields[field_name].widget,
forms.widgets.Select):
# This will also disable the option dropdown
- self.fields[field_name].widget.attrs['disabled'] \
+ self.fields[field_name].widget.attrs["disabled"] \
= "disabled"
else:
# Then it should be a TextInput widget
- self.fields[field_name].widget.attrs['readonly'] \
+ self.fields[field_name].widget.attrs["readonly"] \
= "readonly"
self.helper.layout.extend([HTML("
")])
@@ -139,8 +139,8 @@ def get_question_class(location, q_type, answers_desc):
"%(location)s: ",
_("unknown embedded question type '%(type)s'"))
% {
- 'location': location,
- 'type': q_type})
+ "location": location,
+ "type": q_type})
def parse_question(vctx, location, name, answers_desc):
@@ -439,9 +439,9 @@ class ChoicesAnswer(AnswerBase):
"%(location)s: '%(answer_name)s' ",
_("choice %(idx)d: unable to convert to string")
)
- % {'location': location,
- 'answer_name': self.name,
- 'idx': choice_idx+1})
+ % {"location": location,
+ "answer_name": self.name,
+ "idx": choice_idx+1})
if choice.startswith(self.CORRECT_TAG):
correct_choice_count += 1
@@ -458,9 +458,9 @@ class ChoicesAnswer(AnswerBase):
" for question '%(question_name)s', "
"%(n_correct)d found"))
% {
- 'location': location,
- 'question_name': self.name,
- 'n_correct': correct_choice_count})
+ "location": location,
+ "question_name": self.name,
+ "n_correct": correct_choice_count})
self.hint = getattr(self.answers_desc, "hint", "")
self.width = 0
@@ -695,7 +695,7 @@ class InlineMultiQuestion(TextQuestionBase, PageBaseWithValue):
"%(location)s: ",
_("InlineMultiQuestion requires at least one "
"answer field to be defined."))
- % {'location': location})
+ % {"location": location})
for answers_name in answers_name_list:
if NAME_VALIDATE_RE.match(answers_name) is None:
@@ -883,7 +883,7 @@ class InlineMultiQuestion(TextQuestionBase, PageBaseWithValue):
# This happens when rendering the form in analytics view.
if not request:
- context.update({'csrf_token': "None"})
+ context.update({"csrf_token": "None"})
return loader.render_to_string(
"course/custom-crispy-inline-form.html",
diff --git a/course/page/text.py b/course/page/text.py
index 341eb0258b3eaef552efcff037d71958f693dccf..ae51b786f4ce19ee882b4a7855b916cefe4372cc 100644
--- a/course/page/text.py
+++ b/course/page/text.py
@@ -181,7 +181,7 @@ def get_validator_class(location, validator_type):
"%(location)s: ",
_("unknown validator type"),
"'%(type)s'")
- % {'location': location, 'type': validator_type})
+ % {"location": location, "type": validator_type})
def parse_validator(vctx, location, validator_desc):
@@ -340,7 +340,7 @@ class SymbolicExpressionMatcher(TextAnswerMatcher):
_("unable to check symbolic expression"),
"(%(err_type)s: %(err_str)s)")
% {
- 'location': location,
+ "location": location,
"err_type": tp.__name__,
"err_str": str(e)
})
@@ -550,9 +550,9 @@ def get_matcher_class(location, matcher_type, pattern_type):
_("%(matcherclassname)s only accepts "
"'%(matchertype)s' patterns"))
% {
- 'location': location,
- 'matcherclassname': matcher_class.__name__,
- 'matchertype': matcher_class.pattern_type})
+ "location": location,
+ "matcherclassname": matcher_class.__name__,
+ "matchertype": matcher_class.pattern_type})
return matcher_class
@@ -561,8 +561,8 @@ def get_matcher_class(location, matcher_type, pattern_type):
"%(location)s: ",
_("unknown match type '%(matchertype)s'"))
% {
- 'location': location,
- 'matchertype': matcher_type})
+ "location": location,
+ "matchertype": matcher_type})
def parse_matcher_string(vctx, location, matcher_desc):
@@ -665,8 +665,8 @@ class TextQuestionBase(PageBaseWithTitle):
_("unrecognized widget type"),
"'%(type)s'")
% {
- 'location': location,
- 'type': getattr(page_desc, "widget")})
+ "location": location,
+ "type": getattr(page_desc, "widget")})
def required_attrs(self):
return super(TextQuestionBase, self).required_attrs() + (
diff --git a/course/page/upload.py b/course/page/upload.py
index a3347275608764813c037d7d2add8a8c7986b038..52ff8ca8a28b195265e03478826a51c7b00cc4d6 100644
--- a/course/page/upload.py
+++ b/course/page/upload.py
@@ -44,7 +44,7 @@ from crispy_forms.layout import Layout, Field
class FileUploadForm(StyledForm):
show_save_button = False
uploaded_file = forms.FileField(required=True,
- label=gettext_lazy('Uploaded file'))
+ label=gettext_lazy("Uploaded file"))
def __init__(self, maximum_megabytes, mime_types, *args, **kwargs):
super(FileUploadForm, self).__init__(*args, **kwargs)
@@ -66,15 +66,15 @@ class FileUploadForm(StyledForm):
Field("uploaded_file", **field_kwargs))
def clean_uploaded_file(self):
- uploaded_file = self.cleaned_data['uploaded_file']
+ uploaded_file = self.cleaned_data["uploaded_file"]
from django.template.defaultfilters import filesizeformat
if uploaded_file.size > self.max_file_size:
raise forms.ValidationError(
_("Please keep file size under %(allowedsize)s. "
"Current filesize is %(uploadedsize)s.")
- % {'allowedsize': filesizeformat(self.max_file_size),
- 'uploadedsize': filesizeformat(uploaded_file.size)})
+ % {"allowedsize": filesizeformat(self.max_file_size),
+ "uploadedsize": filesizeformat(uploaded_file.size)})
if self.mime_types is not None and self.mime_types == ["application/pdf"]:
if uploaded_file.read()[:4] != b"%PDF":
@@ -165,7 +165,7 @@ class FileUploadQuestion(PageBaseWithTitle, PageBaseWithValue,
_("unrecognized mime types"),
" '%(presenttype)s'")
% {
- 'presenttype': ", ".join(
+ "presenttype": ", ".join(
set(page_desc.mime_types)
- set(self.ALLOWED_MIME_TYPES))})
@@ -175,7 +175,7 @@ class FileUploadQuestion(PageBaseWithTitle, PageBaseWithValue,
location, ": ",
_("'maximum_megabytes' expects a positive value, "
"got %(value)s instead")
- % {'value': str(page_desc.maximum_megabytes)}))
+ % {"value": str(page_desc.maximum_megabytes)}))
if vctx is not None:
if not hasattr(page_desc, "value"):
diff --git a/course/sandbox.py b/course/sandbox.py
index 9499d5d5bb10e4d1c9e2f3f8a9a4cb75346ce520..8f138010166f7f1c2f72243e1bad1a073e920575 100644
--- a/course/sandbox.py
+++ b/course/sandbox.py
@@ -114,8 +114,8 @@ def view_markup_sandbox(pctx):
preview_text = ""
def make_form(data=None):
- help_text = (gettext("Enter "
+ help_text = (gettext('Enter '
"RELATE markup."))
return SandboxForm(
None, "markdown", request.user.editor_mode,
diff --git a/course/tasks.py b/course/tasks.py
index 6bc61b0360637bc45349422f818834cb5d89f625..fec34acce6ee9809ed72524f966b27524d3d1bd1 100644
--- a/course/tasks.py
+++ b/course/tasks.py
@@ -59,8 +59,8 @@ def expire_in_progress_sessions(self, course_id, flow_id, rule_tag, now_datetime
count += 1
self.update_state(
- state='PROGRESS',
- meta={'current': i, 'total': nsessions})
+ state="PROGRESS",
+ meta={"current": i, "total": nsessions})
repo.close()
@@ -96,8 +96,8 @@ def finish_in_progress_sessions(self, course_id, flow_id, rule_tag, now_datetime
count += 1
self.update_state(
- state='PROGRESS',
- meta={'current': i, 'total': nsessions})
+ state="PROGRESS",
+ meta={"current": i, "total": nsessions})
repo.close()
@@ -127,8 +127,8 @@ def recalculate_ended_sessions(self, course_id, flow_id, rule_tag):
count += 1
self.update_state(
- state='PROGRESS',
- meta={'current': count, 'total': nsessions})
+ state="PROGRESS",
+ meta={"current": count, "total": nsessions})
repo.close()
@@ -161,8 +161,8 @@ def regrade_flow_sessions(self, course_id, flow_id, access_rules_tag, inprog_val
count += 1
self.update_state(
- state='PROGRESS',
- meta={'current': count, 'total': nsessions})
+ state="PROGRESS",
+ meta={"current": count, "total": nsessions})
repo.close()
diff --git a/course/templatetags/coursetags.py b/course/templatetags/coursetags.py
index 641a8d048d1b449d44acff13d1c39a79fa3d6989..1b5ce4e85edae7d18c7048f1d4181b7810682370 100644
--- a/course/templatetags/coursetags.py
+++ b/course/templatetags/coursetags.py
@@ -38,7 +38,7 @@ class GetCurrentLanguageJsFmtNode(Node):
lang_name = (
translation.to_locale(translation.get_language()).replace("_", "-"))
context[self.variable] = lang_name
- return ''
+ return ""
@register.tag("get_current_js_lang_name")
@@ -61,13 +61,13 @@ def do_get_current_js_lang_name(parser, token):
# token.split_contents() isn't useful here because this tag doesn't
# accept variable as arguments
args = token.contents.split()
- if len(args) != 3 or args[1] != 'as':
+ if len(args) != 3 or args[1] != "as":
raise TemplateSyntaxError("'get_current_js_lang_name' requires "
"'as variable' (got %r)" % args)
return GetCurrentLanguageJsFmtNode(args[2])
-@register.filter(name='js_lang_fallback')
+@register.filter(name="js_lang_fallback")
def js_lang_fallback(lang_name, js_name=None):
"""
Return the fallback lang name for js files.
@@ -90,7 +90,7 @@ def js_lang_fallback(lang_name, js_name=None):
# {{{ filter for participation.has_permission()
-@register.filter(name='has_permission')
+@register.filter(name="has_permission")
def has_permission(participation, arg):
"""
Check if a participation instance has specific permission.
@@ -115,7 +115,7 @@ def has_permission(participation, arg):
# }}}
-@register.filter(name='may_set_fake_time')
+@register.filter(name="may_set_fake_time")
def may_set_fake_time(user):
"""
Check if a user may set fake time.
@@ -126,7 +126,7 @@ def may_set_fake_time(user):
return msf(user)
-@register.filter(name='may_set_pretend_facility')
+@register.filter(name="may_set_pretend_facility")
def may_set_pretend_facility(user):
"""
Check if a user may set pretend_facility
@@ -137,7 +137,7 @@ def may_set_pretend_facility(user):
return mspf(user)
-@register.filter(name='commit_message_as_html')
+@register.filter(name="commit_message_as_html")
def commit_message_as_html(commit_sha, repo):
from course.versioning import _get_commit_message_as_html
return _get_commit_message_as_html(repo, commit_sha)
diff --git a/course/utils.py b/course/utils.py
index 8d8792a021c096bc0d38f3b1ea8c9454273554f3..9dc14704fb73dcf194642a25966f2a7bc45c5e4a 100644
--- a/course/utils.py
+++ b/course/utils.py
@@ -982,13 +982,13 @@ def get_codemirror_widget(
if interaction_mode == "vim":
actual_config["vimMode"] = True
- actual_addon_js += ('../keymap/vim',)
+ actual_addon_js += ("../keymap/vim",)
elif interaction_mode == "emacs":
actual_config["keyMap"] = "emacs"
- actual_addon_js += ('../keymap/emacs',)
+ actual_addon_js += ("../keymap/emacs",)
elif interaction_mode == "sublime":
actual_config["keyMap"] = "sublime"
- actual_addon_js += ('../keymap/sublime',)
+ actual_addon_js += ("../keymap/sublime",)
# every other interaction mode goes to default
if config is not None:
@@ -1042,7 +1042,7 @@ class FacilityFindingMiddleware(object):
else:
import ipaddress
remote_address = ipaddress.ip_address(
- str(request.META['REMOTE_ADDR']))
+ str(request.META["REMOTE_ADDR"]))
facilities = set()
@@ -1292,7 +1292,7 @@ class IpynbJinjaMacro(RelateJinjaMacroBase):
if clear_markdown:
nb_source_dict.update(
{"cells": [cell for cell in nb_source_dict["cells"]
- if cell['cell_type'] != "markdown"]})
+ if cell["cell_type"] != "markdown"]})
nb_source_dict.update({"cells": nb_source_dict["cells"]})
@@ -1344,7 +1344,7 @@ class NBConvertHTMLPostprocessor(markdown.postprocessors.Postprocessor):
class NBConvertExtension(markdown.Extension):
def extendMarkdown(self, md, md_globals): # noqa
- md.postprocessors['relate_nbconvert'] = NBConvertHTMLPostprocessor(md)
+ md.postprocessors["relate_nbconvert"] = NBConvertHTMLPostprocessor(md)
# }}}
diff --git a/course/validation.py b/course/validation.py
index 39d135c2a9841bec229bcde20c6101155b321b64..9e28e5e5e8c576c394c92ca8014d6933045cd1b4 100644
--- a/course/validation.py
+++ b/course/validation.py
@@ -81,7 +81,7 @@ def validate_identifier(vctx, location, s, warning_only=False):
msg = (string_concat(
_("invalid identifier"),
" '%(string)s'")
- % {'location': location, 'string': s})
+ % {"location": location, "string": s})
vctx.add_warning(location, msg)
else:
@@ -89,7 +89,7 @@ def validate_identifier(vctx, location, s, warning_only=False):
"%(location)s: ",
_("invalid identifier"),
" '%(string)s'")
- % {'location': location, 'string': s})
+ % {"location": location, "string": s})
raise ValidationError(msg)
@@ -106,7 +106,7 @@ def validate_role(vctx, location, role):
raise ValidationError(
string_concat("%(location)s: ",
_("invalid role '%(role)s'"))
- % {'location': location, 'role': role})
+ % {"location": location, "role": role})
def validate_facility(vctx, location, facility):
@@ -139,7 +139,7 @@ def validate_participationtag(vctx, location, participationtag):
from course.models import ParticipationTag
return list(
ParticipationTag.objects.filter(course=vctx.course)
- .values_list('name', flat=True))
+ .values_list("name", flat=True))
ptag_list = get_ptag_list(vctx)
if participationtag not in ptag_list:
@@ -195,7 +195,7 @@ def validate_struct(
raise ValidationError(
string_concat("%(location)s: ",
_("attribute '%(attr)s' missing"))
- % {'location': location, 'attr': attr})
+ % {"location": location, "attr": attr})
else:
present_attrs.remove(attr)
val = getattr(obj, attr)
@@ -212,10 +212,10 @@ def validate_struct(
"wrong type: got '%(name)s', "
"expected '%(allowed)s'"))
% {
- 'location': location,
- 'attr': attr,
- 'name': type(val).__name__,
- 'allowed': escape(str(allowed_types))})
+ "location": location,
+ "attr": attr,
+ "name": type(val).__name__,
+ "allowed": escape(str(allowed_types))})
if is_markup:
validate_markup(vctx, "%s: attribute %s" % (location, attr), val)
@@ -224,7 +224,7 @@ def validate_struct(
raise ValidationError(
string_concat("%(location)s: ",
_("extraneous attribute(s) '%(attr)s'"))
- % {'location': location, 'attr': ",".join(present_attrs)})
+ % {"location": location, "attr": ",".join(present_attrs)})
datespec_types = (datetime.date, str, datetime.datetime)
@@ -298,7 +298,7 @@ def validate_markup(vctx, location, markup_str):
raise ValidationError(
"%(location)s: %(err_type)s: %(err_str)s" % {
- 'location': location,
+ "location": location,
"err_type": tp.__name__,
"err_str": str(e)})
@@ -399,7 +399,7 @@ def validate_page_chunk(vctx, location, chunk):
raise ValidationError(
string_concat("%(location)s: ",
_("no title present"))
- % {'location': location})
+ % {"location": location})
if hasattr(chunk, "rules"):
for i, rule in enumerate(chunk.rules):
@@ -431,7 +431,7 @@ def validate_staticpage_desc(vctx, location, page_desc):
raise ValidationError(
string_concat("%(location)s: ",
_("must have either 'chunks' or 'content'"))
- % {'location': location})
+ % {"location": location})
# }}}
@@ -458,7 +458,7 @@ def validate_staticpage_desc(vctx, location, page_desc):
string_concat(
"%(location)s: ",
_("chunk id '%(chunkid)s' not unique"))
- % {'location': location, 'chunkid': chunk.id})
+ % {"location": location, "chunkid": chunk.id})
chunk_ids.add(chunk.id)
@@ -527,10 +527,10 @@ def validate_flow_page(vctx, location, page_desc):
": %(err_type)s: "
"%(err_str)s
%(format_exc)s") % { - 'location': location, + "location": location, "err_type": tp.__name__, # type: ignore "err_str": str(e), - 'format_exc': format_exc()}) + "format_exc": format_exc()}) def validate_flow_group(vctx, location, grp): @@ -553,7 +553,7 @@ def validate_flow_group(vctx, location, grp): string_concat( "%(location)s, ", _("group '%(group_id)s': group is empty")) - % {'location': location, 'group_id': grp.id}) + % {"location": location, "group_id": grp.id}) for i, page_desc in enumerate(grp.pages): validate_flow_page( @@ -569,11 +569,11 @@ def validate_flow_group(vctx, location, grp): "%(location)s, ", _("group '%(group_id)s': " "max_page_count is not positive")) - % {'location': location, 'group_id': grp.id}) + % {"location": location, "group_id": grp.id}) elif not hasattr(grp, "shuffle") and grp.max_page_count < len(grp.pages): vctx.add_warning( _("%(location)s, group '%(group_id)s': ") % { - 'location': location, 'group_id': grp.id}, + "location": location, "group_id": grp.id}, _("shuffle attribute will be required for groups with" "max_page_count in a future version. set " "'shuffle: False' to match current behavior.")) @@ -588,7 +588,7 @@ def validate_flow_group(vctx, location, grp): string_concat( "%(location)s: ", _("page id '%(page_desc_id)s' not unique")) - % {'location': location, 'page_desc_id': page_desc.id}) + % {"location": location, "page_desc_id": page_desc.id}) page_ids.add(page_desc.id) @@ -677,7 +677,7 @@ def validate_session_start_rule(vctx, location, nrule, tags): string_concat( "%(location)s: ", _("invalid tag '%(tag)s'")) - % {'location': location, 'tag': nrule.tag_session}) + % {"location": location, "tag": nrule.tag_session}) if hasattr(nrule, "default_expiration_mode"): from course.constants import FLOW_SESSION_EXPIRATION_MODE_CHOICES @@ -687,8 +687,8 @@ def validate_session_start_rule(vctx, location, nrule, tags): string_concat("%(location)s: ", _("invalid default expiration mode '%(expiremode)s'")) % { - 'location': location, - 'expiremode': nrule.default_expiration_mode}) + "location": location, + "expiremode": nrule.default_expiration_mode}) def validate_session_access_rule(vctx, location, arule, tags): @@ -752,7 +752,7 @@ def validate_session_access_rule(vctx, location, arule, tags): string_concat( "%(location)s: ", _("invalid tag '%(tag)s'")) - % {'location': location, 'tag': arule.if_has_tag}) + % {"location": location, "tag": arule.if_has_tag}) if hasattr(arule, "if_expiration_mode"): if arule.if_expiration_mode not in dict( @@ -761,8 +761,8 @@ def validate_session_access_rule(vctx, location, arule, tags): string_concat("%(location)s: ", _("invalid expiration mode '%(expiremode)s'")) % { - 'location': location, - 'expiremode': arule.if_expiration_mode}) + "location": location, + "expiremode": arule.if_expiration_mode}) for j, perm in enumerate(arule.permissions): validate_flow_permission( @@ -879,7 +879,7 @@ def validate_session_grading_rule( string_concat( "%(location)s: ", _("invalid tag '%(tag)s'")) - % {'location': location, 'tag': grule.if_has_tag}) + % {"location": location, "tag": grule.if_has_tag}) has_conditionals = True if hasattr(grule, "due"): @@ -929,7 +929,7 @@ def validate_flow_rules(vctx, location, rules): "the 'rules' block itself.")) raise ValidationError( string_concat("%(location)s: ", error_msg) - % {'location': location}) + % {"location": location}) tags = getattr(rules, "tags", []) @@ -969,8 +969,8 @@ def validate_flow_rules(vctx, location, rules): "must have grading rules with a " "grade_aggregation_strategy")) % { - 'location': location, - 'identifier': rules.grade_identifier}) + "location": location, + "identifier": rules.grade_identifier}) from course.constants import GRADE_AGGREGATION_STRATEGY_CHOICES if ( @@ -993,7 +993,7 @@ def validate_flow_rules(vctx, location, rules): string_concat("%(location)s: ", _("'grading' block is required if grade_identifier " "is not null/None.")) - % {'location': location}) + % {"location": location}) else: has_conditionals = None @@ -1043,7 +1043,7 @@ def validate_flow_permission(vctx, location, permission): raise ValidationError( string_concat("%(location)s: ", _("invalid flow permission '%(permission)s'")) - % {'location': location, 'permission': permission}) + % {"location": location, "permission": permission}) # }}} @@ -1082,7 +1082,7 @@ def validate_flow_desc(vctx, location, flow_desc): raise ValidationError( string_concat("%(location)s: ", _("must have either 'groups' or 'pages'")) - % {'location': location}) + % {"location": location}) # }}} @@ -1106,9 +1106,9 @@ def validate_flow_desc(vctx, location, flow_desc): _("group %(group_index)d ('%(group_id)s'): " "'pages' is not a list")) % { - 'location': location, - 'group_index': i+1, - 'group_id': grp.id}) + "location": location, + "group_index": i+1, + "group_id": grp.id}) for page in grp.pages: group_has_page = flow_has_page = True @@ -1121,9 +1121,9 @@ def validate_flow_desc(vctx, location, flow_desc): _("group %(group_index)d ('%(group_id)s'): " "no pages found")) % { - 'location': location, - 'group_index': i+1, - 'group_id': grp.id}) + "location": location, + "group_index": i+1, + "group_id": grp.id}) if not flow_has_page: raise ValidationError(_("%s: no pages found") @@ -1140,7 +1140,7 @@ def validate_flow_desc(vctx, location, flow_desc): raise ValidationError( string_concat("%(location)s: ", _("group id '%(group_id)s' not unique")) - % {'location': location, 'group_id': grp.id}) + % {"location": location, "group_id": grp.id}) group_ids.add(grp.id) @@ -1247,7 +1247,7 @@ def get_yaml_from_repo_safely(repo, full_name, commit_sha): raise ValidationError( "%(fullname)s: %(err_type)s: %(err_str)s" % { - 'fullname': full_name, + "fullname": full_name, "err_type": tp.__name__, "err_str": str(e)}) @@ -1521,7 +1521,7 @@ def validate_course_content(repo, course_file, events_file, pass else: vctx.add_warning( - 'media/', _( + "media/", _( "Your course repository has a 'media/' directory. " "Linking to media files using 'media:' is discouraged. " "Use the 'repo:' and 'repocur:' linkng schemes instead.")) diff --git a/course/versioning.py b/course/versioning.py index 8bbe1442332d5937c6f58b6fd905951381e5ab00..64c669b1ef0ad2be71702e6deb69a70e30e8812c 100644 --- a/course/versioning.py +++ b/course/versioning.py @@ -401,7 +401,7 @@ def run_course_update_command( "