diff --git a/course/grades.py b/course/grades.py
index ee45197369d23ea4064a25051df700ddffe245b0..fc6d5c1bd41e3b8b35d9c354b7f9507dd5d26661 100644
--- a/course/grades.py
+++ b/course/grades.py
@@ -1045,7 +1045,8 @@ class ImportGradesForm(StyledForm):
         self.fields["attr_type"] = forms.ChoiceField(
                 choices=(
                     ("email_or_id", _("Email or NetID")),
-                    ("inst_id", _("Institutional ID")),
+                    ("institutional_id", _("Institutional ID")),
+                    ("username", _("Username")),
                     ),
                 label=_("User attribute"))
 
@@ -1074,6 +1075,15 @@ class ImportGradesForm(StyledForm):
 
     def clean(self):
         data = super(ImportGradesForm, self).clean()
+        attempt_id = data.get("attempt_id")
+        if attempt_id:
+            attempt_id = attempt_id.strip()
+            flow_session_specific_attempt_id_prefix = "flow-session-"
+            if attempt_id.startswith(flow_session_specific_attempt_id_prefix):
+                self.add_error("attempt_id",
+                               _('"%s" as a prefix is not allowed')
+                               % flow_session_specific_attempt_id_prefix)
+
         file_contents = data.get("file")
         if file_contents:
             column_idx_list = [
@@ -1100,28 +1110,34 @@ class ParticipantNotFound(ValueError):
     pass
 
 
-def find_participant_from_inst_id(course, inst_id_str):
-    inst_id_str = inst_id_str.strip()
+def find_participant_from_user_attr(course, attr_type, attr_str):
+    attr_str = attr_str.strip()
+
+    kwargs = {"user__%s__exact" % attr_type: attr_str}
 
     matches = (Participation.objects
             .filter(
                 course=course,
                 status=participation_status.active,
-                user__institutional_id__exact=inst_id_str)
+                **kwargs)
             .select_related("user"))
 
-    if not matches:
-        raise ParticipantNotFound(
-                # Translators: use institutional_id_string to find user
-                # (participant).
-                _("no participant found with institutional ID "
-                "'%(inst_id_string)s'") % {
-                    "inst_id_string": inst_id_str})
-    if len(matches) > 1:
+    matches_count = matches.count()
+    if not matches_count or matches_count > 1:
+        from django.contrib.auth import get_user_model
+        from django.utils.encoding import force_text
+        attr_verbose_name = force_text(
+            get_user_model()._meta.get_field(attr_type).verbose_name)
+
+        map_dict = {"user_attr": attr_verbose_name, "user_attr_str": attr_str}
+
+        if not matches_count:
+            raise ParticipantNotFound(
+                    _("no participant found with %(user_attr)s "
+                    "'%(user_attr_str)s'") % map_dict)
         raise ParticipantNotFound(
-                _("more than one participant found with institutional ID "
-                "'%(inst_id_string)s'") % {
-                    "inst_id_string": inst_id_str})
+                _("more than one participant found with %(user_attr)s "
+                "'%(user_attr_str)s'") % map_dict)
 
     return matches[0]
 
@@ -1200,9 +1216,10 @@ def csv_to_grade_changes(
             if attr_type == "email_or_id":
                 gchange.participation = find_participant_from_id(
                         course, get_col_contents_or_empty(row, attr_column-1))
-            elif attr_type == "inst_id":
-                gchange.participation = find_participant_from_inst_id(
-                        course, get_col_contents_or_empty(row, attr_column-1))
+            elif attr_type in ["institutional_id", "username"]:
+                gchange.participation = find_participant_from_user_attr(
+                        course, attr_type,
+                        get_col_contents_or_empty(row, attr_column-1))
             else:
                 raise ParticipantNotFound(
                     _("Unknown user attribute '%(attr_type)s'") % {