{"id": 633578769, "node_id": "MDU6SXNzdWU2MzM1Nzg3Njk=", "number": 811, "title": "Support \"allow\" block on root, databases and tables, not just queries", "user": {"value": 9599, "label": "simonw"}, "state": "closed", "locked": 0, "assignee": null, "milestone": {"value": 5512395, "label": "Datasette 0.44"}, "comments": 16, "created_at": "2020-06-07T17:01:09Z", "updated_at": "2020-06-08T19:34:00Z", "closed_at": "2020-06-08T19:32:36Z", "author_association": "OWNER", "pull_request": null, "body": "No reason not to expand the \"allow\" mechanism [described here](https://github.com/simonw/datasette/blob/86dec9e8fffd6c4efec928ae9b5713748dec7e74/docs/authentication.rst#permissions-for-canned-queries) to the root of `metadata.json` plus to databases and tables.\r\n\r\nRefs #810 and #800.\r\n\r\n```json\r\n{\r\n \"databases\": {\r\n \"mydatabase\": {\r\n \"allow\": {\r\n \"id\": [\"root\"]\r\n }\r\n }\r\n }\r\n}\r\n```\r\n\r\nTODO:\r\n\r\n- [x] Instance level\r\n- [x] Database level\r\n- [x] Table level\r\n- [x] Query level\r\n- [x] Affects list of queries\r\n- [x] Affects list of tables on database page\r\n- [x] Affects truncated list of tables on index page\r\n- [x] Affects list of SQL views on database page\r\n- [x] Affects list of databases on index page\r\n- [x] Show \ud83d\udd12 in header on index page for private instances\r\n- [x] Show \ud83d\udd12 in header on private database page\r\n- [x] Show \ud83d\udd12 in header on private table page\r\n- [x] Show \ud83d\udd12 in header on private query page\r\n- [x] Move `assert_permissions_checked()` calls from `test_html.py` to `test_permissions.py`\r\n- [x] Update documentation", "repo": {"value": 107914493, "label": "datasette"}, "type": "issue", "active_lock_reason": null, "performed_via_github_app": null, "reactions": "{\"url\": \"https://api.github.com/repos/simonw/datasette/issues/811/reactions\", \"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "draft": null, "state_reason": "completed"} {"id": 628499086, "node_id": "MDU6SXNzdWU2Mjg0OTkwODY=", "number": 790, "title": "\"flash messages\" mechanism", "user": {"value": 9599, "label": "simonw"}, "state": "closed", "locked": 0, "assignee": null, "milestone": {"value": 5512395, "label": "Datasette 0.44"}, "comments": 20, "created_at": "2020-06-01T14:55:44Z", "updated_at": "2020-06-08T19:33:59Z", "closed_at": "2020-06-02T21:14:03Z", "author_association": "OWNER", "pull_request": null, "body": "> Passing `?_success` like this isn't necessarily the best approach. Potential improvements include:\r\n> \r\n> - Signing this message so it can't be tampered with (I could generate a signing secret on startup)\r\n> - Using a cookie with a temporary flash message in it instead\r\n> - Using HTML5 history API to remove the `?_success=` from the URL bar when the user lands on the page\r\n> \r\n> If I add an option to redirect the user to another page after success I may need a mechanism to show a flash message on that page as well, in which case I'll need a general flash message solution that works for any page.\r\n\r\n_Originally posted by @simonw in https://github.com/simonw/datasette/pull/703_", "repo": {"value": 107914493, "label": "datasette"}, "type": "issue", "active_lock_reason": null, "performed_via_github_app": null, "reactions": "{\"url\": \"https://api.github.com/repos/simonw/datasette/issues/790/reactions\", \"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "draft": null, "state_reason": "completed"} {"id": 634783573, "node_id": "MDU6SXNzdWU2MzQ3ODM1NzM=", "number": 816, "title": "Come up with a new example for extra_template_vars plugin", "user": {"value": 9599, "label": "simonw"}, "state": "closed", "locked": 0, "assignee": null, "milestone": {"value": 5512395, "label": "Datasette 0.44"}, "comments": 2, "created_at": "2020-06-08T16:57:59Z", "updated_at": "2020-06-08T19:06:44Z", "closed_at": "2020-06-08T19:06:11Z", "author_association": "OWNER", "pull_request": null, "body": "This example is obsolete, it's from a time before `request.actor` and authentication as a built-in concept (#699):\r\nhttps://github.com/simonw/datasette/blob/0c064c5fe220b7b3d8dcf85b02b4e60452c47232/docs/plugins.rst#L696-L700\r\n\r\nhttps://github.com/simonw/datasette/blob/0c064c5fe220b7b3d8dcf85b02b4e60452c47232/docs/plugins.rst#extra_template_varstemplate-database-table-view_name-request-datasette", "repo": {"value": 107914493, "label": "datasette"}, "type": "issue", "active_lock_reason": null, "performed_via_github_app": null, "reactions": "{\"url\": \"https://api.github.com/repos/simonw/datasette/issues/816/reactions\", \"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "draft": null, "state_reason": "completed"} {"id": 634844634, "node_id": "MDU6SXNzdWU2MzQ4NDQ2MzQ=", "number": 817, "title": "Drop resource_type from permission_allowed system", "user": {"value": 9599, "label": "simonw"}, "state": "closed", "locked": 0, "assignee": null, "milestone": null, "comments": 1, "created_at": "2020-06-08T18:41:37Z", "updated_at": "2020-06-08T19:00:12Z", "closed_at": "2020-06-08T19:00:12Z", "author_association": "OWNER", "pull_request": null, "body": "Current signature:\r\n\r\n permission_allowed(datasette, actor, action, resource_type, resource_identifier)\r\n\r\nIt turns out the `resource_type` is always the same thing for any given action, so it's not actually useful. I'm going to drop it.\r\n\r\nNew signature will be:\r\n\r\n permission_allowed(datasette, actor, action, resource)\r\n\r\nRefs #811.", "repo": {"value": 107914493, "label": "datasette"}, "type": "issue", "active_lock_reason": null, "performed_via_github_app": null, "reactions": "{\"url\": \"https://api.github.com/repos/simonw/datasette/issues/817/reactions\", \"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "draft": null, "state_reason": "completed"} {"id": 396215043, "node_id": "MDU6SXNzdWUzOTYyMTUwNDM=", "number": 395, "title": "Find a cleaner pattern for fixtures with arguments", "user": {"value": 9599, "label": "simonw"}, "state": "closed", "locked": 0, "assignee": null, "milestone": null, "comments": 1, "created_at": "2019-01-06T00:31:22Z", "updated_at": "2020-06-07T21:23:22Z", "closed_at": "2020-06-07T21:23:22Z", "author_association": "OWNER", "pull_request": null, "body": "A lot of Datasette tests look like this:\r\n\r\nhttps://github.com/simonw/datasette/blob/b65d97792a53f78cb14b226231063209d22c4602/tests/test_api.py#L438-L444\r\n\r\nThe loop here isn't actually expected to loop - it's there because the `make_app_client` function yields a value and then cleans it up afterwards.\r\n\r\nThis pattern works, but it is a little confusing. It would be nice to replace it with something less strange looking.\r\n\r\nThe answer may be to switch to the \"factories as fixtures\" pattern described here: https://docs.pytest.org/en/latest/fixture.html#factories-as-fixtures\r\n\r\nIn particular some variant of this example:\r\n\r\n```\r\n@pytest.fixture\r\ndef make_customer_record():\r\n\r\n created_records = []\r\n\r\n def _make_customer_record(name):\r\n record = models.Customer(name=name, orders=[])\r\n created_records.append(record)\r\n return record\r\n\r\n yield _make_customer_record\r\n\r\n for record in created_records:\r\n record.destroy()\r\n\r\n\r\ndef test_customer_records(make_customer_record):\r\n customer_1 = make_customer_record(\"Lisa\")\r\n customer_2 = make_customer_record(\"Mike\")\r\n customer_3 = make_customer_record(\"Meredith\")\r\n```", "repo": {"value": 107914493, "label": "datasette"}, "type": "issue", "active_lock_reason": null, "performed_via_github_app": null, "reactions": "{\"url\": \"https://api.github.com/repos/simonw/datasette/issues/395/reactions\", \"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "draft": null, "state_reason": "completed"} {"id": 633066114, "node_id": "MDU6SXNzdWU2MzMwNjYxMTQ=", "number": 810, "title": "Refactor permission check for canned query", "user": {"value": 9599, "label": "simonw"}, "state": "closed", "locked": 0, "assignee": null, "milestone": {"value": 5512395, "label": "Datasette 0.44"}, "comments": 1, "created_at": "2020-06-07T05:33:05Z", "updated_at": "2020-06-07T17:03:15Z", "closed_at": "2020-06-07T17:03:15Z", "author_association": "OWNER", "pull_request": null, "body": "This code here (TODO is follow-on from #808).\r\n\r\nhttps://github.com/simonw/datasette/blob/86dec9e8fffd6c4efec928ae9b5713748dec7e74/datasette/views/database.py#L133-L142\r\n\r\nI can improve this with extra code in https://github.com/simonw/datasette/blob/86dec9e8fffd6c4efec928ae9b5713748dec7e74/datasette/default_permissions.py", "repo": {"value": 107914493, "label": "datasette"}, "type": "issue", "active_lock_reason": null, "performed_via_github_app": null, "reactions": "{\"url\": \"https://api.github.com/repos/simonw/datasette/issues/810/reactions\", \"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "draft": null, "state_reason": "completed"} {"id": 631931408, "node_id": "MDU6SXNzdWU2MzE5MzE0MDg=", "number": 800, "title": "Canned query permissions mechanism", "user": {"value": 9599, "label": "simonw"}, "state": "closed", "locked": 0, "assignee": null, "milestone": {"value": 5512395, "label": "Datasette 0.44"}, "comments": 14, "created_at": "2020-06-05T20:28:21Z", "updated_at": "2020-06-07T16:22:53Z", "closed_at": "2020-06-07T16:22:53Z", "author_association": "OWNER", "pull_request": null, "body": "> Idea: default is anyone can execute a query.\r\n> \r\n> Or you can specify the following:\r\n> \r\n> ```json\r\n> \r\n> {\r\n> \"databases\": {\r\n> \"my-database\": {\r\n> \"queries\": {\r\n> \"add_twitter_handle\": {\r\n> \"sql\": \"insert into twitter_handles (username) values (:username)\",\r\n> \"write\": true,\r\n> \"allow\": {\r\n> \"id\": [\"simon\"],\r\n> \"role\": [\"staff\"]\r\n> }\r\n> }\r\n> }\r\n> }\r\n> }\r\n> }\r\n> ```\r\n> These get matched against the actor JSON. If any of the fields in any of the keys of `\"allow\"` match a key on the actor, the query is allowed.\r\n> \r\n> `\"id\": \"*\"` matches any actor with an `id` key.\r\n\r\n_Originally posted by @simonw in https://github.com/simonw/datasette/issues/698#issuecomment-639784651_", "repo": {"value": 107914493, "label": "datasette"}, "type": "issue", "active_lock_reason": null, "performed_via_github_app": null, "reactions": "{\"url\": \"https://api.github.com/repos/simonw/datasette/issues/800/reactions\", \"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "draft": null, "state_reason": "completed"} {"id": 632918799, "node_id": "MDU6SXNzdWU2MzI5MTg3OTk=", "number": 808, "title": "Permission check for every view in Datasette (plus docs)", "user": {"value": 9599, "label": "simonw"}, "state": "closed", "locked": 0, "assignee": null, "milestone": {"value": 5512395, "label": "Datasette 0.44"}, "comments": 2, "created_at": "2020-06-07T01:59:23Z", "updated_at": "2020-06-07T05:30:49Z", "closed_at": "2020-06-07T05:30:49Z", "author_association": "OWNER", "pull_request": null, "body": "Every view in Datasette should perform a permission check to see if the current user/actor is allowed to view that page.\r\n\r\nThis permission check will default to allowed, but having this check will allow plugins to lock down access selectively or even to everything in a Datasette instance.", "repo": {"value": 107914493, "label": "datasette"}, "type": "issue", "active_lock_reason": null, "performed_via_github_app": null, "reactions": "{\"url\": \"https://api.github.com/repos/simonw/datasette/issues/808/reactions\", \"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "draft": null, "state_reason": "completed"} {"id": 582517965, "node_id": "MDU6SXNzdWU1ODI1MTc5NjU=", "number": 698, "title": "Ability for a canned query to write to the database", "user": {"value": 9599, "label": "simonw"}, "state": "closed", "locked": 0, "assignee": null, "milestone": {"value": 5512395, "label": "Datasette 0.44"}, "comments": 26, "created_at": "2020-03-16T18:31:59Z", "updated_at": "2020-06-06T19:43:49Z", "closed_at": "2020-06-06T19:43:48Z", "author_association": "OWNER", "pull_request": null, "body": "Canned queries are currently read-only: https://datasette.readthedocs.io/en/0.38/sql_queries.html#canned-queries\r\n\r\nAdd a `\"write\": true` option to their definition in `metadata.json` which turns them into queries that are submitted via POST and send their queries to the write queue.\r\n\r\nThen they can be used as a really quick way to define a writable interface and JSON API!", "repo": {"value": 107914493, "label": "datasette"}, "type": "issue", "active_lock_reason": null, "performed_via_github_app": null, "reactions": "{\"url\": \"https://api.github.com/repos/simonw/datasette/issues/698/reactions\", \"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "draft": null, "state_reason": "completed"} {"id": 582526961, "node_id": "MDU6SXNzdWU1ODI1MjY5NjE=", "number": 699, "title": "Authentication (and permissions) as a core concept", "user": {"value": 9599, "label": "simonw"}, "state": "closed", "locked": 0, "assignee": null, "milestone": {"value": 5512395, "label": "Datasette 0.44"}, "comments": 40, "created_at": "2020-03-16T18:48:00Z", "updated_at": "2020-06-06T19:42:11Z", "closed_at": "2020-06-06T19:42:11Z", "author_association": "OWNER", "pull_request": null, "body": "Right now Datasette authentication is provided exclusively by plugins:\r\n\r\n* https://github.com/simonw/datasette-auth-github\r\n* https://github.com/simonw/datasette-auth-existing-cookies\r\n\r\nThis is an all-or-nothing approach: either your Datasette instance requires authentication at the top level or it does not.\r\n\r\nBut... as I build new plugins like https://github.com/simonw/datasette-configure-fts and https://github.com/simonw/datasette-edit-tables I increasingly have individual features which should be reserved for logged-in users while still wanting other parts of Datasette to be open to all.\r\n\r\nThis is too much for plugins to own independently of Datasette core. Datasette needs to ship a single \"user is authenticated\" concept (independent of how users actually sign in) so that different plugins can integrate with it.", "repo": {"value": 107914493, "label": "datasette"}, "type": "issue", "active_lock_reason": null, "performed_via_github_app": null, "reactions": "{\"url\": \"https://api.github.com/repos/simonw/datasette/issues/699/reactions\", \"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "draft": null, "state_reason": "completed"} {"id": 632645865, "node_id": "MDExOlB1bGxSZXF1ZXN0NDI5MzY2NjQx", "number": 803, "title": "Canned query permissions", "user": {"value": 9599, "label": "simonw"}, "state": "closed", "locked": 0, "assignee": null, "milestone": null, "comments": 0, "created_at": "2020-06-06T18:20:00Z", "updated_at": "2020-06-06T19:40:21Z", "closed_at": "2020-06-06T19:40:20Z", "author_association": "OWNER", "pull_request": "simonw/datasette/pulls/803", "body": "Refs #800. Closes #786", "repo": {"value": 107914493, "label": "datasette"}, "type": "pull", "active_lock_reason": null, "performed_via_github_app": null, "reactions": "{\"url\": \"https://api.github.com/repos/simonw/datasette/issues/803/reactions\", \"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "draft": 0, "state_reason": null} {"id": 628087971, "node_id": "MDU6SXNzdWU2MjgwODc5NzE=", "number": 786, "title": "Documentation page describing Datasette's authentication system", "user": {"value": 9599, "label": "simonw"}, "state": "closed", "locked": 0, "assignee": null, "milestone": {"value": 5512395, "label": "Datasette 0.44"}, "comments": 2, "created_at": "2020-06-01T01:10:06Z", "updated_at": "2020-06-06T19:40:20Z", "closed_at": "2020-06-06T19:40:20Z", "author_association": "OWNER", "pull_request": null, "body": "_Originally posted by @simonw in https://github.com/simonw/datasette/issues/699#issuecomment-636562999_", "repo": {"value": 107914493, "label": "datasette"}, "type": "issue", "active_lock_reason": null, "performed_via_github_app": null, "reactions": "{\"url\": \"https://api.github.com/repos/simonw/datasette/issues/786/reactions\", \"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "draft": null, "state_reason": "completed"} {"id": 629524205, "node_id": "MDU6SXNzdWU2Mjk1MjQyMDU=", "number": 793, "title": "CSRF protection for /-/messages tool and writable canned queries", "user": {"value": 9599, "label": "simonw"}, "state": "closed", "locked": 0, "assignee": null, "milestone": {"value": 5512395, "label": "Datasette 0.44"}, "comments": 3, "created_at": "2020-06-02T21:22:21Z", "updated_at": "2020-06-06T00:43:41Z", "closed_at": "2020-06-05T19:05:59Z", "author_association": "OWNER", "pull_request": null, "body": "> The `/-/messages` debug tool will need CSRF protection or people will be able to add messages using a hidden form on another website.\r\n_Originally posted by @simonw in https://github.com/simonw/datasette/issues/790#issuecomment-637790860_", "repo": {"value": 107914493, "label": "datasette"}, "type": "issue", "active_lock_reason": null, "performed_via_github_app": null, "reactions": "{\"url\": \"https://api.github.com/repos/simonw/datasette/issues/793/reactions\", \"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "draft": null, "state_reason": "completed"} {"id": 631300342, "node_id": "MDExOlB1bGxSZXF1ZXN0NDI4MjEyNDIx", "number": 798, "title": "CSRF protection", "user": {"value": 9599, "label": "simonw"}, "state": "closed", "locked": 0, "assignee": null, "milestone": {"value": 5512395, "label": "Datasette 0.44"}, "comments": 5, "created_at": "2020-06-05T04:22:35Z", "updated_at": "2020-06-06T00:43:41Z", "closed_at": "2020-06-05T19:05:58Z", "author_association": "OWNER", "pull_request": "simonw/datasette/pulls/798", "body": "Refs #793", "repo": {"value": 107914493, "label": "datasette"}, "type": "pull", "active_lock_reason": null, "performed_via_github_app": null, "reactions": "{\"url\": \"https://api.github.com/repos/simonw/datasette/issues/798/reactions\", \"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "draft": 0, "state_reason": null} {"id": 628025100, "node_id": "MDU6SXNzdWU2MjgwMjUxMDA=", "number": 785, "title": "Datasette secret mechanism - initially for signed cookies", "user": {"value": 9599, "label": "simonw"}, "state": "closed", "locked": 0, "assignee": null, "milestone": {"value": 5512395, "label": "Datasette 0.44"}, "comments": 11, "created_at": "2020-05-31T19:14:52Z", "updated_at": "2020-06-06T00:43:40Z", "closed_at": "2020-06-01T00:18:40Z", "author_association": "OWNER", "pull_request": null, "body": "See comment in https://github.com/simonw/datasette/issues/784#issuecomment-636514974\r\n\r\nDatasette needs to be able to set signed cookies - which means it needs a mechanism for safely handling a signing secret.\r\n\r\nSince Datasette is a long-running process the default behaviour here can be to create a random secret on startup. This means that if the server restarts any signed cookies will be invalidated.\r\n\r\nIf the user wants a persistent secret they'll have to generate it themselves - maybe by setting an environment variable?", "repo": {"value": 107914493, "label": "datasette"}, "type": "issue", "active_lock_reason": null, "performed_via_github_app": null, "reactions": "{\"url\": \"https://api.github.com/repos/simonw/datasette/issues/785/reactions\", \"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "draft": null, "state_reason": "completed"} {"id": 628121234, "node_id": "MDU6SXNzdWU2MjgxMjEyMzQ=", "number": 788, "title": " /-/permissions debugging tool", "user": {"value": 9599, "label": "simonw"}, "state": "closed", "locked": 0, "assignee": null, "milestone": {"value": 5512395, "label": "Datasette 0.44"}, "comments": 2, "created_at": "2020-06-01T03:13:47Z", "updated_at": "2020-06-06T00:43:40Z", "closed_at": "2020-06-01T05:01:01Z", "author_association": "OWNER", "pull_request": null, "body": "> Debugging tool idea: `/-/permissions` page which shows you the actor and lets you type in the strings for `action`, `resource_type` and `resource_identifier` - then shows you EVERY plugin hook that would have executed and what it would have said, plus when the chain would have terminated.\r\n>\r\n> Bonus: if you're logged in as the `root` user (or a user that matches some kind of permission check, maybe a check for `permissions_debug`) you get to see a rolling log of the last 30 permission checks and what the results were across the whole of Datasette. This should make figuring out permissions policies a whole lot easier.\r\n\r\n_Originally posted by @simonw in https://github.com/simonw/datasette/issues/699#issuecomment-636576603_", "repo": {"value": 107914493, "label": "datasette"}, "type": "issue", "active_lock_reason": null, "performed_via_github_app": null, "reactions": "{\"url\": \"https://api.github.com/repos/simonw/datasette/issues/788/reactions\", \"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "draft": null, "state_reason": "completed"} {"id": 632056825, "node_id": "MDU6SXNzdWU2MzIwNTY4MjU=", "number": 802, "title": "\"datasette plugins\" command is broken", "user": {"value": 9599, "label": "simonw"}, "state": "closed", "locked": 0, "assignee": null, "milestone": null, "comments": 1, "created_at": "2020-06-05T23:33:01Z", "updated_at": "2020-06-05T23:46:43Z", "closed_at": "2020-06-05T23:46:43Z", "author_association": "OWNER", "pull_request": null, "body": "I broke it in https://github.com/simonw/datasette/commit/a7137dfe069e5fceca56f78631baebd4a6a19967 - and it turns out there was no test coverage so I didn't realize it was broken.", "repo": {"value": 107914493, "label": "datasette"}, "type": "issue", "active_lock_reason": null, "performed_via_github_app": null, "reactions": "{\"url\": \"https://api.github.com/repos/simonw/datasette/issues/802/reactions\", \"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "draft": null, "state_reason": "completed"} {"id": 631789422, "node_id": "MDU6SXNzdWU2MzE3ODk0MjI=", "number": 799, "title": "TestResponse needs to handle multiple set-cookie headers", "user": {"value": 9599, "label": "simonw"}, "state": "closed", "locked": 0, "assignee": null, "milestone": null, "comments": 2, "created_at": "2020-06-05T17:39:52Z", "updated_at": "2020-06-05T18:34:10Z", "closed_at": "2020-06-05T18:34:10Z", "author_association": "OWNER", "pull_request": null, "body": "Seeing this test failure on #798:\r\n```\r\n_______________________ test_auth_token _______________________\r\napp_client = \r\n def test_auth_token(app_client):\r\n \"The /-/auth-token endpoint sets the correct cookie\"\r\n assert app_client.ds._root_token is not None\r\n path = \"/-/auth-token?token={}\".format(app_client.ds._root_token)\r\n response = app_client.get(path, allow_redirects=False,)\r\n assert 302 == response.status\r\n assert \"/\" == response.headers[\"Location\"]\r\n> assert {\"id\": \"root\"} == app_client.ds.unsign(response.cookies[\"ds_actor\"], \"actor\")\r\nE KeyError: 'ds_actor'\r\ndatasette/tests/test_auth.py:12: KeyError\r\n```\r\nIt looks like that's happening because the ASGI middleware is adding another set-cookie header - but those two set-cookie headers are combined into one when the TestResponse is constructed:\r\n\r\nhttps://github.com/simonw/datasette/blob/0c064c5fe220b7b3d8dcf85b02b4e60452c47232/tests/fixtures.py#L113-L127", "repo": {"value": 107914493, "label": "datasette"}, "type": "issue", "active_lock_reason": null, "performed_via_github_app": null, "reactions": "{\"url\": \"https://api.github.com/repos/simonw/datasette/issues/799/reactions\", \"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "draft": null, "state_reason": "completed"} {"id": 570301333, "node_id": "MDU6SXNzdWU1NzAzMDEzMzM=", "number": 684, "title": "Add documentation on Database introspection methods to internals.rst", "user": {"value": 9599, "label": "simonw"}, "state": "closed", "locked": 0, "assignee": null, "milestone": {"value": 3268330, "label": "Datasette 1.0"}, "comments": 4, "created_at": "2020-02-25T04:20:24Z", "updated_at": "2020-06-04T18:56:15Z", "closed_at": "2020-05-30T18:40:39Z", "author_association": "OWNER", "pull_request": null, "body": "`internals.rst` will be landing as part of #683", "repo": {"value": 107914493, "label": "datasette"}, "type": "issue", "active_lock_reason": null, "performed_via_github_app": null, "reactions": "{\"url\": \"https://api.github.com/repos/simonw/datasette/issues/684/reactions\", \"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "draft": null, "state_reason": "completed"} {"id": 275082158, "node_id": "MDU6SXNzdWUyNzUwODIxNTg=", "number": 119, "title": "Build an \"export this data to google sheets\" plugin", "user": {"value": 9599, "label": "simonw"}, "state": "closed", "locked": 0, "assignee": null, "milestone": null, "comments": 1, "created_at": "2017-11-18T14:14:51Z", "updated_at": "2020-06-04T18:46:40Z", "closed_at": "2020-06-04T18:46:39Z", "author_association": "OWNER", "pull_request": null, "body": "Inspired by https://github.com/kren1/tosheets\r\n\r\nIt should be a plug-in because I'd like to keep all interactions with proprietary / non-open-source software encapsulated in plugins rather than shipped as part of core.", "repo": {"value": 107914493, "label": "datasette"}, "type": "issue", "active_lock_reason": null, "performed_via_github_app": null, "reactions": "{\"url\": \"https://api.github.com/repos/simonw/datasette/issues/119/reactions\", \"total_count\": 1, \"+1\": 1, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "draft": null, "state_reason": "completed"} {"id": 629595228, "node_id": "MDExOlB1bGxSZXF1ZXN0NDI2ODkxNDcx", "number": 796, "title": "New WIP writable canned queries", "user": {"value": 9599, "label": "simonw"}, "state": "closed", "locked": 0, "assignee": null, "milestone": {"value": 3268330, "label": "Datasette 1.0"}, "comments": 9, "created_at": "2020-06-03T00:08:00Z", "updated_at": "2020-06-03T15:16:52Z", "closed_at": "2020-06-03T15:16:50Z", "author_association": "OWNER", "pull_request": "simonw/datasette/pulls/796", "body": "Refs #698. Replaces #703\r\n\r\nStill todo:\r\n\r\n- [x] Unit tests\r\n- ~~Figure out `.json` mode~~\r\n- [x] Flash message solution\r\n- ~~CSRF protection~~\r\n- [x] Better error message display on errors\r\n- [x] Documentation\r\n- ~~Maybe widgets?~~ I'll do these later", "repo": {"value": 107914493, "label": "datasette"}, "type": "pull", "active_lock_reason": null, "performed_via_github_app": null, "reactions": "{\"url\": \"https://api.github.com/repos/simonw/datasette/issues/796/reactions\", \"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "draft": 0, "state_reason": null} {"id": 585597133, "node_id": "MDExOlB1bGxSZXF1ZXN0MzkxOTI0NTA5", "number": 703, "title": "WIP implementation of writable canned queries", "user": {"value": 9599, "label": "simonw"}, "state": "closed", "locked": 0, "assignee": null, "milestone": null, "comments": 3, "created_at": "2020-03-21T22:23:51Z", "updated_at": "2020-06-03T00:08:14Z", "closed_at": "2020-06-02T23:57:35Z", "author_association": "OWNER", "pull_request": "simonw/datasette/pulls/703", "body": "Refs #698.", "repo": {"value": 107914493, "label": "datasette"}, "type": "pull", "active_lock_reason": null, "performed_via_github_app": null, "reactions": "{\"url\": \"https://api.github.com/repos/simonw/datasette/issues/703/reactions\", \"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "draft": 1, "state_reason": null} {"id": 629535669, "node_id": "MDU6SXNzdWU2Mjk1MzU2Njk=", "number": 794, "title": "Show hooks implemented by each plugin on /-/plugins", "user": {"value": 9599, "label": "simonw"}, "state": "closed", "locked": 0, "assignee": null, "milestone": {"value": 3268330, "label": "Datasette 1.0"}, "comments": 2, "created_at": "2020-06-02T21:44:38Z", "updated_at": "2020-06-02T22:30:17Z", "closed_at": "2020-06-02T21:50:10Z", "author_association": "OWNER", "pull_request": null, "body": "e.g.\r\n```json\r\n {\r\n \"name\": \"qs_actor.py\",\r\n \"static\": false,\r\n \"templates\": false,\r\n \"version\": null,\r\n \"hooks\": [\r\n \"actor_from_request\"\r\n ]\r\n }\r\n```", "repo": {"value": 107914493, "label": "datasette"}, "type": "issue", "active_lock_reason": null, "performed_via_github_app": null, "reactions": "{\"url\": \"https://api.github.com/repos/simonw/datasette/issues/794/reactions\", \"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "draft": null, "state_reason": "completed"} {"id": 629459637, "node_id": "MDU6SXNzdWU2Mjk0NTk2Mzc=", "number": 792, "title": "Replace response.body.decode(\"utf8\") with response.text in tests", "user": {"value": 9599, "label": "simonw"}, "state": "closed", "locked": 0, "assignee": null, "milestone": null, "comments": 0, "created_at": "2020-06-02T19:32:24Z", "updated_at": "2020-06-02T21:29:58Z", "closed_at": "2020-06-02T21:29:58Z", "author_association": "OWNER", "pull_request": null, "body": "Make use of the `response.text` property to clean up the tests a tiny bit:\r\n\r\nhttps://github.com/simonw/datasette/blob/57cf5139c552cb7feab9947daa949ca434cc0a66/tests/fixtures.py#L26-L38", "repo": {"value": 107914493, "label": "datasette"}, "type": "issue", "active_lock_reason": null, "performed_via_github_app": null, "reactions": "{\"url\": \"https://api.github.com/repos/simonw/datasette/issues/792/reactions\", \"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "draft": null, "state_reason": "completed"} {"id": 628156527, "node_id": "MDU6SXNzdWU2MjgxNTY1Mjc=", "number": 789, "title": "Mechanism for enabling pluggy tracing", "user": {"value": 9599, "label": "simonw"}, "state": "open", "locked": 0, "assignee": null, "milestone": null, "comments": 2, "created_at": "2020-06-01T05:10:14Z", "updated_at": "2020-06-01T05:11:03Z", "closed_at": null, "author_association": "OWNER", "pull_request": null, "body": "Could be useful for debugging plugins: https://pluggy.readthedocs.io/en/latest/#call-tracing\r\n\r\nI tried this out by adding these two lines in `plugins.py`:\r\n```python\r\npm = pluggy.PluginManager(\"datasette\")\r\npm.add_hookspecs(hookspecs)\r\n# Added these:\r\npm.trace.root.setwriter(print)\r\npm.enable_tracing()\r\n```\r\nOutput looked something like this:\r\n```\r\nINFO: 127.0.0.1:52724 - \"GET /-/-/static/app.css HTTP/1.1\" 404 Not Found\r\n actor_from_request [hook]\r\n datasette: \r\n request: \r\n\r\n finish actor_from_request --> [] [hook]\r\n\r\n extra_body_script [hook]\r\n template: show_json.html\r\n database: None\r\n table: None\r\n view_name: json_data\r\n datasette: \r\n\r\n finish extra_body_script --> [] [hook]\r\n\r\n extra_template_vars [hook]\r\n template: show_json.html\r\n database: None\r\n table: None\r\n view_name: json_data\r\n request: \r\n datasette: \r\n\r\n finish extra_template_vars --> [] [hook]\r\n\r\n extra_css_urls [hook]\r\n template: show_json.html\r\n database: None\r\n table: None\r\n datasette: \r\n\r\n finish extra_css_urls --> [] [hook]\r\n\r\n extra_js_urls [hook]\r\n template: show_json.html\r\n database: None\r\n table: None\r\n datasette: \r\n\r\n finish extra_js_urls --> [] [hook]\r\n\r\nINFO: 127.0.0.1:52724 - \"GET /-/actor HTTP/1.1\" 200 OK\r\n actor_from_request [hook]\r\n datasette: \r\n request: \r\n\r\n finish actor_from_request --> [] [hook]\r\n```", "repo": {"value": 107914493, "label": "datasette"}, "type": "issue", "active_lock_reason": null, "performed_via_github_app": null, "reactions": "{\"url\": \"https://api.github.com/repos/simonw/datasette/issues/789/reactions\", \"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "draft": null, "state_reason": null} {"id": 627836898, "node_id": "MDExOlB1bGxSZXF1ZXN0NDI1NTMxMjA1", "number": 783, "title": "Authentication: plugin hooks plus default --root auth mechanism", "user": {"value": 9599, "label": "simonw"}, "state": "closed", "locked": 0, "assignee": null, "milestone": null, "comments": 0, "created_at": "2020-05-30T22:25:47Z", "updated_at": "2020-06-01T01:16:44Z", "closed_at": "2020-06-01T01:16:43Z", "author_association": "OWNER", "pull_request": "simonw/datasette/pulls/783", "body": "See #699", "repo": {"value": 107914493, "label": "datasette"}, "type": "pull", "active_lock_reason": null, "performed_via_github_app": null, "reactions": "{\"url\": \"https://api.github.com/repos/simonw/datasette/issues/783/reactions\", \"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "draft": 0, "state_reason": null} {"id": 570101428, "node_id": "MDExOlB1bGxSZXF1ZXN0Mzc5MTkyMjU4", "number": 683, "title": ".execute_write() and .execute_write_fn() methods on Database", "user": {"value": 9599, "label": "simonw"}, "state": "closed", "locked": 0, "assignee": null, "milestone": {"value": 3268330, "label": "Datasette 1.0"}, "comments": 14, "created_at": "2020-02-24T19:51:58Z", "updated_at": "2020-05-30T18:40:20Z", "closed_at": "2020-02-25T04:45:08Z", "author_association": "OWNER", "pull_request": "simonw/datasette/pulls/683", "body": "See #682\r\n\r\n- [x] Come up with design for `.execute_write()` and `.execute_write_fn()`\r\n- [x] Build some quick demo plugins to exercise the design\r\n- [x] Write some unit tests\r\n- [x] Write the documentation", "repo": {"value": 107914493, "label": "datasette"}, "type": "pull", "active_lock_reason": null, "performed_via_github_app": null, "reactions": "{\"url\": \"https://api.github.com/repos/simonw/datasette/issues/683/reactions\", \"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "draft": 0, "state_reason": null} {"id": 268453968, "node_id": "MDU6SXNzdWUyNjg0NTM5Njg=", "number": 37, "title": "Ability to serialize massive JSON without blocking event loop", "user": {"value": 9599, "label": "simonw"}, "state": "closed", "locked": 0, "assignee": null, "milestone": null, "comments": 2, "created_at": "2017-10-25T15:58:03Z", "updated_at": "2020-05-30T17:29:20Z", "closed_at": "2020-05-30T17:29:20Z", "author_association": "OWNER", "pull_request": null, "body": "We run the risk of someone attempting a select statement that returns thousands of rows and hence takes several seconds just to JSON encode the response, effectively blocking the event loop and pausing all other traffic.\r\n\r\nThe Twisted community have a solution for this, can we adapt that in some way? http://as.ynchrono.us/2010/06/asynchronous-json_18.html?m=1", "repo": {"value": 107914493, "label": "datasette"}, "type": "issue", "active_lock_reason": null, "performed_via_github_app": null, "reactions": "{\"url\": \"https://api.github.com/repos/simonw/datasette/issues/37/reactions\", \"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "draft": null, "state_reason": "completed"} {"id": 570309546, "node_id": "MDU6SXNzdWU1NzAzMDk1NDY=", "number": 685, "title": "Document (and reconsider design of) Database.execute() and Database.execute_against_connection_in_thread()", "user": {"value": 9599, "label": "simonw"}, "state": "closed", "locked": 0, "assignee": null, "milestone": {"value": 3268330, "label": "Datasette 1.0"}, "comments": 15, "created_at": "2020-02-25T04:49:44Z", "updated_at": "2020-05-30T13:20:50Z", "closed_at": "2020-05-08T17:42:18Z", "author_association": "OWNER", "pull_request": null, "body": "In #683 I started a new section of internals documentation covering the `Database` class: https://datasette.readthedocs.io/en/latest/internals.html#database-class\r\n\r\nI decided not to document `.execute()` and `.execute_against_connection_in_thread()` yet because I'm not 100% happy with their API design yet.", "repo": {"value": 107914493, "label": "datasette"}, "type": "issue", "active_lock_reason": null, "performed_via_github_app": null, "reactions": "{\"url\": \"https://api.github.com/repos/simonw/datasette/issues/685/reactions\", \"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "draft": null, "state_reason": "completed"} {"id": 585633142, "node_id": "MDU6SXNzdWU1ODU2MzMxNDI=", "number": 706, "title": "Documentation for the \"request\" object", "user": {"value": 9599, "label": "simonw"}, "state": "closed", "locked": 0, "assignee": null, "milestone": {"value": 3268330, "label": "Datasette 1.0"}, "comments": 6, "created_at": "2020-03-22T02:55:50Z", "updated_at": "2020-05-30T13:20:00Z", "closed_at": "2020-05-27T22:31:22Z", "author_association": "OWNER", "pull_request": null, "body": "Since that object is passed to the `extra_template_vars` hooks AND the classes registered by `register_facet_classes` it should be part of the documented interface on https://datasette.readthedocs.io/en/stable/internals.html\r\n\r\nI could also start passing it to the `register_output_renderer` callback.", "repo": {"value": 107914493, "label": "datasette"}, "type": "issue", "active_lock_reason": null, "performed_via_github_app": null, "reactions": "{\"url\": \"https://api.github.com/repos/simonw/datasette/issues/706/reactions\", \"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "draft": null, "state_reason": "completed"} {"id": 626078521, "node_id": "MDU6SXNzdWU2MjYwNzg1MjE=", "number": 774, "title": "Consolidate request.raw_args and request.args", "user": {"value": 9599, "label": "simonw"}, "state": "closed", "locked": 0, "assignee": null, "milestone": {"value": 3268330, "label": "Datasette 1.0"}, "comments": 8, "created_at": "2020-05-27T22:30:59Z", "updated_at": "2020-05-29T23:27:35Z", "closed_at": "2020-05-29T23:22:38Z", "author_association": "OWNER", "pull_request": null, "body": "`request.raw_args` is not documented, and I'd like to remove it entirely.\r\n_Originally posted by @simonw in https://github.com/simonw/datasette/issues/706#issuecomment-634975252_\r\n\r\nI use it in a few places in other projects though, so I'll have to fix those first: https://github.com/search?q=user%3Asimonw+raw_args&type=Code", "repo": {"value": 107914493, "label": "datasette"}, "type": "issue", "active_lock_reason": null, "performed_via_github_app": null, "reactions": "{\"url\": \"https://api.github.com/repos/simonw/datasette/issues/774/reactions\", \"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "draft": null, "state_reason": "completed"} {"id": 345469355, "node_id": "MDU6SXNzdWUzNDU0NjkzNTU=", "number": 351, "title": "Automatically create a GitHub release linking to release notes for every tagged release", "user": {"value": 9599, "label": "simonw"}, "state": "closed", "locked": 0, "assignee": null, "milestone": null, "comments": 1, "created_at": "2018-07-28T18:31:12Z", "updated_at": "2020-05-28T18:56:16Z", "closed_at": "2020-05-28T18:56:15Z", "author_association": "OWNER", "pull_request": null, "body": "Can use this API called from Travis: https://developer.github.com/v3/repos/releases/#create-a-release\r\n\r\nThe release it generates should look like this one: https://github.com/simonw/datasette/releases/tag/0.24", "repo": {"value": 107914493, "label": "datasette"}, "type": "issue", "active_lock_reason": null, "performed_via_github_app": null, "reactions": "{\"url\": \"https://api.github.com/repos/simonw/datasette/issues/351/reactions\", \"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "draft": null, "state_reason": "completed"} {"id": 455965174, "node_id": "MDU6SXNzdWU0NTU5NjUxNzQ=", "number": 508, "title": "Ability to set default sort order for a table or view in metadata.json", "user": {"value": 9599, "label": "simonw"}, "state": "closed", "locked": 0, "assignee": {"value": 9599, "label": "simonw"}, "milestone": null, "comments": 1, "created_at": "2019-06-13T21:40:51Z", "updated_at": "2020-05-28T18:53:03Z", "closed_at": "2020-05-28T18:53:02Z", "author_association": "OWNER", "pull_request": null, "body": "It can go here in the documentation: https://datasette.readthedocs.io/en/stable/metadata.html#setting-which-columns-can-be-used-for-sorting\r\n\r\nAlso need to fix this sentence which is no longer true:\r\n\r\n> By default, database views in Datasette do not support sorting", "repo": {"value": 107914493, "label": "datasette"}, "type": "issue", "active_lock_reason": null, "performed_via_github_app": null, "reactions": "{\"url\": \"https://api.github.com/repos/simonw/datasette/issues/508/reactions\", \"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "draft": null, "state_reason": "completed"} {"id": 626663119, "node_id": "MDU6SXNzdWU2MjY2NjMxMTk=", "number": 781, "title": "request.url and request.scheme should obey force_https_urls config setting", "user": {"value": 9599, "label": "simonw"}, "state": "closed", "locked": 0, "assignee": null, "milestone": null, "comments": 3, "created_at": "2020-05-28T16:54:47Z", "updated_at": "2020-05-28T17:39:54Z", "closed_at": "2020-05-28T17:10:13Z", "author_association": "OWNER", "pull_request": null, "body": "I'm trying to get the https://www.niche-museums.com/browse/feed.atom feed to validate and I git this from https://validator.w3.org/feed/check.cgi?url=https%3A%2F%2Fwww.niche-museums.com%2Fbrowse%2Ffeed.atom\r\n\r\n> This feed is valid, but interoperability with the widest range of feed readers could be improved by implementing the following recommendations.\r\n> \r\n> [line 6](https://validator.w3.org/feed/check.cgi?url=https%3A%2F%2Fwww.niche-museums.com%2Fbrowse%2Ffeed.atom#l6), column 73: Self reference doesn't match document location [[help](https://validator.w3.org/feed/docs/warning/SelfDoesntMatchLocation.html \"more information about this error\")]\r\n> \r\n> \r\n\r\nI tried to fix this using `force_https_urls` ([commit](https://github.com/simonw/museums/commit/5dc8e2c717c59f9e949b65e47a59878e01f929e4)) but it didn't work - because that setting isn't respected by the Request class:\r\n\r\nhttps://github.com/simonw/datasette/blob/40885ef24e32d91502b6b8bbad1c7376f50f2830/datasette/utils/asgi.py#L15-L32", "repo": {"value": 107914493, "label": "datasette"}, "type": "issue", "active_lock_reason": null, "performed_via_github_app": null, "reactions": "{\"url\": \"https://api.github.com/repos/simonw/datasette/issues/781/reactions\", \"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "draft": null, "state_reason": "completed"} {"id": 626582657, "node_id": "MDU6SXNzdWU2MjY1ODI2NTc=", "number": 779, "title": "Make human_description_en explicitly available to output renderers", "user": {"value": 9599, "label": "simonw"}, "state": "open", "locked": 0, "assignee": null, "milestone": null, "comments": 0, "created_at": "2020-05-28T14:59:54Z", "updated_at": "2020-05-28T14:59:54Z", "closed_at": null, "author_association": "OWNER", "pull_request": null, "body": "`datasette-atom` uses this:\r\n\r\nhttps://github.com/simonw/datasette-atom/blob/df98a6c43a443224b6cd232f84703ec297ef046b/datasette_atom/__init__.py#L36-L37\r\n```python\r\n if data.get(\"human_description_en\"):\r\n title += \": \" + data[\"human_description_en\"]\r\n```\r\nIt's a nice way to generate a useful title for a filtered table.", "repo": {"value": 107914493, "label": "datasette"}, "type": "issue", "active_lock_reason": null, "performed_via_github_app": null, "reactions": "{\"url\": \"https://api.github.com/repos/simonw/datasette/issues/779/reactions\", \"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "draft": null, "state_reason": null} {"id": 625930207, "node_id": "MDU6SXNzdWU2MjU5MzAyMDc=", "number": 770, "title": "register_output_renderer can_render mechanism", "user": {"value": 9599, "label": "simonw"}, "state": "closed", "locked": 0, "assignee": null, "milestone": {"value": 5471110, "label": "Datasette 0.43"}, "comments": 4, "created_at": "2020-05-27T18:29:14Z", "updated_at": "2020-05-28T05:57:16Z", "closed_at": "2020-05-28T05:57:16Z", "author_association": "OWNER", "pull_request": null, "body": "I would like is the ability for renderers to opt-in / opt-out of being displayed as options on the page.\r\n\r\nhttps://www.niche-museums.com/browse/museums for example shows a atom link because the datasette-atom plugin is installed... but clicking it will give you a 400 error because the correct columns are not present.\r\n\r\n\"browse__museums__102_rows\"\r\n\r\nHere's the code that passes a list of renderers to the template:\r\n\r\nhttps://github.com/simonw/datasette/blob/2d099ad9c657d2cab59de91cdb8bfed2da236ef6/datasette/views/base.py#L411-L423\r\n\r\nA renderer is currently defined as a two-key dictionary:\r\n```python\r\n@hookimpl\r\ndef register_output_renderer(datasette):\r\n return {\r\n 'extension': 'test',\r\n 'callback': render_test\r\n }\r\n```\r\nI can add a third key, `\"should_suggest\"` which is a function that returns `True` or `False` for a given query. If that key is missing it is assumed to return `True`.\r\n\r\nOne catch: what arguments should be passed to the `should_suggest(...)` function?\r\n\r\nUPDATE: now calling it `can_render` instead.\r\n\r\n_Originally posted by @simonw in https://github.com/simonw/datasette/issues/581#issuecomment-634856748_", "repo": {"value": 107914493, "label": "datasette"}, "type": "issue", "active_lock_reason": null, "performed_via_github_app": null, "reactions": "{\"url\": \"https://api.github.com/repos/simonw/datasette/issues/770/reactions\", \"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "draft": null, "state_reason": "completed"} {"id": 611540797, "node_id": "MDU6SXNzdWU2MTE1NDA3OTc=", "number": 751, "title": "Ability to set custom default _size on a per-table basis", "user": {"value": 9599, "label": "simonw"}, "state": "closed", "locked": 0, "assignee": null, "milestone": {"value": 5471110, "label": "Datasette 0.43"}, "comments": 4, "created_at": "2020-05-04T00:13:03Z", "updated_at": "2020-05-28T05:00:22Z", "closed_at": "2020-05-28T05:00:20Z", "author_association": "OWNER", "pull_request": null, "body": "I have some tables where I'd like the default page size to be 10, without affecting the rest of my Datasette instance.", "repo": {"value": 107914493, "label": "datasette"}, "type": "issue", "active_lock_reason": null, "performed_via_github_app": null, "reactions": "{\"url\": \"https://api.github.com/repos/simonw/datasette/issues/751/reactions\", \"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "draft": null, "state_reason": "completed"} {"id": 626001501, "node_id": "MDU6SXNzdWU2MjYwMDE1MDE=", "number": 773, "title": "All plugin hooks should have unit tests", "user": {"value": 9599, "label": "simonw"}, "state": "closed", "locked": 0, "assignee": null, "milestone": {"value": 5471110, "label": "Datasette 0.43"}, "comments": 2, "created_at": "2020-05-27T20:17:41Z", "updated_at": "2020-05-28T04:12:11Z", "closed_at": "2020-05-28T04:09:25Z", "author_association": "OWNER", "pull_request": null, "body": "Four hooks currently missing tests:\r\n\r\n- [x] prepare_jinja2_environment\r\n- [x] publish_subcommand\r\n- [x] register_facet_classes\r\n- [x] register_output_renderer\r\n\r\n```\r\n$ pytest -k test_plugin_hooks_have_tests -vv\r\n====================================== test session starts ======================================\r\nplatform darwin -- Python 3.7.7, pytest-5.2.4, py-1.8.1, pluggy-0.13.1 -- /Users/simon/.local/share/virtualenvs/datasette-AWNrQs95/bin/python\r\ncachedir: .pytest_cache\r\nrootdir: /Users/simon/Dropbox/Development/datasette, inifile: pytest.ini\r\nplugins: asyncio-0.10.0\r\ncollected 486 items / 475 deselected / 11 selected \r\n\r\ntests/test_plugins.py::test_plugin_hooks_have_tests[asgi_wrapper] XPASS [ 9%]\r\ntests/test_plugins.py::test_plugin_hooks_have_tests[extra_body_script] XPASS [ 18%]\r\ntests/test_plugins.py::test_plugin_hooks_have_tests[extra_css_urls] XPASS [ 27%]\r\ntests/test_plugins.py::test_plugin_hooks_have_tests[extra_js_urls] XPASS [ 36%]\r\ntests/test_plugins.py::test_plugin_hooks_have_tests[extra_template_vars] XPASS [ 45%]\r\ntests/test_plugins.py::test_plugin_hooks_have_tests[prepare_connection] XPASS [ 54%]\r\ntests/test_plugins.py::test_plugin_hooks_have_tests[prepare_jinja2_environment] XFAIL [ 63%]\r\ntests/test_plugins.py::test_plugin_hooks_have_tests[publish_subcommand] XFAIL [ 72%]\r\ntests/test_plugins.py::test_plugin_hooks_have_tests[register_facet_classes] XFAIL [ 81%]\r\ntests/test_plugins.py::test_plugin_hooks_have_tests[register_output_renderer] XFAIL [ 90%]\r\ntests/test_plugins.py::test_plugin_hooks_have_tests[render_cell] XPASS [100%]\r\n\r\n========================= 475 deselected, 4 xfailed, 7 xpassed in 1.70s =========================\r\n\r\n_Originally posted by @simonw in https://github.com/simonw/datasette/issues/771#issuecomment-634915104_", "repo": {"value": 107914493, "label": "datasette"}, "type": "issue", "active_lock_reason": null, "performed_via_github_app": null, "reactions": "{\"url\": \"https://api.github.com/repos/simonw/datasette/issues/773/reactions\", \"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "draft": null, "state_reason": "completed"} {"id": 626163974, "node_id": "MDU6SXNzdWU2MjYxNjM5NzQ=", "number": 776, "title": "register_output_renderer render callback should be optionally awaitable", "user": {"value": 9599, "label": "simonw"}, "state": "closed", "locked": 0, "assignee": null, "milestone": {"value": 5471110, "label": "Datasette 0.43"}, "comments": 1, "created_at": "2020-05-28T02:26:29Z", "updated_at": "2020-05-28T02:43:36Z", "closed_at": "2020-05-28T02:43:36Z", "author_association": "OWNER", "pull_request": null, "body": "In #581 I made a bunch of improvements to this, including making `datasette` available to it so it could execute queries.\r\n\r\nBut... it needs to be able to `await` in order to do that. Which means it should be optionally-awaitable.\r\n\r\nOriginal idea here: https://github.com/simonw/datasette/issues/645#issuecomment-560036740", "repo": {"value": 107914493, "label": "datasette"}, "type": "issue", "active_lock_reason": null, "performed_via_github_app": null, "reactions": "{\"url\": \"https://api.github.com/repos/simonw/datasette/issues/776/reactions\", \"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "draft": null, "state_reason": "completed"} {"id": 502993509, "node_id": "MDU6SXNzdWU1MDI5OTM1MDk=", "number": 581, "title": "Redesign register_output_renderer callback", "user": {"value": 9599, "label": "simonw"}, "state": "closed", "locked": 0, "assignee": null, "milestone": {"value": 5471110, "label": "Datasette 0.43"}, "comments": 24, "created_at": "2019-10-05T17:43:23Z", "updated_at": "2020-05-28T02:24:14Z", "closed_at": "2020-05-28T02:21:50Z", "author_association": "OWNER", "pull_request": null, "body": "In building https://github.com/simonw/datasette-atom it became clear that the callback function (which currently accepts just args, data and view_name) would also benefit from access to a mechanism to render templates and a `datasette` instance so it can execute SQL.\r\n\r\nTo maintain backwards compatibility with existing plugins, we can introspect the callback function to see if it wants those new arguments or not.\r\n\r\nAt a minimum I want to make `datasette` and ASGI `scope` available.", "repo": {"value": 107914493, "label": "datasette"}, "type": "issue", "active_lock_reason": null, "performed_via_github_app": null, "reactions": "{\"url\": \"https://api.github.com/repos/simonw/datasette/issues/581/reactions\", \"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "draft": null, "state_reason": "completed"} {"id": 530653633, "node_id": "MDU6SXNzdWU1MzA2NTM2MzM=", "number": 645, "title": "Mechanism for register_output_renderer to suggest extension or not", "user": {"value": 9599, "label": "simonw"}, "state": "closed", "locked": 0, "assignee": null, "milestone": null, "comments": 4, "created_at": "2019-12-01T01:26:27Z", "updated_at": "2020-05-28T02:22:18Z", "closed_at": "2020-05-28T02:22:12Z", "author_association": "OWNER", "pull_request": null, "body": "[datasette-atom](https://github.com/simonw/datasette-atom) only works if the user constructs a SQL query with specific output columns (`atom_id` ,`atom_updated` etc).\r\n\r\nIt would be good if the `.atom` link wasn't shown on the query/table page unless those columns were present. Right now you get a link which results in a 400 error:\r\n\r\n\"browse__museums__51_rows\"\r\n\r\nSee also #581.", "repo": {"value": 107914493, "label": "datasette"}, "type": "issue", "active_lock_reason": null, "performed_via_github_app": null, "reactions": "{\"url\": \"https://api.github.com/repos/simonw/datasette/issues/645/reactions\", \"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "draft": null, "state_reason": "completed"} {"id": 626131309, "node_id": "MDU6SXNzdWU2MjYxMzEzMDk=", "number": 775, "title": "Move test plugins into datasette/tests/plugins/ directory", "user": {"value": 9599, "label": "simonw"}, "state": "closed", "locked": 0, "assignee": null, "milestone": null, "comments": 1, "created_at": "2020-05-28T00:46:58Z", "updated_at": "2020-05-28T00:57:31Z", "closed_at": "2020-05-28T00:57:31Z", "author_association": "OWNER", "pull_request": null, "body": "Right now the plugins used during test runs are baked into strings. It would be nicer if they were actual files on disk.\r\n\r\nWill make #581 easier to write tests for.", "repo": {"value": 107914493, "label": "datasette"}, "type": "issue", "active_lock_reason": null, "performed_via_github_app": null, "reactions": "{\"url\": \"https://api.github.com/repos/simonw/datasette/issues/775/reactions\", \"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "draft": null, "state_reason": "completed"} {"id": 622672640, "node_id": "MDExOlB1bGxSZXF1ZXN0NDIxNDkxODEw", "number": 768, "title": "Use dirs_exist_ok=True", "user": {"value": 9599, "label": "simonw"}, "state": "closed", "locked": 0, "assignee": null, "milestone": {"value": 5471110, "label": "Datasette 0.43"}, "comments": 0, "created_at": "2020-05-21T17:53:44Z", "updated_at": "2020-05-27T20:21:56Z", "closed_at": "2020-05-21T17:53:51Z", "author_association": "OWNER", "pull_request": "simonw/datasette/pulls/768", "body": "Refs #744", "repo": {"value": 107914493, "label": "datasette"}, "type": "pull", "active_lock_reason": null, "performed_via_github_app": null, "reactions": "{\"url\": \"https://api.github.com/repos/simonw/datasette/issues/768/reactions\", \"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "draft": 0, "state_reason": null} {"id": 625922239, "node_id": "MDExOlB1bGxSZXF1ZXN0NDI0MDMyNDQ1", "number": 769, "title": "Backport of Python 3.8 shutil.copytree", "user": {"value": 9599, "label": "simonw"}, "state": "closed", "locked": 0, "assignee": null, "milestone": {"value": 5471110, "label": "Datasette 0.43"}, "comments": 0, "created_at": "2020-05-27T18:17:15Z", "updated_at": "2020-05-27T20:21:56Z", "closed_at": "2020-05-27T18:17:44Z", "author_association": "OWNER", "pull_request": "simonw/datasette/pulls/769", "body": "Closes #744", "repo": {"value": 107914493, "label": "datasette"}, "type": "pull", "active_lock_reason": null, "performed_via_github_app": null, "reactions": "{\"url\": \"https://api.github.com/repos/simonw/datasette/issues/769/reactions\", \"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "draft": 0, "state_reason": null} {"id": 625991831, "node_id": "MDExOlB1bGxSZXF1ZXN0NDI0MDg1MjY0", "number": 772, "title": "Test that plugin hooks are unit tested", "user": {"value": 9599, "label": "simonw"}, "state": "closed", "locked": 0, "assignee": null, "milestone": {"value": 5471110, "label": "Datasette 0.43"}, "comments": 0, "created_at": "2020-05-27T20:01:32Z", "updated_at": "2020-05-27T20:21:56Z", "closed_at": "2020-05-27T20:16:03Z", "author_association": "OWNER", "pull_request": "simonw/datasette/pulls/772", "body": "Refs #771", "repo": {"value": 107914493, "label": "datasette"}, "type": "pull", "active_lock_reason": null, "performed_via_github_app": null, "reactions": "{\"url\": \"https://api.github.com/repos/simonw/datasette/issues/772/reactions\", \"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "draft": 0, "state_reason": null} {"id": 616012427, "node_id": "MDU6SXNzdWU2MTYwMTI0Mjc=", "number": 764, "title": "Add PyPI project urls to setup.py", "user": {"value": 9599, "label": "simonw"}, "state": "closed", "locked": 0, "assignee": null, "milestone": {"value": 5471110, "label": "Datasette 0.43"}, "comments": 3, "created_at": "2020-05-11T16:23:08Z", "updated_at": "2020-05-27T20:21:36Z", "closed_at": "2020-05-11T18:28:55Z", "author_association": "OWNER", "pull_request": null, "body": "Spotted this example here:\r\n```python\r\n project_urls={\r\n \"Issues\": \"https://gitlab.com/Cyb3r-Jak3/ExifReader/issues\",\r\n \"Source Code\": \"https://gitlab.com/Cyb3r-Jak3/ExifReader/-/tree/publish\",\r\n \"CI\": \"https://gitlab.com/Cyb3r-Jak3/ExifReader/pipelines\",\r\n \"Releases\": \"https://github.com/Cyb3r-Jak3/ExifReader\"\r\n },\r\n```\r\nResults in this on https://pypi.org/project/ExifReader/\r\n\r\n\"ExifReader_\u00b7_PyPI\"\r\n", "repo": {"value": 107914493, "label": "datasette"}, "type": "issue", "active_lock_reason": null, "performed_via_github_app": null, "reactions": "{\"url\": \"https://api.github.com/repos/simonw/datasette/issues/764/reactions\", \"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "draft": null, "state_reason": "completed"} {"id": 625980317, "node_id": "MDU6SXNzdWU2MjU5ODAzMTc=", "number": 771, "title": "Unit test that checks that all plugin hooks have corresponding unit tests", "user": {"value": 9599, "label": "simonw"}, "state": "closed", "locked": 0, "assignee": null, "milestone": {"value": 5471110, "label": "Datasette 0.43"}, "comments": 5, "created_at": "2020-05-27T19:42:35Z", "updated_at": "2020-05-27T20:21:36Z", "closed_at": "2020-05-27T20:17:13Z", "author_association": "OWNER", "pull_request": null, "body": "Turns out some hooks are missing unit test coverage: https://github.com/simonw/datasette/issues/581#issuecomment-634893744_", "repo": {"value": 107914493, "label": "datasette"}, "type": "issue", "active_lock_reason": null, "performed_via_github_app": null, "reactions": "{\"url\": \"https://api.github.com/repos/simonw/datasette/issues/771/reactions\", \"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "draft": null, "state_reason": "completed"} {"id": 603295970, "node_id": "MDU6SXNzdWU2MDMyOTU5NzA=", "number": 729, "title": "Visually distinguish integer and text columns", "user": {"value": 9599, "label": "simonw"}, "state": "closed", "locked": 0, "assignee": null, "milestone": null, "comments": 8, "created_at": "2020-04-20T14:47:26Z", "updated_at": "2020-05-18T17:20:02Z", "closed_at": "2020-05-15T18:16:56Z", "author_association": "OWNER", "pull_request": null, "body": "It would be useful if I could tell from looking at the table page if a column was a integer or a text (or a float I guess?). This is particularly important for knowing if it safe to sort by that column.", "repo": {"value": 107914493, "label": "datasette"}, "type": "issue", "active_lock_reason": null, "performed_via_github_app": null, "reactions": "{\"url\": \"https://api.github.com/repos/simonw/datasette/issues/729/reactions\", \"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "draft": null, "state_reason": "completed"} {"id": 616087149, "node_id": "MDU6SXNzdWU2MTYwODcxNDk=", "number": 765, "title": "publish heroku should default to currently tagged version", "user": {"value": 9599, "label": "simonw"}, "state": "open", "locked": 0, "assignee": null, "milestone": null, "comments": 1, "created_at": "2020-05-11T18:24:06Z", "updated_at": "2020-05-11T18:25:43Z", "closed_at": null, "author_association": "OWNER", "pull_request": null, "body": "Had a report that deploying to Heroku was using the previously installed version of Datasette, not the latest.\r\n\r\nCould be because of this:\r\n\r\nhttps://github.com/simonw/datasette/blob/af6c6c5d6f929f951c0e63bfd1c82e37a071b50f/datasette/publish/heroku.py#L172-L179\r\n\r\nHeroku documentation recommends pinning to specific versions https://devcenter.heroku.com/articles/python-pip\r\n\r\nSo... we could ensure we default to an install value of `[\"datasette>=current_tag\"]`.", "repo": {"value": 107914493, "label": "datasette"}, "type": "issue", "active_lock_reason": null, "performed_via_github_app": null, "reactions": "{\"url\": \"https://api.github.com/repos/simonw/datasette/issues/765/reactions\", \"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "draft": null, "state_reason": null} {"id": 534492501, "node_id": "MDU6SXNzdWU1MzQ0OTI1MDE=", "number": 648, "title": "Mechanism for adding arbitrary pages like /about", "user": {"value": 9599, "label": "simonw"}, "state": "closed", "locked": 0, "assignee": null, "milestone": null, "comments": 13, "created_at": "2019-12-08T04:55:19Z", "updated_at": "2020-05-07T15:21:19Z", "closed_at": "2020-04-26T18:46:45Z", "author_association": "OWNER", "pull_request": null, "body": "For www.niche-museums.com I solved this by creating an empty `about.db` database file - see https://simonwillison.net/2019/Nov/25/niche-museums/\r\n\r\nI want a neater mechanism for this.", "repo": {"value": 107914493, "label": "datasette"}, "type": "issue", "active_lock_reason": null, "performed_via_github_app": null, "reactions": "{\"url\": \"https://api.github.com/repos/simonw/datasette/issues/648/reactions\", \"total_count\": 1, \"+1\": 1, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "draft": null, "state_reason": "completed"} {"id": 613467382, "node_id": "MDU6SXNzdWU2MTM0NjczODI=", "number": 761, "title": "Allow-list pragma_table_info(tablename) and similar", "user": {"value": 9599, "label": "simonw"}, "state": "closed", "locked": 0, "assignee": null, "milestone": null, "comments": 8, "created_at": "2020-05-06T16:54:14Z", "updated_at": "2020-05-07T03:09:05Z", "closed_at": "2020-05-06T17:18:38Z", "author_association": "OWNER", "pull_request": null, "body": "It would be great if `pragma_table_info(tablename)` was allowed to be used in queries. See also https://github.com/simonw/til/blob/master/sqlite/list-all-columns-in-a-database.md\r\n> `select * from pragma_table_info(tablename);` is currently disallowed for user-provided queries via a regex restriction - but could help here too.\r\n> \r\n> https://github.com/simonw/datasette/blob/d349d57cdf3d577afb62bdf784af342a4d5be660/datasette/utils/__init__.py#L174\r\n\r\n_Originally posted by @simonw in https://github.com/simonw/datasette/issues/760#issuecomment-624729459_", "repo": {"value": 107914493, "label": "datasette"}, "type": "issue", "active_lock_reason": null, "performed_via_github_app": null, "reactions": "{\"url\": \"https://api.github.com/repos/simonw/datasette/issues/761/reactions\", \"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "draft": null, "state_reason": "completed"} {"id": 613491342, "node_id": "MDU6SXNzdWU2MTM0OTEzNDI=", "number": 762, "title": "Experiment with PRAGMA hard_heap_limit ", "user": {"value": 9599, "label": "simonw"}, "state": "open", "locked": 0, "assignee": null, "milestone": null, "comments": 0, "created_at": "2020-05-06T17:33:23Z", "updated_at": "2020-05-07T03:08:44Z", "closed_at": null, "author_association": "OWNER", "pull_request": null, "body": "This was added in SQLite 2020-01-22 (3.31.0): https://www.sqlite.org/changes.html#version_3_31_0\r\n\r\n> Add the [sqlite3_hard_heap_limit64()](https://www.sqlite.org/c3ref/hard_heap_limit64.html) interface and the corresponding [PRAGMA hard_heap_limit](https://www.sqlite.org/pragma.html#pragma_hard_heap_limit) command. \r\n\r\nThis sounds like it could be a nice extra safety measure.", "repo": {"value": 107914493, "label": "datasette"}, "type": "issue", "active_lock_reason": null, "performed_via_github_app": null, "reactions": "{\"url\": \"https://api.github.com/repos/simonw/datasette/issues/762/reactions\", \"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "draft": null, "state_reason": null} {"id": 613422636, "node_id": "MDU6SXNzdWU2MTM0MjI2MzY=", "number": 760, "title": "Way of seeing full schema for a database", "user": {"value": 9599, "label": "simonw"}, "state": "open", "locked": 0, "assignee": null, "milestone": null, "comments": 3, "created_at": "2020-05-06T15:46:08Z", "updated_at": "2020-05-06T23:49:06Z", "closed_at": null, "author_association": "OWNER", "pull_request": null, "body": "I find myself wanting to quickly figure out all of the BLOB columns in a database.\r\n\r\nA `/-/schema` page showing the full schema (actually since it's per-database probably `/dbname/-/schema` or `/-/schema/dbname`) would be really handy.\r\n\r\nIt would need to be carefully constructed from various queries against `sqlite_master` - just doing `select * from sqlite_master where type='table'` isn't quite enough because I also want to show indexes, triggers etc.", "repo": {"value": 107914493, "label": "datasette"}, "type": "issue", "active_lock_reason": null, "performed_via_github_app": null, "reactions": "{\"url\": \"https://api.github.com/repos/simonw/datasette/issues/760/reactions\", \"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "draft": null, "state_reason": null} {"id": 612089949, "node_id": "MDU6SXNzdWU2MTIwODk5NDk=", "number": 756, "title": "Add pipx to installation documentation", "user": {"value": 9599, "label": "simonw"}, "state": "closed", "locked": 0, "assignee": null, "milestone": null, "comments": 2, "created_at": "2020-05-04T18:49:01Z", "updated_at": "2020-05-04T19:19:06Z", "closed_at": "2020-05-04T19:10:33Z", "author_association": "OWNER", "pull_request": null, "body": "Add to this page: https://datasette.readthedocs.io/en/stable/installation.html\r\n\r\nHere's how to install plugins: https://twitter.com/simonw/status/1257348687979778050\r\n```\r\n$ datasette plugins\r\n[]\r\n\r\n$ pipx inject datasette datasette-json-html \r\n injected package datasette-json-html into venv datasette\r\ndone! \u2728 \ud83c\udf1f \u2728\r\n\r\n$ datasette plugins\r\n[\r\n {\r\n \"name\": \"datasette-json-html\",\r\n \"static\": false,\r\n \"templates\": false,\r\n \"version\": \"0.6\"\r\n }\r\n]\r\n```", "repo": {"value": 107914493, "label": "datasette"}, "type": "issue", "active_lock_reason": null, "performed_via_github_app": null, "reactions": "{\"url\": \"https://api.github.com/repos/simonw/datasette/issues/756/reactions\", \"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "draft": null, "state_reason": "completed"} {"id": 612082842, "node_id": "MDU6SXNzdWU2MTIwODI4NDI=", "number": 755, "title": "Fix \"no such column: id\" output in tests", "user": {"value": 9599, "label": "simonw"}, "state": "closed", "locked": 0, "assignee": null, "milestone": null, "comments": 1, "created_at": "2020-05-04T18:37:49Z", "updated_at": "2020-05-04T18:42:14Z", "closed_at": "2020-05-04T18:42:14Z", "author_association": "OWNER", "pull_request": null, "body": "```\r\npytest\r\n...\r\ntests/test_custom_pages.py ........ [ 33%]\r\ntests/test_database.py ......no such column: id\r\n... [ 35%]\r\n```", "repo": {"value": 107914493, "label": "datasette"}, "type": "issue", "active_lock_reason": null, "performed_via_github_app": null, "reactions": "{\"url\": \"https://api.github.com/repos/simonw/datasette/issues/755/reactions\", \"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "draft": null, "state_reason": "completed"} {"id": 611997130, "node_id": "MDU6SXNzdWU2MTE5OTcxMzA=", "number": 754, "title": "Clean up aiofiles warnings on 3.8", "user": {"value": 9599, "label": "simonw"}, "state": "closed", "locked": 0, "assignee": null, "milestone": null, "comments": 2, "created_at": "2020-05-04T16:14:59Z", "updated_at": "2020-05-04T16:22:30Z", "closed_at": "2020-05-04T16:22:30Z", "author_association": "OWNER", "pull_request": null, "body": "https://travis-ci.org/github/simonw/datasette/jobs/682624476\r\n\r\nLots of warnings like this:\r\n```\r\n/home/travis/virtualenv/python3.8.0/lib/python3.8/site-packages/aiofiles/threadpool/utils.py:33\r\n\r\n/home/travis/virtualenv/python3.8.0/lib/python3.8/site-packages/aiofiles/threadpool/utils.py:33\r\n\r\n /home/travis/virtualenv/python3.8.0/lib/python3.8/site-packages/aiofiles/threadpool/utils.py:33: DeprecationWarning: \"@coroutine\" decorator is deprecated since Python 3.8, use \"async def\" instead\r\n\r\n def method(self, *args, **kwargs):\r\n\r\n/home/travis/virtualenv/python3.8.0/lib/python3.8/site-packages/aiofiles/threadpool/__init__.py:27\r\n\r\n /home/travis/virtualenv/python3.8.0/lib/python3.8/site-packages/aiofiles/threadpool/__init__.py:27: DeprecationWarning: \"@coroutine\" decorator is deprecated since Python 3.8, use \"async def\" instead\r\n\r\n def _open(file, mode='r', buffering=-1, encoding=None, errors=None, newline=None,\r\n```", "repo": {"value": 107914493, "label": "datasette"}, "type": "issue", "active_lock_reason": null, "performed_via_github_app": null, "reactions": "{\"url\": \"https://api.github.com/repos/simonw/datasette/issues/754/reactions\", \"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "draft": null, "state_reason": "completed"} {"id": 611252244, "node_id": "MDU6SXNzdWU2MTEyNTIyNDQ=", "number": 750, "title": "Add notlike table filter", "user": {"value": 9599, "label": "simonw"}, "state": "closed", "locked": 0, "assignee": null, "milestone": null, "comments": 3, "created_at": "2020-05-02T18:54:36Z", "updated_at": "2020-05-02T19:10:44Z", "closed_at": "2020-05-02T19:10:44Z", "author_association": "OWNER", "pull_request": null, "body": "I found myself wanting that for applying the opposite of this: https://github-to-sqlite.dogsheep.net/github/dependent_repos?dependent__like=%25simonw%2F%25&_sort_desc=dependent_stars\r\n\r\n", "repo": {"value": 107914493, "label": "datasette"}, "type": "issue", "active_lock_reason": null, "performed_via_github_app": null, "reactions": "{\"url\": \"https://api.github.com/repos/simonw/datasette/issues/750/reactions\", \"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "draft": null, "state_reason": "completed"} {"id": 610342575, "node_id": "MDU6SXNzdWU2MTAzNDI1NzU=", "number": 748, "title": "?_searchmode=raw should be documented on full-text search page", "user": {"value": 9599, "label": "simonw"}, "state": "closed", "locked": 0, "assignee": null, "milestone": null, "comments": 0, "created_at": "2020-04-30T19:50:06Z", "updated_at": "2020-04-30T21:06:12Z", "closed_at": "2020-04-30T21:06:12Z", "author_association": "OWNER", "pull_request": null, "body": "It's currently documented here: https://datasette.readthedocs.io/en/stable/json_api.html#special-table-arguments\r\n\r\nBut it should also be described here: https://datasette.readthedocs.io/en/stable/full_text_search.html#the-table-view-api", "repo": {"value": 107914493, "label": "datasette"}, "type": "issue", "active_lock_reason": null, "performed_via_github_app": null, "reactions": "{\"url\": \"https://api.github.com/repos/simonw/datasette/issues/748/reactions\", \"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "draft": null, "state_reason": "completed"} {"id": 610192152, "node_id": "MDU6SXNzdWU2MTAxOTIxNTI=", "number": 747, "title": "Directory configuration mode should support metadata.yaml", "user": {"value": 9599, "label": "simonw"}, "state": "closed", "locked": 0, "assignee": null, "milestone": null, "comments": 4, "created_at": "2020-04-30T16:05:30Z", "updated_at": "2020-04-30T19:04:19Z", "closed_at": "2020-04-30T19:04:19Z", "author_association": "OWNER", "pull_request": null, "body": "Refs #739 - `metadata.yml` or `metadata.yaml` should be detected in the same way as `metadata.json` is.", "repo": {"value": 107914493, "label": "datasette"}, "type": "issue", "active_lock_reason": null, "performed_via_github_app": null, "reactions": "{\"url\": \"https://api.github.com/repos/simonw/datasette/issues/747/reactions\", \"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "draft": null, "state_reason": "completed"} {"id": 608752766, "node_id": "MDExOlB1bGxSZXF1ZXN0NDEwNDY5Mjcy", "number": 746, "title": "shutil.Error, not OSError", "user": {"value": 9599, "label": "simonw"}, "state": "closed", "locked": 0, "assignee": null, "milestone": null, "comments": 1, "created_at": "2020-04-29T03:30:51Z", "updated_at": "2020-04-29T07:07:24Z", "closed_at": "2020-04-29T07:07:23Z", "author_association": "OWNER", "pull_request": "simonw/datasette/pulls/746", "body": "Refs #744", "repo": {"value": 107914493, "label": "datasette"}, "type": "pull", "active_lock_reason": null, "performed_via_github_app": null, "reactions": "{\"url\": \"https://api.github.com/repos/simonw/datasette/issues/746/reactions\", \"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "draft": 0, "state_reason": null} {"id": 522334771, "node_id": "MDU6SXNzdWU1MjIzMzQ3NzE=", "number": 633, "title": "Publish to Heroku is broken: \"WARNING: You must pass the application as an import string to enable 'reload' or 'workers\"", "user": {"value": 9599, "label": "simonw"}, "state": "closed", "locked": 0, "assignee": null, "milestone": null, "comments": 3, "created_at": "2019-11-13T16:32:11Z", "updated_at": "2020-04-28T20:37:50Z", "closed_at": "2019-11-13T16:43:23Z", "author_association": "OWNER", "pull_request": null, "body": "```\r\n2019-11-13T16:27:59.821483+00:00 heroku[web.1]: Starting process with command `datasette serve --host 0.0.0.0 -i fixtures.db --cors --port 36817 --inspect-file inspect-data.json`\r\n2019-11-13T16:28:01.856471+00:00 heroku[web.1]: State changed from starting to crashed\r\n2019-11-13T16:28:01.750253+00:00 app[web.1]: Serve! files=() (immutables=('fixtures.db',)) on port 36817\r\n2019-11-13T16:28:01.771524+00:00 app[web.1]: WARNING: You must pass the application as an import string to enable 'reload' or 'workers'.\r\n2019-11-13T16:28:01.837839+00:00 heroku[web.1]: Process exited with status 1\r\n```", "repo": {"value": 107914493, "label": "datasette"}, "type": "issue", "active_lock_reason": null, "performed_via_github_app": null, "reactions": "{\"url\": \"https://api.github.com/repos/simonw/datasette/issues/633/reactions\", \"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "draft": null, "state_reason": "completed"} {"id": 607770595, "node_id": "MDU6SXNzdWU2MDc3NzA1OTU=", "number": 743, "title": "escape_fts() does not correctly escape * wildcards", "user": {"value": 9599, "label": "simonw"}, "state": "closed", "locked": 0, "assignee": null, "milestone": null, "comments": 4, "created_at": "2020-04-27T18:48:53Z", "updated_at": "2020-04-27T19:11:30Z", "closed_at": "2020-04-27T19:11:01Z", "author_association": "OWNER", "pull_request": null, "body": "Spotted in #732. This should not return any results... but it does:\r\n\r\nhttps://latest.datasette.io/fixtures/searchable?_search=bar%2A&_trace=1\r\n\r\n\"fixtures__searchable__1_row_where_where_search_matches__bar__\"\r\n\r\nThe query from trace is:\r\n```\r\n \"sql\": \"select count(*) from searchable where rowid in (select rowid from searchable_fts where searchable_fts match escape_fts(:search))\",\r\n \"params\": {\r\n \"search\": \"bar*\"\r\n }\r\n```", "repo": {"value": 107914493, "label": "datasette"}, "type": "issue", "active_lock_reason": null, "performed_via_github_app": null, "reactions": "{\"url\": \"https://api.github.com/repos/simonw/datasette/issues/743/reactions\", \"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "draft": null, "state_reason": "completed"} {"id": 607211058, "node_id": "MDU6SXNzdWU2MDcyMTEwNTg=", "number": 740, "title": "Don't throw 500 error on attempted directory browse", "user": {"value": 9599, "label": "simonw"}, "state": "closed", "locked": 0, "assignee": null, "milestone": null, "comments": 1, "created_at": "2020-04-27T03:50:11Z", "updated_at": "2020-04-27T18:29:15Z", "closed_at": "2020-04-27T18:29:15Z", "author_association": "OWNER", "pull_request": null, "body": "\"Error_500\"\r\n\r\nThis should be a 403 error instead, because the `--static` mechanism doesn't allow directory browsing.", "repo": {"value": 107914493, "label": "datasette"}, "type": "issue", "active_lock_reason": null, "performed_via_github_app": null, "reactions": "{\"url\": \"https://api.github.com/repos/simonw/datasette/issues/740/reactions\", \"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "draft": null, "state_reason": "completed"} {"id": 607243940, "node_id": "MDU6SXNzdWU2MDcyNDM5NDA=", "number": 742, "title": "Speed up tests with scope=\"session\"?", "user": {"value": 9599, "label": "simonw"}, "state": "closed", "locked": 0, "assignee": null, "milestone": null, "comments": 1, "created_at": "2020-04-27T05:23:54Z", "updated_at": "2020-04-27T18:24:53Z", "closed_at": "2020-04-27T18:24:53Z", "author_association": "OWNER", "pull_request": null, "body": "Tests are pretty slow - could I speed them up with pytest `scope=\"session\"` on some of the fixtures?\r\n\r\nEg https://travis-ci.org/github/simonw/datasette/jobs/679940036 ran 452 tests in 3m53s - the `test_html` ones seem particularly slow.", "repo": {"value": 107914493, "label": "datasette"}, "type": "issue", "active_lock_reason": null, "performed_via_github_app": null, "reactions": "{\"url\": \"https://api.github.com/repos/simonw/datasette/issues/742/reactions\", \"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "draft": null, "state_reason": "completed"} {"id": 605110015, "node_id": "MDU6SXNzdWU2MDUxMTAwMTU=", "number": 731, "title": "Option to automatically configure based on directory layout", "user": {"value": 9599, "label": "simonw"}, "state": "closed", "locked": 0, "assignee": null, "milestone": null, "comments": 9, "created_at": "2020-04-22T22:17:47Z", "updated_at": "2020-04-27T16:32:44Z", "closed_at": "2020-04-27T16:30:26Z", "author_association": "OWNER", "pull_request": null, "body": "My Datasette projects increasingly take on the following structure:\r\n\r\n- `metadata.json` with the metadata\r\n- One or more `something.db` database files\r\n- A `templates/` folder with some custom templates\r\n- A `plugins/` folder with some custom plugins\r\n\r\nThen I have to run Datasette like this:\r\n\r\n datasette *.db -m metadata.json --template-dir=templates --plugins-dir=plugins\r\n\r\nIt would be really interesting if Datasette had a special mode where you could point it at a directory with the above layout and it would automatically configure itself based on the contents.\r\n\r\nMaybe even allow `datasette serve` to detect if it was passed a single argument that's a directory, not a file, and kick in to \"directory layout configuration mode\" in that case:\r\n\r\n datasette .\r\n", "repo": {"value": 107914493, "label": "datasette"}, "type": "issue", "active_lock_reason": null, "performed_via_github_app": null, "reactions": "{\"url\": \"https://api.github.com/repos/simonw/datasette/issues/731/reactions\", \"total_count\": 2, \"+1\": 2, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "draft": null, "state_reason": "completed"} {"id": 607107849, "node_id": "MDExOlB1bGxSZXF1ZXN0NDA5MTUzODcw", "number": 739, "title": "Configuration directory mode", "user": {"value": 9599, "label": "simonw"}, "state": "closed", "locked": 0, "assignee": null, "milestone": null, "comments": 3, "created_at": "2020-04-26T20:37:46Z", "updated_at": "2020-04-27T16:30:25Z", "closed_at": "2020-04-27T16:30:25Z", "author_association": "OWNER", "pull_request": "simonw/datasette/pulls/739", "body": "Refs #731\r\n\r\nTODO:\r\n\r\n- [x] Decide how to combine explicit command-line options with items detected from the directory structure\r\n- [x] Add unit tests\r\n- [x] Implement `inspect-data.json` mechanism for populating `immutables`\r\n- [x] Add documentation", "repo": {"value": 107914493, "label": "datasette"}, "type": "pull", "active_lock_reason": null, "performed_via_github_app": null, "reactions": "{\"url\": \"https://api.github.com/repos/simonw/datasette/issues/739/reactions\", \"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "draft": 0, "state_reason": null} {"id": 607086780, "node_id": "MDU6SXNzdWU2MDcwODY3ODA=", "number": 738, "title": "Pass a request object to custom page templates", "user": {"value": 9599, "label": "simonw"}, "state": "closed", "locked": 0, "assignee": null, "milestone": null, "comments": 1, "created_at": "2020-04-26T18:57:48Z", "updated_at": "2020-04-26T19:01:54Z", "closed_at": "2020-04-26T19:01:54Z", "author_association": "OWNER", "pull_request": null, "body": "Follow-up to #648. I'm not passing a request object to `.render_template()` at the moment, which breaks any other custom plugins using e.g. `extra_template_vars()` that were expecting to be able to access the request.", "repo": {"value": 107914493, "label": "datasette"}, "type": "issue", "active_lock_reason": null, "performed_via_github_app": null, "reactions": "{\"url\": \"https://api.github.com/repos/simonw/datasette/issues/738/reactions\", \"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "draft": null, "state_reason": "completed"} {"id": 607067303, "node_id": "MDExOlB1bGxSZXF1ZXN0NDA5MTIzODk3", "number": 737, "title": "Custom pages mechanism, refs #648", "user": {"value": 9599, "label": "simonw"}, "state": "closed", "locked": 0, "assignee": null, "milestone": null, "comments": 4, "created_at": "2020-04-26T17:31:41Z", "updated_at": "2020-04-26T18:46:43Z", "closed_at": "2020-04-26T18:46:43Z", "author_association": "OWNER", "pull_request": "simonw/datasette/pulls/737", "body": "Refs #648. TODO:\r\n- [x] Pass a `view_name` to `render_template()`\r\n- [x] Mechanism for custom status code / headers / redirect\r\n- [x] Documentation", "repo": {"value": 107914493, "label": "datasette"}, "type": "pull", "active_lock_reason": null, "performed_via_github_app": null, "reactions": "{\"url\": \"https://api.github.com/repos/simonw/datasette/issues/737/reactions\", \"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "draft": 0, "state_reason": null} {"id": 598013965, "node_id": "MDU6SXNzdWU1OTgwMTM5NjU=", "number": 724, "title": "--plugin-secret over-rides existing metadata.json plugin config", "user": {"value": 9599, "label": "simonw"}, "state": "closed", "locked": 0, "assignee": null, "milestone": null, "comments": 3, "created_at": "2020-04-10T17:56:30Z", "updated_at": "2020-04-16T04:58:12Z", "closed_at": "2020-04-10T18:34:21Z", "author_association": "OWNER", "pull_request": null, "body": "This means if you use `--plugin-secret` at all (with e.g. `publish cloudrun`) any existing plugin configuration in your `metadata.json` will be ignored.\r\n\r\nhttps://github.com/simonw/datasette/blob/af9cd4ca64652fae262e6f7b5d201f6e0adc989b/datasette/publish/cloudrun.py#L98-L109\r\n\r\n", "repo": {"value": 107914493, "label": "datasette"}, "type": "issue", "active_lock_reason": null, "performed_via_github_app": null, "reactions": "{\"url\": \"https://api.github.com/repos/simonw/datasette/issues/724/reactions\", \"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "draft": null, "state_reason": "completed"} {"id": 600583271, "node_id": "MDU6SXNzdWU2MDA1ODMyNzE=", "number": 727, "title": "Custom CSS class on body for styling canned queries", "user": {"value": 9599, "label": "simonw"}, "state": "closed", "locked": 0, "assignee": null, "milestone": null, "comments": 5, "created_at": "2020-04-15T20:57:32Z", "updated_at": "2020-04-15T21:14:58Z", "closed_at": "2020-04-15T21:07:50Z", "author_association": "OWNER", "pull_request": null, "body": "https://latest.datasette.io/fixtures/neighborhood_search is a canned query page.\r\n\r\nOne of the templates scanned is `query-fixtures-neighborhood_search.html`\r\n\r\nBUT... the body CSS class just looks like this:\r\n```html\r\n\r\n```\r\nI would be useful if that included a class that can be used to style that specific canned query page.", "repo": {"value": 107914493, "label": "datasette"}, "type": "issue", "active_lock_reason": null, "performed_via_github_app": null, "reactions": "{\"url\": \"https://api.github.com/repos/simonw/datasette/issues/727/reactions\", \"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "draft": null, "state_reason": "completed"} {"id": 594189527, "node_id": "MDU6SXNzdWU1OTQxODk1Mjc=", "number": 717, "title": "See if I can get Datasette working on Zeit Now v2", "user": {"value": 9599, "label": "simonw"}, "state": "closed", "locked": 0, "assignee": null, "milestone": null, "comments": 10, "created_at": "2020-04-05T00:56:48Z", "updated_at": "2020-04-06T22:47:22Z", "closed_at": "2020-04-06T22:47:21Z", "author_association": "OWNER", "pull_request": null, "body": "I thought this was impossible because AWS Lambda doesn't ship the `sqlite3` standard library module... but apparenttly that's not the case on Now v2 any more!\r\n\r\nhttps://now-2-python-versions-ks69olzpi.now.sh/api\r\n\r\n```\r\n _________________________________________________________________________________________________________________________________________________________________ \r\n/ Hello from Python from a ZEIT Now Serverless Function! Version is 3.6.10 (default, Mar 10 2020, 22:54:43) \\\r\n\\ [GCC 4.8.3 20140911 (Red Hat 4.8.3-9)], sqlite3 module = , sqlite3 version = [('3.7.17',)] /\r\n ----------------------------------------------------------------------------------------------------------------------------------------------------------------- \r\n \\ ^__^\r\n \\ (oo)\\_______\r\n (__)\\ )\\/\\\r\n ||----w |\r\n || ||\r\n```\r\nThat's from shipping this code as `api/index.py`:\r\n```python\r\nfrom http.server import BaseHTTPRequestHandler\r\nfrom cowpy import cow\r\nimport sys\r\n\r\n\r\ntry:\r\n import sqlite3\r\nexcept ImportError:\r\n sqlite3 = None\r\n\r\n\r\nclass handler(BaseHTTPRequestHandler):\r\n def do_GET(self):\r\n self.send_response(200)\r\n self.send_header(\"Content-type\", \"text/plain\")\r\n self.end_headers()\r\n message = cow.Cowacter().milk(\r\n \"Hello from Python from a ZEIT Now Serverless Function! Version is {}, sqlite3 module = {}, sqlite3 version = {}\".format(\r\n sys.version, sqlite3, sqlite3.connect(\":memory:\").execute(\"select sqlite_version()\").fetchall()\r\n )\r\n )\r\n self.wfile.write(message.encode())\r\n return\r\n```\r\nNow v2 supports ASGI so this might be possible without too much work: https://zeit.co/docs/runtimes#advanced-usage/advanced-python-usage/asynchronous-server-gateway-interface", "repo": {"value": 107914493, "label": "datasette"}, "type": "issue", "active_lock_reason": null, "performed_via_github_app": null, "reactions": "{\"url\": \"https://api.github.com/repos/simonw/datasette/issues/717/reactions\", \"total_count\": 1, \"+1\": 1, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "draft": null, "state_reason": "completed"} {"id": 594168758, "node_id": "MDU6SXNzdWU1OTQxNjg3NTg=", "number": 716, "title": "extra_template_vars() sending wrong view_name for index", "user": {"value": 9599, "label": "simonw"}, "state": "closed", "locked": 0, "assignee": null, "milestone": null, "comments": 8, "created_at": "2020-04-04T23:57:09Z", "updated_at": "2020-04-05T20:04:08Z", "closed_at": "2020-04-05T18:28:48Z", "author_association": "OWNER", "pull_request": null, "body": "See https://github.com/simonw/museums/issues/20#issuecomment-609103663 - at some point between 286ed286b68793532c2a38436a08343b45cfbc91 and current master (e0e7a0facfc935a835cd73c720bc46661462f0b1 today) a bug was introduced where the `extra_template_vars(request, view_name)` plugin hook started being passed `None` instead of `index` for the `view_name` parameter on the site index page.", "repo": {"value": 107914493, "label": "datasette"}, "type": "issue", "active_lock_reason": null, "performed_via_github_app": null, "reactions": "{\"url\": \"https://api.github.com/repos/simonw/datasette/issues/716/reactions\", \"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "draft": null, "state_reason": "completed"} {"id": 574043218, "node_id": "MDU6SXNzdWU1NzQwNDMyMTg=", "number": 693, "title": "Variables from extra_template_vars() not exposed in _context=1", "user": {"value": 9599, "label": "simonw"}, "state": "closed", "locked": 0, "assignee": null, "milestone": null, "comments": 3, "created_at": "2020-03-02T15:14:51Z", "updated_at": "2020-04-05T19:12:48Z", "closed_at": "2020-04-05T19:12:48Z", "author_association": "OWNER", "pull_request": null, "body": "The `_context=1` debugging mode does not show variables that should have been added to the context by the `extra_template_vars()` plugin hook.", "repo": {"value": 107914493, "label": "datasette"}, "type": "issue", "active_lock_reason": null, "performed_via_github_app": null, "reactions": "{\"url\": \"https://api.github.com/repos/simonw/datasette/issues/693/reactions\", \"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "draft": null, "state_reason": "completed"} {"id": 521323012, "node_id": "MDExOlB1bGxSZXF1ZXN0MzM5NzIyNzkw", "number": 627, "title": "Support Python 3.8, stop supporting Python 3.5", "user": {"value": 9599, "label": "simonw"}, "state": "closed", "locked": 0, "assignee": null, "milestone": null, "comments": 2, "created_at": "2019-11-12T04:36:33Z", "updated_at": "2020-04-05T10:23:58Z", "closed_at": "2019-11-12T05:09:12Z", "author_association": "OWNER", "pull_request": "simonw/datasette/pulls/627", "body": "Refs #622", "repo": {"value": 107914493, "label": "datasette"}, "type": "pull", "active_lock_reason": null, "performed_via_github_app": null, "reactions": "{\"url\": \"https://api.github.com/repos/simonw/datasette/issues/627/reactions\", \"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "draft": 0, "state_reason": null} {"id": 587322443, "node_id": "MDU6SXNzdWU1ODczMjI0NDM=", "number": 710, "title": "Remove Zeit Now v1 support", "user": {"value": 9599, "label": "simonw"}, "state": "closed", "locked": 0, "assignee": null, "milestone": null, "comments": 2, "created_at": "2020-03-24T22:39:49Z", "updated_at": "2020-04-04T23:05:12Z", "closed_at": "2020-04-04T23:05:12Z", "author_association": "OWNER", "pull_request": null, "body": "It will remain supported as a plugin but since no-one can sign up for Docker hosting any more (for over a year now) there's no point including it in Datasette core.", "repo": {"value": 107914493, "label": "datasette"}, "type": "issue", "active_lock_reason": null, "performed_via_github_app": null, "reactions": "{\"url\": \"https://api.github.com/repos/simonw/datasette/issues/710/reactions\", \"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "draft": null, "state_reason": "completed"} {"id": 593006814, "node_id": "MDU6SXNzdWU1OTMwMDY4MTQ=", "number": 715, "title": "Refactor duplicate cell display logic", "user": {"value": 9599, "label": "simonw"}, "state": "open", "locked": 0, "assignee": null, "milestone": null, "comments": 0, "created_at": "2020-04-03T00:58:11Z", "updated_at": "2020-04-03T00:58:11Z", "closed_at": null, "author_association": "OWNER", "pull_request": null, "body": "The logic for rendering cells in table view and in database (or canned query) view is currently very similar:\r\n\r\nhttps://github.com/simonw/datasette/blob/7656fd64d8b6a32ebc34d89c1b8711cc5ea240f7/datasette/views/base.py#L514-L539\r\n\r\nCompared with:\r\n\r\nhttps://github.com/simonw/datasette/blob/7656fd64d8b6a32ebc34d89c1b8711cc5ea240f7/datasette/views/table.py#L104-L195\r\n\r\nI'll be changing this a bit in #698 but I should still try to clean this up more further in the future.", "repo": {"value": 107914493, "label": "datasette"}, "type": "issue", "active_lock_reason": null, "performed_via_github_app": null, "reactions": "{\"url\": \"https://api.github.com/repos/simonw/datasette/issues/715/reactions\", \"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "draft": null, "state_reason": null} {"id": 592829135, "node_id": "MDU6SXNzdWU1OTI4MjkxMzU=", "number": 713, "title": "Support YAML in metadata - metadata.yaml", "user": {"value": 9599, "label": "simonw"}, "state": "closed", "locked": 0, "assignee": null, "milestone": null, "comments": 6, "created_at": "2020-04-02T18:10:05Z", "updated_at": "2020-04-02T19:36:17Z", "closed_at": "2020-04-02T19:30:55Z", "author_association": "OWNER", "pull_request": null, "body": "I was originally going to do this with a plugin - see #357 - but the more I work with `metadata.json` the more I want it to just accept YAML as an optional alternative to JSON.\r\n\r\nThe best example why is still this one: https://github.com/simonw/russian-ira-facebook-ads-datasette/blob/master/russian-ads-metadata.yaml\r\n\r\nYAML is just SO much better than JSON for multi-line strings - in particular HTML and SQL, both of which are common in `metadata.json` files.", "repo": {"value": 107914493, "label": "datasette"}, "type": "issue", "active_lock_reason": null, "performed_via_github_app": null, "reactions": "{\"url\": \"https://api.github.com/repos/simonw/datasette/issues/713/reactions\", \"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "draft": null, "state_reason": "completed"} {"id": 592844348, "node_id": "MDExOlB1bGxSZXF1ZXN0Mzk3NzQ5NjUz", "number": 714, "title": "--metadata accepts YAML as well as JSON", "user": {"value": 9599, "label": "simonw"}, "state": "closed", "locked": 0, "assignee": null, "milestone": null, "comments": 1, "created_at": "2020-04-02T18:36:02Z", "updated_at": "2020-04-02T19:30:54Z", "closed_at": "2020-04-02T19:30:54Z", "author_association": "OWNER", "pull_request": "simonw/datasette/pulls/714", "body": "Refs #713. Still needs tests and documentation.", "repo": {"value": 107914493, "label": "datasette"}, "type": "pull", "active_lock_reason": null, "performed_via_github_app": null, "reactions": "{\"url\": \"https://api.github.com/repos/simonw/datasette/issues/714/reactions\", \"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "draft": 0, "state_reason": null} {"id": 587222354, "node_id": "MDU6SXNzdWU1ODcyMjIzNTQ=", "number": 707, "title": "Consider configuring Jinja in Datasette() constructor, not .app()", "user": {"value": 9599, "label": "simonw"}, "state": "closed", "locked": 0, "assignee": null, "milestone": null, "comments": 0, "created_at": "2020-03-24T19:19:58Z", "updated_at": "2020-03-27T01:12:57Z", "closed_at": "2020-03-27T01:12:57Z", "author_association": "OWNER", "pull_request": null, "body": "Right now the following fails with an error:\r\n```python\r\nds = Datasette([], template_dir=\".\")\r\nrendered = await ds.render_template(\"index.html\")\r\n```\r\nThe error is:\r\n```\r\n async def render_template(\r\n self, templates, context=None, request=None, view_name=None\r\n ):\r\n context = context or {}\r\n if isinstance(templates, Template):\r\n template = templates\r\n select_templates = []\r\n else:\r\n if isinstance(templates, str):\r\n templates = [templates]\r\n> template = self.jinja_env.select_template(templates)\r\nE AttributeError: 'Datasette' object has no attribute 'jinja_env'\r\n```\r\nThis is because `jinja_env` is configured in the `.app()` method, here:\r\n\r\nhttps://github.com/simonw/datasette/blob/a498d0fe6590f9bdbc4faf9e0dd5faeb3b06002c/datasette/app.py#L609-L633\r\n\r\nThis is a little surprising, especially now that `.render_template()` is part of the documented internals API: https://datasette.readthedocs.io/en/stable/internals.html#render-template-template-context-none-request-none\r\n\r\nMaybe this should happen in the Datasette class constructor instead.", "repo": {"value": 107914493, "label": "datasette"}, "type": "issue", "active_lock_reason": null, "performed_via_github_app": null, "reactions": "{\"url\": \"https://api.github.com/repos/simonw/datasette/issues/707/reactions\", \"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "draft": null, "state_reason": "completed"} {"id": 565064079, "node_id": "MDExOlB1bGxSZXF1ZXN0Mzc1MTgwODMy", "number": 672, "title": "--dirs option for scanning directories for SQLite databases", "user": {"value": 9599, "label": "simonw"}, "state": "open", "locked": 0, "assignee": null, "milestone": null, "comments": 15, "created_at": "2020-02-14T02:25:52Z", "updated_at": "2020-03-27T01:03:53Z", "closed_at": null, "author_association": "OWNER", "pull_request": "simonw/datasette/pulls/672", "body": "Refs #417.", "repo": {"value": 107914493, "label": "datasette"}, "type": "pull", "active_lock_reason": null, "performed_via_github_app": null, "reactions": "{\"url\": \"https://api.github.com/repos/simonw/datasette/issues/672/reactions\", \"total_count\": 1, \"+1\": 1, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "draft": 0, "state_reason": null} {"id": 565552217, "node_id": "MDU6SXNzdWU1NjU1NTIyMTc=", "number": 674, "title": "Rethink how sanity checks work", "user": {"value": 9599, "label": "simonw"}, "state": "closed", "locked": 0, "assignee": null, "milestone": null, "comments": 5, "created_at": "2020-02-14T20:57:02Z", "updated_at": "2020-03-26T17:19:23Z", "closed_at": "2020-02-15T17:57:46Z", "author_association": "OWNER", "pull_request": null, "body": "If you specify a file to open using `files` or `-i` then Datasette should show a useful error message and fail to start.\r\n\r\nFiles found by scanning a directory #672 should just be skipped.\r\n\r\n_Split off from comment by @simonw in https://github.com/simonw/datasette/issues/673#issuecomment-586455321_", "repo": {"value": 107914493, "label": "datasette"}, "type": "issue", "active_lock_reason": null, "performed_via_github_app": null, "reactions": "{\"url\": \"https://api.github.com/repos/simonw/datasette/issues/674/reactions\", \"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "draft": null, "state_reason": "completed"} {"id": 588108428, "node_id": "MDU6SXNzdWU1ODgxMDg0Mjg=", "number": 712, "title": "base_url doesn't entirely work for running Datasette inside Binder", "user": {"value": 9599, "label": "simonw"}, "state": "closed", "locked": 0, "assignee": null, "milestone": null, "comments": 12, "created_at": "2020-03-26T02:25:55Z", "updated_at": "2020-03-26T15:11:49Z", "closed_at": "2020-03-26T14:35:43Z", "author_association": "OWNER", "pull_request": null, "body": "> Thanks! I'm trying to launch Datasette from *within* a notebook using the jupyter-server-proxy and the new `base_url` parameter. While the assets load ok, and the breadcrumb navigation works, the facet links don't seem to use the `base_url`. Or have I missed something?\r\n\r\n_Originally posted by @wragge in https://github.com/simonw/datasette/issues/394#issuecomment-604166918_", "repo": {"value": 107914493, "label": "datasette"}, "type": "issue", "active_lock_reason": null, "performed_via_github_app": null, "reactions": "{\"url\": \"https://api.github.com/repos/simonw/datasette/issues/712/reactions\", \"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "draft": null, "state_reason": "completed"} {"id": 585390482, "node_id": "MDU6SXNzdWU1ODUzOTA0ODI=", "number": 702, "title": "Option in metadata.json to set default sort order for a table", "user": {"value": 9599, "label": "simonw"}, "state": "closed", "locked": 0, "assignee": null, "milestone": {"value": 5234079, "label": "Datasette 0.39"}, "comments": 5, "created_at": "2020-03-21T00:19:56Z", "updated_at": "2020-03-25T04:19:36Z", "closed_at": "2020-03-22T02:40:35Z", "author_association": "OWNER", "pull_request": null, "body": "If you access the table page without any `?_sort` or `?_sort_desc` arguments it currently defaults to order by primary key - would be neat to be able to change that.", "repo": {"value": 107914493, "label": "datasette"}, "type": "issue", "active_lock_reason": null, "performed_via_github_app": null, "reactions": "{\"url\": \"https://api.github.com/repos/simonw/datasette/issues/702/reactions\", \"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "draft": null, "state_reason": "completed"} {"id": 587398703, "node_id": "MDU6SXNzdWU1ODczOTg3MDM=", "number": 711, "title": "Release notes for Datasette 0.39", "user": {"value": 9599, "label": "simonw"}, "state": "closed", "locked": 0, "assignee": null, "milestone": {"value": 5234079, "label": "Datasette 0.39"}, "comments": 2, "created_at": "2020-03-25T02:31:13Z", "updated_at": "2020-03-25T04:06:55Z", "closed_at": "2020-03-25T04:06:55Z", "author_association": "OWNER", "pull_request": null, "body": "Then I can ship it.", "repo": {"value": 107914493, "label": "datasette"}, "type": "issue", "active_lock_reason": null, "performed_via_github_app": null, "reactions": "{\"url\": \"https://api.github.com/repos/simonw/datasette/issues/711/reactions\", \"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "draft": null, "state_reason": "completed"} {"id": 585626199, "node_id": "MDU6SXNzdWU1ODU2MjYxOTk=", "number": 705, "title": "latest.datasette.io is no longer updating", "user": {"value": 9599, "label": "simonw"}, "state": "closed", "locked": 0, "assignee": null, "milestone": {"value": 5234079, "label": "Datasette 0.39"}, "comments": 15, "created_at": "2020-03-22T01:59:30Z", "updated_at": "2020-03-25T02:30:24Z", "closed_at": "2020-03-25T02:30:24Z", "author_association": "OWNER", "pull_request": null, "body": "https://latest.datasette.io/-/versions is stuck on 0.35.", "repo": {"value": 107914493, "label": "datasette"}, "type": "issue", "active_lock_reason": null, "performed_via_github_app": null, "reactions": "{\"url\": \"https://api.github.com/repos/simonw/datasette/issues/705/reactions\", \"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "draft": null, "state_reason": "completed"} {"id": 587302139, "node_id": "MDExOlB1bGxSZXF1ZXN0MzkzMjc0NDMz", "number": 708, "title": "base_url configuration setting, refs #394", "user": {"value": 9599, "label": "simonw"}, "state": "closed", "locked": 0, "assignee": null, "milestone": {"value": 5234079, "label": "Datasette 0.39"}, "comments": 2, "created_at": "2020-03-24T21:52:00Z", "updated_at": "2020-03-25T00:18:44Z", "closed_at": "2020-03-25T00:18:44Z", "author_association": "OWNER", "pull_request": "simonw/datasette/pulls/708", "body": "Pull request implementing #394", "repo": {"value": 107914493, "label": "datasette"}, "type": "pull", "active_lock_reason": null, "performed_via_github_app": null, "reactions": "{\"url\": \"https://api.github.com/repos/simonw/datasette/issues/708/reactions\", \"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "draft": 0, "state_reason": null} {"id": 583970196, "node_id": "MDU6SXNzdWU1ODM5NzAxOTY=", "number": 701, "title": "Search box CSS doesn't look great on OS X Safari", "user": {"value": 9599, "label": "simonw"}, "state": "closed", "locked": 0, "assignee": null, "milestone": {"value": 5234079, "label": "Datasette 0.39"}, "comments": 3, "created_at": "2020-03-18T20:00:52Z", "updated_at": "2020-03-24T22:57:18Z", "closed_at": "2020-03-24T22:57:18Z", "author_association": "OWNER", "pull_request": null, "body": "\"twitter__tweets__68_773_rows_where_sorted_by_id_descending_and_twitter__tweets__955_rows\"\r\n", "repo": {"value": 107914493, "label": "datasette"}, "type": "issue", "active_lock_reason": null, "performed_via_github_app": null, "reactions": "{\"url\": \"https://api.github.com/repos/simonw/datasette/issues/701/reactions\", \"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "draft": null, "state_reason": "completed"} {"id": 585597329, "node_id": "MDU6SXNzdWU1ODU1OTczMjk=", "number": 704, "title": "Add datasette-publish-fly to Datasette Publish documentation", "user": {"value": 9599, "label": "simonw"}, "state": "closed", "locked": 0, "assignee": null, "milestone": {"value": 5234079, "label": "Datasette 0.39"}, "comments": 1, "created_at": "2020-03-21T22:25:10Z", "updated_at": "2020-03-24T22:39:09Z", "closed_at": "2020-03-24T22:39:09Z", "author_association": "OWNER", "pull_request": null, "body": "It's a cool example of a plugin that provides a new publish provider - worth mentioning on https://datasette.readthedocs.io/en/stable/publish.html", "repo": {"value": 107914493, "label": "datasette"}, "type": "issue", "active_lock_reason": null, "performed_via_github_app": null, "reactions": "{\"url\": \"https://api.github.com/repos/simonw/datasette/issues/704/reactions\", \"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "draft": null, "state_reason": "completed"} {"id": 587314002, "node_id": "MDU6SXNzdWU1ODczMTQwMDI=", "number": 709, "title": "Each plugin hook should link to example plugins built with it", "user": {"value": 9599, "label": "simonw"}, "state": "closed", "locked": 0, "assignee": null, "milestone": {"value": 5234079, "label": "Datasette 0.39"}, "comments": 1, "created_at": "2020-03-24T22:18:48Z", "updated_at": "2020-03-24T22:30:10Z", "closed_at": "2020-03-24T22:29:43Z", "author_association": "OWNER", "pull_request": null, "body": "", "repo": {"value": 107914493, "label": "datasette"}, "type": "issue", "active_lock_reason": null, "performed_via_github_app": null, "reactions": "{\"url\": \"https://api.github.com/repos/simonw/datasette/issues/709/reactions\", \"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "draft": null, "state_reason": "completed"} {"id": 315960272, "node_id": "MDU6SXNzdWUzMTU5NjAyNzI=", "number": 227, "title": "prepare_context() plugin hook", "user": {"value": 9599, "label": "simonw"}, "state": "closed", "locked": 0, "assignee": null, "milestone": null, "comments": 8, "created_at": "2018-04-19T16:55:26Z", "updated_at": "2020-03-24T22:19:54Z", "closed_at": "2020-03-24T22:19:54Z", "author_association": "OWNER", "pull_request": null, "body": "This would be called with the context dictionary before each template is rendered. It would have the opportunity to modify that context.", "repo": {"value": 107914493, "label": "datasette"}, "type": "issue", "active_lock_reason": null, "performed_via_github_app": null, "reactions": "{\"url\": \"https://api.github.com/repos/simonw/datasette/issues/227/reactions\", \"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "draft": null, "state_reason": "completed"} {"id": 569237568, "node_id": "MDU6SXNzdWU1NjkyMzc1Njg=", "number": 677, "title": "The first time you click sort by ID it should show you results in reverse order", "user": {"value": 9599, "label": "simonw"}, "state": "closed", "locked": 0, "assignee": null, "milestone": null, "comments": 1, "created_at": "2020-02-21T23:38:50Z", "updated_at": "2020-03-21T23:57:46Z", "closed_at": "2020-03-21T23:57:46Z", "author_association": "OWNER", "pull_request": null, "body": "e.g. on https://latest.datasette.io/fixtures/roadside_attractions\r\n\r\nClicking the \"pk\" column header doesn't actually do anything - it sorts by pk asc but since the page was already sorted like that nothing useful changes.\r\n\r\nThe first click on a primary key column that the page is already implicitly sorted by should instead enable sort descending on that column.", "repo": {"value": 107914493, "label": "datasette"}, "type": "issue", "active_lock_reason": null, "performed_via_github_app": null, "reactions": "{\"url\": \"https://api.github.com/repos/simonw/datasette/issues/677/reactions\", \"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "draft": null, "state_reason": "completed"} {"id": 582713554, "node_id": "MDU6SXNzdWU1ODI3MTM1NTQ=", "number": 700, "title": "Request object utility for handling POST form data", "user": {"value": 9599, "label": "simonw"}, "state": "closed", "locked": 0, "assignee": null, "milestone": null, "comments": 1, "created_at": "2020-03-17T02:44:59Z", "updated_at": "2020-03-17T02:47:50Z", "closed_at": "2020-03-17T02:47:50Z", "author_association": "OWNER", "pull_request": null, "body": "> This is also going to need me to handle POST form submissions which means I need to be able to parse the form body. I guess that will go in [datasette/utils/asgi.py](https://github.com/simonw/datasette/blob/master/datasette/utils/asgi.py).\r\n\r\n_Originally posted by @simonw in https://github.com/simonw/datasette/issues/698#issuecomment-599704264_", "repo": {"value": 107914493, "label": "datasette"}, "type": "issue", "active_lock_reason": null, "performed_via_github_app": null, "reactions": "{\"url\": \"https://api.github.com/repos/simonw/datasette/issues/700/reactions\", \"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "draft": null, "state_reason": "completed"} {"id": 275087397, "node_id": "MDU6SXNzdWUyNzUwODczOTc=", "number": 120, "title": "Plugin that adds an authentication layer of some sort", "user": {"value": 9599, "label": "simonw"}, "state": "closed", "locked": 0, "assignee": null, "milestone": null, "comments": 4, "created_at": "2017-11-18T15:39:13Z", "updated_at": "2020-03-16T18:48:06Z", "closed_at": "2020-03-16T18:48:06Z", "author_association": "OWNER", "pull_request": null, "body": "Would allow people who want to host private data to do so.\r\n.sh ", "repo": {"value": 107914493, "label": "datasette"}, "type": "issue", "active_lock_reason": null, "performed_via_github_app": null, "reactions": "{\"url\": \"https://api.github.com/repos/simonw/datasette/issues/120/reactions\", \"total_count\": 7, \"+1\": 5, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 2, \"rocket\": 0, \"eyes\": 0}", "draft": null, "state_reason": "completed"} {"id": 577578306, "node_id": "MDU6SXNzdWU1Nzc1NzgzMDY=", "number": 697, "title": "index.html is not reliably loaded from a plugin", "user": {"value": 9599, "label": "simonw"}, "state": "closed", "locked": 0, "assignee": null, "milestone": null, "comments": 7, "created_at": "2020-03-08T22:37:55Z", "updated_at": "2020-03-08T23:33:28Z", "closed_at": "2020-03-08T23:11:27Z", "author_association": "OWNER", "pull_request": null, "body": "Lots of detail in https://github.com/simonw/datasette-search-all/issues/2 - short version is that I have a plugin with its own `index.html` template and Datasette intermittently fails to load it and uses the default `index.html` that ships with Datasette instead.\r\n\r\nRelated:\r\n\r\n* #689: \"Templates considered\" comment broken in >=0.35\r\n* #693: Variables from extra_template_vars() not exposed in _context=1 (may as well fix this while I'm in there)", "repo": {"value": 107914493, "label": "datasette"}, "type": "issue", "active_lock_reason": null, "performed_via_github_app": null, "reactions": "{\"url\": \"https://api.github.com/repos/simonw/datasette/issues/697/reactions\", \"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "draft": null, "state_reason": "completed"} {"id": 576711589, "node_id": "MDU6SXNzdWU1NzY3MTE1ODk=", "number": 695, "title": "Update SQLite bundled with Docker container", "user": {"value": 9599, "label": "simonw"}, "state": "closed", "locked": 0, "assignee": null, "milestone": null, "comments": 7, "created_at": "2020-03-06T05:42:12Z", "updated_at": "2020-03-08T23:33:23Z", "closed_at": "2020-03-06T06:15:27Z", "author_association": "OWNER", "pull_request": null, "body": "It's 3.26.0 at the moment:\r\nhttps://github.com/simonw/datasette/blob/af9cd4ca64652fae262e6f7b5d201f6e0adc989b/Dockerfile#L9-L11\r\nMost recent release is 3.31.1: https://www.sqlite.org/releaselog/3_31_1.html", "repo": {"value": 107914493, "label": "datasette"}, "type": "issue", "active_lock_reason": null, "performed_via_github_app": null, "reactions": "{\"url\": \"https://api.github.com/repos/simonw/datasette/issues/695/reactions\", \"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "draft": null, "state_reason": "completed"} {"id": 574035432, "node_id": "MDU6SXNzdWU1NzQwMzU0MzI=", "number": 692, "title": "is_hidden_table context variable on table.html page", "user": {"value": 9599, "label": "simonw"}, "state": "open", "locked": 0, "assignee": null, "milestone": null, "comments": 1, "created_at": "2020-03-02T15:03:25Z", "updated_at": "2020-03-02T15:03:48Z", "closed_at": null, "author_association": "OWNER", "pull_request": null, "body": "It's useful to know if a table is hidden when rendering that page. `datasette-configure-fts` for example may want to disallow enabling search on hidden tables.", "repo": {"value": 107914493, "label": "datasette"}, "type": "issue", "active_lock_reason": null, "performed_via_github_app": null, "reactions": "{\"url\": \"https://api.github.com/repos/simonw/datasette/issues/692/reactions\", \"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "draft": null, "state_reason": null} {"id": 573088799, "node_id": "MDExOlB1bGxSZXF1ZXN0MzgxNjY2Nzc3", "number": 688, "title": "Don't count rows on homepage for DBs > 100MB", "user": {"value": 9599, "label": "simonw"}, "state": "closed", "locked": 0, "assignee": null, "milestone": null, "comments": 0, "created_at": "2020-02-29T01:01:06Z", "updated_at": "2020-02-29T01:08:30Z", "closed_at": "2020-02-29T01:08:29Z", "author_association": "OWNER", "pull_request": "simonw/datasette/pulls/688", "body": "Closes #649.", "repo": {"value": 107914493, "label": "datasette"}, "type": "pull", "active_lock_reason": null, "performed_via_github_app": null, "reactions": "{\"url\": \"https://api.github.com/repos/simonw/datasette/issues/688/reactions\", \"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "draft": 0, "state_reason": null} {"id": 534530973, "node_id": "MDU6SXNzdWU1MzQ1MzA5NzM=", "number": 649, "title": "Reduce table counts on index page with many databases", "user": {"value": 9599, "label": "simonw"}, "state": "closed", "locked": 0, "assignee": null, "milestone": null, "comments": 2, "created_at": "2019-12-08T11:56:37Z", "updated_at": "2020-02-29T01:08:29Z", "closed_at": "2020-02-29T01:08:29Z", "author_association": "OWNER", "pull_request": null, "body": "Since #467 the index page has attempted to optimistically count times.\r\n\r\nMy personal Dogsheep has enough connected databases and tables that the page can still take way too long to load - sometimes more than twenty seconds.", "repo": {"value": 107914493, "label": "datasette"}, "type": "issue", "active_lock_reason": null, "performed_via_github_app": null, "reactions": "{\"url\": \"https://api.github.com/repos/simonw/datasette/issues/649/reactions\", \"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "draft": null, "state_reason": "completed"} {"id": 502355384, "node_id": "MDU6SXNzdWU1MDIzNTUzODQ=", "number": 580, "title": "Testing utilities should be available to plugins", "user": {"value": 9599, "label": "simonw"}, "state": "closed", "locked": 0, "assignee": null, "milestone": null, "comments": 5, "created_at": "2019-10-03T23:58:26Z", "updated_at": "2020-02-28T07:58:46Z", "closed_at": "2020-02-28T07:58:46Z", "author_association": "OWNER", "pull_request": null, "body": "I'm trying to write a plugin at the moment ([datasette-atom](https://github.com/simonw/datasette-atom)) which needs to run unit tests against a full in-memory Datasette instance, in the same way that the Datasette test suite itself works.\r\n\r\nI got it working by creating copies of the [TestClient and TestResponse classes](https://github.com/simonw/datasette/blob/a314b761866d250c16f1ff6dd682010cf4181eb4/tests/fixtures.py#L22-L96) within the plugin itself:\r\n\r\nhttps://github.com/simonw/datasette-atom/commit/c0e3bd9556d7b31f253a8bf666d42205cd24f4fc#diff-33337525d2d877f7cc7f33737bfd2d7b\r\n\r\nI had to do this because those classes are in the `tests/` directory within Datasette, so they don't get included in the package that ships to PyPI.\r\n\r\nIt would be better if these classes were included in the main package in a way that made it easy for plugins to reuse them to write their own tests.", "repo": {"value": 107914493, "label": "datasette"}, "type": "issue", "active_lock_reason": null, "performed_via_github_app": null, "reactions": "{\"url\": \"https://api.github.com/repos/simonw/datasette/issues/580/reactions\", \"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "draft": null, "state_reason": "completed"} {"id": 570327466, "node_id": "MDExOlB1bGxSZXF1ZXN0Mzc5Mzc4Nzgw", "number": 686, "title": "?_searchmode=raw option", "user": {"value": 9599, "label": "simonw"}, "state": "closed", "locked": 0, "assignee": null, "milestone": null, "comments": 0, "created_at": "2020-02-25T05:45:50Z", "updated_at": "2020-02-25T05:56:09Z", "closed_at": "2020-02-25T05:56:04Z", "author_association": "OWNER", "pull_request": "simonw/datasette/pulls/686", "body": "Closes #676", "repo": {"value": 107914493, "label": "datasette"}, "type": "pull", "active_lock_reason": null, "performed_via_github_app": null, "reactions": "{\"url\": \"https://api.github.com/repos/simonw/datasette/issues/686/reactions\", \"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "draft": 0, "state_reason": null}