def test_string(self):
     exception = KeyNotFoundError("config file path", "key", "field")
     expected_string = (
         "\n****************************ERROR****************************\n"
         "File: config file path\n\n"
         "Key: key\n"
         '"key" did not match any field\n\n'
         "Options:\n"
         "  - Did you spell the name of the key correctly?\n"
         "  - Does the key exist?\n")
     self.assertEqual(exception.__str__(), expected_string)
Beispiel #2
0
    def load(self):
        """Load the content for learning outcomes.

        Raise:
            MissingRequiredFieldError: when no object can be found with the matching
                attribute.
        """
        learning_outcomes = self.load_yaml_file(self.structure_file_path)
        learning_outcomes_translations = self.get_yaml_translations(
            self.structure_filename,
            required_fields=["text"],
            required_slugs=learning_outcomes.keys()
        )

        for (outcome_slug, outcome_data) in learning_outcomes.items():
            translations = self.get_blank_translation_dictionary()
            translations.update(learning_outcomes_translations.get(outcome_slug, dict()))

            # Create outcome objects and save to db
            outcome = LearningOutcome(
                slug=outcome_slug,
            )
            self.populate_translations(outcome, translations)
            self.mark_translation_availability(outcome, required_fields=["text"])

            outcome.save()

            # Add curriculum areas
            curriculum_area_slugs = outcome_data.get("curriculum-areas", [])

            for curriculum_area_slug in curriculum_area_slugs:
                try:
                    curriculum_area = CurriculumArea.objects.get(
                        slug=curriculum_area_slug
                    )
                    if curriculum_area.children.exists():
                        raise InvalidYAMLValueError(
                            self.structure_file_path,
                            "curriculum-areas - value '{}' is invalid".format(curriculum_area_slug),
                            "Curriculum area with no children (parent curriculum areas are not allowed)"
                        )
                    else:
                        outcome.curriculum_areas.add(curriculum_area)
                except ObjectDoesNotExist:
                    raise KeyNotFoundError(
                        self.structure_file_path,
                        curriculum_area_slug,
                        "Curriculum Areas"
                    )

            self.log("Added learning outcome: {}".format(outcome.__str__()))

        self.log("All learning outcomes loaded!\n")
def check_interactives(interactives, file_path, chapter=None):
    """Check interactives are available.

    If chapter is given, each interactive has a relationship added
    to the chapter.

    Args:
        interactives (set): Set of slugs of interactives.
        file_path (str): File path of file providing interactives.
                            Used when displaying error message.
        chapter (Chapter): Chapter to add relationship to interactive too.

    Raises:
        KeyNotFoundError: If interactive cannot be found.
    """
    for interactive_slug in interactives:
        try:
            interactive = Interactive.objects.get(slug=interactive_slug)
        except ObjectDoesNotExist:
            raise KeyNotFoundError(file_path, interactive_slug, "Interactive")
        if chapter:
            chapter.interactives.add(interactive)
    def load(self):
        """Load the content for learning outcomes.

        Raise:
            MissingRequiredFieldError: when no object can be found with the matching
                attribute.
        """
        learning_outcomes = self.load_yaml_file(
            os.path.join(self.BASE_PATH, self.structure_file_path))

        for (outcome_slug, outcome_data) in learning_outcomes.items():

            if ("text" not in outcome_data) or (outcome_data["text"] is None):
                raise MissingRequiredFieldError(self.structure_file_path,
                                                ["text"], "Learning Outcome")

            # Create outcome objects and save to db
            outcome = LearningOutcome(slug=outcome_slug,
                                      text=outcome_data["text"])
            outcome.save()

            # Add curriculum areas
            curriculum_area_slugs = outcome_data.get("curriculum-areas", [])
            for curriculum_area_slug in curriculum_area_slugs:
                try:
                    curriculum_area = CurriculumArea.objects.get(
                        slug=curriculum_area_slug)
                    outcome.curriculum_areas.add(curriculum_area)
                except:
                    raise KeyNotFoundError(self.structure_file_path,
                                           curriculum_area_slug,
                                           "Curriculum Areas")

            self.log("Added learning outcome: {}".format(outcome.__str__()))

        self.log("All learning outcomes loaded!\n")
    def load(self):
        """Load the content for curriculum integrations.

        Raise:
            KeyNotFoundError: when no object can be found with the matching attribute.
            MissingRequiredFieldError: when a value for a required model field cannot be
                found in the config file.
        """
        structure = self.load_yaml_file(self.structure_file_path)

        for (integration_slug, integration_data) in structure.items():

            if integration_data is None:
                raise MissingRequiredFieldError(self.structure_file_path,
                                                ["number", "curriculum-areas"],
                                                "Curriculum Integration")

            integration_number = integration_data.get("number", None)
            integration_curriculum_areas = integration_data.get(
                "curriculum-areas", None)
            if None in [integration_number, integration_curriculum_areas]:
                raise MissingRequiredFieldError(self.structure_file_path,
                                                ["number", "curriculum-areas"],
                                                "Curriculum Integration")

            integration_content = self.convert_md_file(
                os.path.join(self.BASE_PATH, "{}.md".format(integration_slug)),
                self.structure_file_path)

            integration = self.topic.curriculum_integrations.create(
                slug=integration_slug,
                number=integration_number,
                name=integration_content.title,
                content=integration_content.html_string,
            )
            integration.save()

            # Add curriculum areas
            for curriculum_area_slug in integration_curriculum_areas:
                try:
                    curriculum_area = CurriculumArea.objects.get(
                        slug=curriculum_area_slug)
                    integration.curriculum_areas.add(curriculum_area)
                except:
                    raise KeyNotFoundError(self.structure_file_path,
                                           curriculum_area_slug,
                                           "Curriculum Areas")

            # Add prerequisite lessons
            if "prerequisite-lessons" in integration_data:
                prerequisite_lessons = integration_data["prerequisite-lessons"]
                if prerequisite_lessons is not None:
                    for (unit_plan_slug,
                         lessons) in prerequisite_lessons.items():
                        if lessons is None:
                            raise MissingRequiredFieldError(
                                self.structure_file_path, ["unit-plan"],
                                "Prerequisite Lesson")
                        for lesson_slug in lessons:
                            try:
                                lesson = Lesson.objects.get(slug=lesson_slug)
                                integration.prerequisite_lessons.add(lesson)
                            except:
                                raise KeyNotFoundError(
                                    self.structure_file_path, lesson_slug,
                                    "Lessons")

            self.log(
                "Added curriculum integration: {}".format(integration.name), 1)
