Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

change the structure of _SeafDirentBase #21

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
143 changes: 100 additions & 43 deletions seafileapi/files.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,57 +2,76 @@
import os
import posixpath
import re
from seafileapi.utils import querystr
from .utils import querystr, utf8lize, urlencode, urljoin

ZERO_OBJ_ID = '0000000000000000000000000000000000000000'

class _SeafDirentBase(object):
"""Base class for :class:`SeafFile` and :class:`SeafDir`.

It provides implementation of their common operations.
It provides implementation of their common oqperations.
"""
isdir = None

def __init__(self, repo, path, object_id, size=0):
def __init__(self, repo, name, type, id=None, parent_dir=None, size=None):
"""
:param:`path` the full path of this entry within its repo, like
"/documents/example.md"

:param:`repo` repository object
:param:'name' name of file or directory
:param:'type' dir or file
:param:'id' id of object
:param:'parent_dir' path of upstream directory. If there is no upstream dir, parent_dir should be '/'
:param:`size` The size of a file. It should be zero for a dir.
"""
self.client = repo.client
self.repo = repo
self.path = path
self.id = object_id
self.path = '/'+ name
self.id = id if id is not None else ZERO_OBJ_ID
self.name=name
self.parent_dir = parent_dir if parent_dir is not None else '/'
self.type = type
self.size = size
self.full_path = urljoin(self.parent_dir, self.path)

@property
def name(self):
return posixpath.basename(self.path)
def __str__(self):
return f"_SeafDirentBase[{self.type}: {self.name}, path: {self.full_path}]"
__repr__ = __str__

@classmethod
def from_json(cls, repo, dir_json):
dir_json = utf8lize(dir_json)

repo=repo
name = dir_json['name']
id = dir_json['id']
parent_dir = dir_json.get('parent_dir', '/')
type = dir_json['type']
size = dir_json.get('size', 0)

return cls(repo, name, type, id, parent_dir, size)

def list_revisions(self):
pass

def delete(self):
suffix = 'dir' if self.isdir else 'file'
url = '/api2/repos/%s/%s/' % (self.repo.id, suffix) + querystr(p=self.path)
resp = self.client.delete(url)
resp = self.repo.client.delete(url)
return resp

def rename(self, newname):
"""Change file/folder name to newname
"""
Change file/folder name to newname
"""
suffix = 'dir' if self.isdir else 'file'
url = '/api2/repos/%s/%s/' % (self.repo.id, suffix) + querystr(p=self.path, reloaddir='true')
postdata = {'operation': 'rename', 'newname': newname}
resp = self.client.post(url, data=postdata)
resp = self.repo.client.post(url, data=postdata)
succeeded = resp.status_code == 200
if succeeded:
if self.isdir:
new_dirent = self.repo.get_dir(os.path.join(os.path.dirname(self.path), newname))
else:
new_dirent = self.repo.get_file(os.path.join(os.path.dirname(self.path), newname))
for key in list(self.__dict__.keys()):
for key in self.__dict__.keys():
self.__dict__[key] = new_dirent.__dict__[key]
return succeeded

Expand All @@ -69,7 +88,7 @@ def _copy_move_task(self, operation, dirent_type, dst_dir, dst_repo_id=None):
'src_dirent_name': src_dirent_name, 'dst_repo_id': dst_repo_id,
'dst_parent_dir': dst_parent_dir, 'operation': operation,
'dirent_type': dirent_type}
return self.client.post(url, data=postdata)
return self.repo.client.post(url, data=postdata)

def copyTo(self, dst_dir, dst_repo_id=None):
"""Copy file/folder to other directory (also to a different repo)
Expand All @@ -91,24 +110,54 @@ def moveTo(self, dst_dir, dst_repo_id=None):
resp = self._copy_move_task('move', dirent_type, dst_dir, dst_repo_id)
succeeded = resp.status_code == 200
if succeeded:
new_repo = self.client.repos.get_repo(dst_repo_id)
new_repo = self.repo.client.repos.get_repo(dst_repo_id)
dst_path = os.path.join(dst_dir, os.path.basename(self.path))
if self.isdir:
new_dirent = new_repo.get_dir(dst_path)
else:
new_dirent = new_repo.get_file(dst_path)
for key in list(self.__dict__.keys()):
for key in self.__dict__.keys():
self.__dict__[key] = new_dirent.__dict__[key]
return succeeded

