filesystem.py 11.6 KB
Newer Older
Marcel's avatar
Marcel committed
1
# -*- coding: utf-8 -*-
2

Marcel's avatar
Marcel committed
3
# imports
4
import os
5
import re
6
7
8
9
10
from datetime import datetime
import locale
import shutil
from mimetypes import guess_type
from zipfile import ZipFile
Marcel's avatar
Marcel committed
11

12
13
14
15
16
17
18
19
20
21
22
23

class FileSystem(object):

    FILE_EXTENSIONS = ["png", "jpg", "jpeg", "bmp", "ps", "eps", "pdf", "txt", "xml", "py", "c", "cpp", "root", "pxlio"]
    BROWSER_EXTENSIONS = ["png", "jpg", "jpeg", "bmp", "pdf"]
    ADDITIONAL_MIMES = {"pxlio": "text/plain",
                        "root" : "text/plain"}

    def __init__(self):
        # allowed extensions
        self.allowed_extensions = FileSystem.FILE_EXTENSIONS

24
25
26
    def setup(self, basedir=None):
        if basedir==None:
            basedir = os.path.expanduser("~")
murban's avatar
murban committed
27
28
29
30
        if not os.path.isdir(basedir):
            raise Exception("Basedir ("+str(basedir)+") does not exist!")
        # the basedir
        self.basedir = os.path.join(basedir, ".vispa")
31
32
33
34
35
        if os.path.isdir(self.basedir):
            return "Basedir already exists"
        else: 
            os.makedirs(self.basedir, 0700)
            return "Basedir now exists"
36

murban's avatar
murban committed
37
    def get_mime_type(self, filepath):
38
39
40
41
42
43
44
45
        mime, encoding = guess_type(filepath)
        if mime is not None:
            return mime
        ext = filepath.split(".")[-1]
        if ext is not None and ext != "" and ext.lower() in FileSystem.ADDITIONAL_MIMES.keys():
            return FileSystem.ADDITIONAL_MIMES[ext]
        return None

murban's avatar
murban committed
46
    def check_file_extension(self, path, extensions=[]):
47
48
49
50
51
52
53
54
        if (len(extensions) == 0):
            return True
        for elem in extensions:
            elem = elem if elem.startswith(".") else "." + elem
            if path.lower().endswith(elem.lower()):
                return True
        return False

Marcel's avatar
Marcel committed
55
56
57
    def exists(self, path, type=None):
        # type may be 'f' or 'd'
        if path:
58
            path = os.path.expanduser(os.path.expandvars(path))
59
60
        # path exists physically?
        if not os.path.exists(path):
Marcel's avatar
Marcel committed
61
62
            return None
        # type correct?
63
64
65
66
67
68
69
        target_type = 'd' if os.path.isdir(path) else 'f'
        if not type:
            return target_type
        type = type.lower()
        if type not in ['f', 'd']:
            return None
        return target_type if target_type == type else None
70

71
    def get_file_list(self, path, deep=False, filter=[], reverse=False, hide_hidden=True):
72
        filelist = []
73
74
        path_expand = os.path.expanduser(os.path.expandvars(path))
        for elem in os.listdir(path_expand):
75
76
77
78
79
80
81
82
83
84
            # hide hidden files?
            if elem.startswith('.') and hide_hidden:
                continue

            # excluded by filters?
            match = False
            for filter_elem in filter:
                if re.search(filter_elem, elem):
                    match = True
            if match != reverse:
85
                continue
86

87
            fullpath = os.path.join(path_expand, elem)
88
89
90

            # get locales, mtime, etc
            locale.setlocale(locale.LC_ALL, '')
91
            stats = os.stat(path_expand)
92
93
94
            size = locale.format('%d', stats.st_size, grouping=True)
            mtime = datetime.fromtimestamp(stats.st_mtime).strftime('%Y-%m-%d %H:%M:%S')

95
            if os.path.isdir(fullpath):
96
97
98
                filelist.append({'name': elem, 'type': 'd', 'parent': path, 'extension': '', 'mtime': mtime, 'size': size, 'path': fullpath})
                if deep:
                    filelist.extend(self.get_file_list(fullpath, deep, filter, reverse))
99
            else:
100
101
                extension = elem.split('.')[-1]
                filelist.append({'name': elem, 'type': 'f', 'parent': path, 'extension': extension, 'mtime': mtime, 'size': size, 'path': fullpath})
102
103
104
105
106

            # Determine the parent
            parentpath = path_expand[:-1] if path_expand.endswith(os.sep) and path_expand!=os.sep else path_expand
            parentpath = os.path.dirname(path_expand)
        return {'filelist': filelist, 'parentpath': parentpath}
