Fixed display of database color

Closes #2139, closes #2119
This commit is contained in:
Simon Willison 2023-08-10 22:16:19 -07:00
commit 4535568f2c
11 changed files with 39 additions and 18 deletions

View file

@ -1,6 +1,7 @@
import asyncio import asyncio
from collections import namedtuple from collections import namedtuple
from pathlib import Path from pathlib import Path
import hashlib
import janus import janus
import queue import queue
import sys import sys
@ -62,6 +63,12 @@ class Database:
} }
return self._cached_table_counts return self._cached_table_counts
@property
def color(self):
if self.hash:
return self.hash[:6]
return hashlib.md5(self.name.encode("utf8")).hexdigest()[:6]
def suggest_name(self): def suggest_name(self):
if self.path: if self.path:
return Path(self.path).stem return Path(self.path).stem

View file

@ -10,7 +10,7 @@
{% block body_class %}db db-{{ database|to_css_class }}{% endblock %} {% block body_class %}db db-{{ database|to_css_class }}{% endblock %}
{% block content %} {% block content %}
<div class="page-header" style="border-color: #{{ database_color(database) }}"> <div class="page-header" style="border-color: #{{ database_color }}">
<h1>{{ metadata.title or database }}{% if private %} 🔒{% endif %}</h1> <h1>{{ metadata.title or database }}{% if private %} 🔒{% endif %}</h1>
{% set links = database_actions() %}{% if links %} {% set links = database_actions() %}{% if links %}
<details class="actions-menu-links details-menu"> <details class="actions-menu-links details-menu">

View file

@ -28,7 +28,7 @@
<p class="message-error">This query cannot be executed because the database is immutable.</p> <p class="message-error">This query cannot be executed because the database is immutable.</p>
{% endif %} {% endif %}
<h1 style="padding-left: 10px; border-left: 10px solid #{{ database_color(database) }}">{{ metadata.title or database }}{% if canned_query and not metadata.title %}: {{ canned_query }}{% endif %}{% if private %} 🔒{% endif %}</h1> <h1 style="padding-left: 10px; border-left: 10px solid #{{ database_color }}">{{ metadata.title or database }}{% if canned_query and not metadata.title %}: {{ canned_query }}{% endif %}{% if private %} 🔒{% endif %}</h1>
{% block description_source_license %}{% include "_description_source_license.html" %}{% endblock %} {% block description_source_license %}{% include "_description_source_license.html" %}{% endblock %}

View file

@ -20,7 +20,7 @@
{% endblock %} {% endblock %}
{% block content %} {% block content %}
<h1 style="padding-left: 10px; border-left: 10px solid #{{ database_color(database) }}">{{ table }}: {{ ', '.join(primary_key_values) }}{% if private %} 🔒{% endif %}</h1> <h1 style="padding-left: 10px; border-left: 10px solid #{{ database_color }}">{{ table }}: {{ ', '.join(primary_key_values) }}{% if private %} 🔒{% endif %}</h1>
{% block description_source_license %}{% include "_description_source_license.html" %}{% endblock %} {% block description_source_license %}{% include "_description_source_license.html" %}{% endblock %}

View file

@ -21,7 +21,7 @@
{% endblock %} {% endblock %}
{% block content %} {% block content %}
<div class="page-header" style="border-color: #{{ database_color(database) }}"> <div class="page-header" style="border-color: #{{ database_color }}">
<h1>{{ metadata.get("title") or table }}{% if is_view %} (view){% endif %}{% if private %} 🔒{% endif %}</h1> <h1>{{ metadata.get("title") or table }}{% if is_view %} (view){% endif %}{% if private %} 🔒{% endif %}</h1>
{% set links = table_actions() %}{% if links %} {% set links = table_actions() %}{% if links %}
<details class="actions-menu-links details-menu"> <details class="actions-menu-links details-menu">

View file

