Exemplo n.º 1
0
def get_yaml_from_repo_no_events_file_side_effect(repo,
                                                  full_name,
                                                  commit_sha,
                                                  cached=True):
    if full_name in [events_file, my_events_file]:
        raise ObjectDoesNotExist
    else:
        return get_yaml_from_repo(repo, full_name, commit_sha, cached)
Exemplo n.º 2
0
def validate_course_content(repo,
                            course_file,
                            events_file,
                            validate_sha,
                            course=None):
    course_desc = get_yaml_from_repo_safely(repo,
                                            course_file,
                                            commit_sha=validate_sha)

    vctx = ValidationContext(repo=repo, commit_sha=validate_sha, course=course)

    validate_course_desc_struct(vctx, course_file, course_desc)

    try:
        from course.content import get_yaml_from_repo
        events_desc = get_yaml_from_repo(repo,
                                         events_file,
                                         commit_sha=validate_sha,
                                         cached=False)
    except ObjectDoesNotExist:
        # That's OK--no calendar info.
        pass
    else:
        validate_calendar_desc_struct(vctx, events_file, events_desc)

    check_attributes_yml(vctx, repo, "", get_repo_blob(repo, "", validate_sha))

    try:
        flows_tree = get_repo_blob(repo, "flows", validate_sha)
    except ObjectDoesNotExist:
        # That's OK--no flows yet.
        pass
    else:
        for entry in flows_tree.items():
            if not entry.path.endswith(".yml"):
                continue

            from course.constants import FLOW_ID_REGEX
            flow_id = entry.path[:-4]
            match = re.match("^" + FLOW_ID_REGEX + "$", flow_id)
            if match is None:
                raise ValidationError(
                    string_concat(
                        "%s: ",
                        _("invalid flow name. "
                          "Flow names may only contain (roman) "
                          "letters, numbers, "
                          "dashes and underscores.")) % entry.path)

            location = "flows/%s" % entry.path
            flow_desc = get_yaml_from_repo_safely(repo,
                                                  location,
                                                  commit_sha=validate_sha)

            validate_flow_desc(vctx, location, flow_desc)

    return vctx.warnings
Exemplo n.º 3
0
def validate_course_content(repo, course_file, events_file,
        validate_sha, course=None):
    course_desc = get_yaml_from_repo_safely(repo, course_file,
            commit_sha=validate_sha)

    vctx = ValidationContext(
            repo=repo,
            commit_sha=validate_sha,
            course=course)

    validate_course_desc_struct(vctx, course_file, course_desc)

    try:
        from course.content import get_yaml_from_repo
        events_desc = get_yaml_from_repo(repo, events_file,
                commit_sha=validate_sha, cached=False)
    except ObjectDoesNotExist:
        # That's OK--no calendar info.
        pass
    else:
        validate_calendar_desc_struct(vctx, events_file, events_desc)

    check_attributes_yml(
            vctx, repo, "", get_repo_blob(repo, "", validate_sha))

    try:
        flows_tree = get_repo_blob(repo, "flows", validate_sha)
    except ObjectDoesNotExist:
        # That's OK--no flows yet.
        pass
    else:
        for entry in flows_tree.items():
            if not entry.path.endswith(".yml"):
                continue

            from course.constants import FLOW_ID_REGEX
            flow_id = entry.path[:-4]
            match = re.match("^"+FLOW_ID_REGEX+"$", flow_id)
            if match is None:
                raise ValidationError(
                        string_concat("%s: ",
                            _("invalid flow name. "
                                "Flow names may only contain (roman) "
                                "letters, numbers, "
                                "dashes and underscores."))
                        % entry.path)

            location = "flows/%s" % entry.path
            flow_desc = get_yaml_from_repo_safely(repo, location,
                    commit_sha=validate_sha)

            validate_flow_desc(vctx, location, flow_desc)

    return vctx.warnings
Exemplo n.º 4
0
def get_yaml_from_repo_safely(repo, full_name, commit_sha):
    from course.content import get_yaml_from_repo
    try:
        return get_yaml_from_repo(
                repo=repo, full_name=full_name, commit_sha=commit_sha)
    except:
        from traceback import print_exc
        print_exc()

        tp, e, _ = sys.exc_info()

        raise ValidationError("%s: %s: %s" % (
            full_name, tp.__name__, str(e)))
