Example #1
0
    def __init__(
        self, *,
        url_factories=None,
        schema_url_factory=None,
        arca=None,
        trusted_repo_patterns=(),
        repo_info=None,
    ):
        self.root = self
        self.url_factories = url_factories or {}
        self.schema_url_factory = schema_url_factory
        super().__init__(parent=self)
        self.arca = arca
        self.trusted_repo_patterns = trusted_repo_patterns

        self.courses = {}
        self.run_years = {}
        self.licenses = {}
        self.self_study_courses = {}

        self.set_repo_info(repo_info or get_local_repo_info('.'))

        # For pagination of runs
        # XXX: This shouldn't be necessary
        self.explicit_run_years = set()
Example #2
0
def test_get_local_repo_info_overridden_branch(monkeypatch):
    url = 'https://github.com/encukou/empty-repo'
    monkeypatch.setenv('NAUCSE_MAIN_REPO_URL', url)
    monkeypatch.setenv('NAUCSE_MAIN_REPO_BRANCH', 'somebranch')
    repo_info = edit_info.get_local_repo_info(fixture_path)
    ei = repo_info.get_edit_info('.')
    assert ei.icon == 'github'
    assert ei.url == 'https://github.com/encukou/empty-repo/blob/somebranch/'
Example #3
0
def test_get_local_repo_info_overridden(monkeypatch):
    url = 'https://github.com/encukou/empty-repo'
    monkeypatch.setenv('NAUCSE_MAIN_REPO_URL', url)
    monkeypatch.delenv('NAUCSE_MAIN_REPO_BRANCH', raising=False)
    repo_info = edit_info.get_local_repo_info(fixture_path)
    ei = repo_info.get_edit_info('.')
    assert ei.icon == 'github'
    assert ei.url == url
Example #4
0
    def load_local_courses(self, path):
        """Load local courses and lessons from the given path

        Note: Licenses should be loaded before calling load_local_courses,
        otherwise lessons will have no licences to choose from
        """
        self.set_repo_info(get_local_repo_info(path))

        def _load_local_course(slug, **renderer_kwargs):
            renderer = local_renderer.LocalRenderer(
                path=path,
                slug=slug,
                repo_info=self.repo_info,
                **renderer_kwargs,
            )
            course = Course.from_renderer(parent=self, renderer=renderer)
            self.add_course(course)

        for slug in local_renderer.get_course_slugs(path=path):
            print(path, slug)
            _load_local_course(slug)

        for link_path in chain(
                path.glob('courses/**/link.yml'),
                path.glob('runs/**/link.yml'),
        ):
            raise ValueError(
                '"link.yml" files are not supported since naucse 5.0')

        compiled_path = path / 'courses.yml'
        fetcher = compiled_renderer.Fetcher()
        featured_courses = []
        if compiled_path.exists():
            self.aggregates_courses = True
            with compiled_path.open() as f:
                courses_info = yaml.safe_load(f)
            for slug, course_info in courses_info.items():
                renderer = compiled_renderer.CompiledRenderer(
                    slug,
                    course_info,
                    fetcher=fetcher,
                )
                course = Course.from_renderer(
                    renderer=renderer,
                    parent=self,
                    canonical=course_info.get('canonical', False),
                )
                self.add_course(course)
                feature_index = course_info.get('featured', None)
                if feature_index is not None:
                    featured_courses.append((feature_index, slug, course))
        # Sort featured courses by their index
        self.featured_courses = [c for i, s, c in sorted(featured_courses)]
Example #5
0
def add_test_course(model, slug, data):
    model.add_course(
        models.load(
            models.Course,
            slug=slug,
            repo_info=get_local_repo_info(fixture_path),
            parent=model,
            data={
                'api_version': [0, 0],
                'course': data,
            },
        ))
Example #6
0
def test_dump_local_course(model, assert_model_dump):
    path = fixture_path / 'minimal-courses'
    model.add_course(models.Course.load_local(
        parent=model,
        path=path,
        repo_info=get_local_repo_info(path),
        slug='courses/minimal',
    ))

    assert_model_dump(model, 'minimal-root')
    course = model.courses['courses/minimal']
    assert_model_dump(course, 'minimal-course')
Example #7
0
def test_add_local_course():
    model = models.Root()
    path = fixture_path / 'minimal-courses'
    model.add_course(models.Course.load_local(
        parent=model,
        path=path,
        repo_info=get_local_repo_info(path),
        slug='courses/minimal',
    ))

    assert sorted(model.courses) == ['courses/minimal']

    assert model.courses['courses/minimal'].title == 'A minimal course'
    assert model.courses['courses/minimal'].slug == 'courses/minimal'
Example #8
0
def load_course_from_fixture(model, filename):
    """Load course from a file with info as it would come from a fork.

    Contents of the file are passed as kwargs to DummyRenderer.
    """

    with (fixture_path / filename).open() as f:
        renderer = DummyRenderer(**yaml.safe_load(f))
    course = models.Course.load_local(
        parent=model,
        repo_info=get_local_repo_info('/dummy'),
        slug='courses/complex',
        renderer=renderer,
    )
    model.add_course(course)
    return course
