<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0" xmlns:itunes="http://www.itunes.com/dtds/podcast-1.0.dtd" xmlns:googleplay="http://www.google.com/schemas/play-podcasts/1.0"><channel><title><![CDATA[Datasette Newsletter]]></title><description><![CDATA[News about Datasette and the Datasette ecosystem]]></description><link>https://datasette.substack.com</link><image><url>https://substackcdn.com/image/fetch/$s_!Nk6p!,w_256,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2Fc79ad646-e7b3-42e9-9ac2-adf1c346be80_208x208.png</url><title>Datasette Newsletter</title><link>https://datasette.substack.com</link></image><generator>Substack</generator><lastBuildDate>Sat, 11 Apr 2026 21:51:55 GMT</lastBuildDate><atom:link href="https://datasette.substack.com/feed" rel="self" type="application/rss+xml"/><copyright><![CDATA[Simon Willison]]></copyright><language><![CDATA[en]]></language><webMaster><![CDATA[datasette@substack.com]]></webMaster><itunes:owner><itunes:email><![CDATA[datasette@substack.com]]></itunes:email><itunes:name><![CDATA[Simon Willison]]></itunes:name></itunes:owner><itunes:author><![CDATA[Simon Willison]]></itunes:author><googleplay:owner><![CDATA[datasette@substack.com]]></googleplay:owner><googleplay:email><![CDATA[datasette@substack.com]]></googleplay:email><googleplay:author><![CDATA[Simon Willison]]></googleplay:author><itunes:block><![CDATA[Yes]]></itunes:block><item><title><![CDATA[Datasette enrichments and Datasette comments]]></title><description><![CDATA[Two new features for collaborative data analysis with Datasette]]></description><link>https://datasette.substack.com/p/datasette-enrichments-and-datasette</link><guid isPermaLink="false">https://datasette.substack.com/p/datasette-enrichments-and-datasette</guid><dc:creator><![CDATA[Simon Willison]]></dc:creator><pubDate>Mon, 04 Dec 2023 23:06:27 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!o6f2!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F01027686-ea45-45b9-bbdf-377de311e2a7_2398x1468.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Two new features for Datasette this week: Datasette enrichments and Datasette comments. Combined, these features enable a powerful workflow for collaborative data analysis.</p><h2>Datasette enrichments</h2><p><a href="https://simonwillison.net/2023/Dec/1/datasette-enrichments/">Datasette Enrichments: a new plugin framework for augmenting your data</a> introduces a new <em>enrichments</em> framework for Datasette.</p><p>An enrichment is code that can be executed against every row (or a filtered subset of rows) in a table, modifying or enhancing that data in some way - including importing new data from external APIs.</p><p>One example is geocoding: populating latitude and longitude columns based on the contents of an address column, using an external geocoder such as <a href="https://opencagedata.com/">OpenCage</a>:</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!o6f2!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F01027686-ea45-45b9-bbdf-377de311e2a7_2398x1468.jpeg" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!o6f2!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F01027686-ea45-45b9-bbdf-377de311e2a7_2398x1468.jpeg 424w, https://substackcdn.com/image/fetch/$s_!o6f2!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F01027686-ea45-45b9-bbdf-377de311e2a7_2398x1468.jpeg 848w, https://substackcdn.com/image/fetch/$s_!o6f2!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F01027686-ea45-45b9-bbdf-377de311e2a7_2398x1468.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!o6f2!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F01027686-ea45-45b9-bbdf-377de311e2a7_2398x1468.jpeg 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!o6f2!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F01027686-ea45-45b9-bbdf-377de311e2a7_2398x1468.jpeg" width="1456" height="891" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/01027686-ea45-45b9-bbdf-377de311e2a7_2398x1468.jpeg&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:891,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;Datasette screenshot: Enrich data in Film_Locations_in_San_Francisco. 2,084 rows selected. OpenCage geocoder. Geocode to latitude/longitude points using OpenCage. Geocode input: {{ Locations }}, San Francisco, California. Store JSON in column checkbox. Enrich data button.&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="Datasette screenshot: Enrich data in Film_Locations_in_San_Francisco. 2,084 rows selected. OpenCage geocoder. Geocode to latitude/longitude points using OpenCage. Geocode input: {{ Locations }}, San Francisco, California. Store JSON in column checkbox. Enrich data button." title="Datasette screenshot: Enrich data in Film_Locations_in_San_Francisco. 2,084 rows selected. OpenCage geocoder. Geocode to latitude/longitude points using OpenCage. Geocode input: {{ Locations }}, San Francisco, California. Store JSON in column checkbox. Enrich data button." srcset="https://substackcdn.com/image/fetch/$s_!o6f2!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F01027686-ea45-45b9-bbdf-377de311e2a7_2398x1468.jpeg 424w, https://substackcdn.com/image/fetch/$s_!o6f2!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F01027686-ea45-45b9-bbdf-377de311e2a7_2398x1468.jpeg 848w, https://substackcdn.com/image/fetch/$s_!o6f2!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F01027686-ea45-45b9-bbdf-377de311e2a7_2398x1468.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!o6f2!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F01027686-ea45-45b9-bbdf-377de311e2a7_2398x1468.jpeg 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Enrichments are provided by plugins. There are four plugins available today:</p><ul><li><p><a href="https://datasette.io/plugins/datasette-enrichments-opencage">datasette-enrichments-opencage</a> - geocoding (and reverse-geocoding) using OpenCage</p></li><li><p><a href="https://datasette.io/plugins/datasette-enrichments-gpt">datasette-enrichments-gpt</a> - run prompts against text and images through OpenAI's GPT-3.5 and GPT-4 APIs, writing the results back to a column</p></li><li><p><a href="https://datasette.io/plugins/datasette-enrichments-jinja">datasette-enrichments-jinja</a> - use sandboxed Jinja templates to populate new columns based on existing data</p></li><li><p><a href="https://datasette.io/plugins/datasette-enrichments-re2">datasette-enrichments-re2</a> - execute regular expressions for search-and-replace or to extract data from text</p></li></ul><p>Here's <a href="https://www.youtube.com/watch?v=HqKlJCgdjfg">a video</a> demonstrating the new feature, including how to use GPT-4 to extract structured data from unstructured text and how GPT-4 Vision can be used to provide detailed descriptions of linked images.</p><div id="youtube2-HqKlJCgdjfg" class="youtube-wrap" data-attrs="{&quot;videoId&quot;:&quot;HqKlJCgdjfg&quot;,&quot;startTime&quot;:null,&quot;endTime&quot;:null}" data-component-name="Youtube2ToDOM"><div class="youtube-inner"><iframe src="https://www.youtube-nocookie.com/embed/HqKlJCgdjfg?rel=0&amp;autoplay=0&amp;showinfo=0&amp;enablejsapi=0" frameborder="0" loading="lazy" gesture="media" allow="autoplay; fullscreen" allowautoplay="true" allowfullscreen="true" width="728" height="409"></iframe></div></div><p>There are lots more details in <a href="https://simonwillison.net/2023/Dec/1/datasette-enrichments/">the blog post announcement</a>, including <a href="https://enrichments.datasette.io/en/stable/developing.html">how to write your own enrichments</a>.</p><h2><strong>Datasette comments</strong></h2><p><a href="https://github.com/asg017">Alex Garcia</a> has been working with me on <a href="https://www.datasette.cloud/">Datasette Cloud</a>, generously sponsered by <a href="https://fly.io/">Fly.io</a>.</p><p>Alex&#8217;s latest project is <a href="https://datasette.io/plugins/datasette-comments">datasette-comments</a>, an open source plugin that enabling a new way of collaborating around data analysis. He introduces the new plugin in <a href="https://www.datasette.cloud/blog/2023/datasette-comments/">Annotate and explore your data with datasette-comments</a> on the Datasette Cloud blog.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!1vFq!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7b3c207c-e2de-441a-92d5-88f80c637589_1127x659.jpeg" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!1vFq!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7b3c207c-e2de-441a-92d5-88f80c637589_1127x659.jpeg 424w, https://substackcdn.com/image/fetch/$s_!1vFq!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7b3c207c-e2de-441a-92d5-88f80c637589_1127x659.jpeg 848w, https://substackcdn.com/image/fetch/$s_!1vFq!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7b3c207c-e2de-441a-92d5-88f80c637589_1127x659.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!1vFq!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7b3c207c-e2de-441a-92d5-88f80c637589_1127x659.jpeg 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!1vFq!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7b3c207c-e2de-441a-92d5-88f80c637589_1127x659.jpeg" width="1127" height="659" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/7b3c207c-e2de-441a-92d5-88f80c637589_1127x659.jpeg&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:659,&quot;width&quot;:1127,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;Screenshot of a table in Datasette - a comment thread by Alex Garcia and Cleo Paws has been attached to one of the rows&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="Screenshot of a table in Datasette - a comment thread by Alex Garcia and Cleo Paws has been attached to one of the rows" title="Screenshot of a table in Datasette - a comment thread by Alex Garcia and Cleo Paws has been attached to one of the rows" srcset="https://substackcdn.com/image/fetch/$s_!1vFq!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7b3c207c-e2de-441a-92d5-88f80c637589_1127x659.jpeg 424w, https://substackcdn.com/image/fetch/$s_!1vFq!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7b3c207c-e2de-441a-92d5-88f80c637589_1127x659.jpeg 848w, https://substackcdn.com/image/fetch/$s_!1vFq!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7b3c207c-e2de-441a-92d5-88f80c637589_1127x659.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!1vFq!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7b3c207c-e2de-441a-92d5-88f80c637589_1127x659.jpeg 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Authenticated users can attach and reply to comments on any row of data in any table. Comments support @-mentions, reaction emoji and hashtags, and a comment thread can be marked as resolved to move it to an archive page.</p><h2>Datasette Cloud now has enrichments and comments</h2><p>We&#8217;ve enabled both of these plugins on <a href="https://www.datasette.cloud/">Datasette Cloud</a>. If your team would benefit from collaboratively analyzing data in this way, please drop us a line by replying to this message!</p>]]></content:encoded></item><item><title><![CDATA[Datasette Cloud and the Datasette 1.0 alphas]]></title><description><![CDATA[Plus a security vulnerability in the 1.0 alpha series]]></description><link>https://datasette.substack.com/p/datasette-cloud-and-the-datasette</link><guid isPermaLink="false">https://datasette.substack.com/p/datasette-cloud-and-the-datasette</guid><dc:creator><![CDATA[Simon Willison]]></dc:creator><pubDate>Tue, 22 Aug 2023 17:25:35 GMT</pubDate><enclosure url="https://substack-post-media.s3.amazonaws.com/public/images/1a7a8833-97e8-4356-9986-69c7bed81699_1550x863.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Here's the latest news from the <a href="https://datasette.io/">Datasette</a> ecosystem.</p><h2>Datasette Cloud</h2><p><strong><a href="https://www.datasette.cloud/">Datasette Cloud</a></strong> is the hosted SaaS version of Datasette. It exists to solve several problems:</p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://datasette.substack.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Thanks for reading Datasette Newsletter! Subscribe for free to receive new posts and support my work.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div><ul><li><p>I want teams to be able to use Datasette to collaborate on finding stories in their data - initially targeting newsrooms and data journalism, but the software is very clearly applicable to many other fields as well. Asking people to install software on their own server is <strong>a major barrier to entry</strong>, and I want to remove that barrier.</p></li><li><p>I'd like the project to be <strong>financially sustainable</strong> - not just to cover my own costs, but I want to grow a team of people to work full-time on Datasette and associated projects.</p></li><li><p>I want to make it as easy as possible for people to <strong>publish their data online</strong>. Datasette is great for this already... if you know how to use it (with Fly, Vercel, Cloud Run <a href="https://docs.datasette.io/en/stable/publish.html">or similar</a>). Datasette Cloud aims to make that even easier.</p></li></ul><p>I wrote more about the goals of Datasette Cloud in the first post on the new Datasette Cloud blog: <a href="https://www.datasette.cloud/blog/2023/welcome/">Welcome to Datasette Cloud</a>.</p><p>The Datasette Cloud effort is partially sponsored by <a href="https://fly.io/">Fly.io</a>, who are funding Alex Garcia to work with me on the project. Alex's first piece of work for that is the new <a href="https://datasette.io/plugins/datasette-write-ui">datasette-write-ui</a>, built initially for Datasette Cloud but available as an open source plugin for anyone to use. Alex introduced that in <a href="https://www.datasette.cloud/blog/2023/datasette-write-ui/">datasette-write-ui: a Datasette plugin for editing, inserting, and deleting rows</a> on the Datasette Cloud blog.</p><p>If you want to try out Datasette Cloud you can <a href="https://www.datasette.cloud/preview/">request access to the preview</a> today.</p><h2>Datasette 1.0 alphas</h2><p>Datasette 1.0 will be the version of Datasette that promises stability, in terms of the JSON API, the template context (so you can build custom templates without fear that they will break in future minor releases) and the API for plugins.</p><p>There have been four releases in the 1.0 alpha series so far:</p><ul><li><p><a href="https://docs.datasette.io/en/1.0a4/changelog.html#a0-2022-11-29">1.0a0</a> introduced signed API tokens and the <a href="https://docs.datasette.io/en/1.0a4/json_api.html#the-json-write-api">Datasette write JSON API</a>.</p></li><li><p><a href="https://docs.datasette.io/en/1.0a4/changelog.html#a1-2022-12-01">1.0a1</a> expanded the write API with CORS support and other small features, and was accompanied by <a href="https://simonwillison.net/2022/Dec/2/datasette-write-api/">this blog entry</a> showing detailed examples of what the write API can do.</p></li><li><p><a href="https://docs.datasette.io/en/1.0a4/changelog.html#a2-2022-12-14">1.0a2</a> added upsert support, and a mechanism for creating finely-grained access tokens. <a href="https://simonwillison.net/2022/Dec/15/datasette-1a2/">More in this blog post</a>.</p></li><li><p><a href="https://docs.datasette.io/en/1.0a4/changelog.html#a3-2023-08-09">1.0a3</a> features a new default JSON output, one of the most significant milestones on the way to 1.0 final.</p></li><li><p><a href="https://docs.datasette.io/en/1.0a4/changelog.html#a4-2023-08-21">1.0a4</a> fixes a security vulnerability in the 1.0 alpha series, described next.</p></li></ul><h2>A vulnerability in the 1.0 alphas</h2><p>I released <a href="https://docs.datasette.io/en/1.0a4/changelog.html#a4-2023-08-21">1.0a4</a> this morning, which fixes a security vulnerability in the 1.0 alpha series.</p><p>This affects you if you running a Datasette 1.0 alpha instance on a public server.</p><p>The bug is that an API explorer interface within Datasette (added in 1.0a0) could be visited by unauthenticated users and would reveal the names of the databases and tables in that instance - though not any of the actual table content.</p><p>You should upgrade as quickly as possible if:</p><ul><li><p>You are running a Datasette 1.0 alpha instance on the public internet</p></li><li><p>That Datasette instance is authenticated using a plugin such as <a href="https://datasette.io/plugins/datasette-auth-passwords">datasette-auth-passwords</a></p></li><li><p>There are private databases and tables in that instance where the names of those databases and tables should be considered sensitive information.</p></li></ul><p>For more information read this GitHub security advisory: <a href="https://github.com/simonw/datasette/security/advisories/GHSA-7ch3-7pp7-7cpq">Datasette 1.0 alpha series leaks names of databases and tables to unauthenticated users</a>.</p><p>The vulnerability was present on Datasette Cloud but has now been patched there, and a review of our logs showed that no unauthorized users had accessed that page across any of our hosted instances.</p><h2>LLM: a new Datasette project</h2><p>I've spent a lot of time over the past year immersed in research and exploration of the weird new world of LLMs - Large Language Models, the technology behind ChatGPT and GPT-4 and Bard and Bing and Claude and Llama and other similar projects.</p><p><a href="https://simonwillison.net/2023/Aug/3/weird-world-of-llms/">Catching up on the weird world of LLMs</a> is a 40m talk (plus detailed transcript, notes and links) I gave at North Bay Python in August which attempts to summarize everything I've learned about LLMs so far.</p><p>I'm increasingly optimistic about the role LLMs can play in the Datasette world, especially when combined with Datasette's plugin architecture.</p><p>You can read about some of my early explorations of LLMs and Datasette here:</p><ul><li><p><a href="https://simonwillison.net/2023/Jan/13/semantic-search-answers/">How to implement Q&amp;A against your documentation with GPT3, embeddings and Datasette</a> describes my earliest attempt at building retrieval-augmented generation (RAG) against data in Datasette, where you take a user's question, find relevant content, then feed that to the LLM with "Given the above context, answer the following question: ..." appended to it.</p></li><li><p><a href="https://simonwillison.net/2023/Mar/24/datasette-chatgpt-plugin/">I built a ChatGPT plugin to answer questions about data hosted in Datasette</a> talks about my implementation of a plugin for ChatGPT that turns English questions into SQL queries and lets ChatGPT run those against Datasette and use the results to generate an answer.</p></li><li><p><a href="https://simonwillison.net/2023/Apr/29/enriching-data/">Enriching data with GPT3.5 and SQLite SQL functions</a> shows how my <a href="https://datasette.io/tools/openai-to-sqlite">openai-to-sqlite</a> CLI tool can run enrichments such as sentiment analysis against data in a SQLite database and store the results.</p></li><li><p><a href="https://til.simonwillison.net/llms/openai-embeddings-related-content">Storing and serving related documents with openai-to-sqlite and embeddings</a> shows how to use the same tool to store embeddings for documents in a SQLite database, then use those embeddings to identify related documents, save those to a table and serve them up as a "Related" section on my TIL site.</p></li></ul><p>These explorations have added up to a new project called <strong>LLM</strong>, a new command-line utility and Python library for working with LLMs.</p><p>The LLM project site is <a href="https://llm.datasette.io/">llm.datasette.io</a>.</p><p>LLM is heavily inspired by both Datasette and <a href="https://sqlite-utils.datasette.io/">sqlite-utils</a>. It stores prompts and their responses to SQLite, providing you with a permanent structured log of your LLM interactions. It also provides a plugin architecture for adding new functionality.</p><p>Most importantly, the plugin architecture lets you add support for alternative models - including models like Llama 2 which you can run on your own machine!</p><p>The simplest version of LLM usage looks like this.</p><p>First, install it:</p><pre><code>pip install llm</code></pre><p>Or use <a href="https://pypa.github.io/pipx/">pipx</a> or <a href="https://brew.sh/">Homebrew</a>:</p><pre><code>brew install llm</code></pre><p>If you have an OpenAI API key you can configure that and start using LLM immediately:</p><pre><code>llm keys set openai
&lt;paste key here&gt;</code></pre><p>Then run a prompt:</p><pre><code>llm 'Five nautical names for a pet pelican'</code></pre><p>But where things get really fun is when you start adding <a href="https://llm.datasette.io/en/stable/plugins/directory.html">LLM plugins</a> to install and run alternative models on your own machine.</p><pre><code><code>llm install llm-gpt4all
llm -m orca-mini-7b '3 names for a pet cow'
</code></code></pre><p>The first time you run this it will download the <code>orca-mini-7b</code> model from the <a href="https://gpt4all.io/">GPT4All</a> project and use it to run the prompt.</p><p>LLM has eight plugins already. My current favorite is <a href="https://github.com/simonw/llm-mlc">llm-mlc</a>, which uses the <a href="https://mlc.ai/mlc-llm/docs/">MLC</a> framework to run models like Llama 2 using GPU acceleration on my M2 Mac laptop.</p><p>For more about LLM, read these blog entries:</p><ul><li><p><a href="https://simonwillison.net/2023/May/18/cli-tools-for-llms/">llm, ttok and strip-tags&#8212;CLI tools for working with ChatGPT and other LLMs</a></p></li><li><p><a href="https://simonwillison.net/2023/Jul/12/llm/">The LLM CLI tool now supports self-hosted language models via plugins</a></p></li><li><p><a href="https://simonwillison.net/2023/Jul/18/accessing-llama-2/">Accessing Llama 2 from the command-line with the llm-replicate plugin</a></p></li><li><p><a href="https://simonwillison.net/2023/Aug/1/llama-2-mac/">Run Llama 2 on your own Mac using LLM and Homebrew</a></p></li></ul><h2>Other Datasette news</h2><p>The <a href="https://datasette.io/news">Datasette News page</a> has short-form news about the project. The <a href="https://simonwillison.net/tags/datasette/">datasette tag on my blog</a> is a lot noisier and more frequently updated.</p><p>I plan to send this newsletter out more often, especially given the increased pace of development towards 1.0.</p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://datasette.substack.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Thanks for reading Datasette Newsletter! Subscribe for free to receive new posts and support my work.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div>]]></content:encoded></item><item><title><![CDATA[Datasette Lite, Datasette Tutorials, Datasette Cloud]]></title><description><![CDATA[The Datasette Newsletter is back]]></description><link>https://datasette.substack.com/p/datasette-lite-datasette-tutorials</link><guid isPermaLink="false">https://datasette.substack.com/p/datasette-lite-datasette-tutorials</guid><dc:creator><![CDATA[Simon Willison]]></dc:creator><pubDate>Fri, 19 Aug 2022 01:18:40 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!8PXh!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2F80014b84-d742-4c38-a315-9fa23a7dcad9_1957x1512.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h2>Datasette Lite</h2><p><strong>Datasette Lite</strong> provides a new way to run Datasette: entirely in your browser, thanks to WebAssembly and the <a href="https://pyodide.org/">Pyodide</a> project.</p><p>Visit <a href="https://lite.datasette.io/">https://lite.datasette.io/</a> to try it out.</p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://datasette.substack.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Thanks for reading Datasette Newsletter! Subscribe for free to receive new posts and support my work.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!8PXh!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2F80014b84-d742-4c38-a315-9fa23a7dcad9_1957x1512.jpeg" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!8PXh!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2F80014b84-d742-4c38-a315-9fa23a7dcad9_1957x1512.jpeg 424w, https://substackcdn.com/image/fetch/$s_!8PXh!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2F80014b84-d742-4c38-a315-9fa23a7dcad9_1957x1512.jpeg 848w, https://substackcdn.com/image/fetch/$s_!8PXh!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2F80014b84-d742-4c38-a315-9fa23a7dcad9_1957x1512.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!8PXh!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2F80014b84-d742-4c38-a315-9fa23a7dcad9_1957x1512.jpeg 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!8PXh!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2F80014b84-d742-4c38-a315-9fa23a7dcad9_1957x1512.jpeg" width="1456" height="1125" data-attrs="{&quot;src&quot;:&quot;https://bucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com/public/images/80014b84-d742-4c38-a315-9fa23a7dcad9_1957x1512.jpeg&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1125,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;A screenshot of the pypi_packages database table running in Google Chrome in a page with the URL of lite.datasette.io/#/content/pypi_packages?_facet=author&quot;,&quot;title&quot;:&quot;A screenshot of the pypi_packages database table running in Google Chrome in a page with the URL of lite.datasette.io/#/content/pypi_packages?_facet=author&quot;,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="A screenshot of the pypi_packages database table running in Google Chrome in a page with the URL of lite.datasette.io/#/content/pypi_packages?_facet=author" title="A screenshot of the pypi_packages database table running in Google Chrome in a page with the URL of lite.datasette.io/#/content/pypi_packages?_facet=author" srcset="https://substackcdn.com/image/fetch/$s_!8PXh!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2F80014b84-d742-4c38-a315-9fa23a7dcad9_1957x1512.jpeg 424w, https://substackcdn.com/image/fetch/$s_!8PXh!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2F80014b84-d742-4c38-a315-9fa23a7dcad9_1957x1512.jpeg 848w, https://substackcdn.com/image/fetch/$s_!8PXh!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2F80014b84-d742-4c38-a315-9fa23a7dcad9_1957x1512.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!8PXh!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2F80014b84-d742-4c38-a315-9fa23a7dcad9_1957x1512.jpeg 1456w" sizes="100vw" loading="lazy" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>You can also pass it the URL to a SQLite database, CSV file or SQL initialization script. Here are some examples:</p><ul><li><p><a href="https://lite.datasette.io/?url=https://congress-legislators.datasettes.com/legislators.db">https://lite.datasette.io/?url=https://congress-legislators.datasettes.com/legislators.db</a> loads a database with historic Members of the United States Congress.</p></li><li><p><a href="https://lite.datasette.io/?csv=https%3A%2F%2Fraw.githubusercontent.com%2Ffivethirtyeight%2Fdata%2Fmaster%2Ffight-songs%2Ffight-songs.csv">https://lite.datasette.io/?csv=https%3A%2F%2Fraw.githubusercontent.com%2Ffivethirtyeight%2Fdata%2Fmaster%2Ffight-songs%2Ffight-songs.csv</a> loads US college fight songs from <a href="https://github.com/fivethirtyeight/data/blob/master/fight-songs/fight-songs.csv">this CSV file</a> in the FiveThirtyEight GitHub repository.</p></li></ul><p>Read more about Datasette Lite in this series of articles on my blog:</p><ul><li><p><a href="https://simonwillison.net/2022/May/4/datasette-lite/">Datasette Lite: a server-side Python web application running in a browser</a></p></li><li><p><a href="https://simonwillison.net/2022/Jun/20/datasette-lite-csvs/">Joining CSV files in your browser using Datasette Lite</a></p></li><li><p><a href="https://simonwillison.net/2022/Aug/17/datasette-lite-plugins/">Plugin support for Datasette Lite</a></p></li><li><p><a href="https://til.simonwillison.net/sqlite/sort-by-number-of-json-intersections">Sort by number of JSON intersections</a>, which shows how Datasette Lite can be used as a tool to demonstrate the answer to questions about how to do something using SQL</p></li></ul><h2>Datasette Tutorials</h2><p>The official Datasette website now features <a href="https://datasette.io/tutorials">tutorials</a> to help people get started using Datasette:</p><ul><li><p><a href="https://datasette.io/tutorials/explore">Exploring a database with Datasette</a> shows how to use the Datasette web interface to explore a new database.</p></li><li><p><a href="https://datasette.io/tutorials/learn-sql">Learn SQL with Datasette</a> introduces SQL, and shows how to use that query language to ask questions of your data.</p></li><li><p><a href="https://datasette.io/tutorials/clean-data">Cleaning data with sqlite-utils and Datasette</a> shows how to use sqlite-utils to clean data, import it into SQLite and then explore it with Datasette. Includes a ten minute accompanying video.</p></li></ul><h2>Sign up for the preview of Datasette Cloud</h2><p><strong>Datasette Cloud</strong> is the new hosted SaaS service I am building to help teams run a private Datasette instance to share and collaborate on data analysis. It's nearly ready for preview users - if you'd like to try it out, please add yourself to the waiting list using <a href="https://www.datasette.cloud/">the form on the homepage</a>.</p><p>Datasette Cloud features include:</p><ul><li><p>Upload CSVs and import data from external sources to populate your own, private Datasette instance</p></li><li><p>Invite coworkers to collaborate in your team's own dedicated space</p></li><li><p>Build search engines against your data</p></li><li><p>All of Datasette's standard features: filter and facet your data, execute SQL queries, generate visualizations and more</p></li></ul><h2>Community highlights</h2><p><strong><a href="https://twitter.com/phildini">Philip James</a></strong> has been using Datasette to publish searchable versions of the minutes of city meetings for the cities of Alameda and Oakland:</p><ul><li><p><a href="https://data.oakland.works/oakland_minutes/pages">https://data.oakland.works/oakland_minutes/pages</a> lists 37,000 pages of Oakland civic meeting minutes dating back to 2003 - see Philip&#8217;s <a href="https://twitter.com/phildini/status/1560329634679439360">Twitter thread</a> for details</p></li><li><p><a href="https://data.alameda.one/alameda_minutes/pages">https://data.alameda.one/alameda_minutes/pages</a> provides 18,000 pages of Alameda city minutes - <a href="https://twitter.com/phildini/status/1559234347642695681">thread here</a></p></li></ul><p><strong><a href="https://alexgarcia.xyz/">Alex Garcia</a></strong> has published a series of interactive posts about writing SQLite extensions:</p><ul><li><p><a href="https://observablehq.com/@asg017/introducing-sqlite-lines">Introducing sqlite-lines - a SQLite extension for reading files line-by-line</a></p></li><li><p><a href="https://observablehq.com/@asg017/introducing-sqlite-html">Introducing sqlite-html: query, parse, and generate HTML in SQLite</a></p></li><li><p><a href="https://observablehq.com/@asg017/introducing-sqlite-http">Introducing sqlite-http: A SQLite extension for making HTTP requests</a></p></li></ul><p>Each post includes interactive demos powered Observable notebooks, some of which work by talking to a Datasette backend using Alex&#8217;s <a href="https://observablehq.com/@ambassadors/datasette-client">Datasette Client</a> Observable integration.</p><p><strong><a href="https://twitter.com/jcristharif">Jim Crist-Harif</a></strong> released <a href="https://jcristharif.com/ibis-datasette.html">Ibis-Datasette</a>, a Python library that lets you build SQL queries using a SQLAlchemy style chained query builder syntax, run them against a Datasette instance and get back the results in a DataFrame-like object.</p><pre><code>In [1]: import ibis
In [2]: ibis.options.interactive = True
In [3]: con = ibis.datasette.connect(
   ...:    "https://congress-legislators.datasettes.com/legislators"
   ...: )