def get_yaml_from_repo_side_effect(repo, full_name, commit_sha, cached=True):
    if full_name == events_file:
        return dict_to_struct(
            {"event_kinds": dict_to_struct({
                "lecture": dict_to_struct({
                    "title": "Lecture {nr}",
                    "color": "blue"
                })}),
                "events": dict_to_struct({
                    "lecture 1": dict_to_struct({
                        "title": "l1"})
                })})
    else:
        return get_yaml_from_repo(repo, full_name, commit_sha, cached)
Exemplo n.º 6
0
def get_yaml_from_repo_safely(repo, full_name, commit_sha):
    from course.content import get_yaml_from_repo
    try:
        return get_yaml_from_repo(repo=repo,
                                  full_name=full_name,
                                  commit_sha=commit_sha,
                                  cached=False)
    except:
        from traceback import print_exc
        print_exc()

        tp, e, _ = sys.exc_info()

        raise ValidationError("%s: %s: %s" %
                              (full_name, tp.__name__, unicode(e)))
Exemplo n.º 7
0
def get_yaml_from_repo_safely(repo, full_name, commit_sha):
    from course.content import get_yaml_from_repo

    try:
        return get_yaml_from_repo(repo=repo, full_name=full_name, commit_sha=commit_sha, cached=False)
    except:
        from traceback import print_exc

        print_exc()

        tp, e, _ = sys.exc_info()

        raise ValidationError(
            "%(fullname)s: %(err_type)s: %(err_str)s"
            % {"fullname": full_name, "err_type": tp.__name__, "err_str": six.text_type(e)}
        )
Exemplo n.º 8
0
def get_yaml_from_repo_side_effect(repo, full_name, commit_sha, cached=True):
    if full_name == events_file:
        return dict_to_struct({
            "event_kinds":
            dict_to_struct({
                "lecture":
                dict_to_struct({
                    "title": "Lecture {nr}",
                    "color": "blue"
                })
            }),
            "events":
            dict_to_struct({"lecture 1": dict_to_struct({"title": "l1"})})
        })
    else:
        return get_yaml_from_repo(repo, full_name, commit_sha, cached)
Exemplo n.º 9
0
def get_yaml_from_repo_safely(repo, full_name, commit_sha):
    from course.content import get_yaml_from_repo
    try:
        return get_yaml_from_repo(
                repo=repo, full_name=full_name, commit_sha=commit_sha,
                cached=False)
    except:
        from traceback import print_exc
        print_exc()

        tp, e, _ = sys.exc_info()

        raise ValidationError(
                "%(fullname)s: %(err_type)s: %(err_str)s" % {
                    'fullname': full_name,
                    "err_type": tp.__name__,
                    "err_str": six.text_type(e)})
Exemplo n.º 10
0
def validate_course_content(repo,
                            course_file,
                            events_file,
                            validate_sha,
                            datespec_callback=None):
    course_desc = get_yaml_from_repo_safely(repo,
                                            course_file,
                                            commit_sha=validate_sha)

    ctx = ValidationContext(repo=repo,
                            commit_sha=validate_sha,
                            datespec_callback=datespec_callback)

    validate_course_desc_struct(ctx, course_file, course_desc)

    try:
        from course.content import get_yaml_from_repo
        events_desc = get_yaml_from_repo(repo,
                                         events_file,
                                         commit_sha=validate_sha,
                                         cached=False)
    except ObjectDoesNotExist:
        # That's OK--no calendar info.
        pass
    else:
        validate_calendar_desc_struct(ctx, events_file, events_desc)

    try:
        flows_tree = get_repo_blob(repo, "flows", validate_sha)
    except ObjectDoesNotExist:
        # That's OK--no flows yet.
        pass
    else:
        for entry in flows_tree.items():
            if not entry.path.endswith(".yml"):
                continue

            location = "flows/%s" % entry.path
            flow_desc = get_yaml_from_repo_safely(repo,
                                                  location,
                                                  commit_sha=validate_sha)

            validate_flow_desc(ctx, location, flow_desc)

    return ctx.warnings