107

Marcel's avatar
Marcel committed
108
109
110
111
    def get_suggestions(self, path, length=1, append_hidden=True):
        suggestions = []
        source, filter = None, None
        # does the path exist?
Marcel's avatar
Marcel committed
112
        if os.path.exists(os.path.expanduser(os.path.expandvars(path))):
Marcel's avatar
Marcel committed
113
            # dir case
Marcel's avatar
Marcel committed
114
            if os.path.isdir(os.path.expanduser(os.path.expandvars(path))):
Marcel's avatar
Marcel committed
115
116
117
118
119
120
121
122
123
124
125
                if path.endswith('/'):
                    source = path
                else:
                    suggestions.append(path + os.sep)
                    return suggestions
            # file case
            else:
                return suggestions
        else:
            # try to estimate source and filter
            head, tail = os.path.split(path)
Marcel's avatar
Marcel committed
126
            if os.path.isdir(os.path.expanduser(os.path.expandvars(head))):
Marcel's avatar
Marcel committed
127
128
129
130
131
132
133
                source = head
                filter = tail

        # return empty suggestions when source is not set
        if not source:
            return suggestions

Marcel's avatar
Marcel committed
134
        files = os.listdir(os.path.expanduser(os.path.expandvars(source)))
Marcel's avatar
Marcel committed
135
136
137
        # resort?
        if append_hidden:
            files = sorted(files, cmp=file_compare, key=str.lower)
138
        while (len(suggestions) < length or length == 0) and len(files):
Marcel's avatar
Marcel committed
139
140
141
142
            file = files.pop(0)
            if filter and not file.startswith(filter):
                continue
            suggestion = os.path.join(source, file)
Marcel's avatar
Marcel committed
143
            if not suggestion.endswith('/') and os.path.isdir(os.path.expanduser(os.path.expandvars(suggestion))):
Marcel's avatar
Marcel committed
144
145
146
147
                suggestion += '/'
            suggestions.append(suggestion)

        return suggestions
148

murban's avatar
murban committed
149
    def cut_slashs(self, path):
murban's avatar
murban committed
150
        path = path[1:] if path.startswith(os.sep) else path
151
152
        if path == "":
            return path
murban's avatar
murban committed
153
        path = path[:-1] if path.endswith(os.sep) else path
154
155
        return path

murban's avatar
murban committed
156
    def create_folder(self, path, name):
157
158
159
160
161
162
163
164
165
166
        # folder with the same name existent?
        fullpath = os.path.join(path, name)
        if os.path.isdir(fullpath):
            raise Exception("Name already in use!")
        try:
            os.mkdir(fullpath)
        except Exception as e:
            #raise Exception("You don't have the permission to create this folder!")
            raise Exception(str(e))

murban's avatar
murban committed
167
    def create_file(self, path, name):
168
169
170
171
172
173
174
175
176
177
178
        # file with the same name existent?
        fullpath = os.path.join(path, name)
        if os.path.exists(fullpath):
            raise Exception("Name already in use!")
        try:
            f = file(fullpath, "w")
            f.close()
        except Exception as e:
            raise Exception(str(e))
        

murban's avatar
murban committed
179
    def rename_folder(self, path, name):
180
181
182
183
184
        # file or folder
        if not os.path.isdir(path):
            raise Exception("Renaming file with folder function!")

        # folder with the same name existent?
murban's avatar
murban committed
185
186
        path = path if not path.endswith(os.sep) else path[:-1]
        fullpath = os.path.join(os.sep.join(path.split(os.sep)[:-1]), name)
187
188
189
190
191
        if os.path.exists(fullpath):
            raise Exception("Name already in use!")

        os.renames(path, fullpath)

murban's avatar
murban committed
192
    def rename_file(self, path, name):
193
194
195
196
197
        # file or folder
        if os.path.isdir(path):
            raise Exception("Renaming folder with file function!")

        # file with the same name existent?
murban's avatar
murban committed
198
        fullpath = os.path.join(os.sep.join(path.split(os.sep)[:-1]), name)
199
200
201
202
203
        if os.path.exists(fullpath):
            raise Exception("Name already in use!")

        os.renames(path, fullpath)

murban's avatar
murban committed
204
205
    def remove(self, path):
        if isinstance(path, list):
206
207
208
209
210
211
212
213
214
215
216
217
218
            for p in path:
                self.remove(p)
            return True

        if os.path.isdir(path):
            shutil.rmtree(path)
        else:
            os.remove(path)

    def compress(self, path, paths, name):
        # paths has to be a list of strings
        paths = paths if isinstance(paths, list) else [paths]