In [4]: legislators = con.tables["legislators"]
In [5]: legislators.groupby("bio_gender").count()
Out[5]:
  bio_gender  count
0          F    399
1          M  12195</code></pre><h2>Datasette Discord</h2><p>The Datasette project now has an official presence on Discord! Join our Discord server here to talk about Datasette and the other projects that make up the wider Datasette ecosystem.</p><p><strong><a href="https://discord.gg/ktd74dm5mw">Join Datasette Discord</a></strong> </p><p></p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://datasette.substack.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Thanks for reading Datasette Newsletter! Subscribe for free to receive new posts and support my work.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div>]]></content:encoded></item><item><title><![CDATA[Datasette Desktop - a macOS application version of Datasette]]></title><description><![CDATA[Datasette on your laptop without installing Python]]></description><link>https://datasette.substack.com/p/datasette-desktop-a-macos-application</link><guid isPermaLink="false">https://datasette.substack.com/p/datasette-desktop-a-macos-application</guid><dc:creator><![CDATA[Simon Willison]]></dc:creator><pubDate>Tue, 14 Sep 2021 22:34:13 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!HbZh!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2F38e403fd-36a6-459a-bc48-b369fde202d5_1628x1224.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>The big news this month is <strong><a href="https://datasette.io/desktop">Datasette Desktop</a></strong>, a brand new Mac application I've been building to help people run Datasette - and Datasette plugins - on their own computers.</p><p>An ongoing goal I have for Datasette is to make it available for as many people to use as possible.</p><p>I've tried a bunch of different strategies for this in the past:</p><ul><li><p>Datasette is built using Python, so if you have a working Python environment you can install it using <code>pip install datasette</code></p></li><li><p>I packaged it for <a href="https://brew.sh/">Homebrew</a>, so <code>brew install datasette</code> on macOS should work too</p></li><li><p>I have documentation for <a href="https://docs.datasette.io/en/stable/getting_started.html#getting-started-glitch">running it online using Glitch</a>, where it can be installed using just your web browser</p></li><li><p>The <a href="https://docs.datasette.io/en/stable/publish.html">datasette publish</a> set of commands are designed to help deploy Datasette to Cloud Run, Heroku, Vercel or fly.io as easily as possible</p></li></ul><p>Unfortunately, each of these options require some degree of deep technical knowledge before you can use them - familiarity with the command-line, or knowledge of how to deploy websites. These things are a significant barrier to entry!</p><p>My goal with Datasette Desktop is to make installing Datasette as easy as downloading and installing any other Mac application.</p><p>You can <a href="https://datasette.io/desktop">try that out here</a>: click the Download link, open the zip file and drag the Datasette application to your <code>/Applications</code> folder.</p><p>The application comes with its own copy of Python 3.9 tucked away inside it, which means that even if you don&#8217;t have Python installed it will still be able to run just fine. It also pulls off some tricks to ensure that existing <a href="https://datasette.io/plugins">Datasett plugins</a> can be installed using the &#8220;Plugins &#8594; Install and Manage Plugins&#8230;&#8221; menu option.</p><p>Once installed, the application can be used to open existing CSV or SQLite files on your computer. It can also open CSV files by URL, and uses that capability to offer examples that you can open to try it out (currently using data from the <a href="https://data.cityofnewyork.us/Environment/2018-Central-Park-Squirrel-Census-Squirrel-Data/vfnx-vebw">Central Park Squirrel Census</a> and the <a href="https://data.london.gov.uk/dataset/animal-rescue-incidents-attended-by-lfb">London Fire Brigade&#8217;s list of animal rescue incidents</a>).</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!HbZh!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2F38e403fd-36a6-459a-bc48-b369fde202d5_1628x1224.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!HbZh!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2F38e403fd-36a6-459a-bc48-b369fde202d5_1628x1224.png 424w, https://substackcdn.com/image/fetch/$s_!HbZh!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2F38e403fd-36a6-459a-bc48-b369fde202d5_1628x1224.png 848w, https://substackcdn.com/image/fetch/$s_!HbZh!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2F38e403fd-36a6-459a-bc48-b369fde202d5_1628x1224.png 1272w, https://substackcdn.com/image/fetch/$s_!HbZh!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2F38e403fd-36a6-459a-bc48-b369fde202d5_1628x1224.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!HbZh!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2F38e403fd-36a6-459a-bc48-b369fde202d5_1628x1224.png" width="1456" height="1095" data-attrs="{&quot;src&quot;:&quot;https://bucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com/public/images/38e403fd-36a6-459a-bc48-b369fde202d5_1628x1224.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1095,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:431500,&quot;alt&quot;:&quot;Screenshot of the welcome screen, showing the buttons to import the two example CSVs&quot;,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="Screenshot of the welcome screen, showing the buttons to import the two example CSVs" title="Screenshot of the welcome screen, showing the buttons to import the two example CSVs" srcset="https://substackcdn.com/image/fetch/$s_!HbZh!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2F38e403fd-36a6-459a-bc48-b369fde202d5_1628x1224.png 424w, https://substackcdn.com/image/fetch/$s_!HbZh!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2F38e403fd-36a6-459a-bc48-b369fde202d5_1628x1224.png 848w, https://substackcdn.com/image/fetch/$s_!HbZh!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2F38e403fd-36a6-459a-bc48-b369fde202d5_1628x1224.png 1272w, https://substackcdn.com/image/fetch/$s_!HbZh!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2F38e403fd-36a6-459a-bc48-b369fde202d5_1628x1224.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><h2>How I built it</h2><p>Like Datasette itself, Datasette Desktop is open source. I&#8217;ve been building it entirely in the open in the <a href="https://github.com/simonw/datasette-app">simonw/datasette-app</a> GitHub repository.</p><p>I&#8217;ve been writing up details of how it works as I&#8217;ve gone along:</p><ul><li><p><a href="https://simonwillison.net/2021/Aug/30/datasette-app/">Building a desktop application for Datasette</a> describes the initial research into the project, and talks about how I&#8217;m building it using Electron.</p></li><li><p><a href="https://simonwillison.net/2021/Sep/8/datasette-desktop/">Datasette Desktop&#8212;a macOS desktop application</a> for Datasette was the initial launch announcement for the first installable version. It describes how the application works in some detail, including how I bundled Python inside the application and how I figured out signing and distribution of macOS apps using Electron.</p></li><li><p><a href="https://simonwillison.net/2021/Sep/13/datasette-desktop-2/">Datasette Desktop 0.2.0: The annotated release notes</a> describes the second release, which introduced some significant new features including a plugin management interface for installing, upgrading and uninstalling plugins and the ability to open SQLite and CSV files directly using the application.</p></li></ul><p>I&#8217;ve also been collecting numerous <a href="https://til.simonwillison.net/electron">TILs (Today I Learned) about Electron</a>.</p><h2>Feedback welcome</h2><p>The application so far is just a starting point: I&#8217;m very keen to hear from people who have tried it out. Please leave questions, suggestions and feedback on the <strong><a href="https://github.com/simonw/datasette-app/discussions">GitHub Discussions forum</a></strong> for the project.</p>]]></content:encoded></item><item><title><![CDATA[Everything new in Datasette since January, plus Django SQL Dashboard]]></title><description><![CDATA[What's new in the Datasette ecosystem since January 2021]]></description><link>https://datasette.substack.com/p/everything-new-in-datasette-since</link><guid isPermaLink="false">https://datasette.substack.com/p/everything-new-in-datasette-since</guid><dc:creator><![CDATA[Simon Willison]]></dc:creator><pubDate>Tue, 10 Aug 2021 01:20:38 GMT</pubDate><enclosure url="https://substackcdn.com/image/youtube/w_728,c_limit/OUjd0rkc678" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>It&#8217;s been a while since the <a href="https://datasette.substack.com/p/datasette-054-and-querying-a-database">last edition of this newsletter</a>, so plenty to cover today - starting with what&#8217;s new in <a href="https://datasette.io/">Datasette</a> and <a href="https://sqlite-utils.datasette.io/">sqlite-utils</a>, and then introducing a new Datasette-like tool aimed at Django + PostgreSQL developers called <strong><a href="https://django-sql-dashboard.datasette.io/">Django SQL Dashboard</a></strong>.</p><h2>What&#8217;s new in Datasette</h2><p><strong><a href="https://docs.datasette.io/en/stable/changelog.html#v0-55">Datasette 0.55</a></strong> added support for cross-database SQL queries: you can now start Datasette with the new &#8220;--crossdb&#8221; option which enables cross-database joins! More details <a href="https://docs.datasette.io/en/stable/sql_queries.html#cross-database-queries">in the documentation</a> or you can try out <a href="https://latest.datasette.io/_memory?sql=select%0D%0A++%27fixtures%27+as+database%2C+*%0D%0Afrom%0D%0A++%5Bfixtures%5D.sqlite_master%0D%0Aunion%0D%0Aselect%0D%0A++%27extra_database%27+as+database%2C+*%0D%0Afrom%0D%0A++%5Bextra_database%5D.sqlite_master">an example cross-database query here</a>. </p><p><strong><a href="https://docs.datasette.io/en/stable/changelog.html#v0-56">0.56</a></strong> was mainly small bug fixes and documentation improvements, though it did also introduce a handle you can drag to resize the SQL editor.</p><p><strong><a href="https://docs.datasette.io/en/stable/changelog.html#v0-57">0.57</a></strong> brought a <a href="https://github.com/simonw/datasette/security/advisories/GHSA-xw7c-jx9m-xh5g">critical security fix</a> - if you have any Datasette instances running on the public web you should upgrade to a version higher than this (or 0.56.1 which applies the same fix to 0.56) as soon as possible.</p><p>It also fixed a long-standing issue where SQL errors were displayed without letting you edit your original SQL, and added mechanisms for selectively showing and hiding columns on a table page (<a href="https://docs.datasette.io/en/stable/json_api.html#special-table-arguments">&#8220;?_col=&#8221; and &#8220;?_nocol&#8221;</a>).</p><p><strong><a href="https://docs.datasette.io/en/stable/changelog.html#v0-58">0.58</a></strong> added Unix domain socket support, useful for people running Datasette behind an Apache or NGINX proxy server. It also added two new plugin hooks and a significant performance boost for faceting - all of which are covered in detail in the <a href="https://simonwillison.net/2021/Jul/16/datasette-058/">Datasette 0.58 annotated release notes</a>. </p><h2>What&#8217;s new in sqlite-utils</h2><p>Datasette&#8217;s close companion is <a href="https://sqlite-utils.datasette.io/">sqlite-utils</a> - a combination CLI tool and Python library for creating and manipulating SQLite databases.</p><p>sqlite-utils gained two key new features this year.</p><p><strong><a href="https://sqlite-utils.datasette.io/en/stable/cli.html#querying-data-directly-using-an-in-memory-database">sqlite-utils memory</a></strong> is a new command that lets you pipe JSON and CSV data directly into a temporary, in-memory SQLite database, execute a SQL query that filters or joins that data, then returns the results as tabular, CSV or JSON output.</p><p>It turns sqlite-utils into an ad-hoc querying tool that works against all sorts of data, and can be used to combine data from different sources - effectively running joins between JSON, CSV and other data sources.</p><p>I recorded a YouTube video showing the new tool in action:</p><div id="youtube2-OUjd0rkc678" class="youtube-wrap" data-attrs="{&quot;videoId&quot;:&quot;OUjd0rkc678&quot;,&quot;startTime&quot;:null,&quot;endTime&quot;:null}" data-component-name="Youtube2ToDOM"><div class="youtube-inner"><iframe src="https://www.youtube-nocookie.com/embed/OUjd0rkc678?rel=0&amp;autoplay=0&amp;showinfo=0&amp;enablejsapi=0" frameborder="0" loading="lazy" gesture="media" allow="autoplay; fullscreen" allowautoplay="true" allowfullscreen="true" width="728" height="409"></iframe></div></div><p>Here&#8217;s an example that fetches JSON from the GitHub API, executes a SQL query to select three columns and sort by the number of stars, then outputs the result to the console as CSV:</p><pre><code>$ curl -s 'https://api.github.com/users/dogsheep/repos' \
  | sqlite-utils memory - '
      select full_name, forks_count, stargazers_count as stars
      from stdin order by stars desc limit 3
    ' --csv