Exemplo n.º 11
0
def validate_course_content(repo, course_file, events_file,
        validate_sha, datespec_callback=None):
    course_desc = get_yaml_from_repo_safely(repo, course_file,
            commit_sha=validate_sha)

    ctx = ValidationContext(
            repo=repo,
            commit_sha=validate_sha,
            datespec_callback=datespec_callback)

    validate_course_desc_struct(ctx, course_file, course_desc)

    try:
        from course.content import get_yaml_from_repo
        events_desc = get_yaml_from_repo(repo, events_file,
                commit_sha=validate_sha, cached=False)
    except ObjectDoesNotExist:
        # That's OK--no calendar info.
        pass
    else:
        validate_calendar_desc_struct(ctx, events_file, events_desc)

    try:
        flows_tree = get_repo_blob(repo, "flows", validate_sha)
    except ObjectDoesNotExist:
        # That's OK--no flows yet.
        pass
    else:
        for entry in flows_tree.items():
            if not entry.path.endswith(".yml"):
                continue

            location = "flows/%s" % entry.path
            flow_desc = get_yaml_from_repo_safely(repo, location,
                    commit_sha=validate_sha)

            validate_flow_desc(ctx, location, flow_desc)

    return ctx.warnings
Exemplo n.º 12
0
def validate_course_content(repo, course_file, events_file,
        validate_sha, course=None):
    vctx = ValidationContext(
            repo=repo,
            commit_sha=validate_sha,
            course=course)

    course_desc = get_yaml_from_repo_safely(repo, course_file,
            commit_sha=validate_sha)

    validate_staticpage_desc(vctx, course_file, course_desc)

    try:
        from course.content import get_yaml_from_repo
        events_desc = get_yaml_from_repo(repo, events_file,
                commit_sha=validate_sha, cached=False)
    except ObjectDoesNotExist:
        if events_file != "events.yml":
            vctx.add_warning(
                    _("Events file"),
                    _("Your course repository does not have an events "
                        "file named '%s'.")
                    % events_file)
        else:
            # That's OK--no calendar info.
            pass
    else:
        validate_calendar_desc_struct(vctx, events_file, events_desc)

    if vctx.course is not None:
        from course.models import (
                ParticipationPermission,
                ParticipationRolePermission)
        access_kinds = frozenset(
                ParticipationPermission.objects
                .filter(
                    participation__course=vctx.course,
                    permission=pperm.access_files_for,
                    )
                .values_list("argument", flat=True)) | frozenset(
                        ParticipationRolePermission.objects
                        .filter(
                            role__course=vctx.course,
                            permission=pperm.access_files_for,
                            )
                        .values_list("argument", flat=True))
    else:
        access_kinds = ["public", "in_exam", "student", "ta",
                     "unenrolled", "instructor"]

    check_attributes_yml(
            vctx, repo, "",
            get_repo_blob(repo, "", validate_sha),
            access_kinds)

    try:
        flows_tree = get_repo_blob(repo, "media", validate_sha)
    except ObjectDoesNotExist:
        # That's great--no media directory.
        pass
    else:
        vctx.add_warning(
                'media/', _(
                    "Your course repository has a 'media/' directory. "
                    "Linking to media files using 'media:' is discouraged. "
                    "Use the 'repo:' and 'repocur:' linkng schemes instead."))

    # {{{ flows

    try:
        flows_tree = get_repo_blob(repo, "flows", validate_sha)
    except ObjectDoesNotExist:
        # That's OK--no flows yet.
        pass
    else:
        used_grade_identifiers = set()

        for entry in flows_tree.items():
            entry_path = entry.path.decode("utf-8")
            if not entry_path.endswith(".yml"):
                continue

            from course.constants import FLOW_ID_REGEX
            flow_id = entry_path[:-4]
            match = re.match("^"+FLOW_ID_REGEX+"$", flow_id)
            if match is None:
                raise ValidationError(
                        string_concat("%s: ",
                            _("invalid flow name. "
                                "Flow names may only contain (roman) "
                                "letters, numbers, "
                                "dashes and underscores."))
                        % entry_path)

            location = "flows/%s" % entry_path
            flow_desc = get_yaml_from_repo_safely(repo, location,
                    commit_sha=validate_sha)

            validate_flow_desc(vctx, location, flow_desc)

            # {{{ check grade_identifier

            flow_grade_identifier = None
            if hasattr(flow_desc, "rules"):
                flow_grade_identifier = getattr(
                        flow_desc.rules, "grade_identifier", None)

            if (
                    flow_grade_identifier is not None
                    and
                    set([flow_grade_identifier]) & used_grade_identifiers):
                raise ValidationError(
                        string_concat("%s: ",
                                      _("flow uses the same grade_identifier "
                                        "as another flow"))
                        % location)

            used_grade_identifiers.add(flow_grade_identifier)

            if (course is not None
                    and flow_grade_identifier is not None):
                check_grade_identifier_link(
                        vctx, location, course, flow_id, flow_grade_identifier)

            # }}}

            if course is not None:
                check_for_page_type_changes(
                        vctx, location, course, flow_id, flow_desc)

    # }}}

    # {{{ static pages

    try:
        pages_tree = get_repo_blob(repo, "staticpages", validate_sha)
    except ObjectDoesNotExist:
        # That's OK--no flows yet.
        pass
    else:
        for entry in pages_tree.items():
            entry_path = entry.path.decode("utf-8")
            if not entry_path.endswith(".yml"):
                continue

            from course.constants import STATICPAGE_PATH_REGEX
            page_name = entry_path[:-4]
            match = re.match("^"+STATICPAGE_PATH_REGEX+"$", page_name)
            if match is None:
                raise ValidationError(
                        string_concat("%s: ",
                            _(
                                "invalid page name. "
                                "Page names may only contain "
                                "alphanumeric characters (any language) "
                                "and hyphens."
                                ))
                        % entry_path)

        location = "staticpages/%s" % entry_path
        page_desc = get_yaml_from_repo_safely(repo, location,
                commit_sha=validate_sha)

        validate_staticpage_desc(vctx, location, page_desc)

    # }}}

    return vctx.warnings
