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()
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/'
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
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)]
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, }, ))
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')
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'
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
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')
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'
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')
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'
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()
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())
def get_repo_info(self): return get_local_repo_info('/dummy')