diff --git a/datasette/app.py b/datasette/app.py index 7bbdef3e..3a74a5e5 100644 --- a/datasette/app.py +++ b/datasette/app.py @@ -305,6 +305,7 @@ class Datasette: self._inspect[name] = { "hash": inspect_hash(path), "file": str(path), + "size": path.stat().st_size, "views": inspect_views(conn), "tables": inspect_tables(conn, (self.metadata("databases") or {}).get(name, {})) } diff --git a/datasette/static/app.css b/datasette/static/app.css index 2fc16940..f21d3ea4 100644 --- a/datasette/static/app.css +++ b/datasette/static/app.css @@ -286,3 +286,8 @@ a.not-underlined { display: inline-block; box-shadow: 1px 2px 8px 2px rgba(0,0,0,0.08); } + +.download-sqlite em { + font-style: normal; + font-size: 0.8em; +} diff --git a/datasette/templates/database.html b/datasette/templates/database.html index 05acff84..f64d5c90 100644 --- a/datasette/templates/database.html +++ b/datasette/templates/database.html @@ -57,7 +57,7 @@ {% endif %} {% if config.allow_download %} -

Download SQLite DB: {{ database }}.db

+

Download SQLite DB: {{ database }}.db {{ format_bytes(size) }}

{% endif %} {% include "_codemirror_foot.html" %} diff --git a/datasette/utils.py b/datasette/utils.py index b0f74f0d..30fc4231 100644 --- a/datasette/utils.py +++ b/datasette/utils.py @@ -901,3 +901,15 @@ class StaticMount(click.ParamType): if not os.path.exists(dirpath) or not os.path.isdir(dirpath): self.fail("%s is not a valid directory path" % value, param, ctx) return path, dirpath + + +def format_bytes(bytes): + current = float(bytes) + for unit in ("bytes", "KB", "MB", "GB", "TB"): + if current < 1024: + break + current = current / 1024 + if unit == "bytes": + return "{} {}".format(int(current), unit) + else: + return "{:.1f} {}".format(current, unit) diff --git a/datasette/views/base.py b/datasette/views/base.py index f4a8afaf..e98762b7 100644 --- a/datasette/views/base.py +++ b/datasette/views/base.py @@ -19,6 +19,7 @@ from datasette.utils import ( InterruptedError, InvalidSql, LimitedWriter, + format_bytes, is_url, path_from_row_pks, path_with_added_args, @@ -102,6 +103,7 @@ class RenderMixin(HTTPMethodView): "extra_js_urls": self._asset_urls( "extra_js_urls", template, context ), + "format_bytes": format_bytes, } } ) diff --git a/datasette/views/database.py b/datasette/views/database.py index fb6b242b..9c44a800 100644 --- a/datasette/views/database.py +++ b/datasette/views/database.py @@ -24,6 +24,7 @@ class DatabaseView(BaseView): tables.sort(key=lambda t: (t["hidden"], t["name"])) return { "database": database, + "size": info["size"], "tables": tables, "hidden_count": len([t for t in tables if t["hidden"]]), "views": info["views"], diff --git a/tests/test_utils.py b/tests/test_utils.py index 43e2f44b..b406d70b 100644 --- a/tests/test_utils.py +++ b/tests/test_utils.py @@ -374,3 +374,18 @@ def test_path_with_format(path, format, extra_qs, expected): ) actual = utils.path_with_format(request, format, extra_qs) assert expected == actual + + +@pytest.mark.parametrize( + "bytes,expected", + [ + (120, '120 bytes'), + (1024, '1.0 KB'), + (1024 * 1024, '1.0 MB'), + (1024 * 1024 * 1024, '1.0 GB'), + (1024 * 1024 * 1024 * 1.3, '1.3 GB'), + (1024 * 1024 * 1024 * 1024, '1.0 TB'), + ] +) +def test_format_bytes(bytes, expected): + assert expected == utils.format_bytes(bytes)