@ -102,9 +102,6 @@ class BaseView:
response.body = b"" response.body = b""
return response return response
def database_color(self, database):
return "ff0000"
async def method_not_allowed(self, request): async def method_not_allowed(self, request):
if ( if (
request.path.endswith(".json") request.path.endswith(".json")
@ -150,7 +147,6 @@ class BaseView:
template_context = { template_context = {
**context, **context,
**{ **{
"database_color": self.database_color,
"select_templates": [ "select_templates": [
f"{'*' if template_name == template.name else ''}{template_name}" f"{'*' if template_name == template.name else ''}{template_name}"
for template_name in templates for template_name in templates

View file

@ -146,6 +146,7 @@ class DatabaseView(View):
template = datasette.jinja_env.select_template(templates) template = datasette.jinja_env.select_template(templates)
context = { context = {
**json_data, **json_data,
"database_color": db.color,
"database_actions": database_actions, "database_actions": database_actions,
"show_hidden": request.args.get("_show_hidden"), "show_hidden": request.args.get("_show_hidden"),
"editable": True, "editable": True,
@ -154,7 +155,6 @@ class DatabaseView(View):
and not db.is_mutable and not db.is_mutable
and not db.is_memory, and not db.is_memory,
"attached_databases": attached_databases, "attached_databases": attached_databases,
"database_color": lambda _: "#ff0000",
"alternate_url_json": alternate_url_json, "alternate_url_json": alternate_url_json,
"select_templates": [ "select_templates": [
f"{'*' if template_name == template.name else ''}{template_name}" f"{'*' if template_name == template.name else ''}{template_name}"
@ -179,6 +179,7 @@ class DatabaseView(View):
@dataclass @dataclass
class QueryContext: class QueryContext:
database: str = field(metadata={"help": "The name of the database being queried"}) database: str = field(metadata={"help": "The name of the database being queried"})
database_color: str = field(metadata={"help": "The color of the database"})
query: dict = field( query: dict = field(
metadata={"help": "The SQL query object containing the `sql` string"} metadata={"help": "The SQL query object containing the `sql` string"}
) )
@ -232,9 +233,6 @@ class QueryContext:
show_hide_hidden: str = field( show_hide_hidden: str = field(
metadata={"help": "Hidden input field for the _show_sql parameter"} metadata={"help": "Hidden input field for the _show_sql parameter"}
) )
database_color: Callable = field(
metadata={"help": "Function that returns a color for a given database name"}
)
table_columns: dict = field( table_columns: dict = field(
metadata={"help": "Dictionary of table name to list of column names"} metadata={"help": "Dictionary of table name to list of column names"}
) )
@ -689,6 +687,7 @@ class QueryView(View):
template, template,
QueryContext( QueryContext(
database=database, database=database,
database_color=db.color,
query={ query={
"sql": sql, "sql": sql,
"params": params, "params": params,
@ -721,7 +720,6 @@ class QueryView(View):
), ),
show_hide_hidden=markupsafe.Markup(show_hide_hidden), show_hide_hidden=markupsafe.Markup(show_hide_hidden),
metadata=canned_query or metadata, metadata=canned_query or metadata,
database_color=lambda _: "#ff0000",
alternate_url_json=alternate_url_json, alternate_url_json=alternate_url_json,
select_templates=[ select_templates=[
f"{'*' if template_name == template.name else ''}{template_name}" f"{'*' if template_name == template.name else ''}{template_name}"

View file

@ -105,9 +105,7 @@ class IndexView(BaseView):
{ {
"name": name, "name": name,
"hash": db.hash, "hash": db.hash,
"color": db.hash[:6] "color": db.color,
if db.hash
else hashlib.md5(name.encode("utf8")).hexdigest()[:6],
"path": self.ds.urls.database(name), "path": self.ds.urls.database(name),
"tables_and_views_truncated": tables_and_views_truncated, "tables_and_views_truncated": tables_and_views_truncated,
"tables_and_views_more": (len(visible_tables) + len(views)) "tables_and_views_more": (len(visible_tables) + len(views))

View file

@ -18,7 +18,8 @@ class RowView(DataView):
async def data(self, request, default_labels=False): async def data(self, request, default_labels=False):
resolved = await self.ds.resolve_row(request) resolved = await self.ds.resolve_row(request)
database = resolved.db.name db = resolved.db
database = db.name
table = resolved.table table = resolved.table
pk_values = resolved.pk_values pk_values = resolved.pk_values
@ -60,6 +61,7 @@ class RowView(DataView):
"foreign_key_tables": await self.foreign_key_tables( "foreign_key_tables": await self.foreign_key_tables(
database, table, pk_values database, table, pk_values
), ),
"database_color": db.color,
"display_columns": display_columns, "display_columns": display_columns,
"display_rows": display_rows, "display_rows": display_rows,
"custom_table_templates": [ "custom_table_templates": [

View file

@ -1408,7 +1408,7 @@ async def table_view_data(
return table_name return table_name
async def extra_database_color(): async def extra_database_color():
return lambda _: "ff0000" return db.color
async def extra_form_hidden_args(): async def extra_form_hidden_args():
form_hidden_args = [] form_hidden_args = []

View file

@ -1103,3 +1103,23 @@ async def test_breadcrumbs_respect_permissions(
assert actual == expected_links assert actual == expected_links
finally: finally:
ds_client.ds._metadata_local = orig ds_client.ds._metadata_local = orig
@pytest.mark.asyncio
async def test_database_color(ds_client):
expected_color = ds_client.ds.get_database("fixtures").color
# Should be something like #9403e5
expected_fragments = (
"10px solid #{}".format(expected_color),
"border-color: #{}".format(expected_color),
)
assert len(expected_color) == 6
for path in (
"/",
"/fixtures",
"/fixtures/facetable",
"/fixtures/paginated_view",
"/fixtures/pragma_cache_size",
):
response = await ds_client.get(path)
assert any(fragment in response.text for fragment in expected_fragments)