def _update_file( code: models.File, other: models.FileOwner, ) -> None: if request.args.get('operation', None) == 'rename': code.rename_code(new_name, new_parent, other) db.session.flush() code.parent = new_parent else: with open(code.get_diskname(), 'wb') as f: f.write(request.get_data())
def get_stat_information(file: models.File) -> t.Mapping[str, t.Any]: """Get stat information for a given :class:`.models.File` The resulting object will look like this: .. code:: python { 'is_directory': bool, # Is the given file a directory 'modification_date': int, # When was the file last modified, as # unix timestamp in utc time. 'size': int, # The size on disk of the file or 0 if the file is a # directory. 'id': int, # The id of the given file. } :param file: The file to get the stat information for. :returns: The information as described above. """ mod_date = file.modification_date filename = None if file.is_directory else file.get_diskname() size = 0 if file.is_directory else os.stat(filename).st_size return { 'is_directory': file.is_directory, 'modification_date': round(mod_date.timestamp()), 'size': size, 'id': file.id, }
def _restore_directory_structure( code: models.File, parent: str, cache: t.Mapping[int, t.Sequence[models.File]], ) -> FileTree: """Worker function for :py:func:`.restore_directory_structure` :param code: A file :param parent: Path to parent directory :param cache: The cache to use to get file children. :returns: A tree as described in :py:func:`.restore_directory_structure` """ out = safe_join(parent, code.name) if code.is_directory: os.mkdir(out) subtree: t.List[FileTree] = [ _restore_directory_structure(child, out, cache) for child in cache[code.id] ] return { "name": code.name, "id": code.id, "entries": subtree, } else: # this is a file shutil.copyfile(code.get_diskname(), out, follow_symlinks=False) return {"name": code.name, "id": code.id}
def restore_directory_structure( code: models.File, parent: str, exclude: models.FileOwner = models.FileOwner.teacher) -> FileTree: """Restores the directory structure recursively for a code submission (a :class:`.models.Work`). The directory structure is returned like this: .. code:: python { "id": 1, "name": "rootdir" "entries": [ { "id": 2, "name": "file1.txt" }, { "id": 3, "name": "subdir" "entries": [ { "id": 4, "name": "file2.txt." }, { "id": 5, "name": "file3.txt" } ], }, ], } :param code: A file :param parent: Path to parent directory :param exclude: The file owner to exclude. :returns: A tree as described """ out = os.path.join(parent, code.name) if code.is_directory: os.mkdir(out) children = code.children.filter(models.File.fileowner != exclude).all() subtree: t.List[FileTree] = [ restore_directory_structure(child, out, exclude) for child in children ] return { "name": code.name, "id": code.id, "entries": subtree, } else: # this is a file shutil.copyfile(code.get_diskname(), out, follow_symlinks=False) return {"name": code.name, "id": code.id}
def get_file_url(file: models.File) -> str: """Copies the given file to the mirror uploads folder and returns its name. To get this file, see the :func:`psef.v1.files.get_file` function. :param file: The file object :returns: The name of the newly created file (the copy). """ path, name = psef.files.random_file_path('MIRROR_UPLOAD_DIR') shutil.copyfile(file.get_diskname(), path) return name
def split_code( code: models.File, new_owner: FileOwner, old_owner: FileOwner, ) -> models.File: """Split the given ``code`` into multiple code objects. The old object in the database will be given a ``fileowner`` of ``old_owner`` and the newly created object will be given ``new_owner``. If ``code`` is a directory this directory is splitted (see :py:func:`redistribute_directory`), if it is a file the original content of the file is only copied if ``copy`` is ``True``. :param code: The file to split. :param new_owner: The new ``fileowner`` of the new file. :param old_owner: The new ``fileowner`` of the old file. :returns: The newly constructed file. """ code.fileowner = old_owner old_id = code.id old_diskname = None if code.is_directory else code.get_diskname() db.session.flush() code = db.session.query(models.File).get(code.id) db.session.expunge(code) make_transient(code) code.id = None db.session.add(code) db.session.flush() code.fileowner = new_owner if not code.is_directory: _, code.filename = psef.files.random_file_path() shutil.copyfile(old_diskname, code.get_diskname()) else: redistribute_directory(code, models.File.query.get(old_id)) return code
def get_file_contents(code: models.File) -> bytes: """Get the contents of the given :class:`.models.File`. :param code: The file object to read. :returns: The contents of the file with newlines. """ if code.is_directory: raise APIException( 'Cannot display this file as it is a directory.', f'The selected file with id {code.id} is a directory.', APICodes.OBJECT_WRONG_TYPE, 400) filename = code.get_diskname() if os.path.islink(filename): raise APIException( f'This file is a symlink to `{os.readlink(filename)}`.', 'The file {} is a symlink'.format(code.id), APICodes.INVALID_STATE, 410) with open(filename, 'rb') as codefile: return codefile.read()