Exemplo n.º 13
0
def get_yaml_from_repo_no_events_file_side_effect(
        repo, full_name, commit_sha, cached=True):
    if full_name in [events_file, my_events_file]:
        raise ObjectDoesNotExist
    else:
        return get_yaml_from_repo(repo, full_name, commit_sha, cached)
Exemplo n.º 14
0
def validate_course_content(repo, course_file, events_file,
        validate_sha, course=None):
    vctx = ValidationContext(
            repo=repo,
            commit_sha=validate_sha,
            course=course)

    course_desc = get_yaml_from_repo_safely(repo, course_file,
            commit_sha=validate_sha)

    validate_staticpage_desc(vctx, course_file, course_desc)

    try:
        from course.content import get_yaml_from_repo
        events_desc = get_yaml_from_repo(repo, events_file,
                commit_sha=validate_sha, cached=False)
    except ObjectDoesNotExist:
        if events_file != "events.yml":
            vctx.add_warning(
                    _("Events file"),
                    _("Your course repository does not have an events "
                        "file named '%s'.")
                    % events_file)
        else:
            # That's OK--no calendar info.
            pass
    else:
        validate_calendar_desc_struct(vctx, events_file, events_desc)

    if vctx.course is not None:
        from course.models import (
                ParticipationPermission,
                ParticipationRolePermission)
        access_kinds = frozenset(
                ParticipationPermission.objects
                .filter(
                    participation__course=vctx.course,
                    permission=pperm.access_files_for,
                    )
                .values_list("argument", flat=True)) | frozenset(
                        ParticipationRolePermission.objects
                        .filter(
                            role__course=vctx.course,
                            permission=pperm.access_files_for,
                            )
                        .values_list("argument", flat=True))
    else:
        access_kinds = ["public", "in_exam", "student", "ta",
                     "unenrolled", "instructor"]

    check_attributes_yml(
            vctx, repo, "",
            get_repo_blob(repo, "", validate_sha),
            access_kinds)

    try:
        flows_tree = get_repo_blob(repo, "media", validate_sha)
    except ObjectDoesNotExist:
        # That's great--no media directory.
        pass
    else:
        vctx.add_warning(
                'media/', _(
                    "Your course repository has a 'media/' directory. "
                    "Linking to media files using 'media:' is discouraged. "
                    "Use the 'repo:' and 'repocur:' linkng schemes instead."))

    # {{{ flows

    try:
        flows_tree = get_repo_blob(repo, "flows", validate_sha)
    except ObjectDoesNotExist:
        # That's OK--no flows yet.
        pass
    else:
        used_grade_identifiers = set()

        for entry in flows_tree.items():
            entry_path = entry.path.decode("utf-8")
            if not entry_path.endswith(".yml"):
                continue

            from course.constants import FLOW_ID_REGEX
            flow_id = entry_path[:-4]
            match = re.match("^"+FLOW_ID_REGEX+"$", flow_id)
            if match is None:
                raise ValidationError(
                        string_concat("%s: ",
                            _("invalid flow name. "
                                "Flow names may only contain (roman) "
                                "letters, numbers, "
                                "dashes and underscores."))
                        % entry_path)

            location = "flows/%s" % entry_path
            flow_desc = get_yaml_from_repo_safely(repo, location,
                    commit_sha=validate_sha)

            validate_flow_desc(vctx, location, flow_desc)

            # {{{ check grade_identifier

            flow_grade_identifier = None
            if hasattr(flow_desc, "rules"):
                flow_grade_identifier = getattr(
                        flow_desc.rules, "grade_identifier", None)

            if (
                    flow_grade_identifier is not None
                    and
                    set([flow_grade_identifier]) & used_grade_identifiers):
                raise ValidationError(
                        string_concat("%s: ",
                                      _("flow uses the same grade_identifier "
                                        "as another flow"))
                        % location)

            used_grade_identifiers.add(flow_grade_identifier)

            if (course is not None
                    and flow_grade_identifier is not None):
                check_grade_identifier_link(
                        vctx, location, course, flow_id, flow_grade_identifier)

            # }}}

            if course is not None:
                check_for_page_type_changes(
                        vctx, location, course, flow_id, flow_desc)

    # }}}

    # {{{ static pages

    try:
        pages_tree = get_repo_blob(repo, "staticpages", validate_sha)
    except ObjectDoesNotExist:
        # That's OK--no flows yet.
        pass
    else:
        for entry in pages_tree.items():
            entry_path = entry.path.decode("utf-8")
            if not entry_path.endswith(".yml"):
                continue

            from course.constants import STATICPAGE_PATH_REGEX
            page_name = entry_path[:-4]
            match = re.match("^"+STATICPAGE_PATH_REGEX+"$", page_name)
            if match is None:
                raise ValidationError(
                        string_concat("%s: ",
                            _(
                                "invalid page name. "
                                "Page names may only contain "
                                "alphanumeric characters (any language) "
                                "and hyphens."
                                ))
                        % entry_path)

        location = "staticpages/%s" % entry_path
        page_desc = get_yaml_from_repo_safely(repo, location,
                commit_sha=validate_sha)

        validate_staticpage_desc(vctx, location, page_desc)

    # }}}

    return vctx.warnings