full_name,forks_count,stars
dogsheep/twitter-to-sqlite,12,225
dogsheep/github-to-sqlite,14,139
dogsheep/dogsheep-photos,5,116</code></pre><p>I wrote more about this in <a href="https://simonwillison.net/2021/Jun/19/sqlite-utils-memory/">Joining CSV and JSON data with an in-memory SQLite database</a>.</p><p>The <strong><a href="https://sqlite-utils.datasette.io/en/stable/cli.html#converting-data-in-columns">sqlite-utils convert</a></strong> command is the other big new feature: it lets you apply a conversion function written in Python to every value in a column in your SQLite database.</p><p>Here&#8217;s how to remove commas from a column with 123,456 style numbers:</p><pre><code>sqlite-utils convert states.db states count \
    'value.replace(",", "")'</code></pre><p>And this will split a &#8220;location&#8221; column into two separate &#8220;latitude&#8221; and &#8220;longitude&#8221; columns (using the --multi option):</p><pre><code>sqlite-utils convert data.db places location '
latitude, longitude = value.split(",")
return {
    "latitude": float(latitude),
    "longitude": float(longitude),
}' --multi</code></pre><p>Read <a href="https://simonwillison.net/2021/Aug/6/sqlite-utils-convert/">Apply conversion functions to data in SQLite columns with the sqlite-utils CLI tool</a> for more.</p><h2>Django SQL Dashboard</h2><p>I spent the first few months of the year mainly not working on Datasette at all: I joined the team running <a href="https://www.vaccinatethestates.com/">VaccinateCA</a> / <a href="https://www.vaccinatethestates.com/">Vaccinate The States</a> to build a Django application to help crowdsource information about places that people could go to get vaccinated against COVID-19 in the USA.</p><p>My first order of business was to help replace their Airtable infrastructure - which was rapidly reaching the size limits of what could be kept in Airtable - with a Django equivalent.</p><p>Since a major benefit of building on Airtable was its ad-hoc reporting abilities, I found myself really needing something similar to Datasette that could work against a PostgreSQL database.</p><p>So I built exactly that: <strong><a href="https://django-sql-dashboard.datasette.io/">Django SQL Dashboard</a></strong> - an open source package that adds a Datasette-like querying interface to a Django+PostgreSQL project, protected by the Django authentication layer.</p><p>I recorded this video with a demo of the project:</p><div id="youtube2-ausrmMZkPEY" class="youtube-wrap" data-attrs="{&quot;videoId&quot;:&quot;ausrmMZkPEY&quot;,&quot;startTime&quot;:null,&quot;endTime&quot;:null}" data-component-name="Youtube2ToDOM"><div class="youtube-inner"><iframe src="https://www.youtube-nocookie.com/embed/ausrmMZkPEY?rel=0&amp;autoplay=0&amp;showinfo=0&amp;enablejsapi=0" frameborder="0" loading="lazy" gesture="media" allow="autoplay; fullscreen" allowautoplay="true" allowfullscreen="true" width="728" height="409"></iframe></div></div><p>Lots more detail in <a href="https://simonwillison.net/2021/May/10/django-sql-dashboard/">the Django SQL Dashboard launch announcement</a>.</p><p>I&#8217;m still deciding if and when to bring the lessons from that project back into Datasette itself - having Datasette work against PostgreSQL in addition to SQLite would open up a huge new set of possibilities for the project, and my work on Django SQL Dashboard has helped me start to scope out how much work it would be to make that a reality.</p><p>If you&#8217;re interested in reading more about my work at VaccinateCA I&#8217;ve imported my writing from my internal blog at the organization into my regular blog - you can find that series of posts here: <a href="https://simonwillison.net/series/vaccinateca/">simonwillison.net/series/vaccinateca</a></p><h2>New Datasette Plugins</h2><p>I decided to check how many new Datasette plugins I&#8217;ve released since the last newsletter on January 26th. The answer turned out to be seven! Here&#8217;s the query I used:</p><pre><code>select
  repos.full_name,
  tag_name,
  min(releases.created_at) as launched_at
from
  releases
  join repos on releases.repo = repos.id
where
  repos.full_name like 'simonw/datasette-%'
  and repos.full_name not in (
  -- These are older but the first tagged release was this year
    'simonw/datasette-jellyfish',
    'simonw/datasette-haversine'
  )
group by
  repo
having
  launched_at &gt; '2021-01-26'
