simonw / datasette

@@ -1,6 +1,6 @@
Loading
1 1
import asyncio
2 2
from contextlib import contextmanager
3 -
from collections import OrderedDict
3 +
from collections import OrderedDict, namedtuple
4 4
import base64
5 5
import click
6 6
import hashlib
@@ -54,6 +54,11 @@
Loading
54 54
ENV SQLITE_EXTENSIONS /usr/lib/x86_64-linux-gnu/mod_spatialite.so
55 55
"""
56 56
57 +
# Can replace this with Column from sqlite_utils when I add that dependency
58 +
Column = namedtuple(
59 +
    "Column", ("cid", "name", "type", "notnull", "default_value", "is_pk")
60 +
)
61 +
57 62
58 63
async def await_me_maybe(value):
59 64
    if callable(value):
@@ -525,8 +530,12 @@
Loading
525 530
526 531
527 532
def table_columns(conn, table):
533 +
    return [column.name for column in table_column_details(conn, table)]
534 +
535 +
536 +
def table_column_details(conn, table):
528 537
    return [
529 -
        r[1]
538 +
        Column(*r)
530 539
        for r in conn.execute(
531 540
            "PRAGMA table_info({});".format(escape_sqlite(table))
532 541
        ).fetchall()

@@ -90,19 +90,31 @@
Loading
90 90
        "Returns columns, rows for specified table - including fancy foreign key treatment"
91 91
        db = self.ds.databases[database]
92 92
        table_metadata = self.ds.table_metadata(database, table)
93 +
        column_details = {col.name: col for col in await db.table_column_details(table)}
93 94
        sortable_columns = await self.sortable_columns_for_table(database, table, True)
94 95
        pks = await db.primary_keys(table)
95 96
        pks_for_display = pks
96 97
        if not pks_for_display:
97 98
            pks_for_display = ["rowid"]
98 -
        columns = [
99 -
            {
100 -
                "name": r[0],
101 -
                "sortable": r[0] in sortable_columns,
102 -
                "is_pk": r[0] in pks_for_display,
103 -
            }
104 -
            for r in description
105 -
        ]
99 +
100 +
        columns = []
101 +
        for r in description:
102 +
            if r[0] == "rowid" and "rowid" not in column_details:
103 +
                type_ = "integer"
104 +
                notnull = 0
105 +
            else:
106 +
                type_ = column_details[r[0]].type
107 +
                notnull = column_details[r[0]].notnull
108 +
            columns.append(
109 +
                {
110 +
                    "name": r[0],
111 +
                    "sortable": r[0] in sortable_columns,
112 +
                    "is_pk": r[0] in pks_for_display,
113 +
                    "type": type_,
114 +
                    "notnull": notnull,
115 +
                }
116 +
            )
117 +
106 118
        column_to_foreign_key_table = {
107 119
            fk["column"]: fk["other_table"]
108 120
            for fk in await db.foreign_keys_for_table(table)
@@ -217,12 +229,25 @@
Loading
217 229
            # Add the link column header.
218 230
            # If it's a simple primary key, we have to remove and re-add that column name at
219 231
            # the beginning of the header row.
232 +
            first_column = None
220 233
            if len(pks) == 1:
221 234
                columns = [col for col in columns if col["name"] != pks[0]]
222 -
223 -
            columns = [
224 -
                {"name": pks[0] if len(pks) == 1 else "Link", "sortable": len(pks) == 1}
225 -
            ] + columns
235 +
                first_column = {
236 +
                    "name": pks[0],
237 +
                    "sortable": len(pks) == 1,
238 +
                    "is_pk": True,
239 +
                    "type": column_details[pks[0]].type,
240 +
                    "notnull": column_details[pks[0]].notnull,
241 +
                }
242 +
            else:
243 +
                first_column = {
244 +
                    "name": "Link",
245 +
                    "sortable": False,
246 +
                    "is_pk": False,
247 +
                    "type": "",
248 +
                    "notnull": 0,
249 +
                }
250 +
            columns = [first_column] + columns
226 251
        return columns, cell_rows
227 252
228 253
@@ -291,7 +316,8 @@
Loading
291 316
        )
292 317
293 318
        pks = await db.primary_keys(table)
294 -
        table_columns = await db.table_columns(table)
319 +
        table_column_details = await db.table_column_details(table)
320 +
        table_columns = [column.name for column in table_column_details]
295 321
296 322
        select_columns = ", ".join(escape_sqlite(t) for t in table_columns)
297 323
Files Coverage
datasette 84.35%
Project Totals (28 files) 84.35%
1
coverage:
2
  status:
3
    project:
4
      default:
5
        informational: true
6
    patch:
7
      default:
8
        informational: true
Sunburst
The inner-most circle is the entire project, moving away from the center are folders then, finally, a single file. The size and color of each slice is representing the number of statements and the coverage, respectively.
Icicle
The top section represents the entire project. Proceeding with folders and finally individual files. The size and color of each slice is representing the number of statements and the coverage, respectively.
Grid
Each block represents a single file in the project. The size and color of each block is represented by the number of statements and the coverage, respectively.
Loading