Merge pull request #3539 from nyu-ossd-s18/filesize

Adding file size to views
This commit is contained in:
Thomas Kluyver 2018-04-19 09:39:56 +02:00 committed by GitHub
commit 58752355b9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 114 additions and 2 deletions

View File

@ -814,6 +814,9 @@ definitions:
type: string type: string
description: Last modified timestamp description: Last modified timestamp
format: dateTime format: dateTime
size:
type: integer
description: "The size of the file or notebook in bytes. If no size is provided, defaults to null."
mimetype: mimetype:
type: string type: string
description: "The mimetype of a file. If content is not null, and type is 'file', this will contain the mimetype of the file, otherwise this will be null." description: "The mimetype of a file. If content is not null, and type is 'file', this will contain the mimetype of the file, otherwise this will be null."

View File

@ -245,6 +245,14 @@ class FileContentsManager(FileManagerMixin, ContentsManager):
"""Build the common base of a contents model""" """Build the common base of a contents model"""
os_path = self._get_os_path(path) os_path = self._get_os_path(path)
info = os.lstat(os_path) info = os.lstat(os_path)
try:
# size of file
size = info.st_size
except (ValueError, OSError):
self.log.warning('Unable to get size.')
size = None
try: try:
last_modified = tz.utcfromtimestamp(info.st_mtime) last_modified = tz.utcfromtimestamp(info.st_mtime)
except (ValueError, OSError): except (ValueError, OSError):
@ -270,6 +278,8 @@ class FileContentsManager(FileManagerMixin, ContentsManager):
model['content'] = None model['content'] = None
model['format'] = None model['format'] = None
model['mimetype'] = None model['mimetype'] = None
model['size'] = size
try: try:
model['writable'] = os.access(os_path, os.W_OK) model['writable'] = os.access(os_path, os.W_OK)
except OSError: except OSError:
@ -296,6 +306,7 @@ class FileContentsManager(FileManagerMixin, ContentsManager):
model = self._base_model(path) model = self._base_model(path)
model['type'] = 'directory' model['type'] = 'directory'
model['size'] = None
if content: if content:
model['content'] = contents = [] model['content'] = contents = []
os_dir = self._get_os_path(path) os_dir = self._get_os_path(path)
@ -333,6 +344,7 @@ class FileContentsManager(FileManagerMixin, ContentsManager):
return model return model
def _file_model(self, path, content=True, format=None): def _file_model(self, path, content=True, format=None):
"""Build a model for a file """Build a model for a file
@ -373,13 +385,15 @@ class FileContentsManager(FileManagerMixin, ContentsManager):
""" """
model = self._base_model(path) model = self._base_model(path)
model['type'] = 'notebook' model['type'] = 'notebook'
os_path = self._get_os_path(path)
if content: if content:
os_path = self._get_os_path(path)
nb = self._read_notebook(os_path, as_version=4) nb = self._read_notebook(os_path, as_version=4)
self.mark_trusted_cells(nb, path) self.mark_trusted_cells(nb, path)
model['content'] = nb model['content'] = nb
model['format'] = 'json' model['format'] = 'json'
self.validate_notebook_model(model) self.validate_notebook_model(model)
return model return model
def get(self, path, content=True, type=None, format=None): def get(self, path, content=True, type=None, format=None):

View File