Beispiel #6
0
    def load(self):
        """Load the content for a single lesson.

        Raises:
            KeyNotFoundError: when no object can be found with the matching attribute.
            InvalidConfigValueError: when provided value is not valid.
            MissingRequiredFieldError: when a value for a required model field cannot be
                found in the config file.
        """
        lessons_structure = self.load_yaml_file(
            self.lessons_structure_file_path)

        for (lesson_slug, lesson_structure) in lessons_structure.items():

            if lesson_structure is None:
                raise MissingRequiredFieldError(
                    self.lessons_structure_file_path, ["number"], "Lesson")

            # Build the file path to the lesson"s md file
            file_path = os.path.join(self.BASE_PATH, "lessons",
                                     "{}.md".format(lesson_slug))

            lesson_content = self.convert_md_file(
                file_path,
                self.lessons_structure_file_path,
            )

            if "computational-thinking-links" in lesson_structure:
                file_name = lesson_structure["computational-thinking-links"]
                file_path = os.path.join(self.BASE_PATH, "lessons", file_name)
                ct_links_content = self.convert_md_file(
                    file_path,
                    self.lessons_structure_file_path,
                    heading_required=False,
                    remove_title=False,
                )
                ct_links = ct_links_content.html_string
            else:
                ct_links = None

            if "duration" in lesson_structure:
                lesson_duration = lesson_structure["duration"]
            else:
                lesson_duration = None

            heading_tree = None
            if lesson_content.heading_tree:
                heading_tree = convert_heading_tree_to_dict(
                    lesson_content.heading_tree)

            if "programming-challenges-description" in lesson_structure:
                file_name = lesson_structure[
                    "programming-challenges-description"]
                file_path = os.path.join(self.BASE_PATH, "lessons", file_name)
                programming_description_content = self.convert_md_file(
                    file_path,
                    self.lessons_structure_file_path,
                    heading_required=False,
                    remove_title=False,
                )
                programming_description = programming_description_content.html_string
            else:
                programming_description = None

            classroom_resources = lesson_structure.get("classroom-resources",
                                                       None)
            if isinstance(classroom_resources, list):
                for classroom_resource in classroom_resources:
                    if not isinstance(classroom_resource, str):
                        raise InvalidConfigValueError(
                            self.lessons_structure_file_path,
                            "classroom-resources list item",
                            "A string describing the classroom resource.")
                    elif len(classroom_resource) > 100:
                        raise InvalidConfigValueError(
                            self.lessons_structure_file_path,
                            "classroom-resources list item",
                            "Item description must be less than 100 characters."
                        )
            elif classroom_resources is not None:
                raise InvalidConfigValueError(self.lessons_structure_file_path,
                                              "classroom-resources",
                                              "List of strings.")

            lesson = self.topic.lessons.create(
                unit_plan=self.unit_plan,
                slug=lesson_slug,
                name=lesson_content.title,
                duration=lesson_duration,
                content=lesson_content.html_string,
                computational_thinking_links=ct_links,
                heading_tree=heading_tree,
                programming_challenges_description=programming_description,
                classroom_resources=classroom_resources,
            )
            lesson.save()

            # Add programming challenges
            if "programming-challenges" in lesson_structure:
                programming_challenge_slugs = lesson_structure[
                    "programming-challenges"]
                if programming_challenge_slugs is not None:
                    # Check all slugs are valid
                    for programming_challenge_slug in programming_challenge_slugs:
                        try:
                            ProgrammingChallenge.objects.get(
                                slug=programming_challenge_slug,
                                topic=self.topic)

                        except:
                            raise KeyNotFoundError(
                                self.lessons_structure_file_path,
                                programming_challenge_slug,
                                "Programming Challenges")

                    # Store number of challenge in relationship with lesson.
                    # If three linked challenges have numbers 1.1, 4.2, and 4.5
                    # They will be stored as 1.1, 2.1, and 2.2 respectively.

                    # Order challenges for numbering.
                    programming_challenges = ProgrammingChallenge.objects.filter(
                        slug__in=programming_challenge_slugs,
                        topic=self.topic).order_by("challenge_set_number",
                                                   "challenge_number")

                    # Setup variables for numbering.
                    display_set_number = 0
                    last_set_number = -1
                    display_number = 0
                    last_number = -1

                    # For each challenge, increment number variables if original
                    # numbers are different.
                    for programming_challenge in programming_challenges:
                        if programming_challenge.challenge_set_number > last_set_number:
                            display_set_number += 1
                            display_number = 0
                            last_number = -1
                        if programming_challenge.challenge_number > last_number:
                            display_number += 1
                        last_set_number = programming_challenge.challenge_set_number
                        last_number = programming_challenge.challenge_number

                        # Create and save relationship between lesson and
                        # challenge that contains challenge number.
                        relationship = ProgrammingChallengeNumber(
                            programming_challenge=programming_challenge,
                            lesson=lesson,
                            challenge_set_number=display_set_number,
                            challenge_number=display_number,
                        )
                        relationship.save()

            # Add learning outcomes
            if "learning-outcomes" in lesson_structure:
                learning_outcome_slugs = lesson_structure["learning-outcomes"]
                if learning_outcome_slugs is not None:
                    for learning_outcome_slug in learning_outcome_slugs:
                        try:
                            learning_outcome = LearningOutcome.objects.get(
                                slug=learning_outcome_slug)
                            lesson.learning_outcomes.add(learning_outcome)
                        except:
                            raise KeyNotFoundError(
                                self.lessons_structure_file_path,
                                learning_outcome_slug, "Learning Outcomes")

            # Add generated resources
            if "generated-resources" in lesson_structure:
                resources = lesson_structure["generated-resources"]
                if resources is not None:
                    for (resource_slug, resource_data) in resources.items():
                        if resource_data is None:
                            raise MissingRequiredFieldError(
                                self.lessons_structure_file_path,
                                ["description"], "Generated Resource")
                        try:
                            resource = Resource.objects.get(slug=resource_slug)
                        except:
                            raise KeyNotFoundError(
                                self.lessons_structure_file_path,
                                resource_slug, "Resources")
                        resource_description = resource_data.get(
                            "description", None)
                        if resource_description is None:
                            raise MissingRequiredFieldError(
                                self.lessons_structure_file_path,
                                ["description"], "Generated Resource")

                        relationship = ResourceDescription(
                            resource=resource,
                            lesson=lesson,
                            description=resource_description)
                        relationship.save()

            self.log("Added lesson: {}".format(lesson.__str__()), 2)