Exemplo n.º 15
0
def validate_course_content(repo,
                            course_file,
                            events_file,
                            validate_sha,
                            course=None):
    course_desc = get_yaml_from_repo_safely(repo,
                                            course_file,
                                            commit_sha=validate_sha)

    vctx = ValidationContext(repo=repo, commit_sha=validate_sha, course=course)

    validate_course_desc_struct(vctx, course_file, course_desc)

    try:
        from course.content import get_yaml_from_repo
        events_desc = get_yaml_from_repo(repo,
                                         events_file,
                                         commit_sha=validate_sha,
                                         cached=False)
    except ObjectDoesNotExist:
        if events_file != "events.yml":
            vctx.add_warning(
                _("Events file"),
                _("Your course repository does not have an events "
                  "file named '%s'.") % events_file)
        else:
            # That's OK--no calendar info.
            pass
    else:
        validate_calendar_desc_struct(vctx, events_file, events_desc)

    check_attributes_yml(vctx, repo, "", get_repo_blob(repo, "", validate_sha))

    try:
        flows_tree = get_repo_blob(repo, "media", validate_sha)
    except ObjectDoesNotExist:
        # That's great--no media directory.
        pass
    else:
        vctx.add_warning(
            'media/',
            _("Your course repository has a 'media/' directory. "
              "Linking to media files using 'media:' is discouraged. "
              "Use the 'repo:' and 'repocur:' linkng schemes instead."))

    try:
        flows_tree = get_repo_blob(repo, "flows", validate_sha)
    except ObjectDoesNotExist:
        # That's OK--no flows yet.
        pass
    else:
        used_grade_identifiers = set()

        for entry in flows_tree.items():
            entry_path = entry.path.decode("utf-8")
            if not entry_path.endswith(".yml"):
                continue

            from course.constants import FLOW_ID_REGEX
            flow_id = entry_path[:-4]
            match = re.match("^" + FLOW_ID_REGEX + "$", flow_id)
            if match is None:
                raise ValidationError(
                    string_concat(
                        "%s: ",
                        _("invalid flow name. "
                          "Flow names may only contain (roman) "
                          "letters, numbers, "
                          "dashes and underscores.")) % entry_path)

            location = "flows/%s" % entry_path
            flow_desc = get_yaml_from_repo_safely(repo,
                                                  location,
                                                  commit_sha=validate_sha)

            validate_flow_desc(vctx, location, flow_desc)

            # {{{ check grade_identifier

            flow_grade_identifier = None
            if hasattr(flow_desc, "rules"):
                flow_grade_identifier = getattr(flow_desc.rules,
                                                "grade_identifier", None)

            if (flow_grade_identifier is not None
                    and set([flow_grade_identifier]) & used_grade_identifiers):
                raise ValidationError(
                    string_concat(
                        "%s: ",
                        _("flow uses the same grade_identifier "
                          "as another flow")) % location)

            used_grade_identifiers.add(flow_grade_identifier)

            if (course is not None and flow_grade_identifier is not None):
                check_grade_identifier_link(vctx, location, course, flow_id,
                                            flow_grade_identifier)

            # }}}

            if course is not None:
                check_for_page_type_changes(vctx, location, course, flow_id,
                                            flow_desc)

    return vctx.warnings
