def snapshot( self, version: Optional[dict], tale: dict, new_version: dict, user=None, force=False, ) -> None: """Creates a new version from the current state and an old version. The implementation here differs a bit from https://docs.google.com/document/d/1b2xZtIYvgVXz7EVeV-C18So_a7QLGg59dPQMxvBcA5o since the document assumes traditional use of Girder objects to simulate a filesystem, whereas the current reality is that we are moving more towards Girder being a thin layer on top of an actual FS (i.e. virtual_resources). In particular, the data folder remains in the domain of the DMS, meaning that actual files are only downloaded on demand. The current implementation of the virtual objects does not seem to have a straightforward way of embedding pure girder folders inside a virtual tree. The solution currently adopted to address this issue involves storing the dataset in the version folder itself, which, for efficiency reasons remains a Girder folder (but also a virtual_resources root). It may be relevant to note that this implementation uses option (b) in the above document with respect to the meaning of "copy" in step 4.1.2.1. To be more precise, when a file is changed in the current workspace, the new version will hard link to the file in question instead of doing an actual copy. This allows for O(1) equality comparisons between files, but requires that modifications to files in the workspace always create a new file (which is the case if files are only modified through the WebDAV FS mounted in a tale container). """ new_version_path = Path(new_version["fsPath"]) manifest = Manifest(tale, user, versionId=new_version["_id"], expand_folders=False) with open((new_version_path / "manifest.json").as_posix(), "w") as fp: fp.write(manifest.dump_manifest()) with open((new_version_path / "environment.json").as_posix(), "w") as fp: fp.write(manifest.dump_environment()) oldWorkspace = (None if version is None else Path(version["fsPath"]) / "workspace") workspace = Folder().load(tale["workspaceId"], force=True) crtWorkspace = Path(workspace["fsPath"]) newWorkspace = new_version_path / "workspace" newWorkspace.mkdir() self.snapshotRecursive(oldWorkspace, crtWorkspace, newWorkspace)
def _is_same(self, tale, version, user): workspace = Folder().load(tale["workspaceId"], force=True) tale_workspace_path = Path(workspace["fsPath"]) version_path = None if version is None else Path(version["fsPath"]) version_workspace_path = None if version_path is None else version_path / "workspace" manifest_obj = Manifest(tale, user) manifest = json.loads(manifest_obj.dump_manifest()) environment = json.loads(manifest_obj.dump_environment()) tale_restored_from_wrk = Tale().restoreTale(manifest, environment) tale_restored_from_ver = \ self._restoreTaleFromVersion(version, annotate=False) if version else None if self._sameTaleMetadata(tale_restored_from_ver, tale_restored_from_wrk) and \ self._sameTree(version_workspace_path, tale_workspace_path): raise RestException('Not modified', code=303, extra=str(version["_id"]))