Beispiel #7
0
    def load(self):
        """Load the content for unit plans.

        Raise:
            KeyNotFoundError: when no object can be found with the matching attribute.
            MissingRequiredFieldError: when a value for a required model field cannot
                be found in the config file.
        """
        unit_plan_structure = self.load_yaml_file(self.structure_file_path)

        unit_plan_translations = self.get_blank_translation_dictionary()

        content_filename = "{}.md".format(self.unit_plan_slug)
        content_translations = self.get_markdown_translations(content_filename)
        for language, content in content_translations.items():
            unit_plan_translations[language]["content"] = content.html_string
            unit_plan_translations[language]["name"] = content.title
            if content.heading_tree:
                heading_tree = convert_heading_tree_to_dict(
                    content.heading_tree)
                unit_plan_translations[language]["heading_tree"] = heading_tree

        if "computational-thinking-links" in unit_plan_structure:
            ct_links_filename = unit_plan_structure[
                "computational-thinking-links"]
            ct_links_translations = self.get_markdown_translations(
                ct_links_filename,
                heading_required=False,
                remove_title=False,
            )
            for language, content in ct_links_translations.items():
                unit_plan_translations[language][
                    "computational_thinking_links"] = content.html_string

        unit_plan = self.topic.unit_plans.create(
            slug=self.unit_plan_slug,
            languages=list(content_translations.keys()),
        )

        self.populate_translations(unit_plan, unit_plan_translations)
        self.mark_translation_availability(unit_plan,
                                           required_fields=["name", "content"])

        unit_plan.save()

        self.log("Added unit plan: {}".format(unit_plan.name), 1)

        # Load the lessons for the unit plan
        # Get path to lesson yaml
        lessons_yaml = unit_plan_structure.get("lessons", None)
        if lessons_yaml is None:
            raise MissingRequiredFieldError(self.structure_file_path,
                                            ["lessons", "age-groups"],
                                            "Unit Plan")

        lesson_path, lesson_structure_file = os.path.split(lessons_yaml)

        # Call the loader to save the lessons into the db
        self.factory.create_lessons_loader(
            self.topic,
            unit_plan,
            content_path=os.path.join(self.content_path, lesson_path),
            structure_filename=lesson_structure_file,
            base_path=self.base_path,
        ).load()

        # Create AgeGroup and assign to lessons
        age_groups = unit_plan_structure.get("age-groups", None)
        if age_groups is None:
            raise MissingRequiredFieldError(self.structure_file_path,
                                            ["lessons", "age-groups"],
                                            "Unit Plan")

        for (age_group_slug, age_group_data) in age_groups.items():

            try:
                age_group = AgeGroup.objects.get(slug=age_group_slug)
            except ObjectDoesNotExist:
                raise KeyNotFoundError(self.structure_file_path,
                                       age_group_slug, "Age Range")

            if age_group_data is None:
                raise MissingRequiredFieldError(self.structure_file_path,
                                                ["lesson keys"], "Unit Plan")

            for (lesson_slug, lesson_data) in age_group_data.items():
                try:
                    lesson = Lesson.objects.get(slug=lesson_slug)
                except ObjectDoesNotExist:
                    raise KeyNotFoundError(self.structure_file_path,
                                           lesson_slug, "Lesson")

                if lesson_data is None or lesson_data.get("number",
                                                          None) is None:
                    raise MissingRequiredFieldError(self.structure_file_path,
                                                    ["number"], "Unit Plan")
                else:
                    lesson_number = lesson_data.get("number", None)

                relationship = LessonNumber(
                    age_group=age_group,
                    lesson=lesson,
                    number=lesson_number,
                )
                relationship.save()
    def load(self):
        """Load the content for programming challenges.

        Raises:
            CouldNotFindMarkdownFileError: when no file can be found with the provided filename.
            KeyNotFoundError: when no object can be found with the matching attribute.
            MissingRequiredFieldError: when no object can be found with the matching
                attribute.
        """
        programming_challenges_structure = self.load_yaml_file(self.structure_file_path)

        for (challenge_slug, challenge_structure) in programming_challenges_structure.items():

            if challenge_structure is None:
                raise MissingRequiredFieldError(
                    self.structure_file_path,
                    ["challenge-set-number", "challenge-number",
                        "programming-languages", "difficulty-level"],
                    "Programming Challenge"
                )

            # Retrieve required variables from md file
            challenge_set_number = challenge_structure.get("challenge-set-number", None)
            challenge_number = challenge_structure.get("challenge-number", None)
            challenge_languages = challenge_structure.get("programming-languages", None)
            challenge_difficulty = challenge_structure.get("difficulty-level", None)
            if None in [challenge_set_number, challenge_number, challenge_languages, challenge_difficulty]:
                raise MissingRequiredFieldError(
                    self.structure_file_path,
                    ["challenge-set-number", "challenge-number",
                        "programming-languages", "difficulty-level"],
                    "Programming Challenge"
                )

            # Build the path to the programming challenge's folder
            file_path = os.path.join(
                self.BASE_PATH,
                challenge_slug,
                "{}.md"
            )

            challenge_content = self.convert_md_file(
                file_path.format(challenge_slug),
                self.structure_file_path
            )

            challenge_extra_challenge_file = challenge_structure.get("extra-challenge", None)
            if challenge_extra_challenge_file:
                challenge_extra_challenge_content = self.convert_md_file(
                    file_path.format(challenge_extra_challenge_file[:-3]),
                    self.structure_file_path,
                    heading_required=False,
                )
                challenge_extra_challenge = challenge_extra_challenge_content.html_string
            else:
                challenge_extra_challenge = None

            try:
                difficulty_level = ProgrammingChallengeDifficulty.objects.get(
                    level=challenge_difficulty
                )
            except:
                raise KeyNotFoundError(
                    self.structure_file_path,
                    challenge_difficulty,
                    "Programming Challenge Difficulty"
                )

            programming_challenge = self.topic.programming_challenges.create(
                slug=challenge_slug,
                name=challenge_content.title,
                challenge_set_number=challenge_set_number,
                challenge_number=challenge_number,
                content=challenge_content.html_string,
                extra_challenge=challenge_extra_challenge,
                difficulty=difficulty_level
            )
            programming_challenge.save()

            LOG_TEMPLATE = "Added programming challenge: {}"
            self.log(LOG_TEMPLATE.format(programming_challenge.name), 1)

            for language in challenge_languages:
                if language is None:
                    raise MissingRequiredFieldError(
                        self.structure_file_path,
                        ["challenge-set-number", "challenge-number",
                            "programming-languages", "difficulty-level"],
                        "Programming Challenge"
                    )
                try:
                    language_object = ProgrammingChallengeLanguage.objects.get(
                        slug=language
                    )
                except:
                    raise KeyNotFoundError(
                        self.structure_file_path,
                        language,
                        "Programming Challenge Language"
                    )

                expected_result_content = self.convert_md_file(
                    file_path.format(
                        "{}-expected".format(language)
                    ),
                    self.structure_file_path,
                    heading_required=False
                )

                # Load example solution
                solution_content = self.convert_md_file(
                    file_path.format(
                        "{}-solution".format(language)
                    ),
                    self.structure_file_path,
                    heading_required=False
                )

                # Load hint if given
                try:
                    hint_content = self.convert_md_file(
                        file_path.format(
                            "{}-hints".format(language)
                        ),
                        self.structure_file_path,
                        heading_required=False
                    )
                except CouldNotFindMarkdownFileError:
                    hint_content = None

                implementation = ProgrammingChallengeImplementation(
                    expected_result=expected_result_content.html_string,
                    hints=None if hint_content is None else hint_content.html_string,
                    solution=solution_content.html_string,
                    language=language_object,
                    challenge=programming_challenge,
                    topic=self.topic
                )
                implementation.save()

                LOG_TEMPLATE = "Added language implementation: {}"
                self.log(LOG_TEMPLATE.format(implementation.language), 2)

            if "learning-outcomes" in challenge_structure:
                learning_outcomes = challenge_structure["learning-outcomes"]
                if learning_outcomes is not None:
                    for learning_outcome_slug in learning_outcomes:
                        try:
                            learning_outcome = LearningOutcome.objects.get(
                                slug=learning_outcome_slug
                            )
                            programming_challenge.learning_outcomes.add(learning_outcome)
                        except:
                            raise KeyNotFoundError(
                                self.structure_file_path,
                                learning_outcome_slug,
                                "Learning Outcome")