order by
  launched_at</code></pre><p><a href="https://datasette.io/content?sql=select%0D%0A++repos.full_name%2C%0D%0A++tag_name%2C%0D%0A++min%28releases.created_at%29+as+launched_at%0D%0Afrom%0D%0A++releases%0D%0A++join+repos+on+releases.repo+%3D+repos.id%0D%0Awhere%0D%0A++repos.full_name+like+%27simonw%2Fdatasette-%25%27%0D%0A++and+repos.full_name+not+in+%28%0D%0A++++--+These+are+much+older%2C+but+their+first+tagged+release+was+this+year%0D%0A++++%27simonw%2Fdatasette-jellyfish%27%2C%0D%0A++++%27simonw%2Fdatasette-haversine%27%0D%0A++%29%0D%0Agroup+by%0D%0A++repo%0D%0Ahaving%0D%0A++launched_at+%3E+%272021-01-26%27%0D%0Aorder+by%0D%0A++launched_at">Run that here</a>.</p><p><strong><a href="https://datasette.io/plugins/datasette-tiles">datasette-tiles</a></strong> is a plugin for serving map tiles directly out of a SQLite database, using the MBTiles standard for bundling tiles in a DB file.</p><p><strong><a href="https://datasette.io/plugins/datasette-basemap">datasette-basemap</a></strong> is a related plugin that bundles the first four layers of tiles from OpenStreetMap in a SQLite database file which can be installed in a place where Datasette can find it using &#8220;pip install datasette-basemap&#8221;.</p><p>Here&#8217;s <a href="https://datasette-tiles-demo.datasette.io/-/tiles/basemap">a demo of the plugin</a>, or you can browse the underlying <a href="https://datasette-tiles-demo.datasette.io/basemap/tiles">database of tile images</a>.</p><p>I wrote more about these plugins in <a href="https://simonwillison.net/2021/Feb/4/datasette-tiles/">Serving map tiles from SQLite with MBTiles and datasette-tiles</a>.</p><p><strong><a href="https://datasette.io/plugins/datasette-block">datasette-block</a></strong> is a plugin that lets you block all access to specific path prefixes within Datasette. You probably don&#8217;t need this one!</p><p><strong><a href="https://datasette.io/plugins/datasette-placekey">datasette-placekey</a></strong> adds SQL functions for working with the <a href="https://www.placekey.io/">placekey</a> mechanism for creating identifiers for locations. I built this for some analysis at VaccinateCA.</p><p><strong><a href="https://datasette.io/plugins/datasette-remote-metadata">datasette-remote-metadata</a></strong> lets you define your Datasette metadata in an external file and have Datasette fetch it on-demand while it is running. This is useful for if you have a large (1GB+) database running somewhere like Cloud Run where deploying new metadata can take several minutes - with the plugin you can edit the remote file and have the changes go live a few seconds later.</p><p>I built this as part of working with Stanford&#8217;s <a href="https://biglocalnews.org/">Big Local News</a> to help launch the <strong><a href="https://stanford-school-enrollment-project.datasette.io/">Stanford School Enrollment Project</a></strong>, which makes available detailed school enrollment figures for schools and districts across the USA from this year back to 2015.</p><p><strong><a href="https://datasette.io/plugins/datasette-pyinstrument">datasette-pyinstrument</a></strong> adds the ability to add ?_pyinstrument=1 to any Datasette URL in order to see the output of a profile run while constructing the page.</p><p><strong><a href="https://datasette.io/plugins/datasette-query-links">datasette-query-links</a></strong> is a highly experimental plugin which looks out for SQL queries that return strings that are themselves valid SQL queries, and turns those strings into links that will execute the SQL. I thought this was a neat and unique idea, until I found out that PostgreSQL <a href="https://twitter.com/simonw/status/1424577005417242624">already has a version of this</a> as a built in feature called \gexec! </p><h2>What&#8217;s next</h2><p>I&#8217;ve decided to try and get this newsletter back onto a roughly monthly cadence. In the meantime, I suggest keeping an eye on the official Datasette website at <strong><a href="https://datasette.io/">datasette.io</a></strong> - you can even subscribe to its Atom feeds!</p><p>And a reminder: I&#8217;m still running Datasette Office Hours every Friday, so if you&#8217;d like to have a 25 minute video chat with me about Datasette to talk about things you&#8217;re working on or provide feedback on the project, <strong><a href="https://calendly.com/swillison/datasette-office-hours">sign up for a slot</a></strong>!</p>]]></content:encoded></item><item><title><![CDATA[Datasette 0.54, and querying a database by drawing on a map]]></title><description><![CDATA[Datasette Weekly(ish) volume 6]]></description><link>https://datasette.substack.com/p/datasette-054-and-querying-a-database</link><guid isPermaLink="false">https://datasette.substack.com/p/datasette-054-and-querying-a-database</guid><dc:creator><![CDATA[Simon Willison]]></dc:creator><pubDate>Tue, 26 Jan 2021 19:14:19 GMT</pubDate><enclosure url="https://cdn.substack.com/image/fetch/h_600,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2F12ce5773-5381-459d-a498-b04e265ec89a_1176x994.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Since the last edition of this newsletter I&#8217;ve made <a href="https://datasette.io/content?sql=select%0D%0A++json_object%28%0D%0A++++%27href%27%2C%0D%0A++++releases.html_url%2C%0D%0A++++%27label%27%2C%0D%0A++++repos.name+%7C%7C+%27+%27+%7C%7C+releases.name%0D%0A++%29+as+release%2C%0D%0A++releases.published_at%2C%0D%0A++releases.body%0D%0Afrom%0D%0A++releases%0D%0A++join+repos+on+releases.repo+%3D+repos.id%0D%0Awhere%0D%0A++releases.published_at+%3E%3D+%3Ap0%0D%0A++and+releases.published_at+%3C+%3Ap1%0D%0Aorder+by%0D%0A++published_at+desc&amp;p0=2020-12-22&amp;p1=2021-01-27">33 releases</a> across 13 different Datasette ecosystem projects. Some of the highlights.</p><h2>datasette-leaflet-freedraw</h2><p>Datasette supports <a href="https://docs.datasette.io/en/stable/sql_queries.html#named-parameters">parameterized SQL queries</a>: if you compose a SQL query with a placeholder such as &#8220;select * from counties where population &gt; :number&#8221; Datasette will spot the :number parameter and turn it into <a href="https://covid-19.datasettes.com/covid?sql=select+*+from+us_census_county_populations_2019+where+population+%3E+%3Anumber&amp;number=10000000">a form field on a page</a>:</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!nVZF!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2Fa3be5b28-c74b-4efa-a0e0-76de05a9347b_1462x786.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!nVZF!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2Fa3be5b28-c74b-4efa-a0e0-76de05a9347b_1462x786.png 424w, https://substackcdn.com/image/fetch/$s_!nVZF!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2Fa3be5b28-c74b-4efa-a0e0-76de05a9347b_1462x786.png 848w, https://substackcdn.com/image/fetch/$s_!nVZF!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2Fa3be5b28-c74b-4efa-a0e0-76de05a9347b_1462x786.png 1272w, https://substackcdn.com/image/fetch/$s_!nVZF!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2Fa3be5b28-c74b-4efa-a0e0-76de05a9347b_1462x786.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!nVZF!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2Fa3be5b28-c74b-4efa-a0e0-76de05a9347b_1462x786.png" width="1456" height="783" data-attrs="{&quot;src&quot;:&quot;https://bucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com/public/images/a3be5b28-c74b-4efa-a0e0-76de05a9347b_1462x786.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:783,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:98611,&quot;alt&quot;:&quot;A screenshot of that SQL quey running in Datasette, with a number form field&quot;,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="A screenshot of that SQL quey running in Datasette, with a number form field" title="A screenshot of that SQL quey running in Datasette, with a number form field" srcset="https://substackcdn.com/image/fetch/$s_!nVZF!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2Fa3be5b28-c74b-4efa-a0e0-76de05a9347b_1462x786.png 424w, https://substackcdn.com/image/fetch/$s_!nVZF!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2Fa3be5b28-c74b-4efa-a0e0-76de05a9347b_1462x786.png 848w, https://substackcdn.com/image/fetch/$s_!nVZF!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2Fa3be5b28-c74b-4efa-a0e0-76de05a9347b_1462x786.png 1272w, https://substackcdn.com/image/fetch/$s_!nVZF!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2Fa3be5b28-c74b-4efa-a0e0-76de05a9347b_1462x786.png 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>I&#8217;ve been planning to add <a href="https://github.com/simonw/datasette/issues/1090">custom form field widgets</a> for a while. <a href="https://datasette.io/plugins/datasette-leaflet-freedraw">datasette-leaflet-freedraw</a> is a new plugin which starts exploring this concept, by enhancing any fields ending in &#8220;freedraw&#8221; with a map widget that lets users draw a shape, which will be converted to a GeoJSON MultiPolygon ready to be sent back to Datasette.</p><p>Here&#8217;s <a href="https://calands.datasettes.com/calands?sql=select%0D%0A++AsGeoJSON%28geometry%29%2C+*%0D%0Afrom%0D%0A++CPAD_2020a_SuperUnits%0D%0Awhere%0D%0A++Intersects%28GeomFromGeoJSON%28%3Afreedraw%29%2C+geometry%29+%3D+1%0D%0A++and+CPAD_2020a_SuperUnits.rowid+in+%28%0D%0A++++select%0D%0A++++++rowid%0D%0A++++from%0D%0A++++++SpatialIndex%0D%0A++++where%0D%0A++++++f_table_name+%3D+%27CPAD_2020a_SuperUnits%27%0D%0A++++++and+search_frame+%3D+GeomFromGeoJSON%28%3Afreedraw%29%0D%0A++%29&amp;freedraw=%7B%22type%22%3A%22MultiPolygon%22%2C%22coordinates%22%3A%5B%5B%5B%5B-122.42134094238283%2C37.82009043941308%5D%2C%5B-122.39662170410158%2C37.814123701604466%5D%2C%5B-122.3925018310547%2C37.811953859192705%5D%2C%5B-122.36572265625001%2C37.78645343442073%5D%2C%5B-122.35473632812501%2C37.77017196507861%5D%2C%5B-122.34512329101564%2C37.74954362315467%5D%2C%5B-122.34512329101564%2C37.72836644908416%5D%2C%5B-122.34855651855469%2C37.72021976910832%5D%2C%5B-122.36640930175783%2C37.71044257039148%5D%2C%5B-122.3815155029297%2C37.70772645289051%5D%2C%5B-122.42134094238283%2C37.709899354855125%5D%2C%5B-122.45429992675781%2C37.70609673460725%5D%2C%5B-122.49824523925783%2C37.708269684354526%5D%2C%5B-122.51953125000001%2C37.71315858834301%5D%2C%5B-122.52296447753908%2C37.71533133102705%5D%2C%5B-122.53395080566408%2C37.73108180994824%5D%2C%5B-122.53463745117189%2C37.751172385606196%5D%2C%5B-122.53120422363283%2C37.763115548102924%5D%2C%5B-122.51472473144533%2C37.78808138412046%5D%2C%5B-122.49412536621095%2C37.80761398306056%5D%2C%5B-122.47489929199219%2C37.81792077237497%5D%2C%5B-122.45910644531251%2C37.81737834565083%5D%2C%5B-122.44331359863283%2C37.82171764783966%5D%2C%5B-122.42134094238283%2C37.82009043941308%5D%5D%5D%5D%7D">an interactive demo</a>:</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!5Zep!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2F12ce5773-5381-459d-a498-b04e265ec89a_1176x994.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!5Zep!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2F12ce5773-5381-459d-a498-b04e265ec89a_1176x994.png 424w, https://substackcdn.com/image/fetch/$s_!5Zep!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2F12ce5773-5381-459d-a498-b04e265ec89a_1176x994.png 848w, https://substackcdn.com/image/fetch/$s_!5Zep!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2F12ce5773-5381-459d-a498-b04e265ec89a_1176x994.png 1272w, https://substackcdn.com/image/fetch/$s_!5Zep!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2F12ce5773-5381-459d-a498-b04e265ec89a_1176x994.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!5Zep!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2F12ce5773-5381-459d-a498-b04e265ec89a_1176x994.png" width="1176" height="994" data-attrs="{&quot;src&quot;:&quot;https://bucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com/public/images/12ce5773-5381-459d-a498-b04e265ec89a_1176x994.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:994,&quot;width&quot;:1176,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;A screenshot showing the plugin with a shape drawn around San Francisco&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="A screenshot showing the plugin with a shape drawn around San Francisco" title="A screenshot showing the plugin with a shape drawn around San Francisco" srcset="https://substackcdn.com/image/fetch/$s_!5Zep!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2F12ce5773-5381-459d-a498-b04e265ec89a_1176x994.png 424w, https://substackcdn.com/image/fetch/$s_!5Zep!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2F12ce5773-5381-459d-a498-b04e265ec89a_1176x994.png 848w, https://substackcdn.com/image/fetch/$s_!5Zep!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2F12ce5773-5381-459d-a498-b04e265ec89a_1176x994.png 1272w, https://substackcdn.com/image/fetch/$s_!5Zep!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2F12ce5773-5381-459d-a498-b04e265ec89a_1176x994.png 1456w" sizes="100vw"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>You can read more about this plugin in <a href="https://simonwillison.net/2021/Jan/24/drawing-shapes-spatialite/">Drawing shapes on a map to query a SpatiaLite database</a>. </p><h2>Datasette 0.54</h2><p>I released Datasette 0.54 yesterday. You can read <a href="https://simonwillison.net/2021/Jan/25/datasette/">the annotated release notes</a> on my blog, but the two two big new features in this release are the _internal SQLite in-memory database storing details of all connected databases and tables, and support for JavaScript modules in plugins and additional scripts.</p><h3>The _internal database</h3><p>I want Database to be able to handle dozens of attached database files containing potentially thousands of tables. To support this, the Datasette homepage will need to get a lot smarter: it needs to provide pagination and search and filtering against the list of tables themselves.</p><p>Datasette is already pretty good at providing paginated search and filtering! The new _internal database is the first step to providing this feature: it's an in-memory database which is automatically updated with details of all of the attached databases, plus their columns, foreign keys and indexes.</p><p>As part of building this I <a href="https://github.com/simonw/datasette/issues/1151">added support</a> for shared, named in-memory databases - so plugins can now create an in-memory database that will persist for the lifetime of the Datasette process and can be accessed from multiple connections.</p><p>You can browse a demo of the _internal database by <a href="https://latest.datasette.io/login-as-root">signing into the demo as root</a> and then accessing <a href="https://latest.datasette.io/_internal">latest.datasette.io/_internal</a></p><h3>JavaScript modules</h3><p>JavaScript modules are supported by modern browsers and allow you to use the &#8220;import&#8221; and &#8220;export&#8221; keywords from ECMAScript 2015 without needing a build step.</p><p>They only work in &lt;script&gt; blocks with the type="module" attribute, so Datasette now has the ability to include scripts with that attribute, for both <a href="https://docs.datasette.io/en/stable/plugin_hooks.html#extra-body-script-template-database-table-columns-view-name-request-datasette">plugins</a> and <a href="https://docs.datasette.io/en/stable/custom_templates.html#customization-css-and-javascript">extra scripts</a> loaded through configuration.</p><p>A big benefit of native modules is that they help avoid accidentally loading the same script twice. There are three Datasette plugins that use Leaflet maps now - <a href="https://datasette.io/plugins/datasette-cluster-map">datasette-cluster-map</a>, <a href="https://datasette.io/plugins/datasette-leaflet-geoson">datasette-leaflet-geojson</a> and the new <a href="https://datasette.io/plugins/datasette-leaflet-freedraw">datasette-leaflet-freedraw</a> - so I've started working on a new base plugin called <a href="https://datasette.io/plugins/datasette-leaflet">datasette-leaflet</a> which makes the latest Leaflet available for all of them to use via the modules system.</p><h2>datasette-css-properties and datasette-export-notebook</h2><p>Two more new plugins: these use Datasette's <a href="https://docs.datasette.io/en/stable/plugin_hooks.html#register-output-renderer-datasette">register_output_renderer hook</a> to add extra options for getting data back out of Datasette.</p><p>I wrote <a href="https://simonwillison.net/2021/Jan/7/css-apis-no-javascript/">about datasette-css-properties here</a>  - it's a very weird plugin! It was inspired by <a href="https://css-tricks.com/custom-properties-as-state/">Custom Properties as State</a> by Chris Coyier, which proposed server APIs that return CSS files defining custom properties that can be used to style pages.</p><p>I'm not sure if this is a good idea, but the joy of plugins is that you can try out ideas like this without risking damage to the integrity of the core project!</p><p><a href="https://datasette.io/plugins/datasette-export-notebook">datasette-export-notebook</a> is more obviously useful: it adds instructions for exporting data from Datasette into Jupyter and Observable notebooks. You can try <a href="https://covid-19.datasettes.com/covid/latest_ny_times_counties_with_populations.Notebook">that out here</a> against the Covid-19 Datasette.</p><h2>Datasette Office Hours</h2><p>I've now had 17 conversations with people about Datasette in office hours sessions. It's been absolutely wonderful! Talking to people about what they're building, or what they might build, is incredibly valuable for helping me decide what to work on next, and it's also just really fun talking to people about the project.</p><p>I plan to continue running office hours sessions on Friday for the forseeable future. If you'd like to chat about the project you can <a href="https://calendly.com/swillison/datasette-office-hours">grab a slot with me here</a>.</p>]]></content:encoded></item><item><title><![CDATA[Official project website for Datasette, building a search engine with Dogsheep Beta, sqlite-utils analyze-tables]]></title><description><![CDATA[Datasette Weekly(ish) volume 5]]></description><link>https://datasette.substack.com/p/official-project-website-for-datasette</link><guid isPermaLink="false">https://datasette.substack.com/p/official-project-website-for-datasette</guid><dc:creator><![CDATA[Simon Willison]]></dc:creator><pubDate>Wed, 23 Dec 2020 23:51:21 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!IPVw!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2F275fa7c6-5e4d-4f1d-ae02-152203161438_2178x1878.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Datasette finally has an official project website!</p><p><strong><a href="https://datasette.io/">datasette.io</a></strong> is the new home for Datasette. The site features news about the project, explains the problems that Datasette and its ecosystem of tools try to solve and gathers together a complete directory of <a href="https://datasette.io/plugins">plugins</a> and <a href="https://datasette.io/tools">tools</a> for using with Datasette.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!IPVw!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2F275fa7c6-5e4d-4f1d-ae02-152203161438_2178x1878.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!IPVw!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2F275fa7c6-5e4d-4f1d-ae02-152203161438_2178x1878.png 424w, https://substackcdn.com/image/fetch/$s_!IPVw!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2F275fa7c6-5e4d-4f1d-ae02-152203161438_2178x1878.png 848w, https://substackcdn.com/image/fetch/$s_!IPVw!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2F275fa7c6-5e4d-4f1d-ae02-152203161438_2178x1878.png 1272w, https://substackcdn.com/image/fetch/$s_!IPVw!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2F275fa7c6-5e4d-4f1d-ae02-152203161438_2178x1878.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!IPVw!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2F275fa7c6-5e4d-4f1d-ae02-152203161438_2178x1878.png" width="1456" height="1255" data-attrs="{&quot;src&quot;:&quot;https://bucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com/public/images/275fa7c6-5e4d-4f1d-ae02-152203161438_2178x1878.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1255,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:712970,&quot;alt&quot;:&quot;A screenshot of datasette.io&quot;,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="A screenshot of datasette.io" title="A screenshot of datasette.io" srcset="https://substackcdn.com/image/fetch/$s_!IPVw!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2F275fa7c6-5e4d-4f1d-ae02-152203161438_2178x1878.png 424w, https://substackcdn.com/image/fetch/$s_!IPVw!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2F275fa7c6-5e4d-4f1d-ae02-152203161438_2178x1878.png 848w, https://substackcdn.com/image/fetch/$s_!IPVw!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2F275fa7c6-5e4d-4f1d-ae02-152203161438_2178x1878.png 1272w, https://substackcdn.com/image/fetch/$s_!IPVw!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2F275fa7c6-5e4d-4f1d-ae02-152203161438_2178x1878.png 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>The site itself is built using Datasette - it&#8217;s actually a heavily themed installation of Datasette, making extensive use of the <a href="https://datasette.io/plugins/datasette-template-sql">datasette-template-sql</a> plugin and <a href="https://docs.datasette.io/en/stable/custom_templates.html#custom-pages">custom pages</a> to implement the different sections of the site. The code for the site is available on GitHub at <a href="https://github.com/simonw/datasette.io">simonw/datasette.io</a>.</p><p>I wrote more about how the site works in <a href="https://simonwillison.net/2020/Dec/13/datasette-io/">datasette.io, an official project website for Datasette</a>.</p><h2>Adding search to datasette.io</h2><p>datasette.io also includes <a href="https://datasette.io/-/beta">a search engine</a> for the project.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!Og8g!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2Fc6597ffe-b6d8-4d07-9e73-6c19cab96e9e_1066x1213.jpeg" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!Og8g!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2Fc6597ffe-b6d8-4d07-9e73-6c19cab96e9e_1066x1213.jpeg 424w, https://substackcdn.com/image/fetch/$s_!Og8g!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2Fc6597ffe-b6d8-4d07-9e73-6c19cab96e9e_1066x1213.jpeg 848w, https://substackcdn.com/image/fetch/$s_!Og8g!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2Fc6597ffe-b6d8-4d07-9e73-6c19cab96e9e_1066x1213.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!Og8g!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2Fc6597ffe-b6d8-4d07-9e73-6c19cab96e9e_1066x1213.jpeg 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!Og8g!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2Fc6597ffe-b6d8-4d07-9e73-6c19cab96e9e_1066x1213.jpeg" width="1066" height="1213" data-attrs="{&quot;src&quot;:&quot;https://bucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com/public/images/c6597ffe-b6d8-4d07-9e73-6c19cab96e9e_1066x1213.jpeg&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1213,&quot;width&quot;:1066,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;A screenshot of dogsheep.io search results for ripgrep&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="A screenshot of dogsheep.io search results for ripgrep" title="A screenshot of dogsheep.io search results for ripgrep" srcset="https://substackcdn.com/image/fetch/$s_!Og8g!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2Fc6597ffe-b6d8-4d07-9e73-6c19cab96e9e_1066x1213.jpeg 424w, https://substackcdn.com/image/fetch/$s_!Og8g!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2Fc6597ffe-b6d8-4d07-9e73-6c19cab96e9e_1066x1213.jpeg 848w, https://substackcdn.com/image/fetch/$s_!Og8g!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2Fc6597ffe-b6d8-4d07-9e73-6c19cab96e9e_1066x1213.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!Og8g!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2Fc6597ffe-b6d8-4d07-9e73-6c19cab96e9e_1066x1213.jpeg 1456w" sizes="100vw"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>It provides faceted search across:</p><ul><li><p>Every section of the latest documentation (416 total)</p></li><li><p>48 plugin READMEs</p></li><li><p>22 tool READMEs</p></li><li><p>64 news items posted on the Datasette website</p></li><li><p>212 items from my blog</p></li><li><p>Release notes from 557 package releases</p></li></ul><p>That&#8217;s 1,319 items total! The overall Datasette project continues to grow in all kinds of directions, so having a single search engine to tie it all together felt like a useful addition.</p><p>I built the search engine using <a href="https://github.com/dogsheep/beta">Dogsheep Beta</a>, the cross-table SQLite search engine I originally built for my <a href="https://datasette.substack.com/p/dogsheep-personal-analytics-with">Dogsheep Personal Analytics project</a>.</p><p>You can read more about how the search engine works, including a breakdown of the YAML configuration used to create the unified search index, in <a href="https://simonwillison.net/2020/Dec/19/dogsheep-beta/">Building a search engine for datasette.io</a>.</p><h2>sqlite-utils analyze-tables</h2><p>I released <a href="https://sqlite-utils.readthedocs.io/en/stable/changelog.html#v3-1">sqlite-utils 3.1</a> with a useful feature for getting to grips with new data: the &#8220;sqlite-utils analyze-tables&#8221; command.</p><p>Run this against any SQLite database and it will spit out a summary of every column in every table, showing you common and least-common values, the number of distinct items and how many rows contain nulls or blanks.</p><p>The <a href="https://sqlite-utils.readthedocs.io/en/stable/cli.html#cli-analyze-tables">analyze-tables documentation</a> has more. In addition to outputting the summary to the command-line you can add the &#8220;--save&#8221; option to store the generated summary in a SQLite table called _analyze_tables_ - which means you can then further examine it using Datasette.</p><p>Here&#8217;s <a href="https://github-to-sqlite.dogsheep.net/github/_analyze_tables_">an example</a> of the output when running analyze-tables against a database created using my <a href="https://github.com/dogsheep/github-to-sqlite">github-to-sqlite</a> tool.</p><h2>Office hours for 2021</h2><p>Every Friday I run <strong><a href="https://calendly.com/swillison/datasette-office-hours">Datasette Office Hours</a></strong> sessions, where people who use Datasette (or who are interested in using it) can grab a 20 minute video chat with me to talk about the project. I&#8217;ve done a couple of weeks of these now and they&#8217;ve been fantastic - it&#8217;s so interesting hearing how people are using or plan-to-use the tool, and I&#8217;ve already had a bunch of great feedback to help me plan the next steps for the project.</p><p>If you&#8217;re interested in talking to me, slots in January 2021 are available now!</p><p></p>]]></content:encoded></item><item><title><![CDATA[Datasette Office Hours, Personal Data Warehouses, datasette-ripgrep, datasette-indieauth, Datasette Client for Observable and more]]></title><description><![CDATA[Datasette Weekly(ish) volume 4]]></description><link>https://datasette.substack.com/p/datasette-office-hours-personal-data</link><guid isPermaLink="false">https://datasette.substack.com/p/datasette-office-hours-personal-data</guid><dc:creator><![CDATA[Simon Willison]]></dc:creator><pubDate>Mon, 30 Nov 2020 22:59:57 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!YHfs!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2F297e4bb8-6516-4a7d-aee8-c9f989dfa4a3_1200x750.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>In this edition: book Datasette office hours, build a personal data warehouse, deploy a regular expression code search engine, plus updates on Datasette project releases from the past month.</p><h2>Datasette Office Hours</h2><p>One of the toughest things about running open source projects is the challenge of getting feedback: users often assume they are doing you a favor by leaving you alone, so generally you only hear from people if they find a bug.</p><p>I&#8217;m always really keen to talk to people who are using Datasette - or who aren&#8217;t using it yet, since I want to understand what it&#8217;s missing.</p><p>So I&#8217;m going to try a new approach: I&#8217;m setting aside time every Friday for Datasette Office Hours, where anyone can book a 20 minute Zoom call with me to talk about the project.</p><p>I&#8217;d love to hear from you if:</p><ul><li><p>You&#8217;re solving problems with Datasette</p></li><li><p>You have a problem you think Datasette might be able to help with</p></li><li><p>You&#8217;ve run into problems using Datasette</p></li><li><p>You have thoughts on the direction you think the project should go</p></li><li><p>You&#8217;d like to see some demos of things I&#8217;m working on</p></li><li><p>You&#8217;re just interested in having a chat!</p></li></ul><p>You can sign up for office hours at <a href="https://calendly.com/swillison/datasette-office-hours">calendly.com/swillison/datasette-office-hours</a></p><h2>Personal Data Warehouses</h2><p>A gave a talk for the <a href="https://octo.github.com/">GitHub OCTO Speaker Series</a> a couple of weeks ago about my Datasette and Dogsheep projects called <strong>Personal Data Warehouses: Reclaiming Your Data</strong>.</p><p>GitHub shared the video on their YouTube channel, and I&#8217;ve prepared <a href="https://simonwillison.net/2020/Nov/14/personal-data-warehouses/">an extended, annotated version of the talk</a> with additional screenshots, links and notes.</p><p>The talk shows how I built my own personal data warehouse on top of Datasette that imports, analyzes and visualizes my data from Twitter, Swarm, GitHub, 23AndMe, Apple HealthKit, Apple Photos and more.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://simonwillison.net/2020/Nov/14/personal-data-warehouses/" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!YHfs!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2F297e4bb8-6516-4a7d-aee8-c9f989dfa4a3_1200x750.jpeg 424w, https://substackcdn.com/image/fetch/$s_!YHfs!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2F297e4bb8-6516-4a7d-aee8-c9f989dfa4a3_1200x750.jpeg 848w, https://substackcdn.com/image/fetch/$s_!YHfs!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2F297e4bb8-6516-4a7d-aee8-c9f989dfa4a3_1200x750.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!YHfs!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2F297e4bb8-6516-4a7d-aee8-c9f989dfa4a3_1200x750.jpeg 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!YHfs!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2F297e4bb8-6516-4a7d-aee8-c9f989dfa4a3_1200x750.jpeg" width="1200" height="750" data-attrs="{&quot;src&quot;:&quot;https://bucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com/public/images/297e4bb8-6516-4a7d-aee8-c9f989dfa4a3_1200x750.jpeg&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:750,&quot;width&quot;:1200,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;Simon Willison - FOSS Developer and Consultant, Python, Django, Datasette&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:&quot;https://simonwillison.net/2020/Nov/14/personal-data-warehouses/&quot;,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="Simon Willison - FOSS Developer and Consultant, Python, Django, Datasette" title="Simon Willison - FOSS Developer and Consultant, Python, Django, Datasette" srcset="https://substackcdn.com/image/fetch/$s_!YHfs!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2F297e4bb8-6516-4a7d-aee8-c9f989dfa4a3_1200x750.jpeg 424w, https://substackcdn.com/image/fetch/$s_!YHfs!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2F297e4bb8-6516-4a7d-aee8-c9f989dfa4a3_1200x750.jpeg 848w, https://substackcdn.com/image/fetch/$s_!YHfs!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2F297e4bb8-6516-4a7d-aee8-c9f989dfa4a3_1200x750.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!YHfs!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2F297e4bb8-6516-4a7d-aee8-c9f989dfa4a3_1200x750.jpeg 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><h2>datasette-ripgrep</h2><p><strong><a href="https://github.com/simonw/datasette-ripgrep">datasette-ripgrep</a></strong> is a web application I built for running regular expression searches against source code, built on top of the amazing <a href="https://github.com/BurntSushi/ripgrep">ripgrep</a> command-line tool.</p><p>Here are some example searches, running across around 100 Datasette and Datasette- adjacent GitHub repositories:</p><ul><li><p><a href="https://ripgrep.datasette.io/-/ripgrep?pattern=with.*AsyncClient">with.*AsyncClient</a> - regular expression search for with.*AsyncClient</p></li><li><p><a href="https://ripgrep.datasette.io/-/ripgrep?pattern=.plugin_config(&amp;literal=on">.plugin_config, literal=on</a> - a non-regular expression search for .plugin_config(</p></li><li><p><a href="https://ripgrep.datasette.io/-/ripgrep?pattern=with.*AsyncClient&amp;glob=datasette%2F%2A%2A">with.*AsyncClient glob=datasette/**</a> - search for that pattern only within the datasette/ top folder</p></li><li><p><a href="https://ripgrep.datasette.io/-/ripgrep?pattern=%22sqlite-utils%5B%22%3E%5D&amp;glob=setup.py">"sqlite-utils["&gt;] glob=setup.py</a> - a regular expression search for packages that depend on either sqlite-utils or sqlite-utils&gt;=some-version</p></li><li><p><a href="https://ripgrep.datasette.io/-/ripgrep?pattern=test&amp;glob=%21*.html">test glob=!*.html</a> - search for the string test but exclude results in HTML files</p></li></ul><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!PZxW!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2Ffa49b240-1eb1-4945-9ed5-c31a52294d71_2031x1632.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!PZxW!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2Ffa49b240-1eb1-4945-9ed5-c31a52294d71_2031x1632.png 424w, https://substackcdn.com/image/fetch/$s_!PZxW!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2Ffa49b240-1eb1-4945-9ed5-c31a52294d71_2031x1632.png 848w, https://substackcdn.com/image/fetch/$s_!PZxW!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2Ffa49b240-1eb1-4945-9ed5-c31a52294d71_2031x1632.png 1272w, https://substackcdn.com/image/fetch/$s_!PZxW!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2Ffa49b240-1eb1-4945-9ed5-c31a52294d71_2031x1632.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!PZxW!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2Ffa49b240-1eb1-4945-9ed5-c31a52294d71_2031x1632.png" width="1456" height="1170" data-attrs="{&quot;src&quot;:&quot;https://bucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com/public/images/fa49b240-1eb1-4945-9ed5-c31a52294d71_2031x1632.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1170,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:320553,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!PZxW!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2Ffa49b240-1eb1-4945-9ed5-c31a52294d71_2031x1632.png 424w, https://substackcdn.com/image/fetch/$s_!PZxW!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2Ffa49b240-1eb1-4945-9ed5-c31a52294d71_2031x1632.png 848w, https://substackcdn.com/image/fetch/$s_!PZxW!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2Ffa49b240-1eb1-4945-9ed5-c31a52294d71_2031x1632.png 1272w, https://substackcdn.com/image/fetch/$s_!PZxW!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2Ffa49b240-1eb1-4945-9ed5-c31a52294d71_2031x1632.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>I wrote about the project on my blog, in <strong><a href="https://simonwillison.net/2020/Nov/28/datasette-ripgrep/">datasette-ripgrep: deploy a regular expression search engine for your source code</a></strong></p><p>It&#8217;s an interesting use-case for Datasette in that it doesn&#8217;t use SQLite at all - the tool works by running the &#8220;rg&#8221; executable against a folder full of source code. It does benefit from Datasette&#8217;s &#8220;datasette publish&#8221; mechanism - the following one-liner will deploy a Datasette instance to Google Cloud Run pre-configured to run searches against everything in the &#8220;all/&#8220; directory, which is uploaded as part of the deployment:</p><pre><code>datasette publish cloudrun \
    --metadata metadata.json \
    --static all:all \
    --install=datasette-ripgrep \
    --service datasette-ripgrep \
    --apt-get-install ripgrep</code></pre><p>The official demo is deployed by <a href="https://github.com/simonw/datasette-ripgrep/blob/main/.github/workflows/deploy_demo.yml">this GitHub Actions workflow</a> which pulls a list of repos using <a href="https://github.com/dogsheep/github-to-sqlite">github-to-sqlite</a>, filters down to just the ones I want to include in the demo, then deploys them using the above pattern. </p><h2>datasette-indieauth</h2><p>My other big plugin project this month was <strong><a href="https://github.com/simonw/datasette-indieauth">datasette-indieauth</a></strong>, an authentication plugin which adds support for the emerging <a href="https://indieauth.net/">IndieAuth</a> single sign-on standard.</p><p>You can read more about this project in <a href="https://simonwillison.net/2020/Nov/18/indieauth/">Implementing IndieAuth for Datasette</a> on my blog. IndieAuth is a spiritual successor to OpenID which allows users to sign-in using a website address that they control. It&#8217;s a particularly good fit for Datasette as it allows you to deploy single sign-on without first registering your site with a central authority.</p><p>Here&#8217;s an animation showing what the experience looks like signing in to the official demo at <a href="https://datasette-indieauth-demo.datasette.io/-/indieauth">datasette-indieauth-demo.datasette.io/-/indieauth</a>:</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!5dXm!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2F47fab8d2-57ba-473c-8f08-7f2993236e5c_819x375.gif" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!5dXm!,w_424,c_limit,f_webp,q_auto:good,fl_lossy/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2F47fab8d2-57ba-473c-8f08-7f2993236e5c_819x375.gif 424w, https://substackcdn.com/image/fetch/$s_!5dXm!,w_848,c_limit,f_webp,q_auto:good,fl_lossy/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2F47fab8d2-57ba-473c-8f08-7f2993236e5c_819x375.gif 848w, https://substackcdn.com/image/fetch/$s_!5dXm!,w_1272,c_limit,f_webp,q_auto:good,fl_lossy/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2F47fab8d2-57ba-473c-8f08-7f2993236e5c_819x375.gif 1272w, https://substackcdn.com/image/fetch/$s_!5dXm!,w_1456,c_limit,f_webp,q_auto:good,fl_lossy/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2F47fab8d2-57ba-473c-8f08-7f2993236e5c_819x375.gif 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!5dXm!,w_1456,c_limit,f_auto,q_auto:good,fl_lossy/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2F47fab8d2-57ba-473c-8f08-7f2993236e5c_819x375.gif" width="819" height="375" data-attrs="{&quot;src&quot;:&quot;https://bucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com/public/images/47fab8d2-57ba-473c-8f08-7f2993236e5c_819x375.gif&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:375,&quot;width&quot;:819,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;Animated demo: starts at an IndieAuth login screen, enters simonwillison.net, gets redirected to another site where clicking the verify button completes the sign-in and redirects back to the original page.&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="Animated demo: starts at an IndieAuth login screen, enters simonwillison.net, gets redirected to another site where clicking the verify button completes the sign-in and redirects back to the original page." title="Animated demo: starts at an IndieAuth login screen, enters simonwillison.net, gets redirected to another site where clicking the verify button completes the sign-in and redirects back to the original page." srcset="https://substackcdn.com/image/fetch/$s_!5dXm!,w_424,c_limit,f_auto,q_auto:good,fl_lossy/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2F47fab8d2-57ba-473c-8f08-7f2993236e5c_819x375.gif 424w, https://substackcdn.com/image/fetch/$s_!5dXm!,w_848,c_limit,f_auto,q_auto:good,fl_lossy/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2F47fab8d2-57ba-473c-8f08-7f2993236e5c_819x375.gif 848w, https://substackcdn.com/image/fetch/$s_!5dXm!,w_1272,c_limit,f_auto,q_auto:good,fl_lossy/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2F47fab8d2-57ba-473c-8f08-7f2993236e5c_819x375.gif 1272w, https://substackcdn.com/image/fetch/$s_!5dXm!,w_1456,c_limit,f_auto,q_auto:good,fl_lossy/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2F47fab8d2-57ba-473c-8f08-7f2993236e5c_819x375.gif 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><h2>Datasette Client for Observable</h2><p>Alex Garcia built a beautiful JavaScript client library for interacting with data hosted by Datasette from Observable notebooks. His demo at <a href="https://observablehq.com/@asg017/datasette-client">observablehq.com/@asg017/datasette-client</a> shows how to use the client and demonstrates it integrating with Observable&#8217;s form elements and visualizing data as a stacked area chart using D3.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!t3YI!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2F6605af7f-affc-4ef6-b912-e0d3707209f6_1224x797.gif" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!t3YI!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2F6605af7f-affc-4ef6-b912-e0d3707209f6_1224x797.gif 424w, https://substackcdn.com/image/fetch/$s_!t3YI!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2F6605af7f-affc-4ef6-b912-e0d3707209f6_1224x797.gif 848w, https://substackcdn.com/image/fetch/$s_!t3YI!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2F6605af7f-affc-4ef6-b912-e0d3707209f6_1224x797.gif 1272w, https://substackcdn.com/image/fetch/$s_!t3YI!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2F6605af7f-affc-4ef6-b912-e0d3707209f6_1224x797.gif 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!t3YI!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2F6605af7f-affc-4ef6-b912-e0d3707209f6_1224x797.gif" width="1224" height="797" data-attrs="{&quot;src&quot;:&quot;https://bucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com/public/images/6605af7f-affc-4ef6-b912-e0d3707209f6_1224x797.gif&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:797,&quot;width&quot;:1224,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:1399233,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/gif&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!t3YI!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2F6605af7f-affc-4ef6-b912-e0d3707209f6_1224x797.gif 424w, https://substackcdn.com/image/fetch/$s_!t3YI!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2F6605af7f-affc-4ef6-b912-e0d3707209f6_1224x797.gif 848w, https://substackcdn.com/image/fetch/$s_!t3YI!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2F6605af7f-affc-4ef6-b912-e0d3707209f6_1224x797.gif 1272w, https://substackcdn.com/image/fetch/$s_!t3YI!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2F6605af7f-affc-4ef6-b912-e0d3707209f6_1224x797.gif 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><h2>Other significant releases this month</h2><p><strong><a href="https://docs.datasette.io/en/stable/changelog.html#v0-52">Datasette 0.52</a></strong> (and <a href="https://docs.datasette.io/en/stable/changelog.html#v0-52-1">0.52.1</a>) - a relatively small release, this adds a new <a href="https://docs.datasette.io/en/stable/plugin_hooks.html#plugin-hook-database-actions">database_actions(datasette, actor, database)</a> plugin hook and renames the --config option to --setting - --config still works but shows a deprecation message, and will be removed in Datasette 1.0.</p><p><strong><a href="https://github.com/dogsheep/github-to-sqlite">github-to-sqlite 2.8</a></strong> adds a new &#8220;github-to-sqlite workflows&#8221; command which imports GitHub Actions workflow YAML flies and uses them to populate new <a href="https://github-to-sqlite.dogsheep.net/github/workflows">workflows</a>, <a href="https://github-to-sqlite.dogsheep.net/github/jobs">jobs</a> and <a href="https://github-to-sqlite.dogsheep.net/github/steps?_facet=repo">steps</a> tables. </p><p><strong><a href="https://github.com/simonw/datasette-graphql/releases/tag/1.2">datasette-graphql 1.2</a></strong> and <a href="https://github.com/simonw/datasette-graphql/releases/tag/1.3">1.3</a> add support for the Datasette view-instance permission and view-database permissions, and use the new table actions plugin hook to add example GraphQL queries to the cog action menu on every table page. You can try that out against <a href="https://github-to-sqlite.dogsheep.net/github/commits">this commits table</a> created using github-to-sqlite.</p><p><strong><a href="https://sqlite-utils.readthedocs.io/en/stable/changelog.html#v3-0">sqlite-utils 3.0</a></strong> adds a new command-line tool and Python method for executing full-text searches against a table, and returning the results ordered by relevance. It also adds a new --tsv output option and makes some small changes to other command-line options, hence the 3.0 major version bump.</p>]]></content:encoded></item><item><title><![CDATA[Datasette 0.51 - new visual design, smarter plugins]]></title><description><![CDATA[Datasette Weekly volume 3]]></description><link>https://datasette.substack.com/p/datasette-051-new-visual-design-smarter</link><guid isPermaLink="false">https://datasette.substack.com/p/datasette-051-new-visual-design-smarter</guid><dc:creator><![CDATA[Simon Willison]]></dc:creator><pubDate>Tue, 03 Nov 2020 00:57:29 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!-cPb!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2F288bd363-48dd-40c0-8486-0d090b42eddb_1480x1086.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>In this edition: annotated release notes for Datasette 0.51, and datasette-copyable as plugin of the week.</p><p>(You may have seen these annotated release notes <a href="https://simonwillison.net/2020/Nov/1/datasette-0-51/">on my blog</a> - if so, feel free to skip straight to plugin of the week.)</p><h2>Datasette 0.51, the annotated release notes</h2><p>I shipped <a href="https://docs.datasette.io/en/stable/changelog.html#v0-51">Datasette 0.51</a>  at the weekend, with a new visual design, plugin hooks for adding navigation  options, better handling of binary data, URL building utility methods  and better support for running Datasette behind a proxy. It&#8217;s a lot of  stuff! Here are the annotated release notes.</p><h4>New visual design</h4><blockquote><p>Datasette is no longer white and grey with blue and purple links! <a href="https://twitter.com/natbat">Natalie Downe</a> has been working on a visual refresh, the first iteration of which is included in this release. (<a href="https://github.com/simonw/datasette/pull/1056">#1056</a>)</p></blockquote><p>It&#8217;s about time Datasette grew beyond its  clearly-designed-by-a-mostly-backend-engineer roots. Natalie has been  helping me start adding some visual polish: we&#8217;ve started with an update  to the colour scheme and will be continuing to iterate on the visual  design as the project evolves towards the 1.0 release.</p><p>The new design makes the navigation bar much more obvious, which is  important for this release since the new navigation menu (tucked away  behind a three-bar icon) is a key new feature.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!-cPb!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2F288bd363-48dd-40c0-8486-0d090b42eddb_1480x1086.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!-cPb!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2F288bd363-48dd-40c0-8486-0d090b42eddb_1480x1086.png 424w, https://substackcdn.com/image/fetch/$s_!-cPb!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2F288bd363-48dd-40c0-8486-0d090b42eddb_1480x1086.png 848w, https://substackcdn.com/image/fetch/$s_!-cPb!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2F288bd363-48dd-40c0-8486-0d090b42eddb_1480x1086.png 1272w, https://substackcdn.com/image/fetch/$s_!-cPb!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2F288bd363-48dd-40c0-8486-0d090b42eddb_1480x1086.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!-cPb!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2F288bd363-48dd-40c0-8486-0d090b42eddb_1480x1086.png" width="1456" height="1068" data-attrs="{&quot;src&quot;:&quot;https://bucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com/public/images/288bd363-48dd-40c0-8486-0d090b42eddb_1480x1086.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1068,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;A screenshot of the new Datasette visual design, also showcasing two new menu icons&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="A screenshot of the new Datasette visual design, also showcasing two new menu icons" title="A screenshot of the new Datasette visual design, also showcasing two new menu icons" srcset="https://substackcdn.com/image/fetch/$s_!-cPb!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2F288bd363-48dd-40c0-8486-0d090b42eddb_1480x1086.png 424w, https://substackcdn.com/image/fetch/$s_!-cPb!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2F288bd363-48dd-40c0-8486-0d090b42eddb_1480x1086.png 848w, https://substackcdn.com/image/fetch/$s_!-cPb!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2F288bd363-48dd-40c0-8486-0d090b42eddb_1480x1086.png 1272w, https://substackcdn.com/image/fetch/$s_!-cPb!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2F288bd363-48dd-40c0-8486-0d090b42eddb_1480x1086.png 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><h4>Plugins can now add links within Datasette</h4><blockquote><p>A number of existing Datasette plugins add new pages to the Datasette interface, providig tools for things like <a href="https://github.com/simonw/datasette-upload-csvs">uploading CSVs</a>, <a href="https://github.com/simonw/datasette-edit-schema">editing table schemas</a> or <a href="https://github.com/simonw/datasette-configure-fts">configuring full-text search</a>.</p><p>Plugins like this can now link to themselves from other parts of Datasette interface. The <a href="https://docs.datasette.io/en/stable/plugin_hooks.html#plugin-hook-menu-links">menu_links(datasette, actor)</a> hook (<a href="https://github.com/simonw/datasette/issues/1064">#1064</a>) lets plugins add links to Datasette&#8217;s new top-right application menu, and the <a href="https://docs.datasette.io/en/stable/plugin_hooks.html#plugin-hook-table-actions">table_actions(datasette, actor, database, table)</a> hook (<a href="https://github.com/simonw/datasette/issues/1066">#1066</a>) adds links to a new &#8220;table actions&#8221; menu on the table page.</p></blockquote><p>This feature has been a long time coming. I&#8217;ve been writing an  increasing number of plugins that add new pages to Datasette, and so far  the main way of using them has been to memorise and type in their URLs!</p><p>The new navigation menu (which only displays if it has something in  it) provides a global location to add new links. I&#8217;ve already released  several plugin updates that take advantage of this.</p><p>The new &#8220;table actions&#8221; menu imitates Datasette&#8217;s existing column  header menu icon&#8212;it&#8217;s a cog. Clicking it opens a menu of actions  relating to the current table.</p><p>Want to see a demo?</p><blockquote><p>The demo at <a href="https://latest.datasette.io/">latest.datasette.io</a> now includes some example plugins. To see the new table actions menu first <a href="https://latest.datasette.io/login-as-root">sign into that demo as root</a> and then visit the <a href="https://latest.datasette.io/fixtures/facetable">facetable</a> table to see the new cog icon menu at the top of the page.</p></blockquote><p>Here&#8217;s an animated GIF demo showing the new menus in action.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!5X7B!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2Fca845953-1b27-4b33-8499-76da02489c05_737x445.gif" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!5X7B!,w_424,c_limit,f_webp,q_auto:good,fl_lossy/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2Fca845953-1b27-4b33-8499-76da02489c05_737x445.gif 424w, https://substackcdn.com/image/fetch/$s_!5X7B!,w_848,c_limit,f_webp,q_auto:good,fl_lossy/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2Fca845953-1b27-4b33-8499-76da02489c05_737x445.gif 848w, https://substackcdn.com/image/fetch/$s_!5X7B!,w_1272,c_limit,f_webp,q_auto:good,fl_lossy/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2Fca845953-1b27-4b33-8499-76da02489c05_737x445.gif 1272w, https://substackcdn.com/image/fetch/$s_!5X7B!,w_1456,c_limit,f_webp,q_auto:good,fl_lossy/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2Fca845953-1b27-4b33-8499-76da02489c05_737x445.gif 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!5X7B!,w_1456,c_limit,f_auto,q_auto:good,fl_lossy/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2Fca845953-1b27-4b33-8499-76da02489c05_737x445.gif" width="737" height="445" data-attrs="{&quot;src&quot;:&quot;https://bucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com/public/images/ca845953-1b27-4b33-8499-76da02489c05_737x445.gif&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:445,&quot;width&quot;:737,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;Using the nav menu to upload CSVs, then the table actions menu to edit the table schema and rename latitude and longitude columns so that the Central Park Squirrel Census is displayed on a map!&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="Using the nav menu to upload CSVs, then the table actions menu to edit the table schema and rename latitude and longitude columns so that the Central Park Squirrel Census is displayed on a map!" title="Using the nav menu to upload CSVs, then the table actions menu to edit the table schema and rename latitude and longitude columns so that the Central Park Squirrel Census is displayed on a map!" srcset="https://substackcdn.com/image/fetch/$s_!5X7B!,w_424,c_limit,f_auto,q_auto:good,fl_lossy/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2Fca845953-1b27-4b33-8499-76da02489c05_737x445.gif 424w, https://substackcdn.com/image/fetch/$s_!5X7B!,w_848,c_limit,f_auto,q_auto:good,fl_lossy/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2Fca845953-1b27-4b33-8499-76da02489c05_737x445.gif 848w, https://substackcdn.com/image/fetch/$s_!5X7B!,w_1272,c_limit,f_auto,q_auto:good,fl_lossy/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2Fca845953-1b27-4b33-8499-76da02489c05_737x445.gif 1272w, https://substackcdn.com/image/fetch/$s_!5X7B!,w_1456,c_limit,f_auto,q_auto:good,fl_lossy/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2Fca845953-1b27-4b33-8499-76da02489c05_737x445.gif 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><h4>Binary data</h4><blockquote><p>SQLite tables can contain binary data in <code>BLOB</code> columns. Datasette now provides links for users to download this data directly from Datasette, and uses those links to make binary data available from CSV exports. See <a href="https://docs.datasette.io/en/stable/binary_data.html#binary">Binary data</a> for more details. (<a href="https://github.com/simonw/datasette/issues/1036">#1036</a> and <a href="https://github.com/simonw/datasette/issues/1034">#1034</a>).</p></blockquote><p>I spent a ton of time on this over the past few weeks. The initial  impetus was a realization that Datasette CSV exports included ugly  Python <code>b'\x15\x1c\x02\xc7\xad\x05\xfe'</code> strings, which felt like the worst possible way to display binary in a CSV file, out of universally bad options.</p><p>Datasette&#8217;s main interface punted on binary entirely&#8212;it would show a <code>&lt;Binary data: 7 bytes&gt;</code> label which didn&#8217;t help much either.</p><p>The only way to get at binary data stored in a Datasette instance was  to request the JSON version and then manually decode the Base-64 value  within it!</p><p>This is now fixed: binary columns can be downloaded directly to your computer, using a new <code>.blob</code> output renderer. The approach is described on <a href="https://docs.datasette.io/en/stable/binary_data.html#binary">this new page in the documentation</a>.</p><p>Security was a major consideration when building this feature.  Allowing the download of arbitrary byte payloads from a web server is  dangerous business: it can easily result in XSS holes where HTML with  dangerous <code>&lt;script&gt;</code> content can end up hosted on the primary domain.</p><p>After some research, I decided to serve up binary content for download using the following headings:</p><pre><code><code>content-type: application/binary x-content-type-options: nosniff content-disposition: attachment; filename="data-f30889.blob" </code></code></pre><p><code>application/binary</code> is a safer Content-Type option than the more common <code>application/octet-stream</code>, according to Michal Zalewski&#8217;s renowned web application security book The Tangled Web (<a href="https://neexee.github.io/posts-en/the-tangled-web/">quoted here</a>)</p><p><code>x-content-type-options: nosniff</code> disables the XSS-tastic  content sniffing feature in older versions of Internet Explorer, where  IE would helpfully guess that you intended to serve HTML based on the  first few bytes of the response.</p><p>The <code>content-disposition: attachment</code> header causes the browser to show a &#8220;download this file&#8221; dialog, using the suggested filename.</p><p>If you know of a reason that this isn&#8217;t secure enough, please <a href="https://twitter.com/simonw">let me know</a>!</p><h4>URL building</h4><blockquote><p>The new <a href="https://docs.datasette.io/en/stable/internals.html#internals-datasette-urls">datasette.urls</a> family of methods can be used to generate URLs to key pages within the Datasette interface, both within custom templates and Datasette plugins. See <a href="https://docs.datasette.io/en/stable/writing_plugins.html#writing-plugins-building-urls">Building URLs within plugins</a> for more details. (<a href="https://github.com/simonw/datasette/issues/904">#904</a>)</p></blockquote><p>Datasette&#8217;s <a href="https://docs.datasette.io/en/stable/config.html#config-base-url">base_url</a> configuration setting was the forcing factor around this piece of work.</p><p>It allows you to configure Datasette to serve content starting at a path other than <code>/</code>&#8212;for example:</p><pre><code><code>datasette --config base_url:/path-to-datasette/ </code></code></pre><p>This will serve all Datasette pages at locations starting with <code>/path-to-datasette/</code>.</p><p>Why would you want to do this? It&#8217;s useful if you are proxying  traffic to Datasette from within the URL hierarchy of an existing  website.</p><p>The feature didn&#8217;t work properly, and enough people care about it  that I had a steady stream of bug reports. For 0.51 I gathered them all  into a single giant <a href="https://github.com/simonw/datasette/issues/1023">tracking issue</a> and worked through them all one by one.</p><p>It quickly became apparent that the key challenge was building URLs  within Datasette&#8212;not just within HTML template pages, but also for  things like HTTP redirects.</p><p>Datasette itself needed to generate URLs that took the <code>base_url</code> setting into account, but so do Datasette plugins. So I built <a href="https://docs.datasette.io/en/stable/internals.html#datasette-urls">a new datasette.urls</a> collection of helper methods and made them part of the documented internals API for plugins. The <a href="https://docs.datasette.io/en/stable/writing_plugins.html#building-urls-within-plugins">Building URLs within plugins</a> documentation shows how these should be used.</p><p>I also added documentation on <a href="https://docs.datasette.io/en/stable/deploying.html#deploying-proxy">Running Datasette behind a proxy</a> with example configs (tested on my laptop) for both nginx and Apache.</p><p>The <a href="https://docs.datasette.io/en/stable/internals.html#datasette-client">datasette.client</a>  mechanism from Datasette 0.50 allows plugins to make calls to  Datasette&#8217;s internal JSON API without the overhead of an HTTP request.  This is another place where plugins need to be able to construct valid  URLs to internal Datasette pages.</p><p>I added this example to the documentation showing how the two features can work together:</p><pre><code>table_json = (     await datasette.client.get(         datasette.urls.table("fixtures", "facetable", format="json")     ) ).json()</code></pre><p>One final weird detail on this: Datasette now has various methods that automatically add the <code>base_url</code> prefix to a URL. I got worried about what would happen if these were applied more than once (as above, where <code>datasette.urls.table()</code> applies the prefix so does <code>datasette.client.get()</code>).</p><p>I fixed this using the same trick that Django and Jinja use to avoid  appliying auto-escaping twice to content that will be displayed in HTML:  the <code>datasette.urls</code> methods actually return a <code>PrefixedUrlString</code> object which is a subclass of <code>str</code> that knows that the prefix has been applied! Code for that <a href="https://github.com/simonw/datasette/blob/main/datasette/utils/__init__.py#L1015">lives here</a>.</p><h4>Smaller changes</h4><p>A few highlights from the &#8220;smaller changes&#8221; in Datasette 0.51:</p><blockquote><ul><li><p>Wide tables shown within Datasette now scroll horizontally (<a href="https://github.com/simonw/datasette/issues/998">#998</a>). This is achieved using a new <code>&lt;div class="table-wrapper"&gt;</code> element which may impact the implementation of some plugins (for example <a href="https://github.com/simonw/datasette-cluster-map/commit/fcb4abbe7df9071c5ab57defd39147de7145b34e">this change to datasette-cluster-map</a>).</p></li></ul></blockquote><p>I think this is a big improvement: if your database table is too  wide, it now scrolls horizontally on the page (rather than blowing the  entire page out to a wider width). You can see that in action on the <a href="https://global-power-plants.datasettes.com/global-power-plants/global-power-plants">global-power-plants demo</a>.</p><blockquote><ul><li><p>New <a href="https://docs.datasette.io/en/stable/authentication.html#permissions-debug-menu">debug-menu</a> permission. (<a href="https://github.com/simonw/datasette/issues/1068">#1068</a>)</p></li></ul></blockquote><p>If you are signed in as <code>root</code> the new navigation menu  links to a whole plethora of previously-undiscoverable Datasette  debugging tools. This new permission controls the display of those  items.</p><blockquote><ul><li><p><code>Link:</code> HTTP header pagination. (<a href="https://github.com/simonw/datasette/issues/1014">#1014</a>)</p></li></ul></blockquote><p>Inspired by GitHub and WordPress, which both use the HTTP <code>Link</code> header in this way. It&#8217;s an optional extra though: Datasette will always offer in-JSON pagination information.</p><blockquote><ul><li><p>Edit SQL button on canned queries, (<a href="https://github.com/simonw/datasette/issues/1019">#1019</a>)</p></li></ul></blockquote><p>Suggested by Jacob Fenton <a href="https://github.com/simonw/datasette/issues/1019">in this issue</a>.  The implementation had quite a few edge cases since there are certain  categories of canned query that can&#8217;t be executed as custom SQL by the  user. See the <a href="https://github.com/simonw/datasette/issues/1019#issuecomment-708128286">issue comments</a> for details and a demo.</p><blockquote><ul><li><p><code>--load-extension=spatialite</code> shortcut. (<a href="https://github.com/simonw/datasette/issues/1028">#1028</a>)</p></li></ul></blockquote><p>Inspired by <a href="https://github.com/simonw/sqlite-utils/issues/136">a similar feature</a> in <code>sqlite-utils</code>.</p><blockquote><ul><li><p><code>datasette -o</code> option now opens the most relevant page. (<a href="https://github.com/simonw/datasette/issues/976">#976</a>)</p></li></ul></blockquote><p>This is a fun little feature. If your Datasette only loads a single  database, and that database only has a single table (common if you&#8217;ve  just run a single CSV import) then running this will open your browser  directly to that table page:</p><pre><code><code>datasette data.db -o </code></code></pre><blockquote><ul><li><p><code>datasette --cors</code> option now enables access to <code>/database.db</code> downloads. (<a href="https://github.com/simonw/datasette/issues/1057">#1057</a>)</p></li></ul></blockquote><p>This was inspired by Mike Bostock&#8217;s <a href="https://observablehq.com/@mbostock/sqlite">Observable Notebook</a> that uses the Emscripten-compiled <a href="https://github.com/sql-js/sql.js">JavaScript version</a> of SQLite to run queries against SQLite database files.</p><p>It turned out you couldn&#8217;t use that notebook against SQLite files  hosted in Datasette because they weren&#8217;t covered by Datasette&#8217;s CORS  option. Now they are!</p><blockquote><ul><li><p>New documentation on <a href="https://docs.datasette.io/en/stable/writing_plugins.html#writing-plugins-designing-urls">Designing URLs for your plugin</a>. (<a href="https://github.com/simonw/datasette/issues/1053">#1053</a>)</p></li></ul></blockquote><p>Recommendations for plugin authors, inspired by a question <a href="https://twitter.com/kanedr/status/1320653434895347713">from David Kane</a> on Twitter. David has been building <a href="https://github.com/drkane/datasette-reconcile">datasette-reconcile</a>, a Datasette plugin that offers a reconciliation API endpoint that can be used with <a href="https://openrefine.org/">OpenRefine</a>. What a brilliant idea!</p><h2>Plugin of the week: datasette-copyable</h2><p>One of my goals with Datasette is to make getting data out of it as easy as possible. I want Datasette to be the obvious place to store your data because you&#8217;ll be able to transform it (either manually or automatically) into any other format you could possible want, often by taking advantage of plugins.</p><p><a href="https://github.com/simonw/datasette-copyable">datasette-copyable</a> is an acknowledgement that often the easiest way to export data is using copy and paste! It adds the ability to export Datasette tables in 21 different formats - from TSV to Jira table markup to LaTeX, Mediawiki markup or HTML.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!6vkD!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2F307f42bc-efa5-484a-b198-f3ea22221a11_2028x1248.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!6vkD!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2F307f42bc-efa5-484a-b198-f3ea22221a11_2028x1248.png 424w, https://substackcdn.com/image/fetch/$s_!6vkD!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2F307f42bc-efa5-484a-b198-f3ea22221a11_2028x1248.png 848w, https://substackcdn.com/image/fetch/$s_!6vkD!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2F307f42bc-efa5-484a-b198-f3ea22221a11_2028x1248.png 1272w, https://substackcdn.com/image/fetch/$s_!6vkD!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2F307f42bc-efa5-484a-b198-f3ea22221a11_2028x1248.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!6vkD!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2F307f42bc-efa5-484a-b198-f3ea22221a11_2028x1248.png" width="1456" height="896" data-attrs="{&quot;src&quot;:&quot;https://bucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com/public/images/307f42bc-efa5-484a-b198-f3ea22221a11_2028x1248.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:896,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:336107,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!6vkD!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2F307f42bc-efa5-484a-b198-f3ea22221a11_2028x1248.png 424w, https://substackcdn.com/image/fetch/$s_!6vkD!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2F307f42bc-efa5-484a-b198-f3ea22221a11_2028x1248.png 848w, https://substackcdn.com/image/fetch/$s_!6vkD!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2F307f42bc-efa5-484a-b198-f3ea22221a11_2028x1248.png 1272w, https://substackcdn.com/image/fetch/$s_!6vkD!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2F307f42bc-efa5-484a-b198-f3ea22221a11_2028x1248.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>The most useful of these is the first one: TSV. The great thing about tab-separated data is that you can paste it directly into Microsoft Excel, Apple Numbers or Google Sheets and it will be automatically arranged into cells that correspond to the original table. This makes copy-as-TSV by far the easiest way to get small amounts of data out of Datasette and into a spreadsheet.</p><p>The other flavours of export are provided by the excellent <a href="https://pypi.org/project/tabulate/">Tabulate</a> Python library, which is also used for the different command-line export options <a href="https://sqlite-utils.readthedocs.io/en/stable/cli.html#running-queries-and-outputting-a-table">provided by sqlite-utils</a>.</p><p>You can try out <strong>datasette-copyable</strong> on <a href="https://fivethirtyeight.datasettes.com/fivethirtyeight/airline-safety%2Fairline-safety.copyable">fivethirtyeight.datasettes.com</a> - this linked example provides exports of airline safety data.</p><p>Since copy-and-paste is such a useful way of moving data around, I&#8217;m starting to percolate the idea of supporting copy-and-paste of data <em>into</em> Datasette as well.</p>]]></content:encoded></item><item><title><![CDATA[Dogsheep: Personal analytics with Datasette]]></title><description><![CDATA[Datasette Weekly volume 2]]></description><link>https://datasette.substack.com/p/dogsheep-personal-analytics-with</link><guid isPermaLink="false">https://datasette.substack.com/p/dogsheep-personal-analytics-with</guid><dc:creator><![CDATA[Simon Willison]]></dc:creator><pubDate>Mon, 19 Oct 2020 06:08:44 GMT</pubDate><enclosure url="https://cdn.substack.com/image/fetch/h_600,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2Fa97062c6-f62f-4030-bca0-b19bef05b04c_2216x1970.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>This week&#8217;s newsletter will take a deep dive into <strong><a href="https://dogsheep.github.io/">Dogsheep</a></strong>, my project to build tools for personal analytics on top of Datasette and SQLite.</p><h2>Dogsheep</h2><p>The name requires some explanation. In February 2019 Stephen Wolfram published <a href="https://writings.stephenwolfram.com/2019/02/seeking-the-productive-life-some-details-of-my-personal-infrastructure/">Seeking the Productive Life: Some Details of My Personal Infrastructure</a> - an astonishingly detailed essay about the extreme lengths he has gone to over the years to optimize his personal productivity. Stephen runs a 1,000+ person company as a remote CEO, so he&#8217;s been figuring out productive remote working since long before Covid forced the rest of us to all learn to work from home.</p><p>There&#8217;s a <strong>lot</strong> of stuff in there, but one thing that stood out to me in particular was Stephen&#8217;s description of his &#8220;metasearcher&#8221;:</p><blockquote><p>A critical part of my personal infrastructure is something that in effect dramatically extends my personal memory: my &#8220;metasearcher&#8221;. At the top of my personal homepage is a search box. Type in something like &#8220;rhinoceros elephant&#8221; and I&#8217;ll immediately find every email I&#8217;ve sent or received in the past 30 years in which that&#8217;s appeared, as well as every file on my machine, and every paper document in my archives.</p></blockquote><p>That sounded pretty great to me, so I asked myself if I could bring all of my data from different sources together into a single personal search engine. And since I wanted to build something that was inspired by <strong>Wolfram</strong> but not nearly as impressive, I decided to call it <strong>Dogsheep</strong>.</p><p>Stephen has a search engine called <strong>Wolfram Alpha</strong>, so obviously my search engine would be called <strong>Dogsheep Beta</strong>. And I admit that this pun amused me so much I felt obliged to build the software!</p><p><strong><a href="https://dogsheep.github.io/">Dogsheep</a></strong> is a collection of open source tools designed to convert personal data from different sources into SQLite databases, so that I can explore them with Datasette. The tools I&#8217;ve written so far are:</p><ul><li><p><strong><a href="https://github.com/dogsheep/twitter-to-sqlite">twitter-to-sqlite</a></strong> - your tweets, followers, people you follow,  favourited tweets.</p></li><li><p><strong><a href="https://github.com/dogsheep/healthkit-to-sqlite">healthkit-to-sqlite</a></strong> - converts Apple HealthKit into tables about heart rate, workouts, step counts, running routes.</p></li><li><p><strong><a href="https://github.com/dogsheep/swarm-to-sqlite">swarm-to-sqlite</a></strong> - checkin history from Foursquare Swarm.</p></li><li><p><strong><a href="https://github.com/dogsheep/inaturalist-to-sqlite">inaturalist-to-sqlite</a></strong> - wildlife and plants you have spotted on <a href="https://www.inaturalist.org/">iNaturalist</a></p></li><li><p><strong><a href="https://github.com/dogsheep/google-takeout-to-sqlite">google-takeout-to-sqlite</a></strong>  imports data from Google Takeout, which can include your Google Maps  location history, your search history on Google and more.</p></li><li><p><strong><a href="https://github.com/dogsheep/genome-to-sqlite">genome-to-sqlite</a></strong> imports your genome from <a href="https://www.23andme.com/">23andMe</a> into a SQLite database.</p></li><li><p><strong><a href="https://github.com/dogsheep/github-to-sqlite">github-to-sqlite</a></strong> imports repositories you have created or starred on GitHub, plus issues, issue comments, releases, commits, dependent repos. <a href="https://github-to-sqlite.dogsheep.net/">Live demo here</a>.</p></li><li><p><strong><a href="https://github.com/dogsheep/pocket-to-sqlite">pocket-to-sqlite</a></strong> imports articles you have saved using <a href="https://getpocket.com/">Pocket</a></p></li><li><p><strong><a href="https://github.com/dogsheep/hacker-news-to-sqlite">hacker-news-to-sqlite</a></strong> imports stories and comments posted to <a href="https://news.ycombinator.com/">Hacker News</a></p></li><li><p><strong><a href="https://github.com/dogsheep/dogsheep-photos">dogsheep-photos</a></strong> imports metadata about photos from Apple Photos, including machine learning labels. See <a href="https://simonwillison.net/2020/May/21/dogsheep-photos/">Using SQL to find my best photo of a pelican according to Apple Photos</a>.</p></li><li><p><strong><a href="https://github.com/dogsheep/evernote-to-sqlite">evernote-to-sqlite</a> (</strong>new this week) converts exported notes from Evernote into a SQLite database. See <a href="https://simonwillison.net/2020/Oct/16/building-evernote-sqlite-exporter/">Building an Evernote to SQLite exporter</a> for details of how I built that one.</p></li></ul><p>These tools have inspired some other developers to build their own. Here are the other tools in the Dogsheep family:</p><ul><li><p><strong><a href="https://github.com/bcongdon/beeminder-to-sqlite">beeminder-to-sqlite</a></strong> by Ben Congdon saves your goals and data points from <a href="https://www.beeminder.com/">Beeminder</a>.</p></li><li><p><strong><a href="https://github.com/rixx/goodreads-to-sqlite">goodreads-to-sqlite</a></strong> by Tobias Kunze imports your reading history from <a href="https://www.goodreads.com/">Goodreads</a>.</p></li><li><p><strong><a href="https://github.com/jacobian/pinboard-to-sqlite">pinboard-to-sqlite</a></strong> by Jacob Kaplan-Moss saves your bookmarks from <a href="https://pinboard.in/">Pinboard</a>.</p></li><li><p><strong><a href="https://github.com/bcongdon/todoist-to-sqlite">todoist-to-sqlite</a></strong> by Ben Congdon saves your tasks and projects from <a href="https://todoist.com/">Todoist</a>.</p></li><li><p><strong><a href="https://github.com/mrw34/parkrun-to-sqlite">parkrun-to-sqlite</a></strong> by Mark Woodbridge imports your <a href="https://www.parkrun.com">parkruns</a>.</p></li></ul><p>If you want to see a demo of my own personal Dogsheep instance in action, take a look at the talk I gave about the project at PyCon AU 2020:</p><div id="youtube2-CPQCD3Qxxik" class="youtube-wrap" data-attrs="{&quot;videoId&quot;:&quot;CPQCD3Qxxik&quot;,&quot;startTime&quot;:null,&quot;endTime&quot;:null}" data-component-name="Youtube2ToDOM"><div class="youtube-inner"><iframe src="https://www.youtube-nocookie.com/embed/CPQCD3Qxxik?rel=0&amp;autoplay=0&amp;showinfo=0&amp;enablejsapi=0" frameborder="0" loading="lazy" gesture="media" allow="autoplay; fullscreen" allowautoplay="true" allowfullscreen="true" width="728" height="409"></iframe></div></div><p>I used <a href="https://docs.google.com/document/d/1q0sH954Iv83sctmB_sfHJg2zZMYZGhY_n2s38yOCpOU/edit">this Google Doc</a> as a handout for the session, with links, notes and the Q&amp;A.</p><h2>Dogsheep Beta</h2><p>I finally got <a href="https://github.com/dogsheep/dogsheep-beta">dogsheep-beta</a> working a few weeks ago. It&#8217;s the search engine that ties everything else together, and it works by building a single SQLite table with a search index built against copies of data pulled from all of the other sources.</p><p>It uses YAML configuration to specify queries that should be run against each data source to build the index - extracting out titles, timestamps and searchable text for each record.</p><p>The YAML can also define SQL to be used to load extra details about search results in order to feed variables to a template, and those templates can then be embedded in the YAML file as well. This means you can use Dogsheep Beta to quickly define custom multi-source search engines for any kind of content that you&#8217;ve previously pulled into a SQLite database.</p><p>It may even be useful for things other than personal search engines! I should really get a live demo up of it running somewhere that&#8217;s not full of my own personal data.</p><p>In the meantime, here&#8217;s a screenshot of my personal Dogsheep Beta instance showing a search for #dogfest that returns both tweets and Swarm checkins:</p><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!7DUW!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2Fa97062c6-f62f-4030-bca0-b19bef05b04c_2216x1970.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!7DUW!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2Fa97062c6-f62f-4030-bca0-b19bef05b04c_2216x1970.png 424w, https://substackcdn.com/image/fetch/$s_!7DUW!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2Fa97062c6-f62f-4030-bca0-b19bef05b04c_2216x1970.png 848w, https://substackcdn.com/image/fetch/$s_!7DUW!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2Fa97062c6-f62f-4030-bca0-b19bef05b04c_2216x1970.png 1272w, https://substackcdn.com/image/fetch/$s_!7DUW!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2Fa97062c6-f62f-4030-bca0-b19bef05b04c_2216x1970.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!7DUW!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2Fa97062c6-f62f-4030-bca0-b19bef05b04c_2216x1970.png" width="1456" height="1294" data-attrs="{&quot;src&quot;:&quot;https://bucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com/public/images/a97062c6-f62f-4030-bca0-b19bef05b04c_2216x1970.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1294,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:2192604,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!7DUW!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2Fa97062c6-f62f-4030-bca0-b19bef05b04c_2216x1970.png 424w, https://substackcdn.com/image/fetch/$s_!7DUW!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2Fa97062c6-f62f-4030-bca0-b19bef05b04c_2216x1970.png 848w, https://substackcdn.com/image/fetch/$s_!7DUW!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2Fa97062c6-f62f-4030-bca0-b19bef05b04c_2216x1970.png 1272w, https://substackcdn.com/image/fetch/$s_!7DUW!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2Fa97062c6-f62f-4030-bca0-b19bef05b04c_2216x1970.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><p>I released <a href="https://github.com/dogsheep/dogsheep-beta/releases/tag/0.9">dogsheep-beta 0.9</a> at the weekend, upgraded to use the new <a href="https://docs.datasette.io/en/stable/internals.html#internals-datasette-client">datasette.client</a> internal API request mechanism added in Datasette 0.50.</p><h2>Datasette 1.0 API changes</h2><p>I continue to work towards a Datasette 1.0 release. Datasette is pretty stable at the moment, but for 1.0 I want to be able to make a promise that plugins built against Datasette and external code written against Datasette&#8217;s JSON API will continue to work without changes until a 2.0 release that breaks backwards-compatibility: and with any luck I hope to stay in the 1.x series for as long as possible.</p><p>Datasette&#8217;s default JSON API at the moment isn&#8217;t quite right. When I work with it myself I almost always find myself opting for the ?_shape=array option - compare the following:</p><p>Default: <a href="https://covid-19.datasettes.com/covid/ny_times_us_counties.json">covid-19.datasettes.com/covid/ny_times_us_counties.json</a></p><p>shape=array: <a href="https://covid-19.datasettes.com/covid/ny_times_us_counties.json?_shape=array">covid-19.datasettes.com/covid/ny_times_us_counties.json?_shape=array</a></p><p>I&#8217;m reconsidering the default design at the moment in <a href="https://github.com/simonw/datasette/issues/782">#782: Redesign default JSON format in preparation for Datasette 1.0</a>. This is one of the most significant remaining tasks before a 1.0 release, so I want to be sure to get it right!</p><p>I&#8217;m experimenting with the new default shape in a plugin called <a href="https://github.com/simonw/datasette-json-preview">datasette-json-preview</a>, which adds a new .json-preview extension showing what I&#8217;m thinking about. You can try that out at <a href="https://latest-with-plugins.datasette.io/fixtures/facetable.json-preview">https://latest-with-plugins.datasette.io/fixtures/facetable.json-preview</a> - that&#8217;s a demo Datasette instance I run which installs <a href="https://github.com/simonw/latest-datasette-with-all-plugins">as many plugins as possible</a> in order to see what happens when they all run inside the same instance.</p><p>I am very keen to get feedback and suggestions on the JSON redesign. Please post any comments you might have <a href="https://github.com/simonw/datasette/issues/782">on the issue</a>.</p><h2>Plugin of the week: datasette-cluster-map</h2><p><a href="https://github.com/simonw/datasette-cluster-map">datasette-cluster-map</a> is the first Datasette plugin I released, and remains one of my favourites.</p><p>Once installed, it quietly checks each table to see if it has latitude and longitude columns. If it does, the plugin triggers, loads up a <a href="https://leafletjs.com/">Leaflet</a> map and attempts to render all of the rows with a latitude and longitude on a map.</p><p>It&#8217;s using <a href="https://github.com/Leaflet/Leaflet.markercluster">Leaflet.markercluster</a> under the hood, which in my experienc will quite happily display hundreds of thousands of clustered markers on a single page.</p><p>Here&#8217;s my favorite demo: <a href="https://global-power-plants.datasettes.com/global-power-plants/global-power-plants">a table of 33,000 power plants</a> around the world using data maintained by the <a href="https://www.wri.org/publication/global-power-plant-database">World Resources Institute</a>. You can show them all on one map, or you can use Datasette facets to filter down to e.g. the <a href="https://global-power-plants.datasettes.com/global-power-plants/global-power-plants?primary_fuel=Hydro&amp;country_long=France">429 hydro plants in France</a>:</p><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!otiS!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2F14ab6fc7-87b4-4995-bf77-356d0cf975e6_2786x1424.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!otiS!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2F14ab6fc7-87b4-4995-bf77-356d0cf975e6_2786x1424.png 424w, https://substackcdn.com/image/fetch/$s_!otiS!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2F14ab6fc7-87b4-4995-bf77-356d0cf975e6_2786x1424.png 848w, https://substackcdn.com/image/fetch/$s_!otiS!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2F14ab6fc7-87b4-4995-bf77-356d0cf975e6_2786x1424.png 1272w, https://substackcdn.com/image/fetch/$s_!otiS!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2F14ab6fc7-87b4-4995-bf77-356d0cf975e6_2786x1424.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!otiS!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2F14ab6fc7-87b4-4995-bf77-356d0cf975e6_2786x1424.png" width="1456" height="744" data-attrs="{&quot;src&quot;:&quot;https://bucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com/public/images/14ab6fc7-87b4-4995-bf77-356d0cf975e6_2786x1424.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:744,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:3865849,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!otiS!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2F14ab6fc7-87b4-4995-bf77-356d0cf975e6_2786x1424.png 424w, https://substackcdn.com/image/fetch/$s_!otiS!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2F14ab6fc7-87b4-4995-bf77-356d0cf975e6_2786x1424.png 848w, https://substackcdn.com/image/fetch/$s_!otiS!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2F14ab6fc7-87b4-4995-bf77-356d0cf975e6_2786x1424.png 1272w, https://substackcdn.com/image/fetch/$s_!otiS!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2F14ab6fc7-87b4-4995-bf77-356d0cf975e6_2786x1424.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><p>I find myself using this plugin constantly - any time I come across data with latitude and longitude columns I&#8217;ll render it on a map, and often I&#8217;ll spot interesting anomolies - like the fact that the island of Corsica in the Mediterranean remains a region of France and has 7 Hydro power plants, as shown in the image above.</p><p>This also illustrates a pattern that I&#8217;m keen on exploring more: Datasette plugins which detect something interesting about the data that is being displayed and automatically offer additional ways to explore and visualize it. <strong>datasette-cluster-map</strong> is still the best example of this, but other examples include <a href="https://github.com/simonw/datasette-render-binary">datasette-render-binary</a> (tries to detect the format of binary data) and <a href="https://github.com/simonw/datasette-leaflet-geojson">datasette-leaflet-geojson</a> (spots GeoJSON values and renders them as a map).</p><h2>Join the conversation</h2><p>More newsletter next week, but in the meantime I&#8217;d love to hear from you on Twitter (I&#8217;m <a href="https://twitter.com/simonw">@simonw</a>) or in the <a href="https://github.com/simonw/datasette/discussions">Datasette GitHub Discussions</a>. Thanks for reading! </p><p></p><p></p>]]></content:encoded></item><item><title><![CDATA[Datasette 0.50, git scraping, extracting columns]]></title><description><![CDATA[The first edition of Datasette Weekly]]></description><link>https://datasette.substack.com/p/datasette-050-git-scraping-extracting</link><guid isPermaLink="false">https://datasette.substack.com/p/datasette-050-git-scraping-extracting</guid><dc:creator><![CDATA[Simon Willison]]></dc:creator><pubDate>Sat, 10 Oct 2020 20:48:44 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!Nnj1!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2Fba2e2483-e5dc-47fb-8631-de08c037e3b3_1050x547.gif" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Hello subscribers! Welcome to the first edition of the Datasette Weekly newsletter.</p><p>Every week I&#8217;ll be sharing news and tips from the ecosystem of tools that is growing around Datasette and SQLite.</p><h2>Datasette 0.50</h2><p><a href="https://docs.datasette.io/en/stable/changelog.html#v0-50">Datasette 0.50</a> came out this week, closely followed by 0.50.1 and 0.50.2 to squash some tiny bugs that sneaked into the release.</p><p>The biggest new feature in this release is something I&#8217;m calling the <em>column actions menu</em>. A little cog icon in the header of each table column provides options for sorting or faceting by that column - and there&#8217;s space for new features to be added later, including potentially features defined by plugins.</p><p>You can try the menu out at <a href="https://latest.datasette.io/fixtures/facetable">https://latest.datasette.io/fixtures/facetable</a> - latest.datasette.io is a Datasette instance which is deployed on every commit to the main branch in the repository, so it&#8217;s always available to try out new features. </p><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!Nnj1!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2Fba2e2483-e5dc-47fb-8631-de08c037e3b3_1050x547.gif" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!Nnj1!,w_424,c_limit,f_webp,q_auto:good,fl_lossy/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2Fba2e2483-e5dc-47fb-8631-de08c037e3b3_1050x547.gif 424w, https://substackcdn.com/image/fetch/$s_!Nnj1!,w_848,c_limit,f_webp,q_auto:good,fl_lossy/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2Fba2e2483-e5dc-47fb-8631-de08c037e3b3_1050x547.gif 848w, https://substackcdn.com/image/fetch/$s_!Nnj1!,w_1272,c_limit,f_webp,q_auto:good,fl_lossy/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2Fba2e2483-e5dc-47fb-8631-de08c037e3b3_1050x547.gif 1272w, https://substackcdn.com/image/fetch/$s_!Nnj1!,w_1456,c_limit,f_webp,q_auto:good,fl_lossy/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2Fba2e2483-e5dc-47fb-8631-de08c037e3b3_1050x547.gif 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!Nnj1!,w_1456,c_limit,f_auto,q_auto:good,fl_lossy/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2Fba2e2483-e5dc-47fb-8631-de08c037e3b3_1050x547.gif" width="1050" height="547" data-attrs="{&quot;src&quot;:&quot;https://bucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com/public/images/ba2e2483-e5dc-47fb-8631-de08c037e3b3_1050x547.gif&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:547,&quot;width&quot;:1050,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;Animated demo of the columns action menu, showing it used to sort a column and select two other columns for faceting&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="Animated demo of the columns action menu, showing it used to sort a column and select two other columns for faceting" title="Animated demo of the columns action menu, showing it used to sort a column and select two other columns for faceting" srcset="https://substackcdn.com/image/fetch/$s_!Nnj1!,w_424,c_limit,f_auto,q_auto:good,fl_lossy/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2Fba2e2483-e5dc-47fb-8631-de08c037e3b3_1050x547.gif 424w, https://substackcdn.com/image/fetch/$s_!Nnj1!,w_848,c_limit,f_auto,q_auto:good,fl_lossy/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2Fba2e2483-e5dc-47fb-8631-de08c037e3b3_1050x547.gif 848w, https://substackcdn.com/image/fetch/$s_!Nnj1!,w_1272,c_limit,f_auto,q_auto:good,fl_lossy/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2Fba2e2483-e5dc-47fb-8631-de08c037e3b3_1050x547.gif 1272w, https://substackcdn.com/image/fetch/$s_!Nnj1!,w_1456,c_limit,f_auto,q_auto:good,fl_lossy/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2Fba2e2483-e5dc-47fb-8631-de08c037e3b3_1050x547.gif 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><p>The other big feature in 0.50 is aimed at plugin authors. <strong>datasette.client</strong> is a new internal API for making requests to Datasette&#8217;s JSON HTTP API from within plugins themselves. This means that anything exposed in the external API can now be used by plugins as well. datasette.client is powered by the excellent HTTPX async Python HTTP library, taking advantage of it&#8217;s ability to simulate requests against an ASGI application without making a real network call. The new internal API is <a href="https://docs.datasette.io/en/stable/internals.html#internals-datasette-client">documented here</a>.</p><h2>Deploying Datasette</h2><p>The <a href="https://docs.datasette.io/en/stable/publish.html">datasette publish command</a> has long been the recommended way of publishing a Datasette instance on the internet - just one command-line command can upload a database to Heroku, Cloud Run, Vercel or fly.io and configure it to be served by Datasette.</p><p>Sometimes this isn&#8217;t enough. The providers supported by datasette publish are all read-only: great for hosting static data but no use if you want to make changes to it without redeploying  (e.g. using the <a href="https://github.com/simonw/datasette-upload-csvs">datasette-upload-csvs</a> plugin). Datasette 0.50 includes a new documentation section on <a href="https://docs.datasette.io/en/stable/deploying.html">Deploying Datasette</a> with notes on deploying on Linux <a href="https://docs.datasette.io/en/stable/deploying.html#running-datasette-using-systemd">using systemd</a> - I&#8217;ve used this myself for a few projects but I&#8217;d love to hear from other people who have tried this out and have suggestions for how I can improve the recipe.</p><p>Last week DigitalOcean announced <a href="https://www.digitalocean.com/products/app-platform/">App Platform</a>, their new Heroku-style PaaS hosting platform. I figured out <a href="https://til.simonwillison.net/til/til/digitalocean_datasette-on-digitalocean-app-platform.md">how to run Datasette on it</a> and used that to inform a new section of the documentation on <a href="https://docs.datasette.io/en/stable/deploying.html#deploying-using-buildpacks">Deploying using buildpacks</a> - the mechanism originally invented by Heroku which has now been adopted by various other providers. My <a href="https://github.com/simonw/buildpack-datasette-demo">simonw/buildpack-datasette-demo</a> GitHub repo shows a simple example of how this style of deployment can be used.</p><h2>Git scraping</h2><p>Git scraping is a Datasette-adjacent technique I&#8217;ve been experimenting with over the past few years that involves scraping data sources into a Git repository, in order to track changes to those sources over time.</p><p>I finally wrote an explanation of it on my blog: <a href="https://simonwillison.net/2020/Oct/9/git-scraping/">Git scraping: track changes over time by scraping to a Git repository</a>. This lead to an interesting discussion <a href="https://news.ycombinator.com/item?id=24732943">on Hacker News</a> where a number of people shared their own experiences using this pattern.</p><p>Here&#8217;s a diff I captured illustrating the ongoing Zogg Fires in Northern California (scraped from <a href="https://www.fire.ca.gov/incidents">www.fire.ca.gov/incidents</a> into my <a href="https://github.com/simonw/ca-fires-history">simonw/ca-fires-history</a> repo):</p><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!UA2K!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2F6c3db50b-42a3-4751-b549-40d9328e03d6_1200x986.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!UA2K!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2F6c3db50b-42a3-4751-b549-40d9328e03d6_1200x986.png 424w, https://substackcdn.com/image/fetch/$s_!UA2K!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2F6c3db50b-42a3-4751-b549-40d9328e03d6_1200x986.png 848w, https://substackcdn.com/image/fetch/$s_!UA2K!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2F6c3db50b-42a3-4751-b549-40d9328e03d6_1200x986.png 1272w, https://substackcdn.com/image/fetch/$s_!UA2K!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2F6c3db50b-42a3-4751-b549-40d9328e03d6_1200x986.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!UA2K!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2F6c3db50b-42a3-4751-b549-40d9328e03d6_1200x986.png" width="1200" height="986" data-attrs="{&quot;src&quot;:&quot;https://bucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com/public/images/6c3db50b-42a3-4751-b549-40d9328e03d6_1200x986.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:986,&quot;width&quot;:1200,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;Screenshot of a diff against the Zogg Fires, showing personnel involved dropping from 968 to 798, engines dropping 82 to 59, water tenders dropping 31 to 27 and percent contained increasing from 90 to 92.&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="Screenshot of a diff against the Zogg Fires, showing personnel involved dropping from 968 to 798, engines dropping 82 to 59, water tenders dropping 31 to 27 and percent contained increasing from 90 to 92." title="Screenshot of a diff against the Zogg Fires, showing personnel involved dropping from 968 to 798, engines dropping 82 to 59, water tenders dropping 31 to 27 and percent contained increasing from 90 to 92." srcset="https://substackcdn.com/image/fetch/$s_!UA2K!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2F6c3db50b-42a3-4751-b549-40d9328e03d6_1200x986.png 424w, https://substackcdn.com/image/fetch/$s_!UA2K!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2F6c3db50b-42a3-4751-b549-40d9328e03d6_1200x986.png 848w, https://substackcdn.com/image/fetch/$s_!UA2K!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2F6c3db50b-42a3-4751-b549-40d9328e03d6_1200x986.png 1272w, https://substackcdn.com/image/fetch/$s_!UA2K!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2F6c3db50b-42a3-4751-b549-40d9328e03d6_1200x986.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><p>Having captured data in this way, the next challenge is to visualize and analyze that. In the past I&#8217;ve done this using SQLite and Datasette, by writing a script to walk through the repository history and create a SQLite database of the various changes. My best example of doing that is described in <a href="https://simonwillison.net/2019/Oct/10/pge-outages/">Tracking PG&amp;E outages by scraping to a git repo</a>  from October last year.</p><h2>Extracting columns with sqlite-utils extract</h2><p><a href="https://sqlite-utils.readthedocs.io/">sqlite-utils</a> is my combination Python library and CLI utility for creating and manipulating SQLite databases. It&#8217;s my attempt at answering the question &#8220;how do I get this into SQLite?&#8221; for as many different shapes of data as possible.</p><p><a href="https://sqlite-utils.readthedocs.io/en/stable/changelog.html#v2-20">sqlite-utils 2.20</a>, released last month, added a new tool for a common problem I face when working with data that started out as a CSV file: column extraction.</p><p>It&#8217;s common for data to have duplicated columns - this example has Department Code: POL and Department: Police for example. In an ideal, normalized world we would extract these columns out into a separate database table.</p><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!l1Bh!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2F89747d14-9586-411b-9cf6-e6b382e670f2_1482x566.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!l1Bh!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2F89747d14-9586-411b-9cf6-e6b382e670f2_1482x566.png 424w, https://substackcdn.com/image/fetch/$s_!l1Bh!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2F89747d14-9586-411b-9cf6-e6b382e670f2_1482x566.png 848w, https://substackcdn.com/image/fetch/$s_!l1Bh!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2F89747d14-9586-411b-9cf6-e6b382e670f2_1482x566.png 1272w, https://substackcdn.com/image/fetch/$s_!l1Bh!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2F89747d14-9586-411b-9cf6-e6b382e670f2_1482x566.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!l1Bh!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2F89747d14-9586-411b-9cf6-e6b382e670f2_1482x566.png" width="1456" height="556" data-attrs="{&quot;src&quot;:&quot;https://bucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com/public/images/89747d14-9586-411b-9cf6-e6b382e670f2_1482x566.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:556,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;https://static.simonwillison.net/static/2020/refactored-one-column.png&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="https://static.simonwillison.net/static/2020/refactored-one-column.png" title="https://static.simonwillison.net/static/2020/refactored-one-column.png" srcset="https://substackcdn.com/image/fetch/$s_!l1Bh!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2F89747d14-9586-411b-9cf6-e6b382e670f2_1482x566.png 424w, https://substackcdn.com/image/fetch/$s_!l1Bh!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2F89747d14-9586-411b-9cf6-e6b382e670f2_1482x566.png 848w, https://substackcdn.com/image/fetch/$s_!l1Bh!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2F89747d14-9586-411b-9cf6-e6b382e670f2_1482x566.png 1272w, https://substackcdn.com/image/fetch/$s_!l1Bh!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2F89747d14-9586-411b-9cf6-e6b382e670f2_1482x566.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><p>That&#8217;s what <strong>sqlite-utils extract</strong> lets you do. I wrote a tutorial showing how to use it to refactor the <a href="https://data.sfgov.org/City-Management-and-Ethics/Employee-Compensation/88g8-5mnd">Employee Compensation</a> CSV file for the City of San Francisco into something a bit more relational:</p><p><a href="https://simonwillison.net/2020/Sep/23/sqlite-utils-extract/">Refactoring databases with sqlite-utils extract</a> - 23rd September 2020</p><p>See also the project documentation <a href="https://sqlite-utils.readthedocs.io/en/stable/cli.html#cli-extract">on sqlite-utils extract</a>.</p><h2>Plugin of the week: datasette-graphql</h2><p>Datasette has <a href="https://github.com/topics/datasette-plugin">more than 50 plugins now</a>, so I&#8217;m planning to highlight a plugin in the newsletter every week. This week: <a href="https://github.com/simonw/datasette-graphql">datasette-graphql</a>.</p><p>I started working on this plugin partly to learn more about GraphQL and partly to demonstrate that it wasn&#8217;t a great fit for Datasette - I&#8217;ve long thought that SQL makes a more interesting API language than GraphQL, and Datasette supports SQL out of the box.</p><p>I&#8217;ve since changed my mind: GraphQL running on top of Datasette is actually a really pleasant way of interacting with data, thanks mainly to the brilliant GraphiQL API explorer tool which adds auto-complete and introspected documentation.</p><p>Here&#8217;s an <a href="https://datasette-graphql-demo.datasette.io/graphql?query=%7B%0A%20%20repos(search%3A%20%22datasette%22%2C%20filter%3A%20%5B%7Bstargazers_count%3A%20%7Bgt%3A%2010%7D%7D%5D)%20%7B%0A%20%20%20%20nodes%20%7B%0A%20%20%20%20%20%20full_name%0A%20%20%20%20%20%20stargazers_count%0A%20%20%20%20%20%20issues_list(first%3A%205%2C%20sort_desc%3A%20created_at)%20%7B%0A%20%20%20%20%20%20%20%20totalCount%0A%20%20%20%20%20%20%20%20nodes%20%7B%0A%20%20%20%20%20%20%20%20%20%20title%0A%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%7D%0A%20%20%20%20%7D%0A%20%20%7D%0A%7D%0A">example GraphQL query</a> running against a demo Datasette instance with data I pulled in from GitHub using my <a href="https://github.com/dogsheep/github-to-sqlite">github-to-sqlite</a> tool. The query searches for repos matching &#8220;datasette&#8221; with at least 10 stargazers, then for each repository returns the first 5 issue titles supported by most recently created.</p><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!mqVx!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2F1bae5e73-3eba-4f4e-a549-a6dc7ac12aef_2860x1504.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!mqVx!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2F1bae5e73-3eba-4f4e-a549-a6dc7ac12aef_2860x1504.png 424w, https://substackcdn.com/image/fetch/$s_!mqVx!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2F1bae5e73-3eba-4f4e-a549-a6dc7ac12aef_2860x1504.png 848w, https://substackcdn.com/image/fetch/$s_!mqVx!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2F1bae5e73-3eba-4f4e-a549-a6dc7ac12aef_2860x1504.png 1272w, https://substackcdn.com/image/fetch/$s_!mqVx!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2F1bae5e73-3eba-4f4e-a549-a6dc7ac12aef_2860x1504.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!mqVx!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2F1bae5e73-3eba-4f4e-a549-a6dc7ac12aef_2860x1504.png" width="1456" height="766" data-attrs="{&quot;src&quot;:&quot;https://bucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com/public/images/1bae5e73-3eba-4f4e-a549-a6dc7ac12aef_2860x1504.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:766,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:383378,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!mqVx!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2F1bae5e73-3eba-4f4e-a549-a6dc7ac12aef_2860x1504.png 424w, https://substackcdn.com/image/fetch/$s_!mqVx!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2F1bae5e73-3eba-4f4e-a549-a6dc7ac12aef_2860x1504.png 848w, https://substackcdn.com/image/fetch/$s_!mqVx!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2F1bae5e73-3eba-4f4e-a549-a6dc7ac12aef_2860x1504.png 1272w, https://substackcdn.com/image/fetch/$s_!mqVx!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2F1bae5e73-3eba-4f4e-a549-a6dc7ac12aef_2860x1504.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><p>An interesting challenge with GraphQL is that, because it supports nested queries, it can lead to an enormous number of underlying SQL queries - a classic case of the N+1 queries problem.</p><p>Because SQLite runs as an in-process database library without needing the overhead of network requests to a server it&#8217;s actually an extremely good fit for implementing GraphQL - as explained by the classic essay <a href="https://www.sqlite.org/np1queryprob.html">Many Small Queries Are Efficient In SQLite</a>.</p><p>I&#8217;ve started deploying <strong>datasette-graphql</strong> on all sorts of projects now - a few examples:</p><ul><li><p><a href="https://til.simonwillison.net/graphql?query=%7B%0A%20%20til(search%3A%20%22pytest%22%2C%20sort_desc%3A%20updated_utc)%20%7B%0A%20%20%20%20nodes%20%7B%0A%20%20%20%20%20%20path%0A%20%20%20%20%20%20topic%0A%20%20%20%20%20%20title%0A%20%20%20%20%20%20url%0A%20%20%20%20%20%20created%0A%20%20%20%20%20%20created_utc%0A%20%20%20%20%20%20updated%0A%20%20%20%20%20%20updated_utc%0A%20%20%20%20%20%20shot%0A%20%20%20%20%7D%0A%20%20%7D%0A%7D%0A%0A">til.simonwillison.net/graphql</a></p></li><li><p><a href="https://covid-19.datasettes.com/graphql?query=%7B%0A%20%20ny_times_us_counties(first%3A%20100%2C%20filter%3A%20%7Bstate%3A%20%7Beq%3A%20%22California%22%7D%2C%20county%3A%20%7Beq%3A%20%22San%20Francisco%22%7D%7D)%20%7B%0A%20%20%20%20nodes%20%7B%0A%20%20%20%20%20%20date%0A%20%20%20%20%20%20county%0A%20%20%20%20%20%20state%0A%20%20%20%20%20%20cases%0A%20%20%20%20%20%20deaths%0A%20%20%20%20%7D%0A%20%20%7D%0A%7D">covid-19.datasettes.com/graphql</a></p></li></ul><p>I also used it to help Natalie Downe build <a href="https://www.rockybeaches.com/us/pillar-point">Rocky Beaches</a>, a new website providing tips on when and where to go tidepooling around the SF Bay Area. That site takes advantage of the graphql() template function exposed by the plugin, which allows you to use GraphQL server-side to make data available to custom Datasette templates, as seen in the <a href="https://github.com/natbat/rockybeaches/blob/17306b4315ab7ed4315669b41cb2f95268823630/templates/row-data-places.html">row-data-places.html template</a>.</p><p>You can learn more about the plugin in the <a href="https://github.com/simonw/datasette-graphql/blob/main/README.md">datasette-graphql README</a>. Installation is as easy as running &#8220;datasette install datasette-graphql&#8221;.</p><h2>Feedback, suggestions and more</h2><p>This is my first time sending out this newsletter, so I&#8217;m really keen to hear your feedback. You can contact me at swillison@gmail.com, <a href="https://twitter.com/simonw">@simonw on Twitter</a> or by posting on <a href="https://github.com/simonw/datasette/discussions">Datasette&#8217;s GitHub Discussions</a> forum. I&#8217;d love to hear from you!</p><p></p>]]></content:encoded></item><item><title><![CDATA[Weekly updates on Datasette and the Datasette ecosystem of tools]]></title><description><![CDATA[Welcome to Datasette Weekly by me, Simon Willison (@simonw).]]></description><link>https://datasette.substack.com/p/coming-soon</link><guid isPermaLink="false">https://datasette.substack.com/p/coming-soon</guid><dc:creator><![CDATA[Simon Willison]]></dc:creator><pubDate>Fri, 28 Aug 2020 06:40:38 GMT</pubDate><enclosure url="https://bucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com/public/images/5a30d45c-fcba-407a-bebf-96f51a8944a4_48x48.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Welcome to Datasette Weekly by me, <a href="https://simonwillison.net/">Simon Willison</a> (<a href="https://twitter.com/simonw">@simonw</a>). I&#8217;ll be sending out weekly updates on <a href="https://datasette.io/">Datasette</a>, Datasette plugins and other related tips and tools for solving all kinds of interesting data problems.</p><p>Sign up now so you don&#8217;t miss the first issue.</p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://datasette.substack.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe now&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://datasette.substack.com/subscribe?"><span>Subscribe now</span></a></p><p>In the meantime, <a href="https://datasette.substack.com/p/coming-soon?utm_source=substack&utm_medium=email&utm_content=share&action=share">tell your friends</a>!</p>]]></content:encoded></item></channel></rss>