예제 #1
0
 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())
예제 #2
0
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,
    }
예제 #3
0
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}
예제 #4
0
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}
예제 #5
0
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
예제 #6
0
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
예제 #7
0
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()