Beispiel #9
0
    def load(self):
        """Load the content for unit plans.

        Raise:
            KeyNotFoundError: when no object can be found with the matching attribute.
            MissingRequiredFieldError: when a value for a required model field cannot
                be found in the config file.
        """
        unit_plan_structure = self.load_yaml_file(self.structure_file_path)

        # Convert the content to HTML
        unit_plan_content = self.convert_md_file(
            os.path.join(self.BASE_PATH, "{}.md".format(self.unit_plan_slug)),
            self.structure_file_path)

        heading_tree = None
        if unit_plan_content.heading_tree:
            heading_tree = convert_heading_tree_to_dict(
                unit_plan_content.heading_tree)

        if "computational-thinking-links" in unit_plan_structure:
            file_name = unit_plan_structure["computational-thinking-links"]
            file_path = os.path.join(self.BASE_PATH, file_name)
            ct_links_content = self.convert_md_file(
                file_path,
                self.structure_file_path,
                heading_required=False,
                remove_title=False,
            )
            ct_links = ct_links_content.html_string
        else:
            ct_links = None

        unit_plan = self.topic.unit_plans.create(
            slug=self.unit_plan_slug,
            name=unit_plan_content.title,
            content=unit_plan_content.html_string,
            heading_tree=heading_tree,
            computational_thinking_links=ct_links,
        )
        unit_plan.save()

        self.log("Added unit plan: {}".format(unit_plan.name), 1)

        # Load the lessons for the unit plan
        # Get path to lesson yaml
        lessons_yaml = unit_plan_structure.get("lessons", None)
        if lessons_yaml is None:
            raise MissingRequiredFieldError(self.structure_file_path,
                                            ["lessons", "age-groups"],
                                            "Unit Plan")
        lessons_structure_file_path = os.path.join(self.BASE_PATH,
                                                   lessons_yaml)

        # Call the loader to save the lessons into the db
        self.factory.create_lessons_loader(lessons_structure_file_path,
                                           self.topic, unit_plan,
                                           self.BASE_PATH).load()

        # Create AgeGroup and assign to lessons
        age_groups = unit_plan_structure.get("age-groups", None)
        if age_groups is None:
            raise MissingRequiredFieldError(self.structure_file_path,
                                            ["lessons", "age-groups"],
                                            "Unit Plan")

        for (age_group_slug, age_group_data) in age_groups.items():

            try:
                age_group = AgeGroup.objects.get(slug=age_group_slug)
            except:
                raise KeyNotFoundError(self.structure_file_path,
                                       age_group_slug, "Age Range")

            if age_group_data is None:
                raise MissingRequiredFieldError(self.structure_file_path,
                                                ["lesson keys"], "Unit Plan")

            for (lesson_slug, lesson_data) in age_group_data.items():
                try:
                    lesson = Lesson.objects.get(slug=lesson_slug)
                except:
                    raise KeyNotFoundError(self.structure_file_path,
                                           lesson_slug, "Lesson")

                if lesson_data is None or lesson_data.get("number",
                                                          None) is None:
                    raise MissingRequiredFieldError(self.structure_file_path,
                                                    ["number"], "Unit Plan")
                else:
                    lesson_number = lesson_data.get("number", None)

                relationship = LessonNumber(
                    age_group=age_group,
                    lesson=lesson,
                    number=lesson_number,
                )
                relationship.save()
    def load(self):
        """Load the content for programming challenges.

        Raises:
            CouldNotFindMarkdownFileError: when no file can be found with the provided filename.
            KeyNotFoundError: when no object can be found with the matching attribute.
            MissingRequiredFieldError: when no object can be found with the matching
                attribute.
        """
        programming_challenges_structure = self.load_yaml_file(
            self.structure_file_path)

        for (challenge_slug,
             challenge_structure) in programming_challenges_structure.items():

            if challenge_structure is None:
                raise MissingRequiredFieldError(self.structure_file_path, [
                    "challenge-set-number", "challenge-number",
                    "programming-languages", "difficulty-level"
                ], "Programming Challenge")

            challenge_translations = self.get_blank_translation_dictionary()

            # Retrieve required variables from md file
            challenge_set_number = challenge_structure.get(
                "challenge-set-number", None)
            challenge_number = challenge_structure.get("challenge-number",
                                                       None)
            challenge_prog_languages = challenge_structure.get(
                "programming-languages", None)
            challenge_difficulty = challenge_structure.get(
                "difficulty-level", None)
            if None in [
                    challenge_set_number, challenge_number,
                    challenge_prog_languages, challenge_difficulty
            ]:
                raise MissingRequiredFieldError(self.structure_file_path, [
                    "challenge-set-number", "challenge-number",
                    "programming-languages", "difficulty-level"
                ], "Programming Challenge")

            content_filename = "{0}.md".format(challenge_slug)
            content_translations = self.get_markdown_translations(
                os.path.join(challenge_slug, content_filename))

            for language, content in content_translations.items():
                challenge_translations[language][
                    "content"] = content.html_string
                challenge_translations[language]["name"] = content.title

            challenge_extra_challenge_file = challenge_structure.get(
                "extra-challenge", None)
            if challenge_extra_challenge_file:
                extra_challenge_translations = self.get_markdown_translations(
                    os.path.join(challenge_slug,
                                 challenge_extra_challenge_file),
                    heading_required=False,
                )
                for language, content in extra_challenge_translations.items():
                    challenge_translations[language][
                        "extra_challenge"] = content.html_string

            try:
                difficulty_level = ProgrammingChallengeDifficulty.objects.get(
                    level=challenge_difficulty)
            except ObjectDoesNotExist:
                raise KeyNotFoundError(self.structure_file_path,
                                       challenge_difficulty,
                                       "Programming Challenge Difficulty")

            programming_challenge = self.topic.programming_challenges.create(
                slug=challenge_slug,
                challenge_set_number=challenge_set_number,
                challenge_number=challenge_number,
                difficulty=difficulty_level)
            self.populate_translations(programming_challenge,
                                       challenge_translations)
            self.mark_translation_availability(
                programming_challenge, required_fields=["name", "content"])

            programming_challenge.save()

            LOG_TEMPLATE = "Added programming challenge: {}"
            self.log(LOG_TEMPLATE.format(programming_challenge.name), 1)

            for prog_language in challenge_prog_languages:
                if prog_language is None:
                    raise MissingRequiredFieldError(self.structure_file_path, [
                        "challenge-set-number", "challenge-number",
                        "programming-languages", "difficulty-level"
                    ], "Programming Challenge")
                try:
                    prog_language_object = ProgrammingChallengeLanguage.objects.get(
                        slug=prog_language)
                except ObjectDoesNotExist:
                    raise KeyNotFoundError(self.structure_file_path,
                                           prog_language,
                                           "Programming Challenge Language")

                implementation_translations = self.get_blank_translation_dictionary(
                )

                filename_template = os.path.join(
                    challenge_slug, "{}-{{}}.md".format(prog_language))

                expected_result_translations = self.get_markdown_translations(
                    filename_template.format("expected"),
                    heading_required=False)
                for language, content in expected_result_translations.items():
                    implementation_translations[language][
                        "expected_result"] = content.html_string

                solution_translations = self.get_markdown_translations(
                    filename_template.format("solution"),
                    heading_required=False)
                for language, content in solution_translations.items():
                    implementation_translations[language][
                        "solution"] = content.html_string

                hints_translations = self.get_markdown_translations(
                    filename_template.format("hints"),
                    heading_required=False,
                    required=False)
                for language, content in hints_translations.items():
                    implementation_translations[language][
                        "hints"] = content.html_string

                implementation = ProgrammingChallengeImplementation(
                    language=prog_language_object,
                    challenge=programming_challenge,
                    topic=self.topic)

                self.populate_translations(implementation,
                                           implementation_translations)
                self.mark_translation_availability(
                    implementation,
                    required_fields=["solution", "expected_result"])

                implementation.save()

                LOG_TEMPLATE = "Added language implementation: {}"
                self.log(LOG_TEMPLATE.format(implementation.language), 2)

            test_cases = challenge_structure.get("test-cases", None)
            if (test_cases is not None):
                for (testcase_id, testcase_type) in test_cases.items():
                    test_case_translations = self.get_blank_translation_dictionary(
                    )

                    testcase_filename_template = os.path.join(
                        challenge_slug, 'test-cases',
                        "test-case-{}-{{}}.txt".format(testcase_id))

                    testcase_input = open(self.get_localised_file(
                        "en",
                        testcase_filename_template.format(testcase_type)),
                                          encoding='UTF-8').read()

                    testcase_output = open(self.get_localised_file(
                        "en", testcase_filename_template.format("output")),
                                           encoding='UTF-8').read()

                    test_case = TestCase(number=testcase_id,
                                         test_input=testcase_input,
                                         expected_output=testcase_output,
                                         question_type=testcase_type,
                                         challenge=programming_challenge)

                    required_fields = [
                        'test_input', 'expected_output', 'question_type'
                    ]

                    self.populate_translations(test_case,
                                               test_case_translations)
                    self.mark_translation_availability(
                        test_case, required_fields=required_fields)
                    test_case.save()

                    LOG_TEMPLATE = "Added Programming Challenge Test Case: {}"
                    self.log(LOG_TEMPLATE.format(testcase_id), 2)

            if "learning-outcomes" in challenge_structure:
                learning_outcomes = challenge_structure["learning-outcomes"]
                if learning_outcomes is not None:
                    for learning_outcome_slug in learning_outcomes:
                        try:
                            learning_outcome = LearningOutcome.objects.get(
                                slug=learning_outcome_slug)
                            programming_challenge.learning_outcomes.add(
                                learning_outcome)
                        except ObjectDoesNotExist:
                            raise KeyNotFoundError(self.structure_file_path,
                                                   learning_outcome_slug,
                                                   "Learning Outcome")
    def load(self):
        """Load the content for a single lesson.

        Raises:
            KeyNotFoundError: when no object can be found with the matching attribute.
            InvalidYAMLValueError: when provided value is not valid.
            MissingRequiredFieldError: when a value for a required model field cannot be
                found in the config file.
        """
        lessons_structure = self.load_yaml_file(self.structure_file_path)

        for (lesson_slug, lesson_structure) in lessons_structure.items():

            if lesson_structure is None:
                raise MissingRequiredFieldError(self.structure_file_path,
                                                ["number"], "Lesson")

            lesson_translations = self.get_blank_translation_dictionary()

            content_filename = "{}.md".format(lesson_slug)
            content_translations = self.get_markdown_translations(
                content_filename)
            for language, content in content_translations.items():
                lesson_translations[language]["content"] = content.html_string
                lesson_translations[language]["name"] = content.title
                if content.heading_tree:
                    heading_tree = convert_heading_tree_to_dict(
                        content.heading_tree)
                    lesson_translations[language][
                        "heading_tree"] = heading_tree

            if "computational-thinking-links" in lesson_structure:
                filename = lesson_structure["computational-thinking-links"]
                ct_links_translations = self.get_markdown_translations(
                    filename,
                    heading_required=False,
                    remove_title=False,
                )
                for language, content in ct_links_translations.items():
                    lesson_translations[language][
                        "computational_thinking_links"] = content.html_string

            if "programming-challenges-description" in lesson_structure:
                filename = lesson_structure[
                    "programming-challenges-description"]
                pcd_translations = self.get_markdown_translations(
                    filename,
                    heading_required=False,
                    remove_title=False,
                )
                for language, content in pcd_translations.items():
                    lesson_translations[language][
                        "programming_challenges_description"] = content.html_string

            if "duration" in lesson_structure:
                lesson_duration = lesson_structure["duration"]
            else:
                lesson_duration = None

            lesson = self.topic.lessons.create(
                unit_plan=self.unit_plan,
                slug=lesson_slug,
                duration=lesson_duration,
            )
            self.populate_translations(lesson, lesson_translations)
            self.mark_translation_availability(
                lesson, required_fields=["name", "content"])
            lesson.save()

            # Add programming challenges
            if "programming-challenges" in lesson_structure:
                programming_challenge_slugs = lesson_structure[
                    "programming-challenges"]
                if programming_challenge_slugs is not None:
                    # Check all slugs are valid
                    for programming_challenge_slug in programming_challenge_slugs:
                        try:
                            ProgrammingChallenge.objects.get(
                                slug=programming_challenge_slug,
                                topic=self.topic)

                        except ObjectDoesNotExist:
                            raise KeyNotFoundError(self.structure_file_path,
                                                   programming_challenge_slug,
                                                   "Programming Challenges")

                    # Store number of challenge in relationship with lesson.
                    # If three linked challenges have numbers 1.1, 4.2, and 4.5
                    # They will be stored as 1.1, 2.1, and 2.2 respectively.

                    # Order challenges for numbering.
                    programming_challenges = ProgrammingChallenge.objects.filter(
                        slug__in=programming_challenge_slugs,
                        topic=self.topic).order_by("challenge_set_number",
                                                   "challenge_number")

                    # Setup variables for numbering.
                    display_set_number = 0
                    last_set_number = -1
                    display_number = 0
                    last_number = -1

                    # For each challenge, increment number variables if original
                    # numbers are different.
                    for programming_challenge in programming_challenges:
                        if programming_challenge.challenge_set_number > last_set_number:
                            display_set_number += 1
                            display_number = 0
                            last_number = -1
                        if programming_challenge.challenge_number > last_number:
                            display_number += 1
                        last_set_number = programming_challenge.challenge_set_number
                        last_number = programming_challenge.challenge_number

                        # Create and save relationship between lesson and
                        # challenge that contains challenge number.
                        relationship = ProgrammingChallengeNumber(
                            programming_challenge=programming_challenge,
                            lesson=lesson,
                            challenge_set_number=display_set_number,
                            challenge_number=display_number,
                        )
                        relationship.save()

            # Add learning outcomes
            if "learning-outcomes" in lesson_structure:
                learning_outcome_slugs = lesson_structure.get(
                    "learning-outcomes", None)

                if learning_outcome_slugs is None:
                    raise InvalidYAMLValueError(self.structure_file_path,
                                                ["learning-outcomes"],
                                                "Lesson")
                else:
                    for learning_outcome_slug in learning_outcome_slugs:
                        try:
                            learning_outcome = LearningOutcome.objects.get(
                                slug=learning_outcome_slug)
                            lesson.learning_outcomes.add(learning_outcome)
                        except ObjectDoesNotExist:
                            raise KeyNotFoundError(self.structure_file_path,
                                                   learning_outcome_slug,
                                                   "Learning Outcomes")

            # Add classroom resources
            if "classroom-resources" in lesson_structure:
                classroom_resources_slugs = lesson_structure[
                    "classroom-resources"]
                if classroom_resources_slugs is not None:
                    for classroom_resources_slug in classroom_resources_slugs:
                        try:
                            classroom_resource = ClassroomResource.objects.get(
                                slug=classroom_resources_slug)
                            lesson.classroom_resources.add(classroom_resource)
                        except ObjectDoesNotExist:
                            raise KeyNotFoundError(self.structure_file_path,
                                                   classroom_resources_slug,
                                                   "Classroom Resources")

            # Add generated resources
            if "generated-resources" in lesson_structure:
                resources = lesson_structure["generated-resources"]
                if resources is not None:
                    relationship_strings_filename = "{}-resource-descriptions.yaml".format(
                        lesson_slug)
                    relationship_translations = self.get_yaml_translations(
                        relationship_strings_filename, )
                    for resource_slug in resources:
                        relationship_translation = relationship_translations.get(
                            resource_slug, dict())
                        try:
                            resource = Resource.objects.get(slug=resource_slug)
                        except ObjectDoesNotExist:
                            raise KeyNotFoundError(self.structure_file_path,
                                                   resource_slug, "Resources")

                        relationship = ResourceDescription(
                            resource=resource,
                            lesson=lesson,
                        )
                        self.populate_translations(relationship,
                                                   relationship_translation)
                        self.mark_translation_availability(
                            relationship, required_fields=["description"])

                        relationship.save()

            self.log("Added lesson: {}".format(lesson.__str__()), 2)
