[{"html_url": "https://github.com/simonw/datasette/issues/2041#issuecomment-1477155602", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/2041", "id": 1477155602, "node_id": "IC_kwDOBm6k_c5YC5sS", "user": {"value": 9599, "label": "simonw"}, "created_at": "2023-03-21T01:17:43Z", "updated_at": "2023-03-21T01:17:43Z", "author_association": "OWNER", "body": "Removed code in https://github.com/simonw/datasette/commit/538ca9d2e2d41308f5b149c934e2bc43727fd77c - it will merge when I land:\r\n- #1999", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1633077183, "label": "Remove obsolete table POST code"}, "performed_via_github_app": null}, {"html_url": "https://github.com/simonw/datasette/issues/2041#issuecomment-1477144853", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/2041", "id": 1477144853, "node_id": "IC_kwDOBm6k_c5YC3EV", "user": {"value": 9599, "label": "simonw"}, "created_at": "2023-03-21T01:02:13Z", "updated_at": "2023-03-21T01:02:13Z", "author_association": "OWNER", "body": "This code here:\r\n\r\nhttps://github.com/simonw/datasette/blob/56b0758a5fbf85d01ff80a40c9b028469d7bb65f/datasette/views/table.py#L117-L169", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1633077183, "label": "Remove obsolete table POST code"}, "performed_via_github_app": null}, {"html_url": "https://github.com/simonw/datasette/pull/1999#issuecomment-1477082852", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1999", "id": 1477082852, "node_id": "IC_kwDOBm6k_c5YCn7k", "user": {"value": 9599, "label": "simonw"}, "created_at": "2023-03-20T23:27:25Z", "updated_at": "2023-03-20T23:27:25Z", "author_association": "OWNER", "body": "Urgh getting CSV to work is going to be _so hard_, because the logic for that currently lives in a huge chunk of code in `BaseView` which depends on the old design of the `data()` method:\r\n\r\nhttps://github.com/simonw/datasette/blob/4bb49848697e40b8b9a1557be42b8e59eac965b3/datasette/views/base.py#L177-L343", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1551694938, "label": "?_extra= support (draft)"}, "performed_via_github_app": null}, {"html_url": "https://github.com/simonw/datasette/pull/1999#issuecomment-1476860334", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1999", "id": 1476860334, "node_id": "IC_kwDOBm6k_c5YBxmu", "user": {"value": 9599, "label": "simonw"}, "created_at": "2023-03-20T20:06:56Z", "updated_at": "2023-03-20T22:09:03Z", "author_association": "OWNER", "body": "`pytest -n 8` locally says:\r\n\r\n```\r\nFAILED tests/test_canned_queries.py::test_canned_query_form_csrf_hidden_field[add_name_specify_id-True] - assert '\\n\\n\\n Error 500\\n details.open = false);\\n});\\n\\n\\n\\n Error 500\\n details.open = false);\\n});\\n' in '\\n\\n\\n Error 500\\n details.open = false);\\...\r\nFAILED tests/test_canned_queries.py::test_insert - KeyError: 'ds_csrftoken'\r\nFAILED tests/test_canned_queries.py::test_insert_error - KeyError: 'ds_csrftoken'\r\nFAILED tests/test_canned_queries.py::test_magic_parameters_csrf_json[False-True] - KeyError: 'ds_csrftoken'\r\nFAILED tests/test_canned_queries.py::test_magic_parameters_csrf_json[True-True] - KeyError: 'ds_csrftoken'\r\nFAILED tests/test_canned_queries.py::test_vary_header - KeyError: 'vary'\r\nFAILED tests/test_csv.py::test_csv_trace - AssertionError: assert 'application/...charset=utf-8' == 'text/html; charset=utf-8'\r\nFAILED tests/test_csv.py::test_table_csv - AssertionError: assert 'application/...charset=utf-8' == 'text/plain; charset=utf-8'\r\nFAILED tests/test_csv.py::test_table_csv_blob_columns - AssertionError: assert 'application/...charset=utf-8' == 'text/plain; charset=utf-8'\r\nFAILED tests/test_csv.py::test_table_csv_download - AssertionError: assert 'application/...charset=utf-8' == 'text/csv; charset=utf-8'\r\nFAILED tests/test_csv.py::test_table_csv_no_header - AssertionError: assert 'application/...charset=utf-8' == 'text/plain; charset=utf-8'\r\nFAILED tests/test_csv.py::test_table_csv_stream - assert 1 == 101\r\nFAILED tests/test_csv.py::test_table_csv_stream_does_not_calculate_counts - AttributeError: 'NoneType' object has no attribute 'text'\r\nFAILED tests/test_csv.py::test_table_csv_stream_does_not_calculate_facets - AttributeError: 'NoneType' object has no attribute 'text'\r\nFAILED tests/test_csv.py::test_table_csv_with_labels - AssertionError: assert 'application/...charset=utf-8' == 'text/plain; charset=utf-8'\r\nFAILED tests/test_csv.py::test_table_csv_with_nullable_labels - AssertionError: assert 'application/...charset=utf-8' == 'text/plain; charset=utf-8'\r\nFAILED tests/test_facets.py::test_array_facet_handle_duplicate_tags - KeyError: 'facet_results'\r\nFAILED tests/test_facets.py::test_conflicting_facet_names_json - AssertionError: assert {'results', 'timed_out'} == {'created', '...gs', 'tags_2'}\r\nFAILED tests/test_facets.py::test_facet_size - KeyError: 'suggested_facets'\r\nFAILED tests/test_facets.py::test_json_array_with_blanks_and_nulls - KeyError: 'suggested_facets'\r\nFAILED tests/test_facets.py::test_other_types_of_facet_in_metadata - assert 'created (date)\\n' in '\\n\\n\\n fixtures: facetable: 15 rows\\n \\n\\n\\n\\n' in '\\n\\n\\n fixtures: simple_primary_key: 5 rows\\n ' in '\\n\\n\\n fixtures: table/with/slashes.csv: 1 row\\n ', ...}\r\nFAILED tests/test_plugins.py::test_hook_register_output_renderer_no_parameters - assert b'Hello' == b'{\"ok\": true... \"n\": null}]}'\r\nFAILED tests/test_plugins.py::test_hook_register_output_renderer_returning_broken_value - assert 200 == 500\r\nFAILED tests/test_plugins.py::test_hook_register_output_renderer_returning_response - assert {'next': None...', ...}, ...]} == {'this_is': 'json'}\r\n=== 39 failed, 1259 passed, 2 skipped, 1 xfailed in 58.07s ===\r\n```", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1551694938, "label": "?_extra= support (draft)"}, "performed_via_github_app": null}, {"html_url": "https://github.com/simonw/datasette/pull/1999#issuecomment-1476898261", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1999", "id": 1476898261, "node_id": "IC_kwDOBm6k_c5YB63V", "user": {"value": 9599, "label": "simonw"}, "created_at": "2023-03-20T20:37:52Z", "updated_at": "2023-03-20T20:37:52Z", "author_association": "OWNER", "body": "Manual testing spotted a bug.\r\n\r\n`/content/repos.json?owner=9599&_facet_array=topics` - does not return a `facet_results` key.\r\n\r\n`/content/repos.json?owner=9599&_facet_array=topics&_facet=owner` does.", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1551694938, "label": "?_extra= support (draft)"}, "performed_via_github_app": null}, {"html_url": "https://github.com/simonw/datasette/pull/1999#issuecomment-1476854645", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1999", "id": 1476854645, "node_id": "IC_kwDOBm6k_c5YBwN1", "user": {"value": 9599, "label": "simonw"}, "created_at": "2023-03-20T20:02:24Z", "updated_at": "2023-03-20T20:02:24Z", "author_association": "OWNER", "body": "```\r\n def test_routes(routes, path, expected_class, expected_matches):\r\n match, view = resolve_routes(routes, path)\r\n if expected_class is None:\r\n assert match is None\r\n else:\r\n> assert view.view_class.__name__ == expected_class\r\nE AttributeError: 'function' object has no attribute 'view_class'\r\n```", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1551694938, "label": "?_extra= support (draft)"}, "performed_via_github_app": null}, {"html_url": "https://github.com/simonw/datasette/pull/1999#issuecomment-1476851525", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1999", "id": 1476851525, "node_id": "IC_kwDOBm6k_c5YBvdF", "user": {"value": 9599, "label": "simonw"}, "created_at": "2023-03-20T19:59:51Z", "updated_at": "2023-03-20T20:01:40Z", "author_association": "OWNER", "body": "Three failures in `test_html.py`:\r\n```\r\nFAILED tests/test_html.py::test_templates_considered[/fixtures/simple_primary_key-table-fixtures-simple_primary_key.html, *table.html] - assert '' in '\\n\\n\\n fixtures: simple_primary_key: 5 rows\\n ' in '\\n\\n\\n fixtures: table/with/slashes.csv: 1 row\\n \\xa0']] == [['\\xa0']]\r\nFAILED tests/test_table_html.py::test_table_html_disable_foreign_key_links_with_labels - assert [['b']] == [['b']]\r\nFAILED tests/test_table_html.py::test_table_html_foreign_key_custom_label_column - assert [['']] == [['']]\r\nFAILED tests/test_table_html.py::test_table_html_filter_form_column_options[/fixtures/infinity-expected_column_options0] - AssertionError: assert ['- column -'...wid', 'value'] == ['- column -']\r\nFAILED tests/test_table_html.py::test_table_html_filter_form_column_options[/fixtures/primary_key_multiple_columns-expected_column_options1] - AssertionError: assert ['- column -'...', 'content2'] == ['- column -']\r\nFAILED tests/test_table_html.py::test_table_html_filter_form_column_options[/fixtures/compound_primary_key-expected_column_options2] - AssertionError: assert ['- column -'...2', 'content'] == ['- column -']\r\nFAILED tests/test_table_html.py::test_table_html_filter_form_still_shows_nocol_columns - AssertionError: assert ['- column -'] == ['- column -'...nulls_2', ...]\r\nFAILED tests/test_table_html.py::test_compound_primary_key_with_foreign_key_references - assert [['']] == [['']]\r\nFAILED tests/test_table_html.py::test_view_html - assert None is not None\r\nFAILED tests/test_table_html.py::test_extra_where_clauses - assert [('_where', \"...'_city_id=1')] == []\r\nFAILED tests/test_table_html.py::test_other_hidden_form_fields[/fixtures/facetable?_size=10-expected_hidden0] - AssertionError: assert [] == [('_size', '10')]\r\nFAILED tests/test_table_html.py::test_other_hidden_form_fields[/fixtures/facetable?_size=10&_ignore=1&_ignore=2-expected_hidden1] - AssertionError: assert [] == [('_size', '1...ignore', '2')]\r\nFAILED tests/test_table_html.py::test_search_and_sort_fields_not_duplicated[/fixtures/searchable?_sort=text2&_where=1-expected_hidden3] - AssertionError: assert [] == [('_where', '1')]\r\nFAILED tests/test_table_html.py::test_binary_data_display_in_table - assert [['\\xa0']] == [['\\xa0']]\r\nFAILED tests/test_table_html.py::test_metadata_sort - AttributeError: 'NoneType' object has no attribute 'string'\r\nFAILED tests/test_table_html.py::test_metadata_sort_desc - AttributeError: 'NoneType' object has no attribute 'string'\r\nFAILED tests/test_table_html.py::test_facet_more_links[5-/fixtures/facetable?_facet=_neighborhood-2-True-/fixtures/facetable?_facet=_neighborhood&_facet_size=max] - assert 0 == 2\r\nFAILED tests/test_table_html.py::test_facet_more_links[5-/fixtures/facetable?_facet=_neighborhood&_facet_size=50-5-True-/fixtures/facetable?_facet=_neighborhood&_facet_size=max] - assert 0 == 5\r\nFAILED tests/test_table_html.py::test_facet_total - assert 500 == 200\r\n```\r\n\r\nDeduped that's 30 tests:\r\n\r\n```\r\nFAILED tests/test_table_html.py::test_add_filter_redirects\r\nFAILED tests/test_table_html.py::test_binary_data_display_in_table\r\nFAILED tests/test_table_html.py::test_compound_primary_key_with_foreign_key_references\r\nFAILED tests/test_table_html.py::test_csv_json_export_links_include_labels_if_foreign_keys\r\nFAILED tests/test_table_html.py::test_empty_search_parameter_gets_removed\r\nFAILED tests/test_table_html.py::test_existing_filter_redirects\r\nFAILED tests/test_table_html.py::test_extra_where_clauses\r\nFAILED tests/test_table_html.py::test_facet_more_links\r\nFAILED tests/test_table_html.py::test_facet_total\r\nFAILED tests/test_table_html.py::test_facets_persist_through_filter_form\r\nFAILED tests/test_table_html.py::test_metadata_sort\r\nFAILED tests/test_table_html.py::test_metadata_sort_desc\r\nFAILED tests/test_table_html.py::test_next_does_not_persist_in_hidden_field\r\nFAILED tests/test_table_html.py::test_other_hidden_form_fields\r\nFAILED tests/test_table_html.py::test_reflected_hidden_form_fields\r\nFAILED tests/test_table_html.py::test_rowid_sortable_no_primary_key\r\nFAILED tests/test_table_html.py::test_search_and_sort_fields_not_duplicated\r\nFAILED tests/test_table_html.py::test_searchable_view_persists_fts_table\r\nFAILED tests/test_table_html.py::test_sort_by_desc_redirects\r\nFAILED tests/test_table_html.py::test_sort_links\r\nFAILED tests/test_table_html.py::test_table_csv_json_export_interface\r\nFAILED tests/test_table_html.py::test_table_html_compound_primary_key\r\nFAILED tests/test_table_html.py::test_table_html_disable_foreign_key_links_with_labels\r\nFAILED tests/test_table_html.py::test_table_html_filter_form_column_options\r\nFAILED tests/test_table_html.py::test_table_html_filter_form_still_shows_nocol_columns\r\nFAILED tests/test_table_html.py::test_table_html_foreign_key_custom_label_column\r\nFAILED tests/test_table_html.py::test_table_html_foreign_key_links\r\nFAILED tests/test_table_html.py::test_table_html_no_primary_key\r\nFAILED tests/test_table_html.py::test_table_html_simple_primary_key\r\nFAILED tests/test_table_html.py::test_view_html\r\n```", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1551694938, "label": "?_extra= support (draft)"}, "performed_via_github_app": null}, {"html_url": "https://github.com/simonw/datasette/pull/1999#issuecomment-1475074025", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1999", "id": 1475074025, "node_id": "IC_kwDOBm6k_c5X69fp", "user": {"value": 9599, "label": "simonw"}, "created_at": "2023-03-19T02:14:28Z", "updated_at": "2023-03-19T02:14:51Z", "author_association": "OWNER", "body": "I had to replicate quite a bit of this logic from `base.py`:\r\n\r\nhttps://github.com/simonw/datasette/blob/56b0758a5fbf85d01ff80a40c9b028469d7bb65f/datasette/views/base.py#L526-L544\r\n\r\nI should refactor this when I move the canned / arbitrary query views away from that base class too.", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1551694938, "label": "?_extra= support (draft)"}, "performed_via_github_app": null}, {"html_url": "https://github.com/simonw/datasette/pull/1999#issuecomment-1475016834", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1999", "id": 1475016834, "node_id": "IC_kwDOBm6k_c5X6viC", "user": {"value": 9599, "label": "simonw"}, "created_at": "2023-03-18T22:30:31Z", "updated_at": "2023-03-18T22:30:31Z", "author_association": "OWNER", "body": "`test_paginate_using_link_header` will be tricky.\r\n\r\nThe reason the tests are failing is that `json_renderer()` attempts to populate the `link` header using `data[\"next_url\"]` - but that's not present unless `?_extra=next_url` has been passed:\r\n\r\nhttps://github.com/simonw/datasette/blob/2f38479dcc81f11a4362f4e28511fa88afc34e61/datasette/renderer.py#L101-L102\r\n\r\nBut I can only rely on `next` being present, not `next_url`.\r\n\r\nI thought I could maybe assemble the `link` header using `next`, by turning that into a `next_url` link - but there's some custom logic which kicks in for pagination against SQL views (offset/limit pagination) to calculate the `next_url` which isn't easily replicable at the `json_renderer()` layer, in the `_next_value_and_url()` utility function:\r\n\r\nhttps://github.com/simonw/datasette/blob/2f38479dcc81f11a4362f4e28511fa88afc34e61/datasette/views/table.py#L2275-L2282\r\n\r\n", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1551694938, "label": "?_extra= support (draft)"}, "performed_via_github_app": null}, {"html_url": "https://github.com/simonw/datasette/pull/1999#issuecomment-1475003292", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1999", "id": 1475003292, "node_id": "IC_kwDOBm6k_c5X6sOc", "user": {"value": 9599, "label": "simonw"}, "created_at": "2023-03-18T21:46:20Z", "updated_at": "2023-03-18T21:46:20Z", "author_association": "OWNER", "body": "Now 25 failures in `test_table_api.py`:\r\n```\r\nFAILED tests/test_table_api.py::test_expand_labels - assert {'2': {'pk': 2, 'created': '2019-01-14 08:00:00', 'planet_int': 1, 'on_earth': 1, 'state': 'CA', '_city_id': 1, '_neighborhood': 'Dogpatch', 'tags': '[\"tag1\", \"tag3\"]', 'complex_array'...\r\nFAILED tests/test_table_api.py::test_expand_label - AssertionError: assert {'1': {'pk': '1', 'foreign_key_with_label': '1', 'foreign_key_with_blank_label': '3', 'foreign_key_with_no_label': '1', 'foreign_key_compound_pk1': 'a', 'foreign_key_co...\r\nFAILED tests/test_table_api.py::test_ttl_parameter[/fixtures/facetable.json-max-age=5] - KeyError: 'Cache-Control'\r\nFAILED tests/test_table_api.py::test_ttl_parameter[/fixtures/facetable.json?_ttl=invalid-max-age=5] - KeyError: 'Cache-Control'\r\nFAILED tests/test_table_api.py::test_ttl_parameter[/fixtures/facetable.json?_ttl=10-max-age=10] - KeyError: 'Cache-Control'\r\nFAILED tests/test_table_api.py::test_ttl_parameter[/fixtures/facetable.json?_ttl=0-no-cache] - KeyError: 'Cache-Control'\r\nFAILED tests/test_table_api.py::test_infinity_returned_as_null - AssertionError: assert [{'rowid': 1, 'value': inf}, {'rowid': 2, 'value': -inf}, {'rowid': 3, 'value': 1.5}] == [{'rowid': 1, 'value': None}, {'rowid': 2, 'value': None}, {'rowid': 3, 'value'...\r\nFAILED tests/test_table_api.py::test_null_and_compound_foreign_keys_are_not_expanded - AssertionError: assert [{'pk': '1', 'foreign_key_with_label': '1', 'foreign_key_with_blank_label': '3', 'foreign_key_with_no_label': '1', 'foreign_key_compound_pk1': 'a', 'foreign_key_compoun...\r\nFAILED tests/test_table_api.py::test_binary_data_in_json[/fixtures/binary_data.json?_shape=array-expected_json0-None] - assert [{'rowid': 1, 'data': \"b'\\\\x15\\\\x1c\\\\x02\\\\xc7\\\\xad\\\\x05\\\\xfe'\"}, {'rowid': 2, 'data': \"b'\\\\x15\\\\x1c\\\\x03\\\\xc7\\\\xad\\\\x05\\\\xfe'\"}, {'rowid': 3, 'data': None}] == [{'rowid': 1, 'data': {'...\r\nFAILED tests/test_table_api.py::test_binary_data_in_json[/fixtures/binary_data.json?_shape=array&_nl=on-None-{\"rowid\": 1, \"data\": {\"$base64\": true, \"encoded\": \"FRwCx60F/g==\"}}\\n{\"rowid\": 2, \"data\": {\"$base64\": true, \"encoded\": \"FRwDx60F/g==\"}}\\n{\"rowid\": 3, \"data\": null}] - assert '{\"ok\": false, \"error\": \"Object of type bytes is not JSON serializable\", \"status\": 500, \"title\": null}' == '{\"rowid\": 1, \"data\": {\"$base64\": true, \"encoded\": \"FRwCx60F/g==\"}}\\n{\"rowid\"...\r\nFAILED tests/test_table_api.py::test_paginate_using_link_header[] - assert 1 == 21\r\nFAILED tests/test_table_api.py::test_paginate_using_link_header[?_shape=arrays] - assert 1 == 21\r\nFAILED tests/test_table_api.py::test_paginate_using_link_header[?_shape=arrayfirst] - assert 400 == 200\r\nFAILED tests/test_table_api.py::test_paginate_using_link_header[?_shape=object] - assert 1 == 21\r\nFAILED tests/test_table_api.py::test_paginate_using_link_header[?_shape=objects] - assert 1 == 21\r\nFAILED tests/test_table_api.py::test_paginate_using_link_header[?_shape=array] - assert 1 == 21\r\nFAILED tests/test_table_api.py::test_paginate_using_link_header[?_shape=array&_nl=on] - assert 1 == 21\r\nFAILED tests/test_table_api.py::test_col_nocol[/fixtures/facetable.json?_col=created-expected_columns0] - KeyError: 'columns'\r\nFAILED tests/test_table_api.py::test_col_nocol[/fixtures/facetable.json?_nocol=created-expected_columns1] - KeyError: 'columns'\r\nFAILED tests/test_table_api.py::test_col_nocol[/fixtures/facetable.json?_col=state&_col=created-expected_columns2] - KeyError: 'columns'\r\nFAILED tests/test_table_api.py::test_col_nocol[/fixtures/facetable.json?_col=state&_col=state-expected_columns3] - KeyError: 'columns'\r\nFAILED tests/test_table_api.py::test_col_nocol[/fixtures/facetable.json?_col=state&_col=created&_nocol=created-expected_columns4] - KeyError: 'columns'\r\nFAILED tests/test_table_api.py::test_col_nocol[/fixtures/facetable.json?_nocol=state&_facet=state-expected_columns5] - KeyError: 'columns'\r\nFAILED tests/test_table_api.py::test_col_nocol[/fixtures/simple_view.json?_nocol=content-expected_columns6] - KeyError: 'columns'\r\nFAILED tests/test_table_api.py::test_col_nocol[/fixtures/simple_view.json?_col=content-expected_columns7] - KeyError: 'columns'\r\n============================================================================= 25 failed, 86 passed, 1 xfailed in 7.18s =============================================================================\r\n```", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1551694938, "label": "?_extra= support (draft)"}, "performed_via_github_app": null}, {"html_url": "https://github.com/simonw/datasette/pull/1999#issuecomment-1474704790", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1999", "id": 1474704790, "node_id": "IC_kwDOBm6k_c5X5jWW", "user": {"value": 9599, "label": "simonw"}, "created_at": "2023-03-18T04:52:59Z", "updated_at": "2023-03-18T04:52:59Z", "author_association": "OWNER", "body": "Here are the next set of tests to get passing:\r\n```\r\n% pytest tests/test_table_api.py\r\n```\r\n```\r\nFAILED tests/test_table_api.py::test_facets[/fixtures/facetable.json?_facet=state&_facet=_city_id-expected_facet_results0] - KeyError: 'name'\r\nFAILED tests/test_table_api.py::test_facets[/fixtures/facetable.json?_facet=state&_facet=_city_id&state=MI-expected_facet_results1] - KeyError: 'name'\r\nFAILED tests/test_table_api.py::test_facets[/fixtures/facetable.json?_facet=planet_int-expected_facet_results2] - KeyError: 'name'\r\nFAILED tests/test_table_api.py::test_facets[/fixtures/facetable.json?_facet=planet_int&planet_int=1-expected_facet_results3] - KeyError: 'name'\r\nFAILED tests/test_table_api.py::test_suggested_facets - KeyError: 'suggested_facets'\r\nFAILED tests/test_table_api.py::test_allow_facet_off - KeyError: 'suggested_facets'\r\nFAILED tests/test_table_api.py::test_suggest_facets_off - KeyError: 'suggested_facets'\r\nFAILED tests/test_table_api.py::test_nofacet[True] - KeyError: 'suggested_facets'\r\nFAILED tests/test_table_api.py::test_nofacet[False] - KeyError: 'suggested_facets'\r\nFAILED tests/test_table_api.py::test_nosuggest[True] - KeyError: 'suggested_facets'\r\nFAILED tests/test_table_api.py::test_nosuggest[False] - KeyError: 'suggested_facets'\r\nFAILED tests/test_table_api.py::test_nocount[True-None] - KeyError: 'count'\r\nFAILED tests/test_table_api.py::test_nocount[False-15] - KeyError: 'count'\r\nFAILED tests/test_table_api.py::test_expand_labels - AssertionError: assert {'13': {'_cit...:00:00', ...}} == {'13': {'_cit...:00:00', ...}}\r\nFAILED tests/test_table_api.py::test_expand_label - AssertionError: assert {'1': {'forei...l': '1', ...}} == {'1': {'forei...': '1'}, ...}}\r\nFAILED tests/test_table_api.py::test_ttl_parameter[/fixtures/facetable.json-max-age=5] - KeyError: 'Cache-Control'\r\nFAILED tests/test_table_api.py::test_ttl_parameter[/fixtures/facetable.json?_ttl=invalid-max-age=5] - KeyError: 'Cache-Control'\r\nFAILED tests/test_table_api.py::test_ttl_parameter[/fixtures/facetable.json?_ttl=10-max-age=10] - KeyError: 'Cache-Control'\r\nFAILED tests/test_table_api.py::test_ttl_parameter[/fixtures/facetable.json?_ttl=0-no-cache] - KeyError: 'Cache-Control'\r\nFAILED tests/test_table_api.py::test_infinity_returned_as_null - AssertionError: assert [{'rowid': 1,...'value': 1.5}] == [{'rowid': 1,...'value': 1.5}]\r\nFAILED tests/test_table_api.py::test_null_and_compound_foreign_keys_are_not_expanded - AssertionError: assert [{'foreign_ke...': None, ...}] == [{'foreign_ke...': None, ...}]\r\nFAILED tests/test_table_api.py::test_binary_data_in_json[/fixtures/binary_data.json?_shape=array-expected_json0-None] - assert [{'data': \"b'..., 'rowid': 3}] == [{'data': {'$..., 'rowid': 3}]\r\nFAILED tests/test_table_api.py::test_binary_data_in_json[/fixtures/binary_data.json?_shape=array&_nl=on-None-{\"rowid\": 1, \"data\": {\"$base64\": true, \"encoded\": \"FRwCx60F/g==\"}}\\n{\"rowid\": 2, \"data\": {\"$base64\": true, \"encoded\": \"FRwDx60F/g==\"}}\\n{\"rowid\": 3, \"data\": null}] - assert '{\"ok\": false...title\": null}' == '{\"rowid\": 1,...\"data\": null}'\r\nFAILED tests/test_table_api.py::test_paginate_using_link_header[] - assert 1 == 21\r\nFAILED tests/test_table_api.py::test_paginate_using_link_header[?_shape=arrays] - assert 1 == 21\r\nFAILED tests/test_table_api.py::test_paginate_using_link_header[?_shape=arrayfirst] - assert 400 == 200\r\nFAILED tests/test_table_api.py::test_paginate_using_link_header[?_shape=object] - assert 1 == 21\r\nFAILED tests/test_table_api.py::test_paginate_using_link_header[?_shape=objects] - assert 1 == 21\r\nFAILED tests/test_table_api.py::test_paginate_using_link_header[?_shape=array] - assert 1 == 21\r\nFAILED tests/test_table_api.py::test_paginate_using_link_header[?_shape=array&_nl=on] - assert 1 == 21\r\nFAILED tests/test_table_api.py::test_col_nocol[/fixtures/facetable.json?_col=created-expected_columns0] - KeyError: 'columns'\r\nFAILED tests/test_table_api.py::test_col_nocol[/fixtures/facetable.json?_nocol=created-expected_columns1] - KeyError: 'columns'\r\nFAILED tests/test_table_api.py::test_col_nocol[/fixtures/facetable.json?_col=state&_col=created-expected_columns2] - KeyError: 'columns'\r\nFAILED tests/test_table_api.py::test_col_nocol[/fixtures/facetable.json?_col=state&_col=state-expected_columns3] - KeyError: 'columns'\r\nFAILED tests/test_table_api.py::test_col_nocol[/fixtures/facetable.json?_col=state&_col=created&_nocol=created-expected_columns4] - KeyError: 'columns'\r\nFAILED tests/test_table_api.py::test_col_nocol[/fixtures/facetable.json?_nocol=state&_facet=state-expected_columns5] - KeyError: 'columns'\r\nFAILED tests/test_table_api.py::test_col_nocol[/fixtures/simple_view.json?_nocol=content-expected_columns6] - KeyError: 'columns'\r\nFAILED tests/test_table_api.py::test_col_nocol[/fixtures/simple_view.json?_col=content-expected_columns7] - KeyError: 'columns'\r\n============================================================================= 38 failed, 73 passed, 1 xfailed in 7.25s =============================================================================\r\n```\r\n", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1551694938, "label": "?_extra= support (draft)"}, "performed_via_github_app": null}, {"html_url": "https://github.com/dogsheep/apple-notes-to-sqlite/issues/8#issuecomment-1468898285", "issue_url": "https://api.github.com/repos/dogsheep/apple-notes-to-sqlite/issues/8", "id": 1468898285, "node_id": "IC_kwDOJHON9s5XjZvt", "user": {"value": 41546558, "label": "RhetTbull"}, "created_at": "2023-03-14T22:00:21Z", "updated_at": "2023-03-14T22:00:21Z", "author_association": "NONE", "body": "Well that's embarrassing. I made a fork using macnotesapp and it's actually slower. This is because the Scripting Bridge sometimes fails to return the folder and thus macnotesapp resorts to AppleScript in this situation. The repeated AppleScript calls on a large library are slower than your \"slurp it all in\" approach. I've got some ideas about how to improve this--will make another attempt if I can fix the issues.", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1617823309, "label": "Increase performance using macnotesapp"}, "performed_via_github_app": null}, {"html_url": "https://github.com/simonw/sqlite-utils/pull/531#issuecomment-1465315726", "issue_url": "https://api.github.com/repos/simonw/sqlite-utils/issues/531", "id": 1465315726, "node_id": "IC_kwDOCGYnMM5XVvGO", "user": {"value": 25778, "label": "eyeseast"}, "created_at": "2023-03-12T22:21:56Z", "updated_at": "2023-03-12T22:21:56Z", "author_association": "CONTRIBUTOR", "body": "Exactly, that's what I was running into. On my M2 MacBook, SpatiaLite ends up in what is -- for the moment -- a non-standard location, so even when I passed in the location with `--load-extension`, I still hit an error on `create-spatial-index`.\r\n\r\nWhat I learned doing this originally is that SQLite needs to load the extension for each connection, even if all the SpatiaLite stuff is already in the database. So that's why `init_spatialite()` gets called again.\r\n\r\nHere's the code where I hit the error: https://github.com/eyeseast/boston-parcels/blob/main/Makefile#L30 It works using this branch.\r\n\r\nI'm not attached to this solution if you can think of something better. And I'm not sure, TBH, my test would actually catch what I'm after here.", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1620164673, "label": "Add paths for homebrew on Apple silicon"}, "performed_via_github_app": null}, {"html_url": "https://github.com/simonw/sqlite-utils/issues/533#issuecomment-1465303378", "issue_url": "https://api.github.com/repos/simonw/sqlite-utils/issues/533", "id": 1465303378, "node_id": "IC_kwDOCGYnMM5XVsFS", "user": {"value": 9599, "label": "simonw"}, "created_at": "2023-03-12T21:24:04Z", "updated_at": "2023-03-12T21:24:04Z", "author_association": "OWNER", "body": "Upgraded to Sphinx 6 locally and got the same error:\r\n\r\n```\r\n% just docs\r\nCogging README.md\r\nCogging docs/changelog.rst\r\nCogging docs/cli-reference.rst\r\nCogging docs/cli.rst\r\nCogging docs/contributing.rst\r\nCogging docs/index.rst\r\nCogging docs/installation.rst\r\nCogging docs/python-api.rst\r\nCogging docs/reference.rst\r\nsphinx-autobuild -a -b html \".\" \"_build\" --watch ../sqlite_utils\r\n[sphinx-autobuild] > sphinx-build -b html -a /Users/simon/Dropbox/Development/sqlite-utils/docs /Users/simon/Dropbox/Development/sqlite-utils/docs/_build\r\nRunning Sphinx v6.1.3\r\nloading pickled environment... failed\r\nfailed: Can't get attribute '_stable_repr_object' on \r\nbuilding [mo]: all of 0 po files\r\nwriting output... \r\nbuilding [html]: all source files\r\nupdating environment: [new config] 8 added, 0 changed, 0 removed\r\nreading sources... [ 12%] changelog \r\nException occurred:\r\n File \"/Users/simon/.local/share/virtualenvs/sqlite-utils-C4Ilevlm/lib/python3.11/site-packages/sphinx/ext/extlinks.py\", line 103, in role\r\n title = caption % part\r\n ~~~~~~~~^~~~~~\r\nTypeError: not all arguments converted during string formatting\r\nThe full traceback has been saved in /var/folders/x6/31xf1vxj0nn9mxqq8z0mmcfw0000gn/T/sphinx-err-1ey36c1n.log, if you want to report the issue to the developers.\r\n```", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1620516340, "label": "ReadTheDocs error: not all arguments converted during string formatting"}, "performed_via_github_app": null}, {"html_url": "https://github.com/simonw/sqlite-utils/issues/533#issuecomment-1465302936", "issue_url": "https://api.github.com/repos/simonw/sqlite-utils/issues/533", "id": 1465302936, "node_id": "IC_kwDOCGYnMM5XVr-Y", "user": {"value": 9599, "label": "simonw"}, "created_at": "2023-03-12T21:22:09Z", "updated_at": "2023-03-12T21:22:09Z", "author_association": "OWNER", "body": "Could be the same problem as:\r\n- https://github.com/simonw/datasette/issues/1972\r\n\r\nWhich I fixed in https://github.com/simonw/datasette/commit/3af313e165215696af899e772f47bf7c27873ae3", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1620516340, "label": "ReadTheDocs error: not all arguments converted during string formatting"}, "performed_via_github_app": null}, {"html_url": "https://github.com/simonw/sqlite-utils/pull/531#issuecomment-1465302343", "issue_url": "https://api.github.com/repos/simonw/sqlite-utils/issues/531", "id": 1465302343, "node_id": "IC_kwDOCGYnMM5XVr1H", "user": {"value": 9599, "label": "simonw"}, "created_at": "2023-03-12T21:19:13Z", "updated_at": "2023-03-12T21:19:13Z", "author_association": "OWNER", "body": "Aah, I think I see why you wrote it like that.\r\n\r\nThe problem is that `init_spatialite()` does other stuff too:\r\n\r\nhttps://github.com/simonw/sqlite-utils/blob/fc221f9b62ed8624b1d2098e564f525c84497969/sqlite_utils/db.py#L1161-L1171\r\n\r\nSo it needs to be able to load the SpatiaLite extension from the correct place, and THEN run `select InitSpatialMetadata()` to configure the database, if needed.\r\n\r\nSo the problem you're trying to solve here is to let people optionally pass in the path to SpatiaLite if it's not one of the ones that are searched by default.\r\n\r\n", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1620164673, "label": "Add paths for homebrew on Apple silicon"}, "performed_via_github_app": null}, {"html_url": "https://github.com/simonw/datasette/issues/236#issuecomment-1465208436", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/236", "id": 1465208436, "node_id": "IC_kwDOBm6k_c5XVU50", "user": {"value": 545193, "label": "sopel"}, "created_at": "2023-03-12T14:04:15Z", "updated_at": "2023-03-12T14:04:15Z", "author_association": "NONE", "body": "I keep coming back to this in search for the related exploration, so I'll just link it now:\r\n\r\n@simonw has meanwhile researched _how to deploy Datasette to AWS Lambda using function URLs and Mangum_ via https://github.com/simonw/public-notes/issues/6 and concluded _that's everything I need to know in order to build a datasette-publish-lambda plugin_.", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 317001500, "label": "datasette publish lambda plugin"}, "performed_via_github_app": null}, {"html_url": "https://github.com/simonw/sqlite-utils/pull/531#issuecomment-1465038901", "issue_url": "https://api.github.com/repos/simonw/sqlite-utils/issues/531", "id": 1465038901, "node_id": "IC_kwDOCGYnMM5XUrg1", "user": {"value": 22429695, "label": "codecov[bot]"}, "created_at": "2023-03-11T22:29:19Z", "updated_at": "2023-03-11T22:29:19Z", "author_association": "NONE", "body": "## [Codecov](https://codecov.io/gh/simonw/sqlite-utils/pull/531?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Simon+Willison) Report\nPatch coverage: **`100.00`**% and project coverage change: **`+0.03`** :tada:\n> Comparison is base [(`fc221f9`)](https://codecov.io/gh/simonw/sqlite-utils/commit/fc221f9b62ed8624b1d2098e564f525c84497969?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Simon+Willison) 96.25% compared to head [(`0e4750d`)](https://codecov.io/gh/simonw/sqlite-utils/pull/531?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Simon+Willison) 96.29%.\n\n
Additional details and impacted files\n\n\n```diff\n@@ Coverage Diff @@\n## main #531 +/- ##\n==========================================\n+ Coverage 96.25% 96.29% +0.03% \n==========================================\n Files 6 6 \n Lines 2671 2671 \n==========================================\n+ Hits 2571 2572 +1 \n+ Misses 100 99 -1 \n```\n\n\n| [Impacted Files](https://codecov.io/gh/simonw/sqlite-utils/pull/531?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Simon+Willison) | Coverage \u0394 | |\n|---|---|---|\n| [sqlite\\_utils/db.py](https://codecov.io/gh/simonw/sqlite-utils/pull/531?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Simon+Willison#diff-c3FsaXRlX3V0aWxzL2RiLnB5) | `97.33% <\u00f8> (\u00f8)` | |\n| [sqlite\\_utils/utils.py](https://codecov.io/gh/simonw/sqlite-utils/pull/531?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Simon+Willison#diff-c3FsaXRlX3V0aWxzL3V0aWxzLnB5) | `95.13% <\u00f8> (\u00f8)` | |\n| [sqlite\\_utils/cli.py](https://codecov.io/gh/simonw/sqlite-utils/pull/531?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Simon+Willison#diff-c3FsaXRlX3V0aWxzL2NsaS5weQ==) | `95.26% <100.00%> (+0.09%)` | :arrow_up: |\n\nHelp us with your feedback. Take ten seconds to tell us [how you rate us](https://about.codecov.io/nps?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Simon+Willison). Have a feature suggestion? [Share it here.](https://app.codecov.io/gh/feedback/?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Simon+Willison)\n\n
\n\n[:umbrella: View full report in Codecov by Sentry](https://codecov.io/gh/simonw/sqlite-utils/pull/531?src=pr&el=continue&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Simon+Willison). \n:loudspeaker: Do you have feedback about the report comment? [Let us know in this issue](https://about.codecov.io/codecov-pr-comment-feedback/?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Simon+Willison).\n", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1620164673, "label": "Add paths for homebrew on Apple silicon"}, "performed_via_github_app": null}, {"html_url": "https://github.com/dogsheep/healthkit-to-sqlite/issues/24#issuecomment-1464796494", "issue_url": "https://api.github.com/repos/dogsheep/healthkit-to-sqlite/issues/24", "id": 1464796494, "node_id": "IC_kwDOC8tyDs5XTwVO", "user": {"value": 956433, "label": "Mjboothaus"}, "created_at": "2023-03-11T02:23:42Z", "updated_at": "2023-03-11T02:23:42Z", "author_association": "NONE", "body": "@simonw - maybe put in some error handling to trap for poorly formed XML (from Apple engineers) so that it suggests that there are problems with export.zip rather than odd looking Python errors :)", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1515883470, "label": "DOC: xml.etree.ElementTree.ParseError due to healthkit version 12 "}, "performed_via_github_app": null}, {"html_url": "https://github.com/dogsheep/healthkit-to-sqlite/issues/24#issuecomment-1464786643", "issue_url": "https://api.github.com/repos/dogsheep/healthkit-to-sqlite/issues/24", "id": 1464786643, "node_id": "IC_kwDOC8tyDs5XTt7T", "user": {"value": 956433, "label": "Mjboothaus"}, "created_at": "2023-03-11T02:01:27Z", "updated_at": "2023-03-11T02:01:27Z", "author_association": "NONE", "body": "Thanks for reporting this and providing a solution -- I was puzzled by this error when I revisited my walking data and experienced this issues. I haven't tried the fix yet.", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1515883470, "label": "DOC: xml.etree.ElementTree.ParseError due to healthkit version 12 "}, "performed_via_github_app": null}, {"html_url": "https://github.com/simonw/datasette/pull/1999#issuecomment-1463113856", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1999", "id": 1463113856, "node_id": "IC_kwDOBm6k_c5XNViA", "user": {"value": 9599, "label": "simonw"}, "created_at": "2023-03-10T02:13:15Z", "updated_at": "2023-03-10T02:13:15Z", "author_association": "OWNER", "body": "Idea for if this change ends up making a bunch of breaking changes to the templates (which I think it should) - I can generate a GitHub diff link between the old and new templates and include that link in the 1.0 upgrade documentation to help people who wrote custom templates see what they might need to change - with minimal effort from myself to document those changes.", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1551694938, "label": "?_extra= support (draft)"}, "performed_via_github_app": null}, {"html_url": "https://github.com/simonw/datasette/issues/2038#issuecomment-1463112173", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/2038", "id": 1463112173, "node_id": "IC_kwDOBm6k_c5XNVHt", "user": {"value": 9599, "label": "simonw"}, "created_at": "2023-03-10T02:11:06Z", "updated_at": "2023-03-10T02:11:06Z", "author_association": "OWNER", "body": "Here's an example of something that would break:\r\n\r\nhttps://github.com/simonw/datasette/blob/56b0758a5fbf85d01ff80a40c9b028469d7bb65f/datasette/templates/database.html#L12-L15\r\n\r\nBecause `metadata` is an empty dictionary sometimes, so `{{ metadata.title or database }}` would raise an error and need to be replaced by `{{ metadata.get(\"title\") or database }}`.", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1618249044, "label": "Consider a `strict_templates` setting"}, "performed_via_github_app": null}, {"html_url": "https://github.com/simonw/datasette/issues/2038#issuecomment-1463110978", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/2038", "id": 1463110978, "node_id": "IC_kwDOBm6k_c5XNU1C", "user": {"value": 9599, "label": "simonw"}, "created_at": "2023-03-10T02:09:41Z", "updated_at": "2023-03-10T02:09:41Z", "author_association": "OWNER", "body": "I'm torn on this. It's useful for me right now for refactoring, but I feel like it should be a permanent thing, not a setting - or it should default to on and people should turn it off, but who would ever do that?", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1618249044, "label": "Consider a `strict_templates` setting"}, "performed_via_github_app": null}, {"html_url": "https://github.com/simonw/datasette/pull/1999#issuecomment-1463024951", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1999", "id": 1463024951, "node_id": "IC_kwDOBm6k_c5XM_03", "user": {"value": 9599, "label": "simonw"}, "created_at": "2023-03-10T00:17:58Z", "updated_at": "2023-03-10T00:17:58Z", "author_association": "OWNER", "body": "Renderers have an impact on three different pages: query results, table page and row page.\r\n\r\nThe row page feature is incomplete though:\r\n\r\nhttps://congress-legislators.datasettes.com/legislators/social_media/A000055\r\n\r\n\"image\"\r\n\r\nWhy is there no `.csv` link there?", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1551694938, "label": "?_extra= support (draft)"}, "performed_via_github_app": null}, {"html_url": "https://github.com/simonw/datasette/pull/1999#issuecomment-1463023674", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1999", "id": 1463023674, "node_id": "IC_kwDOBm6k_c5XM_g6", "user": {"value": 9599, "label": "simonw"}, "created_at": "2023-03-10T00:16:03Z", "updated_at": "2023-03-10T00:16:03Z", "author_association": "OWNER", "body": "I also need to figure out the `renderers` stuff, so I can link to the right URLs for CSV and JSON and other formats:\r\n\r\nhttps://github.com/simonw/datasette/blob/6d07a7da1531cd749844fc6827d9a1e57009b2ea/datasette/views/base.py#L474-L518", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1551694938, "label": "?_extra= support (draft)"}, "performed_via_github_app": null}, {"html_url": "https://github.com/simonw/datasette/pull/1999#issuecomment-1463022397", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1999", "id": 1463022397, "node_id": "IC_kwDOBm6k_c5XM_M9", "user": {"value": 9599, "label": "simonw"}, "created_at": "2023-03-10T00:14:21Z", "updated_at": "2023-03-10T00:14:21Z", "author_association": "OWNER", "body": "Ironically the thing I most need right now is comprehensive documentation of what variables are being passed to the templates!\r\n\r\nOne big challenge is that I need to untangle the template context that happens in `BaseView` - I'm hacking that together at the moment, but I need a real answer for how that should work in a world in which view functions aren't using that base class.\r\n\r\nhttps://github.com/simonw/datasette/blob/56b0758a5fbf85d01ff80a40c9b028469d7bb65f/datasette/views/base.py#L110-L145", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1551694938, "label": "?_extra= support (draft)"}, "performed_via_github_app": null}, {"html_url": "https://github.com/simonw/datasette/pull/1999#issuecomment-1463021383", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1999", "id": 1463021383, "node_id": "IC_kwDOBm6k_c5XM-9H", "user": {"value": 9599, "label": "simonw"}, "created_at": "2023-03-10T00:12:50Z", "updated_at": "2023-03-10T00:12:50Z", "author_association": "OWNER", "body": "Now at 34 failed, 34 passed.", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1551694938, "label": "?_extra= support (draft)"}, "performed_via_github_app": null}, {"html_url": "https://github.com/simonw/datasette/pull/1999#issuecomment-1463005744", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1999", "id": 1463005744, "node_id": "IC_kwDOBm6k_c5XM7Iw", "user": {"value": 9599, "label": "simonw"}, "created_at": "2023-03-09T23:52:15Z", "updated_at": "2023-03-09T23:52:23Z", "author_association": "OWNER", "body": "I need to figure out what to do about `extra_context_from_filters` - which was previously passed straight to the HTML context.\r\n\r\nhttps://github.com/simonw/datasette/blob/11f7feb7a3f7166c71389786880863d60ed3d165/datasette/views/table.py#L406-L422", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1551694938, "label": "?_extra= support (draft)"}, "performed_via_github_app": null}, {"html_url": "https://github.com/simonw/datasette/pull/1999#issuecomment-1462997800", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1999", "id": 1462997800, "node_id": "IC_kwDOBm6k_c5XM5Mo", "user": {"value": 9599, "label": "simonw"}, "created_at": "2023-03-09T23:39:47Z", "updated_at": "2023-03-09T23:39:47Z", "author_association": "OWNER", "body": "Found a neat trick:\r\n```diff\r\ndiff --git a/datasette/app.py b/datasette/app.py\r\nindex 186f192d..40416713 100644\r\n--- a/datasette/app.py\r\n+++ b/datasette/app.py\r\n@@ -23,7 +23,13 @@ from pathlib import Path\r\n \r\n from markupsafe import Markup, escape\r\n from itsdangerous import URLSafeSerializer\r\n-from jinja2 import ChoiceLoader, Environment, FileSystemLoader, PrefixLoader\r\n+from jinja2 import (\r\n+ ChoiceLoader,\r\n+ Environment,\r\n+ FileSystemLoader,\r\n+ PrefixLoader,\r\n+ StrictUndefined,\r\n+)\r\n from jinja2.environment import Template\r\n from jinja2.exceptions import TemplateNotFound\r\n \r\n@@ -394,7 +400,10 @@ class Datasette:\r\n ]\r\n )\r\n self.jinja_env = Environment(\r\n- loader=template_loader, autoescape=True, enable_async=True\r\n+ loader=template_loader,\r\n+ autoescape=True,\r\n+ enable_async=True,\r\n+ undefined=StrictUndefined,\r\n )\r\n self.jinja_env.filters[\"escape_css_string\"] = escape_css_string\r\n self.jinja_env.filters[\"quote_plus\"] = urllib.parse.quote_plus\r\n```\r\nThis causes Jinja to raise a hard error if there are any variables referenced in the template that are not available in the context.\r\n\r\nIt's helping me spot things that are still missing, rather than just relying on failed unit tests.", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1551694938, "label": "?_extra= support (draft)"}, "performed_via_github_app": null}, {"html_url": "https://github.com/dogsheep/apple-notes-to-sqlite/issues/11#issuecomment-1462968053", "issue_url": "https://api.github.com/repos/dogsheep/apple-notes-to-sqlite/issues/11", "id": 1462968053, "node_id": "IC_kwDOJHON9s5XMx71", "user": {"value": 9599, "label": "simonw"}, "created_at": "2023-03-09T23:24:01Z", "updated_at": "2023-03-09T23:24:01Z", "author_association": "MEMBER", "body": "I improved the readability by removing some unnecessary table aliases:\r\n```sql\r\nwith recursive nested_folders(folder_id, descendant_folder_id) as (\r\n -- base case: select all immediate children of the root folder\r\n select id, id from folders where parent is null\r\n union all\r\n -- recursive case: select all children of the previous level of nested folders\r\n select nested_folders.folder_id, folders.id from nested_folders\r\n join folders on nested_folders.descendant_folder_id = folders.parent\r\n)\r\n-- Find notes within all descendants of folder 1\r\nselect *\r\nfrom notes\r\nwhere folder in (\r\n select descendant_folder_id from nested_folders where folder_id = 1\r\n);\r\n```", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1618130434, "label": "Implement a SQL view to make it easier to query files in a nested folder"}, "performed_via_github_app": null}, {"html_url": "https://github.com/dogsheep/apple-notes-to-sqlite/issues/11#issuecomment-1462962682", "issue_url": "https://api.github.com/repos/dogsheep/apple-notes-to-sqlite/issues/11", "id": 1462962682, "node_id": "IC_kwDOJHON9s5XMwn6", "user": {"value": 9599, "label": "simonw"}, "created_at": "2023-03-09T23:20:35Z", "updated_at": "2023-03-09T23:22:41Z", "author_association": "MEMBER", "body": "Here's a query that returns all notes in folder 1, including notes in descendant folders:\r\n```sql\r\nwith recursive nested_folders(folder_id, descendant_folder_id) as (\r\n -- base case: select all immediate children of the root folder\r\n select id, id from folders where parent is null\r\n union all\r\n -- recursive case: select all children of the previous level of nested folders\r\n select nf.folder_id, f.id from nested_folders nf\r\n join folders f on nf.descendant_folder_id = f.parent\r\n)\r\n-- Find notes within all descendants of folder 1\r\nselect *\r\nfrom notes\r\nwhere folder in (\r\n select descendant_folder_id from nested_folders where folder_id = 1\r\n);\r\n```\r\nWith assistance from ChatGPT. Prompts were:\r\n\r\n```\r\nSQLite schema:\r\n\r\nCREATE TABLE [folders] (\r\n [id] INTEGER PRIMARY KEY,\r\n [long_id] TEXT,\r\n [name] TEXT,\r\n [parent] INTEGER,\r\n FOREIGN KEY([parent]) REFERENCES [folders]([id])\r\n);\r\n\r\nWrite a recursive CTE that returns the following:\r\n\r\nfolder_id | descendant_folder_id\r\n\r\nWith a row for every nested child of every folder - so the top level folder has lots of rows\r\n```\r\nThen I tweaked it a bit, then ran this:\r\n```\r\nWITH RECURSIVE nested_folders(folder_id, descendant_folder_id) AS (\r\n -- base case: select all immediate children of the root folder\r\n SELECT id, id FROM folders WHERE parent IS NULL\r\n UNION ALL\r\n -- recursive case: select all children of the previous level of nested folders\r\n SELECT nf.folder_id, f.id FROM nested_folders nf\r\n JOIN folders f ON nf.descendant_folder_id = f.parent\r\n)\r\n-- select all rows from the recursive CTE\r\nSELECT * from notes where folder in (select descendant_folder_id FROM nested_folders where folder_id = 1)\r\n\r\nConvert all SQL keywords to lower case, and re-indent\r\n```", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1618130434, "label": "Implement a SQL view to make it easier to query files in a nested folder"}, "performed_via_github_app": null}, {"html_url": "https://github.com/dogsheep/apple-notes-to-sqlite/issues/11#issuecomment-1462965256", "issue_url": "https://api.github.com/repos/dogsheep/apple-notes-to-sqlite/issues/11", "id": 1462965256, "node_id": "IC_kwDOJHON9s5XMxQI", "user": {"value": 9599, "label": "simonw"}, "created_at": "2023-03-09T23:22:12Z", "updated_at": "2023-03-09T23:22:12Z", "author_association": "MEMBER", "body": "Here's what the CTE from that looks like:\r\n\r\n\"image\"\r\n", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1618130434, "label": "Implement a SQL view to make it easier to query files in a nested folder"}, "performed_via_github_app": null}, {"html_url": "https://github.com/simonw/datasette/pull/2031#issuecomment-1462921890", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/2031", "id": 1462921890, "node_id": "IC_kwDOBm6k_c5XMmqi", "user": {"value": 9599, "label": "simonw"}, "created_at": "2023-03-09T22:35:30Z", "updated_at": "2023-03-09T22:35:30Z", "author_association": "OWNER", "body": "> I've implemented the test (thanks for pointing me in the right direction!).\r\n> \r\n> At [tmcl-it/datasette:0.64.1+row-view-expand-labels](https://github.com/tmcl-it/datasette/tree/0.64.1%2Brow-view-expand-labels) I also have a variant of this patch that applies to the 0.64.x branch. Please let me know if you'd be interested in merging that as well and I'll open another PR.\r\n\r\nSure, let's merge that one too - it can go out in the next `0.64.x` series release (maybe even a 0.65).", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1605481359, "label": "Expand foreign key references in row view as well"}, "performed_via_github_app": null}, {"html_url": "https://github.com/simonw/datasette/pull/2034#issuecomment-1462921010", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/2034", "id": 1462921010, "node_id": "IC_kwDOBm6k_c5XMmcy", "user": {"value": 9599, "label": "simonw"}, "created_at": "2023-03-09T22:34:29Z", "updated_at": "2023-03-09T22:34:29Z", "author_association": "OWNER", "body": "Good catch, thanks.", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1613974869, "label": "remove an unused `app` var in cli.py"}, "performed_via_github_app": null}, {"html_url": "https://github.com/dogsheep/apple-notes-to-sqlite/issues/7#issuecomment-1462693867", "issue_url": "https://api.github.com/repos/dogsheep/apple-notes-to-sqlite/issues/7", "id": 1462693867, "node_id": "IC_kwDOJHON9s5XLu_r", "user": {"value": 9599, "label": "simonw"}, "created_at": "2023-03-09T20:01:39Z", "updated_at": "2023-03-09T20:02:11Z", "author_association": "MEMBER", "body": "My `folders` table will have:\r\n\r\n- `id` - rowid\r\n- `long_id` - that long unique string ID\r\n- `name` - the name\r\n- `parent` - foreign key to `id`", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1617769847, "label": "Folder support"}, "performed_via_github_app": null}, {"html_url": "https://github.com/dogsheep/apple-notes-to-sqlite/issues/7#issuecomment-1462691466", "issue_url": "https://api.github.com/repos/dogsheep/apple-notes-to-sqlite/issues/7", "id": 1462691466, "node_id": "IC_kwDOJHON9s5XLuaK", "user": {"value": 9599, "label": "simonw"}, "created_at": "2023-03-09T19:59:52Z", "updated_at": "2023-03-09T19:59:52Z", "author_association": "MEMBER", "body": "Improved script:\r\n```zsh\r\nosascript -e 'tell application \"Notes\"\r\n set allFolders to folders\r\n repeat with aFolder in allFolders\r\n set folderId to id of aFolder\r\n set folderName to name of aFolder\r\n set folderContainer to container of aFolder\r\n if class of folderContainer is folder then\r\n set folderContainerId to id of folderContainer\r\n else\r\n set folderContainerId to \"\"\r\n end if\r\n log \"ID: \" & folderId\r\n log \"Name: \" & folderName\r\n log \"Container: \" & folderContainerId\r\n log \" \"\r\n end repeat\r\nend tell\r\n'\r\n```\r\n```\r\nID: x-coredata://D2D50498-BBD1-4097-B122-D15ABD32BDEC/ICFolder/p6113\r\nName: Blog posts\r\nContainer: \r\n \r\nID: x-coredata://D2D50498-BBD1-4097-B122-D15ABD32BDEC/ICFolder/p698\r\nName: JSK\r\nContainer: \r\n \r\nID: x-coredata://D2D50498-BBD1-4097-B122-D15ABD32BDEC/ICFolder/p7995\r\nName: Nested inside blog posts\r\nContainer: x-coredata://D2D50498-BBD1-4097-B122-D15ABD32BDEC/ICFolder/p6113\r\n \r\nID: x-coredata://D2D50498-BBD1-4097-B122-D15ABD32BDEC/ICFolder/p3526\r\nName: New Folder\r\nContainer: \r\n \r\nID: x-coredata://D2D50498-BBD1-4097-B122-D15ABD32BDEC/ICFolder/p3839\r\nName: New Folder 1\r\nContainer: \r\n \r\nID: x-coredata://D2D50498-BBD1-4097-B122-D15ABD32BDEC/ICFolder/p2\r\nName: Notes\r\nContainer: \r\n \r\nID: x-coredata://D2D50498-BBD1-4097-B122-D15ABD32BDEC/ICFolder/p6059\r\nName: Quick Notes\r\nContainer: \r\n \r\nID: x-coredata://D2D50498-BBD1-4097-B122-D15ABD32BDEC/ICFolder/p7283\r\nName: UK Christmas 2022\r\nContainer: \r\n```\r\nI filtered out things where the parent was an account and not a folder using `if class of folderContainer is folder then`.", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1617769847, "label": "Folder support"}, "performed_via_github_app": null}, {"html_url": "https://github.com/dogsheep/apple-notes-to-sqlite/issues/7#issuecomment-1462682795", "issue_url": "https://api.github.com/repos/dogsheep/apple-notes-to-sqlite/issues/7", "id": 1462682795, "node_id": "IC_kwDOJHON9s5XLsSr", "user": {"value": 9599, "label": "simonw"}, "created_at": "2023-03-09T19:52:20Z", "updated_at": "2023-03-09T19:52:44Z", "author_association": "MEMBER", "body": "Created through several rounds with ChatGPT (including hints like \"rewrite that using setdefault()\"):\r\n```python\r\ndef topological_sort(nodes):\r\n children = {}\r\n for node in nodes:\r\n parent_id = node[\"parent\"]\r\n if parent_id is not None:\r\n children.setdefault(parent_id, []).append(node)\r\n\r\n def traverse(node, result):\r\n result.append(node)\r\n if node[\"id\"] in children:\r\n for child in children[node[\"id\"]]:\r\n traverse(child, result)\r\n\r\n sorted_data = []\r\n\r\n for node in nodes:\r\n if node[\"parent\"] is None:\r\n traverse(node, sorted_data)\r\n\r\n return sorted_data\r\n```", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1617769847, "label": "Folder support"}, "performed_via_github_app": null}, {"html_url": "https://github.com/dogsheep/apple-notes-to-sqlite/issues/7#issuecomment-1462570187", "issue_url": "https://api.github.com/repos/dogsheep/apple-notes-to-sqlite/issues/7", "id": 1462570187, "node_id": "IC_kwDOJHON9s5XLQzL", "user": {"value": 9599, "label": "simonw"}, "created_at": "2023-03-09T18:30:24Z", "updated_at": "2023-03-09T18:30:24Z", "author_association": "MEMBER", "body": "I used ChatGPT to write this:\r\n```\r\nosascript -e 'tell application \"Notes\"\r\n set allFolders to folders\r\n repeat with aFolder in allFolders\r\n set folderId to id of aFolder\r\n set folderName to name of aFolder\r\n set folderContainer to container of aFolder\r\n set folderContainerName to name of folderContainer\r\n log \"Folder ID: \" & folderId\r\n log \"Folder Name: \" & folderName\r\n log \"Folder Container: \" & folderContainerName\r\n log \" \"\r\n --check for nested folders\r\n if count of folders of aFolder > 0 then\r\n set nestedFolders to folders of aFolder\r\n repeat with aNestedFolder in nestedFolders\r\n set nestedFolderId to id of aNestedFolder\r\n set nestedFolderName to name of aNestedFolder\r\n set nestedFolderContainer to container of aNestedFolder\r\n set nestedFolderContainerName to name of nestedFolderContainer\r\n log \" Nested Folder ID: \" & nestedFolderId\r\n log \" Nested Folder Name: \" & nestedFolderName\r\n log \" Nested Folder Container: \" & nestedFolderContainerName\r\n log \" \"\r\n end repeat\r\n end if\r\n end repeat\r\nend tell\r\n'\r\n```\r\nWhich for my account output this:\r\n```\r\nFolder ID: x-coredata://D2D50498-BBD1-4097-B122-D15ABD32BDEC/ICFolder/p6113\r\nFolder Name: Blog posts\r\nFolder Container: iCloud\r\n \r\n Nested Folder ID: x-coredata://D2D50498-BBD1-4097-B122-D15ABD32BDEC/ICFolder/p7995\r\n Nested Folder Name: Nested inside blog posts\r\n Nested Folder Container: Blog posts\r\n \r\nFolder ID: x-coredata://D2D50498-BBD1-4097-B122-D15ABD32BDEC/ICFolder/p698\r\nFolder Name: JSK\r\nFolder Container: iCloud\r\n \r\nFolder ID: x-coredata://D2D50498-BBD1-4097-B122-D15ABD32BDEC/ICFolder/p7995\r\nFolder Name: Nested inside blog posts\r\nFolder Container: Blog posts\r\n \r\nFolder ID: x-coredata://D2D50498-BBD1-4097-B122-D15ABD32BDEC/ICFolder/p3526\r\nFolder Name: New Folder\r\nFolder Container: iCloud\r\n \r\nFolder ID: x-coredata://D2D50498-BBD1-4097-B122-D15ABD32BDEC/ICFolder/p3839\r\nFolder Name: New Folder 1\r\nFolder Container: iCloud\r\n \r\nFolder ID: x-coredata://D2D50498-BBD1-4097-B122-D15ABD32BDEC/ICFolder/p2\r\nFolder Name: Notes\r\nFolder Container: iCloud\r\n \r\nFolder ID: x-coredata://D2D50498-BBD1-4097-B122-D15ABD32BDEC/ICFolder/p6059\r\nFolder Name: Quick Notes\r\nFolder Container: iCloud\r\n \r\nFolder ID: x-coredata://D2D50498-BBD1-4097-B122-D15ABD32BDEC/ICFolder/p7283\r\nFolder Name: UK Christmas 2022\r\nFolder Container: iCloud\r\n```\r\nSo I think the correct approach here is to run code at the start to list all of the folders (no need to do fancy recursion though, just a flat list with the parent containers is enough) and create a model of that hierarchy in SQLite.\r\n\r\nThen when I import notes I can foreign key reference them back to their containing folder.\r\n\r\nI'm tempted to use `rowid` for the foreign keys because the official IDs are pretty long.", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1617769847, "label": "Folder support"}, "performed_via_github_app": null}, {"html_url": "https://github.com/dogsheep/apple-notes-to-sqlite/issues/7#issuecomment-1462564717", "issue_url": "https://api.github.com/repos/dogsheep/apple-notes-to-sqlite/issues/7", "id": 1462564717, "node_id": "IC_kwDOJHON9s5XLPdt", "user": {"value": 9599, "label": "simonw"}, "created_at": "2023-03-09T18:25:39Z", "updated_at": "2023-03-09T18:25:39Z", "author_association": "MEMBER", "body": "So it looks like folders can be hierarchical?", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1617769847, "label": "Folder support"}, "performed_via_github_app": null}, {"html_url": "https://github.com/dogsheep/apple-notes-to-sqlite/issues/7#issuecomment-1462562735", "issue_url": "https://api.github.com/repos/dogsheep/apple-notes-to-sqlite/issues/7", "id": 1462562735, "node_id": "IC_kwDOJHON9s5XLO-v", "user": {"value": 9599, "label": "simonw"}, "created_at": "2023-03-09T18:23:56Z", "updated_at": "2023-03-09T18:25:22Z", "author_association": "MEMBER", "body": "From the Script Editor library docs:\r\n\r\nA note has a:\r\n\r\n> - `container` (folder), r/o) : the folder of the note\r\n\r\nHere's what a folder looks like:\r\n\r\n> folder\u2002n : a folder containing notes\r\n> elements:\r\n>\r\n> - contains folders, notes; contained by application, accounts, folders.\r\n>\r\n> properties:\r\n>\r\n> - `name` (text) : the name of the folder\r\n> - `id` (text, r/o) : the unique identifier of the folder\r\n> - `shared` (boolean, r/o) : Is the folder shared?\r\n> - `container` (account or folder, r/o) : the container of the folder\r\n", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1617769847, "label": "Folder support"}, "performed_via_github_app": null}, {"html_url": "https://github.com/dogsheep/apple-notes-to-sqlite/issues/4#issuecomment-1462556829", "issue_url": "https://api.github.com/repos/dogsheep/apple-notes-to-sqlite/issues/4", "id": 1462556829, "node_id": "IC_kwDOJHON9s5XLNid", "user": {"value": 9599, "label": "simonw"}, "created_at": "2023-03-09T18:20:56Z", "updated_at": "2023-03-09T18:20:56Z", "author_association": "MEMBER", "body": "In terms of the UI: I'm tempted to say that the default behaviour is for it to run until it sees a note that it already knows about AND that has matching update/created dates, and then stop.\r\n\r\nYou can do a full import again ignoring that logic with `apple-notes-to-sqlite notes.db --full`.", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1616429236, "label": "Support incremental updates"}, "performed_via_github_app": null}, {"html_url": "https://github.com/dogsheep/apple-notes-to-sqlite/issues/4#issuecomment-1462554175", "issue_url": "https://api.github.com/repos/dogsheep/apple-notes-to-sqlite/issues/4", "id": 1462554175, "node_id": "IC_kwDOJHON9s5XLM4_", "user": {"value": 9599, "label": "simonw"}, "created_at": "2023-03-09T18:19:34Z", "updated_at": "2023-03-09T18:19:34Z", "author_association": "MEMBER", "body": "It looks like the iteration order is most-recently-modified-first - I tried editing a note a bit further back in my notes app and it was the first one output by `apple-notes-to-sqlite --dump`.", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1616429236, "label": "Support incremental updates"}, "performed_via_github_app": null}, {"html_url": "https://github.com/dogsheep/apple-notes-to-sqlite/issues/2#issuecomment-1461285545", "issue_url": "https://api.github.com/repos/dogsheep/apple-notes-to-sqlite/issues/2", "id": 1461285545, "node_id": "IC_kwDOJHON9s5XGXKp", "user": {"value": 9599, "label": "simonw"}, "created_at": "2023-03-09T05:06:24Z", "updated_at": "2023-03-09T05:06:24Z", "author_association": "MEMBER", "body": "OK, this works!", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1616354999, "label": "First working version"}, "performed_via_github_app": null}, {"html_url": "https://github.com/dogsheep/apple-notes-to-sqlite/issues/2#issuecomment-1461262577", "issue_url": "https://api.github.com/repos/dogsheep/apple-notes-to-sqlite/issues/2", "id": 1461262577, "node_id": "IC_kwDOJHON9s5XGRjx", "user": {"value": 9599, "label": "simonw"}, "created_at": "2023-03-09T04:30:00Z", "updated_at": "2023-03-09T04:30:00Z", "author_association": "MEMBER", "body": "It doesn't have tests yet. I guess I'll need to mock `subprocess` to test this.", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1616354999, "label": "First working version"}, "performed_via_github_app": null}, {"html_url": "https://github.com/dogsheep/apple-notes-to-sqlite/issues/2#issuecomment-1461260978", "issue_url": "https://api.github.com/repos/dogsheep/apple-notes-to-sqlite/issues/2", "id": 1461260978, "node_id": "IC_kwDOJHON9s5XGRKy", "user": {"value": 9599, "label": "simonw"}, "created_at": "2023-03-09T04:27:18Z", "updated_at": "2023-03-09T04:27:18Z", "author_association": "MEMBER", "body": "Before that conversion:\r\n\r\n Monday, March 6, 2023 at 11:55:15 AM\r\n\r\nAfter:\r\n\r\n 2023-03-06T11:55:15", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1616354999, "label": "First working version"}, "performed_via_github_app": null}, {"html_url": "https://github.com/dogsheep/apple-notes-to-sqlite/issues/2#issuecomment-1461259490", "issue_url": "https://api.github.com/repos/dogsheep/apple-notes-to-sqlite/issues/2", "id": 1461259490, "node_id": "IC_kwDOJHON9s5XGQzi", "user": {"value": 9599, "label": "simonw"}, "created_at": "2023-03-09T04:24:27Z", "updated_at": "2023-03-09T04:24:27Z", "author_association": "MEMBER", "body": "Converting AppleScript date strings to ISO format is hard!\r\n\r\nhttps://forum.latenightsw.com/t/formatting-dates/841 has a recipe I'll try:\r\n\r\n set todayISO to (todayDate as \u00abclass isot\u00bb as string)\r\n\r\nNot clear to me how timezones work here. I'm going to ignore them for the moment.", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1616354999, "label": "First working version"}, "performed_via_github_app": null}, {"html_url": "https://github.com/dogsheep/apple-notes-to-sqlite/issues/2#issuecomment-1461234591", "issue_url": "https://api.github.com/repos/dogsheep/apple-notes-to-sqlite/issues/2", "id": 1461234591, "node_id": "IC_kwDOJHON9s5XGKuf", "user": {"value": 9599, "label": "simonw"}, "created_at": "2023-03-09T03:56:45Z", "updated_at": "2023-03-09T03:56:45Z", "author_association": "MEMBER", "body": "My prototype showed that images embedded in notes come out in the HTML export as bas64 image URLs, which is neat.", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1616354999, "label": "First working version"}, "performed_via_github_app": null}, {"html_url": "https://github.com/dogsheep/apple-notes-to-sqlite/issues/2#issuecomment-1461234311", "issue_url": "https://api.github.com/repos/dogsheep/apple-notes-to-sqlite/issues/2", "id": 1461234311, "node_id": "IC_kwDOJHON9s5XGKqH", "user": {"value": 9599, "label": "simonw"}, "created_at": "2023-03-09T03:56:24Z", "updated_at": "2023-03-09T03:56:24Z", "author_association": "MEMBER", "body": "I opened the \"Script Editor\" app on my computer, used Window -> Library to open the Library panel, then clicked on the Notes app there. I got this:\r\n\r\n\"image\"\r\n\r\nSo the notes object has these properties:\r\n\r\n- name (text) : the name of the note (normally the first line of the body)\r\n- id (text, r/o) : the unique identifier of the note\r\n- container ([folder](applewebdata://621FA8D9-C995-4081-B3B3-149B0EA04C7F#Notes-Suite.folder), r/o) : the folder of the note\r\n- body (text) : the HTML content of the note\r\n- plaintext (text, r/o) : the plaintext content of the note\r\n- creation date (date, r/o) : the creation date of the note\r\n- modification date (date, r/o) : the modification date of the note\r\n- password protected (boolean, r/o) : Is the note password protected?\r\n- shared (boolean, r/o) : Is the note shared?\r\n\r\nI'm going to ignore the concept of attachments for the moment.", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1616354999, "label": "First working version"}, "performed_via_github_app": null}, {"html_url": "https://github.com/dogsheep/apple-notes-to-sqlite/issues/2#issuecomment-1461232709", "issue_url": "https://api.github.com/repos/dogsheep/apple-notes-to-sqlite/issues/2", "id": 1461232709, "node_id": "IC_kwDOJHON9s5XGKRF", "user": {"value": 9599, "label": "simonw"}, "created_at": "2023-03-09T03:54:28Z", "updated_at": "2023-03-09T03:54:28Z", "author_association": "MEMBER", "body": "I think the AppleScript I want to pass to `osascript` looks like this:\r\n```applescript\r\ntell application \"Notes\"\r\n repeat with eachNote in every note\r\n set noteId to the id of eachNote\r\n set noteTitle to the name of eachNote\r\n set noteBody to the body of eachNote\r\n log \"------------------------\" & \"\\n\"\r\n log noteId & \"\\n\"\r\n log noteTitle & \"\\n\\n\"\r\n log noteBody & \"\\n\"\r\n end repeat\r\nend tell\r\n```\r\nBut there are a few more properties I'd like to get - created and updated date for example.", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1616354999, "label": "First working version"}, "performed_via_github_app": null}, {"html_url": "https://github.com/dogsheep/apple-notes-to-sqlite/issues/1#issuecomment-1461230436", "issue_url": "https://api.github.com/repos/dogsheep/apple-notes-to-sqlite/issues/1", "id": 1461230436, "node_id": "IC_kwDOJHON9s5XGJtk", "user": {"value": 9599, "label": "simonw"}, "created_at": "2023-03-09T03:51:52Z", "updated_at": "2023-03-09T03:51:52Z", "author_association": "MEMBER", "body": "This did the job! Next step is to turn that into a Python script.", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1616347574, "label": "Initial proof of concept with ChatGPT"}, "performed_via_github_app": null}, {"html_url": "https://github.com/dogsheep/apple-notes-to-sqlite/issues/1#issuecomment-1461230197", "issue_url": "https://api.github.com/repos/dogsheep/apple-notes-to-sqlite/issues/1", "id": 1461230197, "node_id": "IC_kwDOJHON9s5XGJp1", "user": {"value": 9599, "label": "simonw"}, "created_at": "2023-03-09T03:51:36Z", "updated_at": "2023-03-09T03:51:36Z", "author_association": "MEMBER", "body": "After a few more rounds I got to this script, which outputs them to a `/tmp/notes.txt` file:\r\n\r\n```zsh\r\n#!/bin/zsh\r\n\r\nosascript -e '\r\nset notesFile to \"/tmp/notes.txt\"\r\nset fileRef to open for access notesFile with write permission\r\ntell application \"Notes\"\r\n repeat with eachNote in every note\r\n set noteId to the id of eachNote\r\n set noteTitle to the name of eachNote\r\n set noteBody to the body of eachNote\r\n write \"------------------------\" & \"\\n\" to fileRef\r\n write noteId & \"\\n\" to fileRef\r\n write noteTitle & \"\\n\\n\" to fileRef\r\n write noteBody & \"\\n\" to fileRef\r\n end repeat\r\nend tell\r\nclose access fileRef'\r\n```\r\nThen I wrote this little Python script to load them into a database:\r\n```python\r\nimport sqlite_utils\r\nsplit = b\"------------------------\\n\"\r\ns = open(\"/tmp/notes.txt\", \"rb\").read()\r\nnotes = [n.decode(\"mac_roman\") for n in s.split(split) if n]\r\n\r\ncleaned_notes = [{\r\n \"id\": n.split(\"\\n\")[0],\r\n \"title\": n.split(\"\\n\")[1],\r\n \"body\": \"\\n\".join(n.split(\"\\n\")[2:]).strip()\r\n} for n in notes]\r\n\r\ndb = sqlite_utils.Database(\"/tmp/notes.db\")\r\ndb[\"notes\"].insert_all(cleaned_notes)\r\n```", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1616347574, "label": "Initial proof of concept with ChatGPT"}, "performed_via_github_app": null}, {"html_url": "https://github.com/dogsheep/apple-notes-to-sqlite/issues/1#issuecomment-1461226402", "issue_url": "https://api.github.com/repos/dogsheep/apple-notes-to-sqlite/issues/1", "id": 1461226402, "node_id": "IC_kwDOJHON9s5XGIui", "user": {"value": 9599, "label": "simonw"}, "created_at": "2023-03-09T03:47:11Z", "updated_at": "2023-03-09T03:47:11Z", "author_association": "MEMBER", "body": "Me:\r\n> Write applescript to loop through all of my Apple Notes and output their contents\r\n\r\nChatGPT:\r\n\r\n```applescript\r\ntell application \"Notes\"\r\n repeat with eachNote in every note\r\n set noteTitle to the name of eachNote\r\n set noteBody to the body of eachNote\r\n display dialog noteTitle & return & return & noteBody\r\n end repeat\r\nend tell\r\n```\r\nMe:\r\n> How can I turn that into a zsh script which will output the results to my terminal\r\n\r\nChatGPT:\r\n```zsh\r\n#!/usr/bin/zsh\r\n\r\nosascript -e 'tell application \"Notes\"\r\n repeat with eachNote in every note\r\n set noteTitle to the name of eachNote\r\n set noteBody to the body of eachNote\r\n set output to noteTitle & \"\\n\" & noteBody & \"\\n\"\r\n display dialog output\r\n log output\r\n end repeat\r\nend tell'\r\n```\r\nI had to change that first line to `#!/bin/zsh` - then when I ran it it spammed my screen with dialog boxes.\r\n\r\nMe:\r\n\r\n> I want to log the notes contents to the terminal, not display it in a dialog\r\n\r\nChatGPT:\r\n\r\n```zsh\r\n#!/bin/zsh\r\n\r\nosascript -e 'tell application \"Notes\"\r\n repeat with eachNote in every note\r\n set noteTitle to the name of eachNote\r\n set noteBody to the body of eachNote\r\n set output to noteTitle & \"\\n\" & noteBody & \"\\n\"\r\n log output\r\n end repeat\r\nend tell'\r\n```\r\nThis really helps!", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1616347574, "label": "Initial proof of concept with ChatGPT"}, "performed_via_github_app": null}, {"html_url": "https://github.com/simonw/datasette/pull/1999#issuecomment-1461161256", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1999", "id": 1461161256, "node_id": "IC_kwDOBm6k_c5XF40o", "user": {"value": 9599, "label": "simonw"}, "created_at": "2023-03-09T02:10:07Z", "updated_at": "2023-03-09T02:10:07Z", "author_association": "OWNER", "body": "Just ran into a `no such table: columns` error - which I think is because my new view code sometimes bypasses calling this method (currently done in `BaseView.dispatch_request()`):\r\n\r\nhttps://github.com/simonw/datasette/blob/96e94f9b7b2db53865e61390bcce6761727f26d8/datasette/views/base.py#L101-L103", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1551694938, "label": "?_extra= support (draft)"}, "performed_via_github_app": null}, {"html_url": "https://github.com/simonw/datasette/pull/1999#issuecomment-1461148579", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1999", "id": 1461148579, "node_id": "IC_kwDOBm6k_c5XF1uj", "user": {"value": 9599, "label": "simonw"}, "created_at": "2023-03-09T01:54:10Z", "updated_at": "2023-03-09T01:55:33Z", "author_association": "OWNER", "body": "Or... I could temporarily build a quick additional `CannedQueryView` subclass that just does the necessary bits to get the existing code to work. I'm going to try that.", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1551694938, "label": "?_extra= support (draft)"}, "performed_via_github_app": null}, {"html_url": "https://github.com/simonw/datasette/pull/1999#issuecomment-1461148254", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1999", "id": 1461148254, "node_id": "IC_kwDOBm6k_c5XF1pe", "user": {"value": 9599, "label": "simonw"}, "created_at": "2023-03-09T01:53:41Z", "updated_at": "2023-03-09T01:53:41Z", "author_association": "OWNER", "body": "Solving this is proving difficult: https://github.com/simonw/datasette/blob/96e94f9b7b2db53865e61390bcce6761727f26d8/datasette/views/table.py#L1500-L1503\r\n\r\nThe problem is that calling `.data()` on `QueryView` only works here because we expect to ourselves be inside a `.data()` method, with all of the existing magic that knows how to render things that are returned by that.\r\n\r\nSo I may need to substantially re-engineer how `QueryView` works in order to get this to work.", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1551694938, "label": "?_extra= support (draft)"}, "performed_via_github_app": null}, {"html_url": "https://github.com/simonw/datasette/pull/1999#issuecomment-1461075648", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1999", "id": 1461075648, "node_id": "IC_kwDOBm6k_c5XFj7A", "user": {"value": 9599, "label": "simonw"}, "created_at": "2023-03-09T00:24:22Z", "updated_at": "2023-03-09T00:24:22Z", "author_association": "OWNER", "body": "`127.0.0.1:8001/fixtures/neighborhood_search` fails because the forwarding to a canned query does not yet work.", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1551694938, "label": "?_extra= support (draft)"}, "performed_via_github_app": null}, {"html_url": "https://github.com/simonw/datasette/pull/1999#issuecomment-1461074526", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1999", "id": 1461074526, "node_id": "IC_kwDOBm6k_c5XFjpe", "user": {"value": 9599, "label": "simonw"}, "created_at": "2023-03-09T00:23:06Z", "updated_at": "2023-03-09T00:23:06Z", "author_association": "OWNER", "body": " pytest tests/test_table_html.py\r\n\r\nCurrently 44 failed, 24 passed in 7.53s\r\n\r\nFailures here: https://gist.github.com/simonw/df0a52cd7d820b776dc3dfc50e7cb778", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1551694938, "label": "?_extra= support (draft)"}, "performed_via_github_app": null}, {"html_url": "https://github.com/simonw/datasette/pull/1999#issuecomment-1461070937", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1999", "id": 1461070937, "node_id": "IC_kwDOBm6k_c5XFixZ", "user": {"value": 9599, "label": "simonw"}, "created_at": "2023-03-09T00:18:52Z", "updated_at": "2023-03-09T00:19:36Z", "author_association": "OWNER", "body": "I managed to get HTML view working! I did it by continuing to add more things to the extras and the `_html` bundle until the page loaded for me:\r\n\r\n```diff\r\n async def extra_extras():\r\n \"Available ?_extra= blocks\"\r\n return {\r\n@@ -1981,6 +2053,14 @@ async def extra_extras():\r\n \"query\",\r\n \"display_columns\",\r\n \"display_rows\",\r\n+ \"database\",\r\n+ \"table\",\r\n+ \"database_color\",\r\n+ \"table_actions\",\r\n+ \"filters\",\r\n+ \"renderers\",\r\n+ \"custom_table_templates\",\r\n+ \"sorted_facet_results\",\r\n ]\r\n }\r\n \r\n@@ -2006,6 +2086,14 @@ async def extra_extras():\r\n extra_query,\r\n extra_metadata,\r\n extra_extras,\r\n+ extra_database,\r\n+ extra_table,\r\n+ extra_database_color,\r\n+ extra_table_actions,\r\n+ extra_filters,\r\n+ extra_renderers,\r\n+ extra_custom_table_templates,\r\n+ extra_sorted_facet_results,\r\n )\r\n```\r\nI'll probably refactor this into something cleaner, and maybe but a bunch of them in a `\"html\"` dictionary and update the templates to use `{{ html.filters }}` or similar. Will look at that once the tests are passing.\r\n\r\n\"image\"\r\n", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1551694938, "label": "?_extra= support (draft)"}, "performed_via_github_app": null}, {"html_url": "https://github.com/simonw/datasette/pull/1999#issuecomment-1461047607", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1999", "id": 1461047607, "node_id": "IC_kwDOBm6k_c5XFdE3", "user": {"value": 9599, "label": "simonw"}, "created_at": "2023-03-08T23:51:46Z", "updated_at": "2023-03-08T23:51:46Z", "author_association": "OWNER", "body": "This feels quite nice:\r\n\r\n\"image\"\r\n", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1551694938, "label": "?_extra= support (draft)"}, "performed_via_github_app": null}, {"html_url": "https://github.com/simonw/datasette/pull/1999#issuecomment-1461044477", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1999", "id": 1461044477, "node_id": "IC_kwDOBm6k_c5XFcT9", "user": {"value": 9599, "label": "simonw"}, "created_at": "2023-03-08T23:47:26Z", "updated_at": "2023-03-08T23:47:26Z", "author_association": "OWNER", "body": "I want to package together all of the extras that are needed for the HTML format. A few options for doing that:\r\n\r\n- Introduce `?_extra=_html` where the leading underscore indicates that this is a \"bundle\" of extras, then define a bundle that's everything needed for the HTML renderer\r\n- Have some other mechanism whereby different renderers can request a bundle of extras.\r\n\r\nI'm leaning towards the first option. I'll try that and see what it looks like.", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1551694938, "label": "?_extra= support (draft)"}, "performed_via_github_app": null}, {"html_url": "https://github.com/simonw/datasette/pull/1999#issuecomment-1461023559", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1999", "id": 1461023559, "node_id": "IC_kwDOBm6k_c5XFXNH", "user": {"value": 9599, "label": "simonw"}, "created_at": "2023-03-08T23:23:02Z", "updated_at": "2023-03-08T23:23:02Z", "author_association": "OWNER", "body": "To get this unblocked, I'm going to allow myself to pass non-JSON-serializable objects to the HTML template version of things. If I can get that working (and get the existing tests to pass) I can consider a later change that makes those JSON serializable - or admit that it's OK for the templates to have non-JSON data passed to them and figure out how best to document those variables independently from the JSON documentation.", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1551694938, "label": "?_extra= support (draft)"}, "performed_via_github_app": null}, {"html_url": "https://github.com/simonw/datasette/pull/1999#issuecomment-1461002039", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1999", "id": 1461002039, "node_id": "IC_kwDOBm6k_c5XFR83", "user": {"value": 9599, "label": "simonw"}, "created_at": "2023-03-08T22:58:16Z", "updated_at": "2023-03-08T23:02:09Z", "author_association": "OWNER", "body": "The reason for that `Row` thing is that it allows custom templates that do things like this:\r\n\r\nhttps://docs.datasette.io/en/stable/changelog.html#easier-custom-templates-for-table-rows\r\n\r\n```html+jinja\r\n{% for row in display_rows %}\r\n
\r\n

{{ row[\"title\"] }}

\r\n

{{ row[\"description\"] }}\r\n

Category: {{ row.display(\"category_id\") }}

\r\n
\r\n{% endfor %}\r\n```\r\nIs that a good design? the `.display()` thing feels weird - I wonder if anyone has ever actually used that.\r\n\r\nIt's documented here: https://docs.datasette.io/en/0.64.2/custom_templates.html#custom-templates\r\n\r\n> If you want to output the rendered HTML version of a column, including any links to foreign keys, you can use `{{ row.display(\"column_name\") }}`.\r\n\r\nI can't see any examples of anyone using it in this code search: https://cs.github.com/?scopeName=All+repos&scope=&q=datasette+row.display\r\n\r\nIt is however useful to have some kind of abstraction layer here that insulates the SQLite `Row` object, since having an extra layer will help if Datasette ever grows support for alternative database backends such as DuckDB or PostgreSQL.", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1551694938, "label": "?_extra= support (draft)"}, "performed_via_github_app": null}, {"html_url": "https://github.com/simonw/datasette/pull/1999#issuecomment-1460988975", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1999", "id": 1460988975, "node_id": "IC_kwDOBm6k_c5XFOwv", "user": {"value": 9599, "label": "simonw"}, "created_at": "2023-03-08T22:42:57Z", "updated_at": "2023-03-08T22:42:57Z", "author_association": "OWNER", "body": "Aside idea: it might be interesting if there were \"lazy\" template variables available in the context: things that are not actually executed unless a template author requests them.\r\n\r\nImagine if `metadata` was a lazy template reference, such that custom templates that don't display any metadata don't trigger it to be resolved (which might involve additional database queries some day).", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1551694938, "label": "?_extra= support (draft)"}, "performed_via_github_app": null}, {"html_url": "https://github.com/simonw/datasette/pull/1999#issuecomment-1460986533", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1999", "id": 1460986533, "node_id": "IC_kwDOBm6k_c5XFOKl", "user": {"value": 9599, "label": "simonw"}, "created_at": "2023-03-08T22:40:28Z", "updated_at": "2023-03-08T22:40:28Z", "author_association": "OWNER", "body": "Figuring out what to do with `display_columns_and_rows()` is hard. That returns rows as this special kind of object, which is designed to be accessed from the HTML templates:\r\n\r\nhttps://github.com/simonw/datasette/blob/96e94f9b7b2db53865e61390bcce6761727f26d8/datasette/views/table.py#L45-L71", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1551694938, "label": "?_extra= support (draft)"}, "performed_via_github_app": null}, {"html_url": "https://github.com/simonw/datasette/pull/1999#issuecomment-1460970807", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1999", "id": 1460970807, "node_id": "IC_kwDOBm6k_c5XFKU3", "user": {"value": 9599, "label": "simonw"}, "created_at": "2023-03-08T22:31:49Z", "updated_at": "2023-03-08T22:33:03Z", "author_association": "OWNER", "body": "For the HTML version, I need to decide where all of the stuff that happens in `async def extra_template()` is going to live.\r\n\r\nI think it's another one of those extra functions, triggered for `?_extra=context`.\r\n\r\nhttps://github.com/simonw/datasette/blob/96e94f9b7b2db53865e61390bcce6761727f26d8/datasette/views/table.py#L813-L912", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1551694938, "label": "?_extra= support (draft)"}, "performed_via_github_app": null}, {"html_url": "https://github.com/simonw/datasette/pull/1999#issuecomment-1460943097", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1999", "id": 1460943097, "node_id": "IC_kwDOBm6k_c5XFDj5", "user": {"value": 9599, "label": "simonw"}, "created_at": "2023-03-08T22:09:24Z", "updated_at": "2023-03-08T22:09:47Z", "author_association": "OWNER", "body": "The ease with which I added that `?_extra=query` feature in https://github.com/simonw/datasette/pull/1999/commits/96e94f9b7b2db53865e61390bcce6761727f26d8 made me feel really confident that this architecture is going in the right direction.\r\n\r\n```diff\r\ndiff --git a/datasette/views/table.py b/datasette/views/table.py\r\nindex 8d3bb2c930..3e1db9c85f 100644\r\n--- a/datasette/views/table.py\r\n+++ b/datasette/views/table.py\r\n@@ -1913,6 +1913,13 @@ async def extra_request():\r\n \"args\": request.args._data,\r\n }\r\n \r\n+ async def extra_query():\r\n+ \"Details of the underlying SQL query\"\r\n+ return {\r\n+ \"sql\": sql,\r\n+ \"params\": params,\r\n+ }\r\n+\r\n async def extra_extras():\r\n \"Available ?_extra= blocks\"\r\n return {\r\n@@ -1938,6 +1945,7 @@ async def extra_extras():\r\n extra_primary_keys,\r\n extra_debug,\r\n extra_request,\r\n+ extra_query,\r\n extra_extras,\r\n )\r\n```", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1551694938, "label": "?_extra= support (draft)"}, "performed_via_github_app": null}, {"html_url": "https://github.com/simonw/datasette/pull/1999#issuecomment-1460916405", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1999", "id": 1460916405, "node_id": "IC_kwDOBm6k_c5XE9C1", "user": {"value": 9599, "label": "simonw"}, "created_at": "2023-03-08T21:43:27Z", "updated_at": "2023-03-08T21:43:27Z", "author_association": "OWNER", "body": "Just noticed that `_json=colname` is not working, and that's because it's handled by the renderer here:\r\n\r\nhttps://github.com/simonw/datasette/blob/56b0758a5fbf85d01ff80a40c9b028469d7bb65f/datasette/renderer.py#L29-L40\r\n\r\nBut that's not currently being called by my new code.", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1551694938, "label": "?_extra= support (draft)"}, "performed_via_github_app": null}, {"html_url": "https://github.com/simonw/datasette/pull/1999#issuecomment-1460907148", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1999", "id": 1460907148, "node_id": "IC_kwDOBm6k_c5XE6yM", "user": {"value": 9599, "label": "simonw"}, "created_at": "2023-03-08T21:34:30Z", "updated_at": "2023-03-08T21:34:30Z", "author_association": "OWNER", "body": "I'm going to hold off on that refactor until later, when I have tests to show me if the refactor works or not.", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1551694938, "label": "?_extra= support (draft)"}, "performed_via_github_app": null}, {"html_url": "https://github.com/simonw/datasette/pull/1999#issuecomment-1460906741", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1999", "id": 1460906741, "node_id": "IC_kwDOBm6k_c5XE6r1", "user": {"value": 9599, "label": "simonw"}, "created_at": "2023-03-08T21:34:08Z", "updated_at": "2023-03-08T21:34:08Z", "author_association": "OWNER", "body": "So maybe I can refactor it to look a bit more like this:\r\n\r\nhttps://github.com/simonw/datasette/blob/db1a88f4e17a1f50bdaa681e8beddb2276503e7c/datasette/views/table.py#L1602-L1604\r\n\r\nOne thing that's useful here is that `is_view` is handled early, like this:\r\n\r\nhttps://github.com/simonw/datasette/blob/db1a88f4e17a1f50bdaa681e8beddb2276503e7c/datasette/views/table.py#L466-L472\r\n\r\nSo if I omit the `is_view` bit from the extracted function I can simplify more.", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1551694938, "label": "?_extra= support (draft)"}, "performed_via_github_app": null}, {"html_url": "https://github.com/simonw/datasette/pull/1999#issuecomment-1460905469", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1999", "id": 1460905469, "node_id": "IC_kwDOBm6k_c5XE6X9", "user": {"value": 9599, "label": "simonw"}, "created_at": "2023-03-08T21:32:56Z", "updated_at": "2023-03-08T21:32:56Z", "author_association": "OWNER", "body": "I'd really like to extract this ugly logic out into a helper function:\r\n\r\nhttps://github.com/simonw/datasette/blob/db1a88f4e17a1f50bdaa681e8beddb2276503e7c/datasette/views/table.py#L1617-L1684\r\n\r\nI copied it in and asked ChatGPT to \"List all of the variable that are either modified or assigned to by the above code\":\r\n\r\n> Modified variables:\r\n> \r\n> offset\r\n> where_clauses\r\n> params\r\n> order_by\r\n> \r\n> Assigned variables:\r\n> \r\n> sort_value\r\n> next_by_pk_clauses\r\n> order_by (when sort or sort_desc is not None)\r\n\r\nThen I asked which variables were used as inputs, and argued with it a bit about whether it should be counting functions. Eventually got to this:\r\n\r\n> My apologies for the oversight. Here are the variables needed as input by the above code, excluding any functions:\r\n> \r\n> _next\r\n> is_view\r\n> sort\r\n> sort_desc\r\n> use_rowid\r\n> pks\r\n> order_by\r\n\r\nNote that `use_rowid` is actually defined earlier in terms of two of those other variables: https://github.com/simonw/datasette/blob/db1a88f4e17a1f50bdaa681e8beddb2276503e7c/datasette/views/table.py#L1540", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1551694938, "label": "?_extra= support (draft)"}, "performed_via_github_app": null}, {"html_url": "https://github.com/simonw/datasette/issues/2036#issuecomment-1460866243", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/2036", "id": 1460866243, "node_id": "IC_kwDOBm6k_c5XEwzD", "user": {"value": 9599, "label": "simonw"}, "created_at": "2023-03-08T20:57:34Z", "updated_at": "2023-03-08T20:57:34Z", "author_association": "OWNER", "body": "This fix is released in 0.64.2 https://docs.datasette.io/en/stable/changelog.html#v0-64-2", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1615862295, "label": "`publish cloudrun` reuses image tags, which can lead to very surprising deploy problems"}, "performed_via_github_app": null}, {"html_url": "https://github.com/simonw/datasette/issues/2036#issuecomment-1460848869", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/2036", "id": 1460848869, "node_id": "IC_kwDOBm6k_c5XEsjl", "user": {"value": 9599, "label": "simonw"}, "created_at": "2023-03-08T20:40:55Z", "updated_at": "2023-03-08T20:40:55Z", "author_association": "OWNER", "body": "Here's the https://latest.datasette.io/ deployment that just went out, further demonstrating that this change is working correctly:\r\n\r\n\"image\"\r\n\r\n", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1615862295, "label": "`publish cloudrun` reuses image tags, which can lead to very surprising deploy problems"}, "performed_via_github_app": null}, {"html_url": "https://github.com/simonw/datasette/issues/2037#issuecomment-1460840620", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/2037", "id": 1460840620, "node_id": "IC_kwDOBm6k_c5XEqis", "user": {"value": 9599, "label": "simonw"}, "created_at": "2023-03-08T20:33:00Z", "updated_at": "2023-03-08T20:33:00Z", "author_association": "OWNER", "body": "Got the same failure again for a recent commit: https://github.com/simonw/datasette/actions/runs/4368239376/jobs/7640567282", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1615891776, "label": "Test failure: FAILED tests/test_cli.py::test_install_requirements - FileNotFoundError"}, "performed_via_github_app": null}, {"html_url": "https://github.com/simonw/datasette/issues/2037#issuecomment-1460838797", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/2037", "id": 1460838797, "node_id": "IC_kwDOBm6k_c5XEqGN", "user": {"value": 9599, "label": "simonw"}, "created_at": "2023-03-08T20:31:15Z", "updated_at": "2023-03-08T20:31:15Z", "author_association": "OWNER", "body": "It's this test here:\r\n\r\nhttps://github.com/simonw/datasette/blob/1ad92a1d87d79084ebe524ed186c900ff042328c/tests/test_cli.py#L181-L189\r\n\r\nAdded in:\r\n- #2033 ", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1615891776, "label": "Test failure: FAILED tests/test_cli.py::test_install_requirements - FileNotFoundError"}, "performed_via_github_app": null}, {"html_url": "https://github.com/simonw/datasette/issues/2037#issuecomment-1460838109", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/2037", "id": 1460838109, "node_id": "IC_kwDOBm6k_c5XEp7d", "user": {"value": 9599, "label": "simonw"}, "created_at": "2023-03-08T20:30:36Z", "updated_at": "2023-03-08T20:30:36Z", "author_association": "OWNER", "body": "Instead of using `isolated_filesystem()` I could use a `tmpdir` fixture instead.", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1615891776, "label": "Test failure: FAILED tests/test_cli.py::test_install_requirements - FileNotFoundError"}, "performed_via_github_app": null}, {"html_url": "https://github.com/simonw/datasette/issues/2036#issuecomment-1460827178", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/2036", "id": 1460827178, "node_id": "IC_kwDOBm6k_c5XEnQq", "user": {"value": 9599, "label": "simonw"}, "created_at": "2023-03-08T20:25:10Z", "updated_at": "2023-03-08T20:25:10Z", "author_association": "OWNER", "body": "https://console.cloud.google.com/run/detail/us-central1/new-service/revisions?project=datasette-222320 confirms that the image deployed is:\r\n\r\n\"image\"\r\n\r\nCompared to https://console.cloud.google.com/run/detail/us-central1/datasette-io/revisions?project=datasette-222320 which shows that `datasette.io` is running:\r\n\r\n\"image\"\r\n", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1615862295, "label": "`publish cloudrun` reuses image tags, which can lead to very surprising deploy problems"}, "performed_via_github_app": null}, {"html_url": "https://github.com/simonw/datasette/issues/2036#issuecomment-1460816528", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/2036", "id": 1460816528, "node_id": "IC_kwDOBm6k_c5XEkqQ", "user": {"value": 9599, "label": "simonw"}, "created_at": "2023-03-08T20:22:50Z", "updated_at": "2023-03-08T20:23:20Z", "author_association": "OWNER", "body": "Testing this manually:\r\n\r\n```\r\n% datasette publish cloudrun content.db --service new-service\r\nCreating temporary tarball archive of 2 file(s) totalling 13.8 MiB before compression.\r\nUploading tarball of [.] to [gs://datasette-222320_cloudbuild/source/1678306859.271661-805303f364144b6094cc9c8532ab5133.tgz]\r\nCreated [https://cloudbuild.googleapis.com/v1/projects/datasette-222320/locations/global/builds/290f41a4-e29a-443c-a1e5-c54513c6143d].\r\nLogs are available at [ https://console.cloud.google.com/cloud-build/builds/290f41a4-e29a-443c-a1e5-c54513c6143d?project=99025868001 ].\r\n---- REMOTE BUILD OUTPUT ----\r\nstarting build \"290f41a4-e29a-443c-a1e5-c54513c6143d\"\r\n\r\nFETCHSOURCE\r\nFetching storage object: gs://datasette-222320_cloudbuild/source/1678306859.271661-805303f364144b6094cc9c8532ab5133.tgz#1678306862810483\r\nCopying gs://datasette-222320_cloudbuild/source/1678306859.271661-805303f364144b6094cc9c8532ab5133.tgz#1678306862810483...\r\n/ [1 files][ 3.9 MiB/ 3.9 MiB] \r\nOperation completed over 1 objects/3.9 MiB.\r\nBUILD\r\nAlready have image (with digest): gcr.io/cloud-builders/docker\r\nSending build context to Docker daemon 14.52MB\r\nStep 1/9 : FROM python:3.11.0-slim-bullseye\r\n...\r\nInstalling collected packages: rfc3986, typing-extensions, sniffio, PyYAML, python-multipart, pluggy, pint, mergedeep, MarkupSafe, itsdangerous, idna, hupper, h11, click, certifi, asgiref, aiofiles, uvicorn, Jinja2, janus, click-default-group-wheel, asgi-csrf, anyio, httpcore, httpx, datasette\r\nSuccessfully installed Jinja2-3.1.2 MarkupSafe-2.1.2 PyYAML-6.0 aiofiles-23.1.0 anyio-3.6.2 asgi-csrf-0.9 asgiref-3.6.0 certifi-2022.12.7 click-8.1.3 click-default-group-wheel-1.2.2 datasette-0.64.1 h11-0.14.0 httpcore-0.16.3 httpx-0.23.3 hupper-1.11 idna-3.4 itsdangerous-2.1.2 janus-1.0.0 mergedeep-1.3.4 pint-0.20.1 pluggy-1.0.0 python-multipart-0.0.6 rfc3986-1.5.0 sniffio-1.3.0 typing-extensions-4.5.0 uvicorn-0.20.0\r\nWARNING: Running pip as the 'root' user can result in broken permissions and conflicting behaviour with the system package manager. It is recommended to use a virtual environment instead: https://pip.pypa.io/warnings/venv\r\n\r\n[notice] A new release of pip available: 22.3 -> 23.0.1\r\n[notice] To update, run: pip install --upgrade pip\r\nRemoving intermediate container 8ccebfebebc9\r\n ---> b972c85b38bb\r\n...\r\nSuccessfully built 606b7c286d7f\r\nSuccessfully tagged gcr.io/datasette-222320/datasette-new-service:latest\r\nPUSH\r\nPushing gcr.io/datasette-222320/datasette-new-service\r\nThe push refers to repository [gcr.io/datasette-222320/datasette-new-service]\r\n667b1dc69e5e: Preparing\r\n...\r\nd8ddfcff216f: Pushed\r\nlatest: digest: sha256:452daffb2d3d7a8579c2ab39854be285155252c9428b4c1c50caac6a3a269e3f size: 2004\r\nDONE\r\n---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------\r\nID CREATE_TIME DURATION SOURCE IMAGES STATUS\r\n290f41a4-e29a-443c-a1e5-c54513c6143d 2023-03-08T20:21:03+00:00 39S gs://datasette-222320_cloudbuild/source/1678306859.271661-805303f364144b6094cc9c8532ab5133.tgz gcr.io/datasette-222320/datasette-new-service (+1 more) SUCCESS\r\nDeploying container to Cloud Run service [new-service] in project [datasette-222320] region [us-central1]\r\n\u2713 Deploying new service... Done. \r\n \u2713 Creating Revision... \r\n \u2713 Routing traffic... \r\n \u2713 Setting IAM Policy... \r\nDone. \r\nService [new-service] revision [new-service-00001-zon] has been deployed and is serving 100 percent of traffic.\r\nService URL: https://new-service-j7hipcg4aq-uc.a.run.app\r\n```\r\nhttps://new-service-j7hipcg4aq-uc.a.run.app/ was deployed successfully.", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1615862295, "label": "`publish cloudrun` reuses image tags, which can lead to very surprising deploy problems"}, "performed_via_github_app": null}, {"html_url": "https://github.com/simonw/datasette/issues/2036#issuecomment-1460810523", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/2036", "id": 1460810523, "node_id": "IC_kwDOBm6k_c5XEjMb", "user": {"value": 9599, "label": "simonw"}, "created_at": "2023-03-08T20:17:01Z", "updated_at": "2023-03-08T20:17:01Z", "author_association": "OWNER", "body": "I'm going to solve this by using the service name in that `image_id` instead:\r\n\r\n```python\r\nimage_id = f\"gcr.io/{project}/{service_name}\"\r\n```\r\nThis is a nasty bug, so I'm going to backport it to a `0.64.2` release as well.", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1615862295, "label": "`publish cloudrun` reuses image tags, which can lead to very surprising deploy problems"}, "performed_via_github_app": null}, {"html_url": "https://github.com/simonw/datasette/issues/2036#issuecomment-1460809643", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/2036", "id": 1460809643, "node_id": "IC_kwDOBm6k_c5XEi-r", "user": {"value": 9599, "label": "simonw"}, "created_at": "2023-03-08T20:16:10Z", "updated_at": "2023-03-08T20:16:10Z", "author_association": "OWNER", "body": "I think the code at fault is here:\r\n\r\nhttps://github.com/simonw/datasette/blob/1ad92a1d87d79084ebe524ed186c900ff042328c/datasette/publish/cloudrun.py#L176-L182\r\n\r\nThat name ends up defaulting to `datasette` - so multiple different projects may end up deploying to the same `image_id`.\r\n\r\nWhat I think happened in the `datasette.io` bug is that this workflow: https://github.com/simonw/simonwillisonblog-backup/blob/bfb573e96d8622ab52b22fdcd54724fe6e59fd24/.github/workflows/backup.yml and this workflow: https://github.com/simonw/datasette.io/blob/4676db5bf4a3fc9f792ee270ec0c59eb902cd2c3/.github/workflows/deploy.yml both happened to run at the exact same time.\r\n\r\nAnd so the image that was pushed to `gcr.io/datasette-222320/datasette:latest` by the `simonw/simonwillisonblog-backup` action was then deployed by the `simonw/datasette.io/` action, which broke the site.", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1615862295, "label": "`publish cloudrun` reuses image tags, which can lead to very surprising deploy problems"}, "performed_via_github_app": null}, {"html_url": "https://github.com/simonw/datasette/issues/2035#issuecomment-1460808028", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/2035", "id": 1460808028, "node_id": "IC_kwDOBm6k_c5XEilc", "user": {"value": 1176293, "label": "ar-jan"}, "created_at": "2023-03-08T20:14:47Z", "updated_at": "2023-03-08T20:14:47Z", "author_association": "NONE", "body": "+1, I have been wishing for this feature (also for use with template-sql). It was requested before here #1304.", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1615692818, "label": "Potential feature: special support for `?a=1&a=2` on the query page"}, "performed_via_github_app": null}, {"html_url": "https://github.com/simonw/datasette/pull/1999#issuecomment-1460760116", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1999", "id": 1460760116, "node_id": "IC_kwDOBm6k_c5XEW40", "user": {"value": 9599, "label": "simonw"}, "created_at": "2023-03-08T19:48:52Z", "updated_at": "2023-03-08T19:48:52Z", "author_association": "OWNER", "body": "I'm trying to get `http://127.0.0.1:8001/fixtures/compound_three_primary_keys?_next=a,d,v` to return the correct results.", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1551694938, "label": "?_extra= support (draft)"}, "performed_via_github_app": null}, {"html_url": "https://github.com/simonw/datasette/issues/2035#issuecomment-1460682625", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/2035", "id": 1460682625, "node_id": "IC_kwDOBm6k_c5XED-B", "user": {"value": 9599, "label": "simonw"}, "created_at": "2023-03-08T18:40:57Z", "updated_at": "2023-03-08T18:40:57Z", "author_association": "OWNER", "body": "Pushed that prototype to a branch: https://github.com/simonw/datasette/commit/0fe844e9adb006a0138e83102ced1329d9155c59 / https://github.com/simonw/datasette/compare/sql-list-parameters", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1615692818, "label": "Potential feature: special support for `?a=1&a=2` on the query page"}, "performed_via_github_app": null}, {"html_url": "https://github.com/simonw/datasette/issues/2035#issuecomment-1460679434", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/2035", "id": 1460679434, "node_id": "IC_kwDOBm6k_c5XEDMK", "user": {"value": 9599, "label": "simonw"}, "created_at": "2023-03-08T18:39:35Z", "updated_at": "2023-03-08T18:39:35Z", "author_association": "OWNER", "body": "I should consider the existing design of magic parameters here: https://docs.datasette.io/en/stable/sql_queries.html#magic-parameters\r\n\r\n- `_actor_*`\r\n- `_header_*`\r\n- `_cookie_`\r\n- `_now_epoch`\r\n- `_now_date_utc`\r\n- `_now_datetime_utc`\r\n- `_random_chars_*`\r\n\r\nShould this new `id__list` syntax look more like those magic parameters, or is it OK to use `name__magic` syntax here instead?", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1615692818, "label": "Potential feature: special support for `?a=1&a=2` on the query page"}, "performed_via_github_app": null}, {"html_url": "https://github.com/simonw/datasette/issues/2035#issuecomment-1460668431", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/2035", "id": 1460668431, "node_id": "IC_kwDOBm6k_c5XEAgP", "user": {"value": 9599, "label": "simonw"}, "created_at": "2023-03-08T18:35:34Z", "updated_at": "2023-03-08T18:35:34Z", "author_association": "OWNER", "body": "To implement this properly need to do the following:\r\n- Get the page to display multiple `id: [ text input here ]` fields such that re-submission works\r\n- Figure out how this should work for canned queries and for writable canned queries\r\n- Tests that cover queries, canned queries, writable canned queries\r\n\r\nAnd a bonus feature: what if the Datasette UI layer spotted `:id__list` parameters and used them to add a bit of JavaScript that allowed users to click a `+` button next to an `id` form field to add another one?\r\n\r\nAlso, when a page is re-displayed for on of these queries it could potentially add an extra form field allowing people to add another value.\r\n\r\nThough this has an annoying problem: how to tell the difference between an additional `id` input field that the user chose not to populate, v.s. one that is supposed to represent an empty string?\r\n\r\nMaybe only support multiple `id` fields for users with JavaScript in order to avoid this problem.", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1615692818, "label": "Potential feature: special support for `?a=1&a=2` on the query page"}, "performed_via_github_app": null}, {"html_url": "https://github.com/simonw/datasette/issues/2035#issuecomment-1460664619", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/2035", "id": 1460664619, "node_id": "IC_kwDOBm6k_c5XD_kr", "user": {"value": 9599, "label": "simonw"}, "created_at": "2023-03-08T18:32:29Z", "updated_at": "2023-03-08T18:32:29Z", "author_association": "OWNER", "body": "Got a prototype working:\r\n```diff\r\ndiff --git a/datasette/views/database.py b/datasette/views/database.py\r\nindex 8d289105..6f9d8a44 100644\r\n--- a/datasette/views/database.py\r\n+++ b/datasette/views/database.py\r\n@@ -226,6 +226,12 @@ class QueryView(DataView):\r\n ):\r\n db = await self.ds.resolve_database(request)\r\n database = db.name\r\n+ # Disallow x__list query string parameters\r\n+ invalid_params = [k for k in request.args if k.endswith(\"__list\")]\r\n+ if invalid_params:\r\n+ raise DatasetteError(\r\n+ \"Invalid query string parameters: {}\".format(\", \".join(invalid_params))\r\n+ )\r\n params = {key: request.args.get(key) for key in request.args}\r\n if \"sql\" in params:\r\n params.pop(\"sql\")\r\n@@ -258,6 +264,11 @@ class QueryView(DataView):\r\n for named_parameter in named_parameters\r\n if not named_parameter.startswith(\"_\")\r\n }\r\n+ # Handle any __list parameters\r\n+ for named_parameter in named_parameters:\r\n+ if named_parameter.endswith(\"__list\"):\r\n+ list_values = request.args.getlist(named_parameter[:-6])\r\n+ params[named_parameter] = json.dumps(list_values)\r\n \r\n # Set to blank string if missing from params\r\n for named_parameter in named_parameters:\r\n```\r\nThis isn't yet doing the right thing on form re-submission: it breaks because it attempts to pass through the `?id__list=` invalid parameter. But I did manage to get it to do this through careful editing of the URL:\r\n\r\n\"image\"\r\n\r\nThat was this URL: `http://127.0.0.1:8034/content?sql=select+%3Aid__list%2C*+from+releases+where+id+in+(select+value+from+json_each(%3Aid__list))&id=62642726&id=18402901&id=38714866`", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1615692818, "label": "Potential feature: special support for `?a=1&a=2` on the query page"}, "performed_via_github_app": null}, {"html_url": "https://github.com/simonw/datasette/issues/2035#issuecomment-1460659382", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/2035", "id": 1460659382, "node_id": "IC_kwDOBm6k_c5XD-S2", "user": {"value": 9599, "label": "simonw"}, "created_at": "2023-03-08T18:28:00Z", "updated_at": "2023-03-08T18:28:00Z", "author_association": "OWNER", "body": "Also: `datasette-explain` may need to be updated to understand how to handle this:\r\n\r\n`ERROR: conn=, sql = 'explain select * from releases where id in (select id from json_each(:id__list))', params = None: You did not supply a value for binding parameter :id__list.`\r\n", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1615692818, "label": "Potential feature: special support for `?a=1&a=2` on the query page"}, "performed_via_github_app": null}, {"html_url": "https://github.com/simonw/datasette/issues/2035#issuecomment-1460654136", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/2035", "id": 1460654136, "node_id": "IC_kwDOBm6k_c5XD9A4", "user": {"value": 9599, "label": "simonw"}, "created_at": "2023-03-08T18:25:46Z", "updated_at": "2023-03-08T18:25:46Z", "author_association": "OWNER", "body": "Trickiest part of the implementation here is that it needs to know to output three `id` HTML form fields on the page, such that their values are persisted when the form is submitted a second time.", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1615692818, "label": "Potential feature: special support for `?a=1&a=2` on the query page"}, "performed_via_github_app": null}, {"html_url": "https://github.com/simonw/datasette/issues/2035#issuecomment-1460639749", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/2035", "id": 1460639749, "node_id": "IC_kwDOBm6k_c5XD5gF", "user": {"value": 9599, "label": "simonw"}, "created_at": "2023-03-08T18:17:31Z", "updated_at": "2023-03-08T18:17:31Z", "author_association": "OWNER", "body": "Since we are pre-1.0 it's still OK to implement a feature that disallows `?id__list=` in the URL, but allows `:id__list` in SQL queries to reference the JSON list of parameters.\r\n\r\nSo I'm going to prototype this as the `:id__list` feature and see how it feels.", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1615692818, "label": "Potential feature: special support for `?a=1&a=2` on the query page"}, "performed_via_github_app": null}, {"html_url": "https://github.com/simonw/datasette/issues/2035#issuecomment-1460637906", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/2035", "id": 1460637906, "node_id": "IC_kwDOBm6k_c5XD5DS", "user": {"value": 9599, "label": "simonw"}, "created_at": "2023-03-08T18:16:31Z", "updated_at": "2023-03-08T18:16:31Z", "author_association": "OWNER", "body": "I'm pretty sold on this as a feature now. The main question I have is which of these options to implement:\r\n\r\n1. `?id=1&?id=2` results in `:id` in the query being `[\"1\", \"2\"]` - no additional syntax required\r\n2. `:id` in the query continues to reference just the first of those parameters - but `:id__list` (or some other custom syntax) instead gets `[\"1\", \"2\"]` - or, if the URL is `?id=1` - gets `[\"1\"]`\r\n\r\nActually on writing these out I realize that option 2 is the ONLY valid option. It's no good building a query that works against a JSON list if the user might pass just a single ID, `?id=1`, resulting in their query breaking.", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1615692818, "label": "Potential feature: special support for `?a=1&a=2` on the query page"}, "performed_via_github_app": null}, {"html_url": "https://github.com/simonw/datasette/issues/2035#issuecomment-1460632758", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/2035", "id": 1460632758, "node_id": "IC_kwDOBm6k_c5XD3y2", "user": {"value": 9599, "label": "simonw"}, "created_at": "2023-03-08T18:13:49Z", "updated_at": "2023-03-08T18:13:49Z", "author_association": "OWNER", "body": "https://github.com/rclement/datasette-dashboards/issues/54 makes the excellent point that the `\r\n \r\n \r\n \r\n \r\n\r\n\r\n\r\n```\r\nSubmitting that form with the middle two options selected navigates to: `https://www.example.com/?id=32&id=15`", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1615692818, "label": "Potential feature: special support for `?a=1&a=2` on the query page"}, "performed_via_github_app": null}, {"html_url": "https://github.com/simonw/datasette/issues/2035#issuecomment-1460628199", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/2035", "id": 1460628199, "node_id": "IC_kwDOBm6k_c5XD2rn", "user": {"value": 9599, "label": "simonw"}, "created_at": "2023-03-08T18:11:31Z", "updated_at": "2023-03-08T18:11:31Z", "author_association": "OWNER", "body": "One variant on this idea: maybe you have to specify in your query that you want it to be the JSON list version, not the single item (first `?id=` parameter version)? Maybe with syntax like this:\r\n\r\n where id in (select value from json_each(:id__list))\r\n\r\nDatasette would automatically pass `{\"id\": \"11\", \"id__list\": '[\"11\", \"32\", \"62\"]'}` as arguments to the `db.execute()` method, if the page was called with `?id=11&id=32&id=62`.\r\n\r\nThis is more explicit, though the syntax is a bit uglier (maybe there's a nicer design for this?). I also worry about `?id__list=` conflicting with this, but I think that's a risk I can take - tell people not to do that, or even block `?id__list=` style parameters entirely.", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1615692818, "label": "Potential feature: special support for `?a=1&a=2` on the query page"}, "performed_via_github_app": null}, {"html_url": "https://github.com/simonw/datasette/issues/2035#issuecomment-1460621871", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/2035", "id": 1460621871, "node_id": "IC_kwDOBm6k_c5XD1Iv", "user": {"value": 9599, "label": "simonw"}, "created_at": "2023-03-08T18:08:25Z", "updated_at": "2023-03-08T18:09:04Z", "author_association": "OWNER", "body": "My current preferred solution is to lean into SQLite's JSON support.\r\n\r\nWhat if the query page spotted `?id=11&id=32&id=62` and turned that into a JSON string called `:id:` with a value of `[\"11\", \"32\", \"62\"]`?\r\n\r\nNote that this is still a string, not a list. This avoids a nasty problem that occurred in PHP world, where `?id[]=1&id[]=2` would result in an actual PHP array object, which often broke underlying code that had expected `$_GET[\"id\"]` to be a string, not an array.\r\n\r\nSo in a query you'd be able to do this:\r\n\r\n where id in (select value from json_each(:id))\r\n\r\nAnd then call it with `?id=11&id=32&id=62`.", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1615692818, "label": "Potential feature: special support for `?a=1&a=2` on the query page"}, "performed_via_github_app": null}, {"html_url": "https://github.com/simonw/datasette/issues/2035#issuecomment-1460618433", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/2035", "id": 1460618433, "node_id": "IC_kwDOBm6k_c5XD0TB", "user": {"value": 9599, "label": "simonw"}, "created_at": "2023-03-08T18:06:34Z", "updated_at": "2023-03-08T18:06:34Z", "author_association": "OWNER", "body": "One way to do this would be to dynamically generate the `where id in (?, ?, ?)` with the correct number of question marks, then feed in a list from `request.args.getlist(\"id\")` - but that would require rewriting the SQL query text to add those question marks.", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1615692818, "label": "Potential feature: special support for `?a=1&a=2` on the query page"}, "performed_via_github_app": null}, {"html_url": "https://github.com/simonw/datasette/issues/2027#issuecomment-1459455356", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/2027", "id": 1459455356, "node_id": "IC_kwDOBm6k_c5W_YV8", "user": {"value": 1350673, "label": "dmick"}, "created_at": "2023-03-08T04:42:22Z", "updated_at": "2023-03-08T04:42:22Z", "author_association": "NONE", "body": "I managed to make it work by using nginx's 'exact match' (=) combined with 'prefix match'; that is, match explicitly on `/`, and redirect to `//`, and then have the normal ProxyPath for the unadorned (prefix-matching) `/`.\r\n\r\n```\r\n location = / {\r\n return 302 //
;\r\n }\r\n location / {\r\n proxy_pass http://127.0.0.1:8001/;\r\n proxy_set_header Host $host;\r\n }\r\n```\r\n", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1590183272, "label": "How to redirect from \"/\" to a specific db/table"}, "performed_via_github_app": null}, {"html_url": "https://github.com/simonw/datasette/pull/2031#issuecomment-1457243738", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/2031", "id": 1457243738, "node_id": "IC_kwDOBm6k_c5W28Za", "user": {"value": 82332573, "label": "tmcl-it"}, "created_at": "2023-03-07T00:05:25Z", "updated_at": "2023-03-07T00:12:09Z", "author_association": "NONE", "body": "I've implemented the test (thanks for pointing me in the right direction!).\r\n\r\nAt [tmcl-it/datasette:0.64.1+row-view-expand-labels](https://github.com/tmcl-it/datasette/tree/0.64.1%2Brow-view-expand-labels) I also have a variant of this patch that applies to the 0.64.x branch. Please let me know if you'd be interested in merging that as well and I'll open another PR.", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1605481359, "label": "Expand foreign key references in row view as well"}, "performed_via_github_app": null}, {"html_url": "https://github.com/simonw/datasette/pull/2031#issuecomment-1457016788", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/2031", "id": 1457016788, "node_id": "IC_kwDOBm6k_c5W2E_U", "user": {"value": 22429695, "label": "codecov[bot]"}, "created_at": "2023-03-06T21:18:18Z", "updated_at": "2023-03-06T23:32:25Z", "author_association": "NONE", "body": "# [Codecov](https://codecov.io/gh/simonw/datasette/pull/2031?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Simon+Willison) Report\nPatch coverage: **`97.72`**% and no project coverage change\n> Comparison is base [(`a53b893`)](https://codecov.io/gh/simonw/datasette/commit/a53b893c46453f35decc8c145c138671cee6140c?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Simon+Willison) 92.11% compared to head [(`c8a2904`)](https://codecov.io/gh/simonw/datasette/pull/2031?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Simon+Willison) 92.11%.\n\n> :exclamation: Current head c8a2904 differs from pull request most recent head 98c7206. Consider uploading reports for the commit 98c7206 to get more accurate results\n\n
Additional details and impacted files\n\n\n```diff\n@@ Coverage Diff @@\n## main #2031 +/- ##\n=======================================\n Coverage 92.11% 92.11% \n=======================================\n Files 38 38 \n Lines 5555 5556 +1 \n=======================================\n+ Hits 5117 5118 +1 \n Misses 438 438 \n```\n\n\n| [Impacted Files](https://codecov.io/gh/simonw/datasette/pull/2031?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Simon+Willison) | Coverage \u0394 | |\n|---|---|---|\n| [datasette/views/table.py](https://codecov.io/gh/simonw/datasette/pull/2031?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Simon+Willison#diff-ZGF0YXNldHRlL3ZpZXdzL3RhYmxlLnB5) | `92.57% <97.56%> (\u00f8)` | |\n| [datasette/views/base.py](https://codecov.io/gh/simonw/datasette/pull/2031?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Simon+Willison#diff-ZGF0YXNldHRlL3ZpZXdzL2Jhc2UucHk=) | `95.17% <100.00%> (\u00f8)` | |\n| [datasette/views/row.py](https://codecov.io/gh/simonw/datasette/pull/2031?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Simon+Willison#diff-ZGF0YXNldHRlL3ZpZXdzL3Jvdy5weQ==) | `87.93% <100.00%> (+0.10%)` | :arrow_up: |\n\nHelp us with your feedback. Take ten seconds to tell us [how you rate us](https://about.codecov.io/nps?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Simon+Willison). Have a feature suggestion? [Share it here.](https://app.codecov.io/gh/feedback/?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Simon+Willison)\n\n
\n\n[:umbrella: View full report at Codecov](https://codecov.io/gh/simonw/datasette/pull/2031?src=pr&el=continue&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Simon+Willison). \n:loudspeaker: Do you have feedback about the report comment? [Let us know in this issue](https://about.codecov.io/codecov-pr-comment-feedback/?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Simon+Willison).\n", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1605481359, "label": "Expand foreign key references in row view as well"}, "performed_via_github_app": null}, {"html_url": "https://github.com/simonw/datasette/issues/2033#issuecomment-1457172180", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/2033", "id": 1457172180, "node_id": "IC_kwDOBm6k_c5W2q7U", "user": {"value": 25778, "label": "eyeseast"}, "created_at": "2023-03-06T22:54:52Z", "updated_at": "2023-03-06T22:54:52Z", "author_association": "CONTRIBUTOR", "body": "This would be a nice feature to have with `datasette publish` too.", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1612296210, "label": "`datasette install -r requirements.txt`"}, "performed_via_github_app": null}]