def get_share_link(self):
pass
def get_share_link(self, can_edit=False, can_download=True, password=None, expire_days=None, direct_link=True):
url = '/api/v2.1/share-links/'
post_data = {
"repo_id": self.repo.id,
"path": self.path,
"permissions": {
"can_edit": can_edit,
"can_download": can_download
}
}
if password:
post_data['password'] = password
if expire_days:
post_data['expire_days'] = expire_days

resp = self.repo.client.post(url, data=post_data)
link = resp.json()['link']
if direct_link:
link = link + '?dl=1'

return link

def _get_upload_link(self):
"""
get upload link of a file or directory. If object is a file, the file will be uploaded to parent dir.
If object is a directory, upload link will be created for itself
"""
url = '/api2/repos/%s/upload-link/' % self.repo.id
query = '?'+urlencode(dict(p=self.parent_dir if self.type=='file' else self.full_path))
resp = self.repo.client.get(url+query)
return re.match(r'"(.*)"', resp.text).group(1)


class SeafDir(_SeafDirentBase):
isdir = True

def __init__(self, *args, **kwargs):
super(SeafDir, self).__init__(*args, **kwargs)
super().__init__(*args, **kwargs)
self.entries = None
self.entries = kwargs.pop('entries', None)

Expand All @@ -118,18 +167,23 @@ def ls(self, force_refresh=False):
Return a list of objects of class :class:`SeafFile` or :class:`SeafDir`.
"""
if self.entries is None or force_refresh:
self.load_entries()
self.load_entries(type="d")

return self.entries

def share_to_user(self, email, permission):
"""
share dir to other user.
:param: email of other user
:param: permission should be 'r' for read and 'rw' for read and write
"""
url = '/api2/repos/%s/dir/shared_items/' % self.repo.id + querystr(p=self.path)
putdata = {
'share_type': 'user',
'username': email,
'permission': permission
}
resp = self.client.put(url, data=putdata)
resp = self.repo.client.put(url, data=putdata)
return resp.status_code == 200

def create_empty_file(self, name):
Expand All @@ -140,7 +194,7 @@ def create_empty_file(self, name):
path = posixpath.join(self.path, name)
url = '/api2/repos/%s/file/' % self.repo.id + querystr(p=path, reloaddir='true')
postdata = {'operation': 'create'}
resp = self.client.post(url, data=postdata)
resp = self.repo.client.post(url, data=postdata)
self.id = resp.headers['oid']
self.load_entries(resp.json())
return SeafFile(self.repo, path, ZERO_OBJ_ID, 0)
Expand All @@ -153,12 +207,12 @@ def mkdir(self, name):
path = posixpath.join(self.path, name)
url = '/api2/repos/%s/dir/' % self.repo.id + querystr(p=path, reloaddir='true')
postdata = {'operation': 'mkdir'}
resp = self.client.post(url, data=postdata)
resp = self.repo.client.post(url, data=postdata)
self.id = resp.headers['oid']
self.load_entries(resp.json())
return SeafDir(self.repo, path, ZERO_OBJ_ID)

def upload(self, fileobj, filename):
def upload(self, fileobj, filename, replace=False):
"""Upload a file to this folder.

:param:fileobj :class:`File` like object
Expand All @@ -172,11 +226,12 @@ def upload(self, fileobj, filename):
files = {
'file': (filename, fileobj),
'parent_dir': self.path,
'replace': 1 if replace else 0,
}
self.client.post(upload_url, files=files)
self.repo.client.post(upload_url, files=files)
return self.repo.get_file(posixpath.join(self.path, filename))

def upload_local_file(self, filepath, name=None):
def upload_local_file(self, filepath, name=None, replace=False):
"""Upload a file to this folder.

:param:filepath The path to the local file
Expand All @@ -186,12 +241,7 @@ def upload_local_file(self, filepath, name=None):
"""
name = name or os.path.basename(filepath)
with open(filepath, 'r') as fp:
return self.upload(fp, name)

def _get_upload_link(self):
url = '/api2/repos/%s/upload-link/' % self.repo.id
resp = self.client.get(url)
return re.match(r'"(.*)"', resp.text).group(1)
return self.upload(fp, name, replace)

def get_uploadable_sharelink(self):
"""Generate a uploadable shared link to this dir.
Expand All @@ -200,19 +250,26 @@ def get_uploadable_sharelink(self):
"""
pass

def load_entries(self, dirents_json=None):
def load_entries(self, dirents_json=None, type=None):
if dirents_json is None:
url = '/api2/repos/%s/dir/' % self.repo.id + querystr(p=self.path)
dirents_json = self.client.get(url).json()
url = '/api2/repos/%s/dir/' % self.repo.id
# oid: object id-id of upstream dir
# t: type - must be f for file or d for dir
if type is None:
query = '?' + urlencode(dict(oid=self.id, recursive=1))
else:
query = '?' + urlencode(dict(oid=self.id, recursive=1, t=type))
dirents_json = self.repo.client.get(url+query).json()