murban's avatar
murban committed
219
        path = path if not path.endswith(os.sep) else path[:-1]
220
221
222
223
224
225
226
227

        fullpath = os.path.join(path, "%s.zip" % name)

        if os.path.exists(fullpath):
            raise Exception("Name already in use!")

        archive = ZipFile(fullpath, "w")

murban's avatar
murban committed
228
        path = path if path.startswith(os.sep) else path
229
230
231
        for p in paths:
            if p is None or p == "":
                continue
murban's avatar
murban committed
232
            p = p if p.startswith(os.sep) else os.sep + path
233
234
235
236
237
238
239
240
241
242
243
244
245
246
            if os.path.isdir(p):
                for elem in os.listdir(p):
                    fullp = os.path.join(p, elem)
                    if os.path.isdir(fullp):
                        paths.append(fullp)
                    else:
                        ap = fullp[len(path):] if fullp.startswith(path) else fullp
                        archive.write(fullpp, ap)

            ap = p[len(path):] if p.startswith(path) else p
            archive.write(p, ap)

        archive.close()

murban's avatar
murban committed
247
248
249
250
    def paste(self, path, target, cut):
        if isinstance(target, list):
            for p in target:
                self.paste(path, p, cut)
251
252
            return True

murban's avatar
murban committed
253
        fulltarget = os.path.join(path, target.split(os.sep)[-1])
254
255
256
257

        if os.path.exists(fulltarget):
            raise Exception("Name already in use!")

murban's avatar
murban committed
258
259
        if os.path.isdir(target):
            shutil.copytree(target, fulltarget)
260
            if cut:
murban's avatar
murban committed
261
                shutil.rmtree(target)
262
        else:
murban's avatar
murban committed
263
            shutil.copy2(target, path)
264
            if cut:
murban's avatar
murban committed
265
                os.remove(target)
266

murban's avatar
murban committed
267
    def save_file_content(self, path, content, force=True):
Marcel's avatar
Marcel committed
268
269
        path = os.path.expandvars(os.path.expanduser(path))
        # check if file already exists
murban's avatar
murban committed
270
        if os.path.exists(path) and not force:
Marcel's avatar
Marcel committed
271
272
273
            return False
        out = open(path, "wb")
        out.write(content)
274
        out.close()
Marcel's avatar
Marcel committed
275
        return True
276

murban's avatar
murban committed
277
    def get_file_content(self, path):
Marcel's avatar
Marcel committed
278
        path = os.path.expandvars(os.path.expanduser(path))
murban's avatar
murban committed
279
        f = open(path, "r")
280
281
        content = f.read()
        f.close()
Marcel's avatar
Marcel committed
282
        return content
283

Marcel's avatar
Marcel committed
284
285
286
287
    def get_mtime(self, path):
        path = os.path.expandvars(os.path.expanduser(path))
        return os.path.getmtime(path)

murban's avatar
murban committed
288
    def is_browser_file(self, path):
289
        extension = path.split(".")[-1]
murban's avatar
murban committed
290
        return extension in FileSystem.BROWSER_EXTENSIONS
291

murban's avatar
murban committed
292
    def handle_file_name_collision(self, name, path):
293
        # collision?
murban's avatar
murban committed
294
        files = os.listdir(path)
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
        if name not in files:
            return name

        # when this line is reached, there is a collision!

        # cut the file extension
        extension = name.split(".")[-1]
        prename = None
        if name == extension:
            extension = ""
            prename = name
        else:
            prename = name.split("." + extension)[0]

        # has the name already a counter at its end?
        hasCounter = False
        preprename = None
        counter = prename.split("_")[-1]

        if counter != prename:
            try:
                counter = int(counter)
                hasCounter = True
                preprename = "_".join(prename.split("_")[:-1])
            except:
                pass

        if hasCounter:
            # increment and try again
            counter += 1
            newname = "%s_%d%s" % (preprename, counter, "" if extension == "" else "." + extension)
        else:
            newname = "%s_1%s" % (prename, "" if extension == "" else "." + extension)

        # return
murban's avatar
murban committed
330
        return self.handle_file_name_collision(newname, path)
Marcel's avatar
Marcel committed
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349


def string_compare(a, b):
    if a == b:
        return 0
    elif a > b:
        return 1
    else:
        return -1

def file_compare(a, b):
    if not a.startswith('.') and not b.startswith('.'):
        return string_compare(a, b)
    elif a.startswith('.') and b.startswith('.'):
        return string_compare(a, b)
    elif a.startswith('.') and not b.startswith('.'):
        return 1
    elif not a.startswith('.') and b.startswith('.'):
        return -1