Beispiel #12
0
 def test_attributes(self):
     exception = KeyNotFoundError("config file path", "key", "field")
     self.assertEqual(exception.config_file_path, "config file path")
     self.assertEqual(exception.key, "key")
     self.assertEqual(exception.field, "field")
Beispiel #13
0
    def load(self):
        """Load the content for curriculum integrations.

        Raise:
            KeyNotFoundError: when no object can be found with the matching attribute.
            MissingRequiredFieldError: when a value for a required model field cannot be
                found in the config file.
        """
        structure = self.load_yaml_file(self.structure_file_path)

        for (integration_slug, integration_data) in structure.items():
            if integration_data is None:
                raise MissingRequiredFieldError(self.structure_file_path,
                                                ["number", "curriculum-areas"],
                                                "Curriculum Integration")

            integration_translations = self.get_blank_translation_dictionary()

            content_filename = "{}.md".format(integration_slug)
            content_translations = self.get_markdown_translations(
                content_filename)
            for language, content in content_translations.items():
                integration_translations[language][
                    "content"] = content.html_string
                integration_translations[language]["name"] = content.title

            integration_number = integration_data.get("number", None)
            integration_curriculum_areas = integration_data.get(
                "curriculum-areas", None)
            if None in [integration_number, integration_curriculum_areas]:
                raise MissingRequiredFieldError(self.structure_file_path,
                                                ["number", "curriculum-areas"],
                                                "Curriculum Integration")

            integration = self.topic.curriculum_integrations.create(
                slug=integration_slug,
                number=integration_number,
            )
            self.populate_translations(integration, integration_translations)
            self.mark_translation_availability(
                integration, required_fields=["name", "content"])
            integration.save()

            # Add curriculum areas
            for curriculum_area_slug in integration_curriculum_areas:
                try:
                    curriculum_area = CurriculumArea.objects.get(
                        slug=curriculum_area_slug)
                    if curriculum_area.children.exists():
                        raise InvalidYAMLValueError(
                            self.structure_file_path,
                            "curriculum-areas - value '{}' is invalid".format(
                                curriculum_area_slug),
                            "Curriculum area with no children (parent curriculum areas are not allowed)"
                        )
                    else:
                        integration.curriculum_areas.add(curriculum_area)
                except ObjectDoesNotExist:
                    raise KeyNotFoundError(self.structure_file_path,
                                           curriculum_area_slug,
                                           "Curriculum Areas")

            # Add prerequisite lessons
            if "prerequisite-lessons" in integration_data:
                prerequisite_lessons = integration_data["prerequisite-lessons"]
                if prerequisite_lessons is not None:
                    for (unit_plan_slug,
                         lessons) in prerequisite_lessons.items():
                        if lessons is None:
                            raise MissingRequiredFieldError(
                                self.structure_file_path, ["unit-plan"],
                                "Prerequisite Lesson")
                        try:
                            UnitPlan.objects.get(topic__slug=self.topic.slug,
                                                 slug=unit_plan_slug)
                        except ObjectDoesNotExist:
                            raise KeyNotFoundError(self.structure_file_path,
                                                   unit_plan_slug,
                                                   "Unit Plans")
                        for lesson_slug in lessons:
                            try:
                                lesson = Lesson.objects.get(
                                    topic__slug=self.topic.slug,
                                    unit_plan__slug=unit_plan_slug,
                                    slug=lesson_slug,
                                )
                                integration.prerequisite_lessons.add(lesson)
                            except ObjectDoesNotExist:
                                raise KeyNotFoundError(
                                    self.structure_file_path, lesson_slug,
                                    "Lessons")

            self.log(
                "Added curriculum integration: {}".format(integration.name), 1)