self.entries = [self._load_dirent(entry_json) for entry_json in dirents_json]

def _load_dirent(self, dirent_json):
dirent_json = utf8lize(dirent_json)
path = posixpath.join(self.path, dirent_json['name'])
if dirent_json['type'] == 'file':
return SeafFile(self.repo, path, dirent_json['id'], dirent_json['size'])
return SeafFile(self.repo, dirent_json['name'],dirent_json['type'], dirent_json['id'], dirent_json['parent_dir'], dirent_json.get('size', 0))
else:
return SeafDir(self.repo, path, dirent_json['id'], 0)
return SeafDir(self.repo, dirent_json['name'],dirent_json['type'], dirent_json['id'], dirent_json['parent_dir'], dirent_json.get('size', 0))

@property
def num_entries(self):
Expand All @@ -238,13 +295,13 @@ def __str__(self):
(self.repo.id[:6], self.path, self.size)

def _get_download_link(self):
url = '/api2/repos/%s/file/' % self.repo.id + querystr(p=self.path)
resp = self.client.get(url)
url = '/api2/repos/%s/file/' % self.repo.id + querystr(p=self.full_path,reuse=1)
resp = self.repo.client.get(url)
return re.match(r'"(.*)"', resp.text).group(1)

def get_content(self):
"""Get the content of the file"""
url = self._get_download_link()
return self.client.get(url).content
return self.repo.client.get(url).content

__repr__ = __str__
51 changes: 47 additions & 4 deletions seafileapi/repo.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
from urllib.parse import urlencode
from seafileapi.files import SeafDir, SeafFile
from seafileapi.utils import raise_does_not_exist
from .utils import utf8lize
from .files import SeafDir, SeafFile
from .utils import raise_does_not_exist

class Repo(object):
"""
Expand All @@ -17,6 +18,7 @@ def __init__(self, client, repo_id, repo_name,

@classmethod
def from_json(cls, client, repo_json):
repo_json = utf8lize(repo_json)

repo_id = repo_json['id']
repo_name = repo_json['name']
Expand All @@ -30,8 +32,9 @@ def is_readonly(self):
return 'w' not in self.perm

@raise_does_not_exist('The requested file does not exist')
def get_file(self, path):
def get_file(self, path, parent_dir=None):
"""Get the file object located in `path` in this repo.
param: parent_dir: path of parent directory. in case None, parent_dir is "/"

Return a :class:`SeafFile` object
"""
Expand All @@ -40,7 +43,14 @@ def get_file(self, path):
query = '?' + urlencode(dict(p=path))
file_json = self.client.get(url + query).json()

return SeafFile(self, path, file_json['id'], file_json['size'])
return SeafFile(
repo=self,
id=file_json['id'],
name=file_json['name'],
type='file',
parent_dir='/' if parent_dir is None else parent_dir,
size=file_json['size']
)

@raise_does_not_exist('The requested dir does not exist')
def get_dir(self, path):
Expand All @@ -58,6 +68,39 @@ def get_dir(self, path):
dir.load_entries(dir_json)
return dir

# @raise_does_not_exist('The requested dir does not exist')
def get_items(self, type=None, recursive = False):
"""Get the SeafDir and SeafFile objects located in this repo.

Return a :class:`SeafDir` object
"""
# assert path.startswith('/')
url = '/api2/repos/%s/dir/' % self.id
query = '?' + urlencode(dict(t=type if type else '', recursive=1 if recursive else 0))
resp = self.client.get(url + query).json()
data = []
for j in resp:
if j['type'] == 'file':
data.append(SeafFile.from_json(repo=self, dir_json=j))
else:
data.append(SeafDir.from_json(repo=self, dir_json=j))
return data

def get_files(self, oid=None, recursive = True):
"""
get all files in a repository.
input: oid - object id. The id of directory in this repo in case we want to find files in a specific dir
recursive - find file recursive
"""
url = '/api2/repos/%s/dir/' % self.id
query = '?' + urlencode(dict(
oid='' if oid is None else oid,
t='f',
recursive=1 if recursive == True else 0))
resp = self.client.get(url + query).json()

return [SeafFile.from_json(repo=self, dir_json=j) for j in resp]

def delete(self):
"""Remove this repo. Only the repo owner can do this"""
self.client.delete('/api2/repos/' + self.id)
Expand Down