def read_notebook(meeting: Meeting, suffix: str = FileExtensions.Solutionbook): notebook_path = repositories.local_meeting_root(meeting) / repr(meeting) if not notebook_path.with_suffix(suffix).exists(): return nbformat.v4.new_notebook() else: return nbformat.read(str(notebook_path.with_suffix(suffix)), as_version=4)
def update_or_create_folders_and_files(meeting: Meeting): """Performs some initial directory creation / cleanup. Currently this implementation is quite destructive. Ideally, there's some way to go about *intelligently* merging work ~ so this would allow for some temporary titles and the like. Present file locations: - <group>/<semester>/<repr(meeting)>/<repr(meeting)>.solution.ipynb - ucfai.org/content/<group>/<semester>/<meeting-filename>.md """ # TODO rename `placeholder` notebooks # this requires changes in both the `paths.repo_meeting_folder` as well as the # `paths.site_post` # TODO implement a way to track meetings – since `hugo-academic`'s docs doesn't # work the same way Jekyll posts did (YYYY-MM-DD-<filename>.md), we need a new # way to uniquely identify meetings (so we know which to clean and such) # NOTE we might just be able to totally overwrite the group's contents on the # site, since everything in, say, `core/fa19/*` (minus `_index.md`) is # generated from the meeting's Solutionbook # TODO allow for meetings to be moved – this intuitively makes sense to resolve # with the `filename` parameter, but may also need to consider the `date` (you # can get all this from `repr(meeting)`) path = repositories.local_meeting_root(meeting) root = repositories.local_semester_root(meeting) local_neighbors = list(root.iterdir()) for d in local_neighbors: date, name = d.stem[:10], d.stem[11:] # remove placeholder # rename by date shares_meet = ("meeting" in name and int(name[-2:]) == meeting.number - 1) shares_date = (date == repr(meeting)[:10]) if d.stem != repr(meeting) and (shares_meet or shares_date): old_path = d old_soln = d / "".join([d.stem, FileExtensions.Solutionbook]) new_path = old_path.with_name(repr(meeting)) new_soln = old_path / "".join( [repr(meeting), FileExtensions.Solutionbook]) # NOTE: rename internal files before renaming the folder tqdm.write(f"renaming: {old_soln} -> {new_soln}") old_soln.rename(new_soln) tqdm.write(f"renaming: {old_path} -> {new_path}") old_path.rename(new_path) return # TODO: handle meeting swaps (e.g. meeting02 with meeting 06, etc.) # TODO: handle meeting renames with swaps # TODO: allow for renaming to propagate through to platforms like Kaggle path.mkdir(exist_ok=True, parents=True)
def from_meeting(self, meeting: Meeting): nb = read_notebook(meeting) resources = {"output_extension": FileExtensions.Workbook} notebook, resources = super().from_notebook_node(nb, resources) # writer = FilesWriter(build_directory=str(paths.repo_meeting_folder(meeting))) # writer.write(json.dumps(notebook), resources, repr(meeting)) filename = (repositories.local_meeting_root(meeting) / repr(meeting)).with_suffix(FileExtensions.Workbook) open(filename, "w").write(notebook) return notebook, resources
def from_meeting(self, meeting: Meeting): notebook_path = repositories.local_meeting_root(meeting) / "".join( [repr(meeting), FileExtensions.Solutionbook]) # TODO concatenate front matter to notebook output front_matter = templates.load("meeting/hugo-front-matter.md.j2") front_matter = front_matter.render( **{ "group": repr(meeting.group), "meeting": { "title": meeting.required["title"], "date": meeting.meta.date.isoformat(), # TODO decide on what date qualifies to be `lastmod` "lastmod": meeting.meta.date.isoformat(), "authors": meeting.required["instructors"], "tags": meeting.optional["tags"], "description": meeting.required["description"], "weight": meeting.number, "room": meeting.meta.room, "cover": meeting.required["cover"], }, "semester": { "full": str(meeting.group.semester), "short": repr(meeting.group.semester), }, "urls": { "youtube": urlgen.youtube(meeting), "slides": urlgen.slides(meeting), "github": urlgen.github(meeting), "kaggle": urlgen.kaggle(meeting), "colab": urlgen.colab(meeting), }, }) # the notebook is output as a string, so treat it as such when concatenating notebook, resources = self.from_filename(str(notebook_path), resources=None) resources.update({"output_extension": ".md"}) writer = FilesWriter( build_directory=str(paths.site_group_folder_from_meeting(meeting))) front_matter_plus_notebook = f"{front_matter}\n{notebook}" writer.write(front_matter_plus_notebook, resources, meeting.required["filename"]) # writer.write(notebook, resources, meeting.required["filename"]) return notebook, resources
def local_and_remote_kernels_diff(meeting: Meeting) -> bool: _configure_environment() remote_kernel = pull_kernel(meeting) if not remote_kernel: return True local_kernel = repositories.local_meeting_root(meeting) / "".join( [repr(meeting), FileExtensions.Workbook]) remote_kernel_json = json.dumps(json.load(open(remote_kernel, "r"))).encode("utf-8") local_kernel_json = json.dumps(json.load(open(local_kernel, "r"))).encode("utf-8") remote_kernel_hash = sha256(bytes(remote_kernel_json)).hexdigest() local_kernel_hash = sha256(bytes(local_kernel_json)).hexdigest() remote_kernel.unlink() # clean-up after diff return remote_kernel_hash != local_kernel_hash
def from_meeting(self, meeting: Meeting): self.meeting = meeting nb = read_notebook(meeting) resources = {"output_extension": FileExtensions.Solutionbook} # NotebookExporter.from_notebook_node returns a notebook as a string notebook, resources = super().from_notebook_node(nb, resources=resources) # to operate over the notebook, we need it to be a NotebookNode notebook = nbformat.reads(notebook, as_version=4) notebook.cells.insert(0, self._notebook_heading()) # to write to disk, it now needs to be a string notebook = nbformat.writes(notebook) filename = (repositories.local_meeting_root(meeting) / repr(meeting)).with_suffix(FileExtensions.Solutionbook) open(filename, "w").write(notebook) return notebook, resources