@ -1038,7 +1038,57 @@ define([
return (order == 1) ? 1 : -1; return (order == 1) ? 1 : -1;
} }
}; };
/**
source: https://github.com/sindresorhus/pretty-bytes
The MIT License (MIT)
Copyright (c) Sindre Sorhus <sindresorhus@gmail.com> (sindresorhus.com)
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
**/
var format_filesize = function(num) {
if (num === undefined || num === null)
return;
var UNITS = ['B', 'kB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'];
if (!Number.isFinite(num)) {
console.error("Expected finite number, got ", typeof(num) + ": " + num);
}
var neg = num < 0;
if (neg) {
num = -num;
}
if (num < 1) {
return (neg ? '-' : '') + num + ' B';
}
var exponent = Math.min(Math.floor(Math.log10(num) / 3), UNITS.length - 1);
var numStr = Number((num / Math.pow(1000, exponent)).toPrecision(3));
var unit = UNITS[exponent];
return (neg ? '-' : '') + numStr + ' ' + unit;
}
// javascript stores text as utf16 and string indices use "code units", // javascript stores text as utf16 and string indices use "code units",
// which stores high-codepoint characters as "surrogate pairs", // which stores high-codepoint characters as "surrogate pairs",
@ -1180,6 +1230,7 @@ define([
parse_b64_data_uri: parse_b64_data_uri, parse_b64_data_uri: parse_b64_data_uri,
time: time, time: time,
format_datetime: format_datetime, format_datetime: format_datetime,
format_filesize: format_filesize,
datetime_sort_helper: datetime_sort_helper, datetime_sort_helper: datetime_sort_helper,
dnd_contain_file: dnd_contain_file, dnd_contain_file: dnd_contain_file,
js_idx_to_char_idx: js_idx_to_char_idx, js_idx_to_char_idx: js_idx_to_char_idx,

View File

@ -62,9 +62,34 @@ define([
}); });
} }
function size_sorter(ascending) {
var order = ascending ? 1 : 0;
// directories have file size of undefined
return (function(a, b) {
if (a.size === undefined) {
return (ascending) ? -1 : 1;
}
if (b.size === undefined) {
return (ascending) ? 1 : -1;
}
if (a.size > b.size) {
return (ascending) ? -1 : 1;
}
if (b.size > a.size) {
return (ascending) ? 1 : -1;
}
return 0;
});
}
var sort_functions = { var sort_functions = {
'sort-name': name_sorter, 'sort-name': name_sorter,
'last-modified': modified_sorter 'last-modified': modified_sorter,
'file-size': size_sorter
}; };
var NotebookList = function (selector, options) { var NotebookList = function (selector, options) {
@ -520,6 +545,11 @@ define([
.addClass("item_name") .addClass("item_name")
.appendTo(link); .appendTo(link);
$("<span/>")
.addClass("file_size")
.addClass("pull-right")
.appendTo(item);
$("<span/>") $("<span/>")
.addClass("item_modified") .addClass("item_modified")
.addClass("pull-right") .addClass("pull-right")
@ -835,6 +865,9 @@ define([
// Add in the date that the file was last modified // Add in the date that the file was last modified
item.find(".item_modified").text(utils.format_datetime(model.last_modified)); item.find(".item_modified").text(utils.format_datetime(model.last_modified));
item.find(".item_modified").attr("title", moment(model.last_modified).format("YYYY-MM-DD HH:mm")); item.find(".item_modified").attr("title", moment(model.last_modified).format("YYYY-MM-DD HH:mm"));
var filesize = utils.format_filesize(model.size);
item.find(".file_size").text(filesize || '\xA0');
}; };

View File

@ -167,6 +167,11 @@ ul.breadcrumb {
margin-left: @dashboard_lr_pad; margin-left: @dashboard_lr_pad;
} }
.file_size {
width: 65px;
text-align: right;
}
[dir="rtl"] .item_modified.pull-right{ [dir="rtl"] .item_modified.pull-right{
.pull-left(); .pull-left();
} }

View File

@ -117,6 +117,12 @@ data-server-root="{{server_root}}"
{% endfor %} {% endfor %}
</ul> </ul>
</div> </div>
<div id="file_size" class="pull-right sort_button">
<span class="btn btn-xs btn-default sort-action" id="file-size">
{% trans %}File size{% endtrans %}
<i class="fa"></i>
</span>
</div>
<div id="last_modified" class="pull-right sort_button"> <div id="last_modified" class="pull-right sort_button">
<span class="btn btn-xs btn-default sort-action" id="last-modified"> <span class="btn btn-xs btn-default sort-action" id="last-modified">
{% trans %}Last Modified{% endtrans %} {% trans %}Last Modified{% endtrans %}