Example #9
0
def test_dump_local_course(model, assert_model_dump):
    version = (0, 0)
    path = fixture_path / 'minimal-courses'
    renderer = LocalRenderer(
        repo_info=get_local_repo_info(path),
        path=path,
        slug='courses/minimal',
    )
    model.add_course(
        models.Course.from_renderer(
            parent=model,
            renderer=renderer,
        ))

    assert_model_dump(model, 'minimal-root')
    course = model.courses['courses/minimal']
    assert_model_dump(course, 'minimal-course')
Example #10
0
def test_from_renderer():
    model = models.Root()
    path = fixture_path / 'minimal-courses'
    renderer = LocalRenderer(
        path=path,
        repo_info=get_local_repo_info(path),
        slug='courses/minimal',
    )
    model.add_course(
        models.Course.from_renderer(
            parent=model,
            renderer=renderer,
        ))

    assert sorted(model.courses) == ['courses/minimal']

    assert model.courses['courses/minimal'].title == 'A minimal course'
    assert model.courses['courses/minimal'].slug == 'courses/minimal'
Example #11
0
def test_empty_course_from_renderer(model):
    """Valid trvial json that could come from a fork is loaded correctly"""
    source = 'courses/minimal/info.yml'
    renderer = DummyRenderer(
        course={
            'api_version': [0, 0],
            'course': {
                'title': 'A minimal course',
                'sessions': [],
                'source_file': source,
            }
        })
    course = models.Course.load_local(
        parent=model,
        repo_info=get_local_repo_info('/dummy'),
        slug='courses/minimal',
        renderer=renderer,
    )
    check_empty_course_attrs(course, source_file=Path(source))
    assert_yaml_dump(models.dump(course), 'minimal-course.yml')
Example #12
0
def test_lessons_slug():
    """Test that an arbitrary course can have the slug 'lessons',
    which used to be special
    """
    model = models.Root()
    path = fixture_path / 'minimal-courses'
    renderer = LocalRenderer(
        path=path,
        repo_info=get_local_repo_info(path),
        slug='lessons',
        api_slug='courses/minimal',
    )
    model.add_course(
        models.Course.from_renderer(
            parent=model,
            renderer=renderer,
        ))

    assert sorted(model.courses) == ['lessons']

    assert model.courses['lessons'].slug == 'lessons'
    assert model.courses['lessons'].title == 'A minimal course'
Example #13
0
def test_get_local_repo_info(monkeypatch):
    monkeypatch.delenv('NAUCSE_MAIN_REPO_URL', raising=False)
    repo_info = edit_info.get_local_repo_info(fixture_path)
    ei = repo_info.get_edit_info('.')
    assert ei.icon == None
    assert ei.url == fixture_path.as_uri()
Example #14
0
    def load_local_courses(self, path):
        """Load local courses and lessons from the given path

        Note: Licenses should be loaded before calling load_local_courses,
        otherwise lessons will have no licences to choose from
        """
        self.set_repo_info(get_local_repo_info(path))

        self_study_course_path = path / 'courses'
        run_path = path / 'runs'
        lesson_path = path / 'lessons'

        def _load_local_course(course_path, slug, canonical_if_local=False):
            link_path = course_path / 'link.yml'
            if link_path.is_file():
                with link_path.open() as f:
                    link_info = yaml.safe_load(f)
                checked_url = '{repo}#{branch}'.format(**link_info)
                if any(
                    fnmatch(checked_url, l) for l in self.trusted_repo_patterns
                ):
                    course = Course.load_remote(
                        slug, parent=self, link_info=link_info,
                    )
                    self.add_course(course)
                else:
                    logger.debug(f'Untrusted repo: {checked_url}')
            if (course_path / 'info.yml').is_file():
                course = Course.load_local(
                    slug, parent=self, repo_info=self.repo_info, path=path,
                    canonical=canonical_if_local,
                )
                self.add_course(course)

        if self_study_course_path.exists():
            for course_path in self_study_course_path.iterdir():
                slug = 'courses/' + course_path.name
                _load_local_course(course_path, slug, canonical_if_local=True)
        else:
            logger.warning(f'No courses at {self_study_course_path}')

        if run_path.exists():
            for year_path in sorted(run_path.iterdir()):
                if year_path.is_dir():
                    self.explicit_run_years.add(int(year_path.name))
                    for course_path in year_path.iterdir():
                        slug = f'{year_path.name}/{course_path.name}'
                        _load_local_course(course_path, slug)

        if lesson_path.exists():
            self.add_course(Course.load_local(
                'lessons',
                repo_info=self.repo_info,
                canonical=True,
                parent=self,
                path=path,
            ))
        else:
            logger.warning(f'No lessons at {lesson_path}')

        self_study_order_path = self_study_course_path / 'info.yml'
        if self_study_order_path.exists():
            with (path / 'courses/info.yml').open() as f:
                course_info = yaml.safe_load(f)
            self.featured_courses = [
                self.courses[f'courses/{n}'] for n in course_info['order']
            ]
        else:
            logger.warning(f'No featured courses at {self_study_order_path}')
            self.featured_courses = list(self.courses.values())
Example #15
0
 def get_repo_info(self):
     return get_local_repo_info('/dummy')