Exemplo n.º 16
0
def validate_course_content(repo, course_file, events_file, validate_sha, course=None):
    course_desc = get_yaml_from_repo_safely(repo, course_file, commit_sha=validate_sha)

    vctx = ValidationContext(repo=repo, commit_sha=validate_sha, course=course)

    validate_course_desc_struct(vctx, course_file, course_desc)

    try:
        from course.content import get_yaml_from_repo

        events_desc = get_yaml_from_repo(repo, events_file, commit_sha=validate_sha, cached=False)
    except ObjectDoesNotExist:
        if events_file != "events.yml":
            vctx.add_warning(
                _("Events file"), _("Your course repository does not have an events " "file named '%s'.") % events_file
            )
        else:
            # That's OK--no calendar info.
            pass
    else:
        validate_calendar_desc_struct(vctx, events_file, events_desc)

    check_attributes_yml(vctx, repo, "", get_repo_blob(repo, "", validate_sha))

    try:
        flows_tree = get_repo_blob(repo, "media", validate_sha)
    except ObjectDoesNotExist:
        # That's great--no media directory.
        pass
    else:
        vctx.add_warning(
            "media/",
            _(
                "Your course repository has a 'media/' directory. "
                "Linking to media files using 'media:' is discouraged. "
                "Use the 'repo:' and 'repocur:' linkng schemes instead."
            ),
        )

    try:
        flows_tree = get_repo_blob(repo, "flows", validate_sha)
    except ObjectDoesNotExist:
        # That's OK--no flows yet.
        pass
    else:
        used_grade_identifiers = set()

        for entry in flows_tree.items():
            entry_path = entry.path.decode("utf-8")
            if not entry_path.endswith(".yml"):
                continue

            from course.constants import FLOW_ID_REGEX

            flow_id = entry_path[:-4]
            match = re.match("^" + FLOW_ID_REGEX + "$", flow_id)
            if match is None:
                raise ValidationError(
                    string_concat(
                        "%s: ",
                        _(
                            "invalid flow name. "
                            "Flow names may only contain (roman) "
                            "letters, numbers, "
                            "dashes and underscores."
                        ),
                    )
                    % entry_path
                )

            location = "flows/%s" % entry_path
            flow_desc = get_yaml_from_repo_safely(repo, location, commit_sha=validate_sha)

            validate_flow_desc(vctx, location, flow_desc)

            # {{{ check grade_identifier

            flow_grade_identifier = None
            if hasattr(flow_desc, "rules"):
                flow_grade_identifier = getattr(flow_desc.rules, "grade_identifier", None)

            if flow_grade_identifier is not None and set([flow_grade_identifier]) & used_grade_identifiers:
                raise ValidationError(
                    string_concat("%s: ", _("flow uses the same grade_identifier " "as another flow")) % location
                )

            used_grade_identifiers.add(flow_grade_identifier)

            if course is not None and flow_grade_identifier is not None:
                check_grade_identifier_link(vctx, location, course, flow_id, flow_grade_identifier)

            # }}}

            if course is not None:
                check_for_page_type_changes(vctx, location, course, flow_id, flow_desc)

    return vctx.warnings