<?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"><channel><title><![CDATA[Mortimer XYZ Blog]]></title><description><![CDATA[A port of the casper blog built for gatsby]]></description><link>https://mortimer.xyz</link><generator>GatsbyJS</generator><lastBuildDate>Wed, 17 Jun 2026 01:11:29 GMT</lastBuildDate><item><title><![CDATA[Vibe coding a game in 2026 as a data engineer]]></title><description><![CDATA[The Ceiling of my vibes: Building a Game with LLMs when you are not a game developer If you read my other blog posts, or know me well enough…]]></description><link>https://mortimer.xyz/vibe-coding-a-game/</link><guid isPermaLink="false">https://mortimer.xyz/vibe-coding-a-game/</guid><pubDate>Thu, 11 Jun 2026 00:00:00 GMT</pubDate><content:encoded>&lt;h1 id=&quot;The-Ceiling-of-my-vibes-Building-a-Game-with-LLMs-when-you-are-not-a-game-developer&quot; style=&quot;position:relative;&quot;&gt;The Ceiling of my vibes: Building a Game with LLMs when you are not a game developer&lt;a href=&quot;#The-Ceiling-of-my-vibes-Building-a-Game-with-LLMs-when-you-are-not-a-game-developer&quot; aria-label=&quot;The Ceiling of my vibes Building a Game with LLMs when you are not a game developer permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h1&gt;
&lt;p&gt;If you read my other blog posts, or know me well enough you know I am not a game developer. In my day job I’m building data pipelines. Recently, I decided to build a kids’ game (well my daughter decided I would).&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;“You write computer programs don’t you Daddy?“.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;The experience certainly wasn’t pure magic so I thought I would share how far I pushed “vibe coding” and some things that plain didn’t work (at least for me).&lt;/p&gt;
&lt;p&gt;The result is live at &lt;a href=&quot;https://mortie23.github.io/blockvillie/&quot;&gt;https://mortie23.github.io/blockvillie/&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Maybe I wasn’t using the right model, maybe I wasn’t paying for enough tokens or using the best method to vibe. This isn’t a tutorial on how to prompt a game. It’s just me telling me story of what happened when I tried to vibe my way through a genre of programming that I’ve never touched before, in early 2026.&lt;/p&gt;
&lt;h2 id=&quot;My-initial-architecture&quot; style=&quot;position:relative;&quot;&gt;My initial architecture&lt;a href=&quot;#My-initial-architecture&quot; aria-label=&quot;My initial architecture permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;I know enough about web development and hosting web sites and applications so to ensure I at least had some idea of what the LMM generated I decided to have the AI build a web based game. If I didn’t know the tech stack at all I thought the complexity would have killed my momentum.&lt;/p&gt;
&lt;p&gt;I also wanted to be able to easily and cheaply host the final product and share it with friends. So I decided to get the AI to build the game as a static web site with all logic client side in JavaScript.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Zero Backend:&lt;/strong&gt; No databases, no authentication servers, no API Gateways. If I introduced a server, I’d have running costs, maintenance, and other overhead that would have been no value.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;No Real State Management:&lt;/strong&gt; Everything runs entirely in memory within the browser session. If you refresh, it resets. For a kids’ game that only my kids would play, this is perfect.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;The-AI-vibe-developer-tooling&quot; style=&quot;position:relative;&quot;&gt;The AI vibe developer tooling&lt;a href=&quot;#The-AI-vibe-developer-tooling&quot; aria-label=&quot;The AI vibe developer tooling permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;I used Google’s Antigravity to build the game. At the time, it included these models.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 580px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/886b624f19fc846e398044a28394bdcf/12f38/vibe-game-antigravity-models.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 112.99999999999999%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAXCAYAAAALHW+jAAAACXBIWXMAAA7DAAAOwwHHb6hkAAACBElEQVQ4y6WVSY7CQAxFcwwmiUlhTpjDKFggEBtA4v53ceu5+a2CBhqpF18uu1z2L9uVRLlcziqVitVqtZeo1+tP9dDOulgsWlQoFKzb7dpoNLJ+v+8SDAYDGw6HLtM09bXsvV7PddkBtmq1+h2w3W5bkiQ/IAESp06n48CGLhsSH4LKdhcQQ7PZ/AmqQ/00dR32SoI/viCOY2c9nU6/A+bzeT+cpok7zudzm2WZjcdjm0wmtljM3Xm5XPneNMssSXrWaret1WpZo9Gw2Wxmq9XKyuWyRaVSyQvKhhwA2ZFx3LjJ+G4P4C+ge0Cukt0YyfE/iKgLNSCwCq3i0whBtnD/GaLtdmuXy8WOx6Pt93s7nU52Pp9dXq9XX+92O1sul3Y4HAx/akZ9qe0j/MpiGM6aZhBgQ6csGpOXDAlEdqJzkOyLxcL19XrtiQiED6yYhLAxv2rIyIR1SW9zp3oRQLdgL+zsMzhD5gsWXAtm0mFLUKRsGpGXAemg3qU6Hr6MZ9d6F9THhtqoMWoENpI8O/iWIYcYbHU3vLqa8Emguyvrmhpg1vqM8eT07D4J6gxhkwUfBEnsagjl+IghY6PaqSkkSYPPFiVQg/4MyEFYCLALBxwJ07+uLHtE0R+HOXxK0j/+2sCGZyWGrHlym83G19SPZpGd5K+g/YjPtv5ar/Duj/iIL4QouEgblnhgAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;vibe game antigravity models&quot;
        title=&quot;vibe game antigravity models&quot;
        src=&quot;/static/886b624f19fc846e398044a28394bdcf/12f38/vibe-game-antigravity-models.png&quot;
        srcset=&quot;/static/886b624f19fc846e398044a28394bdcf/0eb09/vibe-game-antigravity-models.png 500w,
/static/886b624f19fc846e398044a28394bdcf/12f38/vibe-game-antigravity-models.png 580w&quot;
        sizes=&quot;(max-width: 580px) 100vw, 580px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;The quotas on usage of each of them were different. I was in no rush, so sometimes when I ran out of quota, I just paused the development for a few days until it refreshed.&lt;/p&gt;
&lt;p&gt;What I found (and also took advice from others) is that you can save money/token quota but being selective on what model you choose for different tasks.&lt;/p&gt;
&lt;p&gt;For example, for the initial scaffolding and set-up of the core game, including:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;web libraries/frameworks&lt;/li&gt;
&lt;li&gt;developer tooling&lt;/li&gt;
&lt;li&gt;game mechanics&lt;/li&gt;
&lt;li&gt;data structures&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I used the heavy, expensive models.&lt;/p&gt;
&lt;p&gt;Once that was all built and I had a development site working, I switched to the cheaper, faster models for new features or bug fixes.&lt;/p&gt;
&lt;p&gt;If a major feature or refactor was required, I switched back to the more expensive models.&lt;/p&gt;
&lt;h2 id=&quot;Getting-started&quot; style=&quot;position:relative;&quot;&gt;Getting started&lt;a href=&quot;#Getting-started&quot; aria-label=&quot;Getting started permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;A reality check on where a prompt like the following will get us in a single shot:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Generate a grid based game. Use Vite and REACT.&lt;/p&gt;
&lt;p&gt;The player can move a character on the grid.&lt;/p&gt;
&lt;p&gt;The theme is Lego, for UI elements such as characters and obstacles use lego based images.&lt;/p&gt;
&lt;p&gt;Controls are with keyboard arrows and movement is to adjacent grid tiles from where the character is. The character can pickup gems and they collect in a counter.&lt;/p&gt;
&lt;p&gt;There is a shop where the character can buy outfits with the gems.&lt;/p&gt;
&lt;p&gt;The character can also pickup outfits and chose to wear or put them in a closet.&lt;/p&gt;
&lt;p&gt;The grid can contain walkable tiles and non-walkable tiles. Tiles can be grass, paving, walls, sand.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Antigravity (and most vibe coding IDEs) will take from a prompt like this to an “Implementation plan”.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 1746px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/fc2e6066affc6d9a092af5448d2a7506/1805d/vibe-game-antigravity-implementation-plan.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 63.4%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAANCAIAAAAmMtkJAAAACXBIWXMAAAsSAAALEgHS3X78AAABYUlEQVQoz3WSaZKbMBCFucXUBLSgDdFasBaQcXyRnCjXTiEFF5N4vh9Ul6TWe+pHF9YScvEhI0KGvh+GoT/58Q5CiJAT0rce084YA/PMORspZYwRQlAFY0z+g3HuLUzx5/Dr92DvHQDYinPOWuu9dxUhRN/36CvN12Hw84Mz1sUYQwgxxlSJlZSS995UrLXmxJ46k9ZCiG5bt5zyuq7OuXYCAJq49755ab6uWGullJ21xlqzLAvGuK8De8s/WwihQzm6u4OglGSM0Tqz8QKllHMupRRCXLcwxkfzvj/2+2PbtlyJMbZvOimlPJ/Pfd/XdU0p5Xy8UVS6SSs9z8aYpkwpbYKtGMfxldN1sRWdkkIphU8IIdeE36bdFhFC3ZIKwNyecT2Nv+F1xdGslKwBuhah9/52u/nKsiyvzEIIAMA5b3/h32bQ074/Silbpc0j56wr8zxrradpAoBWCyFetv8Aoj50Ddx8oeUAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;vibe game antigravity implementation plan&quot;
        title=&quot;vibe game antigravity implementation plan&quot;
        src=&quot;/static/fc2e6066affc6d9a092af5448d2a7506/1805d/vibe-game-antigravity-implementation-plan.png&quot;
        srcset=&quot;/static/fc2e6066affc6d9a092af5448d2a7506/0eb09/vibe-game-antigravity-implementation-plan.png 500w,
/static/fc2e6066affc6d9a092af5448d2a7506/1263b/vibe-game-antigravity-implementation-plan.png 1000w,
/static/fc2e6066affc6d9a092af5448d2a7506/1805d/vibe-game-antigravity-implementation-plan.png 1746w&quot;
        sizes=&quot;(max-width: 1746px) 100vw, 1746px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;It gives the “vibe developer” the opportunity to add some more context. Let’s provide this extra context.&lt;/p&gt;
&lt;h3 id=&quot;User-Review-Required-IMPORTANT&quot; style=&quot;position:relative;&quot;&gt;User Review Required, IMPORTANT:&lt;a href=&quot;#User-Review-Required-IMPORTANT&quot; aria-label=&quot;User Review Required IMPORTANT permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Use CSS for the grid tiles (grass, paving, sand, walls) and for gems. Only generate images for characters and outfits.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;Open-Questions-NOTE&quot; style=&quot;position:relative;&quot;&gt;Open Questions, NOTE:&lt;a href=&quot;#Open-Questions-NOTE&quot; aria-label=&quot;Open Questions NOTE permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;Maps should be statically defined. The user should be able to select from many maps. Start with two maps, a park and a house. Maps should be a json array of values representing tile types.&lt;/li&gt;
&lt;li&gt;Initial specific outfits should be school uniform and pyjamas&lt;/li&gt;
&lt;li&gt;Initial grids should start with 40x40&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;So what did this get us?&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 1396px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/5903687d70e5a52c63e7a7edf538271f/bcfad/vibe-game-single-shot-game.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 106.4%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAVCAIAAADJt1n/AAAACXBIWXMAAAsSAAALEgHS3X78AAADUUlEQVQ4y21RXYscRRRtENxVMUoSJPsxC1FE8EVQUCM+BPwAza4z01VdVb2TRVdR0H1RCQoGdHp6ZtTdnd6Q2USz013VH1WdSHaqqhXUF33zf0lVz0x8EA707Vt16pxzr/Pow2fOvbX1/KXLZx47u7T0xPLykwaPnF16aPnxxouvdf96Y/+fC3h88Om5v2+u/XTtqfCj838crvwZrXQ/PO9srF589tLlF15+fWP9mZW1i6s11p9evbC+9twrL308fnXvzsaVr78g67evbVz/oLHnNW5+3rj1ZWMPNpzh0Y1wfz86OBiNoig6OhzZbxSNomh0ePBjv/t92D36IewPR91B1BsehcMo6EfdfhQOI+fnNM1P71fTX7I8z/IszdK8yLPC1FmR54ILwW8JccKzUmRc5AXPuCmygmcOo5TF8Xg8phPKY57HOU84T3gR50VcFEkhkuLOpJhMcpGYX9vPuSkKhzI2ieP4JD7OjsOyH/L+QAz7BoNwjqEYDMRgUBqE3KAvBn0+cLI0TbO0TMvv7nY3q2ZTgXbltSrY1MCgAq0KvFeBlnbfVlevaL+l3brf1K5zzFhEaUmLQIRAITT1seogue1JH0kfKr/9WwcpH0sfTnc82UGSIHuEpO8kjJ3QRNAiKHutCkKJPUUMJG5V5JMCJtffRfcRUAQZYKgwsKdQYSdNU8ZoysS3d3ttDSzZh4aP3Yrs3PO+Odzypsh2iKs7wJxiqAhQ2GFpmjF6g/KveAC0UUbakO0N7Gq8+bv9lUb2neluS3U8heBCmaZUJHkggrb2amVvToYKI0nsW7XtmkbqCw5jLGFJQYuu6LUUhNOZbSAxkBguMEVtedVV21Ciug/kXJlTHpS9tvY8SZDexjZVrVADSbw53W2rHTszvxawZEYFM2TX2CZI+Vt6F9rknrYwV32iMTIv1n0TzdqmCa9ta2MbSrJ5+j6wzhfJbU1mcWYLs5kpW9g2ZKR9pDF8wLQDm+/fdCT25HxgNblbzpTrPHPa/AmJPV2PHYNfMdQYKDTLbJRF0LKZ6zwL8lyQ1CsEmny2j8g97FaLaTPRK4M31a576qOZMvlf20BhknveKQYaOzljUcpuUx6WvaYED/Y8G9J/bNcDUxhUpB7hv/d+XD4Lq+T3AAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;vibe game single shot game&quot;
        title=&quot;vibe game single shot game&quot;
        src=&quot;/static/5903687d70e5a52c63e7a7edf538271f/bcfad/vibe-game-single-shot-game.png&quot;
        srcset=&quot;/static/5903687d70e5a52c63e7a7edf538271f/0eb09/vibe-game-single-shot-game.png 500w,
/static/5903687d70e5a52c63e7a7edf538271f/1263b/vibe-game-single-shot-game.png 1000w,
/static/5903687d70e5a52c63e7a7edf538271f/bcfad/vibe-game-single-shot-game.png 1396w&quot;
        sizes=&quot;(max-width: 1396px) 100vw, 1396px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;Amazing we got something bug free. But, gems are aqua coloured balls, and outfits are red squares. The characters are bounded by grey square backgrounds. And if you could play the game, the character movement is not smooth at all.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 1112px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/830de671d9d0cb80cf0a2d1f4ed9f3af/78ab6/vibe-game-single-shot-game-shop.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 75.8%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAPCAIAAABr+ngCAAAACXBIWXMAAAsSAAALEgHS3X78AAAB4klEQVQoz5WSy27aQBSG/WTsWEU8QF+iL2Fzqyq10EoBtVJXoYsQKa2iirYSWbS0C9NgHK6mLeZuaMAXsD0znvGpMAgFUkXpr381Ot+c/8wZLhQKCYLA70u4RzzPJ58kX7yMRCJcPB4HAM/z4CHyfQCgrTpI5dx5fgt/+y5+KBRFsXJx8alQuByNJusiStmBPM8HWF2JcPn57ftTLhaLAUDvd68m1QbD8aA/ur5u6roBAIwx/1/axMzlc1w0GgUAgxCdUkqw/4DsdAfzAfxuPH0qN6fdrg/AKPXhvksopXuddd34M5utbBsj4mLquMhFCGNyYBch23FXtrOGz3IcLwjrmQfDZ0le+fmr2mrWr0qNWkWS5Yk2JR5FmOwsy3KpVFKUDgCc7DqPNe00f6ZOZsOlQbQKvmkj7N3GNlY6HUmqqr1+AJ9sYYQwBNMyxigD4jFMyF37wZ4xwdvYG3ixMJT+1DCXlmnNTXvlkLttESamZc0XC8O0trAQzNxUuo8TryVF/dFqSF/ONbVOPOYifACL5XKxWKw3Gnux+6Pxx6/V3nSuLU10o9hzdWk7S9s+sKqq7XZ7MBxu4UQi8R9/+9ae168dDoezr7LpTDp1nEpn0nvOpg9PAj8/TmXeZI4eHf0FcP8ElHhI4DsAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;vibe game single shot game shop&quot;
        title=&quot;vibe game single shot game shop&quot;
        src=&quot;/static/830de671d9d0cb80cf0a2d1f4ed9f3af/78ab6/vibe-game-single-shot-game-shop.png&quot;
        srcset=&quot;/static/830de671d9d0cb80cf0a2d1f4ed9f3af/0eb09/vibe-game-single-shot-game-shop.png 500w,
/static/830de671d9d0cb80cf0a2d1f4ed9f3af/1263b/vibe-game-single-shot-game-shop.png 1000w,
/static/830de671d9d0cb80cf0a2d1f4ed9f3af/78ab6/vibe-game-single-shot-game-shop.png 1112w&quot;
        sizes=&quot;(max-width: 1112px) 100vw, 1112px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;The shop UI is ok, but we are buying completely new characters, not outfits.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 1124px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/d006a8c0a3cfbb027012d5b58ba1ff4a/ec55d/vibe-game-single-shot-game-closet.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 86.2%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAARCAIAAABSJhvpAAAACXBIWXMAAAsSAAALEgHS3X78AAACtElEQVQ4y4WT22oTURSG8yQF+wo+gIKgeOmFUKdpmxYv7MEUBcEKWk1qURB65UUFr5QkNWlqjbEp1Ng2CopF2zRJC82hk3PGZHKYZGafZpZkJk21FF387Kv9s/5vr71Mly5fXPR5F/1ez3vP0oclt8/j8iw4nS6nc+F0OVwOt3fR5z9/7oLp1v3bAMAY0zSNURX+XWr7Al0LwLeQ/dmcyXrPCgBv3Muh0NePwZDX619eDlQqVU1TCSH0RBHCABqxCOTSttknJuvUJAC0WnKzKSOEqtVaoyFRSlVV1VRVO60IIQBgtz82WafanRWkKEhBGGtGOk3TAChlmBBDhFJMCMIYEyq3ZACYmZk13dTNuXxh/vlcIn5QLAnJVIpPZ+LJZK3ewIQqCDNVO+TTfr8/GAy+8/l2I9FOZ8NcLJVevpjneV74VU6lDvOFYjJ1WK3Vu+Z0Jru6uroZ2lwJrERjsW7sNjPGuP2MjDHWBtUPIIx2Y9P2OAB0KoRQJ7ZhlppNoZCTJIlQRgjF5MiGjyUprVw9n63mKnXxKPZdgzlvezAVi0b5dGZvbz+eSO5GIoJQJpQpCMsIgQob/OcbaxOWwPWFqAcAHtntHeaKKAbXNwrFUkNqlitirVYXqzWp2UKYGGZK1GSFd0XcjrBrOxMGANsxsz46pqpUpzbgqY5gCGPKWOf/IYT/GlVFrH4P75fKooJwS1ZkfegKwgiTdnOMKVGLDSGU/rLObyaEVId5QmdO8tnJp6+391OJeHxr60e+UKBM7ZoN5rXUp2s+85W3V1/tOADgoe2IuVwRowfZslhrybLUlJutk5JlJNTK29nwz8wOL2Q6nUfvjOnb8r99+qMopQAwPW0znTnbOzg6NDw+MjRqsYwND4+PmC0DHGfu7x84IY4z93FcH8dxnNlsHuzp6f0N1yxWXcxALTkAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;vibe game single shot game closet&quot;
        title=&quot;vibe game single shot game closet&quot;
        src=&quot;/static/d006a8c0a3cfbb027012d5b58ba1ff4a/ec55d/vibe-game-single-shot-game-closet.png&quot;
        srcset=&quot;/static/d006a8c0a3cfbb027012d5b58ba1ff4a/0eb09/vibe-game-single-shot-game-closet.png 500w,
/static/d006a8c0a3cfbb027012d5b58ba1ff4a/1263b/vibe-game-single-shot-game-closet.png 1000w,
/static/d006a8c0a3cfbb027012d5b58ba1ff4a/ec55d/vibe-game-single-shot-game-closet.png 1124w&quot;
        sizes=&quot;(max-width: 1124px) 100vw, 1124px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;The closet is not populated with outfits, but with characters.&lt;/p&gt;
&lt;h2 id=&quot;Where-the-vibe-stalled-the-major-issues&quot; style=&quot;position:relative;&quot;&gt;Where the vibe stalled, the major issues&lt;a href=&quot;#Where-the-vibe-stalled-the-major-issues&quot; aria-label=&quot;Where the vibe stalled the major issues permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;This single shot prompt to working game is actually amazing. Even in the early days of LLMs this wasn’t possible at all. A total bug free game with one prompt.&lt;/p&gt;
&lt;p&gt;But my daughters weren’t happy at all.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;“Daddy, I thought it would look like Minecraft”&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;These are the types of comments from my daughter after the first iteration. It needed refinement to be something she would even play once.&lt;/p&gt;
&lt;h3 id=&quot;Map-generation&quot; style=&quot;position:relative;&quot;&gt;Map generation&lt;a href=&quot;#Map-generation&quot; aria-label=&quot;Map generation permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;The first major wall I hit was map generation. I wanted maps that were sensible layouts, in fact my daughter wanted the maps to represented real spaces she explores in real life. Firstly, left to its own devices, the maps it generated were incredibly boring. I attempted to upload screenshot from Google maps, or house plans and ask it to generate a map based on the image, these were even worse.&lt;/p&gt;
&lt;p&gt;To get past this, I stopped asking the AI to build the maps. Instead, I used it to build an &lt;strong&gt;Age of Empires-style Level Creator&lt;/strong&gt; directly inside the game space. I shifted from trying to automate the content to automating the &lt;em&gt;creative tools&lt;/em&gt;. Once the level creator interface was functional, I could sit down and design the maps myself, bringing human intention to the grid.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 1412px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/30dd7b17a0320012d655253ee4110810/fa6a4/vibe-game-map-generator.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 105.80000000000001%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAVCAIAAADJt1n/AAAACXBIWXMAAAsSAAALEgHS3X78AAACoUlEQVQ4y3VT15LcNhDk/3+XHvwg66SSZN0tNzNiAQ4CgcEAYLDBPZ8l3bqrC4UwzYks0AduvSICIhWCCUFvq9o2aZrm/0FMqXA+XLTnKTTkO8KOfL+xI2Tk07LM6yuXdf2Z8zIX2lM9qkpyhqOcAiSCREOkbRNuwd8C3gKy/C33xhs5mlKhPJXA/yyfv1XnI/CS9yXv95ms5P1xuJ2lOAGvrGYBWcA+YL/ps1iir7z90Vy/nA9Px/LpWH7a776cD59P+0+H3dfrace6567ZsW4/sMNwO4Jove2D81MqRk8yBX1nJBm8DF7FfDQp6hjyDSEQDt7dmV9jiNNUhJjQORhASomIKcZ1XVNKSspMpWKM0zSt7zDPcxFCRESttbWWiGKKy7qklMZxtNY650KIuWHLvPyKKXsOEQAul8swDACgtZ7nbPfmITfmHeZ5TjFlsUc3mnEcR+ecUsraERElGGO0RR9NM6rBOm+UlINwziHi5nku0PuGV0oqUMo5MPLIOYyjKXf7vuutdRQ0gNDG6GzgchmktNbmnIkieSJPwdMy/14Y8t77QD5M6Zene+QFEpW8eeHnj21Vj3Z7+M+Ij6wSp0qfAMWDansKT+z0me//6LqPA+bb5a1SK7PsRfx1o5pPtwdih/Shff7RHr617VdpttvXkq7LeoHzy/AdHBOJPRATxUrxXnChpQv0m4W00IjKoHDBPA5bSyU4l1LC1uphGG6MDULM83wP/zWQR56DtdYYk9fc7NE6dz++F/ysTCkVFKK1o1QKNmhtAoU7EHGbiIzcMr81znsiMsY45/KECc6v12td101d11VmVeV90zR107RtmydXSshp5by4yHM23X8MAGjbtvsXfd+zDf0djAGA0mobLQkAQghE/CfnvwF1p7FYFAjyNAAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;vibe game map generator&quot;
        title=&quot;vibe game map generator&quot;
        src=&quot;/static/30dd7b17a0320012d655253ee4110810/fa6a4/vibe-game-map-generator.png&quot;
        srcset=&quot;/static/30dd7b17a0320012d655253ee4110810/0eb09/vibe-game-map-generator.png 500w,
/static/30dd7b17a0320012d655253ee4110810/1263b/vibe-game-map-generator.png 1000w,
/static/30dd7b17a0320012d655253ee4110810/fa6a4/vibe-game-map-generator.png 1412w&quot;
        sizes=&quot;(max-width: 1412px) 100vw, 1412px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;One tool for converting images to the JSON array of tile identifiers.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 2000px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/afb1f69df2a48395286e3a1c91e01b3e/48458/vibe-game-map-editor.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 52.6%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAALCAIAAADwazoUAAAACXBIWXMAAAsSAAALEgHS3X78AAACgElEQVQozzXJ61MSYRgF8P0b+9qHphovmFrW2JCOV5aLDLGwrtx2gZWLiIIKCoigoAgaoYB4RRDNEl1AGAHZlyVssHrmN3POnAfK3V/9KpV+thRBvd5oNBu/m83/nppPf6/57F88Y5gGVDl3HaczJ5nLxHmKus8Xy9VClS4zdBnQD3X6sVGn6wAAmgE1BtRavQ5qTGukAYCoauvJMEy1UjFfeL+ezSpihhm/fN3CMwSUikOjxIvPujWb5OA3YkAdIbGEUR/ExQn98pkfip4cn6ZTR8lkKp2xBEnCDat4b3Bhm9U6PGMe9BC902gfqeMhH15Yua91TngsrhIHUe6u2BC3Q2kqm6uUS4CmCgXHBmFcGJr0iUVBVLaFmC0jEaR9aaJbu4BsoJ0W0+Ca+tOMjTMRxTl7mOVoFcrmc6Ua/QDA7d0dfmobShDwoZYfw4k1oXO6PyxpX5INaLWjYbRzR8IKSVh+WQ+2jQ7E5a7LAJTNZgGo0zS4/nFNHi9y9uX8GM6N46STt4V170x22Ke+KI0cnZvnJPu3sO6QlOUjPgoDIudlAMoX7x8BaDQahXwBP1kcjykEMRw+INRugU/+flfa4UDZnLWp4bRe6RVtTvX45b1BhOUSv937bodu8rmbXCFXrFK3eVuAHI8puQkNnNDoHLwQ9m4bbZ8jYdiH8Q/V0hC2YmCHpV12XpuF/yoSdUJHyRR1eVBNebIX8X2DYJHslQTEIwmV2cTeEHVpZJ8NyyrBCjK2KxFuIxNBxDjLdo2+VBj7nBk/dJWlynfJp9tQmUrFPPM7WjgwJ9GETbZ1k3vFQuhJ64J51bPsDdtcCbvt1DWf8UYCJkfUepDe+wNv4OgmxPX6wwAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;vibe game map editor&quot;
        title=&quot;vibe game map editor&quot;
        src=&quot;/static/afb1f69df2a48395286e3a1c91e01b3e/f97d7/vibe-game-map-editor.png&quot;
        srcset=&quot;/static/afb1f69df2a48395286e3a1c91e01b3e/0eb09/vibe-game-map-editor.png 500w,
/static/afb1f69df2a48395286e3a1c91e01b3e/1263b/vibe-game-map-editor.png 1000w,
/static/afb1f69df2a48395286e3a1c91e01b3e/f97d7/vibe-game-map-editor.png 2000w,
/static/afb1f69df2a48395286e3a1c91e01b3e/48458/vibe-game-map-editor.png 2842w&quot;
        sizes=&quot;(max-width: 2000px) 100vw, 2000px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;One app for manually editing the tiles, and adding objects and gems (diamonds).&lt;/p&gt;
&lt;h3 id=&quot;Image-generation&quot; style=&quot;position:relative;&quot;&gt;Image generation&lt;a href=&quot;#Image-generation&quot; aria-label=&quot;Image generation permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;This required a more involved process of generating images with Gemini’s image generator, however the raw images generate were inherently messy.&lt;/p&gt;
&lt;p&gt;Every asset went through Photoshop. I had to manually:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;mask out backgrounds&lt;/li&gt;
&lt;li&gt;clean up edges&lt;/li&gt;
&lt;li&gt;fix alignment artifacts&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Then I could export clean, transparent &lt;code class=&quot;language-text&quot;&gt;.png&lt;/code&gt; files that would map perfectly to a coordinate system.&lt;/p&gt;
&lt;h3 id=&quot;Game-play&quot; style=&quot;position:relative;&quot;&gt;Game play&lt;a href=&quot;#Game-play&quot; aria-label=&quot;Game play permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;An LLM does not understand what makes a game engaging for a child. It doesn’t know what triggers a sense of reward, what is too difficult, or what causes frustration. I brought in the ultimate user experience consultant: a real child.&lt;/p&gt;
&lt;p&gt;Every mechanic, win condition, and level progression rule came from my daughters. I watched how they interacted with the screen. When they got bored, we changed the rules. When a level completion didn’t feel rewarding, we adjusted the logic.&lt;/p&gt;
&lt;p&gt;So we ended up with a cinema map with popcorn you can collect.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 2000px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/4411dc80dbd94f14d54afcf718d3c2b8/9d287/vibe-game-cinema.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 52.6%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAALCAIAAADwazoUAAAACXBIWXMAAAsSAAALEgHS3X78AAACO0lEQVQoz13R4UsTcRgH8Pt7It1udzeXW5ibTmXM8Ha62mSbjpYxsJymwhZFDAqtF9pehJS+KgsiiInozEmBaLSyUqa3280Gug1Ubtvvdz/nQV2oU6uH74vnzefh4Xmwvb00hGlR5EQxKaLUfmEVgYQoJuF+qiTEl4d81qF7df7BxpHhLnfnK4Z55+vOTY496xm/zTzGioCHkD+UR4GAhbA8COTX39+5ajFqlZoLGp22Sq026HT3A/2L4ZedNlsrbcFO2b/hjvFcoI2+SFQocVKF4zhOUqTdbnUzdLVaXV+vxxDiUWnzLKi8BUIpWNw4xDVkJU6QJHFcCoWSoCiSopQ4joF8XMh8FrIxIfdFyMZAfl0se343txIJWP/D5GlHENjm0utpn2lmgJ4ZaJm62ZhcnDz4tS1CrlBkV5enIv42+hJ5HldRZ+SsMGHne4aNbrELs3Ph8ADDRiek31kIWBHx+b21cD/90N1iNjWeq6hUqggFrvo7GEKpkpRGB+nwp9hUwJ6ITkjyIUb7KQQ2ZgPWN56G556mUac+5DSMOGpDLsMTl2HUURty6o+uDTkIuB/xpchdO1vGCSjyO7lv073mR9eNvtaaLr2iu4nqu6z1GlU36vDe5upbJg12cltekn4uBF3r82UMAJ/ZWvn64oGzg7F0tPf7+2yudgVZ5e3x+gZ91TW1Tc3mkz8jHkJuIejYmB+X5CwoJgqFZKGQLEnbtivM2NNhWZY+fnjbYDRAkJDlXc81x+hI8A//l5OitQJUWQAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;vibe game cinema&quot;
        title=&quot;vibe game cinema&quot;
        src=&quot;/static/4411dc80dbd94f14d54afcf718d3c2b8/f97d7/vibe-game-cinema.png&quot;
        srcset=&quot;/static/4411dc80dbd94f14d54afcf718d3c2b8/0eb09/vibe-game-cinema.png 500w,
/static/4411dc80dbd94f14d54afcf718d3c2b8/1263b/vibe-game-cinema.png 1000w,
/static/4411dc80dbd94f14d54afcf718d3c2b8/f97d7/vibe-game-cinema.png 2000w,
/static/4411dc80dbd94f14d54afcf718d3c2b8/9d287/vibe-game-cinema.png 2846w&quot;
        sizes=&quot;(max-width: 2000px) 100vw, 2000px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;And a school level with interactions such as a chess game where the pieces move.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 2000px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/430eb34a27bc0b0c779dbcfa7796b68b/b1b5c/vibe-game-school.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 52.800000000000004%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAALCAIAAADwazoUAAAACXBIWXMAAAsSAAALEgHS3X78AAACP0lEQVQoz1WQ/U8ScRzH789ruWX5wNaWTfP0zrvvXTC1WSj4CBonCPeAmBDQpk7WzNN4UiAPuceREoote9CyH3JlqWBbc6tJzGh7//D5vLfX3nu/oaOj/V/nn7Nrwo+Tj29XpzeC/V+zIW01AOuuHO1E53qJYd7vE+YHo4H41OA3M/lhRL8/RL7o8y3iLqh0frilqZsv1UJ4KmFtzlAtvPuBxTZks1sCTzzzc4/aDYDpR9XH90SWzDKkxhAaTShsd6KvCdrLRYVI7CD7NGZsSI0256YNgYG2HmMnx1JO5xjPBy1Ws3+g/ZUblxhcYkFZmOoBSXM9lBxp/rLNh7uuCQ5EndRn7HB+2cWHZ+DWW7lcAm5tmg35tqNu0QHLHCGz4EIMrkzeTZp0UJqClww1GRpTOEKk8bzXYMPr2pCW1ztpgCNLS3Mw2jaMXN/yGkQar4YTJh0kM7jo6qi4HCG5kM0Q5RkfbdTVRp5562pr+Ocz++uBNHVH5shqOGXWQWUG/HUlBlc9enG8faG7YfEhqgVNCxZY9N7fCPSINFYhKzEgM4FCl7/E4Np052pvfdqB7yy7Yl1XZRZkPaTMdIg0pnKEyhFSFa9wxD9Y9ehT5kbBjh0UVk6+Fzbmx1aMN9YnEMVNKiwQnNgKhaiXm5UFXbYVrLcFO/YpHz//fVj6+f7sbE/xmgUKyThR1U0mHVjEhmocqA6HJKZ8OdG46ea7zGzp9E2xuFss7h4f756W9mT/8Npoy8VCHFA58F9tFvwBQueVqvv9XRcAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;vibe game school&quot;
        title=&quot;vibe game school&quot;
        src=&quot;/static/430eb34a27bc0b0c779dbcfa7796b68b/f97d7/vibe-game-school.png&quot;
        srcset=&quot;/static/430eb34a27bc0b0c779dbcfa7796b68b/0eb09/vibe-game-school.png 500w,
/static/430eb34a27bc0b0c779dbcfa7796b68b/1263b/vibe-game-school.png 1000w,
/static/430eb34a27bc0b0c779dbcfa7796b68b/f97d7/vibe-game-school.png 2000w,
/static/430eb34a27bc0b0c779dbcfa7796b68b/b1b5c/vibe-game-school.png 2864w&quot;
        sizes=&quot;(max-width: 2000px) 100vw, 2000px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;h2 id=&quot;The-2026-Reality-Check-for-me&quot; style=&quot;position:relative;&quot;&gt;The 2026 Reality Check for me&lt;a href=&quot;#The-2026-Reality-Check-for-me&quot; aria-label=&quot;The 2026 Reality Check for me permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Being an entrepreneur or building software in 2026 seems like it has never been more accessible. The barrier to technical execution has dropped through the floor.&lt;/p&gt;
&lt;p&gt;But the reality is that launching something meaningful is just as difficult as it has ever been. Why? Because while code generation is cheap, human attention and wallets are scarcer, more competitive, and more stressed than ever. You cannot prompt your way into someone’s attention span.&lt;/p&gt;
&lt;h2 id=&quot;Conclusion-The-Pure-Joy-of-Building&quot; style=&quot;position:relative;&quot;&gt;Conclusion: The Pure Joy of Building&lt;a href=&quot;#Conclusion-The-Pure-Joy-of-Building&quot; aria-label=&quot;Conclusion The Pure Joy of Building permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;I have zero delusions about Blockvillie. I’m not launching a startup, I’m not trying to make money, and I don’t even expect my daughters to play it. Real games require immense depth, complex optimization, and narrative layers that go far beyond a few nights of vibe coding up a frontend experiment.&lt;/p&gt;
&lt;p&gt;Maybe this is the point. The value I got out of vibe coding wasn’t achieving the myth of the automated product to start a business. It’s the joy of learning. Diving into a genre of programming I had zero experience with. Growing from raw prompts into an interactive experience you can touch, play with, and share.&lt;/p&gt;
&lt;p&gt;The code might be generated, but the creative fulfillment is entirely human.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[If databases were rappers (thoughts from a Generative AI)]]></title><description><![CDATA[If databases were rappers (thoughts from a Generative AI) Can you relate to these? Following from my previous posts where we explored what…]]></description><link>https://mortimer.xyz/if-databases-were-rappers/</link><guid isPermaLink="false">https://mortimer.xyz/if-databases-were-rappers/</guid><pubDate>Tue, 17 Mar 2026 12:00:00 GMT</pubDate><content:encoded>&lt;h2 id=&quot;If-databases-were-rappers-thoughts-from-a-Generative-AI&quot; style=&quot;position:relative;&quot;&gt;If databases were rappers (thoughts from a Generative AI)&lt;a href=&quot;#If-databases-were-rappers-thoughts-from-a-Generative-AI&quot; aria-label=&quot;If databases were rappers thoughts from a Generative AI permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Can you relate to these?&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Following from my previous posts where we explored what generative AI thinks &lt;a href=&quot;https://mortimer.xyz/if-cloud-platforms-were-people/&quot;&gt;cloud platforms&lt;/a&gt; and &lt;a href=&quot;https://mortimer.xyz/if-programming-languages-were-people-ai-thoughts/&quot;&gt;programming languages&lt;/a&gt; would be like if they were people, I decided to do something similar but a bit different.&lt;/p&gt;
&lt;p&gt;As a &lt;strong&gt;Hip-hop fan&lt;/strong&gt;, I know each rapper has a certain aura about them and a history of success and failures. They have periods of becoming “pop” or periods of being underground. I asked a Generative AI to bridge the gap between my world as a hip-hop fan and my professional world in data. Here is what it thinks of 90s databases as rappers.&lt;/p&gt;
&lt;h3 id=&quot;1-Oracle-is-The-Notorious-BIG&quot; style=&quot;position:relative;&quot;&gt;1. Oracle is The Notorious B.I.G.&lt;a href=&quot;#1-Oracle-is-The-Notorious-BIG&quot; aria-label=&quot;1 Oracle is The Notorious BIG permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;The Persona:&lt;/strong&gt; The King of New York (and the Enterprise). Massive, expensive, and radiating “Big Poppa” energy.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;The Tech Parallel:&lt;/strong&gt; In the 90s, &lt;strong&gt;Oracle was the undisputed heavyweight&lt;/strong&gt;. It was the most powerful and flamboyant database on the market. However, like Biggie, it was synonymous with &lt;strong&gt;“Mo Money Mo Problems”&lt;/strong&gt;—if you wanted that top-tier performance, you had to pay the “Biggie” tax. Running on Oracle was a status symbol for any 90s corporation.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 2000px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/f13960d90ea454587362bed32351c483/2eb59/db-were-people-biggie-oracle.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 100%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAUCAYAAACNiR0NAAAACXBIWXMAAAsTAAALEwEAmpwYAAAFgklEQVQ4yx2QbVDThwHG/1t36/WFWcAkiLwJIQQCIYQESIgkvAVICBAIxACBBCIEeZM3AREFhRZ8A2vVTi04ztaWWouvWGpVOK221Zbaym6r2+zW63bdh33a599ufHjunrvn7nfP/YSVm3MsvHecT6+c4+Gdj1hcmGHh/VNc+fA0Vy+e5calGS5/cJrL87/n7s33uHvzAncXL/DlygKPVhZY/PgdVh9c48Hteda+uIpwce4ITR4Hu7oaOTk1SpHZwO6+Fsb29VBqzabGWUx1VTHNjU7OnJxg72A7E/v7ODjWz1tToxw/MsL1SzOcOTHO4+UPEe4tvcvU5BCHXh/g+qVZ3K4S3tjfx9HJPXjdFTR5nTQ1OGlvcXP25AQjQx3rwN6djRwc7+fYoWHenT3GqWP7efjZ+wjXLhzlyvkprpyf5g8nx7k6f5rna/dZe7TE06+W+P7LT3j6aIknDxd59mSZP327zNrjW6zev8Y39xb4/NY8Z6b3MTM9zKXZCYSumq20VurY5c7FU5SKJSOWWxdP8d+fv+Wff1zhp6d3+XltmV9++Jxfnn2xnn8/e8jz1U/56+NFrp6fptIox2NOpqfWhLDXb2XIV0C1WYU9S4HPqubsZDdf3/6A1eWL/OP7O/zt60/4y6Mb64Dnq0v88NUNfnxym4/PHWait5adVVl4ClS0Oo0Iw62V7G0pw2NJpcwgZ0eZjn1+G53ufHo8Vsa7XNy/fo7//H2Vn9bucfvyLP/68wN+/O4OMwe7GfVbaHcY2GZS0O4yIdQXp+OvMFBXpKEwTUqzTcubg27sOWocpmSGfFZmpwZZuT7H/DuHqSjaSu+OOnwuCy6rgfaaPNocBmpykmgoSUfY4chad1eWlYA1Q4YrN5k9zXb8znzqbAYGWpz0NBbTUJ5Jrc2AJiECWWQg0vBAZBEiJMEbcJiUlOtj8ZZmIgz4KxjprKajxoyrIHX9WW2ZicGmUgr1SRi1CsrNOnLT4qg0p5GvV1CWp0an2kJKXBibRa+hkUdQka3CnKlE2NvtYe7tcUb7m6m0ZFJkVOGvLaAqX4MlU0luWjxRm4IwahOw5WiwGlOosRnwVpiwmVSEin5HZEgQumQpKQlRCHp13HpJT4wiVR5GQnQIOekKtqpl6JTRiIMCkEVKKDSo0KvlFBqUeOxGBprtNG/LQx4VQoTkNWLCNlJXokdorMyhw1tMZZEOVWwosvBgdMkx6FNiUMdH8MILAqLAV4naGEBY4MsooiWUZCVRoE8gNyMBg1pG5KYgXnnxN/gdJoQDXdWceqOTqZE28jIUlGSrKDQkkp60BaUsjKANLxMu3kBCpASVdDMGVRwVeWmU5KazzZJFaa4WvTKGaMkGmhzZCGN9dRw/0MHBQR8xm4PQqaRkquPQJseikG4mNT4CRZSYfK2MosxEzOlyslKkWLLU9PqqyE5ToIkPp9aaQa/XgnCgp4YT4x2M9bipMqsp1Eahit6ISiomVBSw7lT4lcBLL/6a4IDfopKFUm83oU+VIwgCZkMS9lw1dfZs+rfbEMZ63UwOeDky3LQu2l2gokgbRaUxHvkWCTHhYoIDX0EUHECIOBBx0Ksky8KQRYcSEPASZXlaehqKSYoWkygNRRj02xluq+TocBNzJ/ZzenqENm85Y0Nt9LfXc+LQHjqbXBwe62ffQCu7OhvY3d3Enr4WpieHmXp9EJ/TjEkZiiE5EqHbW8iB7mom+j0Mt1fRV5+HTS9FI5egjA5CEydGLd1IulxC0pZA1LEitDIRaun/NwmqWAmW7DRat7vZ6XMgdNUXMLTDzlhPDaM7nez2FVFujCMjXkKqTIxGJkIbJ0ITG0y6XIwhMQSjMgSDQoxJuQl9YijWnDRqq0rZ7rLwP6HtSgyIkYuZAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;The Notorious Oracle - Life After Deployment&quot;
        title=&quot;The Notorious Oracle - Life After Deployment&quot;
        src=&quot;/static/f13960d90ea454587362bed32351c483/f97d7/db-were-people-biggie-oracle.png&quot;
        srcset=&quot;/static/f13960d90ea454587362bed32351c483/0eb09/db-were-people-biggie-oracle.png 500w,
/static/f13960d90ea454587362bed32351c483/1263b/db-were-people-biggie-oracle.png 1000w,
/static/f13960d90ea454587362bed32351c483/f97d7/db-were-people-biggie-oracle.png 2000w,
/static/f13960d90ea454587362bed32351c483/2eb59/db-were-people-biggie-oracle.png 2048w&quot;
        sizes=&quot;(max-width: 2000px) 100vw, 2000px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;h3 id=&quot;2-IBM-DB2-is-Nas&quot; style=&quot;position:relative;&quot;&gt;2. IBM DB2 is Nas&lt;a href=&quot;#2-IBM-DB2-is-Nas&quot; aria-label=&quot;2 IBM DB2 is Nas permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;The Persona:&lt;/strong&gt; The “Illmatic” lyricist. Deeply technical, incredibly dense, and respected by the most hardcore purists.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;The Tech Parallel:&lt;/strong&gt; While Oracle was the “pop” star of the enterprise world, &lt;strong&gt;DB2 was for the technical elites&lt;/strong&gt;. Built on the research of Codd and IBM labs, it prioritised the “purest” form of the craft. Much like Nas’s complex internal rhyme schemes, DB2’s indexing and optimization engines were a masterclass in complexity. It didn’t need the flash; the performance bars spoke for themselves.&lt;/p&gt;
&lt;p&gt;Nas in the 2020s is exactly like IBM DB2 in the modern enterprise: a foundation laid in the 90s that everyone assumed would be replaced by ‘newer, shinier’ things, yet here it is, still running the most critical workloads on the planet.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 2000px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/c6d4d3f0da00ed43cf2697e53ff0d784/2eb59/db-were-people-nas-ibm-db2.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 100%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAUCAYAAACNiR0NAAAACXBIWXMAAAsTAAALEwEAmpwYAAAFQklEQVQ4yy2UeU+TWRTG3w8yycSJo7ggWEuFFlsKtFKWshQpDMMqCgIqqCwq4r6BoIgsoiwaVCogiBRQR0SrOCCLsgp0RRB1Mskk8wF+E17njydnyX1OnnPPuVfoqjtJ/6NqXj+uw9JdzxtzIxZzA2/MDVi66njVVcdAZy0DHTW8frxqa3nZUcOrVf/RDXruXaH3fjmdjZdouZ6P0N9ezfzMO2xzwzhtH1h0TuKyf8Bpn8BpG8dpG8OxMIJ9YRSX4yMuxwR267h4ZmbiNYX5mSQlGkhJNtJ6+yLC84eVLMwOYZ8fwT4/hm1uFKf1Ay77BIuOCVy2DyLZZf/Ion2Cz47JH3n7R2Ym3vDyaStll45TefU0t8sKEPLSDVwvOy0SrXMjOBZGRaWrhM/OyR9wTIh2yTXFknOSlaVZMV5ZmmFyrJ/05CjSkw2kGQMRjqRFogvwobO1ga/LM9gXRnBYx3DZx1lcVbXapv2HSqd1nOXFaYYHn3G+OJfLZ49QkLubUK0cvVZOeqwGofhAHBq1jKslRawsTfN9ZYG/vs7zdXmWleUZvn2Z5fvKHN++zIm5f/9ZpL6mhIggOUa9GrXCA5nEDX2QL+lxGoS8PREEa33JTjNyMCOehtpSTM3V/NHbQn1tCRWlJ7hz6wqP227T1d5Iu+kWt6rOk38giSSjjh0+7mzZ/AtRoSoOpIStFoxEp1GQ+psepc8WdAEyaspPUXvtFMEaOTvVUhJjdnIoM5ZQrQJ/pZTzJw5Sei6P1PgwVHIPpJ7riAxRkpMajlCYYcDPdxsJ0TqUPh4i6VRhJll7jBgjA6koOYa5o5HRoeecLTpI6E5fCnJSOJq7m93xYWj9pHhtXU+Ixof89CiEY5kxeHu5E6lTIffaRJhWjjEiAK1aSmaqgb7ORuanB1n5PI2pqYJdej+y9sSQlhBOvEFLoFLCdsl6gvxlHE6LQMhKDMF981oMoWo0KqmIEK0PKoUnMRH+VJUXMzjQybtXXVSWFBEb7kdSrI7oUD9xunLZJnEoat+t7E8KQ8hMCEbi6UZspFaERrkNmddGvGWb2O61AZ3Gm7NFWRzOThALxOj9SIgJImyngqgQJf5KCR7uawnW+JCTqkdI3eXPxg1r2OHtzrq1P+HpsQ63DWvEYtl7YzhdmEFbcyWmO1c5lptKfLSG8GAlKl9P9EEKAlUSNrr9jF63g/3JeoSTOfFk7Y2jMHc3h/YnUXw0k5jIQIxRAdRXneXe7SucOZ6N+VE9D+9WEB26g6CA7WKre5OjOFmQwaHMeNJTDORn7EIo2LeLhLhQ8g6mcPH0IeprLhMXrSUtMYyE1XXJiufIgUTuN5VTWXqMAN8tSDx/ReKxloabpbTeq+HU0X0U5WdQWrwPobnqBObOOwy/7cX66b34QQT4yQhUe6HXKQhQSUTVxQXpGMPVSLeuE9dJ7evJi742/v5qZezPp3yatPC8rQqh70EZi7b3uP5/w8uuKY7k7MFP4YFGLcVb6ibeU5whkO1bf0Uu20iQvxcqhQeWl12sLE6J3CX7GH2mawjdTReYHu9nftKC/dMQttkh8TvrMNXxe2wIQYE+JMSFkZOdxIUzedy4doamW2V0tNZj+zSMY24I++wgttl3mO+VIXQ1nGNuyoJ15i2uhWGmxl7S/6ydnif3RbSb6untfkDPkwcMWnqwDHTz4lk7ne13mRob4LNthPkpC07rKH0tFQhNl7Ppai6nt7UG88NqettqRTxpuUFP64/4aftN0ZpNVXS33KC7pZLe1mqePrrJs4460e8xVdFcnsd/2s8K5BjzSXsAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Nas - It Was Written (To Disk)&quot;
        title=&quot;Nas - It Was Written (To Disk)&quot;
        src=&quot;/static/c6d4d3f0da00ed43cf2697e53ff0d784/f97d7/db-were-people-nas-ibm-db2.png&quot;
        srcset=&quot;/static/c6d4d3f0da00ed43cf2697e53ff0d784/0eb09/db-were-people-nas-ibm-db2.png 500w,
/static/c6d4d3f0da00ed43cf2697e53ff0d784/1263b/db-were-people-nas-ibm-db2.png 1000w,
/static/c6d4d3f0da00ed43cf2697e53ff0d784/f97d7/db-were-people-nas-ibm-db2.png 2000w,
/static/c6d4d3f0da00ed43cf2697e53ff0d784/2eb59/db-were-people-nas-ibm-db2.png 2048w&quot;
        sizes=&quot;(max-width: 2000px) 100vw, 2000px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;h3 id=&quot;3-SQL-Server-is-Ice-Cube&quot; style=&quot;position:relative;&quot;&gt;3. SQL Server is Ice Cube&lt;a href=&quot;#3-SQL-Server-is-Ice-Cube&quot; aria-label=&quot;3 SQL Server is Ice Cube permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;The “Group” Era (N.W.A / Sybase):&lt;/strong&gt; Just as Ice Cube started in N.W.A, SQL Server started its life as a port of Sybase SQL Server. Microsoft was the “lyrical contributor” that eventually went solo to build its own empire.&lt;/p&gt;
&lt;p&gt;Ice Cube recognized that his original fanbase from the early 90s had grown up and were now parents themselves. He wanted to make movies that his own children and his fans’ children could enjoy. Like Ice Cube, SQL Server became the “everyman’s” database, and family name.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 2000px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/3a7ee15c802b743cc79e76bea7f3f334/2eb59/db-were-people-ice-cube-sql-server.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 100%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAUCAYAAACNiR0NAAAACXBIWXMAAAsTAAALEwEAmpwYAAAE/ElEQVQ4yx2RWVNaCQKFbyWV2AFRBGST9QKX5QqyXLbLdhFQQGQRtV3RmKDdJlY7TWdx4iSZVM9UdXfVPE131fQfmZ/3dcWH8/qd+s4RYuk8ohTF7vJRqbco12p0Ln6kf/WGg5s3jK9u6b/6QG/6lv50xuHtZ4bf3/Hm4xeub15xeHJKc2ODw8mEaquNsJrO4ZMirLh91BotalWV569nTGYfefXhMy9nd5y9+xcns39ycHPHzZf/MH37mZ9/+ZX7D+85PDpiMBwymZzS6Q8RCsU86XQSl8dHp92gWS3y4se/k5j+G+XlJ5TLL+Quv1C6/Ej18p7G6TWFRpfj4wNuX08p1zQsVjvlcpFAKIDQbVZoVvP4vF6Oh032u3WuXpxRezGjd/cbW+9/YXB7z/Z4j3a3Q0rJEU0VONkfMrs+Z7u7gRIPoySj5HMJhJaaoaGmkPxuBtU0L/db/Hl/xMH5BWe//sng+ic2ux2qjSZhKYQYihNcK6LVKvz0fEivlkEOehg1cmxqCoKWjRMLiWRiIpNugUFL5f//fc39m0s+/v4/Lq6mbLbqjPqbuBw2GhsbZMpVVvwSvXKCq2HpATiuK6Tk4FdllX4zT241wHc7GpOtEn+8O+LmYod/fHrH3rjH5fkuNxe7VNUkZ2dHzN7eklQyrEYDPB+VCHudaMUkyaSMUNJy3F5tM1pXmPQq9KtpZofrXE9GfH++i1pWGQ022Rl2GG3VOD3e5/7Te9obVXIJiW4tQ0dLsrOnIcYkBKWYIl9RWC8mOemUWC8kGDeyTLZrTI+6yIkUrVaNRlNjt19nu9tE29ykVMzTbubpNIvE18LI+VVC0a/AVIxxp0RuTaKakcmnY4w3VUrJCKV8mkQmQ7uzzvF+h6aWw+P14PaHyKhVJleXqOUCUclHRU0TCIkIdpsZp92C02airMiERDdqOkosEmRFlJDTGXodjUq1jKKWCERlSvUWvfEehUqdyx/+xu7RIV6PA5fHjdBq5anWUtisloeRRdGD6HOhhD0kZYlSLslqVKLS6DA+OSFVUTl8fsFvv/9BZHWNYrXOt5NzSpUUwXgYIVlLEinIOJ2OB6A/6GXJYmHFYadSyNKsV5AiEYpVjXg+i9pcZ3x8wtZwiMm8TDgSo1xv4IkGcQZ9CKW6glbPsbxsIRjys2hc5PHTb3i6YMLh8pLLZylrNbaGO2TLVUKyTDqXx+X34w4E6YzGnE6/I5NTSGXiCJVSilRcQj9vwB/woZufR3j0mEdzz1i22WjUikTjSRJKnlZviFIsI4VjOPwBluwOImsp2qMxHjGAyWxCWDKbefpMzzc6PX7Rh8m0xKJhnsfP9LicdryiH5PNicPtJSQn2OoPULMKB6dn3L6/I50vEUumsa64MRhNCAajGZ3BiM6wgMfnxWq1PDTpFo24XU7EYIBapYDX6+GJfgGDyYzb40GKyTS2+qhag2S2wKLJwsKSCcHpdjOnMzwAXV43ZrORZdsyTruVmBxlf9Tm9sUeSTmC0b6C1e1hwWRCZ1hk3riExe7AJYrMmy3oFhYR5heXePJVWT+PxWZDEt047DasNjuZXJazwwGz6bcEAiKWFRfOQAi7YwWLZRm704kvGOTR0zkcLhfuYABhTm/giU7P47lniKKPaCSMT/Szms4gx+MMttvUtRIGsxWnGHzI17Mcbh8eKUomq6BfWn5QXjQa+QuwjZivmtkjOQAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Ice Cube - Straight Outta Transactions&quot;
        title=&quot;Ice Cube - Straight Outta Transactions&quot;
        src=&quot;/static/3a7ee15c802b743cc79e76bea7f3f334/f97d7/db-were-people-ice-cube-sql-server.png&quot;
        srcset=&quot;/static/3a7ee15c802b743cc79e76bea7f3f334/0eb09/db-were-people-ice-cube-sql-server.png 500w,
/static/3a7ee15c802b743cc79e76bea7f3f334/1263b/db-were-people-ice-cube-sql-server.png 1000w,
/static/3a7ee15c802b743cc79e76bea7f3f334/f97d7/db-were-people-ice-cube-sql-server.png 2000w,
/static/3a7ee15c802b743cc79e76bea7f3f334/2eb59/db-were-people-ice-cube-sql-server.png 2048w&quot;
        sizes=&quot;(max-width: 2000px) 100vw, 2000px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;h3 id=&quot;4-Microsoft-Access-is-MC-Hammer&quot; style=&quot;position:relative;&quot;&gt;4. Microsoft Access is MC Hammer&lt;a href=&quot;#4-Microsoft-Access-is-MC-Hammer&quot; aria-label=&quot;4 Microsoft Access is MC Hammer permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;The Persona:&lt;/strong&gt; The massive crossover hit that everyone knew, even if the “hardcore” heads didn’t respect it as much.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;The Tech Parallel:&lt;/strong&gt; &lt;strong&gt;“U Can’t Touch This”&lt;/strong&gt; (quite literally, because the .mdb file is usually locked by another user). Access brought data to the masses, putting it on every desktop in the mid-90s. It wasn’t a “hardcore” enterprise engine, but it was flashy, ubiquitous, and sold millions.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 2000px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/9fdfba2545ad094fcbe1b83a1a9cfbc0/d55ca/db-were-people-mc-hammer-access.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 86.80000000000001%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAARCAYAAADdRIy+AAAACXBIWXMAAAsTAAALEwEAmpwYAAADgUlEQVQ4y2WUW2gcVRjH91XwUQq+qPggXvrUh+pLRbHUVmuUXoQgktQ7mKbGKhgDBhWx1ULbrDGkdZvahppmtWl0s5pdUwlNiS1BsN2oqBDMZdvszuzcZ3Zn1/zkfLPLFjxwGJjvO7/z/y7nixVWVykWi2haEV3XKJV0DEPDsnRmLs0xMpohMfQdo8kU+fwy5bCMYWqylW+ppKFrRWEUCgViAtMbRh3T1LFsA8exSafTHDr0CceOD5L9cYrF5RX++GUG1zawHQvTLGEY0TklRtM0Yk1VOpZpYNuWbNd1yc3nOD8+zljyDCcHj5KdOMe3pwa4ODFKOaxiWyaWZYgIdV6xYqVSdIsy2LYt2w8CfD/gr9+vsqd1J+1bN/B5zw6GPn6edzvbaH/mUa6vLIqfbSkBpkCVsFgUZimCOTae77N6I49afQd6ePiBO+h+9Sl+Gu7izKcv8OKuzbz10k5u5JcohyGO48g5yzJFWEzUqTDrhjUgdfY0/b2v09bawn13rqP18fX0vbmZjt0PMZX9nmu/zjEznZVLHdeJoLYtwmKROkt+up5HuVLBdR2mUkmGE/0cfL+XHVteprPtPd7Zd5jkyDCJwSNcnp0WoMq147l1lQaxRu4UUIUblMtUq1Uaa+zcR/TFu7hw8W8mJufIZjJ07n2N+GdxKmGFIAhwBehI2AIUda6L7/uUK2VxrNVUfnzOftVBOvUsqXSSb8a+5lSin3vuvp1bb4lxLTfPGmtyPgrbbCpUQK8ODMOQsBapHDo+wJPb7mf/2y307t/D4j8LfJEYJJP5AdM0xd9RYQvQUjm8KWTPIygHhGEFVysK8PyJfjbetY7utk2cPtgFfgCVitiqtSqe70V5bIQsQCuqssqFykklDNF/y7EyO8PkwFF2bVzP3i0Pkvywh+UrsyxNT2GvXpe2USKaVTZU2xhCblQ6KkwgIS/l81y6/DNPbHuM7VsfIZVOsbC8hBv4cqnyVeEqmGIIUL0UlQtLOt6WvhJoEMjtan3Q3cXuTfey8Oc81dq/+L6HH/hRdW1HIlQMwygR0zX1lv8PdX1PvmoNHztCy4bbmBwfiZpZVdVttEoTJm+5KGOrKD+UQe3Gm1YKdV1n3yvP0dH+NAd634iGQt3ehBnyjmXaFIpFGtBo6pTEQX1VX+ZyV4nHD5NMjnLyyxNUw/AmH6M+uqIRqID/AaMbogm5aR4wAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;MC Hammer - MS Access&quot;
        title=&quot;MC Hammer - MS Access&quot;
        src=&quot;/static/9fdfba2545ad094fcbe1b83a1a9cfbc0/f97d7/db-were-people-mc-hammer-access.png&quot;
        srcset=&quot;/static/9fdfba2545ad094fcbe1b83a1a9cfbc0/0eb09/db-were-people-mc-hammer-access.png 500w,
/static/9fdfba2545ad094fcbe1b83a1a9cfbc0/1263b/db-were-people-mc-hammer-access.png 1000w,
/static/9fdfba2545ad094fcbe1b83a1a9cfbc0/f97d7/db-were-people-mc-hammer-access.png 2000w,
/static/9fdfba2545ad094fcbe1b83a1a9cfbc0/d55ca/db-were-people-mc-hammer-access.png 2212w&quot;
        sizes=&quot;(max-width: 2000px) 100vw, 2000px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;Since you made it this far, you deserve a bonus AI generated song by MS Hammer. Enjoy!&lt;/p&gt;
&lt;div class=&quot;gatsby-resp-iframe-wrapper&quot; style=&quot;padding-bottom: 56.25%; position: relative; height: 0; overflow: hidden; margin-bottom: 1rem&quot; &gt; &lt;iframe src=&quot;https://www.youtube.com/embed/N0v1YoLbITY&quot; title=&quot;AWS - The Swiss Army Person&quot; frameborder=&quot;0&quot; allow=&quot;accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share&quot; allowfullscreen=&quot;&quot; style=&quot; position: absolute; top: 0; left: 0; width: 100%; height: 100%; &quot;&gt;&lt;/iframe&gt; &lt;/div&gt;
&lt;p&gt;For all those database people who also appreciate hip-hop music (who knows how many of us there are?), do these resonate with your instincts? Or is this just a hallucination?&lt;/p&gt;
&lt;h2 id=&quot;Shout-outs&quot; style=&quot;position:relative;&quot;&gt;Shout outs&lt;a href=&quot;#Shout-outs&quot; aria-label=&quot;Shout outs permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;An encore performance of this blog post might be due with my friend Jacob requesting artists like &lt;strong&gt;Redis Man&lt;/strong&gt; and &lt;strong&gt;Postgres Malone&lt;/strong&gt; be covered.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Implementing a component based design system in PowerBI]]></title><description><![CDATA[Implementing a Component-Based Design System in Power BI Standard Power BI headers, buttons, and callouts often feel disconnected from an…]]></description><link>https://mortimer.xyz/powerbi-design-system/</link><guid isPermaLink="false">https://mortimer.xyz/powerbi-design-system/</guid><pubDate>Wed, 11 Feb 2026 12:00:00 GMT</pubDate><content:encoded>&lt;h1 id=&quot;Implementing-a-Component-Based-Design-System-in-Power-BI&quot; style=&quot;position:relative;&quot;&gt;Implementing a Component-Based Design System in Power BI&lt;a href=&quot;#Implementing-a-Component-Based-Design-System-in-Power-BI&quot; aria-label=&quot;Implementing a Component Based Design System in Power BI permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h1&gt;
&lt;p&gt;Standard Power BI headers, buttons, and callouts often feel disconnected from an organization’s broader web presence. If you are building reports for a large enterprise, especially if it is going to be public facing, following a design system is essential for a cohesive user experience.&lt;/p&gt;
&lt;p&gt;In this post, I’ll show you how to move away from manually drawing rectangles and text boxes in Power BI and instead move toward a &lt;strong&gt;component-based architecture&lt;/strong&gt; using a database, HTML/CSS and the ‘HTML Content (Lite)’ visual.&lt;/p&gt;
&lt;p&gt;This post is based on the repository at: &lt;a href=&quot;https://github.com/mortie23/powerbi-web-components&quot;&gt;https://github.com/mortie23/powerbi-web-components&lt;/a&gt;&lt;/p&gt;
&lt;h2 id=&quot;Why-use-a-Database-for-Design&quot; style=&quot;position:relative;&quot;&gt;Why use a Database for Design?&lt;a href=&quot;#Why-use-a-Database-for-Design&quot; aria-label=&quot;Why use a Database for Design permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Hard-coding hex codes and font sizes into every Power BI report is a maintenance nightmare. By storing our UI components in a database table, we gain:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Centralized Control:&lt;/strong&gt; Update a brand color in one SQL table, and all reports refresh to match.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;High Fidelity:&lt;/strong&gt; Recreate complex CSS layouts that the native Power BI UI simply can’t do.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Consistency:&lt;/strong&gt; Ensure every report uses the exact same design System header and padding.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;The-Architecture&quot; style=&quot;position:relative;&quot;&gt;The Architecture&lt;a href=&quot;#The-Architecture&quot; aria-label=&quot;The Architecture permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Postgres:&lt;/strong&gt; Acts as the “Source of Truth” for our HTML snippets, in an enterprise this might be your data warehouse.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Power BI Semantic Model:&lt;/strong&gt; Fetches the HTML strings via a standard SQL connection.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;DAX:&lt;/strong&gt; Dynamically selects the right component based on the report page or context, replacing templated variables where needed.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;HTML Content Visual:&lt;/strong&gt; Renders the code directly on the canvas.&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id=&quot;Setting-up-the-Postgres-Store&quot; style=&quot;position:relative;&quot;&gt;Setting up the Postgres Store&lt;a href=&quot;#Setting-up-the-Postgres-Store&quot; aria-label=&quot;Setting up the Postgres Store permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;First, we create a schema to house our design assets.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;sql&quot;&gt;&lt;pre class=&quot;language-sql&quot;&gt;&lt;code class=&quot;language-sql&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;-- Create a schema for your design system components&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;create&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;schema&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;not&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;exists&lt;/span&gt; design_system
&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;-- Create the reference table&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;create&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;table&lt;/span&gt;
  design_system&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;reference_web_component &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
    component_id &lt;span class=&quot;token keyword&quot;&gt;serial&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;primary&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;key&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; component_name &lt;span class=&quot;token keyword&quot;&gt;varchar&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;100&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;not&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;null&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; component_html &lt;span class=&quot;token keyword&quot;&gt;text&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;not&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;null&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; last_updated &lt;span class=&quot;token keyword&quot;&gt;timestamp&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;default&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;current_timestamp&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;After we run this we see the table created in Postgres.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 1305px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/3e586ee3a8108c5d4d0407b8e8064fe9/2851b/pbi-web-postgres-table.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 37%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAHCAIAAACHqfpvAAAACXBIWXMAAAsSAAALEgHS3X78AAABLUlEQVQY012Q6W6DMBCEeRTClWJs7LUNtomxuXJAQ1Opat7/USqSVlUrzY9vpVnNzgbD7e6WGxIqF3VKZUKgML4Yr7I7QzuVVY+1o9Mqhhn8aF9X8Ec5LkU3YzsGtJ3c+6c9zWD7ojKoarD2crggZfOqIaotjcP1ISIiLkUGdUxEhFlE65jKQPuzGWfWuJTKCEOIaIR5oVpU25jwXcH+CNEHwHMMDuO1W25EHXbo2xETnoJKQSVMRphHeLM+4Ydh28cQ5FznQv+GIBoTDm7E2hHf5tLksil1tweVgc5lk8smA7WB0EGIaPh9zKYQsT1lYIyelro/ZVAnpRTDmjIVIlYo+yJM5Sd/We31HvxvVbCI8Ixr0Q6qP6al3GGGapk83pGyakd45UZ7nM3bxxecYVKLVLVrKwAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;pbi web postgres table&quot;
        title=&quot;pbi web postgres table&quot;
        src=&quot;/static/3e586ee3a8108c5d4d0407b8e8064fe9/2851b/pbi-web-postgres-table.png&quot;
        srcset=&quot;/static/3e586ee3a8108c5d4d0407b8e8064fe9/0eb09/pbi-web-postgres-table.png 500w,
/static/3e586ee3a8108c5d4d0407b8e8064fe9/1263b/pbi-web-postgres-table.png 1000w,
/static/3e586ee3a8108c5d4d0407b8e8064fe9/2851b/pbi-web-postgres-table.png 1305w&quot;
        sizes=&quot;(max-width: 1305px) 100vw, 1305px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;Now in PowerBI we need to get this data (along with our NFL data to create some visuals for the demonstration).&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 806px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/701daf25e89b2dfb3d52fdb09b0cfba5/30577/pbi-web-get-data-postgres.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 31.6%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAGCAYAAADDl76dAAAACXBIWXMAABJ0AAASdAHeZh94AAAA7klEQVQY04WRW26DMBBFWQuB+oHtcRqoI9GqDalEk4JAClSEpPtfxq3sRv0j/TgaaWR55p6J3HaLj8MBn8cjLBEY5+ABBiE4pJR/CCH+JVJKwTkHMgby1syyDFprMMYRxzFWq1UgTdNAkiSLRIYIZVnCWgtjDIgIxhD27xWu8xdOpwHDMGAcR7Rti6Zp0HXdIhFtNnh5e0VRFMjzPNS1tXje7TBdvzGfz7hcZkzTFD7u+x51XS8SaaVRVVWI7d15hz66pkdwvYZWKmzuNfjtvYq7kf1D556CdH47iJASKsvA2UMYwBj7HcZY4N5RfgALDb/+m86y1QAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;pbi web get data postgres&quot;
        title=&quot;pbi web get data postgres&quot;
        src=&quot;/static/701daf25e89b2dfb3d52fdb09b0cfba5/30577/pbi-web-get-data-postgres.png&quot;
        srcset=&quot;/static/701daf25e89b2dfb3d52fdb09b0cfba5/0eb09/pbi-web-get-data-postgres.png 500w,
/static/701daf25e89b2dfb3d52fdb09b0cfba5/30577/pbi-web-get-data-postgres.png 806w&quot;
        sizes=&quot;(max-width: 806px) 100vw, 806px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;h2 id=&quot;Example-components&quot; style=&quot;position:relative;&quot;&gt;Example components&lt;a href=&quot;#Example-components&quot; aria-label=&quot;Example components permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;We are going to see if we can create Power BI reports that fit into the &lt;strong&gt;Gold Design System (AU)&lt;/strong&gt; (formerly the DTA system) design system:&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://gold.designsystemau.org/&quot;&gt;https://gold.designsystemau.org/&lt;/a&gt;, and the associated &lt;a href=&quot;https://github.com/designsystemau/gold-design-system&quot;&gt;https://github.com/designsystemau/gold-design-system&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 726px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/bd992b08fb9b45c58e2a105dc8aaa04e/c04c8/pbi-web-gold.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 33.800000000000004%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAHCAYAAAAIy204AAAACXBIWXMAABJ0AAASdAHeZh94AAABm0lEQVQoz32PS2sTYRSG5yf4A0RRSQqZcb7vO3PJZZJK0lAS75sKCjaNyWTSmWkLof2Ngis3UnTlBdTSJtCCLtrFIzMVly4ezvMeDudwrPdfPvPu00feHh/z4dtXTlZLvp+e8uPsjJ/LJSerFcvzc5YXF/y6vOT31dV/sbanc0bxLq8nM8ZJxniWsjNNmCYJkyRhHMdlnc7nzNKUJMtI0vTaS7LSC+Z5jrXRvkc7uEW3VaEpN+k2b9MMa1Qqa9i1GloplFK4rltS9Gzb5r7j4DhOmZXrYoxhrVrFevViwHhnm+rdGzx9MuTl1iaPNxWOK0RRRJ7vsVgsiKcxWZ5zdHjIm8mELMvY3z9gPBoxGAwYDh9iRLDmow0Odrfod+6Qxs852nvGo77CthWiFSIGMQbRuvQwCPBF8DwP3/PwxKC1/oflhx7GOLSjkGZDaIQaL/AxYQMT1tEi6KBeZm0Myg/QQeGC9kN0EJaLipcLLNPooIMm9XqDXq9PFLXLQel0kegBxg/KKu0uxvOQ1jrS6V33C2+tY/4uLA7+AbMhINt/7MAiAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;pbi web gold&quot;
        title=&quot;pbi web gold&quot;
        src=&quot;/static/bd992b08fb9b45c58e2a105dc8aaa04e/c04c8/pbi-web-gold.png&quot;
        srcset=&quot;/static/bd992b08fb9b45c58e2a105dc8aaa04e/0eb09/pbi-web-gold.png 500w,
/static/bd992b08fb9b45c58e2a105dc8aaa04e/c04c8/pbi-web-gold.png 726w&quot;
        sizes=&quot;(max-width: 726px) 100vw, 726px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;We are going to change it a little to make it fit the common navigation of Power BI dashboards. We have a few options for how we add styles.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Each component is self contained with all styles&lt;/li&gt;
&lt;li&gt;Host styles on a separate web server and import&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;For this example, for simplicity of architecture we will keep the styles self contained.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;html&quot;&gt;&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;html&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;

&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;style&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token style&quot;&gt;&lt;span class=&quot;token language-css&quot;&gt;
  &lt;span class=&quot;token selector&quot;&gt;body&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;margin&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 0&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;font-family&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; -apple-system&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; BlinkMacSystemFont&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Segoe UI&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; Roboto&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; Helvetica&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; Arial&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; sans-serif&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;background&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; #f5f5f5&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;token selector&quot;&gt;.au-header&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;background&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;linear-gradient&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;to right&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; #313131&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; #585858&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;color&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; #fff&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;padding&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 1.5rem 1rem&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;border-bottom&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 1px solid &lt;span class=&quot;token function&quot;&gt;rgba&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;255&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; 255&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; 255&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; 0.1&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;token selector&quot;&gt;.au-header__heading&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;max-width&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 1200px&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;margin&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 0 auto&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;font-size&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 2rem&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;font-weight&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 700&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;line-height&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 1.25&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;token selector&quot;&gt;.header__badge&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;display&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; inline-block&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;font-size&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 0.875rem&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;border&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 1px solid &lt;span class=&quot;token function&quot;&gt;rgba&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;255&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; 255&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; 255&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; 0.5&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;border-radius&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 2rem&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;padding&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 2px 10px&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;margin-left&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 8px&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;vertical-align&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; middle&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;text-transform&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; lowercase&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;letter-spacing&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 0.05em&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;token atrule&quot;&gt;&lt;span class=&quot;token rule&quot;&gt;@media&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token property&quot;&gt;max-width&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 768px&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token selector&quot;&gt;.au-header__heading&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token property&quot;&gt;font-size&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 1.5rem&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;style&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;

&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;header&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;au-header&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;role&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;banner&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;h1&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;au-header__heading&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
    {{TITLE}} &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;span&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;header__badge&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;beta&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;span&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;h1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;header&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;

&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;html&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Note the inclusion of &lt;code class=&quot;language-text&quot;&gt;{{TITLE}}&lt;/code&gt; within the H1 tag. This will be utilized later in conjunction with DAX to make the component reusable.&lt;/p&gt;
&lt;p&gt;Let’s quickly view this component in a browser to see that it is rendering how we would like. For this I simple use the &lt;strong&gt;Live Server&lt;/strong&gt; extension in VScode.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 860px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/f97213d6ac8d3a98a21c82b33e129fcb/3e3fe/pbi-web-live-server-component.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 29.000000000000004%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAGCAIAAABM9SnKAAAACXBIWXMAAAsSAAALEgHS3X78AAABJElEQVQY02XK2VKCUACAYd4jJUbbJBZRCs0F0GMeRaEDIoxLcmGPnIwLnINW1ls0eGMzzXxX//wUy0oQ2iM3sO0ZQhOEppL0lLnIZTP5v+jsFU1fM8ztJX3DMIV8js3lWYrjZbYgFUWF58qiIIuiXC5XReGBuy/x/KkIcuFOkEtVqahUFLWlw3oDVCua8qhSpmmZptUGQNO0RqNZq9WbTVXX9VarDUCn03nudmGv1zMMYzgcmqb1gpBjO6OROx57VBAEy7el7/sDw4AQAgC6p73f7w8G6W9ZFkLIcRzXdT3P831/Mp3O5vPXxYIKw/dwtdps1rvtNtrt4jiK4winYoxTJIUJSSWEJElqnyQYY2r/+ZN8fJPD8eTrbH+G/yGHY7iOfgHth6Rpv4g1TAAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;pbi web live server component&quot;
        title=&quot;pbi web live server component&quot;
        src=&quot;/static/f97213d6ac8d3a98a21c82b33e129fcb/3e3fe/pbi-web-live-server-component.png&quot;
        srcset=&quot;/static/f97213d6ac8d3a98a21c82b33e129fcb/0eb09/pbi-web-live-server-component.png 500w,
/static/f97213d6ac8d3a98a21c82b33e129fcb/3e3fe/pbi-web-live-server-component.png 860w&quot;
        sizes=&quot;(max-width: 860px) 100vw, 860px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;Great, this is just what we want. Now, to insert this into our Postgres table:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;sql&quot;&gt;&lt;pre class=&quot;language-sql&quot;&gt;&lt;code class=&quot;language-sql&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;insert&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;into&lt;/span&gt;
  design_system&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;reference_web_component &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;component_name&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; component_html&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;values&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;token string&quot;&gt;&apos;Gold Header&apos;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;&amp;lt;html&gt;
&amp;lt;style&gt;
    body {
        margin: 0;
...
&amp;lt;/html&gt;
&apos;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;See we have given the component a &lt;strong&gt;name&lt;/strong&gt; in the table, we will use this later in the DAX.&lt;/p&gt;
&lt;h2 id=&quot;Use-of-the-HTML-components-in-Power-BI&quot; style=&quot;position:relative;&quot;&gt;Use of the HTML components in Power BI&lt;a href=&quot;#Use-of-the-HTML-components-in-Power-BI&quot; aria-label=&quot;Use of the HTML components in Power BI permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;We need to use the HTML Content (Lite) visualization from the Viz store.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 982px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/4ea8e4de7cd4e34ee85a301e87ac730a/9e029/pbi-web-html-lite-viz.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 71.00000000000001%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAOCAIAAACgpqunAAAACXBIWXMAAAsSAAALEgHS3X78AAACRElEQVQoz32SS2sVMRiGZ+PC/+BKBNfuuvAnuFBQQRBKsaBbF0rdtKIWXapFEMGNVnAhiqiIWOjx0HqoCqWb9tjL3Gc6k8ntzEwmmVuiOa0ItvXNm/Bm8ZAvX2JcnFq4cu/r6M0v5ybmxu90x24tjE/3xm4vXprunZ3onL42f+b6jjt7bfiArTvZhi8CpHykfCBCwAJQBIB58a7dmLlRvteGUkqKn5Itqrynim+qIVIpKRulpaOSrfZ+0jDunlyfPOTfPxY8NHg0i6gMfAdjsh1F21EEIQIgaVsppfrHGiadkY0HF+jnp9HMYQ5ekFxBlOaMCSHqulYHawgvjqxOHd9+NhE8MgR8zhlJwZIo4j+V/xdurdH841H66kTVO9LSD1GMfXdVCEazKiuqQVayoj4QftvZevx6ZfbT6szL5XWbDCgJwogxxrnIGWesKArOOS8KLoTgOos8z6uq0vDkk/75Gz8u3105dXVp7jvIKPA833M9y7Jc13Uc27Z1CHzfcRzPdU3TDMOwldKQSnWX4Zv54F03eL8QejELfKff729ubq6trdm27diOZjzP933T3DJNy7Isz/M454aSMsuypvl7qyzLKKXpIE2zjFCCMaZ0QIeTEL2tqqqu66Zphu+MScGF7lwrm6YFAMQ7ivQCYpAMBQBI9NABIiSl1HAQggiQqtbfSEpJKUEIYYwhhHioQZpWZVmVYuhKn9w0u90e6KJIwVjb6lclBAMAMEIgjpMkQRACAGOAIUoTSBOIkiQpy/I3/AvLAud6FdtaZwAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;pbi web html lite viz&quot;
        title=&quot;pbi web html lite viz&quot;
        src=&quot;/static/4ea8e4de7cd4e34ee85a301e87ac730a/9e029/pbi-web-html-lite-viz.png&quot;
        srcset=&quot;/static/4ea8e4de7cd4e34ee85a301e87ac730a/0eb09/pbi-web-html-lite-viz.png 500w,
/static/4ea8e4de7cd4e34ee85a301e87ac730a/9e029/pbi-web-html-lite-viz.png 982w&quot;
        sizes=&quot;(max-width: 982px) 100vw, 982px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;Now let’s render out some of our components.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 1860px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/de797da9ae4c09ad59dc51bba17f1108/4e22f/pbi-web-html-render.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 55.60000000000001%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAALCAYAAAB/Ca1DAAAACXBIWXMAABJ0AAASdAHeZh94AAACaElEQVQoz3WRy08TURSH508jQEIgDYtCCCF1oQsKwrTlsdOFiUvjHyAxJEZdGRNdGFkZXCAopTxKbSxipxBKp9N2Ou1MpzN3Zj7TS+Ij0ZN8OY977v3de64yMTFB4laCqXickZERhoaGGB4eZnR0lLGxMcbHxxn0xGIxJicnicfjkqmpKaanp5mZmWF2dpa5uTkSiQTK3t4e2WyWk5MTjo6OJPl8nlwuR3Z/X3J4eMjx8bGs7e5+kr5QKFAqlX5xdnYmUTzP418WhSG27dDp2kRRSBRFeL7PtW7Q/8+egSmu6+I4Dp4QiDDCcfsEEURAX4T0RSTjMAwJw4Be3ycIQpkPREIpNsgjWVNs28Z2HKyGjnldwTXrCNuUBE4b36zhNas4nTYts43jdOl2LKx2G7Nl4vZ6BCJECI8g8FGCQOB6Pvr2S7TNNS5e3EfbXKf8dEVS//CMVvYdbkuXN/2yXeWqYgEBvicIhJBeCCHXlYGC6wdcvn7E1wdjFB/GaO69oa9r9C6LhOJmXkIE0n/eKdA0OjdzjiLJjbn0OkeDJ3cRIVTfP+HHRorK83v0rr///hz5QQFC+DI/PDjB6joyHsx00NGq+WhFjfzBYxSpHoG+tYFd3PnjmL/N8+GsCh+3DC6Oz3G0Erm35zi5GrW8zVVZp1F9hdLtGFhtnUalQEvX6NotzGYVo34p6x2rjt2p0zCuOS1VMYwazatvGNopV2WNRlnDalaxbR3LrKGsr6msr6pkVhZJq3dYW7lLajnJQvI26vI8aXWBlJoknUqympkntTxPKrVIOrNEOrNAKrOAqiZRl+Zl/0/4mvPEHgTcbwAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;pbi web html render&quot;
        title=&quot;pbi web html render&quot;
        src=&quot;/static/de797da9ae4c09ad59dc51bba17f1108/4e22f/pbi-web-html-render.png&quot;
        srcset=&quot;/static/de797da9ae4c09ad59dc51bba17f1108/0eb09/pbi-web-html-render.png 500w,
/static/de797da9ae4c09ad59dc51bba17f1108/1263b/pbi-web-html-render.png 1000w,
/static/de797da9ae4c09ad59dc51bba17f1108/4e22f/pbi-web-html-render.png 1860w&quot;
        sizes=&quot;(max-width: 1860px) 100vw, 1860px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;The HTML Content (Lite) visual renders the components nicely. But now it is time to make use of the templating we built into the components.
We create a new measure that replaces the &lt;code class=&quot;language-text&quot;&gt;{{TITLE}}&lt;/code&gt; with our own custom title for each page.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;h&quot;&gt;&lt;pre class=&quot;language-h&quot;&gt;&lt;code class=&quot;language-h&quot;&gt;TitleBar = 
   
   VAR BaseHTML = LOOKUPVALUE(&amp;#39;design_system reference_web_component&amp;#39;[component_html], &amp;#39;design_system reference_web_component&amp;#39;[component_name], &amp;quot;Gold Header&amp;quot;)
   VAR Title = &amp;quot;NFL Quarterbacks (2014)&amp;quot;
   
   RETURN
   SUBSTITUTE(
         BaseHTML, 
         &amp;quot;{{TITLE}}&amp;quot;, Title
       )&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Now let’s set the visuals to align with the colour accents in the visuals.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 1851px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/4d3e398b955c5b9a4e9bb4d9bc83d8ee/eb183/pbi-web-html-render-dax-values.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 55.60000000000001%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAALCAYAAAB/Ca1DAAAACXBIWXMAABJ0AAASdAHeZh94AAACSElEQVQoz32SS0tbYRCG83P6FxQVQYm3RptCV4Va6MZS1N7UNFJK6S/oposuu80mAc0i0Ri709JaBAMSpWhrozHmXL5zTs53bk85X2xFaDvwMjMvM8M7zCR6enuZmJhgfHycyclJbqZSyg8PD9PX10d/fz8DAwMMDg4yNDREMplkZGSE0dFRxsbGVG8qlWJqaop0Ok0il8tRLBYpl0qUy2VKpRJra2usrq5SKOQp5POsrKyoPPaFQkHFcd36+jqVSkVhY2ODarVKwvMkf7MwDLEsG1NYf7iO63J6eqb8vyyh6zq6rmFZFlJ6uK4kCEJc6aOZDrrp4MqYC7A7EsNysDsdwjC4hiDwVU3CNE2EEAhhYujnmEaLIIgbXKJIEoWxmgjHcbCEiR0PtCXCcnEcX8Ud11fqoigi8Xu1VusErVml3axgtDYxWh/RzysIbVutb1u2Um+LJnq7jtbaQ+g1HOuQjn2sBKiVY6nxqrE6y9jF0ncQWheWvovvmURRrBGkc8Bx7RHNw1lyH27zfLaHzeJdTusP+bG/TOALErZt4fu+Gij0rwjtsxpmtrdxOz8vD+QThODahxztzdHYv8/7t0nu3LpBMZemsT/NSf1Nd2AYRkjp026fYVx8Qm9tYVxsYWo7BIEkDFFHklISRuB5grOjd9S/3KO2Pc233Qe0G3mCIFK1iauDx+9zoRB6DYh0/mvBAZFXg+j79bfJZuZ5/WqRzNIcmaXHvFxeUP7J/AzPns6QzcwR11yhmy9nF3mRWWA5u3DJdflfLQPtR8eJvhwAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;pbi web html render dax values&quot;
        title=&quot;pbi web html render dax values&quot;
        src=&quot;/static/4d3e398b955c5b9a4e9bb4d9bc83d8ee/eb183/pbi-web-html-render-dax-values.png&quot;
        srcset=&quot;/static/4d3e398b955c5b9a4e9bb4d9bc83d8ee/0eb09/pbi-web-html-render-dax-values.png 500w,
/static/4d3e398b955c5b9a4e9bb4d9bc83d8ee/1263b/pbi-web-html-render-dax-values.png 1000w,
/static/4d3e398b955c5b9a4e9bb4d9bc83d8ee/eb183/pbi-web-html-render-dax-values.png 1851w&quot;
        sizes=&quot;(max-width: 1851px) 100vw, 1851px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;Now, let’s embed this in our public facing website.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 418px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/b3573ee39ee3dd81f477075aa3928922/2bbac/pbi-web-embed.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 87.79904306220097%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAASCAIAAADUsmlHAAAACXBIWXMAAAsSAAALEgHS3X78AAAC/UlEQVQ4y32SyW4bRxCG5w100V0SLYI0X8zIwUgcCxRMi7EEJHQCw7lJ4oPELxD4IAESkQSJJVrkbDTZ0zO9b7NSnWgm1CnId6iuRqOqq/4qx5vPvPks8N0YQiYkwRhjIqVkXACIAFhBCJMkgRACACIAIIRBEDRXZ3r76fffJnfTmwgAwpXvuYEfSiGiGIUAU4KFkFprxlgcx4RSbYzrupTSOEEOjrHgnBIihTBGV1W1XldFUVhrP3789f37n8fj8enp6dnZ2Xg8Pj8/2zjn4/HYYUvOGRcbbE1ZltbaH0ajra2tVqu1u7u7t7e3s7PTbu8/edJqtVqdTmd/f98p1lbWCCGklJRSpVST4uLi8s3x8YtvXvT7B9+fnLwevHr79t3J8cnBy29/evfjYHDkZEWlpGSMUUp5jTHm/v7eWvvLhw/9w8Ph0fCwf/DVs2ffDYfPn389GAxeD14d9F8eDd84cQwJIUmSYIwppYQQhJCU0lo7Go22t7e73W673e7U9mm32+l0ut1u72mv1+s5UkpjjKrJ8zzLMmNM07Pv+xeXl5PJ5Pr6+urq6uGomWxwwjBACEVRRAgJw5AxZq1tym7s/+AURbFer+9rqqoqa6qaLMuqjf9IsaEsS4cQAiFcrVZRDaX0QXqlpBAxpbEQUgiMMWOsmUWe501RWZY5jLFm+5IkAQAopcqyTLNMC7nEOCDUaE3rSKU1F6IoSlt3k6apAwBoRG6mhRDCGGdZZq2VSiIY4wQRhCTniguKcV4//RsshNBac87rYpXWWghRlGVFOPcWeOomnz4nf07x7Yzc3MV/3Cj/SxGhyqRpljllWeZ53gjQCLa21oRA3bqR63lBsIiiBVhBjJYQLmPoeZ5/O6V/fZaIOK7rxnEchiGl9HE8a23WUgs3ZPMFmfl47jMvJG5AZh6eB8xbpMHKSOU0BT/udoPSSkjJlebSgLn75W6+cj3gBf7NNHL9eLGsezbOwz//SVVppR4SpUanRhmtjJZaK2OU1oLzfwT7G/dDmk8M2DXzAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;pbi web embed&quot;
        title=&quot;pbi web embed&quot;
        src=&quot;/static/b3573ee39ee3dd81f477075aa3928922/2bbac/pbi-web-embed.png&quot;
        srcset=&quot;/static/b3573ee39ee3dd81f477075aa3928922/2bbac/pbi-web-embed.png 418w&quot;
        sizes=&quot;(max-width: 418px) 100vw, 418px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;div class=&quot;gatsby-resp-iframe-wrapper&quot; style=&quot;padding-bottom: 59.765625%; position: relative; height: 0; overflow: hidden; margin-bottom: 1rem&quot; &gt; &lt;div class=&quot;container&quot;&gt; &lt;iframe title=&quot;pbip-nfl&quot; src=&quot;https://app.powerbi.com/view?r=eyJrIjoiZmJkYjIyZTgtMTkwZC00YmQ2LTg4YTgtMDIyZjg5MWYyNTdmIiwidCI6ImQ2M2U3OGJkLTllZTMtNGE3ZC1hODY4LTczODgyNGMxM2JkMSJ9&quot; frameborder=&quot;0&quot; allowfullscreen=&quot;true&quot; style=&quot; position: absolute; top: 0; left: 0; width: 100%; height: 100%; &quot;&gt;&lt;/iframe&gt; &lt;/div&gt; &lt;/div&gt;</content:encoded></item><item><title><![CDATA[Power BI PBIP Git version control]]></title><description><![CDATA[How to commit a Power BI report to Git The files from this blog post can be accessed here: https://github.com/mortie23/pbip-nfl/pull/…]]></description><link>https://mortimer.xyz/pbip-git-version-control/</link><guid isPermaLink="false">https://mortimer.xyz/pbip-git-version-control/</guid><pubDate>Sat, 31 Jan 2026 12:00:00 GMT</pubDate><content:encoded>&lt;h1 id=&quot;How-to-commit-a-Power-BI-report-to-Git&quot; style=&quot;position:relative;&quot;&gt;How to commit a Power BI report to Git&lt;a href=&quot;#How-to-commit-a-Power-BI-report-to-Git&quot; aria-label=&quot;How to commit a Power BI report to Git permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h1&gt;
&lt;p&gt;The files from this blog post can be accessed here: &lt;a href=&quot;https://github.com/mortie23/pbip-nfl/pull/1/changes&quot;&gt;https://github.com/mortie23/pbip-nfl/pull/1/changes&lt;/a&gt;&lt;/p&gt;
&lt;h2 id=&quot;What-is-the-problem&quot; style=&quot;position:relative;&quot;&gt;What is the problem&lt;a href=&quot;#What-is-the-problem&quot; aria-label=&quot;What is the problem permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;In the past, a PBIX file was all we had for saving a PowerBI report. Under the hood, it is a series of text files archived into a single file, but tracking these was cumbersome (see &lt;a href=&quot;https://mortimer.xyz/pbix-git-version-control&quot;&gt;Power BI PBIX Git version control&lt;/a&gt;).&lt;/p&gt;
&lt;p&gt;It has been a while since I was seriously working on solutions with Power BI and it seems a lot has changed.&lt;/p&gt;
&lt;h3 id=&quot;Power-BI-June-2023-Feature-Summary&quot; style=&quot;position:relative;&quot;&gt;Power BI June 2023 Feature Summary&lt;a href=&quot;#Power-BI-June-2023-Feature-Summary&quot; aria-label=&quot;Power BI June 2023 Feature Summary permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;In June 2023 Microsoft announced the PBIP format:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://powerbi.microsoft.com/en-us/blog/power-bi-june-2023-feature-summary/#post-23927-_Toc137015481&quot;&gt;Power BI Desktop Developer Mode &lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;Microsoft-Power-BI-Updates-Blog-November-2025&quot; style=&quot;position:relative;&quot;&gt;Microsoft Power BI Updates Blog November 2025&lt;a href=&quot;#Microsoft-Power-BI-Updates-Blog-November-2025&quot; aria-label=&quot;Microsoft Power BI Updates Blog November 2025 permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;In November 2025, this type of format became the default for the PowerBI Service.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://powerbi.microsoft.com/en-us/blog/pbir-will-become-the-default-power-bi-report-format-get-ready-for-the-transition/&quot;&gt;PBIR will become the default Power BI Report Format – Get Ready for the Transition&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;No longer do developers who want to track changes to their Power BI reports need to have complex Git hooks and scripts to expand and archive back the files to and from PBIX. We now have PBIR and PBIP formats.&lt;/p&gt;
&lt;h2 id=&quot;How-have-I-found-using-it&quot; style=&quot;position:relative;&quot;&gt;How have I found using it?&lt;a href=&quot;#How-have-I-found-using-it&quot; aria-label=&quot;How have I found using it permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;It is a super welcome change. Exactly what we have been needing in the Power BI development community.&lt;/p&gt;
&lt;h2 id=&quot;Example&quot; style=&quot;position:relative;&quot;&gt;Example&lt;a href=&quot;#Example&quot; aria-label=&quot;Example permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;First let’s get some data. I have a GCP Compute Engine VM in which I installed a Postgres.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;This is just because it is easier for me to source data this way than any other way (including Excel files)&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;I’m using a Windows laptop, so to make the server address simple I’ll add an entry to the hosts file. Using vim from PowerShell in a terminal opened with Administrator rights.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;ps1&quot;&gt;&lt;pre class=&quot;language-ps1&quot;&gt;&lt;code class=&quot;language-ps1&quot;&gt;vim C:\Windows\System32\drivers\etc\hosts&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;ini&quot;&gt;&lt;pre class=&quot;language-ini&quot;&gt;&lt;code class=&quot;language-ini&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;# Copyright (c) 1993-2009 Microsoft Corp.&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;#&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;# This is a sample HOSTS file used by Microsoft TCP/IP for Windows.&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;#&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;# ...&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;# Postgres on GCP&lt;/span&gt;
&amp;lt;ip-address&gt; thefog&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Now, in PowerBI let’s connect to this Postgres database an import our tables.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 583px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/2a1d3077ecea013377791ba35c06cb31/6d62d/pbip-postgres-get-data.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 74%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAPCAYAAADkmO9VAAAACXBIWXMAABJ0AAASdAHeZh94AAABuElEQVQ4y52Tu47qMBCG/RyIW5ULoJCQ2LkHzCbLLruIho4CGoQEBaLjBaiQaHjg/2jMASGtziY6xcj2yP484/83+/r+wnK5hOM40HUdpmmqMAzjOf8tHvtopGCWZSkYLQio6Rp0Q/8vKAULwxDcF4iiEL4nMMwycNetDO10Omi1Wtjv95jP52C9Xg9SSuRFjsnkA4JzaJpWuX0CNptNnE4nLBYLMM4FuBDwfQHhcQS+D0E5z4NhGpUqbLfbOBwOmM1mYPR+cjxG8V5g8j5BFAawrT76fatyywQ8Ho/3ll3XBRf8WSG1TJfYtgOTlCsR5AfQ8zyM/r5h8VYgTWJwzuG6XiWVf7RMoviBjzAKEUcR3MFAbaI8jWXQbreLer2O8/mM9XpNtokwlCOM38aQQ4lRlilhojBS0CrAWq2G6/WK7XZ7rzAIAkRxjCRJ4Ng2dP1u8tdf8C9DP4CXywWbzQYsjmPEaYIsS5GEMfKRhBDi6cPfKnwF3m437HY7MEqmWaqsk+c5Bo6jjF0F9pg3Gg2sVitMp1MwSqg/rGkqytp8DTqnfCylEpbWrOz2MiD5+PNzqj4G6fEHXnnYTLsinkkAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;pbip postgres get data&quot;
        title=&quot;pbip postgres get data&quot;
        src=&quot;/static/2a1d3077ecea013377791ba35c06cb31/6d62d/pbip-postgres-get-data.png&quot;
        srcset=&quot;/static/2a1d3077ecea013377791ba35c06cb31/0eb09/pbip-postgres-get-data.png 500w,
/static/2a1d3077ecea013377791ba35c06cb31/6d62d/pbip-postgres-get-data.png 583w&quot;
        sizes=&quot;(max-width: 583px) 100vw, 583px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;I’m going to create a really simple report just to demonstrate the PBIP. A single bar chart with total touchdowns by player.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 743px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/e4abf25e2254e606bb9d23ed99b7ef71/46468/pbip-chart-test.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 59.4%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAMCAYAAABiDJ37AAAACXBIWXMAABJ0AAASdAHeZh94AAACEUlEQVQoz22Ty08UQRDG9981HohwQHyEh+LNAyYKxgUFhAMiiIkxMSFBZYPhoFcTLpp9zO7OsDuzOzPM9EzXz/T07LIhVvKluiupr+ur6qo0m008z8P4er1Oq9Wi3W7jdlp0nD8MA5ee2yQceFiT/0CTJClKpVSYsDRNCcMIdMrPC3h/Ch/PLI7O4PBUOKwxxlEN9k6E8982P8vya8Isy+j3+3S7HlfhJbvHObdWhJk14c6qxfQNzLwUbj8Tjn9JWVBGxRAZi6MI3/fp9X3UVcC7rznTq3B/Q7i3PgkKb+Kzr2DlQEiUJVQqo+I4zliyUopez+cq7HHwPWe2CotbwvwmzG9KCXte2BKmnsOnH7aPWluVFdd10VoX/RsOh7jeJUns86Gmi2oevRWWtmFpW1jctmfjl3eEzS/wt2MJRUrCRqNeEMZxTBiGBWEa99n/pplegwevjbxS+oY9z61b8jihrE7Gc6gEQYCITAwmQCUBeyeaqRc22UifrZqeCXNVikE93RdSRVndBGERKgNFha5HGvscnmruVq3UhS2KnhmY+1xV+Hxuc3J9g9BcRoEoiuh23YLwqJbz8A082RUe7xjA8q6FeeCiYQdZqrWE6kaFxpt+itYMI0UQwSCGQSQMInD7CX6oi3uuZbwMo69ncscfe0Q6Wi6zTterZaof0Gm38NwObadB23GKQYrogmhk/wCp9We6jP81hgAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;pbip chart test&quot;
        title=&quot;pbip chart test&quot;
        src=&quot;/static/e4abf25e2254e606bb9d23ed99b7ef71/46468/pbip-chart-test.png&quot;
        srcset=&quot;/static/e4abf25e2254e606bb9d23ed99b7ef71/0eb09/pbip-chart-test.png 500w,
/static/e4abf25e2254e606bb9d23ed99b7ef71/46468/pbip-chart-test.png 743w&quot;
        sizes=&quot;(max-width: 743px) 100vw, 743px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;Save as PBIP&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 884px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/9e3fefee8800b7f2fcdbaee11009cfb5/2b3a9/pbip-save-as-pbip.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 67%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAANCAIAAAAmMtkJAAAACXBIWXMAAAsSAAALEgHS3X78AAABlklEQVQoz4XRbW6jMBAGYC6RYBt/jA22wRBoN2ljEiABKaoUdZXt/S+zwihVWmm7jyz5x4w1rzzRtk6VUpwLIThAuAIR8DsA4F8BQMTulILnjczz3BhTFIXW2jlHKU2SRAghpRRCAABjjBBCKRVCRMaYuq6lhKraFEUh/uExy267zWzBNj7iFHb1oFIgmBBCMMacC0KIUgoA4jgmDzDGSqmyLLXWJEkikhBXO+dKay2lFCFEGWeU7J5MlmUAgAIcIIQA5oxVVSmlIs5Zu/9ljOGcL00IYZrg9sW4QOuMMXYvIQFQBCBlxFhy8A2ARAjd0yWpgq5tnCvz3GZpyjnHGC+xpZRlME9mLDm2T4zx9XodB2EIAjF/EaV0tVp9luI4xhjTAGMc5dbsnktrc2utcy4PS9Jal2W1CZqmqe+WRS4NxpjIe3/s+uv1+v7+++PPx+12ewvG8Xz60TRNkff7Y3ccTsPQD59Ow9B6v/+R936e7B/6Xl/ns9S+ab+aH3d9d7lcpnHy/3M4HM7nc9u2Xd9P49h13V/gI33YE0/TUQAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;pbip save as pbip&quot;
        title=&quot;pbip save as pbip&quot;
        src=&quot;/static/9e3fefee8800b7f2fcdbaee11009cfb5/2b3a9/pbip-save-as-pbip.png&quot;
        srcset=&quot;/static/9e3fefee8800b7f2fcdbaee11009cfb5/0eb09/pbip-save-as-pbip.png 500w,
/static/9e3fefee8800b7f2fcdbaee11009cfb5/2b3a9/pbip-save-as-pbip.png 884w&quot;
        sizes=&quot;(max-width: 884px) 100vw, 884px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;sh&quot;&gt;&lt;pre class=&quot;language-sh&quot;&gt;&lt;code class=&quot;language-sh&quot;&gt;&lt;span class=&quot;token function&quot;&gt;git&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;add&lt;/span&gt; &lt;span class=&quot;token builtin class-name&quot;&gt;.&lt;/span&gt;
&lt;span class=&quot;token function&quot;&gt;git&lt;/span&gt; status&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Look at all the files that were created. Tremendous. Lots of things to explore and edit.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;sh&quot;&gt;&lt;pre class=&quot;language-sh&quot;&gt;&lt;code class=&quot;language-sh&quot;&gt;On branch master

No commits yet

Changes to be committed:
  &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;use &lt;span class=&quot;token string&quot;&gt;&quot;git rm --cached &amp;lt;file&gt;...&quot;&lt;/span&gt; to unstage&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        new file:   .gitignore
        new file:   pbip-nfl.Report/.platform
        new file:   pbip-nfl.Report/StaticResources/SharedResources/BaseThemes/CY25SU12.json
        new file:   pbip-nfl.Report/definition.pbir
        new file:   pbip-nfl.Report/definition/pages/92dc9803368342374bcd/page.json
        new file:   pbip-nfl.Report/definition/pages/92dc9803368342374bcd/visuals/287fab4078bc01b5a7ad/visual.json
        new file:   pbip-nfl.Report/definition/pages/pages.json
        new file:   pbip-nfl.Report/definition/report.json
        new file:   pbip-nfl.Report/definition/version.json
        new file:   pbip-nfl.SemanticModel/.pbi/editorSettings.json
        new file:   pbip-nfl.SemanticModel/.platform
        new file:   pbip-nfl.SemanticModel/definition.pbism
        new file:   pbip-nfl.SemanticModel/definition/cultures/en-AU.tmdl
        new file:   pbip-nfl.SemanticModel/definition/database.tmdl
        new file:   pbip-nfl.SemanticModel/definition/model.tmdl
        new file:   pbip-nfl.SemanticModel/definition/relationships.tmdl
        new file:   pbip-nfl.SemanticModel/definition/tables/DateTableTemplate_773ad8ec-7f97-41c8-b011-1889725e1c74.tmdl
        new file:   pbip-nfl.SemanticModel/definition/tables/LocalDateTable_369ec58b-5270-409d-b6c5-f495eab467bb.tmdl
        new file:   pbip-nfl.SemanticModel/definition/tables/LocalDateTable_52994651-a681-4c39-b4a3-e5451d63e1ba.tmdl
        new file:   pbip-nfl.SemanticModel/definition/tables/LocalDateTable_80eb41b8-b599-42df-8492-fa5f11fe19ff.tmdl
        new file:   pbip-nfl.SemanticModel/definition/tables/LocalDateTable_9e34a27d-0d33-4a72-87b6-8463bf2c84ca.tmdl
        new file:   pbip-nfl.SemanticModel/definition/tables/LocalDateTable_ac434e9a-c556-4da1-90c7-bfcccc08448a.tmdl
        new file:   pbip-nfl.SemanticModel/definition/tables/LocalDateTable_baf0fdc5-35be-4651-95e7-04e98cc5941a.tmdl
        new file:   pbip-nfl.SemanticModel/definition/tables/LocalDateTable_c0e78857-80b9-451f-ab97-b4c962f02c0c.tmdl
        new file:   pbip-nfl.SemanticModel/definition/tables/LocalDateTable_e8be3960-bc06-4306-bc8d-e471476af1cd.tmdl
        new file:   pbip-nfl.SemanticModel/definition/tables/mortimer_nfl game.tmdl
        new file:   pbip-nfl.SemanticModel/definition/tables/mortimer_nfl game_stats.tmdl
        new file:   pbip-nfl.SemanticModel/definition/tables/mortimer_nfl game_type.tmdl
        new file:   pbip-nfl.SemanticModel/definition/tables/mortimer_nfl game_venue.tmdl
        new file:   pbip-nfl.SemanticModel/definition/tables/mortimer_nfl player.tmdl
        new file:   pbip-nfl.SemanticModel/definition/tables/mortimer_nfl team_lookup.tmdl
        new file:   pbip-nfl.SemanticModel/definition/tables/mortimer_nfl venue.tmdl
        new file:   pbip-nfl.SemanticModel/definition/tables/mortimer_nfl weather.tmdl
        new file:   pbip-nfl.pbip&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Now let’s make a change to the basic report we have created.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 1500px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/c5d76fe6413103ebd32d7bc943656573/6ffd1/pbip-chart-test-change.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 29.599999999999998%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAGCAYAAADDl76dAAAACXBIWXMAABJcAAASXAFoxDaJAAABgElEQVQY01XPTWsTURjF8XxNVypSKAiKuqpUkFaxiwoNKhVqqGipQkVBcOkXsJuWWGxNJlMnM5lkWm/zOnfuvbnPX2bajYsHnsXhxzk1pZTLsoFL+2fu8PjcHRwr1+k5F2W4MMWkyjKc/T1MBslF1IvoxIHEJwckRz8Y/vlF0e+Q91rowSnOOWoAOp8QpROe7MJSAx5WJ9zbRPZPfJn46bxXuc6Js1jCvVXC+g2CVwsELxf4vX6N7Pt2SVErCiPWaInSsazviay8F3m6K7Kyg2x8Fm8szK1pTidTVTYYj8aSfK0TvL5N2HhA2LhPuHUXnQaXoNYFpsjpphPWPsKjt8Ljd8LyttBOpKxHrovmZDxWxhhGo6HEX55XzTpbd2jXb6L2v1WYn19NNsWM03jE6g4svbmc/ewDTDUV6Jxrioiy1qKUku6nNdob1+lsLhK8uEUeHf0Pej9HF5a+EnrnQrs7I+7nJeS994hIBZa/tVYK1cecRYy6LS66LYzOucrxD2jhq+4X455mAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;pbip chart test change&quot;
        title=&quot;pbip chart test change&quot;
        src=&quot;/static/c5d76fe6413103ebd32d7bc943656573/6ffd1/pbip-chart-test-change.png&quot;
        srcset=&quot;/static/c5d76fe6413103ebd32d7bc943656573/0eb09/pbip-chart-test-change.png 500w,
/static/c5d76fe6413103ebd32d7bc943656573/1263b/pbip-chart-test-change.png 1000w,
/static/c5d76fe6413103ebd32d7bc943656573/6ffd1/pbip-chart-test-change.png 1500w&quot;
        sizes=&quot;(max-width: 1500px) 100vw, 1500px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;We can clearly see the diff is only to the &lt;code class=&quot;language-text&quot;&gt;visual.json&lt;/code&gt; file and is an addition of an objects properties adding a color from a theme.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 1182px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/7980784dd3cc84cc1c3deaad1d6f1425/312cc/pbip-change-chart-diff.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 59.00000000000001%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAMCAYAAABiDJ37AAAACXBIWXMAABJ0AAASdAHeZh94AAACJklEQVQoz42SW0/bQBCF/TPKLYlj79Vrrx0ntA0RIJq+5OIkfQMKobRFqlSJ///0VetAVQQPfTjSSrPz6cyZiaQ1WGsQUtDpdjAmpSoledbDmpgkjenFPfpJjJApUgmkTEnTPlor8sJhrMZYg9KKyDnByWTEcFTgK4tzkmroqMcjyoHDFRqXK2wm0SZt63ld4gYFmVNYJ1G6j5A9pOwR+VHNYn5O00yZzc4Y1A6lU+z4I9lohLbBVQ+p4rYpywS6MOSnp2RVgbZxWwvQoCj3lnpYUHhLUWjyQmJs2kKllUjVf9IOGJxK0UNqgVAJQiYIEZOmPYSMicLnvf13HB7ts3+w1zbnhSbzhqz2ZN5hrUKbBCnjtuZrTTWp8eOyjceHzMuELE92wKPOAZ3uYask7baZPTtu5Q2+zDBWUNWGxU3J+vc5q3tPc6tY3lo295azmSQKox0e7YAB/AwMAF/avyqrDGMEZW2YX0qa7wWbx0+svjnm132aO8nF0rx0+BJoXwGd0zvglaG5FSx/1Gwep6wfjmm2muaqCsDkv4C+yvDetiPPrzTNnaK5kax+jVk/vGcZ3l8rorCZt4GvRw536Aea2aXeOdpaFtcp8+uU5Vbwee2I+kn3bWBYRNjo01IC0Npw9JbmxrC5V6y2Kas72eb35afifJ4SBcC/wOA43GY+LCmOa/LjmqLyFLlF6j7VwDKeOiaLipN5xcmsZDJ1jC8Egw8JfwBCv3KVRsDkigAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;pbip change chart diff&quot;
        title=&quot;pbip change chart diff&quot;
        src=&quot;/static/7980784dd3cc84cc1c3deaad1d6f1425/312cc/pbip-change-chart-diff.png&quot;
        srcset=&quot;/static/7980784dd3cc84cc1c3deaad1d6f1425/0eb09/pbip-change-chart-diff.png 500w,
/static/7980784dd3cc84cc1c3deaad1d6f1425/1263b/pbip-change-chart-diff.png 1000w,
/static/7980784dd3cc84cc1c3deaad1d6f1425/312cc/pbip-change-chart-diff.png 1182w&quot;
        sizes=&quot;(max-width: 1182px) 100vw, 1182px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;h2 id=&quot;Some-tips&quot; style=&quot;position:relative;&quot;&gt;Some tips&lt;a href=&quot;#Some-tips&quot; aria-label=&quot;Some tips permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;h3 id=&quot;VScode&quot; style=&quot;position:relative;&quot;&gt;VScode&lt;a href=&quot;#VScode&quot; aria-label=&quot;VScode permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;For working on the files you will want to use VSCode and install the TMDL extension. When I am developing a PowerBI report I have both PowerBI Desktop and VSCode open so I can monitor how changes made in the desktop change the PBIP files.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 482px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/66ce67506d92fd0eae7d2b8428b1b67e/b58a3/pbip-tmdl-vscode-extension.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 38.589211618257266%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAICAYAAAD5nd/tAAAACXBIWXMAABJ0AAASdAHeZh94AAABj0lEQVQoz3XQy27TQBiGYd8G0MRjO7bj2E7dHH1KlIObgqOcHCstjdqqAnEXLBB3gwQLBPdWVi/yUKFusnj0//6+GckaRQRDHNvEatqYlonTcvB8D6NhyN2yLanpNGVfdZ7nyazahSZQhfpMoJiei64LnFZTajo2rteSs6Lp4j+hqeiGJlW70dBRRV1+a9q/Xun3fOrqGa7v0g58/GqeewQXPp7fkt1Z7fVJtfobbNtEFTWEVkNpuaa8NJ2lzLKU/rDNIAykYRQQxh2iZy/3l9l0njLPRpiWjnIeuDRMQdBx6fZ9BsNA6vXbdCs9X+ZynhDGXcKog+OaKJbxil2R8/3HN758/Ux52HB3f814MmCaxcwvE2ZZQnaZMpcSObNFKrOqW22u2OzekqRdlEay4OHxyNPTH379/snNbcHjhyO3dyWHmw1FuWRbvKPY55SHNcV+SXlYsdvnHK7X8me2Rc77Y0E67qPoUcZoEvHx0wNFuWJbLFmtF+TLjNF4QBhfECcdohPitCvPLK4m8k3/AlA6B4eGYwntAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;pbip tmdl vscode extension&quot;
        title=&quot;pbip tmdl vscode extension&quot;
        src=&quot;/static/66ce67506d92fd0eae7d2b8428b1b67e/b58a3/pbip-tmdl-vscode-extension.png&quot;
        srcset=&quot;/static/66ce67506d92fd0eae7d2b8428b1b67e/b58a3/pbip-tmdl-vscode-extension.png 482w&quot;
        sizes=&quot;(max-width: 482px) 100vw, 482px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;Now you get some tremendous syntax highlighting in the TMDL for each of your tables in your data model.
In this example we can see the &lt;code class=&quot;language-text&quot;&gt;weather&lt;/code&gt; table’s TMDL, including the Postgres source it is coming from.&lt;/p&gt;
&lt;p&gt;For bulk changes, or changes that are easier with a Find/Replace or in a code editor, we can edit the TMDL as opposed to something that might take a lot of clicks in PowerBI Desktop.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 863px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/9192b9124e0400b8d881daf7c0e15bdf/85c72/pbip-tmdl-code.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 55.99999999999999%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAALCAYAAAB/Ca1DAAAACXBIWXMAABJ0AAASdAHeZh94AAABr0lEQVQoz3WS6W7jMAyE8yRNrIOkZPmKLTuOkwZIj11sse//OFNIbtNt0P3xYUwIJjUcbeLYYVlGnE4TTsuIYaghYuDFQhxBnIXzSQ2cqFttSaFQD1B6m/WTjSstGk84O4+rlLj6gNdQYw4ePhBCxahqyRqCXetK4Ms0UGeIixubdGClQHSM567DtW5wrBuIt9BmC2N2MEnt7oNtxtLuRzZ1427T/ry94O/bLxxeI+KTQ78o9CeNflkZLxq+UiBWYPmZtaE3uThfZrw8XzAdW/Rng/2UdqbA/yLFN4v35IbOm3zDyISJLGZhHJ3DzIyBCU1UGWaVf0rD7/V2w7Rw8Rpj6XCNPZYY8dh1mMrUUNCmYX5t9j+b3xqWgW5pLfsWcdjDtx6+InBpoXkHI0VWnUKhImv+5gI2Qatmy81nKKJQsUZDCiNTtppWcBTGxIxjyVjGgNgxppExjoyhsQhOI1QGtTconfpKOT0d4xSsKCwieGKPCzn8poADMxYvOLUlDrVgngWHg2AoLfqKsB8IQ7Bonf4KpWWDKIRBaA2FBX2w2Lcm28mWabVs7Ad3lhPvyARHc5g20+8AAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;pbip tmdl code&quot;
        title=&quot;pbip tmdl code&quot;
        src=&quot;/static/9192b9124e0400b8d881daf7c0e15bdf/85c72/pbip-tmdl-code.png&quot;
        srcset=&quot;/static/9192b9124e0400b8d881daf7c0e15bdf/0eb09/pbip-tmdl-code.png 500w,
/static/9192b9124e0400b8d881daf7c0e15bdf/85c72/pbip-tmdl-code.png 863w&quot;
        sizes=&quot;(max-width: 863px) 100vw, 863px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Loan Logic, an application to help with mortgage scenario modelling]]></title><description><![CDATA[Loan Logic: A Mortgage scenario modelling application Background I have always been interested in financial modelling and how different…]]></description><link>https://mortimer.xyz/loan-logic-mortgage-scenario-modelling/</link><guid isPermaLink="false">https://mortimer.xyz/loan-logic-mortgage-scenario-modelling/</guid><pubDate>Fri, 14 Nov 2025 00:00:00 GMT</pubDate><content:encoded>&lt;h1 id=&quot;Loan-Logic-A-Mortgage-scenario-modelling-application&quot; style=&quot;position:relative;&quot;&gt;Loan Logic: A Mortgage scenario modelling application&lt;a href=&quot;#Loan-Logic-A-Mortgage-scenario-modelling-application&quot; aria-label=&quot;Loan Logic A Mortgage scenario modelling application permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h1&gt;
&lt;h2 id=&quot;Background&quot; style=&quot;position:relative;&quot;&gt;Background&lt;a href=&quot;#Background&quot; aria-label=&quot;Background permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;I have always been interested in financial modelling and how different financial decisions can impact long term outcomes. Given I am a web developer, a while back I decided to make myself a mortgage managing app, and at the time I wrote the backend in PHP.&lt;/p&gt;
&lt;p&gt;With the introduction of AI coding assistants, I thought it would be a great opportunity to:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;refactor an old PHP application I had written into a Python/Cloud based application.&lt;/li&gt;
&lt;li&gt;test the process of using Google’s Antigravity and VSCode Copilot to do a large refactor&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id=&quot;Architecture&quot; style=&quot;position:relative;&quot;&gt;Architecture&lt;a href=&quot;#Architecture&quot; aria-label=&quot;Architecture permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 1138px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/5aaa8f9e662ba44663987c3dd1c325d2/0ff2a/ll-02-architecture.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 50.2%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAKCAYAAAC0VX7mAAAACXBIWXMAAAsSAAALEgHS3X78AAACUElEQVQoz3XQS2/TUBAF4Pz/DesWkNghYIcQQmJBgVaQtokTJ9Akaty4sWPf99M3zsP3oJQlYjGaxUifzpxeVpA5kVYbH7j1gU8IefnpflFfrDJ5scr45WrFb0jFB7zmQ0GfJhFU9znLiur6mVx/d5b8JJ1NwlFff+6lWSlmBUPYHeHDHr8oefsunRyHguJOCtw9ZsirGsIdoJsdtG/gmi1y4wyV6fNoUxztpDu6KY4muelZ56mREtpsOqNJ95vLtxfLxe5HucadEPF6OYjM+djUV9HrTSSujdJ6FNZLKsdnrUywVcm+c+kJ7PfWlND5coQy+9zNfn3BYCM+Xq5X+6six0yKaE2AbXaoHwZQgiAnIlZUnEBFZXru6RCrxZdDw24R3ajfk8ZTpgyYch1hCsPN5kNq1S41EjMto2oCZBNAbAtmG1Bto7QOK+NUvrk915s+RHl98DxBdEm/Z3ygp05caDttPQZl+bFfl/t+VWLCabwnBPOqRmUMKudQWBsr67DQRtV8dNawAXR9c2jVCNEm/Z72gRofYHyI1oc4rcn7y8fV/mqdY0pJXGYEUjksGAPRFk1oYwgtcmV0SUcvTuB6+e3g6ODvy6eEttk+ga7ZxoVWr0ea78dGYKZEXFf8dMPXhwyLmqCoSKwpxz2T+qG4fXU0Y7RqfNjrFJ1N/gXnUr4ZSrZPJMNcy7jd7uBDCyk0nAvIiyqSmqHyQXE9OevsGI+Li4Nn/0k4V3/BkWKYKh4zrbHUGg/WIDNPO+bWYCql4mpyfgLp+schiOSpwz9mmNoSA08ALQAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Application architecture&quot;
        title=&quot;Application architecture&quot;
        src=&quot;/static/5aaa8f9e662ba44663987c3dd1c325d2/0ff2a/ll-02-architecture.png&quot;
        srcset=&quot;/static/5aaa8f9e662ba44663987c3dd1c325d2/0eb09/ll-02-architecture.png 500w,
/static/5aaa8f9e662ba44663987c3dd1c325d2/1263b/ll-02-architecture.png 1000w,
/static/5aaa8f9e662ba44663987c3dd1c325d2/0ff2a/ll-02-architecture.png 1138w&quot;
        sizes=&quot;(max-width: 1138px) 100vw, 1138px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;h3 id=&quot;1-Frontend-Client--Serving&quot; style=&quot;position:relative;&quot;&gt;1. Frontend (Client &amp;#x26; Serving)&lt;a href=&quot;#1-Frontend-Client--Serving&quot; aria-label=&quot;1 Frontend Client  Serving permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Technology&lt;/strong&gt;: Vanilla JavaScript, &lt;a href=&quot;https://tailwindcss.com/&quot;&gt;TailwindCSS&lt;/a&gt;, &lt;a href=&quot;https://daisyui.com/&quot;&gt;DaisyUI&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Serving&lt;/strong&gt;: Nginx container acts as a web server, delivering static assets (HTML, JS, CSS) to the browser.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Auth Handling&lt;/strong&gt;:
&lt;ul&gt;
&lt;li&gt;after user logs in client receives a JWT (Access Token) that is stored in localStorage.&lt;/li&gt;
&lt;li&gt;the ApiClient intercepts all requests and injects the Authorization: Bearer &lt;token&gt; header.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;2-Backend-Application-API&quot; style=&quot;position:relative;&quot;&gt;2. Backend (Application API)&lt;a href=&quot;#2-Backend-Application-API&quot; aria-label=&quot;2 Backend Application API permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Technology&lt;/strong&gt;: Python FastAPI, running on Uvicorn.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Authentication&lt;/strong&gt; (Internal):
&lt;ul&gt;
&lt;li&gt;Uses python-jose for JWT encoding/decoding.&lt;/li&gt;
&lt;li&gt;Uses passlib[argon2] for secure password hashing.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;3-Database-Layer&quot; style=&quot;position:relative;&quot;&gt;3. Database Layer&lt;a href=&quot;#3-Database-Layer&quot; aria-label=&quot;3 Database Layer permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Technology&lt;/strong&gt;: Remote MySQL Database (managed externally, e.g., AWS RDS, Azure MySQL, or a VPS).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;ORM&lt;/strong&gt;: SQLAlchemy handles object-relational mapping, translating Python objects to SQL queries.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Migrations&lt;/strong&gt;: Alembic manages database schema versioning (creating tables, altering columns) directly against the MySQL database.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;A-fictitious-journey-of-home-ownership-and-financial-uncertainty&quot; style=&quot;position:relative;&quot;&gt;A (fictitious) journey of home ownership and financial uncertainty&lt;a href=&quot;#A-fictitious-journey-of-home-ownership-and-financial-uncertainty&quot; aria-label=&quot;A fictitious journey of home ownership and financial uncertainty permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Now, to demonstrate a use case for the application with screen shots, I thought it would be good to step through how it might be used with an example.&lt;/p&gt;
&lt;p&gt;Meet Sarah Thompson, a fictitious (and AI generated) Australian professional who is about to buy her first property in the Canberra suburb of Franklin.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 1536px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/bd7c0884c4555b238bb6ddb1d55594ac/e8464/ll-sarah-thompson-sold.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 66.60000000000001%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAANCAIAAAAmMtkJAAAACXBIWXMAAAsTAAALEwEAmpwYAAADJElEQVQozwEZA+b8AN/i69jb5NPX39TY4NHX4by/xKioqYiLjXh4d2xrZl9eV1hVTlNRSlJRSldUS2FcTl9ZTHV4d9He7tPd6gBfW1JZU0lOSkFUT0NQSj0tKiBdUTtWTDg9PTNjWUSFc1SAcFRLSj9TVU9pZlqXhWlfTzWFgXrw8/vp6vEAfG1VgnJaW1xXhn1ta15JHxkNmo18m5eNZ3R7oJyTybqnua6dYWhqcHmAjZGRzcCxfnJhq6uq9fj/yMjLALGkla6ik3N0caykm4d9bzAoHJmOgaObkYSAeqObkLarn62kl46If4yGfJePha2jln10ab2/xOvw+nVrXwBdWlVcWVRUUU1aV1BUTkZJQztWUEhYUUdWT0VTTUNKRTtLRzxUS0CTcV5oUkNFQThNSD9XU0xqaWZ2a10AT0YxUEcxU0kzSD8pLiYUHBgKHRoKIBwMIxwKLCQRc2VKd2tPY085uoZuj2VNW0w1gnRYeGdJd3Rtn5GCADMxJjw6MDg3LmJVS3FSREkvI0EpHUEvKEE0Kj48MqqciGxpXEk8LqJuVYRYQDw0KYuFdb2tlmxkWF1OOwAlKCQvMC4qOTeLTEfPVEvHdXPKeHjRZmK4SkNUVEmelXtoZlJPOimYZ0Z7UTNJOSqiloW1p5EuJhogGxMAJSkkLy8sLTw6j0dAzEo9xWpJwYhSt30hsGQkTkZFT1UqgnRPeGpeVVFJTUg/lXpit5qBtKWPLyohFxQMAGZbU2VbUlVaTaJXUMR2J5VsAI1oAZtyCq93M1FBNVBLJox5X1JfYBk4QERRT6KDZ5V3XIN0SDUzDiUdFgCPhlZzdC1jcSCrY0zFgE+wjku+c1LNb124X1VlYyliXxeQfGEoNjcaLi9OTEKNbU+OcFZfUi49Ow9XUycAX2gSf3s7pZl1x3xyzoSKz4+SzI6Qz4iOuW5abHQfenYtkHVZJTAuFSgqUUc3imlKgWRNjX5GcnEjcnMjAK6fgrypkJuSX4x6Q49nMKF6U6d9W5prN5h8R3d3JY2AQZR4Xy07ORYqLFtPPYppSoVoUod6P3JyIHVzJVJPPbDPePRBAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;For Sale Sign with a Sold Sticker&quot;
        title=&quot;For Sale Sign with a Sold Sticker&quot;
        src=&quot;/static/bd7c0884c4555b238bb6ddb1d55594ac/e8464/ll-sarah-thompson-sold.png&quot;
        srcset=&quot;/static/bd7c0884c4555b238bb6ddb1d55594ac/0eb09/ll-sarah-thompson-sold.png 500w,
/static/bd7c0884c4555b238bb6ddb1d55594ac/1263b/ll-sarah-thompson-sold.png 1000w,
/static/bd7c0884c4555b238bb6ddb1d55594ac/e8464/ll-sarah-thompson-sold.png 1536w&quot;
        sizes=&quot;(max-width: 1536px) 100vw, 1536px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;Her financial situation was stable for a long period of time, and she was on track to pay off her mortgage in the standard 30 years. But as with everyone, financial circumstances changed over time.
A few years into her home ownership and she gets a decent tax refund from the ATO. Then a few years later, her uncle Peter passes away and leaves her a small inheritance.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 647px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/6f0487371124a45c0974f6b91408ce57/f9e62/ll-01-events-windfalls.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 63%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAANCAYAAACpUE5eAAAACXBIWXMAABJ0AAASdAHeZh94AAAB2ElEQVQ4y42T227TQBCGBx6MN+DBUKkQXBQkJKK0UG5R7xEFwRW3CGiL0iRN48SHxIesnfWu90M+BIWmoI70aWbH3tn/X8vyY/CN6HJAOBqwuBo2pN6EYDiAIqONCnB3Qh5dlOx9z9k/1zw+0+yfaZ5cGPZ+rnk2cBxcwcG4Y7veMILnY3g6hM8LEHkxRo5DpHeN9D3kyEd6k7buz5DeFHk1aeuXQ+Rwjhz6Lf058jrsngc8+GKRunn/bYAcXiNH0zZv6mMfeVNvmiH9SdcLkBOzheXeSYm8Mzz86pDTJXxcwKcufwg7ojo7TiN2Cbdp33kfwKVySHOZVQllDnbNbrg7AiZfIb98w2CW4c1mTOchyUqxTDJUoVFrjXVgndsCTJd1VTEqAs7VFE/HpGlWK2zDGIu1ljwvyLIVWuuGRqNzf7Hp3Yw0TZGlqghjRRzHJGm2a/iWjdtDV7YgMYrClWRZhnixZb7M8X0fPwiJ44QwWjRKa7YV3hxonGWmF0yKgKVZscrSjWWHsbb9J6oKW1U79m4qvd1ygowjw9BLWEQRpTEYYyjLsrlP2x3yL4XWVXjriFExJzIphVKIWlfk2jb+64+RJOkfyyrP/zuwzrnVKFuwrspm/RttGrZsoTWRJAAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Manage Events - Windfalls&quot;
        title=&quot;Manage Events - Windfalls&quot;
        src=&quot;/static/6f0487371124a45c0974f6b91408ce57/f9e62/ll-01-events-windfalls.png&quot;
        srcset=&quot;/static/6f0487371124a45c0974f6b91408ce57/0eb09/ll-01-events-windfalls.png 500w,
/static/6f0487371124a45c0974f6b91408ce57/f9e62/ll-01-events-windfalls.png 647w&quot;
        sizes=&quot;(max-width: 647px) 100vw, 647px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;She also had some unexpected expenses over the course of her mortgage.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 598px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/5b96952c12efae547c9e260d094f923a/02e46/ll-01-events-expenses.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 55.400000000000006%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAALCAYAAAB/Ca1DAAAACXBIWXMAABJ0AAASdAHeZh94AAABLUlEQVQoz6WS2W7DIBBF+f8P7FvVKPGCsYkXDJhluBWkTqOqTip1JEvGeI7uYWDdHDErjWGQGOQVUl7RDxJEhFwpJfxW+/dEBFoW0DQheQ/20QdMiwHvBEQv0XKBS9Vg1RqLWu/NP8F3YAggIUCcI20b2Nc29v+PGp+lfCz2LiKqdkDLOeqGQ6266Mas8qD9NGHfIzYNkrVggyJYFzDPC9ZV4zqO5Qy1MWX9EhgjSCnQPBd4UQ7BI4QA5/yftJ4qvzUBFZeo66YMQ6kVxlh47+G8f53QWsTLBfF8RjIGzIXbQKy9QbLmOE0FmrVfAonKdUnOlXcWCTB2K835yaB/K3dyQdvychezdid6OOegtflOcpTQe8S6Rjydbsr7ZgaUc3OuaOcBWbsdAo8SfgJJIF808+nDBgAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Manage Events - Expenses&quot;
        title=&quot;Manage Events - Expenses&quot;
        src=&quot;/static/5b96952c12efae547c9e260d094f923a/02e46/ll-01-events-expenses.png&quot;
        srcset=&quot;/static/5b96952c12efae547c9e260d094f923a/0eb09/ll-01-events-expenses.png 500w,
/static/5b96952c12efae547c9e260d094f923a/02e46/ll-01-events-expenses.png 598w&quot;
        sizes=&quot;(max-width: 598px) 100vw, 598px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;This became a roller coaster of financial ups and downs for Sarah and she needed a way to model different scenarios to see how these events impacted her mortgage.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 1024px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/fa61153e40783fdc0ec3fab6da082ea3/42a19/ll-sarah.thompson-expense-windfall.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 75%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAPCAIAAABr+ngCAAAACXBIWXMAAAsSAAALEgHS3X78AAADnklEQVQozwGTA2z8AL23q7+4rcXAtZeQg1RLPG9gTretoMjEusbBt8nEu8K9tL65sMS/t8C6sI2FeH90ZrConcfEvMTAucfDvAC5sqbEvbKZkoU9Jg9HKA8zGwVWQCm5saPHwrrHwrm+ubG+ubCyraVAOSwIAgAUCgJMNyKkmo3Ixb7EwLgAubGmwLqwXEw6pWQ58Kd8zZd2QikScmRT0MvCxsK5vrmwwL20i3xtZzsfdk80XUEsLR0NRzcmw720xsO7ALqypqymm0k0H5lfOMuIYtmcdqd3VEQ2JsnCuMfDur24rrezqrOMbNONZOalevG+nKWAZRwOAJWNgs7LwwC6saOWkYVCKBKaYDnAgFvHiWPTlm1ZPCW1raDLxr69ubGumILAimGda0yye1W3hmPbrIdyTzRpWk3RzsYAsKqcpJB+UjIdmFIrwH1X35Ztt31YQCYQsKOUy8a+t7SssIppqXRPc0Egom1JvItpzqF/vollZUs4xsK6AKidi9CYdVM4Kl8yFapjQ8qFXXtRMSkVAK+gj8vHwK2kmLeNaI5lSoBIJbJ0S+Krh/G9lm5ROUo0IcS7sACihWm9gl5oSTg1HAqvZD3fnHBcOx4uFQCjkHvHxb6xnojSonpDMCJDJhCgXDbLjGjbqYBGLhpNMxy+takAimNArHlabkw2TCkPtHRG1pVlWTQXKREAaEsutaCGyaWFx6WGJRgLDgUAfUwq0JVsw5FoOSALTC4Xt6qaAGRHJ+KkfoZgRh8YC5lxT6VyS0goDjceBohrS7KUceXCo4ZoTCkUAR4PA3JEIcGOY7aGXzccCDYcBoRqUQCBWzjXnXWJcVg9SEUzPjouNzEQGRRfUTutjmmkiGjEqY56XT8oFAQdEAR9Wj7QmG2xfVU+IQstFAF9YUcAuoRbxZZxrJeC1Ma8wretzMO7RlJRWlNBrYxoo4hoqpR6kHNUQCwYEAkAHiMfOTcuMishIxMDYkgumnxcANqheKqDYH9zYs++s9C8r9/GuFhXUWJaRrCQa6aKaqyWfZJ0VX1lSxofHBshHhciIhYfHhoeG5F9ZZR3VwDFl3OPb08mLCVSVU9yW0rDelTWmnrMoIGliGaoi2uii3KJbE1qXEcZJCUgKCUiKSYmLisjLSuVgWuegmIApYJdaVQ6CBIMAw8LCg8IkVc14aF83aaCmntYn4RjkX5mgGRFSUQ3GSQjJCwpICgmIispKDAumYVuqIxsYZ2W3NfwjtUAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Sarah Thompson Stress and Delight&quot;
        title=&quot;Sarah Thompson Stress and Delight&quot;
        src=&quot;/static/fa61153e40783fdc0ec3fab6da082ea3/42a19/ll-sarah.thompson-expense-windfall.png&quot;
        srcset=&quot;/static/fa61153e40783fdc0ec3fab6da082ea3/0eb09/ll-sarah.thompson-expense-windfall.png 500w,
/static/fa61153e40783fdc0ec3fab6da082ea3/1263b/ll-sarah.thompson-expense-windfall.png 1000w,
/static/fa61153e40783fdc0ec3fab6da082ea3/42a19/ll-sarah.thompson-expense-windfall.png 1024w&quot;
        sizes=&quot;(max-width: 1024px) 100vw, 1024px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;h2 id=&quot;Loan-Logic&quot; style=&quot;position:relative;&quot;&gt;Loan Logic&lt;a href=&quot;#Loan-Logic&quot; aria-label=&quot;Loan Logic permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Sarah looked around for something that could give her an idea of how these large financial events would impact her mortgage. She couldn’t find anything that she liked (well she is a fictitious person, but you get the idea).&lt;/p&gt;
&lt;p&gt;Since she actually worked in the tech industry and was comfortable spinning up applications with Docker and setting up backend databases, her friend suggested she try Loan Logic.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 1536px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/2be0becb057c1ba871342ab9ec3081b8/e8464/ll-sarah.thompson-happy.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 66.60000000000001%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAANCAIAAAAmMtkJAAAACXBIWXMAAAsTAAALEwEAmpwYAAADJElEQVQozwEZA+b8AB4cEh0bEhkZERIVEAkQDggPDggODAYMCwQLCgQLCgMLCQQIBi0hFj8yJyMgGRgYEiEgGCYkHS4rIzUyKQAIDwwHDQkPEQonHxIPFhMLExILExEKEQ8JEA4IDw4BCAdyUjvJnIJALyILCgMiIBglIxwsKSEzMCc7Ny4ADBgVBRISQz4zRTwsDBQSDxcVDhYUDRMTCxIRCREQDA8Mk2JBy52Dt5B4Wj0rJCAXKiggMC0lNzQrPzsyABIWDxIXE1xNOzszJhAXFREZFxEYFg8XFQ4VFAwUEwsOC4pgSLOAZ92niHlYQh8ZEDEvJjUyKTs4L0A9NAATFQ8REwwYFw4pIxcZHxwTGhgSGBcQFxYOFRQPFRQGDw1OLx+9fWCyfVYyIBETDAQwKiEyLyc9OjFCPjYABxQSBBANAQkFGRQJFBsaEBwfFyEgGSAeHSQiFRsZFRwYGRgQdEswv4pjj3RdcnRsio2IW1tUPDgvRUI6ABQYEwcMBXt+fMPFybOwo8GymrympcPHydXW2GNmY0BCNTM3KzItH4hzXaOmnpGcl5mjnqCopUtKQkRCOQAWFg8FBQCPkI/////NwcXgsIC3ybfo7fT+//+PkYxMUURkZ1ppbWF8hn6NlpCAh39tcmmjq6doamQ/PTUAEhsZAAkHcXR0////5cvEgaa8rr2m+O/i/P//lZeUNzsxgIV7hImAhoyDj5aPgIeAUFJHnKKciY2IRkQ8ABUgIQUQEGRqatra5qSnuZahuLSruLOzwefn8ZeZlwsUD250a36Gf4KIgIiNhXZ9dDQ3LYOHf5+loy41MgBUPydTOB14aFfKz9nIytnQ0N7Izd6ipMDj5/TFv7qcdmO5moaejn2JjISGj4mEi4V/h4ORmZSZoJwsODcAnm4/nGw8fVgzZFI9blxGcWBNd2lYhHlrlI2DiXpwVjgpVDwtn3tijXxnkIh2mZKBnZeHoJ2Pk5aNHSsqAMCQYb+OXMeYbMyojL6cgLSPcKyFZKN9W5ZxUItnSJNzVrKMasaff86mhc6oh86qi8yois2sj8irk6qLbKXgE6NgHtk7AAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Sarah Thompson Happy to find Loan Logic&quot;
        title=&quot;Sarah Thompson Happy to find Loan Logic&quot;
        src=&quot;/static/2be0becb057c1ba871342ab9ec3081b8/e8464/ll-sarah.thompson-happy.png&quot;
        srcset=&quot;/static/2be0becb057c1ba871342ab9ec3081b8/0eb09/ll-sarah.thompson-happy.png 500w,
/static/2be0becb057c1ba871342ab9ec3081b8/1263b/ll-sarah.thompson-happy.png 1000w,
/static/2be0becb057c1ba871342ab9ec3081b8/e8464/ll-sarah.thompson-happy.png 1536w&quot;
        sizes=&quot;(max-width: 1536px) 100vw, 1536px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;After setting up her MySQL database, and migrating the schema using Alembic she confirmed the tables were deployed.&lt;/p&gt;
&lt;p&gt;She creates her &lt;code class=&quot;language-text&quot;&gt;.env&lt;/code&gt; file for the DB connection and then spins up the application with &lt;code class=&quot;language-text&quot;&gt;docker-compose up --build&lt;/code&gt; and opens up the service &lt;a href=&quot;https://localhost:3000&quot;&gt;https://localhost:3000&lt;/a&gt;.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;ini&quot;&gt;&lt;pre class=&quot;language-ini&quot;&gt;&lt;code class=&quot;language-ini&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;# .env&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;# Database Configuration&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;# Replace with your actual remote MySQL database connection details&lt;/span&gt;
&lt;span class=&quot;token key attr-name&quot;&gt;DATABASE_URL&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token value attr-value&quot;&gt;mysql+pymysql://your_username:your_password@your_mysql_host:3306/mortgage_app&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;# Authentication Configuration&lt;/span&gt;
&lt;span class=&quot;token key attr-name&quot;&gt;SECRET_KEY&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token value attr-value&quot;&gt;your-super-secret-jwt-key&lt;/span&gt;
&lt;span class=&quot;token key attr-name&quot;&gt;ACCESS_TOKEN_EXPIRE_MINUTES&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token value attr-value&quot;&gt;30&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;yaml&quot;&gt;&lt;pre class=&quot;language-yaml&quot;&gt;&lt;code class=&quot;language-yaml&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;# docker-compose.yaml&lt;/span&gt;
&lt;span class=&quot;token key atrule&quot;&gt;version&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;3.8&apos;&lt;/span&gt;

&lt;span class=&quot;token key atrule&quot;&gt;services&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token comment&quot;&gt;# Backend API&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;backend&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;build&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;token key atrule&quot;&gt;context&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; ./backend
      &lt;span class=&quot;token key atrule&quot;&gt;dockerfile&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; Dockerfile
    &lt;span class=&quot;token key atrule&quot;&gt;container_name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; loanlogic_backend
    &lt;span class=&quot;token key atrule&quot;&gt;env_file&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; .env
    &lt;span class=&quot;token key atrule&quot;&gt;ports&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;8000:8000&quot;&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;volumes&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; ./backend&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;/app
    &lt;span class=&quot;token key atrule&quot;&gt;networks&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; loanlogic_network
    &lt;span class=&quot;token key atrule&quot;&gt;command&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; uvicorn app.main&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;app &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;host 0.0.0.0 &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;port 8000 &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;reload

  &lt;span class=&quot;token comment&quot;&gt;# Frontend&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;frontend&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;build&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;token key atrule&quot;&gt;context&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; ./frontend
      &lt;span class=&quot;token key atrule&quot;&gt;dockerfile&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; Dockerfile
    &lt;span class=&quot;token key atrule&quot;&gt;container_name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; loanlogic_frontend
    &lt;span class=&quot;token key atrule&quot;&gt;ports&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;3000:80&quot;&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;depends_on&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; backend
    &lt;span class=&quot;token key atrule&quot;&gt;networks&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; loanlogic_network
    &lt;span class=&quot;token key atrule&quot;&gt;volumes&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; ./frontend/src&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;/usr/share/nginx/html

&lt;span class=&quot;token key atrule&quot;&gt;networks&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;loanlogic_network&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;driver&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; bridge&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 974px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/e538ede756a526439f486ac20226d672/1ed31/ll-register.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 155.99999999999997%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAfCAYAAADnTu3OAAAACXBIWXMAAB2HAAAdhwGP5fFlAAACtElEQVRIx5VWTW/TQBDd38UP4IREEAckLhW0HPkZcIUTorQ9lPbAD0AoIBBVW5AQEgLRNiFtKKXxRxw7jmkSf2TXD83GTmLX3qQrPe16d/J25s3MKkzTdRRC06AZJn7/2MNJ/adcy70y+wSspWkownmrBdM08aFaxddvn+Wa9srsUzDLsqCC2XFgtdU2s2BCCCjBObjg4Jxjrq0QYHEc4yqjzD5OzhjdTIs8aIRhiNqvYxzWGjg4qqNt2RPSvC3NxMVGo9GlW9NvMtB0A/VGE3/OzuG6npKQuCaEQRBiMBgiCEN1aIp9SZiG7PY82LaDfn+QMeJCgHMhZxI9752YWWc8DMMI3r8LSdzzPHTdHqIoGv9IksUFEDPnYqohDd/3YXUcOI6Lju3AtDqT8EVB0jJezhKmIY9GvFCXbJ2Ve5nRkDbJq4sZ/cqEVyGX5UDq5hJ6nkS3607WpO9Us3EyaFZqSF7aTldmm9aG0YbVseE4XXlRUZZLNVykBQvJ8iGnGlJ41BWGaSXhFDR/oXeKOuwPBki/55VKaciphkRGHvpBMK69xCMlypKSb/JJNheBKNGw1/Og6SZsu6t8BFQJyzwOQ9+X4dIbSAeLvM55ZDQcDofQzTbO/rZkLS7SGWkUhXWo7uF5KNCQuoG6ol/Qz4t4eUlDeq2pZ+mBIC19P5C6jpILuQKpp5PCpkGP6snpGY6bp2g0T+VcazQl6fhNLK9LfikpV6sQddlAxHDDIZa+bOPG7ioqe2u4OYPK7gtUPj5HZWcVlf01VPbXk3kNtz9t4PrOMzw6eidJI0kYA+cDF+zVQ7DNe2BbK2Avl8fYXgFbXwJ7fAvs6R2wzftgW8vT860HYBt3ce39k4QwAqO/GpHg2Gkf441+iKp+lIVRQ7VdH8PInr01anitHeC725KEFPJ/0pZBKBiMrL8AAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Loan Logic Register&quot;
        title=&quot;Loan Logic Register&quot;
        src=&quot;/static/e538ede756a526439f486ac20226d672/1ed31/ll-register.png&quot;
        srcset=&quot;/static/e538ede756a526439f486ac20226d672/0eb09/ll-register.png 500w,
/static/e538ede756a526439f486ac20226d672/1ed31/ll-register.png 974w&quot;
        sizes=&quot;(max-width: 974px) 100vw, 974px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;After she registers, she confirms that new user in the users tables.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 779px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/1f40602bbd550007e094ef89a82c77e8/a3e85/ll-02-db-tables.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 47.199999999999996%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAJCAIAAAC9o5sfAAAACXBIWXMAAAsSAAALEgHS3X78AAABRklEQVQoz43O226jMBSFYd6k2MRJ8Nkbg405mZhCkmaUUTua93+UNlGvZyr9F+vmk1bWxbTc/+gw8drLdiC6xkxRN+rp3C6XYb3rIdl5tdNShrnsEneDu77DvJU2ZLZL4+tNd5PuIubmpZSFgCM0oo1tjC6ONm3GhrqfeegLBURYIcHEk52XTHZzuP+dzrd+eyuEwRy4G7gfjjYcIdAQ+Mkjpg6+1teBn9qdAQKVuvRy7bNcN/L64eOy13VOFTGubPo9+EIA5oAlkKoqFGBpiKlIVe2gwsLsDOwMZFg3crm1aS0EPLBuqBsO4BFViGnEVE5VTjVi+jkeIarzZ0/8+qtLG+YPvAcPpwvzY/6N/9U3DmlFTL8cBTGO+elQtYj9DIv0FtLGm+5gGqKdCPFx+ycYqVqcf7dxYU3AXBfSUjfujfuv/MKfjBZnRaYspvwAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;ll 02 db tables&quot;
        title=&quot;ll 02 db tables&quot;
        src=&quot;/static/1f40602bbd550007e094ef89a82c77e8/a3e85/ll-02-db-tables.png&quot;
        srcset=&quot;/static/1f40602bbd550007e094ef89a82c77e8/0eb09/ll-02-db-tables.png 500w,
/static/1f40602bbd550007e094ef89a82c77e8/a3e85/ll-02-db-tables.png 779w&quot;
        sizes=&quot;(max-width: 779px) 100vw, 779px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;First she inputs the baseline mortgage parameters as was the case when she bought the property.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 1578px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/26d5b7b39d60349cb1df3e5346148c90/6e34c/ll-01-mortgage-scenaario-edit-balance.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 26.8%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAFCAYAAABFA8wzAAAACXBIWXMAAB2HAAAdhwGP5fFlAAAAcElEQVQY06WPwQrDMAxD8///unYltuPGsTU8BmtoL6UCXSRknovUAG8BegdUAhHPXHRvIK4YbkgdS2IGsUxZqpuhVoLZeVN6NyzrBmY5lSyCMXzK3P2bvZYV0tq0yVslwyR5+uqf8IdPF4R3nOSqig9ZKosj2NS5HAAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Loan Logic Mortgage Scenario Edit Balance&quot;
        title=&quot;Loan Logic Mortgage Scenario Edit Balance&quot;
        src=&quot;/static/26d5b7b39d60349cb1df3e5346148c90/6e34c/ll-01-mortgage-scenaario-edit-balance.png&quot;
        srcset=&quot;/static/26d5b7b39d60349cb1df3e5346148c90/0eb09/ll-01-mortgage-scenaario-edit-balance.png 500w,
/static/26d5b7b39d60349cb1df3e5346148c90/1263b/ll-01-mortgage-scenaario-edit-balance.png 1000w,
/static/26d5b7b39d60349cb1df3e5346148c90/6e34c/ll-01-mortgage-scenaario-edit-balance.png 1578w&quot;
        sizes=&quot;(max-width: 1578px) 100vw, 1578px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 1572px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/4a17ca6c692a9dbc928b6a529eb5420a/dc8ac/ll-01-mortgage-scenaario-edit-income.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 31.6%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAGCAYAAADDl76dAAAACXBIWXMAAB2HAAAdhwGP5fFlAAAArUlEQVQY04WQ2wrDMAxD+/8/uofdaOJbEscayVjpRssMB4wRwtLCCUj3QLoF1sE1ICkQAUTEF5/5ve9Z1BTubcg2SjHkTGjum1DVQMRImea+3Zh/DLXg+UwgUnQPuHcwy2T/jVnB7f5ArRXujtZ8GmaiTTPui1egaOBxCfAa8PaOexRtP2cVLMQCIpkmvcfkyIhF5kd/OxwRUsoQkUPxiGGloLU2+xPVE12HmeEFWqjY1Nfe1LgAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Loan Logic Mortgage Scenario Edit Income&quot;
        title=&quot;Loan Logic Mortgage Scenario Edit Income&quot;
        src=&quot;/static/4a17ca6c692a9dbc928b6a529eb5420a/dc8ac/ll-01-mortgage-scenaario-edit-income.png&quot;
        srcset=&quot;/static/4a17ca6c692a9dbc928b6a529eb5420a/0eb09/ll-01-mortgage-scenaario-edit-income.png 500w,
/static/4a17ca6c692a9dbc928b6a529eb5420a/1263b/ll-01-mortgage-scenaario-edit-income.png 1000w,
/static/4a17ca6c692a9dbc928b6a529eb5420a/dc8ac/ll-01-mortgage-scenaario-edit-income.png 1572w&quot;
        sizes=&quot;(max-width: 1572px) 100vw, 1572px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 1586px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/8cb77d4bfa5b1905dbe0977426e759a4/867ee/ll-01-mortgage-scenaario-edit-information.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 27.400000000000002%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAFCAYAAABFA8wzAAAACXBIWXMAAB2HAAAdhwGP5fFlAAAAhklEQVQY051P0QrEIAzz//9Vtmlr1dbm0GOwu+3huEAoNE1IAydHio68OYQcgMP9f4YiBSkfUOsrbIy3oGrIxBgPJqkV+5HWvAcWQYwbam2YOAWzAeZyM0zU1hY/7seAqiKIVBDxCrgazQw5EzIRisjvL88WxPzVztC7rrdmk6th7s+mT3gBABKKoDv6zUEAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Loan Logic Mortgage Scenario Edit Information&quot;
        title=&quot;Loan Logic Mortgage Scenario Edit Information&quot;
        src=&quot;/static/8cb77d4bfa5b1905dbe0977426e759a4/867ee/ll-01-mortgage-scenaario-edit-information.png&quot;
        srcset=&quot;/static/8cb77d4bfa5b1905dbe0977426e759a4/0eb09/ll-01-mortgage-scenaario-edit-information.png 500w,
/static/8cb77d4bfa5b1905dbe0977426e759a4/1263b/ll-01-mortgage-scenaario-edit-information.png 1000w,
/static/8cb77d4bfa5b1905dbe0977426e759a4/867ee/ll-01-mortgage-scenaario-edit-information.png 1586w&quot;
        sizes=&quot;(max-width: 1586px) 100vw, 1586px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;After doing all this she setups up a baseline scenario.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 567px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/13cb42e81a29159c48d684db02dffdab/b546d/ll-01-mortgage-baseline-scenario-tile.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 36.6%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAHCAYAAAAIy204AAAACXBIWXMAABJ0AAASdAHeZh94AAABO0lEQVQoz52Ry0rDUBRF+0EOHOsPCY7Eb3Eg/oIzsX6AAx8zUWjTmjYmTdK8n/fm1WZJrlSkdOSGDfecC4tz9hmFUczcWOJ6AfpyxVw3WNkO+sJQNlcO2kzHcT0G9X3/6139V6OJrvF+/cHWeiPSrgjXC5p2SykEsqoohaQoSuqmYV/7MAUUUpJ7JY3IaKTPpmuRVa2AVVVR1bWCDfUAlrJCCMlmszk48SjPC+y1i+36LE0X3w9Jsxw/CInihDhJSdOMMIqI40T9JWlG23WHgW1XkxcZQgjlUpT8VwN09HADZ0dweQoXJ3B+DE/jDNP9xPMibNthqs0Iwoj12mP+ucD4MgmCUB1vMtWwbIembX8mtKY9j7c9r+Oel/ue5zvwrYZCZJSlVDkOK+/yG95DFHlRIKUkThLV22X6DVa1EvZWx4LsAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Loan Logic Mortgage Baseline Scenario Tile&quot;
        title=&quot;Loan Logic Mortgage Baseline Scenario Tile&quot;
        src=&quot;/static/13cb42e81a29159c48d684db02dffdab/b546d/ll-01-mortgage-baseline-scenario-tile.png&quot;
        srcset=&quot;/static/13cb42e81a29159c48d684db02dffdab/0eb09/ll-01-mortgage-baseline-scenario-tile.png 500w,
/static/13cb42e81a29159c48d684db02dffdab/b546d/ll-01-mortgage-baseline-scenario-tile.png 567w&quot;
        sizes=&quot;(max-width: 567px) 100vw, 567px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;She clicks on the baseline scenario to see the mortgage payments over time.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 1023px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/b8bfb02db1f446b4f2f99e99e467bf46/bb4c7/ll-01-mortgage-payment-callouts.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 19.2%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAECAYAAACOXx+WAAAACXBIWXMAABJ0AAASdAHeZh94AAAAx0lEQVQY01WP207DQAxE8///xQtFKn1AorRpSRtaLqHJenNRs2sftCGqYKQjWZZnNM72RUlxeGO9ydm/HlFVkswMTNOAmc7YP5JCCIj3OPGEEMmkq/FdQyMXpHVzUDJANEjx9pc56LZIisp1HKcyWfkSKTcRqaCrfw3pTseWoVow+hz6HdbnWBwm/2fvyC8fnKXmqT5xGtztq2y3MhLre2W7VGLUqZ0FhzV3WPuMyhKTR3SUyfjeOx6+D5z7hkVVsJWvqa2a8gOj1TU07h/nowAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Loan Logic Mortgage Payment Callouts&quot;
        title=&quot;Loan Logic Mortgage Payment Callouts&quot;
        src=&quot;/static/b8bfb02db1f446b4f2f99e99e467bf46/bb4c7/ll-01-mortgage-payment-callouts.png&quot;
        srcset=&quot;/static/b8bfb02db1f446b4f2f99e99e467bf46/0eb09/ll-01-mortgage-payment-callouts.png 500w,
/static/b8bfb02db1f446b4f2f99e99e467bf46/1263b/ll-01-mortgage-payment-callouts.png 1000w,
/static/b8bfb02db1f446b4f2f99e99e467bf46/bb4c7/ll-01-mortgage-payment-callouts.png 1023w&quot;
        sizes=&quot;(max-width: 1023px) 100vw, 1023px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;She can see the mortgage payments over time decreasing the loan balance. She also now can track the cumulative interest paid. She is amazed that over the course of the 30 year mortgage she will pay more in interest than the original loan amount.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 1532px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/5045574643e0ba41446bee2b4be57c87/b5cb9/ll-01-mortgage-payment-chart-baseline.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 74%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAPCAYAAADkmO9VAAAACXBIWXMAAB2HAAAdhwGP5fFlAAACD0lEQVQ4y3VTa2/bMAzM//9vw75tK9a8nNRpEiVxrPeDuoGU46bFKuBgiKKOxxO9qLWilAL+MniRMUjWIuWMkxrRHwdcbwPG4YwcLWLKcgeo870HFnxgnQMRgUoBAcjnM+zlAuMcbmOCGhKc84jmBKcv0MYhBI8qlJ8hhMY6EFdgdRMkodZ51xQAUxOycnJSxN/WcOoFUb9jwcqC94h9j6wU8jDMSgXULJEYtaI5jPDXFdx1iTC+oSSLUhKIMhY5Z4zaIHuPdDwiHg4I6zVi1yEphRLjrLgkD3d9FaIU7qDa4lyIhYmHojDEueWWQCjOIb0fELcd0r6HP67gbn+Rk55bF9+pTN+JkB9EG/vxKGVKqNRUlQjT/YR9+YG42aKMpllR60fuMyE/iLH20wFXZbJkFez5F3J28giFve62iJuNWCHdlC8KSyFY68Dfh3xODPe9+NUS+WKebclaIyyX4vHDIppmWQi9D9NwN4OZiAk/DH9qrTT1TB73e8TdbragEj3mkFtmvzLs+Q+iVZLEZ1yw0BdwnMnZFqUQVivJk8HO8qd4ULIIl9+gpNvU1vI05t9AcgDSI9J6yQqwkInPGerwiuFuMXrgbjJGWwT3CeMXPOKcqyNwex9gur4Rjlpjuz9h+6aw3R2x61XbP2GzO6J7O3+O7U8S63jfK/iQGuE8g/R/VCqIMSCl9G0Og9c/+pyRiVI+zQ4AAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Loan Logic Mortgage Payment Chart Baseline&quot;
        title=&quot;Loan Logic Mortgage Payment Chart Baseline&quot;
        src=&quot;/static/5045574643e0ba41446bee2b4be57c87/b5cb9/ll-01-mortgage-payment-chart-baseline.png&quot;
        srcset=&quot;/static/5045574643e0ba41446bee2b4be57c87/0eb09/ll-01-mortgage-payment-chart-baseline.png 500w,
/static/5045574643e0ba41446bee2b4be57c87/1263b/ll-01-mortgage-payment-chart-baseline.png 1000w,
/static/5045574643e0ba41446bee2b4be57c87/b5cb9/ll-01-mortgage-payment-chart-baseline.png 1532w&quot;
        sizes=&quot;(max-width: 1532px) 100vw, 1532px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;She then sets up a new scenario with the extra expenses and windfalls and clicks on the scenario tile to compare with the baseline scenario.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 613px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/8d5eebc056a964252d7cebaed83fad96/ac863/ll-01-mortgage-events-scenario-tile.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 42%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAICAIAAAB2/0i6AAAACXBIWXMAAAsSAAALEgHS3X78AAABDklEQVQY05WPMU7DQBBFcylaai4DFR1XoOcC9JQgIQonWEIgIUhAwSHJAgI79tqOZ2dnbW/sHRQiiohQ5Hcz0pt5vyPTjJmdc7x9Ol9hBIUajwWV1erKf9kAZ3F6m4nT0R2nF5xdbvt5ZmqbgYZ5DvOCqAKFBQAoVKgBFAAgEaJu23YDXFktiyhXSa5iqhSiLpRSqLWm5UDGmJKI/pp3omQWTtzNOfV79v6qmvQb5vanY9s0DTtn8jwPAhACo6g2xtrFcr+CZRGeHfPBjjva48NdPtln8TEVb+9JnDy/jD6nYuB1Pa/XvfYHvv/68Pg0DOJE/sJp6hom1Zbalehste7mXGNMLWUt5QKR182/AY2Jxi6UZ+sTAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Loan Logic Mortgage Events Scenario Tile&quot;
        title=&quot;Loan Logic Mortgage Events Scenario Tile&quot;
        src=&quot;/static/8d5eebc056a964252d7cebaed83fad96/ac863/ll-01-mortgage-events-scenario-tile.png&quot;
        srcset=&quot;/static/8d5eebc056a964252d7cebaed83fad96/0eb09/ll-01-mortgage-events-scenario-tile.png 500w,
/static/8d5eebc056a964252d7cebaed83fad96/ac863/ll-01-mortgage-events-scenario-tile.png 613w&quot;
        sizes=&quot;(max-width: 613px) 100vw, 613px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 549px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/0856f562c0cb03a460fea969c6492c53/ffc28/ll-01-mortgage-scenario-compare.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 84.2%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAARCAIAAABSJhvpAAAACXBIWXMAAAsSAAALEgHS3X78AAAC70lEQVQ4y3WRS08TURiGT+IkGv+AkrgUddeYSPwzbrws2bo1YaHYqAs0MUAVhQZKaFO8gCJOYTqdkV6p8YbttDMUKNC5X6DMmcs5ZkYSMYEnz+J9F2++xQfi8TjDMCzLMCzDsmwuLCzL0ll6eZk6SYqiJiYmQKFQ0E2rIymKqkmyqhumpKiirB7YEJ8AQghjTFEUyOfzoij/4jdq6+1f/IawI9bW27X1tmjs2wh3vb+i0DC4aN/xMcZLS0vgW54dE/HlHLrO+H0svkZ7fQzqY/HVHIpQbmTZjSzDSNaLUF5QaXSVwRcpNN3BBSoD1opM/xoGSY8YqYMoC4a+gqFqEJ5+A6MN8GoLvBTA2AZ4IYCYACaVU7MQTFr3Gng1S4IfBebudwgeF8/MSMQrnpjYJGI1YqxJPFslRn8TrwXiBRc43gpz/fRYA8Sl+wKuZEnwc4Xu/41B0iXGt8BwPbgW3wUzVqgJEgqY1o5k/VTSAml0ePl7nnm+jc+R6App937q9i7s9y65vTS+SIVmQ6l/9VIWn19E8V1cpEjAMAzGWNvrmrZrQteEnnHgmF144CHo42O1/eBVJEkClmUhdDq7HUVRFFnWVE2SJFVRPcf13eP1HOdwnMvlMMaqquq6qemGqumWtSfLim6YCCH/OGwb+r4fjGma9jxf13XTtAzDNEzLsixN1/esPScEOtCGh0IHQsc5sG3H9YJxJpPpdrut1qYkK9vbOzu7HUmUNrfamqZDx4Ehzv9C23Yg/LS4GIxFUSxXKtVqdWUlX12tVgJWS8VSo9lstVq8IKw1uaP+4Gob4s7CwgIgSVJRFJ7nBZ5vNpuNRoPjuDpX5ziuVKmU8+UPX1fOR2/1PLzZE73d8+TOhUd3zkZvDORnv2So4LLv+0aIHqJpmhoiK7KuqnynPVh896D4brD0frA8Fy3PDxTflqR16nMGxGKxVCqVSCSmjjAZEoSpqempxNxMei4ZOJ9Mz6fSH1Nv3qffDI+M/AGKAiALW0uxbAAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Loan Logic Mortgage Scenario Compare&quot;
        title=&quot;Loan Logic Mortgage Scenario Compare&quot;
        src=&quot;/static/0856f562c0cb03a460fea969c6492c53/ffc28/ll-01-mortgage-scenario-compare.png&quot;
        srcset=&quot;/static/0856f562c0cb03a460fea969c6492c53/0eb09/ll-01-mortgage-scenario-compare.png 500w,
/static/0856f562c0cb03a460fea969c6492c53/ffc28/ll-01-mortgage-scenario-compare.png 549w&quot;
        sizes=&quot;(max-width: 549px) 100vw, 549px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;The way Loan Logic works is by tracking all events in a scenario as changes to an off-set account balance. In the chart below, the light blue line shows the offset account balance over time for the baseline scenario (flat on $0), and the dark blue line shows the offset account balance for the events scenario.&lt;/p&gt;
&lt;p&gt;Sarah can now easily see that even with the extra expenses, the windfalls will allow her to pay off her mortgage in 28 years which will save her more than $82,000 in interest.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 812px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/241f5c71c7750977462caee29e27ead6/d2c78/ll-01-mortgage-payment-chart-compare.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 114.80000000000001%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAXCAYAAAALHW+jAAAACXBIWXMAABJ0AAASdAHeZh94AAADk0lEQVQ4y22Va1MbNxSG/ff7S/q1H/qh08kkLQ1QSFNgaIAaE4fEBpu9aS+6rFbS05HWxiZUnrOSztF5/Z7LaiffFg/czuZ8nt9zdXPLap0RRwghPhlHSPtReDU6KWnaFmsHJt47emuw1m5AdmC9dWhj8WELuwH3AWN6lNb01r4An3S1R2QO27tnsCSA008MfU1wBrwGb5PeB493juD8XhCj3yRfBKancPsXnL0JrOZhE2KM5RLEIYhjgjgiyFnyba3ifXbHSjdciiUfinuG4EeGugtU60CdB5oiYM0OMNiSYB7ArKBfE7xJTtY7lrIiMx0LKRKwTw6ByfoLXLyDmxP4+Cs8fo4uPkUS6iNC+WZkVx0Quk9jEazmz3zO5zZP7I7zOW6T/0lQCp8XuKrBPuQ4Y59zGITAlxU+z9MZP4wFsG7ga1dSW82jqpM478cchuAJaRMTPM7bwni/sW0l6jah7ZX9RTEn4f8aa2+0XUclGvJSoLV5ZQ+bX0qU90zi0kpJWwlE0/GUC1ZZQ16UZHlBVawQIq4ziqykLgqe8iLZiqKkblqapn2OKDFUSqfGdj7QKYPsOqRUKKWQUtMbidYdnYx7iVISqQzKeLwPqfFfAEZn0/cpTy/Cienwu5wS9uxeEcw3XHfNID7i1RTvbXz1PFIb5NdvmKsrzHxOv1gw1HUqgN80kXdjDZzJ0E+/IZe/0C3fIZ8uUcUUP5h0/jnkPjIEhqbBPj5i7u7QFxf00ym+7fFodP6W7v4ndHGFNXL8s+9Dds4hlWYYhqTwm25Ih13ALh9o3/5I9fMPqH8Pdq9uBPJu9Nm2VASMzPKiSkVJRudwwzCy7Rvk0x9occlQCMyHc/TREa4qN+zG8y8A46Pr5HNRkhEw9T3tw3usKgl7rO1qhTo4wN7c7EXiXrdNb8zmagrI7BMyuybeTjE/zg24DfMIENtLnV+gT05wSuNSCvwO0BjD4EiV6lYf6Zvltjei+5bHd3sIi3v86THUYiSzDTneylqWyPXfWNMmY1471pWn6gJlGyiawDL3LLaSOR5amF+tyH4/JRTFrm1i6e9mN8y+rLm8a/hnVnA2rfhwU3JyPcrpdcX5THAxi3PF+W3J+bTgbN5yeHjH4/Se4ByTSH1wjkq0VKJGCEHTNNS1oNmTsizo2nqna8a5i3PX0rvxEzKJ+Yti+z5J39vU5P2LtUVrnT5Mo25fxrMRI87/AR9H8vGBqFP0AAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Loan Logic Mortgage Payment Chart Compare&quot;
        title=&quot;Loan Logic Mortgage Payment Chart Compare&quot;
        src=&quot;/static/241f5c71c7750977462caee29e27ead6/d2c78/ll-01-mortgage-payment-chart-compare.png&quot;
        srcset=&quot;/static/241f5c71c7750977462caee29e27ead6/0eb09/ll-01-mortgage-payment-chart-compare.png 500w,
/static/241f5c71c7750977462caee29e27ead6/d2c78/ll-01-mortgage-payment-chart-compare.png 812w&quot;
        sizes=&quot;(max-width: 812px) 100vw, 812px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Load to Teradata with a Python wrapper for TPT]]></title><description><![CDATA[PyTPT: A Python Wrapper for High-Performance Teradata Data Loading This blog post is paired with the associated Github repository https…]]></description><link>https://mortimer.xyz/python-teradata-tpt/</link><guid isPermaLink="false">https://mortimer.xyz/python-teradata-tpt/</guid><pubDate>Wed, 08 Oct 2025 12:00:00 GMT</pubDate><content:encoded>&lt;h1 id=&quot;PyTPT-A-Python-Wrapper-for-High-Performance-Teradata-Data-Loading&quot; style=&quot;position:relative;&quot;&gt;PyTPT: A Python Wrapper for High-Performance Teradata Data Loading&lt;a href=&quot;#PyTPT-A-Python-Wrapper-for-High-Performance-Teradata-Data-Loading&quot; aria-label=&quot;PyTPT A Python Wrapper for High Performance Teradata Data Loading permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h1&gt;
&lt;p&gt;This blog post is paired with the associated Github repository &lt;a href=&quot;https://github.com/mortie23/python-teradata&quot;&gt;https://github.com/mortie23/python-teradata&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;In my previous blog post about &lt;a href=&quot;https://mortimer.xyz/python-teradata/&quot;&gt;Connecting to Teradata from Python&lt;/a&gt;, I demonstrated how to connect to Teradata from Python using the &lt;code class=&quot;language-text&quot;&gt;teradatasql&lt;/code&gt; driver and load CSV data using the built-in &lt;code class=&quot;language-text&quot;&gt;teradata_read_csv&lt;/code&gt; function. While that approach works well for many use cases, it has limitations when dealing with larger datasets or when you need the full power of Teradata’s native loading tools.&lt;/p&gt;
&lt;p&gt;In this post I have implemented a Python package &lt;strong&gt;PyTPT&lt;/strong&gt; that is basically a wrapper for Teradata’s TPT (Teradata Parallel Transporter) that can be used for bulk loading of CSV files. This approach leverages Teradata’s optimized loading engine while maintaining the simplicity and flexibility of Python.&lt;/p&gt;
&lt;h2 id=&quot;What-is-TPT&quot; style=&quot;position:relative;&quot;&gt;What is TPT?&lt;a href=&quot;#What-is-TPT&quot; aria-label=&quot;What is TPT permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;From the &lt;a href=&quot;https://docs.teradata.com/r/Enterprise_IntelliFlex_VMware/Database-Administration/Loading-and-Exporting-Data-Application-DBAs/Teradata-Parallel-Transporter&quot;&gt;Official docs&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Teradata Parallel Transporter (PT) is an object-oriented client application that provides scalable, high-speed, and parallel data:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Extraction&lt;/li&gt;
&lt;li&gt;Loading&lt;/li&gt;
&lt;li&gt;Updating&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;A TPT job requires at least a &lt;em&gt;Job Script&lt;/em&gt; and &lt;em&gt;Job Variables&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;PyTPT bridges this gap by automatically generating these files from templates and orchestrating the entire loading process through Python.&lt;/p&gt;
&lt;h2 id=&quot;Local-Python-Environment-Setup&quot; style=&quot;position:relative;&quot;&gt;Local Python Environment Setup&lt;a href=&quot;#Local-Python-Environment-Setup&quot; aria-label=&quot;Local Python Environment Setup permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;For this implementation, I’m using:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Python 3.13&lt;/li&gt;
&lt;li&gt;Poetry for dependency management&lt;/li&gt;
&lt;li&gt;Windows and Linux compatibility&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Let’s set up the environment:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;powershell&quot;&gt;&lt;pre class=&quot;language-powershell&quot;&gt;&lt;code class=&quot;language-powershell&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;# Create your python virtual environment&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;# Linux&lt;/span&gt;
python &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;m venv ~&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;venv/pytpt
source ~&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;venv/pytpt/bin/activate

&lt;span class=&quot;token comment&quot;&gt;# Windows&lt;/span&gt;
python &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;m venv C:\venv\pytpt
C:\venv\pytpt\Scripts\Activate&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;ps1

&lt;span class=&quot;token comment&quot;&gt;# Install poetry and then packages&lt;/span&gt;
pip install poetry
poetry install&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2 id=&quot;Prerequisites&quot; style=&quot;position:relative;&quot;&gt;Prerequisites&lt;a href=&quot;#Prerequisites&quot; aria-label=&quot;Prerequisites permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Before we begin, you’ll need:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;A Teradata machine (I’m using TD Express VM)&lt;/li&gt;
&lt;li&gt;Teradata Studio (optional, but helpful for checking results)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Teradata Tools and Utilities (TTU)&lt;/strong&gt; - This is crucial as it includes the &lt;code class=&quot;language-text&quot;&gt;tbuild&lt;/code&gt; binary that TPT requires&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The TTU installation provides the &lt;code class=&quot;language-text&quot;&gt;tbuild&lt;/code&gt; command that PyTPT calls under the hood to execute TPT operations.&lt;/p&gt;
&lt;h2 id=&quot;Configuration-Structure&quot; style=&quot;position:relative;&quot;&gt;Configuration Structure&lt;a href=&quot;#Configuration-Structure&quot; aria-label=&quot;Configuration Structure permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;PyTPT uses a hierarchical configuration system powered by Hydra, making it easy to manage different environments and datasets.&lt;/p&gt;
&lt;h3 id=&quot;Project-Configuration&quot; style=&quot;position:relative;&quot;&gt;Project Configuration&lt;a href=&quot;#Project-Configuration&quot; aria-label=&quot;Project Configuration permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;The main project configuration in &lt;code class=&quot;language-text&quot;&gt;conf/config.yaml&lt;/code&gt;. The database default will then become the name of the next configuration file and must be the same name as the database you are planning to load to.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;yaml&quot;&gt;&lt;pre class=&quot;language-yaml&quot;&gt;&lt;code class=&quot;language-yaml&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;# Main configuration file for pytpt examples&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;# This file sets up the default configuration structure&lt;/span&gt;

&lt;span class=&quot;token key atrule&quot;&gt;defaults&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;database&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; PRD_ADS_PYTHON_NFL_DB
  &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; _self_

&lt;span class=&quot;token comment&quot;&gt;# Data directory configuration&lt;/span&gt;
&lt;span class=&quot;token key atrule&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;dir&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;../../data&quot;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;This configuration tells PyTPT where to find your CSV files and how they map to Teradata tables.&lt;/p&gt;
&lt;h3 id=&quot;Database-Configuration&quot; style=&quot;position:relative;&quot;&gt;Database Configuration&lt;a href=&quot;#Database-Configuration&quot; aria-label=&quot;Database Configuration permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;First, configure your Teradata connection in &lt;code class=&quot;language-text&quot;&gt;conf/database/PRD_ADS_PYTHON_NFL_DB.yaml&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;yaml&quot;&gt;&lt;pre class=&quot;language-yaml&quot;&gt;&lt;code class=&quot;language-yaml&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;# @package _global_&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;# Teradata NFL database configuration&lt;/span&gt;

&lt;span class=&quot;token key atrule&quot;&gt;database&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;ddl_host&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;tdvm&quot;&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;target_host&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;tdvm&quot;&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;working_database&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;PRD_ADS_PYTHON_NFL_DB&quot;&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;PRD_ADS_PYTHON_NFL_DB&quot;&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;ddl_logmech&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;TD2&quot;&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;target_logmech&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;TD2&quot;&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;# Table configurations for NFL data&lt;/span&gt;
&lt;span class=&quot;token key atrule&quot;&gt;tables&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;game&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;GAME&quot;&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;game_type&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;GAME_TYPE&quot;&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;game_stats&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;GAME_STATS&quot;&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;game_venue&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;GAME_VENUE&quot;&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;player&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;PLAYER&quot;&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;venue&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;VENUE&quot;&lt;/span&gt; 
  &lt;span class=&quot;token key atrule&quot;&gt;team_lookup&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;TEAM_LOOKUP&quot;&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;weather&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;WEATHER&quot;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2 id=&quot;How-PyTPT-Works&quot; style=&quot;position:relative;&quot;&gt;How PyTPT Works&lt;a href=&quot;#How-PyTPT-Works&quot; aria-label=&quot;How PyTPT Works permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;PyTPT follows a simple workflow:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Discovery&lt;/strong&gt;: Scans the configured data directory for CSV files&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Mapping&lt;/strong&gt;: Matches CSV files to configured table names&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Template Generation&lt;/strong&gt;: Creates TPT job variables and scripts from Jinja2 templates&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Execution&lt;/strong&gt;: Runs the TPT operations in sequence:
&lt;ul&gt;
&lt;li&gt;Drop existing table (if it exists)&lt;/li&gt;
&lt;li&gt;Create new table structure&lt;/li&gt;
&lt;li&gt;Load CSV data using TPT’s optimized bulk loading&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Let’s look at the core loading function:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;py&quot;&gt;&lt;pre class=&quot;language-py&quot;&gt;&lt;code class=&quot;language-py&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;load_table&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
    table_name&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    csv_file_path&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;bool&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token triple-quoted-string string&quot;&gt;&quot;&quot;&quot;Load a single CSV file to a table using TPT.&quot;&quot;&quot;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;templates &lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; create_tpt_files

    logger&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;info&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-interpolation&quot;&gt;&lt;span class=&quot;token string&quot;&gt;f&quot;Starting table load process: &lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;csv_file_path&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt; -&gt; &lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;table_name&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

    &lt;span class=&quot;token comment&quot;&gt;# Create TPT files&lt;/span&gt;
    jvar_file&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; drop_file&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; create_file&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; load_file &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; create_tpt_files&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
        table_name&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; csv_file_path
    &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

    &lt;span class=&quot;token comment&quot;&gt;# Step 1: Drop table&lt;/span&gt;
    logger&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;info&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-interpolation&quot;&gt;&lt;span class=&quot;token string&quot;&gt;f&quot;Step 1/3: Dropping table &lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;table_name&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    drop_success &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; run_tbuild&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;jvar_file&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; drop_file&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;drop&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;not&lt;/span&gt; drop_success&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
        logger&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;error&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-interpolation&quot;&gt;&lt;span class=&quot;token string&quot;&gt;f&quot;Failed to drop table &lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;table_name&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;False&lt;/span&gt;

    &lt;span class=&quot;token comment&quot;&gt;# Step 2: Create table&lt;/span&gt;
    logger&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;info&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-interpolation&quot;&gt;&lt;span class=&quot;token string&quot;&gt;f&quot;Step 2/3: Creating table &lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;table_name&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    create_success &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; run_tbuild&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;jvar_file&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; create_file&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;create&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;not&lt;/span&gt; create_success&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
        logger&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;error&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-interpolation&quot;&gt;&lt;span class=&quot;token string&quot;&gt;f&quot;Failed to create table &lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;table_name&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;False&lt;/span&gt;

    &lt;span class=&quot;token comment&quot;&gt;# Step 3: Load data&lt;/span&gt;
    logger&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;info&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-interpolation&quot;&gt;&lt;span class=&quot;token string&quot;&gt;f&quot;Step 3/3: Loading data into &lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;table_name&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    load_success &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; run_tbuild&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;jvar_file&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; load_file&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;load&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; load_success&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
        logger&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;success&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-interpolation&quot;&gt;&lt;span class=&quot;token string&quot;&gt;f&quot;Successfully completed table load: &lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;table_name&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
        logger&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;error&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-interpolation&quot;&gt;&lt;span class=&quot;token string&quot;&gt;f&quot;Failed to load data to &lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;table_name&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; load_success&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2 id=&quot;Template-Driven-TPT-Generation&quot; style=&quot;position:relative;&quot;&gt;Template-Driven TPT Generation&lt;a href=&quot;#Template-Driven-TPT-Generation&quot; aria-label=&quot;Template Driven TPT Generation permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;PyTPT uses Jinja2 templates to generate TPT files dynamically. Here’s an example of the create table template:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;sql&quot;&gt;&lt;pre class=&quot;language-sql&quot;&gt;&lt;code class=&quot;language-sql&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;-- Create table template TPT&lt;/span&gt;
DEFINE JOB create_table_job
DESCRIPTION &lt;span class=&quot;token string&quot;&gt;&apos;Create Table TPT JOB&apos;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
  STEP drop_table &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;APPLY&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;DROP TABLE {{working_database}}.&quot;{{table_name}}&quot;;&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;TO&lt;/span&gt; OPERATOR &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;$DDL&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Here is the job variables template:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;ini&quot;&gt;&lt;pre class=&quot;language-ini&quot;&gt;&lt;code class=&quot;language-ini&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;# Template Job variables file&lt;/span&gt;
&lt;span class=&quot;token key attr-name&quot;&gt;DDLTdpId&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token value attr-value&quot;&gt;&apos;&lt;span class=&quot;token inner-value&quot;&gt;{{ddl_host}}&lt;/span&gt;&apos;&lt;/span&gt;
&lt;span class=&quot;token key attr-name&quot;&gt;, DDLUserName&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token value attr-value&quot;&gt;&apos;&lt;span class=&quot;token inner-value&quot;&gt;{{ddl_username}}&lt;/span&gt;&apos;&lt;/span&gt;
&lt;span class=&quot;token key attr-name&quot;&gt;, DDLUserPassword&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token value attr-value&quot;&gt;&apos;&lt;span class=&quot;token inner-value&quot;&gt;{{ddl_password}}&lt;/span&gt;&apos;&lt;/span&gt;
&lt;span class=&quot;token key attr-name&quot;&gt;, DDLLogonMech&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token value attr-value&quot;&gt;&apos;&lt;span class=&quot;token inner-value&quot;&gt;{{ddl_logon_mech}}&lt;/span&gt;&apos;&lt;/span&gt;
&lt;span class=&quot;token key attr-name&quot;&gt;, TargetTdpId&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token value attr-value&quot;&gt;&apos;&lt;span class=&quot;token inner-value&quot;&gt;{{target_host}}&lt;/span&gt;&apos;&lt;/span&gt;
&lt;span class=&quot;token key attr-name&quot;&gt;, TargetUserName&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token value attr-value&quot;&gt;&apos;&lt;span class=&quot;token inner-value&quot;&gt;{{target_username}}&lt;/span&gt;&apos;&lt;/span&gt;
&lt;span class=&quot;token key attr-name&quot;&gt;, TargetUserPassword&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token value attr-value&quot;&gt;&apos;&lt;span class=&quot;token inner-value&quot;&gt;{{target_password}}&lt;/span&gt;&apos;&lt;/span&gt;
&lt;span class=&quot;token key attr-name&quot;&gt;, TargetLogonMech&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token value attr-value&quot;&gt;&apos;&lt;span class=&quot;token inner-value&quot;&gt;{{target_logon_mech}}&lt;/span&gt;&apos;&lt;/span&gt;
&lt;span class=&quot;token key attr-name&quot;&gt;, TargetWorkingDatabase&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token value attr-value&quot;&gt;&apos;&lt;span class=&quot;token inner-value&quot;&gt;{{working_database}}&lt;/span&gt;&apos;&lt;/span&gt;
&lt;span class=&quot;token key attr-name&quot;&gt;, LoadTargetTable&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token value attr-value&quot;&gt;&apos;&lt;span class=&quot;token inner-value&quot;&gt;{{working_database}}.&quot;{{table_name}}&quot;&lt;/span&gt;&apos;&lt;/span&gt;
&lt;span class=&quot;token key attr-name&quot;&gt;, LoadCharacterSet&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token value attr-value&quot;&gt;&apos;&lt;span class=&quot;token inner-value&quot;&gt;UTF8&lt;/span&gt;&apos;&lt;/span&gt;
&lt;span class=&quot;token key attr-name&quot;&gt;, FileReaderFileName&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token value attr-value&quot;&gt;&apos;&lt;span class=&quot;token inner-value&quot;&gt;{{csv_file_path}}&lt;/span&gt;&apos;&lt;/span&gt;
&lt;span class=&quot;token key attr-name&quot;&gt;, FileReaderFormat&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token value attr-value&quot;&gt;&apos;&lt;span class=&quot;token inner-value&quot;&gt;Delimited&lt;/span&gt;&apos;&lt;/span&gt;
&lt;span class=&quot;token key attr-name&quot;&gt;, FileReaderOpenmode&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token value attr-value&quot;&gt;&apos;&lt;span class=&quot;token inner-value&quot;&gt;read&lt;/span&gt;&apos;&lt;/span&gt;
&lt;span class=&quot;token key attr-name&quot;&gt;, FileReaderTextDelimiter&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token value attr-value&quot;&gt;&apos;&lt;span class=&quot;token inner-value&quot;&gt;,&lt;/span&gt;&apos;&lt;/span&gt;
&lt;span class=&quot;token key attr-name&quot;&gt;, FileReaderSkipRows&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token value attr-value&quot;&gt;1&lt;/span&gt;
&lt;span class=&quot;token key attr-name&quot;&gt;, FileReaderCharSet&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token value attr-value&quot;&gt;&apos;&lt;span class=&quot;token inner-value&quot;&gt;UTF8&lt;/span&gt;&apos;&lt;/span&gt;
&lt;span class=&quot;token key attr-name&quot;&gt;, FileReaderPrivateLogName&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token value attr-value&quot;&gt;&apos;&lt;span class=&quot;token inner-value&quot;&gt;.\logs\tpt_{{table_name}}_filereader.log&lt;/span&gt;&apos;&lt;/span&gt;
&lt;span class=&quot;token key attr-name&quot;&gt;, FileReaderQuotedData&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token value attr-value&quot;&gt;&apos;&lt;span class=&quot;token inner-value&quot;&gt;Optional&lt;/span&gt;&apos;&lt;/span&gt;
&lt;span class=&quot;token key attr-name&quot;&gt;, FileReaderMultipleRecordsInField&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token value attr-value&quot;&gt;&apos;&lt;span class=&quot;token inner-value&quot;&gt;Y&lt;/span&gt;&apos;&lt;/span&gt;
&lt;span class=&quot;token key attr-name&quot;&gt;, FileReaderEscapeQuoteDelimiter&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token value attr-value&quot;&gt;&apos;&lt;span class=&quot;token inner-value&quot;&gt;&quot;&lt;/span&gt;&apos;&lt;/span&gt;
&lt;span class=&quot;token key attr-name&quot;&gt;, FileReaderTruncateColumns&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token value attr-value&quot;&gt;&apos;&lt;span class=&quot;token inner-value&quot;&gt;Y&lt;/span&gt;&apos;&lt;/span&gt;
&lt;span class=&quot;token key attr-name&quot;&gt;, FileReaderIndicatorMode&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token value attr-value&quot;&gt;&apos;&lt;span class=&quot;token inner-value&quot;&gt;Y&lt;/span&gt;&apos;&lt;/span&gt;
&lt;span class=&quot;token key attr-name&quot;&gt;, LoadPrivateLogName&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token value attr-value&quot;&gt;&apos;&lt;span class=&quot;token inner-value&quot;&gt;.\logs\tpt_{{table_name}}_load.log&lt;/span&gt;&apos;&lt;/span&gt;
&lt;span class=&quot;token key attr-name&quot;&gt;, DDLPrivateLogName&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token value attr-value&quot;&gt;&apos;&lt;span class=&quot;token inner-value&quot;&gt;.\logs\tpt_{{table_name}}_ddl.log&lt;/span&gt;&apos;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2 id=&quot;Execution-and-Monitoring&quot; style=&quot;position:relative;&quot;&gt;Execution and Monitoring&lt;a href=&quot;#Execution-and-Monitoring&quot; aria-label=&quot;Execution and Monitoring permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;To run PyTPT just execute this command:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;ps1&quot;&gt;&lt;pre class=&quot;language-ps1&quot;&gt;&lt;code class=&quot;language-ps1&quot;&gt;python app.py&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The application provides logging throughout the process to files in the &lt;code class=&quot;language-text&quot;&gt;logs&lt;/code&gt; directory. Example of loading one table.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;log&quot;&gt;&lt;pre class=&quot;language-log&quot;&gt;&lt;code class=&quot;language-log&quot;&gt;&lt;span class=&quot;token date number&quot;&gt;2025-09-29&lt;/span&gt; &lt;span class=&quot;token time number&quot;&gt;17:11:13.883&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;token level info keyword&quot;&gt;INFO&lt;/span&gt;     &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; __main__&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;main&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;73&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt; Processing &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt; CSV files with table mappings
&lt;span class=&quot;token date number&quot;&gt;2025-09-29&lt;/span&gt; &lt;span class=&quot;token time number&quot;&gt;17:11:13.884&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;token level info keyword&quot;&gt;INFO&lt;/span&gt;     &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; __main__&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;main&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;82&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt; Processing&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token domain constant&quot;&gt;venue.csv&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; VENUE
&lt;span class=&quot;token date number&quot;&gt;2025-09-29&lt;/span&gt; &lt;span class=&quot;token time number&quot;&gt;17:11:13.884&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;token level info keyword&quot;&gt;INFO&lt;/span&gt;     &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; pytpt&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;tpt_runner&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;load_table&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;144&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt; Starting table load process&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token file-path string&quot;&gt;C:\git\github\mortie23\python-teradata\eng\pytpt\..\..\data\venue.csv&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; VENUE
&lt;span class=&quot;token date number&quot;&gt;2025-09-29&lt;/span&gt; &lt;span class=&quot;token time number&quot;&gt;17:11:13.919&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;token level info keyword&quot;&gt;INFO&lt;/span&gt;     &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; pytpt&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;tpt_runner&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;load_table&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;152&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt; Step &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; Dropping table VENUE
&lt;span class=&quot;token date number&quot;&gt;2025-09-29&lt;/span&gt; &lt;span class=&quot;token time number&quot;&gt;17:11:13.920&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;token level info keyword&quot;&gt;INFO&lt;/span&gt;     &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; pytpt&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;tpt_runner&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;run_tbuild&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;48&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt; Starting drop operation&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; tbuild &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;f render_tmp\VENUE_drop&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;tpt &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;v render_tmp\VENUE&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;jvar
&lt;span class=&quot;token date number&quot;&gt;2025-09-29&lt;/span&gt; &lt;span class=&quot;token time number&quot;&gt;17:11:20.383&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; SUCCESS  &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; pytpt&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;tpt_runner&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;run_tbuild&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;110&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt; drop operation completed successfully
&lt;span class=&quot;token date number&quot;&gt;2025-09-29&lt;/span&gt; &lt;span class=&quot;token time number&quot;&gt;17:11:20.383&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;token level info keyword&quot;&gt;INFO&lt;/span&gt;     &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; pytpt&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;tpt_runner&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;run_tbuild&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;111&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt; Complete TPT output saved to&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; logs\tpt_VENUE_drop_combined&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;log
&lt;span class=&quot;token date number&quot;&gt;2025-09-29&lt;/span&gt; &lt;span class=&quot;token time number&quot;&gt;17:11:20.383&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;token level info keyword&quot;&gt;INFO&lt;/span&gt;     &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; pytpt&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;tpt_runner&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;load_table&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;160&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt; Step &lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; Creating table VENUE
&lt;span class=&quot;token date number&quot;&gt;2025-09-29&lt;/span&gt; &lt;span class=&quot;token time number&quot;&gt;17:11:20.383&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;token level info keyword&quot;&gt;INFO&lt;/span&gt;     &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; pytpt&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;tpt_runner&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;run_tbuild&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;48&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt; Starting create operation&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; tbuild &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;f render_tmp\VENUE_create&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;tpt &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;v render_tmp\VENUE&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;jvar
&lt;span class=&quot;token date number&quot;&gt;2025-09-29&lt;/span&gt; &lt;span class=&quot;token time number&quot;&gt;17:11:26.824&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; SUCCESS  &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; pytpt&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;tpt_runner&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;run_tbuild&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;110&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt; create operation completed successfully
&lt;span class=&quot;token date number&quot;&gt;2025-09-29&lt;/span&gt; &lt;span class=&quot;token time number&quot;&gt;17:11:26.824&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;token level info keyword&quot;&gt;INFO&lt;/span&gt;     &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; pytpt&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;tpt_runner&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;run_tbuild&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;111&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt; Complete TPT output saved to&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; logs\tpt_VENUE_create_combined&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;log
&lt;span class=&quot;token date number&quot;&gt;2025-09-29&lt;/span&gt; &lt;span class=&quot;token time number&quot;&gt;17:11:26.824&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;token level info keyword&quot;&gt;INFO&lt;/span&gt;     &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; pytpt&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;tpt_runner&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;load_table&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;168&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt; Step &lt;span class=&quot;token number&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; Loading data into VENUE
&lt;span class=&quot;token date number&quot;&gt;2025-09-29&lt;/span&gt; &lt;span class=&quot;token time number&quot;&gt;17:11:26.825&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;token level info keyword&quot;&gt;INFO&lt;/span&gt;     &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; pytpt&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;tpt_runner&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;run_tbuild&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;48&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt; Starting load operation&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; tbuild &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;f render_tmp\VENUE_load&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;tpt &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;v render_tmp\VENUE&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;jvar
&lt;span class=&quot;token date number&quot;&gt;2025-09-29&lt;/span&gt; &lt;span class=&quot;token time number&quot;&gt;17:11:43.988&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; SUCCESS  &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; pytpt&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;tpt_runner&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;run_tbuild&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;110&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt; load operation completed successfully
&lt;span class=&quot;token date number&quot;&gt;2025-09-29&lt;/span&gt; &lt;span class=&quot;token time number&quot;&gt;17:11:43.988&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;token level info keyword&quot;&gt;INFO&lt;/span&gt;     &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; pytpt&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;tpt_runner&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;run_tbuild&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;111&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt; Complete TPT output saved to&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; logs\tpt_VENUE_load_combined&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;log
&lt;span class=&quot;token date number&quot;&gt;2025-09-29&lt;/span&gt; &lt;span class=&quot;token time number&quot;&gt;17:11:43.988&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;token level info keyword&quot;&gt;INFO&lt;/span&gt;     &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; pytpt&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;tpt_runner&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;run_tbuild&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;122&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt; Rows sent to RDBMS&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;32&lt;/span&gt;
&lt;span class=&quot;token date number&quot;&gt;2025-09-29&lt;/span&gt; &lt;span class=&quot;token time number&quot;&gt;17:11:43.989&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;token level info keyword&quot;&gt;INFO&lt;/span&gt;     &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; pytpt&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;tpt_runner&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;run_tbuild&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;124&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt; Rows applied&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;32&lt;/span&gt;
&lt;span class=&quot;token date number&quot;&gt;2025-09-29&lt;/span&gt; &lt;span class=&quot;token time number&quot;&gt;17:11:43.989&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; SUCCESS  &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; pytpt&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;tpt_runner&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;load_table&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;172&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt; Successfully completed table load&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; VENUE
&lt;span class=&quot;token date number&quot;&gt;2025-09-29&lt;/span&gt; &lt;span class=&quot;token time number&quot;&gt;17:11:43.989&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; SUCCESS  &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; __main__&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;main&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;86&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt; Successfully processed&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token domain constant&quot;&gt;venue.csv&lt;/span&gt;
&lt;span class=&quot;token date number&quot;&gt;2025-09-29&lt;/span&gt; &lt;span class=&quot;token time number&quot;&gt;17:11:43.990&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;token level info keyword&quot;&gt;INFO&lt;/span&gt;     &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; __main__&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;main&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;93&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt; Load process completed&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt; files loaded successfully
&lt;span class=&quot;token date number&quot;&gt;2025-09-29&lt;/span&gt; &lt;span class=&quot;token time number&quot;&gt;17:11:43.990&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; SUCCESS  &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; __main__&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;main&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;103&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt; All files loaded successfully&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;PyTPT also captures detailed TPT output in log files for troubleshooting:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;tpt_VENUE_create_combined.log&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;tpt_VENUE_load_combined.log&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;tpt_VENUE_drop_combined.log&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;Results&quot; style=&quot;position:relative;&quot;&gt;Results&lt;a href=&quot;#Results&quot; aria-label=&quot;Results permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;After I ran it on my machine the tables were created and records loaded.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 210px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/92f9f889dd8939f908579b029bb6cc26/7f56b/pytpt-database-tables.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 112.85714285714286%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAXCAYAAAALHW+jAAAACXBIWXMAABJ0AAASdAHeZh94AAAC7ElEQVQ4y41UTW/TQBDNX0EcICSOHceJP9Zee9cfa6ekqloBLUFtUXtphRCCSkjAASE4IXFBQuLXPjST2o0gKT2MN7vZfft23pvpPeg/wvNdjY+vXiBVGqaqEAQBlFJQKoOUEjovkGUpiqJAlkokiUSea/izGcrKwPdnsCwLtm2j97Dfx/5ug/OzEzjuFGmmEIYhEikhhEAURQgjASEiviQlQJnySP/pPMds6t0A0mcwtDB41MevrydY1CmSVCPXCrnWCAIfg8GAD9A4HA67aNdasNFohB59xuMxXHeM06cFlIwgRII4jvm5vu/zRjpwW7R7rgFdTFwXv78d42C3QGXmKHLNT6Q8ERvaR0zWxxZo/cLeauLAcRwsD3LEwsfMD5hZG5PJBK7rwvM8Hmm+zmo9GJA3uS5+fl5ib0dD6QJFkaMsS2SZgjEVqqpC0zQwpubflKKNgDc3jXB5ugOVRhBxzArSc9eftG3cCDiyLHz/cIi6iBGKDFVVIpVJp+Z6vjZd8lcObTiOjXv3+1jUEp/ePEUQhCirCnVdI/D9DnSTsv8ybBccBzoTOHrSsDBSJmwdz5t0yt7GrgNkhraNoWVj77HGx7dLRCJmdhRJkrAQJJIxhl+zzZu99Yk1sqESH0d7qmNIBqdSpDIkYKqcbez+BSSGixIfrl4iigTqpmGriChi+1TGwJu4LNSNSFttswLM0xDHz2rMZj4zSmTC3YdsxI0iDNnYd2a4mOe4er3kDkP5I1MTEOXQ1IZHmrcsbwUkUSoV4WxZd4zSLIPW6vpC6+4qtwzrUuLy/ICrRWt93QNTdgEBtrHV2H8zXBiJdxf7CMKIAfM8RxwLZEqjLAu2Dq25Y+d/tXxtmyzG8nAXnjdd9UQSJkm67k1rJNKdjU398Mv7E4g4QdPUMM0cmYxX3dqyutq+05PJU5E/QVNEXIbTqQdvOuU+uOnw7QydFcNn+wY/vl4gzTTm8zmzVFpj5/EOV8m6qTfZ5g9PtPQgBnGt4AAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Database Tables&quot;
        title=&quot;Database Tables&quot;
        src=&quot;/static/92f9f889dd8939f908579b029bb6cc26/7f56b/pytpt-database-tables.png&quot;
        srcset=&quot;/static/92f9f889dd8939f908579b029bb6cc26/7f56b/pytpt-database-tables.png 210w&quot;
        sizes=&quot;(max-width: 210px) 100vw, 210px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 792px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/ad2b13c414548b1a40b1c64cd68e7193/59f54/pytpt-game-table-load-results.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 35%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAHCAYAAAAIy204AAAACXBIWXMAABJ0AAASdAHeZh94AAABmElEQVQoz02QS2/TUBSE/a/YsChItE1byK9AQuKH8VhUIg1V6SMJtIUlQthNkzaOH4ljx2/Hjh9J+ZBvVMTiaO6Zc8/M6Eiv37zlydMttl7s83znJc+2D9hpvKKx1xRY1+7eIzb/vRv7zU3/33x79wCp0+3RPWtz9vk9385b/Ph6zPdeW2Dvy6HA6+4RlxefuO62OT/+yFWnxeVFi5PWO8FfdY7onR5y2v6AdDsYkkUz1umEP7nNQ2ZB5ZJHOuQ2FA7rdAprjywYUyUmlA7BbIBv3YpZERuQz3hY2ki/ZQXXGrFw78hDjcxXqRKDyB5SRJrgEvee1WKCb/WJ53cUkY41/sVk9JPQHhA7j7tjJFm5IXA0Um9EGZvkoc4qnZK4m752T31VJA/tIQtvJFLODQVv2hdGtWEZGyxDHUmWFXx7I1jEpiDXqUXk1EkMymSy4bLZRtBVqRZTHENhbirkkSH+1ifKIxNJuekTzjWWfi0wJg9UVokuTlBGY1HLYMR6YRA7A1LvnirW8CYy/lShjDWS+VDsLgOVv2LV23r+WDiDAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Game Table Load Results&quot;
        title=&quot;Game Table Load Results&quot;
        src=&quot;/static/ad2b13c414548b1a40b1c64cd68e7193/59f54/pytpt-game-table-load-results.png&quot;
        srcset=&quot;/static/ad2b13c414548b1a40b1c64cd68e7193/0eb09/pytpt-game-table-load-results.png 500w,
/static/ad2b13c414548b1a40b1c64cd68e7193/59f54/pytpt-game-table-load-results.png 792w&quot;
        sizes=&quot;(max-width: 792px) 100vw, 792px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;h2 id=&quot;Error-Handling-and-Robustness&quot; style=&quot;position:relative;&quot;&gt;Error Handling and Robustness&lt;a href=&quot;#Error-Handling-and-Robustness&quot; aria-label=&quot;Error Handling and Robustness permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The system validates that rows sent equal rows applied, providing confidence in data integrity.&lt;/p&gt;
&lt;h2 id=&quot;Comparison-with-Previous-Approach&quot; style=&quot;position:relative;&quot;&gt;Comparison with Previous Approach&lt;a href=&quot;#Comparison-with-Previous-Approach&quot; aria-label=&quot;Comparison with Previous Approach permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Feature&lt;/th&gt;
&lt;th&gt;teradatasql + teradata_read_csv&lt;/th&gt;
&lt;th&gt;PyTPT&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Performance&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Good for small-medium datasets&lt;/td&gt;
&lt;td&gt;Optimized for large datasets&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Complexity&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Simple, direct SQL&lt;/td&gt;
&lt;td&gt;Template-driven, more setup&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Scalability&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Limited by Python/ODBC&lt;/td&gt;
&lt;td&gt;Native TPT parallelism&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Error Handling&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Basic SQL error messages&lt;/td&gt;
&lt;td&gt;Detailed TPT logs and metrics&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Configuration&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Hardcoded in scripts&lt;/td&gt;
&lt;td&gt;Flexible YAML configuration&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Dependencies&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;teradatasql driver&lt;/td&gt;
&lt;td&gt;TTU installation required&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 id=&quot;Debugging&quot; style=&quot;position:relative;&quot;&gt;Debugging&lt;a href=&quot;#Debugging&quot; aria-label=&quot;Debugging permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Some of the defaults I have chosen were a result of issues loading certain types of CSV files that took a long time to find the correct variables.&lt;/p&gt;
&lt;p&gt;For example, loading CSV data where text fields have newlines such as:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;csv&quot;&gt;&lt;pre class=&quot;language-csv&quot;&gt;&lt;code class=&quot;language-csv&quot;&gt;&lt;span class=&quot;token value&quot;&gt;integer_field&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;token value&quot;&gt;text_field_with_newline&lt;/span&gt;
&lt;span class=&quot;token value&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;token value&quot;&gt;test&lt;/span&gt;
&lt;span class=&quot;token value&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;token value&quot;&gt;test&lt;/span&gt;
&lt;span class=&quot;token value&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;token value&quot;&gt;&quot;this long line.
that has newlines&quot;&lt;/span&gt;
&lt;span class=&quot;token value&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;token value&quot;&gt;test&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Requires these job variables:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;ini&quot;&gt;&lt;pre class=&quot;language-ini&quot;&gt;&lt;code class=&quot;language-ini&quot;&gt;&lt;span class=&quot;token key attr-name&quot;&gt;, FileReaderQuotedData&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token value attr-value&quot;&gt;&apos;&lt;span class=&quot;token inner-value&quot;&gt;Optional&lt;/span&gt;&apos;&lt;/span&gt;
&lt;span class=&quot;token key attr-name&quot;&gt;, FileReaderMultipleRecordsInField&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token value attr-value&quot;&gt;&apos;&lt;span class=&quot;token inner-value&quot;&gt;Y&lt;/span&gt;&apos;&lt;/span&gt;
&lt;span class=&quot;token key attr-name&quot;&gt;, FileReaderEscapeQuoteDelimiter&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token value attr-value&quot;&gt;&apos;&lt;span class=&quot;token inner-value&quot;&gt;&quot;&lt;/span&gt;&apos;&lt;/span&gt;
&lt;span class=&quot;token key attr-name&quot;&gt;, FileReaderTruncateColumns&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token value attr-value&quot;&gt;&apos;&lt;span class=&quot;token inner-value&quot;&gt;Y&lt;/span&gt;&apos;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;</content:encoded></item><item><title><![CDATA[If cloud platforms were people (thoughts from a Generative AI)]]></title><description><![CDATA[Can you relate to these? As of writing this blog post, I have been really enjoying the new releases in Google’s Gemini AI for both images…]]></description><link>https://mortimer.xyz/if-cloud-platforms-were-people/</link><guid isPermaLink="false">https://mortimer.xyz/if-cloud-platforms-were-people/</guid><pubDate>Wed, 10 Sep 2025 12:00:00 GMT</pubDate><content:encoded>&lt;h1 id=&quot;Can-you-relate-to-these&quot; style=&quot;position:relative;&quot;&gt;Can you relate to these?&lt;a href=&quot;#Can-you-relate-to-these&quot; aria-label=&quot;Can you relate to these permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h1&gt;
&lt;p&gt;As of writing this blog post, I have been really enjoying the new releases in Google’s Gemini AI for both images (Nano Banana) and videos (Veo 3). I was inspired to continue from my previous blog post &lt;a href=&quot;https://mortimer.xyz/if-programming-languages-were-people-ai-thoughts/&quot;&gt;If programming languages were people (thoughts from a Generative AI)&lt;/a&gt; and try something new. The following is the conversation I had with ChatGPT and the resulting generated images and videos.&lt;/p&gt;
&lt;h1 id=&quot;The-prompt&quot; style=&quot;position:relative;&quot;&gt;The prompt&lt;a href=&quot;#The-prompt&quot; aria-label=&quot;The prompt permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h1&gt;
&lt;h2 id=&quot;Christopher&quot; style=&quot;position:relative;&quot;&gt;Christopher:&lt;a href=&quot;#Christopher&quot; aria-label=&quot;Christopher permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;I wrote this blog post &lt;a href=&quot;https://mortimer.xyz/if-programming-languages-were-people-ai-thoughts/&quot;&gt;https://mortimer.xyz/if-programming-languages-were-people-ai-thoughts/&lt;/a&gt; in 2024, it was fun and (some) people liked it. This time I wanted to use Google Veo 3 to do something similar. “What AI thinks cloud platforms would be if they were personified”?&lt;/p&gt;
&lt;p&gt;I first will use ChatGPT (you) to generate the prompts for the videos and then pass that to Google Veo3.&lt;/p&gt;
&lt;p&gt;Can you write a short script for each of these to generate the 8 second videos&lt;/p&gt;
&lt;h1 id=&quot;The-responses&quot; style=&quot;position:relative;&quot;&gt;The responses&lt;a href=&quot;#The-responses&quot; aria-label=&quot;The responses permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h1&gt;
&lt;h2 id=&quot;ChatGPT&quot; style=&quot;position:relative;&quot;&gt;ChatGPT:&lt;a href=&quot;#ChatGPT&quot; aria-label=&quot;ChatGPT permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Thanks for sharing your blog post—from …&lt;/p&gt;
&lt;p&gt;This is a great fit for Veo. Short, snappy, visually symbolic, and fun. For each 8-second clip, Veo prompts work best when they’re compact, descriptive, and directive, almost like “scene notes”.&lt;/p&gt;
&lt;p&gt;Here’s a draft script for each of your three “cloud humans”:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;The following video prompts were generated by ChatGPT, and videos were generated with Google Veo 3.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&quot;AWS---The-Swiss-Army-Person&quot; style=&quot;position:relative;&quot;&gt;AWS - The Swiss Army Person&lt;a href=&quot;#AWS---The-Swiss-Army-Person&quot; aria-label=&quot;AWS   The Swiss Army Person permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;A human shaped like a Swiss Army multitool in a bright workshop. Wearing a rugged jacket with endless orange and black gadgets and attachments. Tools unfold rapidly from arms and shoulders, sparks flying as gears click. Camera zooms in on many hands juggling tools, then pulls back to reveal a cluttered, overwhelming workspace. Ambient audio of clanking metal and machinery hum.&lt;/p&gt;
&lt;div class=&quot;gatsby-resp-iframe-wrapper&quot; style=&quot;padding-bottom: 56.25%; position: relative; height: 0; overflow: hidden; margin-bottom: 1rem&quot; &gt; &lt;iframe src=&quot;https://www.youtube.com/embed/vLX8wmbRV_g&quot; title=&quot;AWS - The Swiss Army Person&quot; frameborder=&quot;0&quot; allow=&quot;accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share&quot; allowfullscreen=&quot;&quot; style=&quot; position: absolute; top: 0; left: 0; width: 100%; height: 100%; &quot;&gt;&lt;/iframe&gt; &lt;/div&gt;
&lt;h3 id=&quot;My-take-from-personal-experience&quot; style=&quot;position:relative;&quot;&gt;My take from personal experience&lt;a href=&quot;#My-take-from-personal-experience&quot; aria-label=&quot;My take from personal experience permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 1024px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/703992ddf01c28a13688ec7be20fbaa3/42a19/cloud-platforms-were-people-aws-human.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 100%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAUCAIAAAAC64paAAAACXBIWXMAAAsTAAALEwEAmpwYAAAEWElEQVQ4yxXTyW7jZAAAYIuhnTZN4zrxGq/xktj+vSRektjZ1yZpm3TapO20SSbdaUcaJCpGiGUYIYFYJMSFC0eucOECEoIDB0CckEAcB14FzTt8H7R8P7oOYxjFIxiNs6IAHLO8Ob17//s///rt3/9+ffHix7//uX7+mdceyaZP8Ok4ySI4kyDY5ZV1aDUCJ3CGlTSSk2lJ17xqZXR8/t7Hn3/73dc///LN73989cNPTz79sn10qQctXrVxVmJlPckrkbUNaD2WQElWVG0ubYqmb5e7W48en77z0fb8qjt5dHj9+uzu3dsPvxhd3gWdnbQT8qqt54oYycVgDKJ5mWLEtOkZhboRtAr9g92ru7Nnn1Q6AwCyEic2diaLp897u0etvZld7QG/mpIBRnIkJ0McL6m2L6QNt9LNbz7oThaT195YPH0+np+Xs1bZBLPrxxdvf3Dz1rPx7ZvhYBI2+qICDDdQDAcSODFrOZqdt4r1+ui4P398cftkuDueDgf5FO+lhOvp0fz8av/0tn9y1ZksnLCZNrycWxAkFeIFJWs7bqHq1gf10cn+zdPD0+vFbFa2TBJG8NhGw3PPzs8HO6Phw0Vzb+63doq1tmO5vAwgmhUzmuW7xXxzsHVysTe/ujw+CmqtRbfU9J2K6+zWy93N/nw6HV/fbR2fFwdjN18CioZRKShOMLJqFfJhqbuzc3KxPb0aGDJDJB/WCgfN8lm/PmuXeBQr5dze7GZyelPpjexc3jQsSQFQPEHRgqpl89Xudu/4slzrNAQKx6idHKhomZ4NepqEImiJRlv11u7itrI59Io1TTOZVBoicJoVMprt+9VO7/C0HxQzGJpglazlAI5zJcnWtERSyJDEsBJ290+C5sDI5hlWQuMExMlAUG2QLbS2x9tHi/3hkKF5WtIARZg8n+U5geElw03S/IOtwdbeYdgcgGxe0F0xbUBepe3VOkF3uLl/3Nt72ArDAtAl1cwJnEzRmSStK2oR6K6m9RuNaqtf6+12h+Nw+0D3KhBJpzQ777uFarUdNnulfMH1A4IWWCLJoCSPkXSSFygm5xYapXJYazebm4V8mDFydFKAYjBGMimGVzTVME3b0vT+0RnPCDocpeJYKoGK61FeSHf2pyCjezk3o1uMqFFcGokTUCQaX4dRIinIqpVRTUMDjeGhICj4OkyxIkUL2FpUENPdvRNdSmump2gWTvExGFuJwBCM4HEsiVOcotmq4YgK0C0PJ2iU4qIwGt3AUZTgONGv9VKiarhBBjg0J+MEjSQICEHwJEFLui1pWdXyskEDOMEGgsXgBMHKJCfFElQcJYPW4KVqyzdyRdMrqykZQXBoaSkCxwmKk17GTIMwrOXCRjSWWLm/ZgUNCThLy2sbCSqsd22nKCo6TnGRKAJsP8mkoHv37r+6tLoagTEsmeLljGoBt0QiWGRlLVfuZHJBZHmVJRnDDSVFp2ghBqOv3FsJXUvmmf8BJez2iV+Kp0MAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;cloud platforms were people aws human&quot;
        title=&quot;cloud platforms were people aws human&quot;
        src=&quot;/static/703992ddf01c28a13688ec7be20fbaa3/42a19/cloud-platforms-were-people-aws-human.png&quot;
        srcset=&quot;/static/703992ddf01c28a13688ec7be20fbaa3/0eb09/cloud-platforms-were-people-aws-human.png 500w,
/static/703992ddf01c28a13688ec7be20fbaa3/1263b/cloud-platforms-were-people-aws-human.png 1000w,
/static/703992ddf01c28a13688ec7be20fbaa3/42a19/cloud-platforms-were-people-aws-human.png 1024w&quot;
        sizes=&quot;(max-width: 1024px) 100vw, 1024px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;AWS, although extremely capable, can seem complex. To get from vanilla to a working solution with correct IAM isn’t easy. I was once forced to sign up for &lt;strong&gt;developer support&lt;/strong&gt; on an AWS account to get passed an error just to be told I did nothing wrong, my IAM was correct and it was an “internal” issue that has been resolved (of course I asked for a refund for the developer support and they said yes, I can’t fault the support team at all).&lt;/p&gt;
&lt;p&gt;The names of the services, the shear number of them with many providing overlapping solutions.&lt;/p&gt;
&lt;p&gt;I definitely feel the:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;pulls back to reveal a cluttered, overwhelming workspace&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&quot;Azure---The-Polished-Corporate-Networker&quot; style=&quot;position:relative;&quot;&gt;Azure - The Polished Corporate Networker&lt;a href=&quot;#Azure---The-Polished-Corporate-Networker&quot; aria-label=&quot;Azure   The Polished Corporate Networker permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;A corporate professional in a sleek blue suit, standing in a futuristic glass office. Blue holographic network diagrams float around them as they connect glowing threads between icons. They pause to check a floating Outlook email window, then smoothly return to connecting nodes. Camera slowly circles around. Soft background lounge music, occasional email notification chime. Color theme: Azure blue and silver glow.&lt;/p&gt;
&lt;div class=&quot;gatsby-resp-iframe-wrapper&quot; style=&quot;padding-bottom: 56.25%; position: relative; height: 0; overflow: hidden; margin-bottom: 1rem&quot; &gt; &lt;iframe src=&quot;https://www.youtube.com/embed/qzhUBeeI1CE&quot; title=&quot;Azure - The Polished Corporate Networker&quot; frameborder=&quot;0&quot; allow=&quot;accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share&quot; allowfullscreen=&quot;&quot; style=&quot; position: absolute; top: 0; left: 0; width: 100%; height: 100%; &quot;&gt;&lt;/iframe&gt; &lt;/div&gt;
&lt;h3 id=&quot;My-take-from-personal-experience-1&quot; style=&quot;position:relative;&quot;&gt;My take from personal experience&lt;a href=&quot;#My-take-from-personal-experience-1&quot; aria-label=&quot;My take from personal experience 1 permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 1024px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/ea40e7d8f0e868f4f03725c4a53fac0b/42a19/cloud-platforms-were-people-azure-human.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 100%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAUCAIAAAAC64paAAAACXBIWXMAAAsTAAALEwEAmpwYAAAEz0lEQVQ4ywHEBDv7ADxNXoKZrxciNBEZIxQdJxUeKBYfKhgiLRsmMxwnNRwoNRwoNRsmMxcgKxYfKhUeKBQcJg0XJXCEmFxugQAIEh1RZnt7k6oZJzkZJjUdKzoeLDsmNkknOEstP1MvQFUqO08oN0ogLj8cKjodKzsWJDVtgphpgJYOGSYAGyg1Gi9CZX2UdY6kMEFXPE9lTHCLT3CMQ1dtQ0ZRPkFMRVpwQVZuOk1iOk9kP1x3bIacdI+nGCg7FR8qAB8xQzRUcCY6UWKAm1x6llRviHmjvn2lv2t/kpd6cG9QSl9yh32ivWOAmWGDnnamxGyQrCxDWiAuPx0sPQArQ15Mco9Lc5FNdZVnmbdkiqRjhqF6p8GFn7K2komMamV7jqKDsMpjhZ9ehKFxpMJPdpY/WnVOdJM1UnAAMElmXI6udazJb6nIfLXSeqnDbI2mibDHlLjLpZCOalFSlbXIkrXJcpKpcJSucJu4VHudW4alWoWkOFNxADdQa2OUsn+51J7N4Ye61IayyoiovKnI16HD1Y2OmFpgbD9aeoipwY2twoquxJPC2ZLC22iWtlBykD5XdABDXHZrm7iIwNra9vyy3OuhxNaguMd1mLhcg6l0iaBifJoPLFolSHdQcZOpy92xxtWEl6ttkKlkhJ5JZIAAVmyCdpSpfJitbXaIpLHAsMfUiaO2R3CcLUx1PFV4LEt3LUpyESRIHzpdxNrmgouWMT5RjJ+wg5apanmOAHF9joqQnIONmklTYml7jNvk5nGMpjhfix83Xxs2YCA4YBQhPRUmQiRIc4KYr0xYZoyOk9Ha4La/yYaIkwBwhZyQn7Bqdolqeo0lMkZXaoJEYYIfO2EpR28iPmQfO2EdOFc0TGkuSG0XMVkbJDqJipK0tb2doa1ud4cARVt0VXGLUm+IRVx2MkJaBBIuBxc1OE5pJEFnITxfHjZVBhQtVmiATVx0AAQgOERcY3uSWmuBW2mARVtyACMwQSo/Uz9ng2OIo3aPomyClmJ0iGl+lRUtVRYtUw8gQAINJVlthmRzhldqf3yOokBPXSo3RzpNYycyQgASGCIpOUoyR1ljfY5leIhrfo12iJZthp4RJ0sQJEYNHToBCyNNXXRjdYRndoZod4ZIV2Y3RVMxP1AKDhYACw4WKThHGiQvJjREKzxOMEJVRFdqO1d3DR49DR04GC5NDSA8LEppU3OMQVx1OlNpGSQxIS48KTdHBwoRACAuPyAtOz9Wall0h2R8jXePn5iywEhmiQgXNA4eNy5WeB5DaEFqjJfE15TD13GfuWB8kExmfCQzQxomNgAmN0guQFE1SV5Xc4l0jZ+Mp7i61OBQaYkCDikQIj0pT3AbP2I1XX6t2Oai0OJ6pr9beZFTcIg2S2AjNEcAFyMvIC09HSo3NEheUGqAa4qhl7TET2aAAAYcCxcrGjZTCyRAOVdxncPVg6zCYYefLD1QJTJCHzBCFiQzABEaJRQcJiAvPjpRaERddU1pgGyLoD1UbwAIHAMHEwgSKQAGFyUxRXCOpFdyiUNddCo9UBsvQS86RxwkLwAOFB0SGiQfLTwuQVYpOk0sPlM4TmQmOFABBxgLEiIPGjAABRUUHzQ4T2gtQFYsPVEqRFwZSWkyOkQaICoWnMBo677inQAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;cloud platforms were people azure human&quot;
        title=&quot;cloud platforms were people azure human&quot;
        src=&quot;/static/ea40e7d8f0e868f4f03725c4a53fac0b/42a19/cloud-platforms-were-people-azure-human.png&quot;
        srcset=&quot;/static/ea40e7d8f0e868f4f03725c4a53fac0b/0eb09/cloud-platforms-were-people-azure-human.png 500w,
/static/ea40e7d8f0e868f4f03725c4a53fac0b/1263b/cloud-platforms-were-people-azure-human.png 1000w,
/static/ea40e7d8f0e868f4f03725c4a53fac0b/42a19/cloud-platforms-were-people-azure-human.png 1024w&quot;
        sizes=&quot;(max-width: 1024px) 100vw, 1024px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;Azure is usually used when at work. It can sometimes feel forced upon you because, well, your enterprise IT team already supports Microsoft Windows and M365, and probably already has a great relationship with Microsoft. Azure is chosen as the cloud and it focuses on being appealing to large enterprises.&lt;/p&gt;
&lt;p&gt;I chuckled when the AI titled it:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Corporate Networker&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&quot;Google-Cloud---The-Data-Savvy-Friend&quot; style=&quot;position:relative;&quot;&gt;Google Cloud - The Data-Savvy Friend&lt;a href=&quot;#Google-Cloud---The-Data-Savvy-Friend&quot; aria-label=&quot;Google Cloud   The Data Savvy Friend permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;A cheerful person in colorful casual clothes, sitting at a cafe table. Bright holographic charts and icons in Google’s red, yellow, green, and blue colors hover around them. They swipe through graphs with ease, wink at the camera, and slide a glowing chart to someone nearby. Camera pans close to their friendly face, then tilts up to show vibrant floating data visuals. Cafe ambience with light upbeat tones and a soft chime.&lt;/p&gt;
&lt;div class=&quot;gatsby-resp-iframe-wrapper&quot; style=&quot;padding-bottom: 56.25%; position: relative; height: 0; overflow: hidden; margin-bottom: 1rem&quot; &gt; &lt;iframe src=&quot;https://www.youtube.com/embed/m47icKVig5k&quot; title=&quot;Google Cloud - The Data-Savvy Friend&quot; frameborder=&quot;0&quot; allow=&quot;accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share&quot; allowfullscreen=&quot;&quot; style=&quot; position: absolute; top: 0; left: 0; width: 100%; height: 100%; &quot;&gt;&lt;/iframe&gt; &lt;/div&gt;
&lt;h3 id=&quot;My-take-from-personal-experience-2&quot; style=&quot;position:relative;&quot;&gt;My take from personal experience&lt;a href=&quot;#My-take-from-personal-experience-2&quot; aria-label=&quot;My take from personal experience 2 permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 1024px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/ad2cea8f84fbf6013175d08d75dd7831/42a19/cloud-platforms-were-people-gcp-human.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 100%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAUCAIAAAAC64paAAAACXBIWXMAAAsTAAALEwEAmpwYAAAEuElEQVQ4ywXB6U9TBwAA8PfJDAWhd1/bd/S99vW9Hq8HpS29KKW0pdKWtvSGHpYWQRAYyCFqI3ggqGUasYID1CAR5mRzi3HOmGCW6bJF45JdX5bt82KyZH/Afj+ggclnMEAOU8DnCHksPsjiw1wBBkISIYwJYFSIyGAxLcZIEYyJEFQkFgjEAgGGQLgKlwIwm+fRyZUYbtXQjQqVUiIjMQkqQHAQphBcgeIUitM44Wk2Fl1tIZvFJJdHzI1tKsqjJAGRAKVgdKJT++buyJ0xX8ysNpEyv93U7jtiVKpTTrOWoOIO0/N7i/tXzpyLeXpamtIOU4fJ6LXZARiSihHSZTQWjrQm2u1airaoNSFnSyAWzTn0PhILqslzMdetY8nT6choJnYimyqPj6xVLlSvXgAQvhDmCaN26+pWNdgVtmv0xWi8193RZaSPN+EjXrtPr++xaCZ9lmKgc/+z6s/7O7++/vyXV1sblfMAIUHUJJmPBHZW5iay3Xm3YywaKsbD3kb11oDn69HwjK+1ZFDPemzb08f++/vlh9/23j5dfXb/2sbCDKDHca9GmbI1DfnbF/tScwO9lQuFuYlim9u3OxV9MRTc6AlsZiKng66Hc8O///H8/f69H79ce7K9+uJBBaBR1CxXuJv0kyHvXClyvXJ569HH5wvhXMh/eyzxtpzazPqro9nKQPLOSM/O2sKT9WvPtqsP12+USwlAikgaUcRj0J9NhFdmi98+Xtu9OTUYDZ7KRif7j+6WcxvdtotBdy6ZuVspby2XP10s//RVdef2QqeaAihK69co3fbWTCpd6PCdSge/GEkcjwSWZ0e/2716cyTxTc5Wzfp93sjk1PSD+RO3xot7t+Ynulw5bxtgUKnzgY5Md9fG/PhYvLvocbxMW67F2saOJt89urQ6Vfj+9vSVofR8X3Kw7+h8f2Ztqv9eeTjvaaVkKkCDYS4tPdDSNONrni91zw323h2KPDmZ3Ls+uXG6f+lY8vXOwubJ/I3+eCkVnyz0rI8mllIdKbtJLiUBHSw0YrCVbsy5Wpx6erg3+fLO3L/Pl3ZmCzFbc6i5aXW6b2U4ca43kPR3zmTC/R02h4Iwy6QkJgO8OBhVY+0m00IhUuh0Dofa32yefXe58LjUVY54I1bjpd7AJ8P5fCxi1RsGA+5KUJdoJBAIh4QosBQwrve5zkRa1ocDH14v/PXqyj8/LL9fLizaVWe95lG39aLP0qnT6eS0kVIY6MaiFnVhoJAPCTg84LhdfT5gWYk7np4M/rk3+XZ7/P4J/6Vwc8lEZvVERk+ElGKnFDahAi0CkYSKhiAFyAP5EMjmAzibaUJApwxplUKtMqQZh3UwpEVhjRg1UjI5ghAQaqAoQgRLUUKp0BkoBQmCbD7CYYEAyGRLeTwBV8CqY/BZbAUmoQlCJaMUpBzHCDZXVFfPZbFBBJJKpSpCoqTFmJzFQBlMVv1hoIOEB6zyAacW43JhNrdJgoE8YQODX1PH+aiWc6CmobaeDWFyIaZq4IlFAtSMI4TOMhZMN5sigJR5mOI00Hw281CdjM/XiZEDh5g1Bxk8BudwTW3twXpITAnFygYOUlPLkYCgRyFGrW0Kb06lT/8PbY1umqY3prEAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;cloud platforms were people gcp human&quot;
        title=&quot;cloud platforms were people gcp human&quot;
        src=&quot;/static/ad2cea8f84fbf6013175d08d75dd7831/42a19/cloud-platforms-were-people-gcp-human.png&quot;
        srcset=&quot;/static/ad2cea8f84fbf6013175d08d75dd7831/0eb09/cloud-platforms-were-people-gcp-human.png 500w,
/static/ad2cea8f84fbf6013175d08d75dd7831/1263b/cloud-platforms-were-people-gcp-human.png 1000w,
/static/ad2cea8f84fbf6013175d08d75dd7831/42a19/cloud-platforms-were-people-gcp-human.png 1024w&quot;
        sizes=&quot;(max-width: 1024px) 100vw, 1024px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;GCP feels the most approachable and &lt;em&gt;can&lt;/em&gt; be more enjoyable (with the fair share of IAM pain). The terminology around “projects” seems to make sense to how you normally separate work. You can get a solution up in GCP quickly from vanilla. The fact the AI focuses on &lt;em&gt;data-savvy&lt;/em&gt; may be because GCP has BigQuery which is a great serverless database if you have big data solutions to tackle.&lt;/p&gt;
&lt;p&gt;My favourite line form the AI generated prompt:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;wink at the camera, and slide a glowing chart to someone nearby&lt;/p&gt;
&lt;/blockquote&gt;</content:encoded></item><item><title><![CDATA[Quackpipe, running DuckDB local and on AWS Glue]]></title><description><![CDATA[Quackpipe Stop waiting for AWS Glue jobs while in development by using a local-first workflow with DuckDB and deploy with Terraform.
Your…]]></description><link>https://mortimer.xyz/quackpipe-aws-glue-duckdb/</link><guid isPermaLink="false">https://mortimer.xyz/quackpipe-aws-glue-duckdb/</guid><pubDate>Fri, 15 Aug 2025 12:00:00 GMT</pubDate><content:encoded>&lt;h1 id=&quot;Quackpipe&quot; style=&quot;position:relative;&quot;&gt;Quackpipe&lt;a href=&quot;#Quackpipe&quot; aria-label=&quot;Quackpipe permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h1&gt;
&lt;p&gt;Stop waiting for AWS Glue jobs while in development by using a local-first workflow with DuckDB and deploy with Terraform.
Your new addiction to fast, flexible data pipelines. The quickest high from local to cloud.&lt;/p&gt;
&lt;p&gt;Based on the following Github repository &lt;a href=&quot;https://github.com/mortie23/quackpipe&quot;&gt;https://github.com/mortie23/quackpipe&lt;/a&gt;&lt;/p&gt;
&lt;h3 id=&quot;Developing-on-a-platform-that-wasnt-made-for-it&quot; style=&quot;position:relative;&quot;&gt;Developing on a platform that wasn’t made for it&lt;a href=&quot;#Developing-on-a-platform-that-wasnt-made-for-it&quot; aria-label=&quot;Developing on a platform that wasnt made for it permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Have you ever had the pain of being in a situation where, as a developer you are asked to do your development inside a tool that really isn’t meant for development, like the AWS Glue web console.&lt;/p&gt;
&lt;p&gt;Each change to your code leads to:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;slow runs/job startups that take half a minute&lt;/li&gt;
&lt;li&gt;limited debuggability, where you can’t add breakpoints&lt;/li&gt;
&lt;li&gt;multiple clicks to open log files&lt;/li&gt;
&lt;li&gt;downloading and opening resulting files from S3 is slow&lt;/li&gt;
&lt;li&gt;packaging friction and dependency trial-and-error&lt;/li&gt;
&lt;li&gt;permissions / IAM / S3 path errors only show up late&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Not the optimal DX (Developer Experience).&lt;/p&gt;
&lt;h3 id=&quot;Quack-Pipe-gives-you-a-local-first-workflow&quot; style=&quot;position:relative;&quot;&gt;Quack Pipe gives you a local-first workflow&lt;a href=&quot;#Quack-Pipe-gives-you-a-local-first-workflow&quot; aria-label=&quot;Quack Pipe gives you a local first workflow permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;Develop and iterate Python (and DuckDB SQL) locally against local data (initially CSV)&lt;/li&gt;
&lt;li&gt;Re-use the exact same code when packaging for AWS Glue Python Shell&lt;/li&gt;
&lt;li&gt;Deploy with deterministic Terraform infrastructure (S3 artifacts, IAM, Glue Job)&lt;/li&gt;
&lt;li&gt;Run fast (seconds) for small/medium datasets and can scale later if needed&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 674px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/37c837669e83e1d548e9158b94fb776e/84f54/quackpipe-architecture.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 64%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAANCAYAAACpUE5eAAAACXBIWXMAAAsSAAALEgHS3X78AAADTklEQVQ4y1WSS2zcRByH98aJO2eElKioqAknxL2IG4165I64c0AiF7iCxENCCA4gFUIRQlWDUtq8aLuboDZNUvblteNNsvGu7bXXr7FnbI/tmV/lpUJw+DSjkean7/9oBITeG8+ikekRNUqYFsR0jkf+OQMyZxAQalkhu1p1/1jkRvuUa9t9fnHc50ZX4Uanx42OxkdP9xplUZyuNTu4e6iiKApENEXMMogsA2UZaJ6D8RyEpnCi5Hp5PlguzlrAzy+j7N8Ed8coTAWFpYFP+nGDsGxwMTExsZ2SZlwkaSaiJBUbTiZMlglnQoQ1IUVMU3iUvyOU3aXcaMNpflGx4V+isHWRTwYVN1XwcddthAlTCcsRs1z4hGJsOdBND2/ddnAapzjvePLhb+fV1A/QPBq839299XZpn4BPbVlYQ1mYAwhXkZUzAB/3ZvNAP/RBEiqmXoiaMKbQ1Qn8IMHYdqSmG5VPYlgBu160772eTbqw9U1Jx8eyLtVtD2WkDlGY3VkjpKUaDlaRjL4WdsAx9XzwUmDryd8YTXSwrJQRZRVJUtiEXoNxesU9fYAPf3xJKr0bEtYE371Xyu0vCRC0a8NMzWMNeXImwjBBSBgcP0brYB/GWANhXMYsq+qh2FG6An1veaY/xl7rc2mq92U+1tC+1ZPDpo7S6niNiKaqbrmwZ77YOfewo/YR+AEs14flBogS9jyQYUqyFWjbS/FFHyeDngzOuhJTFb8eHUhNvQ9YyqwRs1z9qbWO24ctcWx0YZzdQJoDSZqBpvWwsn8Nrdpw+HB5dn6MT9e/kZrSknAN7B2tyWT4C0pT9xqEZqrrhUhYUR08eiA37qzLtJCyNiM0nROzrJzvIclWqv7mEjcHIMOOyMeKlO5Qbt/8Sjz983fA1d1GGDMtSigKAfn9D59gdfXd+o76rV7w58wNnThfKTt3lutAbqnIJwrgG/jog6tY+/ZjIDS8RpSk/foTyzkfXtiVcjISMUsFof+jiJJ5D69VvbtXuKmAm0rJTaUqzEE1fLJXWr0DVGbfaTQP2pd39g/f3Gw+fsOcea9lVXXp3Jy9akz9/3LJsL3L+jR+sdr+7IW0v7uYdrcWst7OAu1sLcB5tIDR/mJyvPHKM3OhjIZaWPHPAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Architecture Diagram&quot;
        title=&quot;Architecture Diagram&quot;
        src=&quot;/static/37c837669e83e1d548e9158b94fb776e/84f54/quackpipe-architecture.png&quot;
        srcset=&quot;/static/37c837669e83e1d548e9158b94fb776e/0eb09/quackpipe-architecture.png 500w,
/static/37c837669e83e1d548e9158b94fb776e/84f54/quackpipe-architecture.png 674w&quot;
        sizes=&quot;(max-width: 674px) 100vw, 674px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;h3 id=&quot;Architecture&quot; style=&quot;position:relative;&quot;&gt;Architecture&lt;a href=&quot;#Architecture&quot; aria-label=&quot;Architecture permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Components:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Example local runner: &lt;code class=&quot;language-text&quot;&gt;nfl_team_season_total.py&lt;/code&gt; (calls DuckDB over local or S3 CSVs)&lt;/li&gt;
&lt;li&gt;Example data lake (S3):&lt;/li&gt;
&lt;/ul&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;folder&lt;/th&gt;
&lt;th&gt;description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code class=&quot;language-text&quot;&gt;scripts/&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;For source script and wheel artifacts&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code class=&quot;language-text&quot;&gt;data/nfl/&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;The source data&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code class=&quot;language-text&quot;&gt;results/&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Output results&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code class=&quot;language-text&quot;&gt;tmp/&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Required in Glue job properties&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;ul&gt;
&lt;li&gt;Artifact Publishing: Terraform-managed S3 objects for script and wheel&lt;/li&gt;
&lt;li&gt;Glue Python Shell Job: Executes the same script with &lt;code class=&quot;language-text&quot;&gt;--extra-py-files&lt;/code&gt; pointing to built wheel&lt;/li&gt;
&lt;li&gt;IAM Roles/Policies: Least-privilege access to bucket objects &amp;#x26; logs&lt;/li&gt;
&lt;/ul&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Data flow&lt;/th&gt;
&lt;th&gt;Data files&lt;/th&gt;
&lt;th&gt;Execution&lt;/th&gt;
&lt;th&gt;Query&lt;/th&gt;
&lt;th&gt;Output&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;local&lt;/td&gt;
&lt;td&gt;CSV on disk&lt;/td&gt;
&lt;td&gt;Python&lt;/td&gt;
&lt;td&gt;DuckDB in-memory&lt;/td&gt;
&lt;td&gt;stdout / local file&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;cloud&lt;/td&gt;
&lt;td&gt;CSV in S3&lt;/td&gt;
&lt;td&gt;Glue Python Shell&lt;/td&gt;
&lt;td&gt;DuckDB in-memory&lt;/td&gt;
&lt;td&gt;S3&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 992px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/cd5b7dff4b80c98bfa5cec1ad7e39cf2/43738/quackpipe-glue-job-details.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 53.400000000000006%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAALCAIAAADwazoUAAAACXBIWXMAAAsSAAALEgHS3X78AAABn0lEQVQoz42RyW7bMBRF9RtFt6lnGEkcO13UQFb9rP5Zl3FTII4tSkU3MRwNtSRqiMWKFGcmjFB00fHgASQInkfiPudsvhxNF73h+avXbwbji95oNpouTvpng8m8P56dDE4Hk/l4ejmczHvD8/5odnqxvFws3y+vrt6+c65Xn13gu6632QIX+AD4W9eztQXdxgVfXOB7ngeAv77b3K7Xd+vNanX76frG0Vqrn2iltPkdQhqhjDZGGaM1p8VXzZGDG7zb7aMoDKMwDIMgCJoGY0KaBhPS4hdIy75BlhSsbnhS6WORxB8/kGTjENLWCBGqg1RGULVUHo8147yqHlOY2zaEHJK0sT1IBnOYFzVCxthfOJyLukbVI8rgMc2PDWaMcc4lpaxtKeeCc0Ep41wIYQ8ptReUlAghR2tzf7/b7x/iOI6i6Dvm5l9obXMpitJRShdFUdeIM0ZbqpT6T7ksKytDCB+C8HBIwigWUnb56z/TPWBlKVVdozTNIMwzCMuyzCBsMNbadJP7taT8IXMuuwnLl1UIaZOR6i9Ii3qWnwCdb0TD2PiKKwAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;AWS Console Glue Job&quot;
        title=&quot;AWS Console Glue Job&quot;
        src=&quot;/static/cd5b7dff4b80c98bfa5cec1ad7e39cf2/43738/quackpipe-glue-job-details.png&quot;
        srcset=&quot;/static/cd5b7dff4b80c98bfa5cec1ad7e39cf2/0eb09/quackpipe-glue-job-details.png 500w,
/static/cd5b7dff4b80c98bfa5cec1ad7e39cf2/43738/quackpipe-glue-job-details.png 992w&quot;
        sizes=&quot;(max-width: 992px) 100vw, 992px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;h3 id=&quot;Why-DuckDB-Here&quot; style=&quot;position:relative;&quot;&gt;Why DuckDB Here?&lt;a href=&quot;#Why-DuckDB-Here&quot; aria-label=&quot;Why DuckDB Here permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;The main reason here was an alternative to Glue Spark jobs and to make the local runner option easy to install and setup. However, DuckDB is also extremely suitability for this type of analytical workflow on moderate sized datasets. There are zero external services required and you get a consistent behavior locally and in Glue.&lt;/p&gt;
&lt;h3 id=&quot;Local-Development-Workflow&quot; style=&quot;position:relative;&quot;&gt;Local Development Workflow&lt;a href=&quot;#Local-Development-Workflow&quot; aria-label=&quot;Local Development Workflow permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;Create &amp;#x26; activate virtual environment&lt;/li&gt;
&lt;li&gt;Install deps via Poetry&lt;/li&gt;
&lt;li&gt;Run the script (iterative changes are seconds)&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;I built this on my Windows 11 machine (but it also runs on Linux machine), so I am using PowerShell. The instructions will be similar if Linux|MacOS system.&lt;/p&gt;
&lt;p&gt;Let’s create a virtual python environment.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;shell&quot;&gt;&lt;pre class=&quot;language-shell&quot;&gt;&lt;code class=&quot;language-shell&quot;&gt;&lt;span class=&quot;token function&quot;&gt;mkdir&lt;/span&gt; C:&lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;venv
python &lt;span class=&quot;token parameter variable&quot;&gt;-m&lt;/span&gt; venv C:&lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;venv&lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;quackpipe
C:&lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;venv&lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;quackpipe&lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;Scripts&lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;Activate.ps1&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Create a dot env file. (&lt;code class=&quot;language-text&quot;&gt;.env&lt;/code&gt;) in the root of the project.
Copy the contents of the &lt;code class=&quot;language-text&quot;&gt;.example&lt;/code&gt; file and paste it in the &lt;code class=&quot;language-text&quot;&gt;.env&lt;/code&gt; file&lt;/p&gt;
&lt;p&gt;Your virtual environment should be activated. We will use pip to install poetry and then poetry to manage all other dependencies.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;shell&quot;&gt;&lt;pre class=&quot;language-shell&quot;&gt;&lt;code class=&quot;language-shell&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;# From this cloned directory as the working directory&lt;/span&gt;
pip &lt;span class=&quot;token function&quot;&gt;install&lt;/span&gt; poetry
poetry &lt;span class=&quot;token function&quot;&gt;install&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h4 id=&quot;Run-a-local-quackpipe&quot; style=&quot;position:relative;&quot;&gt;Run a local quackpipe&lt;a href=&quot;#Run-a-local-quackpipe&quot; aria-label=&quot;Run a local quackpipe permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h4&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;shell&quot;&gt;&lt;pre class=&quot;language-shell&quot;&gt;&lt;code class=&quot;language-shell&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;# From this cloned directory as the working directory&lt;/span&gt;
python nfl_team_season_total.py&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 944px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/f69c7b92e25ce4b1fae1039d0c6d000c/26530/quackpipe-vscode-local.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 80.60000000000001%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAQCAYAAAAWGF8bAAAACXBIWXMAAAsSAAALEgHS3X78AAADSElEQVQ4y3WSuY4dVRRF6zdILLr7vVfzdKea53qvu1o9YNnIDixBjOAzkEgQEYkDSPlBZJHUQlVt5Ihg6Q46Onefu7flX97+7WUdJ1WvJ1Vj64agGIjqCTksVMtrittnRH+H7BfkcL+TnZ92wmrmpJrVMx2navpk1fdviUK9ClngBynXNy5X1w5+qGi6hX58YJwfGecnhvGBYXpgPj/v63bXdnfYnsAP9HqMFFbc3q7KtCwPb9f59hlTjGTlRCwqElmR6oZU10RpgdANwjSESY4pelTWEqUlsWxX1zf42bhaYnwmTivy5ozUNUFscHyB40tS1aDzDp01pLJAZQ0m70hkSZhkL48oQ9vm1JWhHBus7O4DQVxQNzlRYjjYIV/fuNycAoRU9OOF6fY1zbBgiplhXpgv95wvdySq4fsPI399fMMfvz3z8ff3WEm3EISCearxY8mrK4erg8vBSTlGE2L8ifHdr/RvfkY//EJSfUtZFujsRd3RPlGUGcPQoNoWS/T3OE5MLBShMNheguvHOGGGV/9ANn1HOz8SZyOxGekv39D0A0EqCIUkSATddEHnJSdRbCO/xw8zYlVRtjNlN1J2M2GiefXqK5SW3D89EwmJG0b000w/n/HjdG+2NZ3vFvKqwhYZlr68I4oLlDFEKucUJNh+wslNcLyEVJVU3RmZ1SSqIK8G8nogljlhqndM0ZKqHFdVWHFzwfMSjMk5boZcu19M0Q2x2NztdtxAvWQuMkjdEotyr9liFEQG37RYYX2L78d0rcIU3e709dHnYEfosqUaJ/JmQpiOaMumand3t4xuj3mhxt3wJY6ssILqjOMJsrKnakYSmRPEGi9U5FXDZTlT9bdU7ULd339mi9CwK91U7/gSW5Sbwgu2mxKpGp3VhInh6uBxcwqp+zuyTV3WE8uGSGz/2L3s05w4LQjjbP/bVGbYaYmlpsfVdiKOTrBu6rxQcnX09pHDpMDxJLYncQKFE8gX/M9nX/7Huk1pi3K19PREKuo1KzpsN+FwCjnaEScn2ZVvhf+Hu+HLHc8Tq50WWMuPf34y/RPd8rRGeYenaoKsxdM1vm5wZUVgmi/7rMWVJb6ucUTBTag4RHq9CSTHvPvnX0CNDCZaKXB+AAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;VS Code Local Run Screenshot&quot;
        title=&quot;VS Code Local Run Screenshot&quot;
        src=&quot;/static/f69c7b92e25ce4b1fae1039d0c6d000c/26530/quackpipe-vscode-local.png&quot;
        srcset=&quot;/static/f69c7b92e25ce4b1fae1039d0c6d000c/0eb09/quackpipe-vscode-local.png 500w,
/static/f69c7b92e25ce4b1fae1039d0c6d000c/26530/quackpipe-vscode-local.png 944w&quot;
        sizes=&quot;(max-width: 944px) 100vw, 944px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;Inspect outputs / adjust transformations as you need.&lt;/p&gt;
&lt;h3 id=&quot;Switching-to-Remote-Glue-Execution&quot; style=&quot;position:relative;&quot;&gt;Switching to Remote (Glue) Execution&lt;a href=&quot;#Switching-to-Remote-Glue-Execution&quot; aria-label=&quot;Switching to Remote Glue Execution permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Firstly produce a versioned wheel under &lt;code class=&quot;language-text&quot;&gt;dist/&lt;/code&gt;&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;shell&quot;&gt;&lt;pre class=&quot;language-shell&quot;&gt;&lt;code class=&quot;language-shell&quot;&gt;poetry build&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;log&quot;&gt;&lt;pre class=&quot;language-log&quot;&gt;&lt;code class=&quot;language-log&quot;&gt;Building quackpipe &lt;span class=&quot;token operator&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0.1.0&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;)&lt;/span&gt;
Building sdist
  &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt; Building sdist
  &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt; Built quackpipe&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;0&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;1&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;0&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;tar&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;gz
Building wheel
  &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt; Building wheel
  &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt; Built quackpipe&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0.1.0&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;py3&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;none&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;any&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;whl
&lt;span class=&quot;token operator&quot;&gt;(&lt;/span&gt;quackpipe&lt;span class=&quot;token operator&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Now, we can use Terraform to:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Deploy all the infra and IAM required&lt;/li&gt;
&lt;li&gt;Sync the Python script and wheel to S3&lt;/li&gt;
&lt;li&gt;Sync the data to S3&lt;/li&gt;
&lt;li&gt;Create the Glue job&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id=&quot;Terraform-variable-and-state-hygiene&quot; style=&quot;position:relative;&quot;&gt;Terraform variable and state hygiene&lt;a href=&quot;#Terraform-variable-and-state-hygiene&quot; aria-label=&quot;Terraform variable and state hygiene permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;Before you deploy the infra using IaC (Terraform) it is important to know what belongs in &lt;code class=&quot;language-text&quot;&gt;variables.tf&lt;/code&gt; vs &lt;code class=&quot;language-text&quot;&gt;terraform.tfvars&lt;/code&gt;.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;variables.tf&lt;/code&gt; declares schema (name, type, default, validation).&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;terraform.tfvars&lt;/code&gt; (untracked by Git) supplies environment / secret values.&lt;/li&gt;
&lt;li&gt;Commit &lt;code class=&quot;language-text&quot;&gt;terraform.tfvars.example&lt;/code&gt; only.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 1162px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/bec749cd3f484f7bd4a608ec8e8afd8a/8283d/quackpipe-terraform-vars.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 38.99999999999999%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAICAIAAAB2/0i6AAAACXBIWXMAAAsSAAALEgHS3X78AAABCElEQVQY05XQ2WrDMBAFUP9LLI1mk+R4kRPLbryE1in5/98pCaUPLYUWDsM83MvAFF1KxOocOnwQ9e00duulnadmykRsLQA4AGctGGO/WAvFdd+HZW7HIU257roQq1BV23bd99uybCEe2y69vu3jNLEoi4p69V5UibiY5yzekwiJILNDJJbLvO7v92W9ptMw5Jf9dp+XrW5Sl87nYcrjpT8N1bEpqtioenB4KM2hNMYCsdDjiBcNPkRWL+pZFFmQxCE/EBNLETX7GEsLZWlKY40FJHZIDgmf83MnQuJvCh+TD4GJLZJzCOB+hn5TjKHuRHrVmtgC2H+Vc+zzua9jqEQsOHh+7I/lD9JHVph0GOYIAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Terraform variables&quot;
        title=&quot;Terraform variables&quot;
        src=&quot;/static/bec749cd3f484f7bd4a608ec8e8afd8a/8283d/quackpipe-terraform-vars.png&quot;
        srcset=&quot;/static/bec749cd3f484f7bd4a608ec8e8afd8a/0eb09/quackpipe-terraform-vars.png 500w,
/static/bec749cd3f484f7bd4a608ec8e8afd8a/1263b/quackpipe-terraform-vars.png 1000w,
/static/bec749cd3f484f7bd4a608ec8e8afd8a/8283d/quackpipe-terraform-vars.png 1162w&quot;
        sizes=&quot;(max-width: 1162px) 100vw, 1162px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;NOTE: the state files should be stored in remote cloud storage (such as S3) but for this simple demo example they are just kept local&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Create a &lt;code class=&quot;language-text&quot;&gt;terraform.tfvars&lt;/code&gt; and populate it with the contents of the example (with setting the correct secret values for you use case)&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;shell&quot;&gt;&lt;pre class=&quot;language-shell&quot;&gt;&lt;code class=&quot;language-shell&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;# First check the plan&lt;/span&gt;
terraform plan
&lt;span class=&quot;token comment&quot;&gt;# Apply the plan&lt;/span&gt;
terraform apply -auto-approve&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;Issue: I couldn’t see the Glue job in the web console after the successful deploy, but I realised that I have the wrong region selected. 🤪&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id=&quot;Data-Input-Modes&quot; style=&quot;position:relative;&quot;&gt;Data Input Modes&lt;a href=&quot;#Data-Input-Modes&quot; aria-label=&quot;Data Input Modes permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;The simple solution that solves the two modes (local and remote).&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Execution Mode&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Local&lt;/td&gt;
&lt;td&gt;Uses on-disk CSVs under &lt;code class=&quot;language-text&quot;&gt;data/&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Remote&lt;/td&gt;
&lt;td&gt;Uploads same CSVs to S3 and script switches to &lt;code class=&quot;language-text&quot;&gt;s3://&lt;/code&gt; paths&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;In the main Glue script we use this handy function to get the job parameters. The key parameter is &lt;code class=&quot;language-text&quot;&gt;execution_mode&lt;/code&gt; which is set to ‘local’ in the &lt;code class=&quot;language-text&quot;&gt;.env&lt;/code&gt; for local running and left absent in the Glue job.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;py&quot;&gt;&lt;pre class=&quot;language-py&quot;&gt;&lt;code class=&quot;language-py&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;get_job_params&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
    required_params&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;list&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;dict&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token triple-quoted-string string&quot;&gt;&quot;&quot;&quot;Get job parameters for a AWS Glue job

    Args:
        required_params (list): list of the parameters to get

    Returns:
        dict: a dictionary of the parameters values
    &quot;&quot;&quot;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;try&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;token comment&quot;&gt;# If in Glue get from job parameters&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; awsglue&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;utils &lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; getResolvedOptions

        args &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; getResolvedOptions&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;sys&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;argv&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; required_params&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;except&lt;/span&gt; ModuleNotFoundError&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;token comment&quot;&gt;# Local fallback using environment variables (populated by .env)&lt;/span&gt;
        args &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;k&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; os&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;getenv&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;k&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; k &lt;span class=&quot;token keyword&quot;&gt;in&lt;/span&gt; required_params&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; args


&lt;span class=&quot;token comment&quot;&gt;# Load .env file into environment&lt;/span&gt;
load_dotenv&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
execution_mode &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; os&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;getenv&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;execution_mode&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Since the leave it absent in the Glue job, it drops into the else and looks for the files in S3.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;py&quot;&gt;&lt;pre class=&quot;language-py&quot;&gt;&lt;code class=&quot;language-py&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; execution_mode &lt;span class=&quot;token operator&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;local&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;try&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
        file_path &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; Path&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;data&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt; data_collection &lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt; file_name
        df &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; pd&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;read_csv&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;file_path&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        df &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; sanitize_column_names&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;df&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        con&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;register&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;table_name&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; df&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        registered_status &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token boolean&quot;&gt;True&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;None&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;except&lt;/span&gt; Exception &lt;span class=&quot;token keyword&quot;&gt;as&lt;/span&gt; e&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
        registered_status &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token boolean&quot;&gt;False&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string-interpolation&quot;&gt;&lt;span class=&quot;token string&quot;&gt;f&apos;&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token builtin&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;e&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;: &lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;file_name&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;try&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
        key &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string-interpolation&quot;&gt;&lt;span class=&quot;token string&quot;&gt;f&quot;data/&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;data_collection&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;file_name&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
        content &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; read_s3_file&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
            bucket&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;bucket&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
            key&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;key&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

        df &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; pd&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;read_csv&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;StringIO&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;content&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        df &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; sanitize_column_names&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;df&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;token comment&quot;&gt;# Add a file row number for tracking later&lt;/span&gt;
        df&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;insert&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;Row Number&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;range&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;len&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;df&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        con&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;register&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;table_name&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; df&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        registered_status &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token boolean&quot;&gt;True&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;None&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;except&lt;/span&gt; Exception &lt;span class=&quot;token keyword&quot;&gt;as&lt;/span&gt; e&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
        registered_status &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token boolean&quot;&gt;False&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string-interpolation&quot;&gt;&lt;span class=&quot;token string&quot;&gt;f&apos;&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token builtin&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;e&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;: &lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;file_name&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 679px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/1c6468e1d0e2603de44643a5d3716e74/a96cc/quackpipe-data-local-s3.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 74.8%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAPCAIAAABr+ngCAAAACXBIWXMAAAsSAAALEgHS3X78AAAB6klEQVQoz3WTW2/bMAxG8/9/XLHXdd2SIIkVWxfrbokUqcFW1wLrJvDBgHDAw0/0qTX6q4hYGph1nXW9z+WpqrY4G6wVlSmrbyklpTQzn77CrVHKkDfaKueNUm4xQ0gAgGmDtGGtUEpFbP+AERtz//Xz7eXl5Xw+S6n6f86A2xeYvl/9j7OcpmmepTFWKZPzllJWSlvrtV5T2k7HkJ25Y6NRgERENraYMXhvrQshhpgO2+Ks09p4H0qpp5SLWydlbKmtAJXatn0cImoIME2TEKL3zsy99xDCkZaJKe3aWwEjL4uUFfeeo1pjxD2Y53OWpqgVnqpaD9y5tRZjpOOciAgAzLpqraWUOefeO9EOA8Dj8ZjmLA1OS7UBjytazbqP2vuJeU9IHeQ8zyGEAQNAhXq5XIXMckUhq3HIh38I8RjiA1Z6WRbvfc6ZmUfnWqsQ4qk2adA49LEd0bJzbkTwDmuthRDD+QMGgNvtLpb8VFAqjbc9YP8BMyCu1hpjtNbOOSI+tPfAbrf7rLbFYMxtAMwcU/rsDNiMWafHw3s/rltrH4GJJU8L3EQRqgLuViOwfbeJdu2UsnM+pUzEY+fGU93v99UW47AC/ZmIDjv6/DGO7z5IxH1dh/blen29xm+v29ttI3rXDiEM7d89FWMQ4q5QLgAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Data Folder Layout Screenshot&quot;
        title=&quot;Data Folder Layout Screenshot&quot;
        src=&quot;/static/1c6468e1d0e2603de44643a5d3716e74/a96cc/quackpipe-data-local-s3.png&quot;
        srcset=&quot;/static/1c6468e1d0e2603de44643a5d3716e74/0eb09/quackpipe-data-local-s3.png 500w,
/static/1c6468e1d0e2603de44643a5d3716e74/a96cc/quackpipe-data-local-s3.png 679w&quot;
        sizes=&quot;(max-width: 679px) 100vw, 679px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;h3 id=&quot;Run-the-Glue-job-via-the-web-console&quot; style=&quot;position:relative;&quot;&gt;Run the Glue job via the web console&lt;a href=&quot;#Run-the-Glue-job-via-the-web-console&quot; aria-label=&quot;Run the Glue job via the web console permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 970px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/b148b6e6713882776a65fddf8196a576/adb12/quackpipe-glue-runs.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 105.2%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAVCAIAAADJt1n/AAAACXBIWXMAAAsSAAALEgHS3X78AAADMElEQVQ4y01Uy5KkNhDkl/1zvu3VPtgHb3hn3DvTDU0DAoQQeqv0dghi167QQQFkZVVWFo1zru26tns+n3337B/3h9EKrFFaA4CxVhsNEEbsX6tjMqI9M3r0v/6iXr81AEDI7pxfFjxNSCozLCC1JxtBaBZSam3GEXEhtdLLsq4Y04OllHLOjXPuYExpo7QFF0LMXAYXUk7Be3999PMSf0QpRSndGK1fw4CmYScz5/KQUUMhR6Qyeu+VUgDgfTDGKKWNMVqbC9x1zybGGEK8tez3r/jbJ12IHhCbMDCVnXNCSGPtmUVLIZVUSukLfH88mhBq1r9u9Msf+59v9KD7QWmMoZSccy5n5PzfvZScUgrBt21bmaU0O9novoI1jCupzIKpkIaQY5rxshKyMyH0uh3rRgnlStsF7+//3BppwscILZq/vyZ0aG4L2vVMLVXx8dq+fQxvn+Nnvy6HfbuPb/fx1s6IqImov99uTQhRG0cnRJfNuuBjVsY5n0IsmBz9MFdyvCsNr3Eep2VAC5dGKHu7fW9iqjOR2yopTSmVUs7BVEk45+uKCSE7pd57jPFGCMbYnhK+v783KSXvvH11gNdLEAC49NwpHUe0LHhdMQBME5qXtRpJKrAwDGPjnHfOmXmydL+cAM6FEHLOrDKv20YoPQBgXfF2htbaWpBSVvCJqecC/4yYUoz1XHNK5/tUH8aca3dn2SEzmXYeYkycC8YEY/w4eAiRCxFjZExs2w7gjDaMi1JK3w9oXpoYAsLi/pL9DFwFwTkhlOxUSBVipMchT1sqpbU2jPF9P6RUjPNtI9VhlBmlPUCKqUgh0DwvyypOzn2n2pirbCEEqpLVznP1dts4H5SOQiUug9BpIwdCaELzwereKaWMtekESymHcZomROlRSqn2BBd75NoRPnvz8TSUuxCq/tba//m5nIJFOMNaWxfjfm8shCcSykBIJabifLAA4ZxzzlnXHdRXlhDCldR5X0qpPVsw09LPeJRKpBQZY4+uE1KWUmJKj7Ztu85aKKUYY7gQzjl/gkspjfd+I7uQCsD5cz03QoyxF7P3PoTw49eh1hVfNZdStNb/Ak56qouED5UqAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Glue Job Run Screenshot&quot;
        title=&quot;Glue Job Run Screenshot&quot;
        src=&quot;/static/b148b6e6713882776a65fddf8196a576/adb12/quackpipe-glue-runs.png&quot;
        srcset=&quot;/static/b148b6e6713882776a65fddf8196a576/0eb09/quackpipe-glue-runs.png 500w,
/static/b148b6e6713882776a65fddf8196a576/adb12/quackpipe-glue-runs.png 970w&quot;
        sizes=&quot;(max-width: 970px) 100vw, 970px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;You can see that before I got a successful run, I had some issues. I initially had an error saying the Glue version was not valid.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;log&quot;&gt;&lt;pre class=&quot;language-log&quot;&gt;&lt;code class=&quot;language-log&quot;&gt;&lt;span class=&quot;token property&quot;&gt;InvalidInputException:&lt;/span&gt; Given GlueVersion &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;3.0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; is not valid&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;
&lt;span class=&quot;token property&quot;&gt;InvalidInputException:&lt;/span&gt; Given GlueVersion &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;4.0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; is not valid&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;I removed &lt;code class=&quot;language-text&quot;&gt;glue_version&lt;/code&gt; from the Terraform definition and set &lt;code class=&quot;language-text&quot;&gt;python_version = &quot;3.9&quot;&lt;/code&gt;. It defaulted to deploy to Glue version 5.&lt;/p&gt;
&lt;p&gt;The first time I got the configuration correct and a successful run started, the job spent many minutes (over 11 until I stopped it) compiling DuckDB from source. I realised it was that DuckDB &gt;=1.2.x had dropped Python 3.7 wheels. Actual Glue runtime resolved to Python 3.7 (despite attempts for 3.9) and since there is no wheel it resulted in a source build.&lt;/p&gt;
&lt;p&gt;To fix this I pinned the DuckDB dependency to:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;ini&quot;&gt;&lt;pre class=&quot;language-ini&quot;&gt;&lt;code class=&quot;language-ini&quot;&gt;&lt;span class=&quot;token key attr-name&quot;&gt;duckdb&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token value attr-value&quot;&gt;=1.1.3&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;This version still publishes Python 3.7 manylinux wheels. I rebuilt the quackpipe wheel and redeployed and the startup time was much faster.&lt;/p&gt;
&lt;p&gt;After getting a successful run, the results exported to S3 folder.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 632px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/28932e0c320da04f8441361406887883/afa26/quackpipe-results-s3.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 65.4%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAANCAIAAAAmMtkJAAAACXBIWXMAAAsSAAALEgHS3X78AAABlUlEQVQoz41SXW/iMBDM//9f93pSK5Jcq0KAQkIcoCRx7PV+uXKgavpwpxvNw2il8Y5Hm7VnrA2053BsYd/A+Ua1Cf1IiNi25lg3IhpnaIyqSb8f6svlw5guAxCWyBqJ4+TZeXKeIBBiYghIxHeqiorEKBiACDFg5r3fVNvyz+t6XVlrVSMTCwsvKCIh8KaGbQPVEapj2DVhnDhzzud5+fz0nOfFME4p3hxTVb/NLAG5amB/Sjy04WjC6DgTUdGIlJKryOT8MNi+H6fJiehyf/p1evjOFCcj4s3B/Xrqf5fj+QOHYdhsdtV2f2o7kbg0E/0kS4bEo0XnyU5oHRJJyjLvvHf2d3I21/hA8i0Q/wmi2Syi3sPk3MvL663v4//hYZ53Pqj6VYw+6tGvknQxEdHv2CKCiMMwOlAHAkE8iA/iQAKKnXjySbh5eL+5x2ZmBoDRjrv9wVyxMf7UwekM5krmQt2V6i60F7zcyFyxu1HAhbmu69Uqz/OiLMuiyIsiX61W67c3ZhJJT6dgs0jnMosQ0Fr7CeV+8Lb7jxw5AAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Results of Glue job&quot;
        title=&quot;Results of Glue job&quot;
        src=&quot;/static/28932e0c320da04f8441361406887883/afa26/quackpipe-results-s3.png&quot;
        srcset=&quot;/static/28932e0c320da04f8441361406887883/0eb09/quackpipe-results-s3.png 500w,
/static/28932e0c320da04f8441361406887883/afa26/quackpipe-results-s3.png 632w&quot;
        sizes=&quot;(max-width: 632px) 100vw, 632px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;h3 id=&quot;Terraform-Infrastructure-Highlights&quot; style=&quot;position:relative;&quot;&gt;Terraform Infrastructure Highlights&lt;a href=&quot;#Terraform-Infrastructure-Highlights&quot; aria-label=&quot;Terraform Infrastructure Highlights permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Bucket name includes region for uniqueness&lt;/li&gt;
&lt;li&gt;Explicit folder objects created for clarity (not strictly required by S3)&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;fileset&lt;/code&gt; used to push all &lt;code class=&quot;language-text&quot;&gt;data/nfl/*.csv&lt;/code&gt; into &lt;code class=&quot;language-text&quot;&gt;data/nfl/&lt;/code&gt; prefix&lt;/li&gt;
&lt;li&gt;Glue job avoids hard-coded Glue version (region variability) while pinning Python 3.9&lt;/li&gt;
&lt;li&gt;IAM policies refer to actual bucket resource IDs—prevents stale string drift&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Example from &lt;code class=&quot;language-text&quot;&gt;terraform plan&lt;/code&gt; when a new wheel is built.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 1414px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/a61502e8254f8af49d8062e0cbe2bd50/05de1/quackpipe-terraform-plan.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 57.199999999999996%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAALCAYAAAB/Ca1DAAAACXBIWXMAAAsSAAALEgHS3X78AAACR0lEQVQoz03R2W7bSBCFYT5GYllxbNiSRXFvrs29uYgUZcWeIJggyfs/Bv8BaWOQiw+Fujk43aUZRznbx4zDLp53TzG+UFRlT5a1SKmQSY1MKtJVuZJRThKmJEFKLFICTxJ4GYHI0YQ5EpnTnDjfKMIfNNkvXvo/vF1+8236xeX0L+f2B0P5ylC8MOYXxmxkTE8M8Yku6Mm9ntR9px33xWwfFM6xm2PvQh5cKeJXmuKNU31FyZEiHCiDjjpoUL6iDZuV8htyV5E4NYmjkG6D9vQQ4+hLYEvonanS79TpP+scmp9Mzfe1XZ+eaZOBJuxow5Ym6qmjE5nfkwXvM1kCtxuP/X3M4THD2Jf4dk8kRmQwkcs32uKVsXrlVFzpsiuNnGjlmS57QcmJIpko5QtlMhG5agkUfPnsoO/ytWHoDhTyjTS64tkdga0ofUUmGqS3PKv+UBHaFcJ651vlumu3Nx73G8HuIeW4q7D1BmGd8J2BWJwJnZ4yGqmTkTIeSdx6Fbs1gVPjWzW+XRPYNaFdo9198fm68bjfhuiPFcvVPaNHWAPC7BCGwjcUUrTEniJyakLnI8yu/28YfNC2t+68/WRwd2PN+4cQ12jwjBZhdTiG+mtvCN1m/YJFaKu1nTArgg++WaE97lN2u2Te7yWWWSGcisBtiLzTepzIn4jE4kIsBmK3Qy7TafH0Auc5X9nPOdZzjra5d+e7rya3t8f56dHHOEQYBx9Tj7D0FFMvVpZeYunLH1e4eoWjV+wfEu6Wo24E25t3/wGhp2xPKYlH7wAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Terraform Plan Screenshot&quot;
        title=&quot;Terraform Plan Screenshot&quot;
        src=&quot;/static/a61502e8254f8af49d8062e0cbe2bd50/05de1/quackpipe-terraform-plan.png&quot;
        srcset=&quot;/static/a61502e8254f8af49d8062e0cbe2bd50/0eb09/quackpipe-terraform-plan.png 500w,
/static/a61502e8254f8af49d8062e0cbe2bd50/1263b/quackpipe-terraform-plan.png 1000w,
/static/a61502e8254f8af49d8062e0cbe2bd50/05de1/quackpipe-terraform-plan.png 1414w&quot;
        sizes=&quot;(max-width: 1414px) 100vw, 1414px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;h3 id=&quot;Handling-Python-Dependencies&quot; style=&quot;position:relative;&quot;&gt;Handling Python Dependencies&lt;a href=&quot;#Handling-Python-Dependencies&quot; aria-label=&quot;Handling Python Dependencies permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Strategy: Build once locally → upload wheel → use &lt;code class=&quot;language-text&quot;&gt;--extra-py-files&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;DuckDB pinned to a version with prebuilt manylinux wheels (faster cold start)&lt;/li&gt;
&lt;li&gt;Avoids runtime &lt;code class=&quot;language-text&quot;&gt;pip install&lt;/code&gt; penalties&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;Summary&quot; style=&quot;position:relative;&quot;&gt;Summary&lt;a href=&quot;#Summary&quot; aria-label=&quot;Summary permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;DuckDB in-memory operations sub-second for small test set running locally&lt;/li&gt;
&lt;li&gt;Running in cloud the cold start time is dominated by Glue container and dependency load&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This is really just a very slim example of an idea. It could be extend to more jobs or Parquet outputs and adding features such as:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Add CI step to build/upload artifacts before Terraform apply (ensures script exists).&lt;/li&gt;
&lt;li&gt;Migrate to Python 3.9 runtime once consistently supported (or adopt Glue 5.0 when stable) and restore newer DuckDB.&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title><![CDATA[Query Salesforce with Python]]></title><description><![CDATA[Query SalesForce objects with Python Ever found yourself exporting data from Salesforce using the Web UI over and over again. Ever wished…]]></description><link>https://mortimer.xyz/query-salesforce-with-python/</link><guid isPermaLink="false">https://mortimer.xyz/query-salesforce-with-python/</guid><pubDate>Sat, 05 Jul 2025 00:00:00 GMT</pubDate><content:encoded>&lt;h1 id=&quot;Query-SalesForce-objects-with-Python&quot; style=&quot;position:relative;&quot;&gt;Query SalesForce objects with Python&lt;a href=&quot;#Query-SalesForce-objects-with-Python&quot; aria-label=&quot;Query SalesForce objects with Python permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h1&gt;
&lt;p&gt;Ever found yourself exporting data from Salesforce using the Web UI over and over again. Ever wished you could just automate the process with scripts and then setup a job schedule and never click through the Salesforce Web UI again? Maybe you will find this useful?&lt;/p&gt;
&lt;p&gt;The source code for this blog post is here: &lt;a href=&quot;https://github.com/mortie23/sf-de/tree/master/eng/sf/sf-extract&quot;&gt;https://github.com/mortie23/sf-de/tree/master/eng/sf/sf-extract&lt;/a&gt;.
If you are simply interested in the example usage and already have a Salesforce organistaion and user with a security token, then skip to &lt;a href=&quot;#Example-usage&quot;&gt;Example Usage&lt;/a&gt;&lt;/p&gt;
&lt;h2 id=&quot;Setting-Up-a-Salesforce-Developer-Edition-for-API-Access&quot; style=&quot;position:relative;&quot;&gt;Setting Up a Salesforce Developer Edition for API Access&lt;a href=&quot;#Setting-Up-a-Salesforce-Developer-Edition-for-API-Access&quot; aria-label=&quot;Setting Up a Salesforce Developer Edition for API Access permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The first step is getting an API user set up. We can use a Salesforce Developer Edition account to create a user specifically for API use and then get an API key (security token).&lt;/p&gt;
&lt;h3 id=&quot;Step-1-Sign-Up-for-Salesforce-Developer-Edition&quot; style=&quot;position:relative;&quot;&gt;Step 1: Sign Up for Salesforce Developer Edition&lt;a href=&quot;#Step-1-Sign-Up-for-Salesforce-Developer-Edition&quot; aria-label=&quot;Step 1 Sign Up for Salesforce Developer Edition permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;Go to &lt;a href=&quot;https://developer.salesforce.com/signup&quot;&gt;https://developer.salesforce.com/signup&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Fill in your details and create your account.&lt;/li&gt;
&lt;li&gt;Check your email and verify your Salesforce Developer account.&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id=&quot;Step-2-Log-Into-Your-Developer-Org&quot; style=&quot;position:relative;&quot;&gt;Step 2: Log Into Your Developer Org&lt;a href=&quot;#Step-2-Log-Into-Your-Developer-Org&quot; aria-label=&quot;Step 2 Log Into Your Developer Org permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Visit &lt;a href=&quot;https://login.salesforce.com&quot;&gt;https://login.salesforce.com&lt;/a&gt; and sign in with your new Developer account.&lt;/li&gt;
&lt;li&gt;Click the ⚙️ gear icon → &lt;strong&gt;Setup&lt;/strong&gt; to access the admin panel.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 377px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/fecb6c23c96bb65665fd21edce09edcc/fdd90/sfde-01-setup.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 52.254641909814325%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAKCAIAAAA7N+mxAAAACXBIWXMAAAsSAAALEgHS3X78AAABlElEQVQoz2WSzWoUQRDH+wE8CaIv4kP4AnpQcvMBfADvuYqnPSSCp+xBCMgiq+xxUTawJJklKrgy0/Ph2JPenkx/TG9Xf8huQwjO71B0VfGvP93VKOxhjBFClFJ5no/HYyHEYrHo9lBK8zxPkoRSOp/P9dYcnUzff5hdXK1RCMF7b4zp+x4AlFIYYwAQQgBA7GqtlVLWWqX6EEKW1z9/Yb01yBjjnPPex+i9DyE452K01t7WAcA5B9bFidY5JAXPsixN06Io1us1pTT2bjURzjuwViuplQwh8JtWdi3CpPVgpBDRJLr9B4ANzhImJhf1NCGfV38ny/LjskKzc/wlSaeXGaPNarUqyzI6hztYt0u/fa/uPR09eHH86ODd/edHDw+OUVptrsrNj4opKTDGdV0PxTEtSPv29Gw0WY4my8Px18evTpDZ6q5lpP7DGOOcN01TlqUQIgwAYzz0HnQIhlxvnrw+RarvDUDck9kTz0Ox1nrDWnbTCc5/F+TZ4SckpRw+Egyw1kopd5+G890Fq+blm9k/cVUoffWW4BsAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;sfde 01 setup&quot;
        title=&quot;sfde 01 setup&quot;
        src=&quot;/static/fecb6c23c96bb65665fd21edce09edcc/fdd90/sfde-01-setup.png&quot;
        srcset=&quot;/static/fecb6c23c96bb65665fd21edce09edcc/fdd90/sfde-01-setup.png 377w&quot;
        sizes=&quot;(max-width: 377px) 100vw, 377px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;h3 id=&quot;Step-3-Create-a-New-Standard-User-for-API-Access&quot; style=&quot;position:relative;&quot;&gt;Step 3: Create a New Standard User for API Access&lt;a href=&quot;#Step-3-Create-a-New-Standard-User-for-API-Access&quot; aria-label=&quot;Step 3 Create a New Standard User for API Access permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;To keep your main account separate from scripts, it’s best to create a dedicated API user:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;In &lt;strong&gt;Setup → Quick Find&lt;/strong&gt;, search &lt;strong&gt;Users&lt;/strong&gt; → click &lt;strong&gt;Users&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Click &lt;strong&gt;New User&lt;/strong&gt;.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 300px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/02e495f7e88bac0a1df916364937849d/a8a0d/sfde-02-users.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 117.33333333333333%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAXCAIAAACEf/j0AAAACXBIWXMAAAsSAAALEgHS3X78AAABw0lEQVQ4y51T7W7iMBDk/Z+l0j3ASf139+eu7Z0CcWLnG0JJKCRO7KztxObkFCrKFQSsxla82snIs94JCKEkrDc/kvzblv7a3RITAFBSvm0cEj+Wm9m5OmPMF+T/iz7q3r+OM6fkQ4He7bTvub6PCfaRh5Dn58ul66IkSaIwcBEKo+iSsjZ6GEMp1fe91mbctVKKMSaEuESuq4oxzhjjnDHeAUDTNFLKs4YdH+I4IoREIcHYx5gUZekitCqKaw27rVXHTqdJEhAydZzXYm3PZwS/VuacU0q3203bsluVx6uNy7ZOa3NZ94QcBeT5+Q/GeJ7NMba2hVF8+Ofuqhf2vg/9IIS40KdTcrXd1pQ2TdO27U13toLLxcLHOImjbJ7fYdj9fd4RHzmOs8hf7yEDAGuZEPJa8n5oR/RjSCmtz8oGAIgxpJSccwCwc3agfFJO4tjzvCxNZ+4MY/L35clFfhQQEoT5PHOmUx95U8eBcTaNMRPKBSjVSQs5DL3RvdFcKC5UzXgn+w+AGriQ1Cb39ZNwVa1pt6pYQSFdPqbLh0X5s6h5UfOS8qJmJxiTe0yionprOpttuiT/jYLvQfZSUn4N/gGZfjgheEdRVwAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;sfde 02 users&quot;
        title=&quot;sfde 02 users&quot;
        src=&quot;/static/02e495f7e88bac0a1df916364937849d/a8a0d/sfde-02-users.png&quot;
        srcset=&quot;/static/02e495f7e88bac0a1df916364937849d/a8a0d/sfde-02-users.png 300w&quot;
        sizes=&quot;(max-width: 300px) 100vw, 300px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;ol start=&quot;3&quot;&gt;
&lt;li&gt;Fill in the required fields:
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;First Name / Last Name:&lt;/strong&gt; &lt;code class=&quot;language-text&quot;&gt;API User&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Email:&lt;/strong&gt; your accessible email&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Username:&lt;/strong&gt; unique across all Salesforce orgs&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Alias:&lt;/strong&gt; e.g., &lt;code class=&quot;language-text&quot;&gt;apiuser&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;User License:&lt;/strong&gt; &lt;strong&gt;Salesforce&lt;/strong&gt; (standard)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Profile:&lt;/strong&gt; &lt;strong&gt;Standard User&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Save the user.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 1053px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/7232d1703b1c01997d5135d41b4cbaf5/c2a07/sfde-03-user-settings.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 33.4%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAHCAIAAACHqfpvAAAACXBIWXMAAAsSAAALEgHS3X78AAABGUlEQVQY042Q227CMBBE8/+/RlvRkFKVAMZxErvB98uuTQlUQX1o3zoaaZ6Odmcqxlgp5f5vXeebha+ISD7WFe0YIOZSEgAAOu+NsVJpbWyIERAB/hpzArzOc11vqtXzunn7oB1XJkjtuVCsF7QbWS+4UEoHqfxZOam91F4pz8eJnPqBT6unl4qLT8aGvh+cj/7hEBNgdiHJUdATJbQbuXA+WOudDyFEYx1gfq03VQhRcHHcbo21JeflTYCc83SWfH94bzb7QxuCL6UYa2JKl8sl53y/35qmqQBAK0V3u13bEkKmaVqqIzrnlVJS6RhjSssgKSVEzMsBnOd5gafpPPZDW9fHB+mdSwDOOUKo0iZBKqXkh37iF/wNH52IZr4GaksAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;sfde 03 user settings&quot;
        title=&quot;sfde 03 user settings&quot;
        src=&quot;/static/7232d1703b1c01997d5135d41b4cbaf5/c2a07/sfde-03-user-settings.png&quot;
        srcset=&quot;/static/7232d1703b1c01997d5135d41b4cbaf5/0eb09/sfde-03-user-settings.png 500w,
/static/7232d1703b1c01997d5135d41b4cbaf5/1263b/sfde-03-user-settings.png 1000w,
/static/7232d1703b1c01997d5135d41b4cbaf5/c2a07/sfde-03-user-settings.png 1053w&quot;
        sizes=&quot;(max-width: 1053px) 100vw, 1053px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;You would have gotten an email to the new users email inbox.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 656px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/ece516c3a84a2f8b0701feaff4f55c52/5ad6b/sfde-04-welcome-email.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 54.199999999999996%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAALCAIAAADwazoUAAAACXBIWXMAAAsSAAALEgHS3X78AAABk0lEQVQoz22QPW7bQBCFVeYGuYURtzlCytwgSeEiQFqfwG5SuHKVUkCAVD6Bj2BBtgFGlkVS1PJHa60o/uwMyeXujrES4ESQXvGwA7xvMW8GRGQtEVEYC89fsmzDsjxK80xU6UsRMDFbrMIkn4Q84YXPBDTKIY6gwRscJevxJH6OVkEsfLZaZJuEF1GajyeJF3DPX4bx+mGaYrsP/y+l+rKSdQ1t00qJUmLXdiABsaMD/YPtVqrXcY4+hxnfufQ5BBw20sFmt+Qx2HnV6NNL7/35/ceff08uvJML78Ol9+7H6OqWE5E2+7DdFrDuVzeDMt//LD7/8s9+R1+G86/D6Ntw/un6+ea+cLC1b/njnaUECU0tsa4RsQWJCAjQHOm8RJpVlCE9lcSAgrK/i+UohccljlP0OI5SvEtx8tIwaaeVCzMgJqkzNEBNVUfQU771RlNLpIiUJTTUGDeidQ/ZU6Wo7l0MeldzcLiM1lp1arUuZxGPEoHYGq3pmPbg3cGLCsNYTOc8jMUiy1m2yUSpjTmEXwF9tmkQ4Wr4ywAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;sfde 04 welcome email&quot;
        title=&quot;sfde 04 welcome email&quot;
        src=&quot;/static/ece516c3a84a2f8b0701feaff4f55c52/5ad6b/sfde-04-welcome-email.png&quot;
        srcset=&quot;/static/ece516c3a84a2f8b0701feaff4f55c52/0eb09/sfde-04-welcome-email.png 500w,
/static/ece516c3a84a2f8b0701feaff4f55c52/5ad6b/sfde-04-welcome-email.png 656w&quot;
        sizes=&quot;(max-width: 656px) 100vw, 656px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;h3 id=&quot;Step-4-API-Access&quot; style=&quot;position:relative;&quot;&gt;Step 4: API Access&lt;a href=&quot;#Step-4-API-Access&quot; aria-label=&quot;Step 4 API Access permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;For a Standard User, API access is usually enabled by default.&lt;/p&gt;
&lt;h3 id=&quot;Step-5-Get-the-Security-Token&quot; style=&quot;position:relative;&quot;&gt;Step 5: Get the Security Token&lt;a href=&quot;#Step-5-Get-the-Security-Token&quot; aria-label=&quot;Step 5 Get the Security Token permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Salesforce requires a security token for API login from outside their UI:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Log in as the new Standard User.&lt;/li&gt;
&lt;li&gt;Click your avatar → &lt;strong&gt;Settings&lt;/strong&gt;.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 464px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/b1c3974cdc486fb3a6f9d10af945192c/0359b/sfde-04-api-user-settings.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 41.16379310344828%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAICAIAAAB2/0i6AAAACXBIWXMAAAsSAAALEgHS3X78AAABLklEQVQY03XQyU7DMBAA0Hw4fwB3bpzggBA/gFgOIMQBlQq1qppWoiVtVicOcbyMHc9AU0CA6LvOPgH95pxTSjnnsizjnLdt65yTUhpjRE9KMZktDg5PJ+EiICJE9N4jIhFprRlj1trBYBCG4Wq1qutaKSWEiKIoz4ssTYaj2fH55XAUBtjbttgOxx02IfrM5PWb9xgAGMbF3eMUbAdgpJTOOd9D/4+u67z3WisAG1QtHJ1d7+2fXNw+8ypfvEbz2Vy08ucufyAiGB1zFYAxV/fjm4fx0/jFSJHGa84rVmTF5ryUMZbnWcmK9TpOkiTtlWVJRFK2gTHGeyzrloiWCZsuk6xqGqlXRROXDVgLPfNFGwMAiKiUCrTW32+qhE65bBS4DhtttfW7Nieij+J37p/G0DNec1oAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;sfde 04 api user settings&quot;
        title=&quot;sfde 04 api user settings&quot;
        src=&quot;/static/b1c3974cdc486fb3a6f9d10af945192c/0359b/sfde-04-api-user-settings.png&quot;
        srcset=&quot;/static/b1c3974cdc486fb3a6f9d10af945192c/0359b/sfde-04-api-user-settings.png 464w&quot;
        sizes=&quot;(max-width: 464px) 100vw, 464px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;ol start=&quot;3&quot;&gt;
&lt;li&gt;In Quick Find, search &lt;strong&gt;Reset My Security Token&lt;/strong&gt;.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 332px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/1097c0c61c19c7423eb3247de8f37b15/557e0/sfde-05-my-security-token.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 68.37349397590361%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAOCAIAAACgpqunAAAACXBIWXMAAAsSAAALEgHS3X78AAABoUlEQVQoz51SzU7bQBD2O/EgfQUegV4IRPRQ9UwfAA6EABWV2hMibVWkCpCdwqUnQuu1TewlTZUm6zUp3rVnf6pd49AigWg/fRp9mp1vd2a0zkq7u7jhNttes91darmNDXd502u2vaWWu2j1yla3uek1Wu5yy23Ygmfb3YW1o7mF147WWkqlHw2lTDGZ8icv9o2Z8bIsAUCUAIWVQqoZ5d8ChNJakSmbf3lgzJMJGQ6Ho9EoCIIwDOM4fvBlE7Nf/OnasTGDEAAgpRQ11P2oZrzKi+c7p87sskfPbOI0L1fffvlP8zUv1ztnN9suLTjnVdsAUGXAQghxa7aRFfDmMHA4yxljCKEoinzfj6Ko1igIIoTCXu8cYyyl/NPMC/H+NKnarjYh7zRoT6TW8k7WmuHDzKy1zvM8DEOMcWKBv3/sD3YvLl8NfnxKEowQopTOZuaF6HyOq5nNhSUASQ2yLCMkJbQ/Jt/G5GuaYUqzn+NxnjOldPUbGRfvTuJ/33bdduckdoIBRZj6mKLL1Mc1k9THpOZNEhmaymBAe/3JnnfxGyOQDHvlX0MhAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;sfde 05 my security token&quot;
        title=&quot;sfde 05 my security token&quot;
        src=&quot;/static/1097c0c61c19c7423eb3247de8f37b15/557e0/sfde-05-my-security-token.png&quot;
        srcset=&quot;/static/1097c0c61c19c7423eb3247de8f37b15/557e0/sfde-05-my-security-token.png 332w&quot;
        sizes=&quot;(max-width: 332px) 100vw, 332px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;ol start=&quot;4&quot;&gt;
&lt;li&gt;Click &lt;strong&gt;Reset Security Token&lt;/strong&gt; → Salesforce emails it to you.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 663px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/b235ae03642d2e095a2971b2aa96b426/5c8e6/sfde-06-reset-token.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 43.2%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAJCAIAAAC9o5sfAAAACXBIWXMAAAsSAAALEgHS3X78AAABhklEQVQoz43L226cMBCAYR62r9P7Sn2Nqk2yWbUqS9RGWZaDwSzQ7qkcjA9AIBjbmFTJJop619F/8c1IY8RxfNjv0zQ5nY5JkmTZHwjDosjneX58mxd3XFYtx52iDMvKN/KiwJggVCFUFUVZ101ZIkqZ1rPW86TnadJnaD1LpQcxPcWF4L0RBDDaxhBGMNqCIIzjBARhFG3X9mbjOK7ngyBcrzcAhJ4PbHvjAwBAkKS/mrYzbn/emt9N07RWqxvL+nF3Z9u2A0Doe0EQQNf1wzB6+vQADLeeCxzHgzDe7Q6cj0bN9pTuagJbFrcsZhiK4TiJ7KXxuVfo17scDmK8N3rwcXA/sObr8fQpyz/j6oqQBaXXdb2kdEHIgrElQpeELjC+KssLhC4IuZ4nS4nSaNz33HzXd99weYnLL12z1NJSwlLjahLPOFtak7QkN9W4UuLmUZpqLI28OGRZWmRpkf8eejLJe8EbMf6TfEMrzyuvheAGJYRSRijDhD70gxBKCPk/San+AtRt57ivi9dwAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;sfde 06 reset token&quot;
        title=&quot;sfde 06 reset token&quot;
        src=&quot;/static/b235ae03642d2e095a2971b2aa96b426/5c8e6/sfde-06-reset-token.png&quot;
        srcset=&quot;/static/b235ae03642d2e095a2971b2aa96b426/0eb09/sfde-06-reset-token.png 500w,
/static/b235ae03642d2e095a2971b2aa96b426/5c8e6/sfde-06-reset-token.png 663w&quot;
        sizes=&quot;(max-width: 663px) 100vw, 663px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;You will get an email with the security token in the email.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 660px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/ab26dd68d2808c330cdb45421c9d98d5/7c811/sfde-07-email-token.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 28.4%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAGCAIAAABM9SnKAAAACXBIWXMAAAsSAAALEgHS3X78AAAA9UlEQVQY05WLy06DQABF+Wr/wm9w595tW8HEpNA0Jq3VhSI1aQYKzHsoA/M2paYbV96cxTmLG73t3j8/8jwvvvJ9UXxvNq8Q4hCC9z78WTe4mqmam5aIsd5GSByZhKIv+QmIvqLiMOhWe6IcVg5rR7Qjk5x9tFhqNAG9t5Eo7snurhuXTTODcIHxI0ILSmPGEkJiQuMpE8afGEsYS5xbhbBWdmndEB23tw/PN4DP23pWn/9zShNrM2tXxmTGZFqnVzcm9WEdwot2qfVDhLqm4gciADtVF2hXEvEL7cprXhxzgDlAHPRSRlKqcTBS6l5q+R/GUf0ANydMIJEwSYQAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;sfde 07 email token&quot;
        title=&quot;sfde 07 email token&quot;
        src=&quot;/static/ab26dd68d2808c330cdb45421c9d98d5/7c811/sfde-07-email-token.png&quot;
        srcset=&quot;/static/ab26dd68d2808c330cdb45421c9d98d5/0eb09/sfde-07-email-token.png 500w,
/static/ab26dd68d2808c330cdb45421c9d98d5/7c811/sfde-07-email-token.png 660w&quot;
        sizes=&quot;(max-width: 660px) 100vw, 660px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 672px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/6728e7cb55b9a86f0404d3c39e18b3d1/00e42/sfde-08-the-token.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 59.199999999999996%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAMCAIAAADtbgqsAAAACXBIWXMAAAsSAAALEgHS3X78AAAB+UlEQVQoz11RS27bQAz1LYrCsSVbtjQ/zX800ow0kqXIdt04CZp00y56gy676BW6yJULOUkbBCAIguTjeyRn4XgK+8Nwvg/Hm88Pj6eHr8OnU7c/Hs63h/Nddzqfvjzqul2kIAZ4Bclbm7lhP5zvsdCqcpnrMzek1ZC6IXPXmRt007l+1HXLrMtN9R7889fvP09PcxXS0/e5v/kw/vjYf5v72ytRL3gFhcaqyI0l2mJVvAc34/F4vruidl4Mca6Wpl0KH1OzAmiVwShDyxRG6RREGVpBEgP8T/9sm4sVyonUMKeAScJFLjVRGnCNdYmkIaYkemJG0kBp0CSkTHA+gat+dP1omk6HnQl90Q7CNW7Ym9CpOpiwU/7i61B2g/Rt2e54VdOiQsrMXkYKhaQGTGJlAFdYGsA14IpoC6aSAVwiqZ9PkHG5RhdmYkpeulSYZXEd2etYNbH0MdVrSBJM15gmb2x92fn/wWhRMeuIKbFtoC5J4Ujhc1sDJpn1zDpqqqmn9NKHjIooQzF4BQMmIFeTYKmx0pAJwDiWZpI6JQvAJdY2pQIKhVWBZJEx+cw/476VdcdcUKEnrk3cPrG7jRu3pknsbmu7jWm2/rC2feL3Wzdumd4Q9sJ8WbKOpYt1Ewm30N1StTGREcxjxCKUR4i+xixC9O2f/wLslYylIPizZAAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;sfde 08 the token&quot;
        title=&quot;sfde 08 the token&quot;
        src=&quot;/static/6728e7cb55b9a86f0404d3c39e18b3d1/00e42/sfde-08-the-token.png&quot;
        srcset=&quot;/static/6728e7cb55b9a86f0404d3c39e18b3d1/0eb09/sfde-08-the-token.png 500w,
/static/6728e7cb55b9a86f0404d3c39e18b3d1/00e42/sfde-08-the-token.png 672w&quot;
        sizes=&quot;(max-width: 672px) 100vw, 672px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;h2 id=&quot;Example-usage&quot; style=&quot;position:relative;&quot;&gt;Example usage&lt;a href=&quot;#Example-usage&quot; aria-label=&quot;Example usage permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Setup your Python environment.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;sh&quot;&gt;&lt;pre class=&quot;language-sh&quot;&gt;&lt;code class=&quot;language-sh&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;# Create a Python virtual environment&lt;/span&gt;
python3 &lt;span class=&quot;token parameter variable&quot;&gt;-m&lt;/span&gt; venv ~/venv/sfde
&lt;span class=&quot;token comment&quot;&gt;# Activate the environment&lt;/span&gt;
&lt;span class=&quot;token builtin class-name&quot;&gt;source&lt;/span&gt; ~/venv/sfde/bin/activate
&lt;span class=&quot;token comment&quot;&gt;# Install poetry into the virtual environment&lt;/span&gt;
pip &lt;span class=&quot;token function&quot;&gt;install&lt;/span&gt; poetry
&lt;span class=&quot;token comment&quot;&gt;# Use poetry to install the dependencies &lt;/span&gt;
poetry lock &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; poetry &lt;span class=&quot;token function&quot;&gt;install&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Add you environment variables (if you need step by step guide on setting up a developer SalesForce org and creating the credentials read above) in a new &lt;code class=&quot;language-text&quot;&gt;.env&lt;/code&gt; file:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;ini&quot;&gt;&lt;pre class=&quot;language-ini&quot;&gt;&lt;code class=&quot;language-ini&quot;&gt;&lt;span class=&quot;token key attr-name&quot;&gt;SALESFORCE_USERNAME&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token value attr-value&quot;&gt;your_salesforce_username&lt;/span&gt;
&lt;span class=&quot;token key attr-name&quot;&gt;SALESFORCE_PASSWORD&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token value attr-value&quot;&gt;your_salesforce_password&lt;/span&gt;
&lt;span class=&quot;token key attr-name&quot;&gt;SALESFORCE_SECURITY_TOKEN&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token value attr-value&quot;&gt;your_salesforce_security_token&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Run the Python script to execute the queries&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;sh&quot;&gt;&lt;pre class=&quot;language-sh&quot;&gt;&lt;code class=&quot;language-sh&quot;&gt;python3 app.py &lt;span class=&quot;token parameter variable&quot;&gt;--query&lt;/span&gt; all&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;log&quot;&gt;&lt;pre class=&quot;language-log&quot;&gt;&lt;code class=&quot;language-log&quot;&gt;&lt;span class=&quot;token date number&quot;&gt;2025-05-05&lt;/span&gt; &lt;span class=&quot;token time number&quot;&gt;14:47:40,042&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token level info keyword&quot;&gt;INFO&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt; Successfully connected to Salesforce
&lt;span class=&quot;token date number&quot;&gt;2025-05-05&lt;/span&gt; &lt;span class=&quot;token time number&quot;&gt;14:47:40,051&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token level info keyword&quot;&gt;INFO&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt; Executing query&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; 
select
  count&lt;span class=&quot;token operator&quot;&gt;(&lt;/span&gt;Id&lt;span class=&quot;token operator&quot;&gt;)&lt;/span&gt;
from
  account
&lt;span class=&quot;token date number&quot;&gt;2025-05-05&lt;/span&gt; &lt;span class=&quot;token time number&quot;&gt;14:47:40,553&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token level info keyword&quot;&gt;INFO&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt; Query returned &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt; records
OrderedDict&lt;span class=&quot;token operator&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;attributes&apos;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; OrderedDict&lt;span class=&quot;token operator&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;type&apos;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;AggregateResult&apos;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;expr0&apos;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;14&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token date number&quot;&gt;2025-05-05&lt;/span&gt; &lt;span class=&quot;token time number&quot;&gt;14:47:40,559&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token level info keyword&quot;&gt;INFO&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt; Executing query&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; 
select
  count&lt;span class=&quot;token operator&quot;&gt;(&lt;/span&gt;Id&lt;span class=&quot;token operator&quot;&gt;)&lt;/span&gt;
from
  contact
&lt;span class=&quot;token date number&quot;&gt;2025-05-05&lt;/span&gt; &lt;span class=&quot;token time number&quot;&gt;14:47:40,908&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token level info keyword&quot;&gt;INFO&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt; Query returned &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt; records
OrderedDict&lt;span class=&quot;token operator&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;attributes&apos;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; OrderedDict&lt;span class=&quot;token operator&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;type&apos;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;AggregateResult&apos;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;expr0&apos;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;21&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token date number&quot;&gt;2025-05-05&lt;/span&gt; &lt;span class=&quot;token time number&quot;&gt;14:47:40,915&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token level info keyword&quot;&gt;INFO&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt; Executing query&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; 
select
  Id
&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; name
from
  contact
&lt;span class=&quot;token date number&quot;&gt;2025-05-05&lt;/span&gt; &lt;span class=&quot;token time number&quot;&gt;14:47:41,194&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token level info keyword&quot;&gt;INFO&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt; Query returned &lt;span class=&quot;token number&quot;&gt;21&lt;/span&gt; records
OrderedDict&lt;span class=&quot;token operator&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;attributes&apos;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; OrderedDict&lt;span class=&quot;token operator&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;type&apos;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;Contact&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;url&apos;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;/services/data/vM.m/sobjects/Contact/&amp;lt;&gt;hQAE&apos;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;Id&apos;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;&amp;lt;&gt;hQAE&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;Name&apos;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;Rose Gonzalez&apos;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;)&lt;/span&gt;
OrderedDict&lt;span class=&quot;token operator&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;attributes&apos;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; OrderedDict&lt;span class=&quot;token operator&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;type&apos;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;Contact&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;url&apos;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;/services/data/vM.m/sobjects/Contact/&amp;lt;&gt;iQAE&apos;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;Id&apos;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;&amp;lt;&gt;iQAE&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;Name&apos;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;Sean Forbes&apos;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;)&lt;/span&gt;
OrderedDict&lt;span class=&quot;token operator&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;attributes&apos;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; OrderedDict&lt;span class=&quot;token operator&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;type&apos;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;Contact&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;url&apos;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;/services/data/vM.m/sobjects/Contact/&amp;lt;&gt;jQAE&apos;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;Id&apos;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;&amp;lt;&gt;jQAE&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;Name&apos;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;Jack Rogers&apos;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;)&lt;/span&gt;
OrderedDict&lt;span class=&quot;token operator&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;attributes&apos;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; OrderedDict&lt;span class=&quot;token operator&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;type&apos;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;Contact&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;url&apos;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;/services/data/vM.m/sobjects/Contact/&amp;lt;&gt;kQAE&apos;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;Id&apos;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;&amp;lt;&gt;kQAE&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;Name&apos;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;Pat Stumuller&apos;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;)&lt;/span&gt;
OrderedDict&lt;span class=&quot;token operator&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;attributes&apos;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; OrderedDict&lt;span class=&quot;token operator&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;type&apos;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;Contact&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;url&apos;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;/services/data/vM.m/sobjects/Contact/&amp;lt;&gt;lQAE&apos;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;Id&apos;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;&amp;lt;&gt;lQAE&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;Name&apos;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;Andy Young&apos;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;)&lt;/span&gt;
OrderedDict&lt;span class=&quot;token operator&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;attributes&apos;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; OrderedDict&lt;span class=&quot;token operator&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;type&apos;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;Contact&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;url&apos;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;/services/data/vM.m/sobjects/Contact/&amp;lt;&gt;mQAE&apos;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;Id&apos;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;&amp;lt;&gt;mQAE&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;Name&apos;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;Tim Barr&apos;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;)&lt;/span&gt;
OrderedDict&lt;span class=&quot;token operator&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;attributes&apos;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; OrderedDict&lt;span class=&quot;token operator&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;type&apos;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;Contact&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;url&apos;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;/services/data/vM.m/sobjects/Contact/&amp;lt;&gt;nQAE&apos;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;Id&apos;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;&amp;lt;&gt;nQAE&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;Name&apos;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;John Bond&apos;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;)&lt;/span&gt;
OrderedDict&lt;span class=&quot;token operator&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;attributes&apos;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; OrderedDict&lt;span class=&quot;token operator&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;type&apos;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;Contact&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;url&apos;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;/services/data/vM.m/sobjects/Contact/&amp;lt;&gt;oQAE&apos;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;Id&apos;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;&amp;lt;&gt;oQAE&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;Name&apos;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;Stella Pavlova&apos;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;)&lt;/span&gt;
OrderedDict&lt;span class=&quot;token operator&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;attributes&apos;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; OrderedDict&lt;span class=&quot;token operator&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;type&apos;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;Contact&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;url&apos;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;/services/data/vM.m/sobjects/Contact/&amp;lt;&gt;pQAE&apos;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;Id&apos;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;&amp;lt;&gt;pQAE&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;Name&apos;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;Lauren Boyle&apos;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;)&lt;/span&gt;
OrderedDict&lt;span class=&quot;token operator&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;attributes&apos;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; OrderedDict&lt;span class=&quot;token operator&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;type&apos;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;Contact&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;url&apos;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;/services/data/vM.m/sobjects/Contact/&amp;lt;&gt;qQAE&apos;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;Id&apos;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;&amp;lt;&gt;qQAE&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;Name&apos;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;Babara Levy&apos;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;)&lt;/span&gt;
OrderedDict&lt;span class=&quot;token operator&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;attributes&apos;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; OrderedDict&lt;span class=&quot;token operator&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;type&apos;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;Contact&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;url&apos;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;/services/data/vM.m/sobjects/Contact/&amp;lt;&gt;rQAE&apos;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;Id&apos;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;&amp;lt;&gt;rQAE&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;Name&apos;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;Josh Davis&apos;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;)&lt;/span&gt;
OrderedDict&lt;span class=&quot;token operator&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;attributes&apos;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; OrderedDict&lt;span class=&quot;token operator&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;type&apos;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;Contact&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;url&apos;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;/services/data/vM.m/sobjects/Contact/&amp;lt;&gt;sQAE&apos;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;Id&apos;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;&amp;lt;&gt;sQAE&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;Name&apos;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;Jane Grey&apos;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;)&lt;/span&gt;
OrderedDict&lt;span class=&quot;token operator&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;attributes&apos;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; OrderedDict&lt;span class=&quot;token operator&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;type&apos;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;Contact&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;url&apos;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;/services/data/vM.m/sobjects/Contact/&amp;lt;&gt;tQAE&apos;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;Id&apos;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;&amp;lt;&gt;tQAE&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;Name&apos;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;Arthur Song&apos;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;)&lt;/span&gt;
OrderedDict&lt;span class=&quot;token operator&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;attributes&apos;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; OrderedDict&lt;span class=&quot;token operator&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;type&apos;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;Contact&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;url&apos;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;/services/data/vM.m/sobjects/Contact/&amp;lt;&gt;uQAE&apos;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;Id&apos;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;&amp;lt;&gt;uQAE&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;Name&apos;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;Ashley James&apos;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;)&lt;/span&gt;
OrderedDict&lt;span class=&quot;token operator&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;attributes&apos;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; OrderedDict&lt;span class=&quot;token operator&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;type&apos;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;Contact&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;url&apos;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;/services/data/vM.m/sobjects/Contact/&amp;lt;&gt;vQAE&apos;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;Id&apos;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;&amp;lt;&gt;vQAE&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;Name&apos;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;Tom Ripley&apos;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;)&lt;/span&gt;
OrderedDict&lt;span class=&quot;token operator&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;attributes&apos;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; OrderedDict&lt;span class=&quot;token operator&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;type&apos;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;Contact&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;url&apos;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;/services/data/vM.m/sobjects/Contact/&amp;lt;&gt;wQAE&apos;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;Id&apos;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;&amp;lt;&gt;wQAE&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;Name&apos;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Liz D&apos;Cruz&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;)&lt;/span&gt;
OrderedDict&lt;span class=&quot;token operator&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;attributes&apos;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; OrderedDict&lt;span class=&quot;token operator&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;type&apos;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;Contact&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;url&apos;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;/services/data/vM.m/sobjects/Contact/&amp;lt;&gt;xQAE&apos;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;Id&apos;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;&amp;lt;&gt;xQAE&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;Name&apos;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;Edna Frank&apos;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;)&lt;/span&gt;
OrderedDict&lt;span class=&quot;token operator&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;attributes&apos;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; OrderedDict&lt;span class=&quot;token operator&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;type&apos;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;Contact&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;url&apos;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;/services/data/vM.m/sobjects/Contact/&amp;lt;&gt;yQAE&apos;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;Id&apos;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;&amp;lt;&gt;yQAE&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;Name&apos;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;Avi Green&apos;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;)&lt;/span&gt;
OrderedDict&lt;span class=&quot;token operator&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;attributes&apos;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; OrderedDict&lt;span class=&quot;token operator&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;type&apos;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;Contact&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;url&apos;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;/services/data/vM.m/sobjects/Contact/&amp;lt;&gt;zQAE&apos;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;Id&apos;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;&amp;lt;&gt;zQAE&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;Name&apos;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;Siddartha Nedaerk&apos;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;)&lt;/span&gt;
OrderedDict&lt;span class=&quot;token operator&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;attributes&apos;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; OrderedDict&lt;span class=&quot;token operator&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;type&apos;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;Contact&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;url&apos;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;/services/data/vM.m/sobjects/Contact/?&apos;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;Id&apos;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;?&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;Name&apos;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;Jake Llorrac&apos;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;)&lt;/span&gt;
OrderedDict&lt;span class=&quot;token operator&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;attributes&apos;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; OrderedDict&lt;span class=&quot;token operator&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;type&apos;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;Contact&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;url&apos;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;/services/data/vM.m/sobjects/Contact/&amp;amp;&apos;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;Id&apos;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;&amp;amp;&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;Name&apos;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;Shane Warne&apos;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;</content:encoded></item><item><title><![CDATA[Publishing a Python Streamlit app to Posit Connect]]></title><description><![CDATA[Python applications published to Posit Connect This is a simple Streamlit application structured as a modular project and managed using…]]></description><link>https://mortimer.xyz/streamlit-app-on-posit-connect/</link><guid isPermaLink="false">https://mortimer.xyz/streamlit-app-on-posit-connect/</guid><pubDate>Wed, 09 Apr 2025 12:00:00 GMT</pubDate><content:encoded>&lt;h1 id=&quot;Python-applications-published-to-Posit-Connect&quot; style=&quot;position:relative;&quot;&gt;Python applications published to Posit Connect&lt;a href=&quot;#Python-applications-published-to-Posit-Connect&quot; aria-label=&quot;Python applications published to Posit Connect permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h1&gt;
&lt;p&gt;This is a simple Streamlit application structured as a modular project and managed using Poetry. The application is designed to be deployed to a Posit Connect server.&lt;/p&gt;
&lt;p&gt;Reference: &lt;a href=&quot;https://github.com/mortie23/ml/tree/master/app/streamlit-posit-app&quot;&gt;https://github.com/mortie23/ml/tree/master/app/streamlit-posit-app&lt;/a&gt;&lt;/p&gt;
&lt;h2 id=&quot;Project-Structure&quot; style=&quot;position:relative;&quot;&gt;Project Structure&lt;a href=&quot;#Project-Structure&quot; aria-label=&quot;Project Structure permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;📁streamlit-posit-app
├── 📁streamlit_posit_app
│   └── 📁pages
│       └── __init__.py
│   │   └── home.py
├── 📁tests
│   └── __init__.py
├── app.py
├── .gitignore
├── pyproject.toml
└── README.md&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2 id=&quot;Configure-Python-applications-on-Posit-Connect&quot; style=&quot;position:relative;&quot;&gt;Configure Python applications on Posit Connect&lt;a href=&quot;#Configure-Python-applications-on-Posit-Connect&quot; aria-label=&quot;Configure Python applications on Posit Connect permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;I built my own Posit Connect server VM for testing this out. If you want to do the same, I suggest reading my previous blog post &lt;a href=&quot;../install-posit-connect-ubuntu/&quot;&gt;Install Posit Connect on Ubuntu&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;I followed the recommended installation of Python with the latest version available at the time of writing this. Docs can be found at &lt;a href=&quot;https://docs.posit.co/resources/install-python.html&quot;&gt;https://docs.posit.co/resources/install-python.html&lt;/a&gt;&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;sh&quot;&gt;&lt;pre class=&quot;language-sh&quot;&gt;&lt;code class=&quot;language-sh&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;# Set preferred Python version&lt;/span&gt;
&lt;span class=&quot;token builtin class-name&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token assign-left variable&quot;&gt;PYTHON_VERSION&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;3.12.4&quot;&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;# Download and install&lt;/span&gt;
&lt;span class=&quot;token function&quot;&gt;curl&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-O&lt;/span&gt; https://cdn.rstudio.com/python/ubuntu-2404/pkgs/python-&lt;span class=&quot;token variable&quot;&gt;${PYTHON_VERSION}&lt;/span&gt;_1_amd64.deb
&lt;span class=&quot;token function&quot;&gt;sudo&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;apt-get&lt;/span&gt; update
&lt;span class=&quot;token function&quot;&gt;sudo&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;apt-get&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;install&lt;/span&gt; ./python-&lt;span class=&quot;token variable&quot;&gt;${PYTHON_VERSION}&lt;/span&gt;_1_amd64.deb
&lt;span class=&quot;token comment&quot;&gt;# check install&lt;/span&gt;
/opt/python/&lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token variable&quot;&gt;${PYTHON_VERSION}&lt;/span&gt;&quot;&lt;/span&gt;/bin/python &lt;span class=&quot;token parameter variable&quot;&gt;--version&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Then you need to edit the Connect config file to enable Python apps and set the location of the kernel. Docs can be found at &lt;a href=&quot;https://docs.posit.co/connect/admin/python/&quot;&gt;https://docs.posit.co/connect/admin/python/&lt;/a&gt;&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;ini&quot;&gt;&lt;pre class=&quot;language-ini&quot;&gt;&lt;code class=&quot;language-ini&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;; /etc/rstudio-connect/rstudio-connect.gcfg&lt;/span&gt;
&lt;span class=&quot;token section&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token section-name selector&quot;&gt;Python&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token key attr-name&quot;&gt;Enabled&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token value attr-value&quot;&gt;true&lt;/span&gt;
&lt;span class=&quot;token key attr-name&quot;&gt;Executable&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token value attr-value&quot;&gt;/opt/python/3.12.4/bin/python3&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2 id=&quot;Development-of-the-application&quot; style=&quot;position:relative;&quot;&gt;Development of the application&lt;a href=&quot;#Development-of-the-application&quot; aria-label=&quot;Development of the application permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;I used a local development environment of WSL2 on my Dell XPS with Windows 11. I could have done the development through RStudio on the VM server, but I prefer VSCode myself.&lt;/p&gt;
&lt;p&gt;On the development machine, create a virtual environment and install &lt;strong&gt;poetry&lt;/strong&gt;.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;sh&quot;&gt;&lt;pre class=&quot;language-sh&quot;&gt;&lt;code class=&quot;language-sh&quot;&gt;python3 &lt;span class=&quot;token parameter variable&quot;&gt;-m&lt;/span&gt; venv ~/venv/posit
&lt;span class=&quot;token builtin class-name&quot;&gt;source&lt;/span&gt; ~/venv/posit/bin/activate
pip &lt;span class=&quot;token function&quot;&gt;install&lt;/span&gt; poetry&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;All packages will be managed by poetry. The application directory on the referenced Github repo was created with this following command.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;sh&quot;&gt;&lt;pre class=&quot;language-sh&quot;&gt;&lt;code class=&quot;language-sh&quot;&gt;poetry new ./streamlit-posit-app&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Then each new package is added with the following command.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;sh&quot;&gt;&lt;pre class=&quot;language-sh&quot;&gt;&lt;code class=&quot;language-sh&quot;&gt;poetry &lt;span class=&quot;token function&quot;&gt;add&lt;/span&gt; rsconnect-python&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;But, if we restoring this from the referenced repo, then we won’t manually be adding each package, we just use poetry to install all dependencies.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;sh&quot;&gt;&lt;pre class=&quot;language-sh&quot;&gt;&lt;code class=&quot;language-sh&quot;&gt;poetry lock &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; poetry &lt;span class=&quot;token function&quot;&gt;install&lt;/span&gt; --no-root&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2 id=&quot;Running-the-App&quot; style=&quot;position:relative;&quot;&gt;Running the App&lt;a href=&quot;#Running-the-App&quot; aria-label=&quot;Running the App permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;To run the Streamlit application locally, use the following command:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;sh&quot;&gt;&lt;pre class=&quot;language-sh&quot;&gt;&lt;code class=&quot;language-sh&quot;&gt;poetry run streamlit run app.py
&lt;span class=&quot;token comment&quot;&gt;# or just&lt;/span&gt;
streamlit run app.py&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;Deployment&quot; style=&quot;position:relative;&quot;&gt;Deployment&lt;a href=&quot;#Deployment&quot; aria-label=&quot;Deployment permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;To deploy the app to a Posit Connect server use the following command.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;sh&quot;&gt;&lt;pre class=&quot;language-sh&quot;&gt;&lt;code class=&quot;language-sh&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;# Adding server and api key for use later via name&lt;/span&gt;
rsconnect &lt;span class=&quot;token function&quot;&gt;add&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;--name&lt;/span&gt; vm &lt;span class=&quot;token parameter variable&quot;&gt;--server&lt;/span&gt; http://192.168.110.133:3939 --api-key &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;api-key&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;# Deploy&lt;/span&gt;
rsconnect deploy streamlit &lt;span class=&quot;token parameter variable&quot;&gt;--name&lt;/span&gt; vm &lt;span class=&quot;token parameter variable&quot;&gt;--entrypoint&lt;/span&gt; app.py &lt;span class=&quot;token builtin class-name&quot;&gt;.&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 982px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/3a7c74532a6a6f55e965792d438f2793/9e029/streamlit-app-deployed.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 57.8%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAMCAIAAADtbgqsAAAACXBIWXMAAAsSAAALEgHS3X78AAABwUlEQVQoz4WRy27bMBBF/flF/6BAPyJAF10EWTTozoIFPxLbcevmAdsSJUocDp9DigpoI0HRRXowGwJzh3PnTjTnvAMlsW+b6njgDWP1ibwdU0xDSCmmlMZMUkoJIU6Mn6qKc26MmZBWXiEZ5TU6Lb1BbzCQjsEE0t5bZ83VdXF1XTijBUDf95xzAEDESUpDHvtX5WcahzQOw0AUjFafv37/9OWbhP54OjHGWF13XUdEk/FDiEgrVTHOe0lE3nvnnPfeWhtCmJhmuljeT6ez7cOvzXa3Xm/3+8fl8m4+Xw3DEEJgjIHonbNSSkR0zllrtdZZ3D7drlb3RVHOZvOiKHe730/PL/s/j5vNQwiByNd1xRirqupwOLRt697I4tufRVkuVnfrm5sfZbk4G07vaxtDzy/18Xi4iAHgXZw9W2MQFUoUAnohUCKABCkBpNbaeRKA3ruL2xBCSjkzRMw/103bNG1ds7pidd0wxo6nivPOWud9vhAA5F7EPPCM1loplcUDypH8B9fmLRcgBWAvsrrruxgjnZlEoxPR+BbvP8QYAcBabx3lstYYE2MM52P+J+cQwsWt1hokgkSJSoC8rP0KQZGo1fftHFAAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;streamlit app deployed&quot;
        title=&quot;streamlit app deployed&quot;
        src=&quot;/static/3a7c74532a6a6f55e965792d438f2793/9e029/streamlit-app-deployed.png&quot;
        srcset=&quot;/static/3a7c74532a6a6f55e965792d438f2793/0eb09/streamlit-app-deployed.png 500w,
/static/3a7c74532a6a6f55e965792d438f2793/9e029/streamlit-app-deployed.png 982w&quot;
        sizes=&quot;(max-width: 982px) 100vw, 982px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;log&quot;&gt;&lt;pre class=&quot;language-log&quot;&gt;&lt;code class=&quot;language-log&quot;&gt;    &lt;span class=&quot;token property&quot;&gt;Warning:&lt;/span&gt; Capturing the environment using &lt;span class=&quot;token string&quot;&gt;&apos;pip freeze&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;
             Consider creating a &lt;span class=&quot;token domain constant&quot;&gt;requirements.txt&lt;/span&gt; file instead&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;
Validating server&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;    &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;OK&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
Validating app mode&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;  &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;OK&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
Making bundle &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;       &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;OK&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
Deploying bundle &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;    &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;OK&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
Saving deployed information&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;  &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;OK&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
Building Streamlit application&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;
Bundle created with Python version &lt;span class=&quot;token number&quot;&gt;3.12.4&lt;/span&gt; is compatible with environment Local with Python version &lt;span class=&quot;token number&quot;&gt;3.12.4&lt;/span&gt; from &lt;span class=&quot;token file-path string&quot;&gt;/opt/python/3.12.4/bin/python3.12&lt;/span&gt;
Bundle requested Python version &lt;span class=&quot;token number&quot;&gt;3.12.4&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;;&lt;/span&gt; using &lt;span class=&quot;token file-path string&quot;&gt;/opt/python/3.12.4/bin/python3.12&lt;/span&gt; from Local which has version &lt;span class=&quot;token number&quot;&gt;3.12.4&lt;/span&gt;
&lt;span class=&quot;token date number&quot;&gt;2025/04/09&lt;/span&gt; &lt;span class=&quot;token time number&quot;&gt;11:20:54.466483344&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;rsc&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;session&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token property&quot;&gt;Content GUID:&lt;/span&gt; &lt;span class=&quot;token uuid constant&quot;&gt;a90a5b19-d989-4cf1-99fd-7fa748fadae4&lt;/span&gt;
&lt;span class=&quot;token date number&quot;&gt;2025/04/09&lt;/span&gt; &lt;span class=&quot;token time number&quot;&gt;11:20:54.467263345&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;rsc&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;session&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token property&quot;&gt;Content ID:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;6&lt;/span&gt;
&lt;span class=&quot;token date number&quot;&gt;2025/04/09&lt;/span&gt; &lt;span class=&quot;token time number&quot;&gt;11:20:54.467448246&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;rsc&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;session&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token property&quot;&gt;Bundle ID:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;10&lt;/span&gt;
&lt;span class=&quot;token date number&quot;&gt;2025/04/09&lt;/span&gt; &lt;span class=&quot;token time number&quot;&gt;11:20:54.467591546&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;rsc&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;session&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token property&quot;&gt;Job Key:&lt;/span&gt; CXzbEAP0fl5PEUf6
Determining session server location &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;
Connecting to session server &lt;span class=&quot;token url&quot;&gt;http://127.0.0.1:35199&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;
Connected to session server &lt;span class=&quot;token url&quot;&gt;http://127.0.0.1:35199&lt;/span&gt;
&lt;span class=&quot;token date number&quot;&gt;2025/04/09&lt;/span&gt; &lt;span class=&quot;token time number&quot;&gt;11:20:55.567199857&lt;/span&gt; Running on host&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; ubuntu&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;2404&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;server
&lt;span class=&quot;token date number&quot;&gt;2025/04/09&lt;/span&gt; &lt;span class=&quot;token time number&quot;&gt;11:20:55.613260740&lt;/span&gt; Linux distribution&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; Ubuntu &lt;span class=&quot;token number&quot;&gt;24.04.2&lt;/span&gt; LTS &lt;span class=&quot;token operator&quot;&gt;(&lt;/span&gt;noble&lt;span class=&quot;token operator&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token date number&quot;&gt;2025/04/09&lt;/span&gt; &lt;span class=&quot;token time number&quot;&gt;11:20:55.633468077&lt;/span&gt; Running as user&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; uid&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;996&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;(&lt;/span&gt;rstudio&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;connect&lt;span class=&quot;token operator&quot;&gt;)&lt;/span&gt; gid&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;987&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;(&lt;/span&gt;rstudio&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;connect&lt;span class=&quot;token operator&quot;&gt;)&lt;/span&gt; groups&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;987&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;(&lt;/span&gt;rstudio&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;connect&lt;span class=&quot;token operator&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token date number&quot;&gt;2025/04/09&lt;/span&gt; &lt;span class=&quot;token time number&quot;&gt;11:20:55.633742877&lt;/span&gt; Connect version&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;2024.09.0&lt;/span&gt;
&lt;span class=&quot;token date number&quot;&gt;2025/04/09&lt;/span&gt; &lt;span class=&quot;token time number&quot;&gt;11:20:55.634468878&lt;/span&gt; LANG&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; en_US&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;UTF&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;8&lt;/span&gt;
&lt;span class=&quot;token date number&quot;&gt;2025/04/09&lt;/span&gt; &lt;span class=&quot;token time number&quot;&gt;11:20:55.634618579&lt;/span&gt; Working directory&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token file-path string&quot;&gt;/opt/rstudio-connect/mnt/app&lt;/span&gt;
&lt;span class=&quot;token date number&quot;&gt;2025/04/09&lt;/span&gt; &lt;span class=&quot;token time number&quot;&gt;11:20:55.635216580&lt;/span&gt; Building environment using Python &lt;span class=&quot;token number&quot;&gt;3.12.4&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;(&lt;/span&gt;main&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token date number&quot;&gt;Feb 25&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;2025&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token time number&quot;&gt;22:32:47&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;GCC &lt;span class=&quot;token number&quot;&gt;13.3.0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; at &lt;span class=&quot;token file-path string&quot;&gt;/opt/python/3.12.4/bin/python3.12&lt;/span&gt;
&lt;span class=&quot;token date number&quot;&gt;2025/04/09&lt;/span&gt; &lt;span class=&quot;token time number&quot;&gt;11:20:55.647333402&lt;/span&gt; Using cached environment&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; Bq5M9EoyWyCJi_KEsAiGSg
&lt;span class=&quot;token date number&quot;&gt;2025/04/09&lt;/span&gt; &lt;span class=&quot;token time number&quot;&gt;11:20:58.194650391&lt;/span&gt; Packages in the environment&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; altair&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;5.5.0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; anyio&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;4.9.0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; asttokens&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;3.0.0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; attrs&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;25.3.0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; blinker&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1.9.0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; build&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;1&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;2&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;2&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;post1&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; CacheControl&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0.14.2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; cachetools&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;5.5.2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; certifi&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;2025.1.31&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; cffi&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1.17.1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; charset&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;normalizer&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;3.4.1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; cleo&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;2.1.0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; click&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;8.1.8&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; comm&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0.2.2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; crashtest&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0.4.1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; cryptography&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;44.0.2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; debugpy&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1.8.13&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; decorator&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;5.2.1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; distlib&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0.3.9&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; dulwich&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0.22.8&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; executing&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;2.2.0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; fastjsonschema&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;2.21.1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; filelock&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;3.18.0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; findpython&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0.6.3&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; gitdb&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;4.0.12&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; GitPython&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;3.1.44&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; h11&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0.14.0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; httpcore&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1.0.7&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; httpx&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0.28.1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; idna&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;3.10&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; installer&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0.7.0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; ipykernel&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;6.29.5&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; ipython&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;9.1.0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; ipython_pygments_lexers&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1.1.1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; jaraco&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;classes&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;3.4.0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; jaraco&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;context&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;6.0.1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; jaraco&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;functools&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;4.1.0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; jedi&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0.19.2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; jeepney&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0.9.0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; Jinja2&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;3.1.6&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; jsonschema&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;4.23.0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; jsonschema&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;specifications&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;2024.10.1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; jupyter_client&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;8.6.3&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; jupyter_core&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;5.7.2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; keyring&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;25.6.0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; MarkupSafe&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;3.0.2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; matplotlib&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;inline&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0.1.7&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; more&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;itertools&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;10.6.0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; msgpack&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1.1.0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; narwhals&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1.34.0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; nest&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;asyncio&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1.6.0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; num
py&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;2.2.4&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; packaging&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;24.2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; pandas&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;2.2.3&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; parso&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0.8.4&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; pbs&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;installer&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;2025.3.17&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; pexpect&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;4.9.0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; pillow&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;11.1.0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; pkginfo&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token ip-address constant&quot;&gt;1.12.1.2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; platformdirs&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;4.3.7&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; poetry&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;2.1.2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; poetry&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;core&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;2.1.2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; prompt_toolkit&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;3.0.50&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; protobuf&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;5.29.4&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; psutil&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;7.0.0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; ptyprocess&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0.7.0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; pure_eval&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0.2.3&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; pyarrow&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;19.0.1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; pycparser&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;2.22&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; pydeck&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0.9.1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; Pygments&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;2.19.1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; PyJWT&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;2.10.1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; pyproject_hooks&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1.2.0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; python&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;dateutil&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;2&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;9&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;0&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;post0&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; pytz&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;2025.2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; pyzmq&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;26.4.0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; RapidFuzz&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;3.13.0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; referencing&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0.36.2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; requests&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;2.32.3&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; requests&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;toolbelt&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1.0.0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; rpds&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;py&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0.24.0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; SecretStorage&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;3.3.3&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; semver&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;3.0.4&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; setuptools&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;78.1.0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; shellingham&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1.5.4&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; six&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1.17.0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; smmap&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;5.0.2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; sniffio&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1.3.1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; stack&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;data&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0.6.3&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; streamlit&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1.44.1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; tenacity&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;9.1.2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; toml&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0.10.2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; tomlkit&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0.13.2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; tornado&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;6.4.2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; traitlets&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;5.14.3&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; trove&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;classifiers&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;2025.3.19.19&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; typing_extensions&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;4.13.1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; tzdata&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;2025.2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; urllib3&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;2.3.0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; virtualenv&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;20.30.0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; watchdog&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;6.0.0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; wcwidth&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0.2.13&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; wheel&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0.46.1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; zstandard&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0.23.0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;token date number&quot;&gt;2025/04/09&lt;/span&gt; &lt;span class=&quot;token time number&quot;&gt;11:20:58.195191592&lt;/span&gt; Creating lockfile&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; python&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;requirements&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;txt&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;lock
Stopped session pings to &lt;span class=&quot;token url&quot;&gt;http://127.0.0.1:35199&lt;/span&gt;
&lt;span class=&quot;token property&quot;&gt;Completed Python build using Local against Python version:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;3.12.4&apos;&lt;/span&gt;
Launching Streamlit application&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;
Deployment completed successfully&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;
         &lt;span class=&quot;token property&quot;&gt;Dashboard content URL:&lt;/span&gt; &lt;span class=&quot;token url&quot;&gt;http://192.168.110.133:3939/connect/#/apps/a90a5b19-d989-4cf1-99fd-7fa748fadae4/access&lt;/span&gt;
         &lt;span class=&quot;token property&quot;&gt;Direct content URL:&lt;/span&gt; &lt;span class=&quot;token url&quot;&gt;http://192.168.110.133:3939/content/a90a5b19-d989-4cf1-99fd-7fa748fadae4/&lt;/span&gt;
Verifying deployed content&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;   &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;OK&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;</content:encoded></item><item><title><![CDATA[Making Machine Learning predictions from BigQuery Dataform]]></title><description><![CDATA[NFL Touchdown Prediction with Dataform and BigQuery ML This blog post demonstrates how to build an end-to-end ML pipeline using Google Cloud…]]></description><link>https://mortimer.xyz/gcp-bigquery-dataform-vertex-predict/</link><guid isPermaLink="false">https://mortimer.xyz/gcp-bigquery-dataform-vertex-predict/</guid><pubDate>Thu, 03 Apr 2025 12:00:00 GMT</pubDate><content:encoded>&lt;h1 id=&quot;NFL-Touchdown-Prediction-with-Dataform-and-BigQuery-ML&quot; style=&quot;position:relative;&quot;&gt;NFL Touchdown Prediction with Dataform and BigQuery ML&lt;a href=&quot;#NFL-Touchdown-Prediction-with-Dataform-and-BigQuery-ML&quot; aria-label=&quot;NFL Touchdown Prediction with Dataform and BigQuery ML permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h1&gt;
&lt;p&gt;This blog post demonstrates how to build an end-to-end ML pipeline using Google Cloud Platform (GCP) services to predict NFL game touchdowns.&lt;/p&gt;
&lt;p&gt;There is a lot more that is setup and explained in previous blog posts:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;../gcp-predict-service-cloudrun/&quot;&gt;GCP Serverless prediction service using Cloud Run from a Vertex AI model&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;../gcp-ml-predict-two-routes-bigquery-vertex/&quot;&gt;The Tale of Two Routes: A Love Story Between BigQuery and Vertex AI for Machine Learning predictions&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;Architecture&quot; style=&quot;position:relative;&quot;&gt;Architecture&lt;a href=&quot;#Architecture&quot; aria-label=&quot;Architecture permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The solution uses:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Dataform for data transformation and pipeline orchestration&lt;/li&gt;
&lt;li&gt;BigQuery for data storage and SQL operations&lt;/li&gt;
&lt;li&gt;Cloud Run for hosting the ML model endpoint &lt;a href=&quot;https://github.com/mortie23/ml/blob/master/infra/tf/gcp/environment/cloudrun.tf&quot;&gt;https://github.com/mortie23/ml/blob/master/infra/tf/gcp/environment/cloudrun.tf&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Python for ML model training (stored separately)&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;Project-Structure&quot; style=&quot;position:relative;&quot;&gt;Project Structure&lt;a href=&quot;#Project-Structure&quot; aria-label=&quot;Project Structure permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;ml-df/
├── 📁definitions/         # Dataform SQL definitions
│   ├── 📁t0_mdl/          # Model declarations
│   ├── 📁t1_raw/          # Raw data loading
│   └── 📁t2_cur/          # Curated/transformed/predicted data
├── 📁docs/                # Documentation and screenshots
│   ├── 📁img/             # Screenshots
└── workflow_settings.yaml # Dataform configuration&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2 id=&quot;Setup-and-Usage&quot; style=&quot;position:relative;&quot;&gt;Setup and Usage&lt;a href=&quot;#Setup-and-Usage&quot; aria-label=&quot;Setup and Usage permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;Set up a GCP project with BigQuery, Dataform and many other GCP infra components enabled&lt;/li&gt;
&lt;li&gt;Fork this repository and setup a workspace in Dataform&lt;/li&gt;
&lt;li&gt;Configure &lt;code class=&quot;language-text&quot;&gt;workflow_settings.yaml&lt;/code&gt; with your project details&lt;/li&gt;
&lt;li&gt;Run the Dataform pipeline&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id=&quot;Pipeline-Flow&quot; style=&quot;position:relative;&quot;&gt;Pipeline Flow&lt;a href=&quot;#Pipeline-Flow&quot; aria-label=&quot;Pipeline Flow permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 826px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/e40ad619bd78490662dc548aa3ef9a1d/e5adc/gcp-bqdf-pipeline-graph.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 25%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAFCAIAAADKYVtkAAAACXBIWXMAAAsSAAALEgHS3X78AAAAwklEQVQY01XP0W7DIAwFUP7/Y/bWP5mmSglpJugaJZNGAGMbTNqGdlN3Hvxiyb5XiUghyjkTcSlla0SkVtm2+mp7cb3uOwXfiz28XS5T1/fTNMEvahfblSoiOQsiEVFKiEiIKCKKmcG5EKNv1nV1zhFRrTWXAgkLp+Po33WyZtTDqet6PZzG8ZOZFWAwy8c8z1oP1lpjzPn8RcR/n0UkAkYgZqZ97DM3Cgl/1gUAvA8hBO9DjDFBKi3ws219ZP/n3vkGJMIe+d00A0AAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;gcp bqdf pipeline graph&quot;
        title=&quot;gcp bqdf pipeline graph&quot;
        src=&quot;/static/e40ad619bd78490662dc548aa3ef9a1d/e5adc/gcp-bqdf-pipeline-graph.png&quot;
        srcset=&quot;/static/e40ad619bd78490662dc548aa3ef9a1d/0eb09/gcp-bqdf-pipeline-graph.png 500w,
/static/e40ad619bd78490662dc548aa3ef9a1d/e5adc/gcp-bqdf-pipeline-graph.png 826w&quot;
        sizes=&quot;(max-width: 826px) 100vw, 826px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Raw NFL game statistics are loaded into BigQuery&lt;/li&gt;
&lt;li&gt;Data is transformed and prepared for prediction&lt;/li&gt;
&lt;li&gt;Each game’s features are sent to the ML model endpoint (Cloud Run service is required and discussed in other previously linked blog posts)&lt;/li&gt;
&lt;li&gt;Predictions are stored back in BigQuery&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;The main part of interest would be the SQLX for the prediction:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;sql&quot;&gt;&lt;pre class=&quot;language-sql&quot;&gt;&lt;code class=&quot;language-sql&quot;&gt;config {
  &lt;span class=&quot;token keyword&quot;&gt;type&lt;/span&gt;: &lt;span class=&quot;token string&quot;&gt;&quot;incremental&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  description: &lt;span class=&quot;token string&quot;&gt;&quot;NFL game touchdown predictions&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  uniqueKey: &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;game_team_id&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;columns&lt;/span&gt;: {
    game_team_id: &lt;span class=&quot;token string&quot;&gt;&apos;Unique identifier for game and team combination&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    predicted_touchdowns: &lt;span class=&quot;token string&quot;&gt;&apos;Predicted number of touchdowns&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    created_timestamp: &lt;span class=&quot;token string&quot;&gt;&apos;Created timestamp&apos;&lt;/span&gt;
  }&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;schema&lt;/span&gt;: dataform&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;projectConfig&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;vars&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;layer_cur
}

&lt;span class=&quot;token keyword&quot;&gt;with&lt;/span&gt; batched_games &lt;span class=&quot;token keyword&quot;&gt;as&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;select&lt;/span&gt;
      ifnull&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
        array_agg&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
          json_object&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
            &lt;span class=&quot;token string&quot;&gt;&apos;game_team_id&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; game_team_id&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;token string&quot;&gt;&apos;total_first_downs&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; total_first_downs&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;token string&quot;&gt;&apos;total_yards&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; total_yards&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;token string&quot;&gt;&apos;interceptions&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; interceptions&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;token string&quot;&gt;&apos;punts&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; punts
          &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
          &lt;span class=&quot;token keyword&quot;&gt;ignore&lt;/span&gt; nulls
        &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;json_object&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;as&lt;/span&gt; batch
    &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt;
      ${ref&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;game_stats&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;}
      ${&lt;span class=&quot;token keyword&quot;&gt;when&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;incremental&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token identifier&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;`&lt;/span&gt;where game_team_id not in (select distinct game_team_id from ${self()})&lt;span class=&quot;token punctuation&quot;&gt;`&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;}
&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; predictions &lt;span class=&quot;token keyword&quot;&gt;as&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;select&lt;/span&gt;
      ${ref&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;nfltouchdown_cloudrun&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;}&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;parse_json&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;to_json_string&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;batch&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;as&lt;/span&gt; predictions_json
    &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt;
      batched_games
&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; unnested_predictions &lt;span class=&quot;token keyword&quot;&gt;as&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;select&lt;/span&gt;
        json_value&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;prediction&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;$.game_team_id&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;as&lt;/span&gt; game_team_id
      &lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; json_value&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;prediction&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;$.prediction&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;as&lt;/span&gt; predicted_touchdowns
    &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt;
      predictions
    &lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; unnest&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;json_query_array&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;predictions_json&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;$&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;as&lt;/span&gt; prediction
&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;select&lt;/span&gt;
    cast&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;up&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;game_team_id &lt;span class=&quot;token keyword&quot;&gt;as&lt;/span&gt; INT64&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;as&lt;/span&gt; game_team_id
  &lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; cast&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;up&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;predicted_touchdowns &lt;span class=&quot;token keyword&quot;&gt;as&lt;/span&gt; FLOAT64&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;as&lt;/span&gt; predicted_touchdowns
  &lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;current_timestamp&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;as&lt;/span&gt; created_timestamp
&lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt;
  unnested_predictions up&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;This method will incrementally take new game stats (for arguments sake, let’s say it was run weekly), and then only make one request for all the games that week to the Cloud Run predict service.&lt;/p&gt;
&lt;p&gt;It does this by using &lt;code class=&quot;language-text&quot;&gt;array_agg&lt;/code&gt; and &lt;code class=&quot;language-text&quot;&gt;json_object&lt;/code&gt; to create an array of JSON objects and POSTing all that data to the BigQuery Remote Function connection. It then uses &lt;code class=&quot;language-text&quot;&gt;unnest&lt;/code&gt; to split back out the responses.&lt;/p&gt;
&lt;h3 id=&quot;Example-BigQuery-objects&quot; style=&quot;position:relative;&quot;&gt;Example BigQuery objects&lt;a href=&quot;#Example-BigQuery-objects&quot; aria-label=&quot;Example BigQuery objects permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;This shows what the BigQuery objects look like when viewed in the BigQuery web UI.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 416px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/94cd6998c2f4c7ee6f2dcca8df3600b7/5d0f3/gcp-bqdf-bigquery-objects.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 93.75%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAATCAIAAAAf7rriAAAACXBIWXMAAAsSAAALEgHS3X78AAACWElEQVQ4y32T25ajIBBF/f/f62kvMV6iiBcQEBJBgXKWYaaT7oc+D1gsOLWLQqJVayHEREiaZmmaJXF8LcqyrNIk+YyTP3GCcX8cB8DxQ4TQyHu/7zsAeO+dc/u+W2u98yEICqve+2Bzzh3HsW179Mx6ppVSZlmWPeFFUSZpdsmyS37tcX/JsjhJ6qq2zgMA7gcPfp5ZFJIBgHPOGGP0Oez7bozZztBYa89425xzofh934/jsNa+yEqpPM/TJI0/P4uijJPk7MEl7zHO80t2uQan934YRu/hJHvvwXupFONcKWWtde45/FfoRThn0ItsjGGcD+NU17dxHJzzx6/6Rv7XLaW4EOu6SqlmSueZUTqzIH7G1tov/7adt7Pve6S1ZowNw3RrGkLIME4dQgh1CHVt29b17ZJfq6rezPZFHscJABjjr4aJRd6aRinFOdda03k2xnzR4KkQGGMAYNu2l3mayMfHR9uirsPjOJVluUj5tfp+5nEkAMD5N/JS1Tcp5cyYVEpwTinFuGOcM84RQozxsFk/yca8kwmN46RtUdsi3PdN06AW3W5N3/cIdXVdjyMJZEIoAAghIue9dW4idBxHSqZVm9+vCgDWVZ98rc9u87MwMfQDJdRs24+t7wrkaTrPvCxL9JzD4/HQ2nQIkYmgrus6TCgVQvzwv+dd1zWY/bIsqzaobRljuMOUzkKIx+P+S/3LIqP3uXOuaVrcoSTNyqrO82ueXzHGZVUlSVoURfirGWMAIKV6Pclglkqt62NZpJRKKqXkKaXu4evP5wycCwC43+9/AZMKPjsuNF4qAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;gcp bqdf bigquery objects&quot;
        title=&quot;gcp bqdf bigquery objects&quot;
        src=&quot;/static/94cd6998c2f4c7ee6f2dcca8df3600b7/5d0f3/gcp-bqdf-bigquery-objects.png&quot;
        srcset=&quot;/static/94cd6998c2f4c7ee6f2dcca8df3600b7/5d0f3/gcp-bqdf-bigquery-objects.png 416w&quot;
        sizes=&quot;(max-width: 416px) 100vw, 416px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;h3 id=&quot;Example-execution&quot; style=&quot;position:relative;&quot;&gt;Example execution&lt;a href=&quot;#Example-execution&quot; aria-label=&quot;Example execution permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;A successful execution of the complete pipeline in Dataform looks like this:&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 1343px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/c71648e7bd286f956cb77901d38988d5/4f865/gcp-bqdf-execution.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 33.4%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAHCAIAAACHqfpvAAAACXBIWXMAAAsSAAALEgHS3X78AAAAuElEQVQY042LQQ7DIAzA+P8/p3XQFgIkoSQk09YP1CdLlsNxHP7nmhM7ErGI+DNCzvm2hjV+NlUxt6fzeZ63McsntoZrPX09DB63mftaLvp4dQ8A0HtvrTPzWmpmqqKquv6YrbVEZNmNm/kvms05AxKltL9e71wKj9E7ppRKgZzLNec1pda6be/WKiG2XpkJoMa0I1Fwd0LKpQBUVZU5AYCY85lba2MMHpIO3E86Mm+pZ2BViTH2jl9mcZnX8BLGyAAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;gcp bqdf execution&quot;
        title=&quot;gcp bqdf execution&quot;
        src=&quot;/static/c71648e7bd286f956cb77901d38988d5/4f865/gcp-bqdf-execution.png&quot;
        srcset=&quot;/static/c71648e7bd286f956cb77901d38988d5/0eb09/gcp-bqdf-execution.png 500w,
/static/c71648e7bd286f956cb77901d38988d5/1263b/gcp-bqdf-execution.png 1000w,
/static/c71648e7bd286f956cb77901d38988d5/4f865/gcp-bqdf-execution.png 1343w&quot;
        sizes=&quot;(max-width: 1343px) 100vw, 1343px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;h3 id=&quot;Example-results&quot; style=&quot;position:relative;&quot;&gt;Example results&lt;a href=&quot;#Example-results&quot; aria-label=&quot;Example results permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;When the pipeline is run, the predicted touchdowns are then available in the &lt;code class=&quot;language-text&quot;&gt;game_touchdown_predict&lt;/code&gt; table.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 600px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/832b36914f185687bd7a83124651d242/dface/gcp-bqdf-bigquery-predictions.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 51.2%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAKCAIAAAA7N+mxAAAACXBIWXMAAAsSAAALEgHS3X78AAABkUlEQVQozz2SwY7cIBBE/f/fkGOk/YkcIuWYQ6SNlFWU1c5YY0NDAw0MNIPXhsj2JnVqIV5VSd3D9TrmzFpr6xw5Qo1aaSJPREKIEGLvvbUW0urje0xr7z3cV4rvXLZhHMec8wlba4nIWmuMIbIGVUqxtdZ7L7Xd88aPrfd+z+s9b3XZhm1biSjEPaHW6pxj5t0+dU2dH7337qwjcs7ZR+HnXy+fPj99+fqttTYcz0Q+OPLGWFCafADt/1zw9WrGyfmQ9C6UoHyIQqrvP36+/H5lLgMoVBqJvNKo0UzzfLogopACAISEyzjepvntcp2F9D5wLswlZR5SZku+PGo/5L1f1/WcU8q1LkQEAIgIoLTW0zRJCcuy9N6HeE9oLHNph0IItdbW2rZtIYRSygErRAMAGrWUoBGXurS2wxmNYy5nGjMfrruR9zvsPmAEAKWUEEJp/T85K23yPzjGWGs9d+u9Z87G2HmeAdQ8z1LK222vff45alvHpXwcQwin6zmXUkIIiMY5h4jW2v0irF2WvfZfNmk0OeGKnaYAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;gcp bqdf bigquery predictions&quot;
        title=&quot;gcp bqdf bigquery predictions&quot;
        src=&quot;/static/832b36914f185687bd7a83124651d242/dface/gcp-bqdf-bigquery-predictions.png&quot;
        srcset=&quot;/static/832b36914f185687bd7a83124651d242/0eb09/gcp-bqdf-bigquery-predictions.png 500w,
/static/832b36914f185687bd7a83124651d242/dface/gcp-bqdf-bigquery-predictions.png 600w&quot;
        sizes=&quot;(max-width: 600px) 100vw, 600px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;</content:encoded></item><item><title><![CDATA[The Tale of Two Routes: A Love Story Between BigQuery and Vertex AI for Machine Learning predictions]]></title><description><![CDATA[This blog post is the first, and probably last where I thought I would see what the Github Copilot VSCode extension could do with writing a…]]></description><link>https://mortimer.xyz/gcp-ml-predict-two-routes-bigquery-vertex/</link><guid isPermaLink="false">https://mortimer.xyz/gcp-ml-predict-two-routes-bigquery-vertex/</guid><pubDate>Mon, 31 Mar 2025 12:00:00 GMT</pubDate><content:encoded>&lt;blockquote&gt;
&lt;p&gt;This blog post is the first, and probably last where I thought I would see what the Github Copilot VSCode extension could do with writing a blog post giving one file as context.
The blog post is written with respect to the following Python script &lt;a href=&quot;https://github.com/mortie23/ml/blob/master/model/nfl/touchdown/nfltouchdownpredict/predict.py&quot;&gt;https://github.com/mortie23/ml/blob/master/model/nfl/touchdown/nfltouchdownpredict/predict.py&lt;/a&gt;.
I gave it to Claude 3.5 and asked it to write a blog post based on explaining this file and asked it to tell it as a story with some humor.
I had to create the images, add some extra context here and there and fix some mistakes (like you have to do with the code it generates). Here is the result:&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Have you ever tried to make two different client applications happy with the same ML prediction service? It’s like trying to serve breakfast to both an early bird and a night owl - they want the same food but at different times and in different ways! This blog post will outline how I used FastAPI and a Vertex AI trained model, to make both Cloud Run and Vertex AI online endpoints be able to host the same model and allow BigQuery remote functions to call the service as well.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 826px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/e40ad619bd78490662dc548aa3ef9a1d/e5adc/gcp-ml-predict-pipeline-graph.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 25%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAFCAIAAADKYVtkAAAACXBIWXMAAAsSAAALEgHS3X78AAAAwklEQVQY01XP0W7DIAwFUP7/Y/bWP5mmSglpJugaJZNGAGMbTNqGdlN3Hvxiyb5XiUghyjkTcSlla0SkVtm2+mp7cb3uOwXfiz28XS5T1/fTNMEvahfblSoiOQsiEVFKiEiIKCKKmcG5EKNv1nV1zhFRrTWXAgkLp+Po33WyZtTDqet6PZzG8ZOZFWAwy8c8z1oP1lpjzPn8RcR/n0UkAkYgZqZ97DM3Cgl/1gUAvA8hBO9DjDFBKi3ws219ZP/n3vkGJMIe+d00A0AAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;gcp ml predict pipeline graph&quot;
        title=&quot;gcp ml predict pipeline graph&quot;
        src=&quot;/static/e40ad619bd78490662dc548aa3ef9a1d/e5adc/gcp-ml-predict-pipeline-graph.png&quot;
        srcset=&quot;/static/e40ad619bd78490662dc548aa3ef9a1d/0eb09/gcp-ml-predict-pipeline-graph.png 500w,
/static/e40ad619bd78490662dc548aa3ef9a1d/e5adc/gcp-ml-predict-pipeline-graph.png 826w&quot;
        sizes=&quot;(max-width: 826px) 100vw, 826px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;h2 id=&quot;The-Characters-in-Our-Story&quot; style=&quot;position:relative;&quot;&gt;The Characters in Our Story&lt;a href=&quot;#The-Characters-in-Our-Story&quot; aria-label=&quot;The Characters in Our Story permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;First, let’s meet the cast:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;FastAPI&lt;/strong&gt;: The lightning-fast matchmaker&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Pydantic&lt;/strong&gt;: The strict but fair bouncer checking IDs at the door&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Vertex AI&lt;/strong&gt;: The enterprise consultant who loves formal meetings at &lt;code class=&quot;language-text&quot;&gt;/predict&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;BigQuery&lt;/strong&gt;: The data engineer who prefers verbose catch-ups at the root path &lt;code class=&quot;language-text&quot;&gt;/&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 604px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/bead369b8d27ad9ff0df264123c87eb7/1994c/gcp-ml-predict-multiple-routes-callers.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 47.800000000000004%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAKCAYAAAC0VX7mAAAACXBIWXMAAAsSAAALEgHS3X78AAAC1ElEQVQoz3WQXUhTcQDFz6bpsun82KZ3c2a6tXuvU6/TOTfnStvdzIyZVhZlFCsqM5MgpLLGSp1lSZifpW74VVHTvgwsMEoog8joJTIII4rqJR96qah/ZNRLdOAH5+HwezhY456Cfd+IoOLse/Hezpkgfcs7OBomcNrjxnT3ATwiRPh57q2o9sUHiC62QjHiBxXwgQr0zfNPCCHz1AyTbdv9P9Ik7mfyAu8U29RwKuVpXa56yH9LVzdKdnG3x2NiL3erlQFfMjXqCwEhSPf2CVdG1QFdjUCbV4iuJoDOLoBalyLid/dyBXuuLB50OfGwpTLi9YliKVPUCWvZQXn65gFtitMp0ZZtADXqT4i/1CvW+HqhP+6HLbT2jxDobAK0RpucyeE1bKae02VlMXEcH9u4MSdueKdZGco6ZDqjldZymbTW5GB0canRqs4WlXR8cElaU2tyxpGz1a78MSE6GmLR5t2Bdi+gM/OSKHX+IkKKATiDELpKVL9pecSNaosUWCuCVC8CEMwmZyy0AiHxPa2U9M4FDXf4GKuv9rgciqwF6GmWoa2xEO3eYHSdKQH5XiF+9aB8eubuutpf38w156o+eS0q4o0Rfxz3xdQEiEp2eTI89nEgkhr108rrQyIWEKtlShWdY4uQbncB505C0NoAkK9bQMhWyeTVstmR8yUecg+C5x5LzGx9fviXQ8rI+wMd8qr+b2mK9oFwxdUemeJKX1T0k5sCOtehpC1FGtpok/GQIuiUR2AJUwOMeTWS9AXRnNXGpC+3U2Q2G2/cJrx0WwFMQMrmJSQyTBKTYoqTT4+ButYPasQHOjMftIkHbbRhqZEHl2iALjUPMPClwazZoVFn2RM1Bl7D5doiISwGKAdSTSsUOhO/mDUXxdM5vFZrKYzmEIGl9hIw3LK/Qq3hN786VlVUhZW69keW79wnqTx4NNhgc8K6ej1Yk31+8D/+l58P5ujBtLzvPQAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Diagram showing the two different routes and their callers&quot;
        title=&quot;Diagram showing the two different routes and their callers&quot;
        src=&quot;/static/bead369b8d27ad9ff0df264123c87eb7/1994c/gcp-ml-predict-multiple-routes-callers.png&quot;
        srcset=&quot;/static/bead369b8d27ad9ff0df264123c87eb7/0eb09/gcp-ml-predict-multiple-routes-callers.png 500w,
/static/bead369b8d27ad9ff0df264123c87eb7/1994c/gcp-ml-predict-multiple-routes-callers.png 604w&quot;
        sizes=&quot;(max-width: 604px) 100vw, 604px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;h2 id=&quot;The-Plot-Thickens-Two-Routes-One-Model&quot; style=&quot;position:relative;&quot;&gt;The Plot Thickens: Two Routes, One Model&lt;a href=&quot;#The-Plot-Thickens-Two-Routes-One-Model&quot; aria-label=&quot;The Plot Thickens Two Routes One Model permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;This is our bouncer’s checklist (Pydantic) making sure everyone’s properly dressed for the party. It’s like a strict dress code, but instead of “no sneakers,” it’s “no string where an integer should be”!&lt;/p&gt;
&lt;p&gt;For demonstrating our use case, we will be creating a prediction service that predicts the number of touchdowns a team scored based on the:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Number of first downs&lt;/li&gt;
&lt;li&gt;Total yards gained&lt;/li&gt;
&lt;li&gt;Number of interceptions thrown&lt;/li&gt;
&lt;li&gt;Number of punts&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The Python class looks like the following:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;py&quot;&gt;&lt;pre class=&quot;language-py&quot;&gt;&lt;code class=&quot;language-py&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ModelInputItem&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;BaseModel&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    game_team_id&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; Field&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; example&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;411&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    total_first_downs&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; Field&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; example&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;26&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    total_yards&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; Field&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; example&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;419&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    interceptions&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; Field&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; example&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    punts&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; Field&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; example&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;This bouncer also polite enough to give a really detailed of description of why you are not allowed in if you don’t meet the checklist.&lt;/p&gt;
&lt;p&gt;This example shows what response is given when we arrive at the endpoint with a POST request that contains only a bad field name. We are told we missed the required fields.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 946px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/c839120cb73f77c9aff08e0857b55eea/b2dbf/gcp-ml-predict-fastapi-swagger-pydantic.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 120.19999999999999%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAYCAIAAAB1KUohAAAACXBIWXMAAAsSAAALEgHS3X78AAADjklEQVQ4y42U7U8TBxzH718hRNLSo3DPV1jvKH2+6/UIOC9rQZnIC0MmdW/2bgbKFfdmLiwxS5agFJiGlcIKUpxmWSZgnRs60Gmy7Mk5gbb2eqW9ri3QrWYohpb4yeftN9/fi29+wHp8/Ztb30ZWIkt3l7+7vVgysnjrzuLinaVDXPp+OZ56Dvz0cJVxMLyL4d12TrA6XTaDlUYxDK4AgiAQBOEE/uDxQ2Bl9V6L2WjiDLRZrze+Rdso2qwnGwkIeoPwvbX7xhaqjTfxtmYb3WQkCASCURTFKoDjOIqiBIGvPX4A/LC6omvQmrEGcyNsIiGqTlN7pOZIyfLU1NRUV1dDELz6aA34/ekfA0Pec+c+FMVBcXDQ6xPFj4Z8hyqe9434/X/H1oFfn/zW7+33+sSPhz8ZvvjpheEL4nmxXxwY8Hkr2S8OeIe8fz57Avy18fTy2Ojnl0YmJq8EZgKTwcnxK+P+Cf8hjo6Pjl+deBbfAORsKpGRC7s7kiJv724X3xilkAUSaWk9sSll5Wgy9jwtReX4RiK2KcUyBSVfLOSK+bIqO9mtXBrYyqXThUwyK0/PhUbG/IGZ4Nz1a6Hw7LWv5+dvhA8avrkwuzAX+TGSySulcGZH2UxG21t5U2MTjaE00qCrA0G1WqUug0ajqaqqEgRBVlKlsLKtbMoxgefdFnOnk+FaKL6pSYfjpUEcgCTJ+vr6zs5OKZ180bytxOQ463DQOtQt2HkjZWsk9ShamuIBMAwDQbCjw518GY7KMc7JtVJ0u4nmLPo2K20kCQiGK4VdLpe0Je01p+J2xm622btOvtt2tN1iMmA4Vi5bCtfW1vb09KRzmb3mZIxv5XEMoyiKpmkSJ5AKYBimUqnOvn82Xyy8drbRZKZbmnU0iRE4BFcMq1WqPk9fbjf/6myWZSm9ThAYs15HIShxaLPH48kV86+anU6O05ECa2iz0ixJmnC8bDeBEyqVus/jyRf3N3MOlmHfEY4yjNlmpQkSh/Z9EwRBtNo6q4092d3b+94HFz+7/P/C0oWMpCS/+PLqpbHRyelgKDw3NRMIzASmQsHg7PRLp0LBr+Zn528shG9eX74bSf2ztTdPKXrG09fd3X2q59Tp3tMnurrcHW4H52BYZr92xm5n7FarhWHtP//y6MU8d7LriY3jJ45znOPYsbctFktzs8FgMKAIAoJg3etotVpQA8Iw/N8P+xcEIr70/HuXCQAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Diagram showing Pydantic 422 detailed error message&quot;
        title=&quot;Diagram showing Pydantic 422 detailed error message&quot;
        src=&quot;/static/c839120cb73f77c9aff08e0857b55eea/b2dbf/gcp-ml-predict-fastapi-swagger-pydantic.png&quot;
        srcset=&quot;/static/c839120cb73f77c9aff08e0857b55eea/0eb09/gcp-ml-predict-fastapi-swagger-pydantic.png 500w,
/static/c839120cb73f77c9aff08e0857b55eea/b2dbf/gcp-ml-predict-fastapi-swagger-pydantic.png 946w&quot;
        sizes=&quot;(max-width: 946px) 100vw, 946px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;h2 id=&quot;The-Tale-of-Two-Requests&quot; style=&quot;position:relative;&quot;&gt;The Tale of Two Requests&lt;a href=&quot;#The-Tale-of-Two-Requests&quot; aria-label=&quot;The Tale of Two Requests permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;h3 id=&quot;Vertex-AI-The-Formal-One&quot; style=&quot;position:relative;&quot;&gt;Vertex AI: The Formal One&lt;a href=&quot;#Vertex-AI-The-Formal-One&quot; aria-label=&quot;Vertex AI The Formal One permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Vertex AI shows up being very particular about hosting at &lt;code class=&quot;language-text&quot;&gt;/predict&lt;/code&gt; wearing a three-piece suit, wrapping everything in an “instances” array. Very formal, very proper.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;json&quot;&gt;&lt;pre class=&quot;language-json&quot;&gt;&lt;code class=&quot;language-json&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;&quot;instances&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token property&quot;&gt;&quot;game_team_id&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;411&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;token property&quot;&gt;&quot;total_first_downs&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;26&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;token property&quot;&gt;&quot;total_yards&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;419&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;token property&quot;&gt;&quot;interceptions&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;token property&quot;&gt;&quot;punts&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;4&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;We can host the Vertex AI trained model using the Deploy and test directly from the model registry.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 751px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/5aa5742c99b888f406bb1290b3892ab8/9ae5c/gcp-ml-predict-vertex-online-endpoint-deploy.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 51.6%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAKCAIAAAA7N+mxAAAACXBIWXMAAAsSAAALEgHS3X78AAABW0lEQVQoz52RTW4UMRCF516wTY4CO7LmIjkAQogTRJHCDRB7FJCCMkzb7vZvt+36sdtGntmwAsS3qVeLV/VKdUBE4pq3GEOIW0wpI1LwwTlvnXfOA9I+aLW2US7NmUMIIadEe+fStDbGaCXFoud9rzFGrQ0x11oSNKW5lNZ/4xBjJCIAMMYAAGBRGo2n2RJSI0TnnPd+22JrewjB+2CNJaJhBkRmLqXknJk45rpYMp6NH5qZ4Qwz760hEiICQK11xM4AlzH/wTk2MwI8Pf1QSi3LYox9fj6u69Z7b382p5xrrQh4PJ6EUELMQsyTkFrry7VaG2uHtNbZ8QBvjNXapJTGZgCshVancnQxLKubvZ2llEKIaZJSKimkEEOcTpMUSql527Za6yGEtTD8XMq7O/vxwX14cO/v7afP2z/djEi971++ppevH69vvl29+f7i1ePb26n3vre/mH8BU7k41MFfKkYAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Diagram showing Vertex AI request flow&quot;
        title=&quot;Diagram showing Vertex AI request flow&quot;
        src=&quot;/static/5aa5742c99b888f406bb1290b3892ab8/9ae5c/gcp-ml-predict-vertex-online-endpoint-deploy.png&quot;
        srcset=&quot;/static/5aa5742c99b888f406bb1290b3892ab8/0eb09/gcp-ml-predict-vertex-online-endpoint-deploy.png 500w,
/static/5aa5742c99b888f406bb1290b3892ab8/9ae5c/gcp-ml-predict-vertex-online-endpoint-deploy.png 751w&quot;
        sizes=&quot;(max-width: 751px) 100vw, 751px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;h3 id=&quot;BigQuery-The-Chatty-One&quot; style=&quot;position:relative;&quot;&gt;BigQuery: The Chatty One&lt;a href=&quot;#BigQuery-The-Chatty-One&quot; aria-label=&quot;BigQuery The Chatty One permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;BigQuery comes directly to root route &lt;code class=&quot;language-text&quot;&gt;/&lt;/code&gt; (and this cannot be changed). It also sends it’s life story (and this cannot be changed) - who it is, where it’s from, and oh by the way, the actual data is nested three levels deep because… why not? 🤷‍♂️&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;json&quot;&gt;&lt;pre class=&quot;language-json&quot;&gt;&lt;code class=&quot;language-json&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;&quot;requestId&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;unique-request-id&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;&quot;caller&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;//bigquery.googleapis.com/projects/...&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;&quot;sessionUser&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;user@example.com&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;&quot;calls&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
          &lt;span class=&quot;token property&quot;&gt;&quot;game_team_id&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;411&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
          &lt;span class=&quot;token property&quot;&gt;&quot;total_first_downs&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;26&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
          &lt;span class=&quot;token property&quot;&gt;&quot;total_yards&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;419&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
          &lt;span class=&quot;token property&quot;&gt;&quot;interceptions&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
          &lt;span class=&quot;token property&quot;&gt;&quot;punts&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;4&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 948px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/9267ea79b7ce573d238b30f6b3f42eaf/6648e/gcp-ml-predict-fastapi-swagger-bigquery-root-route.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 128.2%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAaCAIAAAA44esqAAAACXBIWXMAAAsSAAALEgHS3X78AAAC0ElEQVQ4y43TW2/kNBQH8Hx/CYlKSHwBeES7QvDCAwghHtA+bGmFpjOTmU5mEseJ4/gW344vQemUMtDdtkc/WbaSv+PIOsXIGOcc4x61+L5CiKyb8fcQ3bxUnl+s4nhCf3z48+PH27t1iTs68IrIm5j8/IYq8qv7vxAGACmlMSblpR62en23lJbXi5GJ21VZt32e55hyjHkZX5RS9hAAQnF7zD9ezx/KeGrHIxZUgjAz1/lVA/fFHUE/lTc3uKJSYsaIENLZVwlr2KSKNdv+2vz217gaJtpy3ImeeyngEfeCeyGe4Y4PmhYGDBlpN/SIdjVp6x61tEMDbkjbjp2CaQpawfRJBTeiPN7v62p/OmwOuwM67uvqvqn29WF3OjSk5VZ8Mim9KqRTHSOY9WhoH+CGoHbEiOJT36Ch7TiRQUn4lwpaBb2EDdiBjUwKyOFzfIInDqxhgxmJsaqQdtqhQ0VqGxxE+BwfwQe/jN5qSszQG6MK6dVyZoqpZipMl8e7pKMxyZpkp6inaKZkJUyFA9cR0lOqpQhOhxRCCjHFSymnY13fbbbbcte0iNABYzzysTDejIKJSVqwcY5nFz/pfQLIMDCKuhb1mIpRGsUUF1oUOhjtLBNcakXZeKatsd5Zbx0446z1DlJIS9vMec5pTnnOJtjCZ9/35Pr6FrV4W+5Xq816syMDlUpxKaVSQkohFtbZpSmWhloYsIWJ1oM31lx2XJ7zc+fMkyWsweQ5e+/vD8dDdeoImbTO+f+vPvef8Gq1Xm/KukZciBDDm8Im2BCDddYDOO8A4Hw3b/2y8/4h8Fj/PM7pxcOfwzokn2aIs4+zD9mfJ2cP9/wIMlwupVPFBBor0QiJhGyF7CfZymV+RjVn5hGRdNTsabl0lfL6m5/xF9/VV++br75H3/7Sff0D+vJdc/W+uXp32jYdNyORdFC0rHYd7+m0zHtOuBF/A+BW0KCetFcGAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Screenshot of Swagger UI showing the ModelInputItem schema&quot;
        title=&quot;Screenshot of Swagger UI showing the ModelInputItem schema&quot;
        src=&quot;/static/9267ea79b7ce573d238b30f6b3f42eaf/6648e/gcp-ml-predict-fastapi-swagger-bigquery-root-route.png&quot;
        srcset=&quot;/static/9267ea79b7ce573d238b30f6b3f42eaf/0eb09/gcp-ml-predict-fastapi-swagger-bigquery-root-route.png 500w,
/static/9267ea79b7ce573d238b30f6b3f42eaf/6648e/gcp-ml-predict-fastapi-swagger-bigquery-root-route.png 948w&quot;
        sizes=&quot;(max-width: 948px) 100vw, 948px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;h2 id=&quot;The-Magic-of-FastAPI-Routes&quot; style=&quot;position:relative;&quot;&gt;The Magic of FastAPI Routes&lt;a href=&quot;#The-Magic-of-FastAPI-Routes&quot; aria-label=&quot;The Magic of FastAPI Routes permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Once I started attempting to get BigQuery remote functions to call the &lt;code class=&quot;language-text&quot;&gt;/predict&lt;/code&gt; route that Vertex AI produced, I realised this wouldn’t work. So we needed another route. It is like having two different entrances to the same restaurant - the formal dining room for Vertex AI and the casual café for BigQuery. Same kitchen (our ML model), different serving styles!&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;py&quot;&gt;&lt;pre class=&quot;language-py&quot;&gt;&lt;code class=&quot;language-py&quot;&gt;&lt;span class=&quot;token decorator annotation punctuation&quot;&gt;@app&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;post&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;/&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;predict_bigquery&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;input_data&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; BigQueryRemoteRequest&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;# BigQuery&apos;s casual coffee chat&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;# ...existing code...&lt;/span&gt;

&lt;span class=&quot;token decorator annotation punctuation&quot;&gt;@app&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;post&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;os&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;environ&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;AIP_PREDICT_ROUTE&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;predict_vertex&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;input_data&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; VertexPredictionRequest&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;# Vertex AI&apos;s formal business meeting&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;# ...existing code...&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2 id=&quot;The-Setup-Environment-Magic-and-Config-Sorcery&quot; style=&quot;position:relative;&quot;&gt;The Setup: Environment Magic and Config Sorcery&lt;a href=&quot;#The-Setup-Environment-Magic-and-Config-Sorcery&quot; aria-label=&quot;The Setup Environment Magic and Config Sorcery permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Before our restaurant even opens its doors, we need to set up the kitchen! Let’s peek behind the curtain:&lt;/p&gt;
&lt;h3 id=&quot;The-env-Chefs-Secret-Recipe&quot; style=&quot;position:relative;&quot;&gt;The .env Chef’s Secret Recipe&lt;a href=&quot;#The-env-Chefs-Secret-Recipe&quot; aria-label=&quot;The env Chefs Secret Recipe permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;It’s like having a secret recipe card - keeping our environment spices separate from the main cookbook! Vertex AI is very opinionated about how it uses environment variables, what they need to be called and in fact, sometimes different names for the same variable in different parts of the life cycle because… why not? 🤷‍♂️&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;ini&quot;&gt;&lt;pre class=&quot;language-ini&quot;&gt;&lt;code class=&quot;language-ini&quot;&gt;&lt;span class=&quot;token key attr-name&quot;&gt;env&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token value attr-value&quot;&gt;dev&lt;/span&gt;
&lt;span class=&quot;token key attr-name&quot;&gt;project_id&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token value attr-value&quot;&gt;prj-xyz-dev-nfl-0&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;# For training&lt;/span&gt;
&lt;span class=&quot;token key attr-name&quot;&gt;AIP_MODEL_DIR&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token value attr-value&quot;&gt;gs://bkt-xyz-dev-nfl-vertex-0/aiplatform-custom-training-yyyy-mm-dd-hh:mm:ss.ms/model&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;# For prediction&lt;/span&gt;
&lt;span class=&quot;token key attr-name&quot;&gt;AIP_STORAGE_URI&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token value attr-value&quot;&gt;gs://bkt-xyz-dev-nfl-vertex-0/aiplatform-custom-training-yyyy-mm-dd-hh:mm:ss.ms/model&lt;/span&gt;
&lt;span class=&quot;token key attr-name&quot;&gt;AIP_HEALTH_ROUTE&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token value attr-value&quot;&gt;/ping&lt;/span&gt;
&lt;span class=&quot;token key attr-name&quot;&gt;AIP_PREDICT_ROUTE&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token value attr-value&quot;&gt;/predict&lt;/span&gt;
&lt;span class=&quot;token key attr-name&quot;&gt;etc&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Vertex AI will be setting these for us. We need to be clever about setting them when deploying the service to Cloud Run for our BigQuery remote function.&lt;/p&gt;
&lt;h3 id=&quot;Hydra-The-Configuration-Sous-Chef&quot; style=&quot;position:relative;&quot;&gt;Hydra: The Configuration Sous Chef&lt;a href=&quot;#Hydra-The-Configuration-Sous-Chef&quot; aria-label=&quot;Hydra The Configuration Sous Chef permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;yaml&quot;&gt;&lt;pre class=&quot;language-yaml&quot;&gt;&lt;code class=&quot;language-yaml&quot;&gt;&lt;span class=&quot;token key atrule&quot;&gt;model_bucket&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; bkt&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;xyz&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;&amp;lt;env&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;nfl&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;vertex&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;
&lt;span class=&quot;token key atrule&quot;&gt;model_path&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; nfl&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;touchdown/model.joblib
etc&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Hydra is our sous chef who knows how to adapt the recipe for different kitchens (environments). Watch the magic:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;py&quot;&gt;&lt;pre class=&quot;language-py&quot;&gt;&lt;code class=&quot;language-py&quot;&gt;initialize&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;version_base&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token boolean&quot;&gt;None&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; config_path&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;.&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
cfg &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; compose&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;config_name&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;predict&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;# Season the config with environment flavoring&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; k&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; v &lt;span class=&quot;token keyword&quot;&gt;in&lt;/span&gt; cfg&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;items&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    cfg&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;k&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; v&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;replace&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;&amp;lt;env&gt;&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; env&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 760px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/a073f7601f7f93c78466448717dc819c/efd7b/gcp-ml-predict-hydra-flow.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 51%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAKCAYAAAC0VX7mAAAACXBIWXMAAAsSAAALEgHS3X78AAACiklEQVQoz2N48vQVw7Nnrxn+//vPMHHeSsai5okM5R1TGRwmbWZkWPiGgXPmFSnrRSv0GSb/Z2DIOM3I8P8/A0PXfxad+dss+tdGc1SvzmPIXV7FAAf///9nuHP3Eef7N2+ZJs1fzVDSMgls4AnXJQypc3oYApdOcl60zrvcevoGBkMGBgb5snkMDDM/cnasSZr4dTuDyPsdrAxHN2sxaJi7QAw8cPAUw5UrdyadOXcth4GBgaVxwlzp8vapEhUdU8UKm6cpTJoxWaN/2WrdsPxpfPq2bpLmnpFizT39ElOWLDKLW/iWtWzDa4bU7o1gA2GGsnZPnOPQ2T/LjIFXjnXZloOs7TOWsi3YfoKpvneWnHd6raxFRJ7kjq2rmNXM3dld/CNZAzNruc3D85X84hJYzN0DGcy9QhEGBsdlM/HK6ciKa5hqapi7qMvrWfNLaZoxMzAwMLdOXSJk7hooo2xgY6Ji4iipYeHKYOIcwCCqrM+mYmSrrWHmrKdh7ioMEkd2IQiwMDAw8GtZukrJ6lqJKhnaCXKIq/BXdc0QcwlOFFM1cVTXtHCVAGlwC05gMHYOYNS0cDXQMHcx1zB3MdEwdwHJiWiYu3Aw3L3zhOHWzYeJ16/fdwJF0OIN+7h7567hmr58G0d5+1TZK0+/MIFcpWriCHeFhrkLi4a5C5uWhRuDurkLo4a5CyuIr2HuwsRw6PBZhuMnLuYdPHw2EuTSup5ZYqWtk4VKWyeLVHRM49hx8gaDqUsgI9wwMxB2BtPqCAswvMwH8rKxZxRD08R5DJWd0xkq2qcxlCPhio5pGJqxYQbXsFRmt5AkMefgRGn3iHRQWDJkVXXADayA4vL2qSga1ZEwMgAAKH7uYigqH4EAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Diagram showing Hydra config templating&quot;
        title=&quot;Diagram showing Hydra config templating&quot;
        src=&quot;/static/a073f7601f7f93c78466448717dc819c/efd7b/gcp-ml-predict-hydra-flow.png&quot;
        srcset=&quot;/static/a073f7601f7f93c78466448717dc819c/0eb09/gcp-ml-predict-hydra-flow.png 500w,
/static/a073f7601f7f93c78466448717dc819c/efd7b/gcp-ml-predict-hydra-flow.png 760w&quot;
        sizes=&quot;(max-width: 760px) 100vw, 760px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;h2 id=&quot;The-Model-From-Training-Kitchen-to-Serving-Station&quot; style=&quot;position:relative;&quot;&gt;The Model: From Training Kitchen to Serving Station&lt;a href=&quot;#The-Model-From-Training-Kitchen-to-Serving-Station&quot; aria-label=&quot;The Model From Training Kitchen to Serving Station permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Remember that Vertex AI training job that created our model (well, this is for another blog post)? It’s like a master chef preparing components in the main kitchen. Now we need to get that perfectly trained model to our serving station:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;py&quot;&gt;&lt;pre class=&quot;language-py&quot;&gt;&lt;code class=&quot;language-py&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;with&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;open&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;model.joblib&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;wb&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;as&lt;/span&gt; model_f&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    client&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;download_blob_to_file&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
        &lt;span class=&quot;token string-interpolation&quot;&gt;&lt;span class=&quot;token string&quot;&gt;f&quot;&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;os&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;environ&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;AIP_STORAGE_URI&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;/model.joblib&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; model_f
    &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

_model &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; joblib&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;load&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;model.joblib&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;This is like getting our special sauce from the central kitchen (Cloud Storage) where the training job left it. The &lt;code class=&quot;language-text&quot;&gt;AIP_STORAGE_URI&lt;/code&gt; environment variable is our delivery instructions, courtesy of Vertex AI!&lt;/p&gt;
&lt;p&gt;Think of it as a relay race:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Training Job: “I’ve created this perfect model! Storing it in GCS!”&lt;/li&gt;
&lt;li&gt;Cloud Run or Vertex Endpoint Service: “Got the location from &lt;code class=&quot;language-text&quot;&gt;AIP_STORAGE_URI&lt;/code&gt;!”&lt;/li&gt;
&lt;li&gt;Model Loading: “Model loaded and ready to make predictions!”&lt;/li&gt;
&lt;/ol&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;py&quot;&gt;&lt;pre class=&quot;language-py&quot;&gt;&lt;code class=&quot;language-py&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;# Get our environment settings&lt;/span&gt;
env_path &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; Path&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;__file__&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;parent&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;parent &lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;.env&quot;&lt;/span&gt;
load_dotenv&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;env_path&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
env &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; os&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;getenv&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;env&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;# Set up our prediction server&lt;/span&gt;
app &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; FastAPI&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
client &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; storage&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Client&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;os&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;environ&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;project_id&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;# Download our pre-trained model&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;with&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;open&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;model.joblib&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;wb&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;as&lt;/span&gt; model_f&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    client&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;download_blob_to_file&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
        &lt;span class=&quot;token string-interpolation&quot;&gt;&lt;span class=&quot;token string&quot;&gt;f&quot;&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;os&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;environ&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;AIP_STORAGE_URI&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;/model.joblib&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        model_f
    &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2 id=&quot;The-Happy-Ending&quot; style=&quot;position:relative;&quot;&gt;The Happy Ending&lt;a href=&quot;#The-Happy-Ending&quot; aria-label=&quot;The Happy Ending permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Thanks to FastAPI and Pydantic, we’ve created a ML model serving solution that’s like a restaurant that makes both fine diners and casual brunchers feel at home. The model happily predicts NFL touchdowns while BigQuery and Vertex AI are think they’re getting special treatment!&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 540px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/f323b5014d4109ca6b8959735b6d20c5/03ec9/gcp-ml-predict-different-request-same-response.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 97.8%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAUCAIAAAAC64paAAAACXBIWXMAAAsSAAALEgHS3X78AAAEV0lEQVQ4y0WNaVNTZwBG79d2tFUrdkhpSFAxIcRgyAYh+3Il292X903ufm9WiGypaE1QElABdYDIiGupVmoXnWn/Q39Bf1CnOtPOnI/POQ9SFUBNhDUBVEq8AVkdMBpPaTypcLgGcA1gMluQGUJmCIUlVY76hMbTGk8jdQnW5WJdKlb/9ykd4jokRDorkBkN4CpH/ieUPx78C2SROVVYKqsNWaiKxUqJLxdZA1JGiZQ5rAXSc2RMoPMqSygs9UmQOPrjjNMBg1RKIBtwNchkQy5VSsCArMZTZUjKPLm/XNqdoyGLa4DUOFoH7JwEeloOD09qgDUgiwgsQQdtf24qi0bJ+NjTOEoF5HcMpo/Z79ltD8IzMotrPK1DrimBv/vG4TVcYKi6CJA6VfgFC/+1zM0roFykNY6WaLym8KTfGzl5ZisSTZmGi+m4UeQqkC0L8AbMPiVCVZasihDZtV/+6YTpD7e7pcGaAlXIihRuJKLeUZvf59doduKKO2W3yVhOF4EuglYqfjR6XguHKrKA5EdGwOiF9VEbiIWYeHjR71dJDF5N4NFgxuPOD1oKkRmZKzTQ2A0mVy0xq27XtUHT1YCvoZYQn2fiq88/44ctoQknhcbEdFwi8yokqgJ72Twyefrr2eBMVWHVTGI+4O3Fp9sOm/vUmXhkZl4BCIdlaZd7ORBcqsh1mVcALZAFlcPpTMpquWQ2j4zbL8tMTuYIkSMNmhCj8azHbxSBypMIR+Q5klQIXKRxicZBIcNk0SKWDfu8tguX8omI+ZthJoOyWZTNpkWWSibQcCBYJAlYyCKJaf/ZL05+eeLElMtJZ1LZeGg2GsSSkSvjTteYg0Ljo9aL057JfDKcT0ZSoSmTyTJwdnA2GmZm00ghnRg6d254YCA5FZBojMmk8FSMQBNBhyMwZsfTsZjfN262EKkYm0WZTPrcWZPvitso8iUKQ7hcevy8dezSxdi0twZJicjAXAq7Gs06HdMjVo/T4XU6fENDqHeSx1C2gJ4+NZBLRMtFVmZJJDnt9006odMpRmeaKpgXmEaJ0uk8dvF8NuBmMvF8Ihz/1owHAwYkZLYwaDLn0ZgOcJEqIMUJlxIN3YxFO2jy+5Vauy51msp6U+3oYH1B7S3qt5vKdYVpleFKudhuKP6xsaTP29SAyuNI2eep+32E21UzDbUF4u5q7cHNxt7aYr+7sre29Hh95aDb2r+z/KjT7C6orUSoZLHA8XEFEgYkkLjFUrWOLEjUmkTfbUiPu63DzRuvdtpHDztHD9d+fLR29LDzcqf9arv9ZOP6vVb1NsRlqxVis4VUFFkRmKXw1NaScbBx/cnm6svtW6937/z8uHfc7x73u+8ONt4dbBz3e68frb/Y6ewv6Bt+j2S3yXSuDAjkbb93tN97s9d9u999d3D31yf33z/b/vB85/en998/2/rwfPv9s63fDu8d9zfe7HV/eLC2e2thd33lcHP1+f2b/wDfWZfaEjiu6gAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Meme about different request formats but same predictions&quot;
        title=&quot;Meme about different request formats but same predictions&quot;
        src=&quot;/static/f323b5014d4109ca6b8959735b6d20c5/03ec9/gcp-ml-predict-different-request-same-response.png&quot;
        srcset=&quot;/static/f323b5014d4109ca6b8959735b6d20c5/0eb09/gcp-ml-predict-different-request-same-response.png 500w,
/static/f323b5014d4109ca6b8959735b6d20c5/03ec9/gcp-ml-predict-different-request-same-response.png 540w&quot;
        sizes=&quot;(max-width: 540px) 100vw, 540px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;In the end, it’s not about the route you take, it’s about the predictions you make.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 600px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/832b36914f185687bd7a83124651d242/dface/gcp-ml-predict-bigquery-predictions.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 51.2%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAKCAIAAAA7N+mxAAAACXBIWXMAAAsSAAALEgHS3X78AAABkUlEQVQozz2SwY7cIBBE/f/fkGOk/YkcIuWYQ6SNlFWU1c5YY0NDAw0MNIPXhsj2JnVqIV5VSd3D9TrmzFpr6xw5Qo1aaSJPREKIEGLvvbUW0urje0xr7z3cV4rvXLZhHMec8wlba4nIWmuMIbIGVUqxtdZ7L7Xd88aPrfd+z+s9b3XZhm1biSjEPaHW6pxj5t0+dU2dH7337qwjcs7ZR+HnXy+fPj99+fqttTYcz0Q+OPLGWFCafADt/1zw9WrGyfmQ9C6UoHyIQqrvP36+/H5lLgMoVBqJvNKo0UzzfLogopACAISEyzjepvntcp2F9D5wLswlZR5SZku+PGo/5L1f1/WcU8q1LkQEAIgIoLTW0zRJCcuy9N6HeE9oLHNph0IItdbW2rZtIYRSygErRAMAGrWUoBGXurS2wxmNYy5nGjMfrruR9zvsPmAEAKWUEEJp/T85K23yPzjGWGs9d+u9Z87G2HmeAdQ8z1LK222vff45alvHpXwcQwin6zmXUkIIiMY5h4jW2v0irF2WvfZfNmk0OeGKnaYAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;gcp ml predict bigquery predictions&quot;
        title=&quot;gcp ml predict bigquery predictions&quot;
        src=&quot;/static/832b36914f185687bd7a83124651d242/dface/gcp-ml-predict-bigquery-predictions.png&quot;
        srcset=&quot;/static/832b36914f185687bd7a83124651d242/0eb09/gcp-ml-predict-bigquery-predictions.png 500w,
/static/832b36914f185687bd7a83124651d242/dface/gcp-ml-predict-bigquery-predictions.png 600w&quot;
        sizes=&quot;(max-width: 600px) 100vw, 600px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;A good ML service is like a well-run restaurant - the customers don’t need to know about the complex kitchen operations, they just enjoy the consistent, delicious predictions.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[GCP Serverless prediction service using Cloud Run from a Vertex AI model]]></title><description><![CDATA[Deploying ML Models on Cloud Run: From Placeholder to Production This guide walks through deploying a machine learning model on Google Cloud…]]></description><link>https://mortimer.xyz/gcp-predict-service-cloudrun/</link><guid isPermaLink="false">https://mortimer.xyz/gcp-predict-service-cloudrun/</guid><pubDate>Sat, 15 Feb 2025 12:00:00 GMT</pubDate><content:encoded>&lt;h1 id=&quot;Deploying-ML-Models-on-Cloud-Run-From-Placeholder-to-Production&quot; style=&quot;position:relative;&quot;&gt;Deploying ML Models on Cloud Run: From Placeholder to Production&lt;a href=&quot;#Deploying-ML-Models-on-Cloud-Run-From-Placeholder-to-Production&quot; aria-label=&quot;Deploying ML Models on Cloud Run From Placeholder to Production permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h1&gt;
&lt;p&gt;This guide walks through deploying a machine learning model on Google Cloud Run, using a two-phase deployment strategy: placeholder service followed by model deployment.&lt;/p&gt;
&lt;h2 id=&quot;Architecture-Overview&quot; style=&quot;position:relative;&quot;&gt;Architecture Overview&lt;a href=&quot;#Architecture-Overview&quot; aria-label=&quot;Architecture Overview permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The architecture connects these components:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Vertex AI for training and model registry&lt;/li&gt;
&lt;li&gt;Model artifacts stored in Cloud Storage&lt;/li&gt;
&lt;li&gt;Cloud Run service running our prediction container&lt;/li&gt;
&lt;li&gt;BigQuery for making predictions via remote functions (not discussed in this blog post)&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id=&quot;Prerequisites&quot; style=&quot;position:relative;&quot;&gt;Prerequisites&lt;a href=&quot;#Prerequisites&quot; aria-label=&quot;Prerequisites permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;I have built this on my Windows development machine with WSL2, VScode and Docker Desktop. The are some of the other requirements on your local development machine.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Google Cloud Project with required APIs enabled&lt;/li&gt;
&lt;li&gt;Terraform &gt;= 1.0&lt;/li&gt;
&lt;li&gt;Python &gt;= 3.9&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;gcloud&lt;/code&gt; CLI configured&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;Phase-1-Placeholder-Service&quot; style=&quot;position:relative;&quot;&gt;Phase 1: Placeholder Service&lt;a href=&quot;#Phase-1-Placeholder-Service&quot; aria-label=&quot;Phase 1 Placeholder Service permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;First, we deploy a minimal Nginx container that serves a simple status endpoint. This helps us establish our infrastructure and permissions before deploying the actual model.&lt;/p&gt;
&lt;h3 id=&quot;Placeholder-Container-Structure&quot; style=&quot;position:relative;&quot;&gt;Placeholder Container Structure&lt;a href=&quot;#Placeholder-Container-Structure&quot; aria-label=&quot;Placeholder Container Structure permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Our placeholder service uses Nginx to serve a static JSON response:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;📁devops/serve/placeholder/
├── Dockerfile
├── nginx.conf
└── status.json&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;nginx&quot;&gt;&lt;pre class=&quot;language-nginx&quot;&gt;&lt;code class=&quot;language-nginx&quot;&gt;&lt;span class=&quot;token directive&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;server&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token directive&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;listen&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;8080&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token directive&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;server_name&lt;/span&gt; localhost&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token directive&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;location&lt;/span&gt; /&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token directive&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;root&lt;/span&gt; /usr/share/nginx/html&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token directive&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;try_files&lt;/span&gt; /status.json =404&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token directive&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;default_type&lt;/span&gt; application/json&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;Initial-Deployment&quot; style=&quot;position:relative;&quot;&gt;Initial Deployment&lt;a href=&quot;#Initial-Deployment&quot; aria-label=&quot;Initial Deployment permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;The placeholder is deployed via Terraform:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;hcl&quot;&gt;&lt;pre class=&quot;language-hcl&quot;&gt;&lt;code class=&quot;language-hcl&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;resource &lt;span class=&quot;token type variable&quot;&gt;&quot;google_cloud_run_service&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;cr_nfl_predict&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;name&lt;/span&gt;     &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;nfl-touchdown&quot;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;location&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; var.region

  &lt;span class=&quot;token keyword&quot;&gt;template&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;spec&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token keyword&quot;&gt;containers&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token property&quot;&gt;image&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token type variable&quot;&gt;region&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;-docker.pkg.dev/prj-xyz-shr-rep-0/rpo-bld-dkr-0/placeholder:latest&quot;&lt;/span&gt;
        &lt;span class=&quot;token comment&quot;&gt;// ...configuration...&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2 id=&quot;ModelPorter-A-Custom-MLOps-CLI&quot; style=&quot;position:relative;&quot;&gt;ModelPorter: A Custom MLOps CLI&lt;a href=&quot;#ModelPorter-A-Custom-MLOps-CLI&quot; aria-label=&quot;ModelPorter A Custom MLOps CLI permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;ModelPorter is our custom command-line tool that orchestrates the ML model lifecycle across Google Cloud Platform services. It simplifies the complex workflow of training, deploying, and serving machine learning models.&lt;/p&gt;
&lt;h3 id=&quot;Why-We-Built-It&quot; style=&quot;position:relative;&quot;&gt;Why We Built It&lt;a href=&quot;#Why-We-Built-It&quot; aria-label=&quot;Why We Built It permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Traditional ML deployment often involves juggling multiple cloud services, configuration files, and deployment steps. For our NFL touchdown prediction model, we needed to:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Build and push Docker containers for both training and prediction&lt;/li&gt;
&lt;li&gt;Manage training jobs on Vertex AI&lt;/li&gt;
&lt;li&gt;Deploy prediction endpoints to Cloud Run&lt;/li&gt;
&lt;li&gt;Handle environment-specific configurations&lt;/li&gt;
&lt;li&gt;Maintain consistent model versioning&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Rather than managing these steps manually or with complex shell scripts, ModelPorter provides a unified interface.&lt;/p&gt;
&lt;h3 id=&quot;Key-Features&quot; style=&quot;position:relative;&quot;&gt;Key Features&lt;a href=&quot;#Key-Features&quot; aria-label=&quot;Key Features permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;# Build containers&lt;/span&gt;
modelporter &lt;span class=&quot;token parameter variable&quot;&gt;--env&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;dev nfl.touchdown build &lt;span class=&quot;token parameter variable&quot;&gt;--phase&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;predict

&lt;span class=&quot;token comment&quot;&gt;# Train models&lt;/span&gt;
modelporter &lt;span class=&quot;token parameter variable&quot;&gt;--env&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;dev nfl.touchdown train &lt;span class=&quot;token parameter variable&quot;&gt;--service&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;vertex

&lt;span class=&quot;token comment&quot;&gt;# Deploy prediction service&lt;/span&gt;
modelporter &lt;span class=&quot;token parameter variable&quot;&gt;--env&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;dev nfl.touchdown serve &lt;span class=&quot;token parameter variable&quot;&gt;--service&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;cloudrun&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The CLI handles:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Environment-specific configuration management&lt;/li&gt;
&lt;li&gt;Container image building and pushing&lt;/li&gt;
&lt;li&gt;Vertex AI training job orchestration&lt;/li&gt;
&lt;li&gt;Cloud Run service updates&lt;/li&gt;
&lt;li&gt;Model artifact management&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 511px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/52d3e05d1e141ac82022f46e60efeed0/40f84/gcp-cr-modelporter-architecture.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 60.8%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAMCAYAAABiDJ37AAAACXBIWXMAAAsSAAALEgHS3X78AAADQUlEQVQoz2WTXUxTZxjH/0WpwfmRQLEwSqGc9pzzHttC6cc5/QDO4ZxDW1BTnVN24y6MiLaLSycby6YgerEtG6jrED8gXkiMQQwOpsHFRJZ94jTbLswywXhhzDKzKxOJddm7tDrmtn/y3LzP8/7zPE9+D/AfXfviK8h7Txh8e87g3ZOXt+375PDFkePdNeLGLjX18bXlM5RCXL/DwIsa/o5/qb1vHlsPzGPTvjls7pkDpfRpQmjAlW9vHvnh+6HM8GCPifPH6nLPoY1JOOojeSOL4M3Xj89mFwNLtBuo77gFwAFgFMU23uDwq2ADmsPCeaQlJo+7uk5RiKiu5fxKPS/pLl7UwryoFVW6ws/amkPfhQXsH1sAqhIX4d40hBcS92Bs/QX732qHp7ENgqStFMJxrrYxyroi0SouoFbwUqyGF1UTL2rla4N6YWdqFx4+uF0A1wLe+4yib+IJ0PLGT1A6p6F3zSktXbdT2pt3K/S9P8NcApSztXgldSg31oruCRo8YgQYZTccHjE/6sDnFP3T9MVDE49794zSZemzfwJ6930UAQXNr31DmlKzsrJrujRDKeSOSTQlthdkKcXQ5PzynZk7AjCMttAquJWXDVLHBYTS1xFM3yyOHbzXcHTsx4J0/2WABFvBS9Eyxkls1Q6TlfUG7STUVkRC62D3NqHYyoD1qybiCxFXpHWltzkBVyQOyxqgYhXAesRKa005sdc3mCtYJyCE28CLqpkE404itTp5qYXlAs1F1poSaFuS0LcmlxpXrzYDWGHh3OZLvz2lgPPL4PxNS3kpWi1ENjC8qFc5fDLABdRCIuk2u1e2kEjUTiTdzovaGl7SDDN3qWHqxiOc/pLK52efjA1cpWUfTD3G4NU/FjnkRH2RRy6gA8r41wbf6wch9mYitb3HTlX2nDayW5JwvpPB+HePDJ9ef4jhmWx4fDZ74v2prLnv3K8YmPz9ObD/MeRFFdA700gc+BDyqzsZ37ak7GDshYSxwCXH4dU2wxmOl5faWCuAMsYTFoRQrNSZX1OuOxXPX0zOHO2HR/BS/wg29H4E79sDuE8pjADsjA+2uobcJ6sQjBF343pC8lCrZi6g/v/knukv88kMEyfLFVcAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;gcp cr modelporter architecture&quot;
        title=&quot;gcp cr modelporter architecture&quot;
        src=&quot;/static/52d3e05d1e141ac82022f46e60efeed0/40f84/gcp-cr-modelporter-architecture.png&quot;
        srcset=&quot;/static/52d3e05d1e141ac82022f46e60efeed0/0eb09/gcp-cr-modelporter-architecture.png 500w,
/static/52d3e05d1e141ac82022f46e60efeed0/40f84/gcp-cr-modelporter-architecture.png 511w&quot;
        sizes=&quot;(max-width: 511px) 100vw, 511px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;h3 id=&quot;Under-the-Hood&quot; style=&quot;position:relative;&quot;&gt;Under the Hood&lt;a href=&quot;#Under-the-Hood&quot; aria-label=&quot;Under the Hood permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;ModelPorter uses several key technologies:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Click&lt;/strong&gt;: For CLI interface and command structure&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Hydra&lt;/strong&gt;: For configuration management&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Cloud Build API&lt;/strong&gt;: For container image building&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Vertex AI API&lt;/strong&gt;: For model training orchestration&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Cloud Run API&lt;/strong&gt;: For deployment management&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Each command in ModelPorter maps to a specific workflow:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Build Command&lt;/strong&gt;: Packages model code into containers&lt;/li&gt;
&lt;/ol&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;py&quot;&gt;&lt;pre class=&quot;language-py&quot;&gt;&lt;code class=&quot;language-py&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;# Key environment variables set during build&lt;/span&gt;
buildargs &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token string&quot;&gt;&quot;env&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; env&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token string&quot;&gt;&quot;project_id&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; cfg&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;project_id
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;ol start=&quot;2&quot;&gt;
&lt;li&gt;&lt;strong&gt;Train Command&lt;/strong&gt;: Manages Vertex AI training jobs&lt;/li&gt;
&lt;/ol&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;py&quot;&gt;&lt;pre class=&quot;language-py&quot;&gt;&lt;code class=&quot;language-py&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;# Vertex AI training configuration&lt;/span&gt;
job &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; aiplatform&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;CustomContainerTrainingJob&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
    display_name&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string-interpolation&quot;&gt;&lt;span class=&quot;token string&quot;&gt;f&quot;&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;display_name&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;-train-job&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    container_uri&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;container_uri&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    model_serving_container_image_uri&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;model_serving_container_image_uri
&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;ol start=&quot;3&quot;&gt;
&lt;li&gt;&lt;strong&gt;Serve Command&lt;/strong&gt;: Updates Cloud Run services&lt;/li&gt;
&lt;/ol&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;py&quot;&gt;&lt;pre class=&quot;language-py&quot;&gt;&lt;code class=&quot;language-py&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;# Cloud Run service configuration&lt;/span&gt;
service &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token string&quot;&gt;&quot;template&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token string&quot;&gt;&quot;containers&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token string&quot;&gt;&quot;image&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; model_serving_container_image_uri&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;token string&quot;&gt;&quot;env&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;
                &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;name&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;AIP_STORAGE_URI&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;value&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; model&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;uri&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
                &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;name&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;AIP_PREDICT_ROUTE&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;value&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;/predict&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;This abstraction allows our team to focus on model development rather than deployment mechanics, while maintaining consistency across environments.&lt;/p&gt;
&lt;h2 id=&quot;Phase-2-Model-Training-and-Deployment&quot; style=&quot;position:relative;&quot;&gt;Phase 2: Model Training and Deployment&lt;a href=&quot;#Phase-2-Model-Training-and-Deployment&quot; aria-label=&quot;Phase 2 Model Training and Deployment permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;h3 id=&quot;Training-on-Vertex-AI&quot; style=&quot;position:relative;&quot;&gt;Training on Vertex AI&lt;a href=&quot;#Training-on-Vertex-AI&quot; aria-label=&quot;Training on Vertex AI permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Using our custom &lt;code class=&quot;language-text&quot;&gt;modelporter&lt;/code&gt; CLI tool:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;sh&quot;&gt;&lt;pre class=&quot;language-sh&quot;&gt;&lt;code class=&quot;language-sh&quot;&gt;modelporter &lt;span class=&quot;token parameter variable&quot;&gt;--env&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;dev nfl.touchdown train &lt;span class=&quot;token parameter variable&quot;&gt;--service&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;vertex&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;This command:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Initiates a training job on Vertex AI&lt;/li&gt;
&lt;li&gt;Saves the trained model to GCS bucket&lt;/li&gt;
&lt;li&gt;Registers the model in Vertex AI registry&lt;/li&gt;
&lt;/ol&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;sh&quot;&gt;&lt;pre class=&quot;language-sh&quot;&gt;&lt;code class=&quot;language-sh&quot;&gt;No existing model found with display name &lt;span class=&quot;token string&quot;&gt;&apos;nfl-touchdown&apos;&lt;/span&gt;&lt;span class=&quot;token builtin class-name&quot;&gt;.&lt;/span&gt; Creating a new model.
⠴ Training model nfl.touchdown is training on vertex&lt;span class=&quot;token punctuation&quot;&gt;..&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;..&lt;/span&gt;Training Output directory:
gs://bkt-xyz-dev-nfl-vertex-0/aiplatform-custom-training-2025-02-28-16:29:02.387
⠼ Training model nfl.touchdown is training on vertex&lt;span class=&quot;token punctuation&quot;&gt;..&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;..&lt;/span&gt;View Training:
https://console.cloud.google.com/ai/platform/locations/australia-southeast1/training/&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;training-job-id&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;?project&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;project-number&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
⠸ Training model nfl.touchdown is training on vertex&lt;span class=&quot;token punctuation&quot;&gt;..&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;..&lt;/span&gt;CustomContainerTrainingJob projects/&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;project-number&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;/locations/australia-southeast1/trainingPipelines/&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;training-job-id&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; current state:
PipelineState.PIPELINE_STATE_RUNNING
View backing custom job:
https://console.cloud.google.com/ai/platform/locations/australia-southeast1/training/&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;id&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;?project&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;project-number&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
⠴ Training model nfl.touchdown is training on vertex&lt;span class=&quot;token punctuation&quot;&gt;..&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;..&lt;/span&gt;CustomContainerTrainingJob projects/&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;project-number&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;/locations/australia-southeast1/trainingPipelines/&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;training-job-id&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; current state:
PipelineState.PIPELINE_STATE_RUNNING
⠙ Training model nfl.touchdown is training on vertex&lt;span class=&quot;token punctuation&quot;&gt;..&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;..&lt;/span&gt;CustomContainerTrainingJob run completed. Resource name: projects/&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;project-number&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;/locations/australia-southeast1/trainingPipelines/&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;training-job-id&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
⠴ Training model nfl.touchdown is training on vertex&lt;span class=&quot;token punctuation&quot;&gt;..&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;..&lt;/span&gt;Model available at projects/&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;project-number&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;/locations/australia-southeast1/models/&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;model-id&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 900px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/6e8c59a201392f328d84833d2b2fa111/e7448/gcp-cr-vertex-training-pipeline.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 28.799999999999997%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAGCAYAAADDl76dAAAACXBIWXMAABJ0AAASdAHeZh94AAAA60lEQVQY021Q2W6DMBDk/3+xDwkkBRvjFnxufDCRtwWlUUdazVo73mO6vh+wLBpKKWj9xRxjRM4ZKWWk/BP5l1vNWsfvhn3f8YquCUKI8N4jhMDcxLVWlFKQS2E+IqV0DmzN3qPbNgNrLTd1zsEYy1zr/mf6+yb/gRterhcYY/C9blBqgXUe1jnESAgxYhwF1+jxwDwrCCGxrhuG2x3jJLAZi9v9E0LO/K+bF49IGUQPaK3ZR/Z00YhEGKYeUs6cCyEwCcnNpZSsISIe1JZpmu7jSphUagufvh3RUPeCWirn7RLvw3neYcNr/gSy3dGgcO+sQgAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;gcp cr vertex training pipeline&quot;
        title=&quot;gcp cr vertex training pipeline&quot;
        src=&quot;/static/6e8c59a201392f328d84833d2b2fa111/e7448/gcp-cr-vertex-training-pipeline.png&quot;
        srcset=&quot;/static/6e8c59a201392f328d84833d2b2fa111/0eb09/gcp-cr-vertex-training-pipeline.png 500w,
/static/6e8c59a201392f328d84833d2b2fa111/e7448/gcp-cr-vertex-training-pipeline.png 900w&quot;
        sizes=&quot;(max-width: 900px) 100vw, 900px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;The Training pipeline results in a registered model in Vertex AI.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 1190px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/6370295df7939bd104a1a5331e680609/ba099/gcp-cr-vertex-training-registered-model.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 76.6%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAPCAIAAABr+ngCAAAACXBIWXMAAAsSAAALEgHS3X78AAABkElEQVQoz52SD8vjIAzG+/0/4tqO22ZbrXa1if9N7YvdcXDcxuAefgiGSJ4kNs5FAFDLsq7aOQcAIUQ0OcQ957LvJedcSjneqfHe50yZyHtPRDEm5xzRvhlSOrmQnbWZaC/lXxo0bhjFOM3DINjAx2kep5kNfOIS0OrNAHoN9i1NDNGHEFOOMcX0F9XRC3pHpkZr7bw7/kuNUguiKeXYq+qEtN4A0HlfWz0jn2g45w82aK1r5hmy1iIgok3p2+NpmsaR51zzXu/XdZVyfs38e+X77e59eLVRSpFK3x9ilkBUvldu2+5+fzDGft1uAIgmSAXWUco70Ud+V2Zs4FyM4yTEHGMKIRiD1lqDBgFNlUU0pmLrDQ2iSSk3jDEhZDV8lNP2oTd4PtcY87mCbz1PE982IKJSDqKyLIvW277vn9b756c3YhZt13d9P8/S+1COY3mu66qtORcGeLo1L9sI9bQubGCtDQ0XvO97NoyISOcobv18uciuV22nLhd1vS59L9tWXq9L26muU/1FcL6BST89Z2Ia7zbcogAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;gcp cr vertex training registered model&quot;
        title=&quot;gcp cr vertex training registered model&quot;
        src=&quot;/static/6370295df7939bd104a1a5331e680609/ba099/gcp-cr-vertex-training-registered-model.png&quot;
        srcset=&quot;/static/6370295df7939bd104a1a5331e680609/0eb09/gcp-cr-vertex-training-registered-model.png 500w,
/static/6370295df7939bd104a1a5331e680609/1263b/gcp-cr-vertex-training-registered-model.png 1000w,
/static/6370295df7939bd104a1a5331e680609/ba099/gcp-cr-vertex-training-registered-model.png 1190w&quot;
        sizes=&quot;(max-width: 1190px) 100vw, 1190px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;As well as the model artifact in storage.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 826px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/a716ca9ee471d7b4af684af42a3c0f2d/e5adc/gcp-cr-vertex-training-registered-model-joblib.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 57.199999999999996%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAALCAIAAADwazoUAAAACXBIWXMAAAsSAAALEgHS3X78AAABqUlEQVQoz1VR2XLbMAzU/3+e47hJ3YntOJIsHhJBggd4gB07bZru7Asw2MEuMMQYhRBE1FqrtTJzrVUIIaVKKYExWq/O4WNMeu/7NwzB+3Ect22z1jnnrLVENI6jVrrVKoWcpjnG6BHfrx/GQO+dmf+ImXvjzp1LKTnnUkrv3Bp/umDm9kDv/cvav81ipfc5JmoeUSltDNRaHeK6roihlGoMWOuYGREB4D8xej/Ns9Jaa1Vr+R6JCtfWaykAQA/gHfd0AOC9H7zH62VUKtxuVki/LOiNzR4LUU4pJWqNwZHFGlNNqRigROWRoA52Wa5v5x+H+fUnXM7mfNrmm58X/3E1ywROKYrJ+bwBOaSYMrhSSss5M/OQ0LnNvh1vx6Ne5X1ntHcmh9EhSKWlVEppLZWSWmuiFGNcFpGIht57imEc5+uEmyH02bk7rSPEHBLNt2Ucp9P5amy0LvoQQ0ghJOf80DtrDb8uePoguWaL1diyQTGuON9CiNu6CiGm2zYuUa2hlExEtdbPP3+dnh/8Wzz6ALDb7Q6Hw/Pz/pP7/f7p6enl5eV0Ov0GmB1z0ksFL5IAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;gcp cr vertex training registered model joblib&quot;
        title=&quot;gcp cr vertex training registered model joblib&quot;
        src=&quot;/static/a716ca9ee471d7b4af684af42a3c0f2d/e5adc/gcp-cr-vertex-training-registered-model-joblib.png&quot;
        srcset=&quot;/static/a716ca9ee471d7b4af684af42a3c0f2d/0eb09/gcp-cr-vertex-training-registered-model-joblib.png 500w,
/static/a716ca9ee471d7b4af684af42a3c0f2d/e5adc/gcp-cr-vertex-training-registered-model-joblib.png 826w&quot;
        sizes=&quot;(max-width: 826px) 100vw, 826px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;h3 id=&quot;Deploying-the-Model-Service&quot; style=&quot;position:relative;&quot;&gt;Deploying the Model Service&lt;a href=&quot;#Deploying-the-Model-Service&quot; aria-label=&quot;Deploying the Model Service permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Once training is complete, we deploy our FastAPI prediction service:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;modelporter &lt;span class=&quot;token parameter variable&quot;&gt;--env&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;dev nfl.touchdown serve&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Behind the scenes, &lt;code class=&quot;language-text&quot;&gt;modelporter&lt;/code&gt;:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Locates the latest trained model in Vertex AI&lt;/li&gt;
&lt;li&gt;Updates the Cloud Run service with our prediction container&lt;/li&gt;
&lt;li&gt;Configures environment variables to point to the model in GCS&lt;/li&gt;
&lt;/ol&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;log&quot;&gt;&lt;pre class=&quot;language-log&quot;&gt;&lt;code class=&quot;language-log&quot;&gt;Waiting for operation to complete&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;
Cloud Run service updated&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt; URL&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token url&quot;&gt;https://nfl-touchdown-&amp;lt;&gt;.a.run.app&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 852px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/3aeaf5932bdf697b697639eb2e3f13ae/1dbb4/gcp-cr-cloudrun-revisions.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 67.19999999999999%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAANCAIAAAAmMtkJAAAACXBIWXMAAAsSAAALEgHS3X78AAAB2UlEQVQoz4VS267jIAzM/3/geegetU2zaRrA3AyYO6skq9V529EIzDx47BHT7XbT2jDGORe995hHjGnf952x12uJMY0xlC2gi1BFmqKxgi6l9DHGFGIopQQfiCIRhUC1tlJqziXnXEotpbT2VzmfB2s99GlmDwwoQXIutm17v9eUMqLVSgEIpRQIEWOiSIjWOYeI16mUmlJMKaVjYIpjjHjWFJsLrfcRDlBrPYTgvL/8vQ+1tmNsrY0xJqXsfKBYLXofyg55l5lS994771trznlEl0s553LXIpO19vPZEbH1IxhtqzIlpqNx70cqP4ufiDFNiA5AEpExxjs3/odjqSPXIKWajHW32/dzXriQ6PzJgC64QIyJdd2UNvfH8/e6WXQ748/X8vnsr2Vdt336erivx/xY2H3m8wL3eV82JXSaV3i82PM3MAi/7tv3Y3sz/HD3ZshVXHf7EW6SyhAlY1BIDaAZE9pgbaOd9J5SrkpbxND6KfZRSiNKRHkyxhAFpTXnQgAwzqVUtdbee2/deZ9zEQKMMf1C67XWlNIRmLUYY9LacAFcAGNcKf0vHh+O/wcgAWSt9RJb6zFGojQBSGOMlOrwBRDHJZ3zF7Ux1uKpg7V4iZdfSvkPdNroNd2MBSYAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;gcp cr cloudrun revisions&quot;
        title=&quot;gcp cr cloudrun revisions&quot;
        src=&quot;/static/3aeaf5932bdf697b697639eb2e3f13ae/1dbb4/gcp-cr-cloudrun-revisions.png&quot;
        srcset=&quot;/static/3aeaf5932bdf697b697639eb2e3f13ae/0eb09/gcp-cr-cloudrun-revisions.png 500w,
/static/3aeaf5932bdf697b697639eb2e3f13ae/1dbb4/gcp-cr-cloudrun-revisions.png 852w&quot;
        sizes=&quot;(max-width: 852px) 100vw, 852px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;Once this has finished, if we browse to the Cloud Run service in the web console, and look at the Revisions, we can see a new revision taking 100% of the traffic.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;py&quot;&gt;&lt;pre class=&quot;language-py&quot;&gt;&lt;code class=&quot;language-py&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;# Key environment variables set during deployment&lt;/span&gt;
env &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;name&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;AIP_STORAGE_URI&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;value&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; model&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;uri&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;name&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;AIP_PREDICT_ROUTE&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;value&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;/predict&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;name&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;PROJECT_ID&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;value&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; project_id&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2 id=&quot;Testing-the-Deployment&quot; style=&quot;position:relative;&quot;&gt;Testing the Deployment&lt;a href=&quot;#Testing-the-Deployment&quot; aria-label=&quot;Testing the Deployment permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;You can verify the deployment first getting an identity token to authenticate to the Cloud Run API:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;sh&quot;&gt;&lt;pre class=&quot;language-sh&quot;&gt;&lt;code class=&quot;language-sh&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;# Get an identity token to pass as a bearer&lt;/span&gt;
gcloud auth print-identity-token&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;I like to use Thunder Client (The VSCode extension to test constructing API calls).&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 516px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/7d7ba3e3155c4e6d10707024a43fde2a/fbb32/gcp-cr-cloudrun-predict-thunder-client.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 170.6%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAiCAIAAADQyG7qAAAACXBIWXMAAAsSAAALEgHS3X78AAAEPElEQVRIx4VV227bRhDVr6RxIy73fr+Jd4qSLMmxHadOA/Sh7VPSl/5DgSJ/UrTP/b8Uu5TtOE5SYECsRB7OmZkzh4u6OU7b17vj68Pl2+3xdne83V+82R1+mHavxu2rJRTPzsB338NTnIElbalqKLdnS7JwYdP0x2F7Oe1vNvvX692rcXO13l4P08uq2xdQPH+Bzpb4IYAEkBSgePZ8uSiQLKBYlqd4AfgSnM4FlCXWn4QBSMhw6dfvwvSbjNcLSEysJ+tbRI0NfdNtYzWuminW67bf+tjX3bYfz5tu240H5/zb3//946+PH/75ePP+7wVAClGLqC2xRtRSETDzmDnCPOE+3SIGM0u4T/9TFcZfN7d/bm4/uP7nRYm1i4OydQGldq2PvQkDJMb4VtoWc3+4+rEejgBpF3vMHIBMKiuVAZAtHlelMXMQa4AVpq6EErFgqkumxwJKSAzMlUPiEPOQ2ES7G86rdsoUeoAUVysmQ0m0dg1MrzNCr4SutGsJs4Q5Ezpt060ENmEtTV9ASXkASGHmMHMl1lSE3AiXC3ZMxpScGCoD4emBRNuE3rhWmsqGTqi4LAVACiBVQDlfHwIpABWTUdkaEpPAQldcRa5W2jWEWZAwCVZiDVBqxJwQEjP3hauVsk0CA5xeNj+EuUNyRUxNbUtMjXlA1MwsPo1lKSBJwzt1GxKbb2jhex4GaltqakQtpqYoRYFUkbkApOdCiAhUxEfgPA+NZeRxxDIy11HbUdsiYgn2BZJLKE5g/gQ8U6KuC+c3otqoZqf7g6g2ujvoekNFdCpN9AmY2vsOY12Z4ShWEwuD7vd8NTh/3q6uIDGMVZCYAorPMpsHsFqZ8UJWGzNe6G6vux3XrVUDImae4imzfEK7gIKaxoxH2Wy57bioIXOA6HlOdw2XkFhM3SPaWWQeJXmZvL2qTKqQT6c1qyDJExKDmC+goiLtYBLWHF/CfA5G1CGa9MyEB1hBnMVEdIq7jqQDeVi+JAGWaRd5+mUOiE+1lcikwPel6vnnvVSzPFHSKhMRCYOVhjxtFVMBCY2kLlnyGS4jFgZJBekj5nmfx8N6eilrJweDndCmGaYDX2m11jRKKvw4HW1dsVrRqO9XLYFLpCh3hM8yVii5hIXUAWQQcZi6AiUPxdQBmIqa3Q5mY1sg6o9Xv1y/ebe7+AlRp10T67WwDaLOxcGvekiMdm2s15gnPzCuI9wzGaWpF0keJGJR46QHX1ILiCnpnFzN1p1s4FOHwPNBLTIHXUBOTa3qraw3PAy8mohpuAzfHvXiTpupAcm0qEMilMQg7iHziPv/Bd8pESm7funPb2S9dZtr1e9VGJ1aS1HPm/zVzEX2zeQhrk/XbCaEeCV6SmPxJbWeMqdNZMlik+nkT1wB8yF/Bouv6DxvVVoMR4XPndRg1mO2zlyO/mrN+Ykk9G839ovg/wDqh5mljzYiXAAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;gcp cr cloudrun predict thunder client&quot;
        title=&quot;gcp cr cloudrun predict thunder client&quot;
        src=&quot;/static/7d7ba3e3155c4e6d10707024a43fde2a/fbb32/gcp-cr-cloudrun-predict-thunder-client.png&quot;
        srcset=&quot;/static/7d7ba3e3155c4e6d10707024a43fde2a/0eb09/gcp-cr-cloudrun-predict-thunder-client.png 500w,
/static/7d7ba3e3155c4e6d10707024a43fde2a/fbb32/gcp-cr-cloudrun-predict-thunder-client.png 516w&quot;
        sizes=&quot;(max-width: 516px) 100vw, 516px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;Thunder Client can also help you generate the programmatic calls in many languages.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;sh&quot;&gt;&lt;pre class=&quot;language-sh&quot;&gt;&lt;code class=&quot;language-sh&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;# Make a prediction&lt;/span&gt;
&lt;span class=&quot;token function&quot;&gt;curl&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-X&lt;/span&gt; POST &lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;
  &lt;span class=&quot;token string&quot;&gt;&apos;https://nfl-touchdown-&amp;lt;project-id&gt;.&amp;lt;region&gt;.run.app/predict&apos;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;
  &lt;span class=&quot;token parameter variable&quot;&gt;--header&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;Accept: */*&apos;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;
  &lt;span class=&quot;token parameter variable&quot;&gt;--header&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;User-Agent: Thunder Client (https://www.thunderclient.com)&apos;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;
  &lt;span class=&quot;token parameter variable&quot;&gt;--header&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;Authorization: Bearer &amp;lt;identity-token&gt;&apos;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;
  &lt;span class=&quot;token parameter variable&quot;&gt;--header&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;Content-Type: application/json&apos;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;
  --data-raw &lt;span class=&quot;token string&quot;&gt;&apos;{&quot;instances&quot;:[[23.0, 150.0, 1.0, 20.0]]}&apos;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Inspecting the Google Cloud Logs Explorer we can see the service was started and called successfully, confirming the response we got in Thunder Client was indeed returned from the Cloud Run service.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 990px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/faf665481bae414e3f65856c85ab07d0/d28e0/gcp-cr-cloudrun-predict-thunder-client-logs.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 30.599999999999998%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAGCAIAAABM9SnKAAAACXBIWXMAAAsSAAALEgHS3X78AAAA7ElEQVQY002QS07GMAyEe/9zsQHpFxdgiUST2LEdu3EeLaAUgfhW1owsj2fzdrIYpISICaBWn3PmTJmImPM9qep1XV//+LzZvJ1qlYhEhIjdfZ6nlMI/LHFZ7ZfevXe/rnMt1zZJNIQQUwoxMrOZxRUkI2ZIESCJSEoJAAGQ2ItOLTbG2Po47aj3jcIstVZVW4lZAPNHLBENMYcYicVMTR5uj+OA1sZWfRKVfd9DjHsIzBLiAnH9i1kTckwEaCyFKHN+JXgGeGfRrfXTrIpIUWXm3tsYo5Ti7r231nyMPupLP55ao9GP0d7+avsG9NhUa8KmxUMAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;gcp cr cloudrun predict thunder client logs&quot;
        title=&quot;gcp cr cloudrun predict thunder client logs&quot;
        src=&quot;/static/faf665481bae414e3f65856c85ab07d0/d28e0/gcp-cr-cloudrun-predict-thunder-client-logs.png&quot;
        srcset=&quot;/static/faf665481bae414e3f65856c85ab07d0/0eb09/gcp-cr-cloudrun-predict-thunder-client-logs.png 500w,
/static/faf665481bae414e3f65856c85ab07d0/d28e0/gcp-cr-cloudrun-predict-thunder-client-logs.png 990w&quot;
        sizes=&quot;(max-width: 990px) 100vw, 990px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;h2 id=&quot;Infrastructure-as-Code&quot; style=&quot;position:relative;&quot;&gt;Infrastructure as Code&lt;a href=&quot;#Infrastructure-as-Code&quot; aria-label=&quot;Infrastructure as Code permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;All base Cloud infrastructure components are managed via Terraform:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Cloud Run service configuration&lt;/li&gt;
&lt;li&gt;IAM permissions&lt;/li&gt;
&lt;li&gt;BigQuery connections&lt;/li&gt;
&lt;li&gt;Storage buckets&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This ensures consistent deployments across environments and makes it easy to replicate the setup.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;sh&quot;&gt;&lt;pre class=&quot;language-sh&quot;&gt;&lt;code class=&quot;language-sh&quot;&gt;terraform init -backend-config&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;vars/backend-dev.hcl&quot;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;log&quot;&gt;&lt;pre class=&quot;language-log&quot;&gt;&lt;code class=&quot;language-log&quot;&gt;Initializing the backend&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;
Initializing provider plugins&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;
&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt; Reusing previous version of hashicorp&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;google from the dependency lock file
&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt; Reusing previous version of hashicorp&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;random from the dependency lock file
&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt; Using previously&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;installed hashicorp&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;google &lt;span class=&quot;token number&quot;&gt;v4.51.0&lt;/span&gt;
&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt; Using previously&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;installed hashicorp&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;random &lt;span class=&quot;token number&quot;&gt;v3.7.1&lt;/span&gt;

Terraform has been successfully initialized&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;

You may now begin working with Terraform&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt; Try running &lt;span class=&quot;token string&quot;&gt;&quot;terraform plan&quot;&lt;/span&gt; to see
any changes that are required for your infrastructure&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt; All Terraform commands
should now work&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;

If you ever set or change modules or backend configuration for Terraform&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
rerun this command to reinitialize your working directory&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt; If you forget&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; other
commands will detect it and remind you to do so if necessary&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The complete solution demonstrates a production-ready ML serving infrastructure that’s both scalable and maintainable.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Checking the public IP address of a GCP Cloud Function]]></title><description><![CDATA[Public IP address of a GCP Cloud Function When you deploy a Cloud Function without configuring a VPC, it operates within Google’s internal…]]></description><link>https://mortimer.xyz/gcp-function-ip-checker-terraform/</link><guid isPermaLink="false">https://mortimer.xyz/gcp-function-ip-checker-terraform/</guid><pubDate>Mon, 04 Nov 2024 12:00:00 GMT</pubDate><content:encoded>&lt;h1 id=&quot;Public-IP-address-of-a-GCP-Cloud-Function&quot; style=&quot;position:relative;&quot;&gt;Public IP address of a GCP Cloud Function&lt;a href=&quot;#Public-IP-address-of-a-GCP-Cloud-Function&quot; aria-label=&quot;Public IP address of a GCP Cloud Function permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h1&gt;
&lt;p&gt;When you deploy a Cloud Function without configuring a VPC, it operates within Google’s internal network. This means it leverages Google’s infrastructure to handle incoming and outgoing traffic.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;When your function needs to make outbound requests to external services (e.g., HTTP requests), it will use Google’s internal network to reach those services.&lt;/li&gt;
&lt;li&gt;The specific IP addresses used for outbound traffic will change over time.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This blog post details how to check this dynamic IP address (and log it to a database).&lt;/p&gt;
&lt;p&gt;The source code can be found in the following repository &lt;a href=&quot;https://github.com/mortie23/ml/tree/master/eng/misc/fn-misc-ipchecker&quot;&gt;https://github.com/mortie23/ml/tree/master/eng/misc/fn-misc-ipchecker&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&quot;Directory-structure&quot; style=&quot;position:relative;&quot;&gt;Directory structure&lt;a href=&quot;#Directory-structure&quot; aria-label=&quot;Directory structure permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;We will start with the standard directory structure for this GCP Cloud Functions following on from the previous post of developing a GCP Cloud Function.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;📁 fn-misc-ipchecker/
├── 📁 src/
│   │   📄 __init__.py
│   │   📄 misc.yaml
│   │   📄 main.py
│   │   📄 requirements.txt
├── 📁 tests/
│   │   📄 test_fn-misc-ipchecker.py
│   📄 deploy.sh
│   📄 poetry.lock
│   📄 pyproject.toml
│   📄 pytest.ini
📄 .env&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2 id=&quot;How-it-works&quot; style=&quot;position:relative;&quot;&gt;How it works&lt;a href=&quot;#How-it-works&quot; aria-label=&quot;How it works permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The configuration file defines the BigQuery database (project), schema (dataset) and table where to log the IP addresses and the base URL of the service that returns the IP address that made the HTTP request.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;yml&quot;&gt;&lt;pre class=&quot;language-yml&quot;&gt;&lt;code class=&quot;language-yml&quot;&gt;&lt;span class=&quot;token key atrule&quot;&gt;project_id&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;prj-xyz-&amp;lt;env&gt;-misc-ip-1&apos;&lt;/span&gt;
&lt;span class=&quot;token key atrule&quot;&gt;dataset_name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;demo&apos;&lt;/span&gt;
&lt;span class=&quot;token key atrule&quot;&gt;table_name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;ipchecker&apos;&lt;/span&gt;
&lt;span class=&quot;token key atrule&quot;&gt;base_url&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;https://api64.ipify.org/?format=json&apos;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The Python function simple makes the HTTP request and consumes and extracts the IP address that is returned.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;py&quot;&gt;&lt;pre class=&quot;language-py&quot;&gt;&lt;code class=&quot;language-py&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;ip_checker&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token triple-quoted-string string&quot;&gt;&quot;&quot;&quot;Call a service that returns the calling network IP address

    Returns:
        str: The IP Address
    &quot;&quot;&quot;&lt;/span&gt;
    data &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; requests&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;get&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;cfg&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;base_url&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    ip_address &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; json&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;loads&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;data&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;content&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;decode&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;utf-8&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;ip&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; ip_address&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;Running-the-source-code-in-interactive&quot; style=&quot;position:relative;&quot;&gt;Running the source code in interactive&lt;a href=&quot;#Running-the-source-code-in-interactive&quot; aria-label=&quot;Running the source code in interactive permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;As a test, running the source code &lt;code class=&quot;language-text&quot;&gt;main.py&lt;/code&gt; script from the developer machine is a good way to have a comparison IP address logged to the BigQuery table.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 573px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/1f065053a87a1c254f2e59ce68ebafd3/f5c00/ipchecker-interactive.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 83.2%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAARCAIAAABSJhvpAAAACXBIWXMAAAsSAAALEgHS3X78AAAB/UlEQVQ4y4WSiWocMQyG5zGS7JyWD1k+5rDHc26SkraE9P3fp+wO2RZCEvgQRvavX1jKhuD3fd72FKLnon443eXFw0FRnsoqL6u8eM/kxf3pdGcdeWfrush8q8fUxbH1HY1T3/cuTWGew5h6Y6WQDRdViG0IPsQuTUNM3bqlNPbD4DJNgBoQL9E6Zay0TjmP1qMmoZAdeeukdej8BXN9ZqzMpGrSNJwfl32f0tTPS1jXcdunZR3387xu4zQPY+qkaj6SKWSauLFSm2skTkYchckIMkIT18QVso9cxMpAIDVLFaM/P83GSqma4/p2+FysgRQzChC5MQoRLsmr7PIdn3O0zewUXDJhYXFnaaO+tz51rrcKv3NGZNoIbUAbMA6MFRoFkkItULNvncGmwY0mrqyN0M8Qd5ieIc6K3qd14zNnThYOf22AHKDlErlUlz0Rsj7i/7X+OftEYWFdgrDAuLMwyS2IbahDMM8/tqen5eXn47LGtqPD9l2sgRxqK8g15MC23HbcetE67i1YK9uO2o76wfpWGysPW008Qw3Aa85r1JwISTOAsmmqhlX1lYaVdVPcaFjJoGJQcVFnziODSpFMUxhi33leVfd5mRdfkhd53ZSZc6iQWYdtZxBBk9IkhGy+5bLbZISQ9bant7dfr68vb39+7+f56628jeovhDKijpNf8YkAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;ipchecker interactive&quot;
        title=&quot;ipchecker interactive&quot;
        src=&quot;/static/1f065053a87a1c254f2e59ce68ebafd3/f5c00/ipchecker-interactive.png&quot;
        srcset=&quot;/static/1f065053a87a1c254f2e59ce68ebafd3/0eb09/ipchecker-interactive.png 500w,
/static/1f065053a87a1c254f2e59ce68ebafd3/f5c00/ipchecker-interactive.png 573w&quot;
        sizes=&quot;(max-width: 573px) 100vw, 573px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;h2 id=&quot;Development-and-Deployment&quot; style=&quot;position:relative;&quot;&gt;Development and Deployment&lt;a href=&quot;#Development-and-Deployment&quot; aria-label=&quot;Development and Deployment permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Given this function is developed and deployed in the same way as a previous blog post, to avoid duplication, I will just reference it here: &lt;a href=&quot;https://mortimer.xyz/gcp-function-afl/#Development-process-and-tooling&quot;&gt;GCP Cloud Function, Development process and tooling&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;However, this time I will outline the process to deploy the infrastructure using the GCP Terraform provider as opposed to the Gcloud SDK CLI.&lt;/p&gt;
&lt;h2 id=&quot;Infrastructure-as-code&quot; style=&quot;position:relative;&quot;&gt;Infrastructure as code&lt;a href=&quot;#Infrastructure-as-code&quot; aria-label=&quot;Infrastructure as code permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;To ensure that all required infrastructure is deployed, we will use a Infrastructure as Code (IaC) solution, namely Terraform.
The below example is from the &lt;code class=&quot;language-text&quot;&gt;main.tf&lt;/code&gt; file and shows how the Google Terraform provider is required and how the Google provider is authenticated with the Application Default Credentials (ADC).&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;hcl&quot;&gt;&lt;pre class=&quot;language-hcl&quot;&gt;&lt;code class=&quot;language-hcl&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;terraform&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;required_providers&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;google&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token property&quot;&gt;source&lt;/span&gt;  &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;hashicorp/google&quot;&lt;/span&gt;
      &lt;span class=&quot;token property&quot;&gt;version&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;4.51.0&quot;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;provider&lt;span class=&quot;token type variable&quot;&gt; &quot;google&quot; &lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;credentials&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; file(&lt;span class=&quot;token string&quot;&gt;&quot;~/.config/gcloud/application_default_credentials.json&quot;&lt;/span&gt;)
  &lt;span class=&quot;token property&quot;&gt;region&lt;/span&gt;      &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; var.region
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;resource &lt;span class=&quot;token type variable&quot;&gt;&quot;google_project&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;misc_ip_1&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;name&lt;/span&gt;            &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;prj-xyz-&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token type variable&quot;&gt;env&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;-misc-ip-1&quot;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;project_id&lt;/span&gt;      &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;prj-xyz-&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token type variable&quot;&gt;env&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;-misc-ip-1&quot;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;billing_account&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; var.billing_account
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;Requirements&quot; style=&quot;position:relative;&quot;&gt;Requirements&lt;a href=&quot;#Requirements&quot; aria-label=&quot;Requirements permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;I am using a WSL2 installation of the Ubuntu 20.04 distro. I have previously installed the following requirements on the machine.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Terraform &lt;a href=&quot;https://developer.hashicorp.com/terraform/tutorials/aws-get-started/install-cli&quot;&gt;https://developer.hashicorp.com/terraform/tutorials/aws-get-started/install-cli&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Gcloud SDK &lt;a href=&quot;https://cloud.google.com/sdk/docs/install#deb&quot;&gt;https://cloud.google.com/sdk/docs/install#deb&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The GCP Terraform provider uses an Application Default Credential (ADC) to authenticate to GCP as we mentioned before. Use the following to create yours.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;sh&quot;&gt;&lt;pre class=&quot;language-sh&quot;&gt;&lt;code class=&quot;language-sh&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;# Create an ADC JSON file&lt;/span&gt;
gcloud auth application-default login&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;Initialising-the-backend&quot; style=&quot;position:relative;&quot;&gt;Initialising the backend&lt;a href=&quot;#Initialising-the-backend&quot; aria-label=&quot;Initialising the backend permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;The next step is to re-initialise the backend.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;re-initialises and configures a Terraform working directory&lt;/li&gt;
&lt;li&gt;identifies the providers and modules used in your configuration files and downloads them&lt;/li&gt;
&lt;li&gt;loads up variables used in the Terraform configuration for managing different environments&lt;/li&gt;
&lt;/ul&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;sh&quot;&gt;&lt;pre class=&quot;language-sh&quot;&gt;&lt;code class=&quot;language-sh&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;# Change to the Terraform working directory&lt;/span&gt;
&lt;span class=&quot;token builtin class-name&quot;&gt;cd&lt;/span&gt; infra/tf/
&lt;span class=&quot;token comment&quot;&gt;# Initialise&lt;/span&gt;
terraform init &lt;span class=&quot;token parameter variable&quot;&gt;-reconfigure&lt;/span&gt; -var-file&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;env-dev.tfvars&quot;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;Plan-and-Apply&quot; style=&quot;position:relative;&quot;&gt;Plan and Apply&lt;a href=&quot;#Plan-and-Apply&quot; aria-label=&quot;Plan and Apply permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;First we run &lt;code class=&quot;language-text&quot;&gt;plan&lt;/code&gt; which generates a detailed execution plan. The plan let’s us know the changes that Terraform will make to the cloud infrastructure before we apply them, including:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;creating (add) new resources,&lt;/li&gt;
&lt;li&gt;modifying (change) existing ones, or&lt;/li&gt;
&lt;li&gt;deleting (destroy) unnecessary resources&lt;/li&gt;
&lt;/ul&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;sh&quot;&gt;&lt;pre class=&quot;language-sh&quot;&gt;&lt;code class=&quot;language-sh&quot;&gt;terraform plan -var-file&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;env-dev.tfvars&quot;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Example output:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;hcl&quot;&gt;&lt;pre class=&quot;language-hcl&quot;&gt;&lt;code class=&quot;language-hcl&quot;&gt;google_project.misc_ip_1: Refreshing state... &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token property&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;projects/prj-xyz-dev-misc-ip-&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
...

Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols:
-/+ destroy and then create replacement

Terraform will perform the following actions:

  &lt;span class=&quot;token comment&quot;&gt;# google_project.misc_ip_1&lt;/span&gt;
-/+ &lt;span class=&quot;token keyword&quot;&gt;resource &lt;span class=&quot;token type variable&quot;&gt;&quot;google_project&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;misc_ip_1&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      ~ &lt;span class=&quot;token property&quot;&gt;id&lt;/span&gt;                  &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;projects/prj-xyz-dev-misc-ip-1&quot;&lt;/span&gt; -&gt; (known after apply)
      - &lt;span class=&quot;token property&quot;&gt;labels&lt;/span&gt;              &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; -&gt; null
      ~ &lt;span class=&quot;token property&quot;&gt;name&lt;/span&gt;                &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;prj-xyz-dev-misc-ip-1&quot;&lt;/span&gt;
      ~ &lt;span class=&quot;token property&quot;&gt;number&lt;/span&gt;              &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;
      ~ &lt;span class=&quot;token property&quot;&gt;project_id&lt;/span&gt;          &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;prj-xyz-dev-misc-ip-1&quot;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
...

Plan: &lt;span class=&quot;token number&quot;&gt;17&lt;/span&gt; to add, &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt; to change, &lt;span class=&quot;token number&quot;&gt;17&lt;/span&gt; to destroy.&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The above plan let’s us know that 17 resources will be added, and what they are.
Assuming we are happy with the plan, we will continue with the &lt;code class=&quot;language-text&quot;&gt;apply&lt;/code&gt; step, which creates all the infra defined in the configurations.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;sh&quot;&gt;&lt;pre class=&quot;language-sh&quot;&gt;&lt;code class=&quot;language-sh&quot;&gt;terraform apply -auto-approve -var-file&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;env-dev.tfvars&quot;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Once a successful apply has finished, you can log into the Cloud console and check that the resources, such as the new project and/or new BigQuery dataset have been created.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 447px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/e8182c7c35acc674b6af4623645e27fb/74a2b/ipchecker-bigquery-created-resources.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 66.8903803131991%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAANCAIAAAAmMtkJAAAACXBIWXMAAAsSAAALEgHS3X78AAABt0lEQVQoz22SW3ObMBCF+f8/q+lbJzNJJqY2cWwDFgiELgwS0q4utNhNm9r5Ri8a6Wj3nFWGiLRttFsEFZzRvu36ng2D8N4DACGko7Sq67qqzoTUdd31TCpl5nlZliyEYIwGvzhtjNZ2tlprpcYQogMQQkgp18eEkEoJwbWZ7IpbxZzz4/G4LAuXom0b0jTOrQfpg+UT4JMcvbHeQdQGM6XUZrNJKY1KHd73h8NRG3MVf37isk3WBS5hth59nK3PrpdCCN77EGNMCRDXtgDuFyCsUSCY2YTgs7iyqqZptcov9D3jQgzDwBjTWv9t+2rCOvfw8B0Rs7qDYQzWRcmHtiUD6xmjjFFE6xGcW+vd45zz3me7GsgQZ5f4aF733f7Q7fa0eO/oYGf4L60bYoyZdajGMYYw1ZTmFX0qym+P5HFb/8jF7pjA3ctSSjHGtbJH1NbqXTFVJXnbluWBtOeyOhF6niidXl7uxcNl7FrrDAFGreeimAkB9PnrZpv/5AP3Us6nky3Lf0F9wDn/Iw4hOIAUgiXEFAV7euqfn1WeQ9OglF+6RUTv/Zo2Iiqlbn7ScjOcr/jt+ReIgOil87WoagAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;ipchecker bigquery created resources&quot;
        title=&quot;ipchecker bigquery created resources&quot;
        src=&quot;/static/e8182c7c35acc674b6af4623645e27fb/74a2b/ipchecker-bigquery-created-resources.png&quot;
        srcset=&quot;/static/e8182c7c35acc674b6af4623645e27fb/74a2b/ipchecker-bigquery-created-resources.png 447w&quot;
        sizes=&quot;(max-width: 447px) 100vw, 447px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;h2 id=&quot;Test-calling-the-function&quot; style=&quot;position:relative;&quot;&gt;Test calling the function&lt;a href=&quot;#Test-calling-the-function&quot; aria-label=&quot;Test calling the function permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Once you have deployed the required infrastructure and the Cloud Function it is now time to run it.
To call the Cloud Function we can use a basic curl command. Ensure you are logged in as a user that has invoke permissions.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;sh&quot;&gt;&lt;pre class=&quot;language-sh&quot;&gt;&lt;code class=&quot;language-sh&quot;&gt;&lt;span class=&quot;token function&quot;&gt;curl&lt;/span&gt; https://australia-southeast1-prj-xyz-dev-misc-ip-1.cloudfunctions.net/fn-misc-ipchecker-0 &lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;
  &lt;span class=&quot;token parameter variable&quot;&gt;-H&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Authorization: Bearer &lt;span class=&quot;token variable&quot;&gt;&lt;span class=&quot;token variable&quot;&gt;$(&lt;/span&gt;gcloud auth print-identity-token&lt;span class=&quot;token variable&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&quot;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;If you check the target table, it should include an extra row with the public IP address that was used to make the external HTTP request.
The first record is from the successful run of the source code from the development machine, which will log the public IP address of the development machines network.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 462px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/7a457e2ac2e3e6540538da803609f218/a9e02/ipchecker-bigquery-ipaddresses.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 45.45454545454545%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAJCAIAAAC9o5sfAAAACXBIWXMAAAsSAAALEgHS3X78AAABbUlEQVQoz2WR0Y7jIAxF8/+/tw+jHbVpEtKkhBIgwYANht1mpH3ZIz9cybKvrdsBQAjhPM8YEwSMiVLKhJhShIDWBUwREdNV//jR3XnhnDk83QX1It5F2F00xjxl/PVtCCETMWcumYhKIS75ktRprb33ztn1pab5vbt0QjEur2/cHcZUaq0x5d3icVJh9pDdQRAzM3dfX1/ruozj8Hj09/sNEVtrudRNp3WLIZbWmjlyL4LUibkqQ9MST8it1Y6Zj8MZY4iotRZCUO+393Ct4N2htsi1tv+otXaIaPZdqbcH4FK891prAJ9zVXt6zDA8QVtEyjmXkgtz4VqZ6+fsbdu03olIytf9dpPbVmstJYeQ3BE9xN3Y39+PYRiH4dH3vbU2XQDAxzleUUkpJzEJIay1/KG0+vGIEWYxbUqJSUzTJOa57/tlWVNKn+GfqOXrNV7teZ6X59M5dz3WUozTOCml5GtdLsZhkHL7G9UfAwz/tlQR1t0AAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;ipchecker bigquery ipaddresses&quot;
        title=&quot;ipchecker bigquery ipaddresses&quot;
        src=&quot;/static/7a457e2ac2e3e6540538da803609f218/a9e02/ipchecker-bigquery-ipaddresses.png&quot;
        srcset=&quot;/static/7a457e2ac2e3e6540538da803609f218/a9e02/ipchecker-bigquery-ipaddresses.png 462w&quot;
        sizes=&quot;(max-width: 462px) 100vw, 462px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;h2 id=&quot;Where-does-the-IP-address-geo-locate-to&quot; style=&quot;position:relative;&quot;&gt;Where does the IP address geo locate to&lt;a href=&quot;#Where-does-the-IP-address-geo-locate-to&quot; aria-label=&quot;Where does the IP address geo locate to permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;This result could be obvious, but I found it interesting none the less.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 856px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/d8fb495849caa45c0a3b6f48d57b192d/1712d/ipchecker-geolocation.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 97.4%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAATCAIAAAAf7rriAAAACXBIWXMAAAsSAAALEgHS3X78AAADN0lEQVQ4y2VTaW/bRhTU//8N/dC0QIvUaRKnAWykPhOJOkjxWHK5JylKdqskkkxatLgXSbWUmvQavA/73mIws8f0pJRCVFVVSSmlUlJIIaQ2pm7bxjRdNU1d101d75uu2sOiPQx7Wqv5IqEpIBnkBC4Scvd7tkiTWYrCDcFFhOaYZRlbLifpfJjO3Wxu8Xtyf6eN7glZRqyPMEQJ4nAQOx8iFLC5n7g3MLvCq8AngQu8YOFek/C1j18G+DuXvgZQyqpXqV1/9jPN3t+ldJ5ihgGOhhCPZ8lo5r+fBdOUMT8ElJNshlBk+dHwmg6HwNNK9YQox8nLq8UPAJ+msY8YpNim3gCGVsz9FPRTbxRH0wg4zHN46KVxwIIxQpFSqreTO5u8sfHpBL7A6YCEIxBNk9jGxILjS2ydEzRidMKhlbgjDqYMurE3gRRrrXpCysv0Fyt4C/2rmIYYIeo7MA5C4IbORWjdxMPbeGqlaJBFQ8IDztxFYCUo6mzvRPkrO3GyAcAuAz5PZzgMUDAiDJIkTJidTvqMhgxNOByk+DaDH+bTixkcail7T9X2bfjMQtdTPIbQTjzAYZwgG4UhojFOPer5FHkQ9BEc0NkYIStyLRD7SslO+TT4/s30ZIRvAJ9yRjiwKfBY5JA4IkkU+x6mALGI4X6aTDoXwKUcdeQnUZ5Mv33ufHPm/fjOe3UZnNloQjwPR2MeTTgeY+Bx5NAZIMQh3MZoyJwBo4GSnfL2Fp5fwfOz6MU78NNN/KoPL2KGYj8A9oQAHweAU8QSwgikCaacMBTwjHRPVde1VPILlJJKKFGJ6iHPP69Wm80mz3Nx3PtSR3R/uyMLqZUyuouD1lpJpZTS/4bS/x3+RV5v8i5IdSPK3X6/3263RVHs/4fHx0elZNu2x7Zt257WZr3JV6uHh/yxzB9rY8qy3KzXxpivIsaYTmO93qw3x9aYLqs9peTnT5+KongqS30g5Hm+XH6s67/JB8Nm2eGjMeZwN+qQZ6WKPD96aZrmaPv+/rfNQ6F0LaSWyhzm7Wq1KvKi/Qe6M5dlqZQ69vv9Xgix3Zblk9hV6mknd5Wqm6Zt26qq6rr+yvzT9h8/hfg/TiXPQAAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;ipchecker geolocation&quot;
        title=&quot;ipchecker geolocation&quot;
        src=&quot;/static/d8fb495849caa45c0a3b6f48d57b192d/1712d/ipchecker-geolocation.png&quot;
        srcset=&quot;/static/d8fb495849caa45c0a3b6f48d57b192d/0eb09/ipchecker-geolocation.png 500w,
/static/d8fb495849caa45c0a3b6f48d57b192d/1712d/ipchecker-geolocation.png 856w&quot;
        sizes=&quot;(max-width: 856px) 100vw, 856px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;h2 id=&quot;Testing&quot; style=&quot;position:relative;&quot;&gt;Testing&lt;a href=&quot;#Testing&quot; aria-label=&quot;Testing permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;To run the unit tests during development we can run this:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;sh&quot;&gt;&lt;pre class=&quot;language-sh&quot;&gt;&lt;code class=&quot;language-sh&quot;&gt;pytest ./tests/test_fn-misc-ipchecker.py::test_ip_checker&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;log&quot;&gt;&lt;pre class=&quot;language-log&quot;&gt;&lt;code class=&quot;language-log&quot;&gt;&lt;span class=&quot;token separator comment&quot;&gt;======================&lt;/span&gt; test session starts &lt;span class=&quot;token separator comment&quot;&gt;===========================&lt;/span&gt;
platform linux &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt; Python &lt;span class=&quot;token number&quot;&gt;3.10.7&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; pytest&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;8.3.3&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; pluggy&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1.5.0&lt;/span&gt;
&lt;span class=&quot;token property&quot;&gt;rootdir:&lt;/span&gt; &lt;span class=&quot;token file-path string&quot;&gt;/mnt/c/git/github/mortie23/ml/eng/misc/fn-misc-ipchecker&lt;/span&gt;
&lt;span class=&quot;token property&quot;&gt;configfile:&lt;/span&gt; &lt;span class=&quot;token domain constant&quot;&gt;pytest.ini&lt;/span&gt;
&lt;span class=&quot;token property&quot;&gt;plugins:&lt;/span&gt; env&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1.1.5&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; hydra&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;core&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1.3.2&lt;/span&gt;
collected &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt; item

tests&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;test_fn&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;misc&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;ipchecker&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;py &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;                               &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;100&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;%&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;

&lt;span class=&quot;token separator comment&quot;&gt;======================&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt; passed in &lt;span class=&quot;token number&quot;&gt;2.72s&lt;/span&gt; &lt;span class=&quot;token separator comment&quot;&gt;=============================&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;</content:encoded></item><item><title><![CDATA[If programming languages were people (thoughts from a Generative AI)]]></title><description><![CDATA[Can you relate to these? Inspired from these (https://leftoversalad.com/c/015_programmingpeople/), let’s see what a Generative AI thinks of…]]></description><link>https://mortimer.xyz/if-programming-languages-were-people-ai-thoughts/</link><guid isPermaLink="false">https://mortimer.xyz/if-programming-languages-were-people-ai-thoughts/</guid><pubDate>Fri, 25 Oct 2024 00:00:00 GMT</pubDate><content:encoded>&lt;h1 id=&quot;Can-you-relate-to-these&quot; style=&quot;position:relative;&quot;&gt;Can you relate to these?&lt;a href=&quot;#Can-you-relate-to-these&quot; aria-label=&quot;Can you relate to these permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h1&gt;
&lt;p&gt;Inspired from these (&lt;a href=&quot;https://leftoversalad.com/c/015_programmingpeople/&quot;&gt;https://leftoversalad.com/c/015_programmingpeople/&lt;/a&gt;), let’s see what a Generative AI thinks of what personifications of programming languages might be.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;The following text and images were all generated with AI by Meta&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&quot;General-purpose&quot; style=&quot;position:relative;&quot;&gt;General purpose&lt;a href=&quot;#General-purpose&quot; aria-label=&quot;General purpose permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;h3 id=&quot;Python&quot; style=&quot;position:relative;&quot;&gt;Python&lt;a href=&quot;#Python&quot; aria-label=&quot;Python permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Name: Clement (Clem for short)&lt;/li&gt;
&lt;li&gt;Age: Late 20s&lt;/li&gt;
&lt;li&gt;Personality: Easy-going, versatile, and adaptable&lt;/li&gt;
&lt;li&gt;Appearance: Casual, laid-back attire (hoodie, jeans)&lt;/li&gt;
&lt;li&gt;Interests: Data science, machine learning, web development, automation&lt;/li&gt;
&lt;li&gt;Skills: Quick learner, problem-solver, team player&lt;/li&gt;
&lt;li&gt;Quirks: Loves indentation, hates unnecessary complexity&lt;/li&gt;
&lt;li&gt;Favorite hangouts: Data science conferences, hackathons, research labs&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Clem is the friendly, approachable tech enthusiast. Always willing to lend a helping hand or share knowledge.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Python: versatile, easy-to-learn, and widely applicable&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 1280px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/b98633fd2dd6e2cda9068045462b62fb/62aaf/programming-language-person-python.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 100%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDAAEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQH/2wBDAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQH/wgARCAAUABQDASIAAhEBAxEB/8QAGQABAAMBAQAAAAAAAAAAAAAAAAUGCAQH/8QAGAEAAgMAAAAAAAAAAAAAAAAABAUDBgf/2gAMAwEAAhADEAAAAcN89X0ecmz0jyfQaLIk1Z84DIX/xAAbEAACAwEBAQAAAAAAAAAAAAADBAEFBhMCFf/aAAgBAQABBQJ3G19eVmqX0QVMUVmFbfsT6GOLU2WhXhqfAy56vnvRaEY1nf/EACMRAAICAQMDBQAAAAAAAAAAAAIDAQQRABIUBiIjBSExQrH/2gAIAQMBAT8BVfa9wr4JLhlQjYMOIyEgiGmNVr11cmZr2RgJKRjIQY5KafTvTbalVln1W8mwaFG5UWqXjYQDJh5b62dhds7gGcx8aAA5Fado549qc4jOfL76sREPbGPvP7Gv/8QAIhEAAgICAQQDAQAAAAAAAAAAAQIDBBEhAAUSEyIUMVFh/9oACAECAQE/AZ6ywEsLCM0U6xxyeHCureoksRpLZXHa2SA6rnAbsf6l6j1fyyeOCiYxI4Qmg5JUNgH1gZdj8JHHZvi2fY6uVgNnQ7q2h/NnXKRJqVySSTEuSTknXP/EACcQAAICAgIBAwMFAAAAAAAAAAEDAgQFERITABQhIiMxQRUkMjNh/9oACAEBAAY/AsyrFPZdXispcjXyLatmnayePx6gx7EQr2pVfSTBj+4nXaOUtKcf4nHusmFDJfpT7xcutVopfXi2utCOSlwSY1eRVGbZl82M63Rkfkp06Ftluv3EBysE6+snrXMiNlIYmfASEZdXCBmJSEByJOOo5KzGlAYPoE3OYILsW5OPqQQn6fWVVuSeLvpxZDs+Z4YJmRtpyEsfUpVcnVgLFVLEJfZMDjnGAFqTLMYWbHZ6d65r64nUZTZI4G0cRjZ7YmjKT2lfZOcyeY7dmexKW5bEzIfYDy3kpLj6ynkHJQ4cuQXNonKJ2TvcmMP+c5cdb8zpaBMqs4/q2P6xJPbMQ1ogTkNSH5jr8gHxUK8ApcqVZnCO+IlOJ5ceWyAT8tb+5Ovb28//xAAYEAEBAQEBAAAAAAAAAAAAAAABESEAMf/aAAgBAQABPyFkgFRPghr6PB3P/a8flcqzFzoSga17mYdUVcZvgIVuiREYAEfoXl5cPSzjB9InZVWFnZba5DQnZ+dR9Ed6bwEdI5RAJDUjLkAYTuwlz1qESqBgQP/aAAwDAQACAAMAAAAQ8MfD/8QAGBEBAQEBAQAAAAAAAAAAAAAAAREhADH/2gAIAQMBAT8Qc6umENBHpE0HUHaPd0kUuiwVYxtA2h0lsy28RgAYAAHiBga53//EABgRAQEBAQEAAAAAAAAAAAAAAAERIQBB/9oACAECAQE/ENuG6iDPGFV0icELJvGqUIUzEkKCKLptF3bKSMqs15cIKCHSqqr6rXv/xAAXEAEBAQEAAAAAAAAAAAAAAAABABEh/9oACAEBAAE/ENkRwED8hnSw7YQgMBQB8BYUQ8DJ0wavdOOlhFuuqbPp2YNeAHEN0Y3+iY3jtsZPv5SD5Oq+KuenABw8yEhzmdMiy0I9+qbZxQXGOX71/9k=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;programming language person python&quot;
        title=&quot;programming language person python&quot;
        src=&quot;/static/b98633fd2dd6e2cda9068045462b62fb/62aaf/programming-language-person-python.jpg&quot;
        srcset=&quot;/static/b98633fd2dd6e2cda9068045462b62fb/953fe/programming-language-person-python.jpg 500w,
/static/b98633fd2dd6e2cda9068045462b62fb/0a251/programming-language-person-python.jpg 1000w,
/static/b98633fd2dd6e2cda9068045462b62fb/62aaf/programming-language-person-python.jpg 1280w&quot;
        sizes=&quot;(max-width: 1280px) 100vw, 1280px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;h3 id=&quot;JavaScript&quot; style=&quot;position:relative;&quot;&gt;JavaScript&lt;a href=&quot;#JavaScript&quot; aria-label=&quot;JavaScript permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Name: Jax (short for JavaScripton)&lt;/li&gt;
&lt;li&gt;Age: Early 20s&lt;/li&gt;
&lt;li&gt;Personality: Dynamic, energetic, and creative&lt;/li&gt;
&lt;li&gt;Appearance: Trendy, edgy attire (streetwear, bold accessories)&lt;/li&gt;
&lt;li&gt;Interests: Web development, frontend engineering, gaming, VR&lt;/li&gt;
&lt;li&gt;Skills: Quick thinker, innovative problem-solver, master of async&lt;/li&gt;
&lt;li&gt;Quirks: Loves callbacks, hates IE browser compatibility issues&lt;/li&gt;
&lt;li&gt;Favorite hangouts: Tech meetups, hackathons, gaming communities&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Jax is the vibrant, entrepreneurial coder. Always pushing boundaries and exploring new possibilities.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;JavaScript: dynamic, innovative, and web-centric&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 1280px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/c19e31323b412f6b9e1e2be99d4082a1/62aaf/programming-language-person-javascript.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 100%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDAAEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQH/2wBDAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQH/wgARCAAUABQDASIAAhEBAxEB/8QAGAABAQEBAQAAAAAAAAAAAAAAAAcIAwn/xAAZAQACAwEAAAAAAAAAAAAAAAAEBQIGBwj/2gAMAwEAAhADEAAAAfPjrL7HMrPi9CFWW9diW+QoapzX/8QAGxAAAwADAQEAAAAAAAAAAAAAAwQFAQIGERX/2gAIAQEAAQUC1k8z8V7m8FUJCyuTl10ab0cqMk7oQjZT82pViEUfN3V6O3//xAAjEQACAgEDBAMBAAAAAAAAAAACAwEEBRESIQAUIjEGE0FR/9oACAEDAQE/Ab1ZLDtLxNZOPrNl1vH2rDKTWgnZRQ+sx16e77WySLyJZvIqYWjsChTxrGdYK7K6GFexO40qI/rytVq98gMnsYIEJhv12lBFrGnlPvqitYUvlBAACQvWAyIxEiIC0gEZiNYEC8hiOBLmOer2AwTrdhjcLiWsNkyTGY6mZlP9IiTJTPEe5/Ov/8QAIhEAAgIBAgcBAAAAAAAAAAAAAQIDBBIAEQUGExQhIjFC/9oACAECAQE/AYrvbkJJnblHRjsRKtsskuUksUsIrkJmqyQOVMTwzlOhJL7YrJ3KyOvRuAB2Azquj47+uaM6lWx2yBVdj+V+aSxYj5k5dWOeZFanadgkjqGfeMZkAgFtvGR86i4jxBY1Vb1xQPgWzMAPJPwPr//EACQQAAICAQMEAwEBAAAAAAAAAAECAwQSBRETABQhIiMxQTJC/9oACAEBAAY/Ar9vscr1TtpIYXt8Xc9xKoxj49pJTXQu8/EDwmPCbjy3FmOlBNZv9jRrQ1S81+KG9PNDKdM052RtrzV7HeNAj+DKqxZ8jdPFYgspIp+nUodiAfon8JKnb/SkfYI6aXVdSrVYaktLKOzM9Oa6tgtFa7ecRzfPGS0kgwd5wcE9sR1au0tfRL6awZxoz0b1mGJ6M/JpdtJp66RvqVd/VRLjuvh9o2ZOpVl002GyJ55eJ3ly9jIUfE1siSe22+L99ieqUTLvDJJCkkWUmDgqM9/fMGQ+WKsDufXHxtpnamKsstOEYRU6AWMbP/G9UsW2RV5JWklxH977nq9TrGjJGLs7Z2qEFiZmyw8uw8DFFAVQqj8Hk9f/xAAZEAEBAQEBAQAAAAAAAAAAAAABEQAhMUH/2gAIAQEAAT8hApDcZNcqIholkxp8sDHYgFFrxBR342VWx0CcOt5VWOEFYywBqOpdtR3prwPfKfHTo0D6lQ9Gt+vAnAoGguYXGOJ01on6D93FaRR2t0BYIEZ8oqP/2gAMAwEAAgADAAAAEFMggv/EABcRAQEBAQAAAAAAAAAAAAAAAAERACH/2gAIAQMBAT8QtqrWKKIrNNKQ+JUAOS4jJLVXCAqt7eHUscGdYq9a3QAxAVGAsDf/xAAZEQEBAQEBAQAAAAAAAAAAAAABESEAMWH/2gAIAQIBAT8Q8VkmaBfPaIx8T4jMFlouEoqYT8TClW5ZgzlnAjJ1eRAhVVhqq69//8QAGRABAQEAAwAAAAAAAAAAAAAAAREAECEx/9oACAEBAAE/EHqGj50piJ6H4GliGG8xRNHE9BogYobCzTvgUphmYVu1TBCCLcBmrRoYH3ZUJCXqIziSglxwHINYGBCadOXrtCTeUatLcNN458kHcYbq7//Z&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;programming language person javascript&quot;
        title=&quot;programming language person javascript&quot;
        src=&quot;/static/c19e31323b412f6b9e1e2be99d4082a1/62aaf/programming-language-person-javascript.jpg&quot;
        srcset=&quot;/static/c19e31323b412f6b9e1e2be99d4082a1/953fe/programming-language-person-javascript.jpg 500w,
/static/c19e31323b412f6b9e1e2be99d4082a1/0a251/programming-language-person-javascript.jpg 1000w,
/static/c19e31323b412f6b9e1e2be99d4082a1/62aaf/programming-language-person-javascript.jpg 1280w&quot;
        sizes=&quot;(max-width: 1280px) 100vw, 1280px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;h3 id=&quot;C&quot; style=&quot;position:relative;&quot;&gt;C#&lt;a href=&quot;#C&quot; aria-label=&quot;C permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Name: Jane&lt;/li&gt;
&lt;li&gt;Age: Polished professional (30s)&lt;/li&gt;
&lt;li&gt;Personality: Modern, efficient, and business-savvy&lt;/li&gt;
&lt;li&gt;Appearance: Tailored suit, sleek tech gadgets&lt;/li&gt;
&lt;li&gt;Interests: Enterprise software, web development, mobile apps&lt;/li&gt;
&lt;li&gt;Skills: Expert in .NET framework, master of LINQ&lt;/li&gt;
&lt;li&gt;Quirks: Loves garbage collection, hates manual memory management&lt;/li&gt;
&lt;li&gt;Favorite hangouts: Corporate offices, tech meetups&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Jane is the sophisticated, enterprise-focused developer. Always prioritizing productivity.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;C#: enterprise-focused, modern, efficient, .NET framework&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 1280px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/54a614039b0f7641d9e268ba162d92cc/62aaf/programming-language-person-csharp.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 100%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDAAEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQH/2wBDAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQH/wgARCAAUABQDASIAAhEBAxEB/8QAGAABAQEBAQAAAAAAAAAAAAAAAAgGBwn/xAAWAQEBAQAAAAAAAAAAAAAAAAAGBQT/2gAMAwEAAhADEAAAAfODNaXqu+VPqr1KZBG7DqymgCa//8QAGxAAAwADAQEAAAAAAAAAAAAAAwQFAQIGFRb/2gAIAQEAAQUCs8L4paXMjS0+Wrg1r9oe5WQ0Az2b9SrTMxgaxZ3Z0gYlsLnT/8QAJBEAAwABAgYCAwAAAAAAAAAAAQIDEQQSAAUTISMxMmEiUYH/2gAIAQMBAT8B0lNSNLaNdN1uZAIYSkpbc+6hwAhLMX2pLaoI/Jj8toNOeaqFHjqZ6SN5O07S8jdN1OGQkMe6nIP2MfXHKq1nqVE6PMN8gjsobPvO0jP94EIYz0ZZPc+NPf79cf/EACURAAIBAwIFBQAAAAAAAAAAAAECAwQREgAxBSEiI2ITFDRRk//aAAgBAgEBPwGoj6qeVKgRUrfId3sir2lufUxAVc3lyZr2QDbLUXCYp445ovcSxSoskcoMYEiOAVcdOzA3H2OeuMRRyUpaSNJGW+LOisVtzGJYEix2tpqmoyPfm/V/Hy1//8QAIhAAAgIBBAIDAQAAAAAAAAAAAQIDBBIFERMhABQiIzEz/9oACAEBAAY/AlbV2jgrcjRtJX+uy7Y7qkKyhYsn63cyNFGu7sxA80v0s7FnVqD3fRXOzYqgT4xRc/DEtszQ/cjVUaNlDbbFSPE9nT7NUyryxpPDxs8ZJXkCt8sS6Oo3A7Q+QR3pYeERWElFe1yUrEjbMrZHo9rxRvuV49hiFyXxrWnNhThiR9PRarS8YnqVsasUhaOIVahmdLLc6rVZ3eVAGBIN+tptCemnpcIflyRXeykucdiRDn7PWDldgPJmgijR6MhjRyDLy8bcec6TGSFnYdtjGiZHdUXx63o6HJCWcbNpNaNuyu55q3rzBvrj+fJn8FJboeRyNptIH86a+34FA/pekb86/dvP/8QAGRABAQEBAQEAAAAAAAAAAAAAAREhMQBB/9oACAEBAAE/IeufuAr6U4rQicp3bi4DAzAk3E0qMX1AMZjhk8+C4Nqa9q2uFCevGGWHh7Xf9uSsP1kxoqWHyoavmLambccAqE5mcwKtkhqBYYD9BE3GREcghav/2gAMAwEAAgADAAAAEAA/Av/EABkRAQEBAQEBAAAAAAAAAAAAAAERIQAxUf/aAAgBAwEBPxCOvI4fjxrErPSWzwtPikmmjIEeMjn0kJEFgDqhHOcE9EWVNUtquq6uu9//xAAZEQEBAAMBAAAAAAAAAAAAAAABEQAhQTH/2gAIAQIBAT8Qm2JQ27BsVB9lNj3KEDtLKfyIUbhep0ph0AlCOyOBAMEA0gQBwAAHDWf/xAAXEAEBAQEAAAAAAAAAAAAAAAABABEh/9oACAEBAAE/ECYYQgfaT9AipI2paJgB2iVRmnH3MfbeUAqSkxQ1U20RsPWWpNGycG7y+5OcWugFdHF6wOtMzEfSINs+Hb6UhCk0qpYqO6cG57takBnAhkoVT//Z&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;programming language person csharp&quot;
        title=&quot;programming language person csharp&quot;
        src=&quot;/static/54a614039b0f7641d9e268ba162d92cc/62aaf/programming-language-person-csharp.jpg&quot;
        srcset=&quot;/static/54a614039b0f7641d9e268ba162d92cc/953fe/programming-language-person-csharp.jpg 500w,
/static/54a614039b0f7641d9e268ba162d92cc/0a251/programming-language-person-csharp.jpg 1000w,
/static/54a614039b0f7641d9e268ba162d92cc/62aaf/programming-language-person-csharp.jpg 1280w&quot;
        sizes=&quot;(max-width: 1280px) 100vw, 1280px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;h3 id=&quot;C-1&quot; style=&quot;position:relative;&quot;&gt;C++&lt;a href=&quot;#C-1&quot; aria-label=&quot;C 1 permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Name: Axel (Cpp)&lt;/li&gt;
&lt;li&gt;Age: Ambitious young adult (20s)&lt;/li&gt;
&lt;li&gt;Personality: Powerful, complex, and assertive&lt;/li&gt;
&lt;li&gt;Appearance: Edgy, avant-garde attire (black leather, piercings)&lt;/li&gt;
&lt;li&gt;Interests: Game development, high-performance computing, systems programming&lt;/li&gt;
&lt;li&gt;Skills: Template metaprogramming master, performance optimization expert&lt;/li&gt;
&lt;li&gt;Quirks: Loves operator overloading, hates unnecessary constraints&lt;/li&gt;
&lt;li&gt;Favorite hangouts: Gaming communities, tech conferences&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Axel is the bold, cutting-edge developer. Always pushing boundaries.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;C++: high-performance, complex, game development&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 1280px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/e272b97a177bfa4421ecf1a3fad62836/62aaf/programming-language-person-cplusplus.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 100%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDAAEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQH/2wBDAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQH/wgARCAAUABQDASIAAhEBAxEB/8QAGQABAQADAQAAAAAAAAAAAAAAAAcDBAYJ/8QAFgEBAQEAAAAAAAAAAAAAAAAABAUC/9oADAMBAAIQAxAAAAHzOoXJ6z58nVcCtE+rNZyBUz//xAAbEAADAAMBAQAAAAAAAAAAAAADBAUBAgYAFP/aAAgBAQABBQL5WKOei58DSzXLOhIekjvrU6W3TKvRUNqkYu5JaC+UTMnk+//EACURAAICAAUEAgMAAAAAAAAAAAECAwQFERIhMQATFCIjMkGy8P/aAAgBAwEBPwGDB7WFV5YVepH5+H0J6yPVaz4Ukzmz8kguZjYdqaOPRIX16ZTGqmStg9h4Imnx6lHMVHcRKlcqrbZhScW4/szyYvZZC3sVAC576QO3kBnwB+AOOpANb7D7v+x6/8QAJREAAgICAQALAAAAAAAAAAAAAQIDEQQhAAUSEyIxM2FykbHw/9oACAECAQE/AWzhOQQGISeYTksqmaOFezAjVoSRbMJA3XNARhtsyrJnAOwj6OmZL7rM+SCRrZrAr98Tkq4CmgfGtXayE3XrvkXlp7V+uf/EACcQAAMAAQMEAgAHAAAAAAAAAAECAwQFERIAExQiITEGIyRBUVJi/9oACAEBAAY/ApY/aFbsGxZ/rN4zpRlE23y2XhL/AAxM1+9l6xsf8MaQRlYK5Wn6rXSqdgXHCaylnWYJHMqcnelV/M7k5sBY+oiEWuA3ovPlqOAjLUbrVGQ5BIKUVl+zvty/fqmZpPlzby5MWOWrypKCTycrtMJSYpSi0Tj3AvGn8zXrSTmLpyrHD4489NWXi+KsOcltKTmdaqrP5HeY377s9uDty6Zlz9deauUkcfQUtMIADtucskexb1OxA25DkSTs1GZPHMu2T6kGYG/9uW30eXx9D468r3FcjO1LBfZvXx1WfEKp3AYc29vk/Pzv1LDw6ukRMv8AJJZnpapLMRsN/pR8D1VR1//EABoQAQEBAQEBAQAAAAAAAAAAAAERIQAxUYH/2gAIAQEAAT8hr1xaJzjKKY1A6DHSrWptcYUeqJY9IW5nDCAYA+4i38CdZMAlkFie7G95CzaYHttRSLaK2xcCAuqHrRX5AMqJjpkatpiJkcdBnlwySuoDg0YJaCK//9oADAMBAAIAAwAAABDYKIP/xAAaEQEAAwEBAQAAAAAAAAAAAAABABFBITGh/9oACAEDAQE/EK9OyXDMqYoJyOWFPwWgVR6LeAIS1gF3GH2QqUAKACz4MjKMn//EABkRAQADAQEAAAAAAAAAAAAAAAEAESExQf/aAAgBAgEBPxBfIsXNIDMAYlRop9g4BQp2nzxshgBFDQkAoKgS6oLsSuqqyraqFVdVes//xAAYEAEBAQEBAAAAAAAAAAAAAAABABEhMf/aAAgBAQABPxAKrd2/2oOHTSKGBMS9TSCCAnJycW0AklkmnjIn+pYu8jyyvWDIN/iT9ahzRkjPjOnDhJ9mve4RX0ITstUFTTK9MF/n0BgAAfckZkoLeIsIv//Z&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;programming language person cplusplus&quot;
        title=&quot;programming language person cplusplus&quot;
        src=&quot;/static/e272b97a177bfa4421ecf1a3fad62836/62aaf/programming-language-person-cplusplus.jpg&quot;
        srcset=&quot;/static/e272b97a177bfa4421ecf1a3fad62836/953fe/programming-language-person-cplusplus.jpg 500w,
/static/e272b97a177bfa4421ecf1a3fad62836/0a251/programming-language-person-cplusplus.jpg 1000w,
/static/e272b97a177bfa4421ecf1a3fad62836/62aaf/programming-language-person-cplusplus.jpg 1280w&quot;
        sizes=&quot;(max-width: 1280px) 100vw, 1280px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;h3 id=&quot;Java&quot; style=&quot;position:relative;&quot;&gt;Java&lt;a href=&quot;#Java&quot; aria-label=&quot;Java permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Name: Jake (The Architect)&lt;/li&gt;
&lt;li&gt;Age: Established/Professional (40s)&lt;/li&gt;
&lt;li&gt;Personality: Structured, organized, and meticulous&lt;/li&gt;
&lt;li&gt;Appearance: Business attire (suit, tie), clean-cut hair&lt;/li&gt;
&lt;li&gt;Interests: Enterprise software, Android app development, scalability&lt;/li&gt;
&lt;li&gt;Skills: Master of object-oriented design, expert in multithreading&lt;/li&gt;
&lt;li&gt;Quirks: Loves stability, hates reinventing the wheel&lt;/li&gt;
&lt;li&gt;Favorite hangouts: Oracle conferences, Java user groups&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Jake is the seasoned, methodical enterprise developer. Always building robust systems.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Java: structured, scalable, enterprise-focused&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 1280px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/8c3f4135c45f547a3644c4f4674bab63/62aaf/programming-language-person-java.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 100%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDAAEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQH/2wBDAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQH/wgARCAAUABQDASIAAhEBAxEB/8QAGQABAAMBAQAAAAAAAAAAAAAAAAUGCAkH/8QAGAEAAgMAAAAAAAAAAAAAAAAABAYCAwX/2gAMAwEAAhADEAAAAeZd9lrDCzz5Fic/TuBgsboiGZO//8QAGhAAAwEBAQEAAAAAAAAAAAAAAwQFBgIBFP/aAAgBAQABBQI+PB99dGdlkGYtA/XB6Qz6Fospav7oezZzPJ6edUccJbYggGT/xAAiEQACAgEEAgMBAAAAAAAAAAABAgMEEQASEzEUISNBQlH/2gAIAQMBAT8BqWhi0HVbiWIF+TYqzQ2uOsJyDCqRYgljtShBHHxvKYyxjVCtgQ88vEYkj3kKpdpCACP3uG7PecfeqSIKSqFUL4kDYAAG5/KLnHWXPtj2x7zqzVqtPKzVoCSxJJhjJJ/pJX3r/8QAJBEAAgEDAwMFAAAAAAAAAAAAAQIDBBESABMiITFBI0JSYaH/2gAIAQIBAT8BraBBshZCmyWUJmzQz027Iqqc3eS8sbQx7mT5pHkOTHKGn9KPJ5C2IvwCeB0wvxt2t9aqSTUuxJJFVIoJNyFUQYqD8V9o7DxqmnnEEQE0oAXoBI4A/df/xAAkEAACAgICAgICAwAAAAAAAAACAwEEBRIREwAUISMiUQYVJP/aAAgBAQAGPwLH2ntB2PyNvIpEcMFwt5pNZWUSvdqKZC8i0IZWk1doVyk2qWz6/MRmaK1vy6kyyYrv+qUHZtUVAuAr902NRE2G+B5kvgQnUfF2RrY6xFuvXudySyJqL2lC7gGqpMW2Q362GMx9wMHX8fn+PV7AZeRpsy6rD7DISi8OQOtakKbEkV5D563mTZhptZ1DXHlh+deNpRjf6uetaLavX6b1jdZMCnYiZs27DymdTF2swT7EbiHlZVAM0urToU6IBWVWSkTqK6bGq0nIjzZFxTtAHyU7rGfMrksk657VNptqlXYtcJKslofjslhT7A8C/Yi+BHp6ePFG6093VaU1QNKCWsoKCjWOI+In9zM/ufNk3L6Rsf6iWLK5ALHkRs0ltZjNZOZmINhzHPHPHER//8QAGRABAQEBAQEAAAAAAAAAAAAAAREAIUEx/9oACAEBAAE/IbEy/QpJZ0D4hWBB/oi5+Ry9u9YwnXRFhngFODMlHWL3g5abIQIrjzB0b7bCbO644XCZ14lMIcwWZM8UPsBSelBQHRamlv100gQy59PBABPD/9oADAMBAAIAAwAAABBj2IP/xAAXEQEBAQEAAAAAAAAAAAAAAAABEQAh/9oACAEDAQE/EHzkSaHETOjJCTGMNYBUjYgih5zIzTGlWwCIRQC0Gch8YgKgUvVVV3//xAAYEQEBAQEBAAAAAAAAAAAAAAABESEAYf/aAAgBAgEBPxAVjzYF+76zUXdANxgIqFKiiWj6KuMqlXQqVYpVgvCmiAAKAAQAwAh3/8QAFxABAQEBAAAAAAAAAAAAAAAAAQARIf/aAAgBAQABPxA48VeSW9TZ2y2tRPEDnlq0r0PDlN40LJRlQyKC7Dj23DK2WLd6366WeM+enH38oQXBkV6HBY5iS4MJshh7xpufqYY5pdb6ejGU7zEko3//2Q==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;programming language person java&quot;
        title=&quot;programming language person java&quot;
        src=&quot;/static/8c3f4135c45f547a3644c4f4674bab63/62aaf/programming-language-person-java.jpg&quot;
        srcset=&quot;/static/8c3f4135c45f547a3644c4f4674bab63/953fe/programming-language-person-java.jpg 500w,
/static/8c3f4135c45f547a3644c4f4674bab63/0a251/programming-language-person-java.jpg 1000w,
/static/8c3f4135c45f547a3644c4f4674bab63/62aaf/programming-language-person-java.jpg 1280w&quot;
        sizes=&quot;(max-width: 1280px) 100vw, 1280px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;h2 id=&quot;Maths-and-stats-and-data-science&quot; style=&quot;position:relative;&quot;&gt;Maths and stats and data science&lt;a href=&quot;#Maths-and-stats-and-data-science&quot; aria-label=&quot;Maths and stats and data science permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;h3 id=&quot;R&quot; style=&quot;position:relative;&quot;&gt;R&lt;a href=&quot;#R&quot; aria-label=&quot;R permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Name: Darren (Daz for short)&lt;/li&gt;
&lt;li&gt;Age: Late-20s&lt;/li&gt;
&lt;li&gt;Personality: Analytical, meticulous, and detail-oriented&lt;/li&gt;
&lt;li&gt;Appearance: Professional, polished (business attire)&lt;/li&gt;
&lt;li&gt;Interests: Statistics, data visualization, research, academia&lt;/li&gt;
&lt;li&gt;Skills: Expert data wrangler, visualization master, research ninja&lt;/li&gt;
&lt;li&gt;Quirks: Loves tidy data, hates messy code&lt;/li&gt;
&lt;li&gt;Favorite hangouts: Academic conferences, research institutions, statistical meetups&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Darren is the sharp, insightful statistician. Always focused on precision and accuracy.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;R: precise, analytical, and research-focused&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 1280px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/7eda8d1d9437b96f0cc82ba18acbe44b/62aaf/programming-language-person-r.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 100%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDAAEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQH/2wBDAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQH/wgARCAAUABQDASIAAhEBAxEB/8QAGgABAAIDAQAAAAAAAAAAAAAAAAYHAgQFCf/EABgBAAIDAAAAAAAAAAAAAAAAAAQGAQMF/9oADAMBAAIQAxAAAAHz+mePPkSHrNaYdcUKL7Nqisn/xAAbEAACAwEBAQAAAAAAAAAAAAAEBQEDBgIAFv/aAAgBAQABBQLkW0YkoMMdH9HcP7TvSyw3GilkRFef7ggy9wgypFvLjQdza3//xAAhEQACAgICAQUAAAAAAAAAAAABAgMRBBIABSETIjR0s//aAAgBAwEBPwHGHYwx5YzMaCJp40GIRIZG13RjLVaxOWjaKg0hKElipOimXs1JU9T2RKkqSmBkuhK+CVdI2R1NeGUlWFEEjmCTJ2PU+oS+2Zj7b+7a5oLu7u7N3zO+dm/cyf3bn//EACMRAAICAgIBBAMAAAAAAAAAAAECAxEEEgAFExQhIiNEUrP/2gAIAQIBAT8Bb088sfikuLHlbyFGPy+n3WRWhj1oybKAXX4qwbi4mCyqy9p1GrAMN+0gR6NEbI7K6N+yMoZTYIB5lAR9d23jATXEyNdBrrUc1VVVVCq5gk+iwvf8TG/jFz//xAAmEAACAgEDBAICAwAAAAAAAAACAwEEEQUSEwAUITEGIhUjQlJx/9oACAEBAAY/Au2bSs8oWH1rfeaPQixWeA8X7BVdUyIzJ53lXyOfMwMj1ouqdvZMrFjUm3FQu5U72qiwoUlUKo2zFBBRMq3Ng1YGSB3NvxspI0FCCywU2NFrXrC+QpLY63qWmXrTiAZEYIrLI44DE++vk1mykdL1eyd61qKrEqaT5sKGATTWNPjVMNN7BsovEQIxuURZZ18U7K+iwnTNF+OaSSlGBjXzVcN0WCmJjK3sab487vBFuKeo7rU7HOH6y7L8c2rtD6rlRzmSGV7J849+o6G3qBzZtlUaRWmEZPko5BzJkU58fxmJCP69VnCW1i4ZIEP0mCWuWrLIbZ3LZEEE5zEx1cYe2TMgkp2x5niDz/s+5mfMz1//xAAZEAEBAQEBAQAAAAAAAAAAAAABESEAQTH/2gAIAQEAAT8hbSkzlZlfT0O3MtrTSk++5RWOOxtOdtl4f9RoQigDrx1+81T8X3j97UUs0dJ96bIrFl62qyh0qVJZHM+TxA8jSsu5X/MdMjx9+IxNWVQoKFXe/9oADAMBAAIAAwAAABBH2EL/xAAYEQEAAwEAAAAAAAAAAAAAAAABABEhMf/aAAgBAwEBPxByZqmBoCPzvjzdrEICNBZ/FkVERtEuw16E0tVesBwIn//EABsRAQEAAwADAAAAAAAAAAAAAAERACExcZHw/9oACAECAQE/EE9qUHUEZHyW9aBZI6WERgEhYOpg4DAAwokQDRAA4Z2Tyv1t95//xAAYEAEBAQEBAAAAAAAAAAAAAAABEQAhQf/aAAgBAQABPxB4wPGBQHa9h1N1z/4M9qNblxgHF1B7ECgzPD9tAV6vJ9Svkh0UxwNN5I9imwYDMMeAwoiMQTyh+7Z/OIFhnknXXgI81y11kwZuqIt0UTx4/9k=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;programming language person r&quot;
        title=&quot;programming language person r&quot;
        src=&quot;/static/7eda8d1d9437b96f0cc82ba18acbe44b/62aaf/programming-language-person-r.jpg&quot;
        srcset=&quot;/static/7eda8d1d9437b96f0cc82ba18acbe44b/953fe/programming-language-person-r.jpg 500w,
/static/7eda8d1d9437b96f0cc82ba18acbe44b/0a251/programming-language-person-r.jpg 1000w,
/static/7eda8d1d9437b96f0cc82ba18acbe44b/62aaf/programming-language-person-r.jpg 1280w&quot;
        sizes=&quot;(max-width: 1280px) 100vw, 1280px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;h3 id=&quot;SAS&quot; style=&quot;position:relative;&quot;&gt;SAS&lt;a href=&quot;#SAS&quot; aria-label=&quot;SAS permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Name: Glendon (Glen for short)&lt;/li&gt;
&lt;li&gt;Age: Late 40s&lt;/li&gt;
&lt;li&gt;Personality: Conservative, reliable, and detail-oriented&lt;/li&gt;
&lt;li&gt;Appearance: Classic business attire (suit, pearls)&lt;/li&gt;
&lt;li&gt;Interests: Data analysis, reporting, pharmaceuticals, finance&lt;/li&gt;
&lt;li&gt;Skills: Expert data manipulation, precise reporting&lt;/li&gt;
&lt;li&gt;Quirks: Loves structured data, hates unformatted code&lt;/li&gt;
&lt;li&gt;Favorite hangouts: Corporate offices, conference rooms&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Glen is the seasoned, no-nonsense professional. Always prioritizing accuracy and precision.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;SAS: reliable, precise, and industry-focused (pharmaceuticals, finance)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 1280px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/bdb7c03519aebde693ed84aa0b094a9f/62aaf/programming-language-person-sas.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 100%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDAAEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQH/2wBDAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQH/wgARCAAUABQDASIAAhEBAxEB/8QAGQABAAMBAQAAAAAAAAAAAAAAAAYICQUH/8QAFwEAAwEAAAAAAAAAAAAAAAAAAgMFBv/aAAwDAQACEAMQAAABqTNKUWgEuilClP8AEdOiDos7Aln/xAAcEAACAgIDAAAAAAAAAAAAAAAFBgIEAwcBExX/2gAIAQEAAQUCaaA2uSLeFCazrc620V4zKvkJCrzDmXbToriUgaIndHdPMdkEGEEyf//EACMRAAMAAgEDBAMAAAAAAAAAAAECAwQRIQAGEgUiMkEkYXH/2gAIAQMBAT8B9ONZRrjJJ7/krk0uFLVnEB/MccCWyoZyUHxJOvjkY/cAvUSfDnMOQiOmMXVRoANuZJOv2f7z13C7wXCnBmimsc+EiZptj7j4pobP2dc/fUoRac2aMiSqkkzUkn28kkc9f//EACIRAAMAAQMEAwEAAAAAAAAAAAECAxEEEiEAEyJRBQZBgf/aAAgBAgEBPwHVRRmXVVvNX7fZlIiaBmO3Yqc+RXyKjzYgEHcx3Npr/AmEjaWsepUGjI1wrMeSVAsOP4OvryJZte9kWrhL4eqijDCDGGbJGPz109rB2ArQAMcAOwA59Z6//8QAJBAAAgIBBAIDAAMAAAAAAAAAAgMBBAUGERITFCIAFSEWIzH/2gAIAQEABj8C1HZqmSn82HWY6sYi26doWGFPtVIvMoJiERts1wFIcljz+VJJeIu3m4pCF6ZzIZple9l/rCd/dd+3p1cYp1eYc2FhCoyAeKx0bzAuyNF+kcEqva+vPG0Klq4hDUVarCiXg9vOxxeMWpl9gpsQwu4uX4d6xf8ALslkFy1dqwEFK0cjrXFFaPjB1WiEiIluIcoGJCfwLuR0s7LSFZdZJUrUPasrTEuJzquObYtMrWK0gRsIDXVl9hjoBsCa6uOx2J1MlRRL2/x/T2Kv491jfxmO7LCYct8xWEXKYITJB3xBC6GHWJ+Hx9gSDdi2g7ZsgO3uxb1vjn/p9bl8p+OXjqycBW8W2fi4ArGNXyqUyeE9wPK6PYax7uu0HbHLf2IinxqGrNUQFnGYvIOlmYcZnZuVRY0pmIGNo9VrGI9VgA/u28//xAAZEAEAAwEBAAAAAAAAAAAAAAABABEhMRD/2gAIAQEAAT8hSzSzqZiyOoMiPpcT7JD8DDpyOQxpM2G5cQQulllugmKXzoQKDJIdy6t9rcnfZRIb3kEDi+c926sm5x1SshK7S5D6lSutUiK3evD+GzBL4lL/2gAMAwEAAgADAAAAEBsvvP/EABkRAQEBAQEBAAAAAAAAAAAAAAEhEQAxUf/aAAgBAwEBPxDY6PTfYEVWKIA5tQIcMY4xqs/VKASloyozjLBE81B51BKFS1VVfe//xAAZEQEBAQEBAQAAAAAAAAAAAAABEQAhMVH/2gAIAQIBAT8Qa0GlbPriFQU2ZiACMUIYmwJcEEgb78imEJl0CV0mN/iDAoAIB8Cb/8QAFxABAQEBAAAAAAAAAAAAAAAAAQAREP/aAAgBAQABPxBeRT6zhlNXvHgD8yk3f1M1pBxAnGXNzRcxXjTSjqJky61OrD4exYip4fBlcOUQcnbSnD1/+xbDAiICeX88eOQxT0Bw+1N1ClLuHGTOP//Z&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;programming language person sas&quot;
        title=&quot;programming language person sas&quot;
        src=&quot;/static/bdb7c03519aebde693ed84aa0b094a9f/62aaf/programming-language-person-sas.jpg&quot;
        srcset=&quot;/static/bdb7c03519aebde693ed84aa0b094a9f/953fe/programming-language-person-sas.jpg 500w,
/static/bdb7c03519aebde693ed84aa0b094a9f/0a251/programming-language-person-sas.jpg 1000w,
/static/bdb7c03519aebde693ed84aa0b094a9f/62aaf/programming-language-person-sas.jpg 1280w&quot;
        sizes=&quot;(max-width: 1280px) 100vw, 1280px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;h3 id=&quot;Matlab&quot; style=&quot;position:relative;&quot;&gt;Matlab&lt;a href=&quot;#Matlab&quot; aria-label=&quot;Matlab permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Name: Matilda (Maddie)&lt;/li&gt;
&lt;li&gt;Age: Late 20s (with a grad student vibe)&lt;/li&gt;
&lt;li&gt;Personality: Intellectual, creative, and slightly introverted&lt;/li&gt;
&lt;li&gt;Appearance: Academic attire (fleece, jeans)&lt;/li&gt;
&lt;li&gt;Interests: Numerical computing, signal processing, research&lt;/li&gt;
&lt;li&gt;Skills: Expert numerical analyst, algorithm designer&lt;/li&gt;
&lt;li&gt;Quirks: Loves vectorized operations, hates loop optimizations&lt;/li&gt;
&lt;li&gt;Favorite hangouts: Research labs, academic seminars&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Matilda is the brilliant, analytical thinker. Always exploring new mathematical concepts.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Matlab: intellectual, creative, and research-oriented (numerical computing, signal processing)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 1280px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/f53f13280b01502457f506e0e8c61322/62aaf/programming-language-person-matlab.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 100%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDAAEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQH/2wBDAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQH/wgARCAAUABQDASIAAhEBAxEB/8QAFwABAQEBAAAAAAAAAAAAAAAAAAcGCP/EABkBAAIDAQAAAAAAAAAAAAAAAAYHAQIEBf/aAAwDAQACEAMQAAAB5c0sJsE82uIaIwnYcaFtMjC/b/8A/8QAGhAAAwEBAQEAAAAAAAAAAAAABAUGAwECFP/aAAgBAQABBQKf3bcKEx4yVWqjW2MrKAnBRk7gcwCRpk33Nyqa3xp2reQeK+8+T//EACYRAAMAAQIFAwUAAAAAAAAAAAECAwQREwAFBhIiFBUkISMzQVL/2gAIAQMBAT8Bx+QvzfpnLyZenpmUyaRILxhs8umWm/pTkPHdsbTaddbJpDIbbAsB3P0r1c7s3uUZAse2fsGbTbTUBE3J53ZQqugNE8XPkANQOOVfMfIjl/KjjRemNLI+9LHo6KzvCdO5ZO7EszIFLN9SSeMdm2I+R/FP9n+Bx//EACYRAAIBAQcDBQAAAAAAAAAAAAECAxIABAUREyEjBhQiFTNBc7L/2gAIAQIBAT8BlxP0/FrsCswu2nWKVeZpL7sy9zoCRYoqGDJxNywLqNpt4J1NgaqqmB2IAqbvoFqbao0td81BbMhTuoOXxaThSNouIzOglMfgZQHIAkK5VgDYBs8hab3pfsf9W//EACkQAAICAQMDAwMFAAAAAAAAAAIDAQQFERITAAYUISIyFSUxIyQzYnH/2gAIAQEABj8Ci9QqTZyVu6xMIt9t0ZY6XQVgzJtfOIBSmv0WywsNik/t5ptRMaUW9yfbs6DMqA1KVauZoVVoHx0bYsbuZvcZwnc4JVKgTXS1kcR4nLU/rYqRgaWN+7XfIeU033PcrgJYJRIsHSvsGVu5vzrunDDRqV6UZgU5U/FKNYp1lod4DZ8euTQ5rCzLaz9QBJMRITsjH5TI5YlLuVzrMTUhr3zdYw3lzxYiqLYQToh9yC/j9TjyIOGRbp96oUl4QcIC0pXBOsxxTBNTElEREkYBsKS+Uzr1cPKVzpjg4oVKasO5lBRKuVOZ/MO5syUyhYRxEoYCPjund1f7cxeVtNw9ZorCjkQp3a5A1cbxNB1Yrz6HI6ioZ09ZmS93Q+wPm38j/efT/Ov/xAAbEAEBAQACAwAAAAAAAAAAAAABEQAhMUFRYf/aAAgBAQABPyEeq+UrG42pUXoTe+cpo6VOOwC3zjipt4WsByZjIU3ktpjhk5OtFOfE2npdGiLBjmynlxpCDeTRr/mwcE8N2myydBhDYlcHYLtf0QYLxWxYHg3/2gAMAwEAAgADAAAAEPAP/P/EABoRAQACAwEAAAAAAAAAAAAAAAEAETFBYZH/2gAIAQMBAT8QWctlVuMMiBHGRBQhpiCtswCTICDtuOJzyfRcLAGCYwD14T//xAAaEQEAAgMBAAAAAAAAAAAAAAABABEhMXFh/9oACAECAQE/EBK8TXIRaAzLjSbWNRhKMBKwgNWz0FX7lTSiEQAxAaDl5n//xAAYEAEBAQEBAAAAAAAAAAAAAAABEQAhEP/aAAgBAQABPxDNsOdgC4zXjBO6/r53Sy0Ej+9OqrsXKkM1IYjgRgQYJPj1gaRsNnaJzrsqn0VgP8ZqHq2xesdquXy3q9tgB6HAQZEUE0dqUvRKUpWArLv/2Q==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;programming language person matlab&quot;
        title=&quot;programming language person matlab&quot;
        src=&quot;/static/f53f13280b01502457f506e0e8c61322/62aaf/programming-language-person-matlab.jpg&quot;
        srcset=&quot;/static/f53f13280b01502457f506e0e8c61322/953fe/programming-language-person-matlab.jpg 500w,
/static/f53f13280b01502457f506e0e8c61322/0a251/programming-language-person-matlab.jpg 1000w,
/static/f53f13280b01502457f506e0e8c61322/62aaf/programming-language-person-matlab.jpg 1280w&quot;
        sizes=&quot;(max-width: 1280px) 100vw, 1280px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;h3 id=&quot;Haskell&quot; style=&quot;position:relative;&quot;&gt;Haskell&lt;a href=&quot;#Haskell&quot; aria-label=&quot;Haskell permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Name: Helena (The Theorist)&lt;/li&gt;
&lt;li&gt;Age: Intellectual/Philosophical (30s)&lt;/li&gt;
&lt;li&gt;Personality: Logical, abstract, and innovative&lt;/li&gt;
&lt;li&gt;Appearance: Academic attire (turtleneck, glasses), eclectic accessories&lt;/li&gt;
&lt;li&gt;Interests: Functional programming, type theory, category theory&lt;/li&gt;
&lt;li&gt;Skills: Master of lazy evaluation, expert in monads&lt;/li&gt;
&lt;li&gt;Quirks: Loves elegance, hates imperative programming&lt;/li&gt;
&lt;li&gt;Favorite hangouts: Academic conferences, functional programming meetups&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Helena is the brilliant, theoretical functional programming enthusiast. Always exploring new concepts.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Haskell: logical, abstract, functionally elegant&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 1280px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/cde104c541c88496bbcd6722d2e2ca5f/62aaf/programming-language-person-haskell.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 100%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDAAEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQH/2wBDAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQH/wgARCAAUABQDASIAAhEBAxEB/8QAGQABAAMBAQAAAAAAAAAAAAAAAAUHCAED/8QAFwEAAwEAAAAAAAAAAAAAAAAAAwUGBP/aAAwDAQACEAMQAAABpb2x3uYBIFQpnineknRZFFij/8QAGhAAAwEBAQEAAAAAAAAAAAAABAUGAwECB//aAAgBAQABBQKLZFJldIw2NP8AVGj4Y5YUO4c0vFmUn3TgGd2nN22VF2xiLzbPGFLQf//EACURAAIBAgUDBQAAAAAAAAAAAAECAxESAAQFEyMhIjEyQVFScf/aAAgBAwEBPwGBtucOYI3jMMkZRlJbcYqYpY3eqBVCkMlod7htyKLlMjyJIyqoCq1B2E9OnuZRXGhc+k3T8zLmGtaXkZaRZqlC9SPSvj6j4GG8n9x//8QAJhEAAgEBBwMFAAAAAAAAAAAAAQIDEQAEEhMhIiMFMTJRcZGSwv/aAAgBAgEBPwG9wxZOyZhJnI6kMMGFQcxHCUY96qdUTBvUmjWguV3mhjlLPV1DGsiDX2yjT5t1Pi6jhi41MQqsexTy3TuFoD5N9j6m0GkUYGgwr+bf/8QAJBAAAgICAgICAgMAAAAAAAAAAgMBBAUSERMAFAYxFSEjQVH/2gAIAQEABj8CxNrOfGMgtN5p135Z7cdWWbgqusV5hDWJMW32LKshLor8t14kt+IUXxy1YpcZGn9Xq7KxJcNQySylVrmQH0tYxzDtOGYiYJei9/MmnLFSxdqpk7dSEe4D5NVcoXDinqDrIyg4lMjyGv8AcTE+YSgf5fVj7FqywK9qV5F3ug2pa6ZA5766xIoS6N1nG4DAFEz6Oax4syl2uXyTK1jME5GBsW4qYWjq2ZsVoYjrY8bJrr1V3z4EGdggfNMFMLD4ondQpHtPqMRaz+KSN0qFYtY2Zcwhk2yRzJSrKNmTZRlAJqy2x6ZbxsRNVDoNk7TtrLeuZEdgnWPGsp4rDNLMKxbr/vLyN4WjEGwK2ljJGMVFEMSuvx1x9zEzAzFrL5RglbsAmC6FilIiA8CK1R+gGP8APP/EABkQAQEBAQEBAAAAAAAAAAAAAAERACExQf/aAAgBAQABPyFM7zLcv5kAUDFuAGA8Pyp68RAYBSM5b7o5VQOBnno/zBW/NeEcLxZy37nGAB6g/g1maZ7uj1yj7cFCYY1hg6QkzTMkKTDEuMTDSagVheq/d//aAAwDAQACAAMAAAAQKy88/8QAGhEBAAMBAQEAAAAAAAAAAAAAAREhQTEAkf/aAAgBAwEBPxCkS+F8iqHPpeLTQCFAwhrjqG5wjkCBgACNQiEBTlZ21Ura3q3h8Pf/xAAbEQEBAQACAwAAAAAAAAAAAAABESExUQBBkf/aAAgBAgEBPxAFoKgrultHVIMIxIcXFKd9wVBL7rbSdYhrIhU0dXuAMAIAgEwBhy/Xz//EABYQAQEBAAAAAAAAAAAAAAAAAAEAEf/aAAgBAQABPxCLmPVgIVZFDI4pDIlHafXwZdoeWLujKfZoSyN8pbAdLItjXZeODOp+4MA04GaDc21xFBOnmSo4eQuWSvOnw9j1gnRrTrfEHz2A5fVV/9k=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;programming language person haskell&quot;
        title=&quot;programming language person haskell&quot;
        src=&quot;/static/cde104c541c88496bbcd6722d2e2ca5f/62aaf/programming-language-person-haskell.jpg&quot;
        srcset=&quot;/static/cde104c541c88496bbcd6722d2e2ca5f/953fe/programming-language-person-haskell.jpg 500w,
/static/cde104c541c88496bbcd6722d2e2ca5f/0a251/programming-language-person-haskell.jpg 1000w,
/static/cde104c541c88496bbcd6722d2e2ca5f/62aaf/programming-language-person-haskell.jpg 1280w&quot;
        sizes=&quot;(max-width: 1280px) 100vw, 1280px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;h2 id=&quot;Databases&quot; style=&quot;position:relative;&quot;&gt;Databases&lt;a href=&quot;#Databases&quot; aria-label=&quot;Databases permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;h3 id=&quot;SQL&quot; style=&quot;position:relative;&quot;&gt;SQL&lt;a href=&quot;#SQL&quot; aria-label=&quot;SQL permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Name: Sammy (SQLicious)&lt;/li&gt;
&lt;li&gt;Age: Mid-30s&lt;/li&gt;
&lt;li&gt;Personality: Logical, methodical, and direct&lt;/li&gt;
&lt;li&gt;Appearance: Simple, functional attire (button-down, khakis)&lt;/li&gt;
&lt;li&gt;Interests: Database management, querying, data modeling&lt;/li&gt;
&lt;li&gt;Skills: Master query writer, database optimizer&lt;/li&gt;
&lt;li&gt;Quirks: Loves normalization, hates data redundancy&lt;/li&gt;
&lt;li&gt;Favorite hangouts: Database conferences, online forums&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Sammy is the straightforward, analytical expert. Always seeking efficient data solutions.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;SQL: logical, methodical, and database-centric&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 1280px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/db21a2d446ea3a8e4beda6a3f6c76d2c/62aaf/programming-language-person-sql.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 100%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDAAEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQH/2wBDAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQH/wgARCAAUABQDASIAAhEBAxEB/8QAGQABAQEAAwAAAAAAAAAAAAAAAAgGBQcJ/8QAFQEBAQAAAAAAAAAAAAAAAAAABAX/2gAMAwEAAhADEAAAAYO5ak9xDoz+rNYB58ZsAro8XQf/xAAaEAADAQEBAQAAAAAAAAAAAAAEBQYDAgcB/9oACAEBAAEFApgozduRF0xTFt4vkqJRjy1Lo7Kx2qubFys5gyfvDf0JqxTma3FgT1//xAAnEQACAQEHAgcAAAAAAAAAAAABAgMRAAQFEhMUITE0UVJTcpHC4f/aAAgBAwEBPwGZZGGHLDoiCJ7zvpJGfVkjaEm7tDV8gZZkVXRwQQzECvQ4jcASDK4INCNnJxQ09H88CRzZ+2Pu+wtmbzN8m3//xAAmEQACAQMCBAcAAAAAAAAAAAABAgMABBIRNAUTFEEhUlRhkrPw/9oACAECAQE/AUdNLoyCTqG5AtkiRMA4mVZhKcS4UwszK6sCpABIGlDhtyQCIQdQDvV7+Pqv3tVtv1HbE/WawTyr8RX/xAAmEAACAgICAgIABwAAAAAAAAACAwEEBRIREwAhBhQVIiMxMjND/9oACAEBAAY/AqnbdbhaoPBMlXAZt2XbAR11XZSco7ElqJKEZQwhY0tePMcm8d7n5Daf+GTBoA76oZEy4rLnwzmVNW2StNRLdufXPoakIyOQLoU1j9KnOzeSgZGuzJQuerrPRlntjfkgCJHylnfWMdgbUV7NSnFZFSzQ5ZHMVtHAJiO0uIA7GxJy7affmIpIx91eHwldjqw4qlOtemVRXaCcgw21otSldb6FRxB1j6EWDPkV6NYKVGfzU12sDQK62rH6KbN1wW1xYt2AVDHN0idp6v8APzIMBKB+w5wODr2WekMswfBkUiUlGhwMwBqIhIJ9TClYm9cxil5K/K10bVmsAQVbHM04W2NgAv64ZvpzMRPHnbZ+SZZ7OIHd1kjmBCNQAdv4AMRwIBwMfvxzMzP/xAAYEAEBAQEBAAAAAAAAAAAAAAABEQAhMf/aAAgBAQABPyHjjVN7XKbn3Th4K05NVC391vSoWjPV1IvyIyV4wyugrs9zM+eISqfxvESoYgeNPdY/ubiqDndX0q6AIFZ4buEIM4e977cFJmVscRaQSDgu8az/2gAMAwEAAgADAAAAEJP4Pf/EABwRAQACAQUAAAAAAAAAAAAAAAERIQAxQVHB8P/aAAgBAwEBPxA5pxdb1xyLwS4XPqYyJCt4RlFUKhJowokBKQVQeFtN29c9l3n/xAAdEQEBAAIBBQAAAAAAAAAAAAABEQAhQWGRocHw/9oACAECAQE/EDLYmg0GEua9AgEgFSwVQ+QTkRAC4FKiUUMY6oaHg0az4j10O2f/xAAYEAEBAQEBAAAAAAAAAAAAAAABABEhQf/aAAgBAQABPxDSe6H4ZaNjBQqTwnIF8vsuYTtZZGu1uU9cSvI0aHgblnyASQtAgiZ4R4IUWhqE4tuy46E2OyAgJJy+7x44RJKEap5Yj4cl9W6ewOqh/9k=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;programming language person sql&quot;
        title=&quot;programming language person sql&quot;
        src=&quot;/static/db21a2d446ea3a8e4beda6a3f6c76d2c/62aaf/programming-language-person-sql.jpg&quot;
        srcset=&quot;/static/db21a2d446ea3a8e4beda6a3f6c76d2c/953fe/programming-language-person-sql.jpg 500w,
/static/db21a2d446ea3a8e4beda6a3f6c76d2c/0a251/programming-language-person-sql.jpg 1000w,
/static/db21a2d446ea3a8e4beda6a3f6c76d2c/62aaf/programming-language-person-sql.jpg 1280w&quot;
        sizes=&quot;(max-width: 1280px) 100vw, 1280px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;h2 id=&quot;Mostly-Web&quot; style=&quot;position:relative;&quot;&gt;Mostly Web&lt;a href=&quot;#Mostly-Web&quot; aria-label=&quot;Mostly Web permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;h3 id=&quot;HTML&quot; style=&quot;position:relative;&quot;&gt;HTML&lt;a href=&quot;#HTML&quot; aria-label=&quot;HTML permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Name: Hollis (Ollie)&lt;/li&gt;
&lt;li&gt;Age: Structured/Organized (25)&lt;/li&gt;
&lt;li&gt;Personality: Logical, systematic, and foundational&lt;/li&gt;
&lt;li&gt;Appearance: Clean-cut, minimalist attire (button-down, slacks)&lt;/li&gt;
&lt;li&gt;Interests: Web development, structure, accessibility&lt;/li&gt;
&lt;li&gt;Skills: Master of semantic markup, expert in document flow&lt;/li&gt;
&lt;li&gt;Quirks: Loves order, hates ambiguity&lt;/li&gt;
&lt;li&gt;Favorite hangouts: Web development forums, W3C meetups&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Hollis is the meticulous, detail-oriented web architect. Always building solid foundations.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;HTML: structural, logical, foundational&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 1280px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/3f0772693c92c6cc02f68c64f7aedca2/62aaf/programming-language-person-html.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 100%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDAAEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQH/2wBDAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQH/wgARCAAUABQDASIAAhEBAxEB/8QAGQABAAMBAQAAAAAAAAAAAAAAAAIFBgcI/8QAFwEAAwEAAAAAAAAAAAAAAAAAAQMEBf/aAAwDAQACEAMQAAAB4ldcxv25mVQIv8X6AZRIAf/EABsQAAIDAQEBAAAAAAAAAAAAAAQFAgMGBwET/9oACAEBAAEFAisFlrs+s541d3mcxs9PRuZAumhlQC2l/wBErj9pE35S2RWn0hhnj3//xAAjEQACAQQBBAMBAAAAAAAAAAABAgMEERIhBQATIkEjMTNR/9oACAEDAQE/AafiPjnjp+QeR5o5EyrYt0pWSGZNzeffVY5ImyZv1sLulmiowscavUzFwiByI5VBbFcjj2tbvr66ofLkOKDbBkgBB2CDUU4IIPq2rfzqoJE8wGgJHAA0AMvXX//EACMRAQACAgEEAQUAAAAAAAAAAAECAwQREgAFITIiIzFDUWH/2gAIAQIBAT8Buz3lWzojqq2L9Gey6LGdb6ajKqXOM46I+u/WXiWVNlJjXj8eTrd1K634/L1nfHt/dGPxSnJRPCJRcia+yPk/vWJGLi46xFaa1UFVibV/fX//xAAlEAACAgEDAwUBAQAAAAAAAAABAwIEEQUSEwAUIgYVISMyJDH/2gAIAQEABj8C031L6fuMoV7DaRYmEveKCrNvlTGAp8NkcsbiyhxrsWuJEuacAM9DTHaovRl0tAqajf7vioTtOdYtxlqKxVql9dNZVVy2kmAhVw1jAGNMryMP1CNKyakLcL1li3RjBbQxLDOJmlnNyLlL/YzGDtx16Y0i/dBRo863ecQguKrq7btShcsWET7dzHRFPcvJUmyPNgiCOn6nPUJX7CKkoWZ0Yzkuu5pbKyk32cHMXwM0r44OVs3La8/gjsaGpKrMghqgz2+RK51k8E/vpTcDKvxFgm1v3cpjIQMYQruIiodilxQjK60mynJU5lIO3M8mc8YBYc/nx6Uqx9yHe4ciGGUkS/jsz28JJXt3ASwAPkdamlNyzVTWsyrqVWdNaxCABHjk/Plj4wNoiABjr//EABoQAQEBAQADAAAAAAAAAAAAAAERIQAxQXH/2gAIAQEAAT8hsfqkU+rnZuH4eRdkEF0TdAJhsUl1RYOWoYxc79mRl0Hu5QmuRMhCEAbahwhJpbf4FTc6pURgRLBc9utIri/wsxy4Mx1QSc0qbi+4fl/lE//aAAwDAQACAAMAAAAQTCcD/8QAGBEBAQEBAQAAAAAAAAAAAAAAAREAMSH/2gAIAQMBAT8QBig8Wmo+AQhLMFsscIgAWkghoedICO5sVQKAkRAJCZmVECAgAQAAACAE5v/EABkRAQADAQEAAAAAAAAAAAAAAAEAESExYf/aAAgBAgEBPxBBqDUMmNorAalQZNiSkpyKpaAN3tg5PTCKBCigoUiURjLKQgaAqnVVV1bn/8QAGBABAQEBAQAAAAAAAAAAAAAAAREAECH/2gAIAQEAAT8QMmUxiBE7DYriMih/1tjOI/8ANeGK2QpAaycb+W8lv07yvIBuIgETaG0/wAivcsmwfjeUQkIH1+U7XJIprcif1wrNg6dhFGn4ynMZHcAVWf/Z&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;programming language person html&quot;
        title=&quot;programming language person html&quot;
        src=&quot;/static/3f0772693c92c6cc02f68c64f7aedca2/62aaf/programming-language-person-html.jpg&quot;
        srcset=&quot;/static/3f0772693c92c6cc02f68c64f7aedca2/953fe/programming-language-person-html.jpg 500w,
/static/3f0772693c92c6cc02f68c64f7aedca2/0a251/programming-language-person-html.jpg 1000w,
/static/3f0772693c92c6cc02f68c64f7aedca2/62aaf/programming-language-person-html.jpg 1280w&quot;
        sizes=&quot;(max-width: 1280px) 100vw, 1280px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;h3 id=&quot;CSS&quot; style=&quot;position:relative;&quot;&gt;CSS&lt;a href=&quot;#CSS&quot; aria-label=&quot;CSS permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Name: Cassie (Stylista)&lt;/li&gt;
&lt;li&gt;Age: Fashionable/Creative (28)&lt;/li&gt;
&lt;li&gt;Personality: Artistic, expressive, and versatile&lt;/li&gt;
&lt;li&gt;Appearance: Trendy, stylish attire (designer clothing, bold accessories)&lt;/li&gt;
&lt;li&gt;Interests: Web design, visual effects, user experience&lt;/li&gt;
&lt;li&gt;Skills: Expert in layout, master of color theory&lt;/li&gt;
&lt;li&gt;Quirks: Loves aesthetics, hates bland designs&lt;/li&gt;
&lt;li&gt;Favorite hangouts: Design conferences, web design communities&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Cassie is the creative, visually-oriented web designer. Always styling and refining.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;CSS: artistic, expressive, visual&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 1280px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/fe2b4978026be6b4cc7227ce11ca009a/62aaf/programming-language-person-css.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 100%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDAAEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQH/2wBDAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQH/wgARCAAUABQDASIAAhEBAxEB/8QAGQABAQEAAwAAAAAAAAAAAAAAAAYFBAcI/8QAGAEBAAMBAAAAAAAAAAAAAAAABgADBwj/2gAMAwEAAhADEAAAAfHOVytUszmHShpmlHVFkqgP6I//xAAZEAEBAQEBAQAAAAAAAAAAAAAEAwUCBiP/2gAIAQEAAQUCzkT6yy6jM1PbFppJk2ZJbC9DnJ1dCS4fCZ1V6XoGhBX/xAAhEQADAQABBAIDAAAAAAAAAAACAwQBBQAGEhMRIQcjJP/aAAgBAwEBPwHjOSOokcfx1kz+UjtU2diVTQ0Mq9abIwx3n/MuLRJYbvq27xltzJSFPVXasNVLqLK+X2prNKnQLJwJ/wBY3RnROCEDp4W4hK1pVn61LWA4AsIoqq6YyKSlcElAUTbqHhR8Uh7warxYLvDMD24WH45g/PxnX4/7c7e5Lsvtu/keB4a+6vjEvrtt4uGqul7NImOooeg3Oae/ZsYZGW/e7vX/xAAfEQADAQADAAMBAQAAAAAAAAABAgMEBRITABEhFDH/2gAIAQIBAT8B2xy8bPdt359M8NJUpp7Pp5AwRnbPc+Cq719Jn2aUlcZk9sxW4LdYcxvlGSZsvF/zCaeHcir+JAMi1X0d6t0I7VYlqH7csxP2dsZaU46GiU7xtOyWjZFrKqNOJZKTcMjqT+lWBBP+/OY27M3Kbc+bXpz543aUYQvWUZTTqqTlJGVJoq/iooCqPwD6+f/EACQQAAICAgICAgIDAAAAAAAAAAIDAQQFEhETFCIAIRUjMUFi/9oACAEBAAY/AksxuEB9qWz4RlTlnTUepCW1cS428hbHIiNi19bw61DYMOI3WAsJeJtzdUc+IuqNOQkCaou2Hl6nqRTvA2kEMtXJevxjqt/DJpMLmlD2pxxnX0GOzxSHsASdDde32kYjXZXWU4+/jq+Rx+HxjcgdKqOs28pZN6bJLrgpn60Y9gNr/kXaySupPUzVkgvEZKzZxTHZC5lINePm3Xb0JSCVWEMkdqE1+4XGnUIOA5k44ldysdNxTStOpcVIk0rGqXSK4PSZPURjWZ4/XIDAiAjELUqSAPBamYhjOJIMe66FnjfiLQuHjuGB2UZqOCCeIp2A0RLKzEypCwBEDfZ4jtVcTETChjr/AKEvuYL40euGkXDGNdyTWsKPYzmJEeZ/yIx9fx8//8QAGBABAQEBAQAAAAAAAAAAAAAAAREAITH/2gAIAQEAAT8hGZq8UqQSeC/ZET6V87B0hStAXmiPeP8AC+lPPS126VUl0hasovFxvUAOmTEjQQxd1uPXpcaoLt71dWSToFANtdb8avBuf69FyajeIjEp9IOKCZ//2gAMAwEAAgADAAAAEAg3wP/EABgRAQADAQAAAAAAAAAAAAAAAAEAESFR/9oACAEDAQE/EAp6JhUBlwq1IcBl0V+a1VQrbzg9qZcrk5gepZAQOXra7bZ//8QAGBEBAQEBAQAAAAAAAAAAAAAAAREAIUH/2gAIAQIBAT8QQLduQ6TpNBz417Uor8xvpWR17SmZxIo5eBe4ngyViDWDcghN/8QAFxABAQEBAAAAAAAAAAAAAAAAAQARMf/aAAgBAQABPxAlWevJ9q0a5WHpqp6/ZtUpdM9Dyenm20Qu3IxbXZLpBO9OiA2Bz8yqSsYnhEWFdrD6jTahYii82eFc+iQXKm6uqScq0aA59ISEsAUXV//Z&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;programming language person css&quot;
        title=&quot;programming language person css&quot;
        src=&quot;/static/fe2b4978026be6b4cc7227ce11ca009a/62aaf/programming-language-person-css.jpg&quot;
        srcset=&quot;/static/fe2b4978026be6b4cc7227ce11ca009a/953fe/programming-language-person-css.jpg 500w,
/static/fe2b4978026be6b4cc7227ce11ca009a/0a251/programming-language-person-css.jpg 1000w,
/static/fe2b4978026be6b4cc7227ce11ca009a/62aaf/programming-language-person-css.jpg 1280w&quot;
        sizes=&quot;(max-width: 1280px) 100vw, 1280px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;h3 id=&quot;PHP&quot; style=&quot;position:relative;&quot;&gt;PHP&lt;a href=&quot;#PHP&quot; aria-label=&quot;PHP permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Name: Pete (PHPster)&lt;/li&gt;
&lt;li&gt;Age: Seasoned/Pragmatic (35)&lt;/li&gt;
&lt;li&gt;Personality: Dynamic, flexible, and server-side savvy&lt;/li&gt;
&lt;li&gt;Appearance: Comfortable, practical attire (t-shirt, jeans)&lt;/li&gt;
&lt;li&gt;Interests: Web development, server-side scripting, database integration&lt;/li&gt;
&lt;li&gt;Skills: Master of dynamic content, expert in database interactions&lt;/li&gt;
&lt;li&gt;Quirks: Loves flexibility, hates unnecessary complexity&lt;/li&gt;
&lt;li&gt;Favorite hangouts: Web development forums, LAMP meetups&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Pete is the experienced, pragmatic web developer. Always dynamically solving problems.&lt;/p&gt;
&lt;p&gt;This personification reflects their characteristics, strengths, and typical use cases:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;PHP: dynamic, flexible, server-side focused&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 1280px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/9dfed6dc1bf980e3bd4fd10ed39a1ee8/62aaf/programming-language-person-php.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 100%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDAAEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQH/2wBDAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQH/wgARCAAUABQDASIAAhEBAxEB/8QAGAABAQEBAQAAAAAAAAAAAAAAAAgHBAb/xAAXAQADAQAAAAAAAAAAAAAAAAACAwQF/9oADAMBAAIQAxAAAAHG/A81noGdE5jtuWEjOqxsMH//xAAcEAACAgMBAQAAAAAAAAAAAAAFBgIEAQMHABH/2gAIAQEAAQUCJakuAQQAqjmwfsT8eZLldaug7y7FXc1kwUYOdVqBBW6QSzOzD6Yh/8QAHhEAAwACAwEBAQAAAAAAAAAAAQIDBBEAEhMFMSH/2gAIAQMBAT8BxsLEbDtJpZ5W65Pal8tK+GR3jjzAqlwfL3nTykJKerv6P36ryvxqJRlT5tnRTpXnK3mwGtMhHqCp/VPo+x/ex5PHx6Y+e1IRo2PtscvNGMGqZPRolgTNqOzO5TRZmLNsk8REKISqklV2SBv8HP/EAB8RAAMAAgMBAAMAAAAAAAAAAAECAwQRABIhEwUiQf/aAAgBAgEBPwG9rrkzZaY36ieljGsxWIU1YlDIg0EqTLsXKluoVSuzyOVi0lN3z5ozKGZHpLujHW1bfzPYHw7Rff55xq1nk/jROtJjIp1uEdlF1mtkmtgpH0WaKqIH2FVVVdADlaOtKBXcAUcABiAAGOgAD4Of/8QAJRAAAgICAQQCAgMAAAAAAAAAAgMBBAUSEwAGESIUIRUjMlFi/9oACAEBAAY/AhoV8BlKfc9BmEo38m9rHY289/F89tGum65l8iSR2AGtUXARHsSuqFZyStDnBs0yIGCH41iTJi2uSS5JgOBH8wL1GfBRBjrN5HdORxWFy9PJW6bKKLNhsCtBQKzOSR9ML22gfIz4g4120HCWO35fjqZZt15LX5Icwx4mhVB1pgsUyLFOFNsQUMiQeDICVbEJdKv1cRhs53R2sdlC7dR7pPJ2cj5eDrU2xqstP4f2fHdUcCuGIUELKOrmSheJAL0LeCZy2PpSiNeOUlXsPF4kBge0tESKZkvEDr1hsxlcdTy1hlXhYrJC2zVOK8K4z4ZdHG7dQtlySWzkiPbUQEdaVSth1DZlnBizvJRLI5I3433LMQX+h1n7n+56G9fKXWjjiY76E2wj9KybrEbshQAEnPsUDG3mfvr/xAAZEAEAAwEBAAAAAAAAAAAAAAABABEhMUH/2gAIAQEAAT8hJsoIu/UBWKYIkhCGxv2GwPEo13L8/wA/vCRbLASpC8rqV/0DWjTlhQn1ByBKc+wILWMM7honm4YmU1EzmAgjmCNgB0RvqKqt5BXfWLwACrGrV//aAAwDAQACAAMAAAAQaBjA/8QAFxEBAQEBAAAAAAAAAAAAAAAAAREhAP/aAAgBAwEBPxCvYLpBOd/Abrmk+OsaEJkBoaF5XQu7RxJmbRZYCEiSsqoqrqur3//EABgRAQEAAwAAAAAAAAAAAAAAAAERACEx/9oACAECAQE/EKjSg32XCRA4Ywn5QCDIqCASDYewTSbgEbFgMBVcdIAAAAAAABzP/8QAGBABAQEBAQAAAAAAAAAAAAAAAQARECH/2gAIAQEAAT8QQtF8OAX3G7xPHH/XYjWYpX9f9URmKdGJzm3JXA8A4qli8Mqr8zilMrd9UwkdhQqxlxqxWox6Ri9LhORUFzsFO+iihgqguSiepD9x81f/2Q==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;programming language person php&quot;
        title=&quot;programming language person php&quot;
        src=&quot;/static/9dfed6dc1bf980e3bd4fd10ed39a1ee8/62aaf/programming-language-person-php.jpg&quot;
        srcset=&quot;/static/9dfed6dc1bf980e3bd4fd10ed39a1ee8/953fe/programming-language-person-php.jpg 500w,
/static/9dfed6dc1bf980e3bd4fd10ed39a1ee8/0a251/programming-language-person-php.jpg 1000w,
/static/9dfed6dc1bf980e3bd4fd10ed39a1ee8/62aaf/programming-language-person-php.jpg 1280w&quot;
        sizes=&quot;(max-width: 1280px) 100vw, 1280px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;h2 id=&quot;Low-Level&quot; style=&quot;position:relative;&quot;&gt;Low Level&lt;a href=&quot;#Low-Level&quot; aria-label=&quot;Low Level permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;h3 id=&quot;C-2&quot; style=&quot;position:relative;&quot;&gt;C&lt;a href=&quot;#C-2&quot; aria-label=&quot;C 2 permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Name: Charlie (Cee)&lt;/li&gt;
&lt;li&gt;Age: Grizzled veteran (50s)&lt;/li&gt;
&lt;li&gt;Personality: Pragmatic, efficient, and no-nonsense&lt;/li&gt;
&lt;li&gt;Appearance: Weathered denim, leather jacket&lt;/li&gt;
&lt;li&gt;Interests: Operating systems, embedded systems, performance optimization&lt;/li&gt;
&lt;li&gt;Skills: Master of memory management, expert coder&lt;/li&gt;
&lt;li&gt;Quirks: Loves pointers, hates unnecessary abstraction&lt;/li&gt;
&lt;li&gt;Favorite hangouts: Basement labs, hackathons&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Charlie is the seasoned, battle-hardened programmer. Always focusing on raw performance.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;C: performance-oriented, low-level, embedded systems&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 1280px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/55a88147ca6f001bad192e784dd4c753/62aaf/programming-language-person-c.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 100%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDAAEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQH/2wBDAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQH/wgARCAAUABQDASIAAhEBAxEB/8QAGgABAAIDAQAAAAAAAAAAAAAAAAcIAgMFCf/EABcBAAMBAAAAAAAAAAAAAAAAAAMEBgX/2gAMAwEAAhADEAAAAaIS9GeYDRc5hle4W8h9DzuF4h//xAAdEAACAgIDAQAAAAAAAAAAAAADBAEFAgYABxQl/9oACAEBAAEFAmFkjtXumU+FoymskRfOB8ZhuSvV9w9nq31mPIIc9hnPls//xAAiEQACAQMEAgMAAAAAAAAAAAABAwIEERIAEyExBUFSYYH/2gAIAQMBAT8Bh4lko1GLHoc6SZ0U9wsXTRXTxm3JcJFYp3C0AJyutl5+zpVQBABm1neWV2Jmb5fIqJP6eOtVURPPMCV6iAOQy4DW2HPoa7uTySSST2STck/Z1//EACERAAMAAQQCAwEAAAAAAAAAAAECAyEABBESE0EFUWGh/9oACAECAQE/AR8nPtHySlWU/Om6RZoj7g0sVn1d1FGtIcuSmHXhSdOvVyJd2njoRHcgccKcAXUfwetK7oeqOyKJxIVWKqCduhOBjJyfs51tsbfbgYAhIAegBNeAPwa//8QAIxAAAgICAgMAAgMAAAAAAAAAAgMBBAUSERMAFCEGFTEyYv/aAAgBAQAGPwI46L2FyGTnFuo0sDZtTj6jWZO3SuFkZsrfkGRovtpj7gmJb7bBAgFnBUfynK3bFMKZ3H2tkAyHlIw9Smnqyv3hKp5OCVHBSDI++dJ4t9/+3DxsqX8BrE6kHsDxMSqZj/MjPPHlG6tLrkUsTYyrFrAnhX9W6w/VNriW+W91kXUVEmy+Vz2S/UtV0cmxEUsh+SYmmpbLtivFms6bVpSBqBcaDkS+GUTgKsCcGSt4BO3ivfQpr6Kf1vdctYg2N9N7ltMGm2Zar2e+BZHwpgvpfTLLWbmhMr2KKauqkwFWWYijaY5ISuYhxWB7YktwXMyCgWvUBBkwtjpyDX95U8eDoZR1JZCaKiZiWSES+f5OfsSMwMja2afPRVn5wuNmphzC0VALGTawzLQBiSIimOZmfP/EABkQAQEBAQEBAAAAAAAAAAAAAAERADEhQf/aAAgBAQABPyEVwjGRRwlnYyn0JTB1aOnFNQaURuvY3uoNOSAk8KX3UFBpmLxJyJ6EwilfaAYuhK1vMu/aFMElgs82QxLstuUteTj+i4ZPp4rop2kGWReHB7//2gAMAwEAAgADAAAAEEQAPv/EABgRAQEBAQEAAAAAAAAAAAAAAAERACFB/9oACAEDAQE/EH5sYpwEI9XtADWcIZ2jNbCiErjrVdACUUQqioPBVC6CAAwFAg1UVVVVVa7/xAAZEQEAAwEBAAAAAAAAAAAAAAABABEhMUH/2gAIAQIBAT8QWfIxAwVlVY0GnTEUqmChAtCsuPYKdSaHxwOoF6LdiVCBCgAAwAADAAMJ/8QAFhABAQEAAAAAAAAAAAAAAAAAAQAR/9oACAEBAAE/EN3XdJ0++eNXK67czfT0Hc3wNI2Dt9i3z7sYo3uTFaBBrLJAFZNyWi0jI52eYlf3I8aoTxP71ziPh4ZKZNaCXkn89UsLuZRls4UeiSM//9k=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;programming language person c&quot;
        title=&quot;programming language person c&quot;
        src=&quot;/static/55a88147ca6f001bad192e784dd4c753/62aaf/programming-language-person-c.jpg&quot;
        srcset=&quot;/static/55a88147ca6f001bad192e784dd4c753/953fe/programming-language-person-c.jpg 500w,
/static/55a88147ca6f001bad192e784dd4c753/0a251/programming-language-person-c.jpg 1000w,
/static/55a88147ca6f001bad192e784dd4c753/62aaf/programming-language-person-c.jpg 1280w&quot;
        sizes=&quot;(max-width: 1280px) 100vw, 1280px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;h3 id=&quot;Rust&quot; style=&quot;position:relative;&quot;&gt;Rust&lt;a href=&quot;#Rust&quot; aria-label=&quot;Rust permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Name: Ruby (Rusty)&lt;/li&gt;
&lt;li&gt;Age: Environmentalist/Outdoor enthusiast (late 20s)&lt;/li&gt;
&lt;li&gt;Personality: Safety-conscious, resourceful, and meticulous&lt;/li&gt;
&lt;li&gt;Appearance: Eco-friendly attire (recycled materials, outdoor gear)&lt;/li&gt;
&lt;li&gt;Interests: Systems programming, security, sustainable coding&lt;/li&gt;
&lt;li&gt;Skills: Memory safety expert, performance optimizer&lt;/li&gt;
&lt;li&gt;Quirks: Loves borrow checker, hates data races&lt;/li&gt;
&lt;li&gt;Favorite hangouts: Open-source communities, hacking meetups&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Ruby is the diligent, earthy systems programmer. Always prioritizing safety and efficiency.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Rust: safety-focused, systems programming, performance optimization&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 1280px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/07eba4b94c5e59e9b61ebdb9e51d53f4/62aaf/programming-language-person-rust.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 100%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDAAEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQH/2wBDAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQH/wgARCAAUABQDASIAAhEBAxEB/8QAFwABAQEBAAAAAAAAAAAAAAAAAAgHBv/EABgBAQADAQAAAAAAAAAAAAAAAAYBAgMF/9oADAMBAAIQAxAAAAGK+TtnJOdHGtAbXg+gQaxywMi//8QAGxAAAgMAAwAAAAAAAAAAAAAABAUCAwYBBxT/2gAIAQEAAQUCrxa82o3GUe+nqdJdxotxnHEMoSaOWrVhnBjb1nOQu9atGdtk7Y//xAAiEQADAAEDBAMBAAAAAAAAAAABAgMEABEhBRITQQYkMTL/2gAIAQMBAT8B+P8AQsys3lmvVLZdhDAOVVE8BEwyGrF3crkF9husyvifliCmpYC2lO0ZVtKiK86rfp/bRWAIdPtfy36p9qQfesAB0gXAcq6lSw7iD42XcE77ccceuNYuLjLjY6rjwVVhFVVZTAUCagAALsAAAAB+a//EACIRAAMAAQQCAgMAAAAAAAAAAAECAxEEBRIhACITFDEzQf/aAAgBAgEBPwHe9902jH2dLP5Z6OYtqU06lvnUsy1RAq8WpATLYy37Jg8eSuX3iMnadrSjVGK0jSOtFJODhpuPrdOhHFx/GBHm7eltVNPRDKrFF9UJ5BslR1nPecfnvyl7tSjG1SzUdmJo5LMz5Zic5JYkkk9kkk+f/8QAJxAAAgIBAwQCAQUAAAAAAAAAAgMBBAUREhMAFCEiBiMVJDEyQVH/2gAIAQEABj8CsWavC+soOV0JMLMFAn7wDD/f6iAhZPbJFkgBlByYBhq9FPyLFoyFQ7T1ueL1bIVrDq7krVwjywSXIsp+syCFPfE69FuyhE1Zcb4K+kmqbtE5U6IHVbIgxmVnocQQzMeY6pYtfwarSg2Lgm49ePvHZsUK7wZjHVcdTU5irt5UCQJcU6bTjUtOrScu2xj6N7CXQdNsMhW/GvTJtoJqvshG1yD5orVmcMSvjSYOJu6V2LBYLvmanfZlcz2llz3T3CygJcfIoKTaqAdyu3ykvfxsCop2NwjYqvK3Xns31yVZfK2OauaVyts5DWMysIFQ6fWAeem0G0sOtHY7y/SOtmZiIzBEWUtZD21KfYdpf1M7dY6QdgpsNlUxLG/y2g9wAOi+MIEAERGBCPEf71//xAAYEAEBAQEBAAAAAAAAAAAAAAABESEAMf/aAAgBAQABPyFXJI7lbpfJh7zVeLtn9tZyYUyMSdLph5xYRnDhHezcTuMh5c1YGrmWGXEfSf6brI5YnIJ7iQ8o8EG5g4EhWGFOPZOCIp7UuBxcoHxdqTSpX//aAAwDAQACAAMAAAAQXwc+/8QAFxEBAQEBAAAAAAAAAAAAAAAAASEAEf/aAAgBAwEBPxAvo0gDC68XHrix633+nTarAOhHuWIIQqRNQkpMX84LY8HAAAAAN//EABgRAQEBAQEAAAAAAAAAAAAAAAERIQAx/9oACAECAQE/EEQXcsfLA2FXT2ABcxGFQ607BiqWCqNBNLZzVHtd6tPM5CTWnKAVV//EABgQAQEAAwAAAAAAAAAAAAAAAAEAEBEh/9oACAEBAAE/EGdWuVjgzddy6ajqt4emF5WxU+gm42h0R9w1IqTVeLUJsR40k7sfAK35lfNwrJdbWm2LDAPmoPoyC+UZmbGX0ny9YcpkGcUKYqEHZN//2Q==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;programming language person rust&quot;
        title=&quot;programming language person rust&quot;
        src=&quot;/static/07eba4b94c5e59e9b61ebdb9e51d53f4/62aaf/programming-language-person-rust.jpg&quot;
        srcset=&quot;/static/07eba4b94c5e59e9b61ebdb9e51d53f4/953fe/programming-language-person-rust.jpg 500w,
/static/07eba4b94c5e59e9b61ebdb9e51d53f4/0a251/programming-language-person-rust.jpg 1000w,
/static/07eba4b94c5e59e9b61ebdb9e51d53f4/62aaf/programming-language-person-rust.jpg 1280w&quot;
        sizes=&quot;(max-width: 1280px) 100vw, 1280px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;h3 id=&quot;Go&quot; style=&quot;position:relative;&quot;&gt;Go&lt;a href=&quot;#Go&quot; aria-label=&quot;Go permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Name: Gideon (Goody)&lt;/li&gt;
&lt;li&gt;Age: Efficient productivity enthusiast (mid-30s)&lt;/li&gt;
&lt;li&gt;Personality: Simple, pragmatic, and concurrent&lt;/li&gt;
&lt;li&gt;Appearance: Minimalist attire (clean lines, monochromatic)&lt;/li&gt;
&lt;li&gt;Interests: Network programming, cloud computing, scalability&lt;/li&gt;
&lt;li&gt;Skills: Goroutine master, performance optimizer&lt;/li&gt;
&lt;li&gt;Quirks: Loves simplicity, hates unnecessary complexity&lt;/li&gt;
&lt;li&gt;Favorite hangouts: Cloud conferences, DevOps meetups&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Gideon is the streamlined, productivity-focused developer. Always optimizing for efficiency.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Go: efficient, concurrent, scalable, cloud computing&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 1280px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/07a4c73c3f1f8f46cf276c2322e91842/62aaf/programming-language-person-go.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 100%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDAAEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQH/2wBDAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQH/wgARCAAUABQDASIAAhEBAxEB/8QAGAABAAMBAAAAAAAAAAAAAAAAAAQGBwj/xAAXAQADAQAAAAAAAAAAAAAAAAADBAUC/9oADAMBAAIQAxAAAAHl/Tc/gJntTFWRxroVFO1hTgf/xAAbEAADAQEAAwAAAAAAAAAAAAAEBQYDBwACEf/aAAgBAQABBQJhJJxmQeKfFqcOmDJBdqmE+7n/AFlaBplQkeC12rFP1b6P0zlHMUFhF//EACMRAQEAAQMDBAMAAAAAAAAAAAECAwURIQAEEgYTIlExQpL/2gAIAQMBAT8B07vNOxTGWvLN3BdfBlnJBMpkglfbr4ZMbv5ILL5hx1frj05F1DnxDNMo9xpYjLsiVqUvDv8AmZfuTk68ZL4A2L22D9qmq/quX7eXnrJpOl1d1Wm6fVVTVVXZ9u1VLu1S491XlXlev//EACYRAQABAgQEBwAAAAAAAAAAAAECBCEAAxESBSIxMgYUJEFRgZH/2gAIAQIBAT8B4hQ1mbvp2WzL2R0masdd0ZQl2ky8JHbc3WW5l+DeKShCTOesoxeXKrGNw6ekbfbiMpLdW3ur2mkfwUPgUMeerY8sayqjGNiJUZoAaAAT0AOhj//EACYQAAICAgIBBAEFAAAAAAAAAAIDAQQFERITABQhIiMGFTJBQkP/2gAIAQEABj8CqWOyFVXFACMbPtNshCxRwg5Ls38IHcn/AJ8p15dwtilVWKKJ3KeTFVuqi9XrO6rnNORQp9d9Q9EwfsAlTJiyeJaYp9rG1iniwAdbSopWYxIMGC1sC/qUe0/x5k6i237mWu4xCMVXsuRCcPuzLKD1GuspzrJK5V7NiZHo9PBKGRZ1xXK3lMhosIobA1bYeo/UMmtpwgGGqUoQkOZWwhcmw4b+w5iBx5Vcqi8Q4yqu4/L2K9Vs3Ylktiqmsl8RR4kuUy4+8ylpGI7GPMPjqmEwH4/Vr58LMDhK99bGvsoFRk12RyOSdIiMfWkDBAFMl178uY0TM6qK9KRFhSRGTcYD2mwvbkTHPYw59vcpiND8fKWdy9rM+tfdyyCirdWlAqp5GxVQILKqyY0pQ7+c7nc+f//EABkQAQEBAQEBAAAAAAAAAAAAAAERACFBMf/aAAgBAQABPyFMffCKChP1gET3mxsiQKMsq/3uTFWcCBqJMVOk2jL4pHa06VISfGpNCgqZq2xljArOoG28vGWJMH+l5hN0mAMMPQy7AphVcsfsw8D7Xglm/9oADAMBAAIAAwAAABAc2L3/xAAZEQEBAAMBAAAAAAAAAAAAAAABEQAhMUH/2gAIAQMBAT8QRrYCRaqPqJ4uMn/53DoBDJEwUFZaAiwIEgT5lAOU7utxZVykoVV3n//EABkRAQEBAQEBAAAAAAAAAAAAAAERIQAxQf/aAAgBAgEBPxB0hAWlN3SMFBgl6VwSrCkW2QiOg4oi+i8roNZq1cX4AKcRQBNXTGAAAMAO/8QAFxABAQEBAAAAAAAAAAAAAAAAAQARQf/aAAgBAQABPxA8nbmipz5POsyFWpOWoHAfAKCZW4LaZeMEELIqy4WLp5yK9HS/vP2MSYvmB/SB5/pR+htxLmidbqNICtptjYHFeTYzK5n3GHHcBvaXFf/Z&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;programming language person go&quot;
        title=&quot;programming language person go&quot;
        src=&quot;/static/07a4c73c3f1f8f46cf276c2322e91842/62aaf/programming-language-person-go.jpg&quot;
        srcset=&quot;/static/07a4c73c3f1f8f46cf276c2322e91842/953fe/programming-language-person-go.jpg 500w,
/static/07a4c73c3f1f8f46cf276c2322e91842/0a251/programming-language-person-go.jpg 1000w,
/static/07a4c73c3f1f8f46cf276c2322e91842/62aaf/programming-language-person-go.jpg 1280w&quot;
        sizes=&quot;(max-width: 1280px) 100vw, 1280px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;h3 id=&quot;Zig&quot; style=&quot;position:relative;&quot;&gt;Zig&lt;a href=&quot;#Zig&quot; aria-label=&quot;Zig permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Name: Zara (Zee)&lt;/li&gt;
&lt;li&gt;Age: Rebellious/Nonconformist (early 20s)&lt;/li&gt;
&lt;li&gt;Personality: Fearless, experimental, and liberated&lt;/li&gt;
&lt;li&gt;Appearance: Counterculture attire (bold, eclectic)&lt;/li&gt;
&lt;li&gt;Interests: Systems programming, embedded systems, compiler design&lt;/li&gt;
&lt;li&gt;Skills: Master of compile-time evaluation, performance hacker&lt;/li&gt;
&lt;li&gt;Quirks: Loves generics, hates runtime overhead&lt;/li&gt;
&lt;li&gt;Favorite hangouts: Alternative tech communities, hackathons&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Zara is the bold, unconventional systems programmer. Always pushing boundaries.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Zig: experimental, liberated, compile-time evaluation, performance hacking&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 1280px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/7ddf427fad39bd7285ec2ec4ca9b406d/62aaf/programming-language-person-zig.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 100%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDAAEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQH/2wBDAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQH/wgARCAAUABQDASIAAhEBAxEB/8QAGAABAQEBAQAAAAAAAAAAAAAAAAgHBAn/xAAYAQEAAwEAAAAAAAAAAAAAAAAFAQIDBv/aAAwDAQACEAMQAAAB81dpziyqqxsx0xyvPQwWvFA3j//EABkQAAIDAQAAAAAAAAAAAAAAAAMEAQIFBv/aAAgBAQABBQLmsxaz/TKoaPMJBSoCD3LDE8zgZEsAqUbjCefTodJhxmZoT//EACIRAAMBAAIBAwUAAAAAAAAAAAECAwQFETEABgcSFBUhcf/aAAgBAwEBPwEbdm32lyfFST8RPRo5LkLrmFdrFc65vojJAEgdLwg/27a6xlF9zJMahGppPLJJRRfj72puCRih2chDktW7UyyQPo1aDrY1tZwzu3flugAB0HtVOPmErRAeRw9hXZQe77WPYB/fbAN/QD5HrLouM8QL2AE0AAo4AHS+B36//8QAIREAAwABAwUBAQAAAAAAAAAAAQIDBAUREgAGEyEiUTL/2gAIAQIBAT8Bx74uPq+Al9QrVNpTUsnBLu8qJNr8ePwK0+vGH9QLUOKroym+Lyfy9wdwRfyPyji102eNLdztKKHT2KzQbKoLMdh7Y+z1OEGfLo0ZM642Qqu00LqrQgpUMRuFIOxAOxHrpkTk3yv9fg/R1//EACgQAAICAAUEAQQDAAAAAAAAAAIDAQQABRESExQhIzEGIiQyM0FS0f/aAAgBAQAGPwJratmWmmq/o2cUV7LXdJuX49zBBqGSa1NkpKRGD7eh+PX5YptoyBWeZmEMiVhTg1VqRKN4Q251MslZ1Fwiasg25YgYnEDUsVqgb28inZRQzJ8s5C8jrt0tzWMXxlPCtFdfZS1RsIz+P5fmjFpRWuJqxWqVZr5guncsEwWWIERsN+stqZsFDIAx0EhnUsxpZWp6RQ+uhAWvuIug5azbygA7IOwtp8qyAwAQnyYsDXv2VoGy4VdLPhNcHMAwZ01ncOnvv/GGMQyRlC8uMImImORLROGFr3k5MYMi1/L1tjtiLu9SHdLH6EjA+WjViZ0ZySJDGsCQEJd53SU40H+oz6j/ADH/xAAaEAEBAQEBAQEAAAAAAAAAAAABESEAQTFx/9oACAEBAAE/IS4cofpB2yrs2djjymVKkBIryn72R77k+UmruyCE8Vy6E+28YHBlgKMIZj0ENdrOLvzDDtBirKtAGg4esfwx9nD9pFKjtfUas1eIpBkpunvqX9vf/9oADAMBAAIAAwAAABDY/wB//8QAGBEBAQEBAQAAAAAAAAAAAAAAAREAITH/2gAIAQMBAT8QjMs7kwbUivp4Jpz7m8wpgJry6W/QCKNRrvBA/MFBAAgFYBN//8QAFxEBAQEBAAAAAAAAAAAAAAAAAQARIf/aAAgBAgEBPxBtcMfFKIjp5GMNZOqyqTtluBSo7qiCQpZjiVCKTGWVKrKuqqat/8QAGRABAQEBAQEAAAAAAAAAAAAAAQARIRBB/9oACAEBAAE/EG4C9Q4VY0IHg4XxCCC6u+GAC0oMXUSCrhxPEnCzYZRcEGUeea5LToZNBXZe6/z6Xj5IK4BSXbLK721Hame2pL4sKAUHAgAfEIQqKJgrrqX3D//Z&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;programming language person zig&quot;
        title=&quot;programming language person zig&quot;
        src=&quot;/static/7ddf427fad39bd7285ec2ec4ca9b406d/62aaf/programming-language-person-zig.jpg&quot;
        srcset=&quot;/static/7ddf427fad39bd7285ec2ec4ca9b406d/953fe/programming-language-person-zig.jpg 500w,
/static/7ddf427fad39bd7285ec2ec4ca9b406d/0a251/programming-language-person-zig.jpg 1000w,
/static/7ddf427fad39bd7285ec2ec4ca9b406d/62aaf/programming-language-person-zig.jpg 1280w&quot;
        sizes=&quot;(max-width: 1280px) 100vw, 1280px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;h3 id=&quot;Assembly&quot; style=&quot;position:relative;&quot;&gt;Assembly&lt;a href=&quot;#Assembly&quot; aria-label=&quot;Assembly permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Name: Axel (The Tinkerer)&lt;/li&gt;
&lt;li&gt;Age: Grizzled/Veteran (50s)&lt;/li&gt;
&lt;li&gt;Personality: Low-level, practical, and resourceful&lt;/li&gt;
&lt;li&gt;Appearance: Wearable tech, utilitarian attire (overalls, goggles)&lt;/li&gt;
&lt;li&gt;Interests: Embedded systems, reverse engineering, optimization&lt;/li&gt;
&lt;li&gt;Skills: Expert in machine code, master of memory management&lt;/li&gt;
&lt;li&gt;Quirks: Loves efficiency, hates unnecessary abstraction&lt;/li&gt;
&lt;li&gt;Favorite hangouts: Hacker gatherings, embedded systems meetups&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Axel is the battle-hardened, hands-on embedded systems expert. Always optimizing performance.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Assembly: low-level, efficient, embedded systems-oriented&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 1280px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/d2197269c72707f7fca0e6583fa3e856/62aaf/programming-language-person-assembly.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 100%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDAAEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQH/2wBDAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQH/wgARCAAUABQDASIAAhEBAxEB/8QAGQABAAMBAQAAAAAAAAAAAAAAAAYHCAQF/8QAGAEBAAMBAAAAAAAAAAAAAAAABgMEBQf/2gAMAwEAAhADEAAAAcge1wzzJlpZOhdnlSyhqAu4WuRf/8QAGhAAAgMBAQAAAAAAAAAAAAAABAUCAwYUAf/aAAgBAQABBQJRnVU3E2VQLwlSWslIzxxg+TRAL2b494YT1qNDkdQ0BYUzsqs//8QAIhEAAgIBBQACAwAAAAAAAAAAAQIDBAUAERITIRYkMTJh/9oACAEDAQE/AaVntpWYONaPLYWk9+zMTa6bLsKoWCKCrFeB+wWXczMvAlu3qDyax1NL1Gpcs4rHwT2YUmlhS9gQkTuAWRBkcol4Kp8AtKJgP3G+q4D2cqjAMnx+9Jwb1exEkZH4nzmrIjK226lFIO6jUlicSOBNKByPgkcD8j+6/8QAIREAAgICAgIDAQAAAAAAAAAAAQIDBAUREiEAExQiMTL/2gAIAQIBAT8BaNpZK9mRJjj8vejpxQStXFmpHD7ve0sks1cfaLgzFFAUxhAjSlV8unJ1bditVy0s9eCVooZpVyPskRToO/wqYqcj+n44ER74jR8zyKmDpzIqrMMxV1KoCyDl6lbTj7DayOp77DuD0x3xU6JVSSq9kDf8jz//xAAiEAACAgICAgMBAQAAAAAAAAACAwEEBRIRExQiABUhJCP/2gAIAQEABj8Cv+bVpeStd+Mw4sfY+gWjYPrjq1026NewRsA6zlkUVCiR6wIo9bdenjxs4C2dtdSljkW1FF61R+rsFUPsY+1OM2j+AP50mpBh7FOoVovnWHrhoocqpjWrFxEwe1OQw1y4wyid5sOfPbt6RC4H5icFhlPVE5YrtZY1ps5V6Gxq5jtfIapDFnwIvMqcyQMR/sginHZZpVaFhORM0VbAnF2m2TJn2y8XzYc6rID0ssNqBXbtLF+pfjLueotK6MLpgZ4yrVYdakEV1EyuuGwouQP1It9YEmCBzIwGAx2UytTHGSqjQRfctjU9Q8wZhI/hdhRKxgU8esLgfz5awg+M6lWvrBM2EQdkfIIgaXkiQN3kSONueZgyEuR4GLsAUCM3XFAwquMDHACIRoodoABEIJm7ZgY3Yc/vz//EABgQAQEBAQEAAAAAAAAAAAAAAAERACEx/9oACAEBAAE/IQ5ijlgZLc0mRkPnzyKVauQvTro71g4157he49mWLFUmQi3WwnNFcCoUxTZgQw7PyIgL1J3s3pjX8ldV65RNWoZbd4k410zYw1XxJbayf//aAAwDAQACAAMAAAAQOCA+/8QAGREBAQEAAwAAAAAAAAAAAAAAASERADGB/9oACAEDAQE/EFRKUSF41zFr8iNaumo1rAIOMWaQHTOjimqEhc8QEHSAQ8Of/8QAGBEBAQEBAQAAAAAAAAAAAAAAAREAIUH/2gAIAQIBAT8QA9sIF9I1GZ4A4xgHjY2VUUSAF1Y84ggiUJWh5MwCpJaKoqvq7//EABgQAQEBAQEAAAAAAAAAAAAAAAEAESEQ/9oACAEBAAE/EFHSqjeVYoxbGoJcoa1nsPUk0DkC6hazWKRDEZp9YOAQwFfit9nECokfpHlMbNqZO/BwA8friC5URsyDO+R7iMF5WS7slnGUa6m1Tlf/2Q==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;programming language person assembly&quot;
        title=&quot;programming language person assembly&quot;
        src=&quot;/static/d2197269c72707f7fca0e6583fa3e856/62aaf/programming-language-person-assembly.jpg&quot;
        srcset=&quot;/static/d2197269c72707f7fca0e6583fa3e856/953fe/programming-language-person-assembly.jpg 500w,
/static/d2197269c72707f7fca0e6583fa3e856/0a251/programming-language-person-assembly.jpg 1000w,
/static/d2197269c72707f7fca0e6583fa3e856/62aaf/programming-language-person-assembly.jpg 1280w&quot;
        sizes=&quot;(max-width: 1280px) 100vw, 1280px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;h2 id=&quot;Scripting&quot; style=&quot;position:relative;&quot;&gt;Scripting&lt;a href=&quot;#Scripting&quot; aria-label=&quot;Scripting permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;h3 id=&quot;Bash&quot; style=&quot;position:relative;&quot;&gt;Bash&lt;a href=&quot;#Bash&quot; aria-label=&quot;Bash permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Name: Barney (Bashy)&lt;/li&gt;
&lt;li&gt;Age: Seasoned veteran (40s)&lt;/li&gt;
&lt;li&gt;Personality: Practical, straightforward, and reliable&lt;/li&gt;
&lt;li&gt;Appearance: Comfortable, worn-in attire (flannel, jeans)&lt;/li&gt;
&lt;li&gt;Interests: System administration, scripting, Unix philosophy&lt;/li&gt;
&lt;li&gt;Skills: Master of pipes, expert in command-line fu&lt;/li&gt;
&lt;li&gt;Quirks: Loves brevity, hates unnecessary complexity&lt;/li&gt;
&lt;li&gt;Favorite hangouts: Unix forums, command-line meetups&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Barney is the dependable, no-frills sysadmin. Always getting the job done.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Bash: reliable, practical, Unix-centric&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 1280px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/b6793ac34985a17be5298069aa61cbde/62aaf/programming-language-person-bash.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 100%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDAAEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQH/2wBDAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQH/wgARCAAUABQDASIAAhEBAxEB/8QAGAABAQEBAQAAAAAAAAAAAAAAAAYHCAX/xAAWAQEBAQAAAAAAAAAAAAAAAAAFBgH/2gAMAwEAAhADEAAAAeUb3A9/mX5FHGxeZ6MQzwBJUv8A/8QAGhAAAwEBAQEAAAAAAAAAAAAAAwQFBgIBFf/aAAgBAQABBQL4ucpMGMitkz0brTErScV3FE/QBsztLdqdgANumftFEm5qVPf/xAAhEQADAAICAgIDAAAAAAAAAAABAgMEEhETAAUhIjFSYf/aAAgBAwEBPwF5vkevrSeM+bldFWAx3jCaOaTcwfaXKzTH717fkrRsVnbQUZ4JQwgaetMKGMt4nJgxk+i7TLdA2KHlduPt+fPYu8h6wzZpktTkoxQnTqCc68c6gkL+oJ48xwKRk9AHdkUs7/ZmP1+WY8kn+nz/xAAiEQADAAEEAgIDAAAAAAAAAAABAgMEERIhIgATBTJRYWL/2gAIAQIBAT8BeaYOZjjJo8MdbS2rT2ZBc+tlS+op2LX9bEar1nkLNA7SmrZmMrMo+QjQBm0cSySH7Ht1qV5/nj8eTAo7rQB1baGVxuDBxq4IOoIY8sD9jyfMoCOTaUgJTSjKk5jYiDUcKi6Ko/QHn//EACUQAAICAQMEAgMBAAAAAAAAAAIDAQQFERITABQhIwYVIjEycf/aAAgBAQAGPwLsjxzsb3N48VXm6nhtG6UtcF1YALA7UBECbDTjelwMCYnb0OAoYxVhTeecpkd9AKk43HOMLtmlZKxLCsJdVgq1YkwWShh11xyrMYe/4/hadnDmzWg5tF9dho2jtklkIeZ/rxEh52hOyIiPqLtbGVL9hdK8i6VarSY2/UAONb7EAsFpKmHasVHEt0CRkB2mjrOZgceLFT9pVxoqZXOuUWbQuRdWnJtlzd/KthxVRWEGc4CUT+VzNck/HIyRIeGLCyp8KAKlesLOVHrPmhHJExpO2Y3xv16yVOUixeE91QzJsPMoaYe9ymLMx0j9BK/P+DpPyPFy7EZivaqIi5jruRUbVnyAUNhlxozrtgpgICJLXWJiZjqLGYp4fL3AGEzeu0Zi20I9kc51HVRczcw5lzAJxa6EZQI6f//EABgQAQEBAQEAAAAAAAAAAAAAAAERACEx/9oACAEBAAE/IUZQkAlT/dhaNmaGQhJKEjhuYg34oIPfmpIdT+DJ0xnwCKu6dKuNmasVVRZwZF1z5f3Tlp43qUAAI1zuYeyvgbBGQFBUlwNaNTGwcVSY2GANd//aAAwDAQACAAMAAAAQBD+9/8QAGBEBAQEBAQAAAAAAAAAAAAAAAREAQSH/2gAIAQMBAT8QZVWovmnq/sG9gTDzp6NkIEJEVUJyNAHJJMCsCBRAopg6VRbqK9d//8QAGREBAQEBAQEAAAAAAAAAAAAAAREhADFR/9oACAECAQE/ELYBZgUjK8OhDJ0+LHgWfTVfwMDyIYOpeBgiACoPDLPEbI0GsEFc3v/EABgQAQEBAQEAAAAAAAAAAAAAAAEAESEQ/9oACAEBAAE/ECSO5Bqvzeuvwl1johU0eWQBIDyCFE1HIHOMpyoV2k6HGhvBcicslfDrio8lkaG7fH4U53+8NReycZ7J7ihyBF0KNcMA/VCAlC0St8L/2Q==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;programming language person bash&quot;
        title=&quot;programming language person bash&quot;
        src=&quot;/static/b6793ac34985a17be5298069aa61cbde/62aaf/programming-language-person-bash.jpg&quot;
        srcset=&quot;/static/b6793ac34985a17be5298069aa61cbde/953fe/programming-language-person-bash.jpg 500w,
/static/b6793ac34985a17be5298069aa61cbde/0a251/programming-language-person-bash.jpg 1000w,
/static/b6793ac34985a17be5298069aa61cbde/62aaf/programming-language-person-bash.jpg 1280w&quot;
        sizes=&quot;(max-width: 1280px) 100vw, 1280px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;h3 id=&quot;PowerShell&quot; style=&quot;position:relative;&quot;&gt;PowerShell&lt;a href=&quot;#PowerShell&quot; aria-label=&quot;PowerShell permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Name: Dale (PowerDale)&lt;/li&gt;
&lt;li&gt;Age: Ambitious professional (30s)&lt;/li&gt;
&lt;li&gt;Personality: Modern, efficient, and integrated&lt;/li&gt;
&lt;li&gt;Appearance: Business-casual attire (button-down, khakis)&lt;/li&gt;
&lt;li&gt;Interests: Windows administration, DevOps, automation&lt;/li&gt;
&lt;li&gt;Skills: Expert in cmdlets, master of pipeline management&lt;/li&gt;
&lt;li&gt;Quirks: Loves consistency, hates outdated methods&lt;/li&gt;
&lt;li&gt;Favorite hangouts: Microsoft conferences, DevOps meetups&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Dale is the polished, results-driven Windows admin. Always streamlining processes.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;PowerShell: modern, efficient, Windows-focused&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 1280px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/a3f30c9a94ba09457934013a28a32d10/62aaf/programming-language-person-powershell.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 100%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDAAEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQH/2wBDAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQH/wgARCAAUABQDASIAAhEBAxEB/8QAGAABAAMBAAAAAAAAAAAAAAAAAAUHCAL/xAAYAQACAwAAAAAAAAAAAAAAAAAEBgADBf/aAAwDAQACEAMQAAAByjxr2m65UytRpMZJihtyYd07/8QAGxAAAgMBAQEAAAAAAAAAAAAABAUCAwYHExX/2gAIAQEAAQUCI5+3qnZnlC8WfOMhCePbtmT7T/VadDIo9SM46MWutCeUFobQadPL/8QAJhEAAgICAQMCBwAAAAAAAAAAAQIDEQQSAAUTFAZBFSIxQ1RhYv/aAAgBAwEBPwF8Kd+nYuN3Ak8mTjtNNah9hcWRD3WNLCAzyDaxcS7fKL5D6D65kRRTplZRSWNJFPchSw6qb08d9QbsKWYgUCbHGVfgDPqu46oAHobAeOxoN9av2vnmZn5eT7ffl/X9c//EACYRAAIBAgUCBwAAAAAAAAAAAAECAwQSAAURITEVYQYTFCIjQVP/2gAIAQIBAT8BSoRa6eaMDy445GWJkd0aIlZI7UDKRM5CRhwSVEhtRjth/EeXxuydNU2sRqEkf729/qo7jxqwRQTqQAMK79fhjvaw5Y5KXGwkVMIBK8HQcbYNPT6n4IefyTt2x//EACYQAAICAgIBBAEFAAAAAAAAAAIDAQQFEhETFAAhIiMVBiQyQUL/2gAIAQEABj8CvU/wbu/FUpPMQTRv1VstLltNmyf8NBldS65vsSyz/NkKmVrVZapJYpt+aUYWw+bVihfrqHvazHkwbOJW9oNIbKCWlkwwfuWERJS7NNp9xE9KL1fHA0UNmSVrDFt+rj4hIsIfjPvtt6zlyxdr2MtTzWMi7+Or/sixDq8V7K/ENu/jQsggdTYcSUOVtOvP63w9FiNLOIposc1xVfWiixNl9moR8iu/YiREvkkrFV7E8a8mDoyVq/Tt1pXTZTRcYcVfESqvoyazYT2NkJs/Hf63r5OZ9IrV+uUZlOKpZJbe1nlKdSGGGRdsMF0mpbhaswkHDBhr6tZOi1lO1i0Y/wAWUtdrwoX1ohvaxhv2UUwfaZSf9zx7eozWWljchaBfkNSXjC2VDCxMlogF76CIyUD7wMev/8QAGhABAQADAQEAAAAAAAAAAAAAAREAITFBUf/aAAgBAQABPyEK2EiEtZTwZws0FVhcQnFYpiG7didUCRSCaxAfYb7GyNRPrJgfCBpgfTyhyWJvs2Vgw0P2STDcmSzUdmEnsTBaBNwm46ko7UvJUM8rIMBocz//2gAMAwEAAgADAAAAEO/nvv/EABkRAQEBAQEBAAAAAAAAAAAAAAEhEQBBUf/aAAgBAwEBPxB1AwotSpeFSkSmKceZ1Wkukz318mVVgyqQDbm8ZBAAEAAgeQIHhO//xAAaEQEBAAIDAAAAAAAAAAAAAAABEQAhYXGR/9oACAECAQE/EFIWD+GJ+ZZJHpB5DoCGIiLPGAc6PtABZESkGGsMQAMAEC6B3fc//8QAGBABAQEBAQAAAAAAAAAAAAAAAQARITH/2gAIAQEAAT8QN6EIrUivvtdp37/fV25uwnCCO8lOeuIABH5mjnuEZKvJpcQXhVilJIRNv6XyKp3PLNyP8Yuc+YY9mBzysVGRNCVHxghQSjvNJxAUc7zf/9k=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;programming language person powershell&quot;
        title=&quot;programming language person powershell&quot;
        src=&quot;/static/a3f30c9a94ba09457934013a28a32d10/62aaf/programming-language-person-powershell.jpg&quot;
        srcset=&quot;/static/a3f30c9a94ba09457934013a28a32d10/953fe/programming-language-person-powershell.jpg 500w,
/static/a3f30c9a94ba09457934013a28a32d10/0a251/programming-language-person-powershell.jpg 1000w,
/static/a3f30c9a94ba09457934013a28a32d10/62aaf/programming-language-person-powershell.jpg 1280w&quot;
        sizes=&quot;(max-width: 1280px) 100vw, 1280px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;h3 id=&quot;Perl&quot; style=&quot;position:relative;&quot;&gt;Perl&lt;a href=&quot;#Perl&quot; aria-label=&quot;Perl permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Name: Penny (Perlie)&lt;/li&gt;
&lt;li&gt;Age: Quirky/Artistic (20s)&lt;/li&gt;
&lt;li&gt;Personality: Versatile, expressive, and slightly eccentric&lt;/li&gt;
&lt;li&gt;Appearance: Eclectic attire (vintage clothing, colorful hair)&lt;/li&gt;
&lt;li&gt;Interests: Text processing, regex, open-source activism&lt;/li&gt;
&lt;li&gt;Skills: Master of pattern matching, expert in scripting&lt;/li&gt;
&lt;li&gt;Quirks: Loves complexity, hates boredom&lt;/li&gt;
&lt;li&gt;Favorite hangouts: Open-source communities, hacker gatherings&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Penny is the creative, unconventional problem-solver. Always finding innovative solutions.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Perl: versatile, expressive, text-processing expert&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 1280px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/b8041a896ef06e23d3af2dd23f449247/62aaf/programming-language-person-perl.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 100%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDAAEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQH/2wBDAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQH/wgARCAAUABQDASIAAhEBAxEB/8QAGQABAQADAQAAAAAAAAAAAAAAAAgEBQkH/8QAFwEAAwEAAAAAAAAAAAAAAAAABAUGAf/aAAwDAQACEAMQAAABkfY4tsgn8yFtqeM5/ejCSmlsNN//xAAcEAABBQEBAQAAAAAAAAAAAAAFAQIDBAYUABP/2gAIAQEAAQUCpA8z9JQYIuKfgh0nq/Nxn1ClAylc8Ic6nCKU81KwGfTnKrf/xAAmEQADAQAABAQHAAAAAAAAAAABAgMEAAUREgchIjEGExQjUWFi/9oACAEDAQE/Ac+WmVOavCejRj1Z8zHHGlns9MzU2lnj87tN+yqxySM5khVsCfqBxs8QfiDHq0ZV8N5USFXnOtd+adayU/arWR5ZQxrWfa9YNR2g7NJnZkLcX9PMOWhfSC2hiF8urGMerHp7n9+/D7NYYgatAHl5C9PwP64//8QAIhEAAwACAQQCAwAAAAAAAAAAAQIDBBEhAAUGEhMUMUFR/9oACAECAQE/AX7iw+rj0sJVL1R63KTkCdY82ZhIESm4atiHZVPsh5l1i+P4+TjxuPLiPlmrES7d7yDceyyocgfJNW2EroCigOOG6gS+Jks5LncBtj7HQy3AGz+gOAP51Ht+A01LYOGxO9k40STyfySnX//EACcQAAICAgICAAUFAAAAAAAAAAIDAQQFERITABQGFSEiIzJCYXGR/9oACAEBAAY/AjFhqYz2Wbr0kFjK1irWrmRWRmrY6BS94eqk/XJRNmTYSO1HP4cshwo3Xd/zM6aFVRLvNXEh11MIa4pkekLL5ZMNIJ5yQT3lfsaedgl724utdt9aJI1tINzKC+2DOR/SZc4KIjIYUgZjr2NyAjTca7OTXkXSsLXZfMC9VEeucIWXpw1dlfQbnKnyhV+VzFQWLcpOKsJVlFY/urOEqQMGpptbIA1Dync9LnKDRPjZUQyS6VdUxNNOfwzLWW9YwEgK2dNfUB8pNfXOmDAbZ907nElW7J712UuFrCNTBXWMdkrcKk5/dPH+ojz4nSn8YV8lj1p4aAxE69azrmHEimGz9CKZLUREzOvKqk5GzA+mgp5NYZEUj9SIjIp3P+fx5//EABoQAQEBAQEBAQAAAAAAAAAAAAERADEhQRD/2gAIAQEAAT8hZysIbFT7nYOF1nD2sGHaazZbHEzOupqiHphdidIwSg+kXef88NHe9sDH1F+MhTasgBCwV2gsFfCTovxvweYj6YCtYbF7Wya2Zoqa4kyxPFA2/wD/2gAMAwEAAgADAAAAEAMHgf/EABoRAQEAAgMAAAAAAAAAAAAAAAERACExUWH/2gAIAQMBAT8QIKfjhI5gD10C7fYejaa6TanjN0sQQKqrqrXeBAgAEAgAEDRx1n//xAAYEQEBAQEBAAAAAAAAAAAAAAABESEAUf/aAAgBAgEBPxByjBOisDvXsAg8BBoDHNnDMyHgSu1oqwFEAAQ47hJemyiq+qvf/8QAGRABAQEBAQEAAAAAAAAAAAAAAQARIRBB/9oACAEBAAE/EHQchU9vRv1ebQIp3A/8awkImejnq2KlWlyyrW0ZGHgzlAR41uLKLEuFWf559ox2uusrfWRxltprrLbeRy6NzjkOSOXaYevfVErOwk6Ap//Z&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;programming language person perl&quot;
        title=&quot;programming language person perl&quot;
        src=&quot;/static/b8041a896ef06e23d3af2dd23f449247/62aaf/programming-language-person-perl.jpg&quot;
        srcset=&quot;/static/b8041a896ef06e23d3af2dd23f449247/953fe/programming-language-person-perl.jpg 500w,
/static/b8041a896ef06e23d3af2dd23f449247/0a251/programming-language-person-perl.jpg 1000w,
/static/b8041a896ef06e23d3af2dd23f449247/62aaf/programming-language-person-perl.jpg 1280w&quot;
        sizes=&quot;(max-width: 1280px) 100vw, 1280px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Debugging Dataform with Node.js in VScode]]></title><description><![CDATA[Debugging Dataform with Node.js in VScode Debugging Dataform JavaScript If you happen to be a data engineer who is not familiar with the…]]></description><link>https://mortimer.xyz/debugging-dataform-nodejs/</link><guid isPermaLink="false">https://mortimer.xyz/debugging-dataform-nodejs/</guid><pubDate>Mon, 16 Sep 2024 12:00:00 GMT</pubDate><content:encoded>&lt;h1 id=&quot;Debugging-Dataform-with-Nodejs-in-VScode&quot; style=&quot;position:relative;&quot;&gt;Debugging Dataform with Node.js in VScode&lt;a href=&quot;#Debugging-Dataform-with-Nodejs-in-VScode&quot; aria-label=&quot;Debugging Dataform with Nodejs in VScode permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h1&gt;
&lt;h2 id=&quot;Debugging-Dataform-JavaScript&quot; style=&quot;position:relative;&quot;&gt;Debugging Dataform JavaScript&lt;a href=&quot;#Debugging-Dataform-JavaScript&quot; aria-label=&quot;Debugging Dataform JavaScript permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;If you happen to be a data engineer who is not familiar with the Node.js ecosystem, and end up having to debug complex JavaScript in a BigQuery Dataform code base might be feeling lost.&lt;/p&gt;
&lt;p&gt;I will try to explain how I debug JavaScript and maybe you will find this helpful.&lt;/p&gt;
&lt;h3 id=&quot;Run-and-Debug-in-VSCode&quot; style=&quot;position:relative;&quot;&gt;Run and Debug in VSCode&lt;a href=&quot;#Run-and-Debug-in-VSCode&quot; aria-label=&quot;Run and Debug in VSCode permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Let’s start by clicking on the Run and Debug tab in VScode and opening a JavaScirpt Debug Terminal.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 393px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/ea65e05e79fa13c5ebb40b0580cfb59e/52621/dfdebug-vscode-debug-terminal.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 94.91094147582697%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAATCAIAAAAf7rriAAAACXBIWXMAAAsSAAALEgHS3X78AAADkklEQVQ4y22T20/jRhTG/S+0WoITx5exx7fxdWzP+BI7IYkhbEiy2aWsgLIQErZiYds+sN1NafdpKfTyVKlUVf/aygRVfaj006czn+bTnHOkYYA10FAvNFIkU+K0EYio26FuBwGCjZarJjaMEYiw2TIrzam74cDY09LQKhhe35DUHOuxKZMA5boYEKdDnI4lV2FPzxyYWDIJzBw9OInfc7XEURNidxixDqFoNGu6qya+kYVWEVptFyZy3VmhNFyl4a4KwDqAteW6Azlf5TxGlhxZQlIdUbdL7LYphU7VJ1E5X23+P5DzVgWzvibV14HctJNogK2iWUMia0vrFqhXjzzqqnhoBHLevzBCU1cki2eNUMSpHKRymOhJ5HQ8vRoBm9lqBRjlnp46aqw0HgepwmwNfPY5L7BmKNMUJhGgEUyxUXhqy1UzB6aelodow9fy6qimOh8+EFRhICJi4mYNxtbo9NmvJ+Pb2fh2tnOzmP78bvHHu8X9+8Wfy/O/vn31+2znp/n4djG5O5v+crT90RQJo0noql8qDdOW8w7ea/u7bf+LCrxXxvtlclAm+4PscDM9KLzKz6sLe5k7VXnMSILJcarImq6WumYaOW1stTDKsJl6eho5RWhmmeS1gJcBvwuDLgw6Cu7AQGt6TK87TGmbW9MCmQQKxRVxAKmvxBGMA4X6MCEwjhRKFIrE0BACg/d1HqurmWtPBL5uERBsSs6m6E5Fa5dHw4axz+nzOtxi9ec8mvLmkLdxA8E1Tao7YrVtt2obQkdZgyMpXeTns+LyZfvt4eBqPlkuJsv5ZPn62XI+ej+fLM/HHy42Lk/ik1P6ai8+1gTCCJwmSUiuqTtivMheHxWXh/nFV9tXX+9en7+4Pplen+19XEy/P39xfbl7fdz75qB4+4YcH9kTTaRMg1WEpibUEVJoIAeF5G3IuC15bd7rSt4A+D3B6wO/I3odCXcB7itBhnJLi5WGy2jQ11WfX9ep2y3oIA5LGpZxVCZhGQe9VlQWUb9FNlvxoBVvZ3RgaYmlJpYSVWG2BtZrQGRNW05je4uikqCSWiUx+5HZx1o3NHqh2aPWJjH7FJU+bNsgt0FWfQxRMCCwG0+UxBrNx3cnO59mo5vZ6OZ4+OnNy99+vPj7u7P7D2f3p49+pWeTuy+3fzBEwjQbKs9pAmtYIE3sYWw9fQQ9Texh7o9zf9xyR//1YzQkxpbaxP8Avv7xbnxsf+AAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;dfdebug vscode debug terminal&quot;
        title=&quot;dfdebug vscode debug terminal&quot;
        src=&quot;/static/ea65e05e79fa13c5ebb40b0580cfb59e/52621/dfdebug-vscode-debug-terminal.png&quot;
        srcset=&quot;/static/ea65e05e79fa13c5ebb40b0580cfb59e/52621/dfdebug-vscode-debug-terminal.png 393w&quot;
        sizes=&quot;(max-width: 393px) 100vw, 393px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;When you click on this you will get a second terminal. This will be below any other terminals you may already have open.&lt;/p&gt;
&lt;p&gt;Now, we can add &lt;code class=&quot;language-text&quot;&gt;debugger;&lt;/code&gt; statements within our JavaScript code for breakpoints.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 688px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/5cbeacfa98121476e876885441f9d309/39eee/dfdebug-vscode-debug-terminal-show.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 62.6%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAANCAIAAAAmMtkJAAAACXBIWXMAAAsSAAALEgHS3X78AAACB0lEQVQoz22R3W7bMAxG/SArukqiKNnyj2xJlBS7duIky4puXYFi201v9v4PMTjrxYoNOCAIAYcfSBVuujRhbdPJ+rUrR3U7wI0VH7r/Aje2VzuEqYJR3doih8/ZL/dpTe7sumPfHaQIknu8Iv8BmDcyNipJ7gspfKrni7vcN/2+dS26Bp0UDvgbkrl3MvclUI0RRShKHb1anrqXsz08u/Njf3qwx6k6jdU56YM1e2xmZMO7fOaBuU2uVEL0IDywAIz+oARpHrQg5EECSUEotkd1bSQP1ymhaHU62ECYCHIAikAr9xFywpEEkRwHOXYsKJERMoqsIavNd5scdDxx832Mr6f9Wu8zTqR2SdFOp1HRUqWL8pePJsihQe+1L8W29luy0bnCYVDWQx94JE6WDY7bgdvAbRI2yN5g6JS36J32JfwlVyrVinq5WFwqSXds4Nwz5hm/wjb4nePMC7ZVuB5/+wXui9ZMRudYr76c87DW5c6o3FSjUblSaauY6nIsZSwxCu4lJolJYSoxFbWZpAgIAfjgu6Utd1rSdZekgLSM+iproOiOYzvTXe9YX3GngAq7f+joWJulafalnoyZjZkrMytMpsx/AhGC4uHX/Pj68OP58O15ff65//riDsUUl/s4TdPjPD019dz3K4VP/fgFgSq1hStJKIIGajGSDFGGiBSROhl/AzGokmUlKlNXAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;dfdebug vscode debug terminal show&quot;
        title=&quot;dfdebug vscode debug terminal show&quot;
        src=&quot;/static/5cbeacfa98121476e876885441f9d309/39eee/dfdebug-vscode-debug-terminal-show.png&quot;
        srcset=&quot;/static/5cbeacfa98121476e876885441f9d309/0eb09/dfdebug-vscode-debug-terminal-show.png 500w,
/static/5cbeacfa98121476e876885441f9d309/39eee/dfdebug-vscode-debug-terminal-show.png 688w&quot;
        sizes=&quot;(max-width: 688px) 100vw, 688px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;In Dataform, we then need to run a compile within the Debug Terminal. We also need to pass the timeout option to a high time (otherwise it will error out before we drop into debug mode).&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;powershell&quot;&gt;&lt;pre class=&quot;language-powershell&quot;&gt;&lt;code class=&quot;language-powershell&quot;&gt;dataform compile &lt;span class=&quot;token operator&quot;&gt;--&lt;/span&gt;timeout 10m&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Now we can inspect the state of variables, step through lines of code and use the Debug Console to test lines of JavaScript code.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 925px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/e27d1bda83f5d6b78a4940b1e2d84b40/c6456/dfdebug-vscode-debugger.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 78.2%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAQCAIAAACZeshMAAAACXBIWXMAAAsSAAALEgHS3X78AAAC6ElEQVQoz02S2Y7cRBSG/RCDGHpx2y7XYleVy67N+9bunoVhutOZJelAACF4jASUBLjJDW+MekaISJ9+nZtPZ9FxrL3StK3kpJMuiysjOsW7ig+H7ObevHyw+0d9U7BBxI2ibSknw3uBSg5Lw3snWEaG9zYZsa8otiKuIqCgm4XLTPOhMVdtMQmUC08lgRa+SmBBw4KS1tLaWbk0CcucjZI0eCmxK8lKkZVEK8WjprZX22E7lJtLNWyy7oUarottV15XcmzN2ilxxjyTo4H5FrsSLlK4TOFcQFfyqFZiKlRXyFqhoo9qTaoCVz1rM9qxuHbuqUpXOoU1drNwLp4BMxH5mWBtqcqx2Q7NNPXl1JRj06zbatO1kisCc4d6jLpao4546gs5IZ4UtDXiJJd2tKoyQpepLjPTSpNRTWDhBD5PYaVRG610HBi4SE/+LDmNTZsIViwyGOYQ5BGuCaqAb8LAYljQqHJCj9HA4GUG/mv7JJ92FrQVccNJndKGk8omveY9CXMSWorKGBUO8FhwzsIvzSegm8XRSNklY5eUbgmZMBpRUCDfSD7mcpur7UkGs+R5TzBLgm84mAv/nIFFisUt1kcs9pB0CFYI1yjMQ09qOR1f/fpy/9aBQSKjxtAOLbPYNwxYuEgzUiW4ZMkFFjuEWxwoBPQTBgaakupiutuML5xgRXOxvhoOU3O76XZ3t98frl8/7N7W5iKNW8OGNOoEH3nc8qiOwgKDHAMLVpIA67hzTDxVysmKwSZDma0rdXpg7CkZ95ZPkvUyXfO4YVHN44aSisU1JWVCm9O1eZg3asuAYYFJYM6AZYGGK4lgjqBFoSUoJ6HB4H8Q0BQXznwR1cVNri6hb2Zf0fOzeHYWn5/F/tcMLwRZCLQU3iJduNnSzZ7zVCzTlaecj6+O//zy2+c3P33+4ec/749/7B/e7+5/3999+u7w6Wb/8dv9h+vdX7eHD3ev3x0en/Pd4fH94eHv44//AkH1v0jP4WIDAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;dfdebug vscode debugger&quot;
        title=&quot;dfdebug vscode debugger&quot;
        src=&quot;/static/e27d1bda83f5d6b78a4940b1e2d84b40/c6456/dfdebug-vscode-debugger.png&quot;
        srcset=&quot;/static/e27d1bda83f5d6b78a4940b1e2d84b40/0eb09/dfdebug-vscode-debugger.png 500w,
/static/e27d1bda83f5d6b78a4940b1e2d84b40/c6456/dfdebug-vscode-debugger.png 925w&quot;
        sizes=&quot;(max-width: 925px) 100vw, 925px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;The above screenshot of VSCode debugger shows the debugger actions at the top, highlights the current line of code, and the state of all variables in different context (global, local etc).
Down the bottom we have a console where we can type in JavaScript commands to test them.&lt;/p&gt;
&lt;p&gt;Try cloning &lt;a href=&quot;https://github.com/mortie23/dataform-gist&quot;&gt;this example repo&lt;/a&gt; and testing it out for yourself. I have left the &lt;code class=&quot;language-text&quot;&gt;debugger;&lt;/code&gt; statement in &lt;a href=&quot;https://github.com/mortie23/dataform-gist/blob/master/definitions/debug_example.sqlx&quot;&gt;this SQLX file&lt;/a&gt;.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;js&quot;&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;config &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token literal-property property&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;table&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token literal-property property&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;debug_example&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token literal-property property&quot;&gt;schema&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; dataform&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;projectConfig&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;vars&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;ref&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

js &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// Place this where you need to debug&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;debugger&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; simple_sql &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;select &apos;hello world&apos; as test_column&lt;/span&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

$&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; simple_sql &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;</content:encoded></item><item><title><![CDATA[GCP Cloud Functions for Web Scraping AFL data]]></title><description><![CDATA[Scraping web data using GCP Cloud Functions The main reason for this blog post is a practical example of writing a GCP Cloud Function with…]]></description><link>https://mortimer.xyz/gcp-function-afl/</link><guid isPermaLink="false">https://mortimer.xyz/gcp-function-afl/</guid><pubDate>Fri, 02 Aug 2024 12:00:00 GMT</pubDate><content:encoded>&lt;h1 id=&quot;Scraping-web-data-using-GCP-Cloud-Functions&quot; style=&quot;position:relative;&quot;&gt;Scraping web data using GCP Cloud Functions&lt;a href=&quot;#Scraping-web-data-using-GCP-Cloud-Functions&quot; aria-label=&quot;Scraping web data using GCP Cloud Functions permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h1&gt;
&lt;p&gt;The main reason for this blog post is a practical example of writing a GCP Cloud Function with in the box features of:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Tests&lt;/li&gt;
&lt;li&gt;Configurations&lt;/li&gt;
&lt;li&gt;Environment management for deployment&lt;/li&gt;
&lt;li&gt;The best sport in the world (AFL)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The source code can be found in the following repository &lt;a href=&quot;https://github.com/mortie23/ml/tree/master/eng/afl/fn-afl-teams&quot;&gt;https://github.com/mortie23/ml/tree/master/eng/afl/fn-afl-teams&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&quot;Directory-structure&quot; style=&quot;position:relative;&quot;&gt;Directory structure&lt;a href=&quot;#Directory-structure&quot; aria-label=&quot;Directory structure permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;We will start with a standard directory structure for each of our Cloud Functions like below.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;📁 fn-afl-teams/
├── 📁 src/
│   │   📄 __init__.py
│   │   📄 afl.yaml
│   │   📄 main.py
│   │   📄 requirements.txt
├── 📁 tests/
│   │   📄 test_fn-afl-teams.py
│   📄 deploy.sh
│   📄 poetry.lock
│   📄 pyproject.toml
│   📄 pytest.ini
📄 .env&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2 id=&quot;Development-process-and-tooling&quot; style=&quot;position:relative;&quot;&gt;Development process and tooling&lt;a href=&quot;#Development-process-and-tooling&quot; aria-label=&quot;Development process and tooling permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;In this example I am running on a Windows machine with WSL (Ubunutu). The first step it so install all packages on a local Python virtual environment (&lt;em&gt;venv&lt;/em&gt;).&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;sh&quot;&gt;&lt;pre class=&quot;language-sh&quot;&gt;&lt;code class=&quot;language-sh&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;# create the venv. assuming created within a venv directory on your home directory&lt;/span&gt;
python3 &lt;span class=&quot;token parameter variable&quot;&gt;-m&lt;/span&gt; venv ~/venv/fn_afl_teams
&lt;span class=&quot;token builtin class-name&quot;&gt;source&lt;/span&gt; ~/venv/fn_afl_teams/bin/activate
&lt;span class=&quot;token comment&quot;&gt;# install poetry to manage the remaining packages&lt;/span&gt;
pip &lt;span class=&quot;token function&quot;&gt;install&lt;/span&gt; poetry&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Now we use poetry to install everything else from the &lt;em&gt;pyproject.toml&lt;/em&gt; file.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;sh&quot;&gt;&lt;pre class=&quot;language-sh&quot;&gt;&lt;code class=&quot;language-sh&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;# install all packages to the venv&lt;/span&gt;
poetry lock &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; poetry &lt;span class=&quot;token function&quot;&gt;install&lt;/span&gt; --no-root
&lt;span class=&quot;token comment&quot;&gt;# register the ipykernel for the user&lt;/span&gt;
python3 &lt;span class=&quot;token parameter variable&quot;&gt;-m&lt;/span&gt; ipykernel &lt;span class=&quot;token function&quot;&gt;install&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;--user&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;--name&lt;/span&gt; fn_afl_news&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Given this Cloud Function writes to BigQuery, we need to authenticate the &lt;strong&gt;gcloud&lt;/strong&gt; SDK on our WSL installation.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;sh&quot;&gt;&lt;pre class=&quot;language-sh&quot;&gt;&lt;code class=&quot;language-sh&quot;&gt;gcloud auth login
gcloud atuh application-default login&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;With the &lt;code class=&quot;language-text&quot;&gt;./src/main.py&lt;/code&gt; Python script open in VScode, and ensure the active kernel is the &lt;em&gt;venv&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 355px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/23899330de8fd4dd3b72181322b7dced/ed409/gcp-fn-vscode-kernel.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 20.281690140845072%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAECAIAAAABPYjBAAAACXBIWXMAAAsSAAALEgHS3X78AAAAzklEQVQI1zXL7U6DMBiGYQ7EuL7t222u6wcf20AYiQ4oIAVlRM3U8z8NQ8iS68eTPLk9jtFdyMAwMEAUZwEDwzEAogAUZz4lenmRGs7CJfE4RjivAwVDYVeWztqhKLo8r15fWlv1thpcN9X1u+umt3bMzyUFzfEwx2sWbXC2piESI7YnvU+NTI3MtHhWIlEikbvEl5mvsv1TzFaaEYMQIASe4Ee5iePwkh4rtU3OJ/vR3Vz92TdfQ/s99r+j+xndrSmu0/DXFFd8NPRBLf4Bm4UwwWMNRrAAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;gcp fn vscode kernel&quot;
        title=&quot;gcp fn vscode kernel&quot;
        src=&quot;/static/23899330de8fd4dd3b72181322b7dced/ed409/gcp-fn-vscode-kernel.png&quot;
        srcset=&quot;/static/23899330de8fd4dd3b72181322b7dced/ed409/gcp-fn-vscode-kernel.png 355w&quot;
        sizes=&quot;(max-width: 355px) 100vw, 355px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;Run the script in interactive.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 1225px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/66e966837160133f44b9a2eadf7252a6/2f3a8/gcp-fn-vscode-interactive.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 62.6%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAANCAIAAAAmMtkJAAAACXBIWXMAAAsSAAALEgHS3X78AAACGElEQVQoz1WR62qlMBRGfY2e0lYTTczdW0yMMVrrac9tyvwonfd/kyGWlhlYbD42+SDslUjpKxWk8JTYPGvSA88O/OmOZfciO4h98h2R3vH0wNOvvM9Eci/4JLln1HHiKOpJ0Vfc0yIGBDqOLUMGw05SR7GJ+1xL6kiuE86cEhMhrkRGt+s0nis5Wfvq/SVMVyl9XYd5fm+aRXdrCLdl/qVU6PttDrckhy3GWomeUku4x3SgairLAcGOFIZjJ/DIkOPIFbAFWQ2yOoctBA3I6qTIO0JMW1suvdJb1W+q31S1UGQV9Q1bGrbUbO7Ec4kNQhqjHiGNihhiGcEWZi1MK/ikdqoctEXeFbDJYQO/iS//J8lBCx4UQoZWC5NBNKtkGj5I8CBhRP0A9uW/JARb8CgZtoJ68FTDtIGPIr1j6S7sy00Ucy9+SL9dJiU2BPdKeNO9CDIoNtYqVMIL5ppqVtxLNgrqWGm/oNgI5moZOBkSVGgpxq59vt3+LPMthMv18nk+fYzj6Xr5XNf30b1Zc6zUVFehrueqCiFczqcP3T0nELaUWFIapSYlJ5DVqNDWHOOPStu1Kyp0vHMRL1zkbYl702+6W0tsomfORs5cr190t1JilZz8eFZyEnwc7KsUoxSe0YESO9jjHK7WHK05SuFjmdGhxGawr73eIGgoHbaX35w5zpwfT7FGh+hpd0tK44a3EC6kNH8BLrmQC8WSX9EAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;gcp fn vscode interactive&quot;
        title=&quot;gcp fn vscode interactive&quot;
        src=&quot;/static/66e966837160133f44b9a2eadf7252a6/2f3a8/gcp-fn-vscode-interactive.png&quot;
        srcset=&quot;/static/66e966837160133f44b9a2eadf7252a6/0eb09/gcp-fn-vscode-interactive.png 500w,
/static/66e966837160133f44b9a2eadf7252a6/1263b/gcp-fn-vscode-interactive.png 1000w,
/static/66e966837160133f44b9a2eadf7252a6/2f3a8/gcp-fn-vscode-interactive.png 1225w&quot;
        sizes=&quot;(max-width: 1225px) 100vw, 1225px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;When in interactive mode in VScode, another good way to explore any resulting dataframes is using the Data Wrangler extension:&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 1347px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/9e2e1423aadef79a4b44a0de9829831d/a7485/gcp-fn-vscode-wrangler.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 80.19999999999999%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAQCAIAAACZeshMAAAACXBIWXMAAAsSAAALEgHS3X78AAACsklEQVQoz0WS6U4bMRSF8x6AVDKL7dnH++xbJgkJSciCCkQsRUhtQQUSRIX6r29eTRJSybqyLH/3nHvsluvErpN4bmogqSiUiTqUPcHqJBz4btY+co8P7Pahs13Kkds+dI4P7OODprYQlI6d2HZsmaFrJxYMJa171TyS/TgYuHaMgLBQYCBhGlJTfEqKQNSCVowULYwLKWoEpedmCEpVIQQXrhNDKAjOgc41jRFSGCjynGLQv5pObtJ4nESjLBm3gM4hEAhK7GWUFIJ3PDf1vZSzalsF72Avy8X1tPtyMX5fzj5Oq6eM3VXht5aqEFUhQOe21QxPSOG5qeem2M8RDDDp2FZkoLAK72f99Xywvpj8XgzecvaQsduWplJVIRCIQHYNJAkpLDOyzBABBs2Slo/KFwca5eX8z4+bv8vpx/xkdT58+zp6H1U/dzDQeSC7thX5Xt4+xkobA8C56Ao5FLzDWKeXPpwP14vBatp7nvaeh/ljHd+3dI3pGmts2zGCEkEJAd8f6iptNhqL/KtJ/TLrr+Ynq2HxlJH7hFw3gQGdW2ZIcAGBADqXojaQNFBASalrbNsxxstJ53V+sl4M3ob5Ux1+z+jtTnlzdQsLRqtN/sL3M01lCAoEZIyvRuWvWf/1rPty1n2Z1M9NYI03nZtmyFm18SzERhlBSXABdG40PyRIyPVZvZr1V9Peqon9ZJ2xu52yaYSUlBAICARnnUYNStwoU7RpZIGCe6M6WVbRZYhnoT8j5nAPB4yWW2XOdrYJzndw8+19BIKIjgI8sEFu67kLi0/YjPbKgtdbZYKLBt4EpqnUMuIyWiR84oDS0QsHlDvYMiOMcwia5DfDN10o2c4sDShVlZgoTvgklZOQnAZ4yN3+f3j/VJ8w3/qHQFhm2MAwSuVZzMbU7lGnh836HwaW0R1UaRm6AAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;gcp fn vscode wrangler&quot;
        title=&quot;gcp fn vscode wrangler&quot;
        src=&quot;/static/9e2e1423aadef79a4b44a0de9829831d/a7485/gcp-fn-vscode-wrangler.png&quot;
        srcset=&quot;/static/9e2e1423aadef79a4b44a0de9829831d/0eb09/gcp-fn-vscode-wrangler.png 500w,
/static/9e2e1423aadef79a4b44a0de9829831d/1263b/gcp-fn-vscode-wrangler.png 1000w,
/static/9e2e1423aadef79a4b44a0de9829831d/a7485/gcp-fn-vscode-wrangler.png 1347w&quot;
        sizes=&quot;(max-width: 1347px) 100vw, 1347px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;If you have to add any new packages while you are developing, you add them using poetry:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;sh&quot;&gt;&lt;pre class=&quot;language-sh&quot;&gt;&lt;code class=&quot;language-sh&quot;&gt;poetry &lt;span class=&quot;token function&quot;&gt;add&lt;/span&gt; pandas&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2 id=&quot;Deploy&quot; style=&quot;position:relative;&quot;&gt;Deploy&lt;a href=&quot;#Deploy&quot; aria-label=&quot;Deploy permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;If you have had to add or update any packages in the &lt;code class=&quot;language-text&quot;&gt;pyproject.toml&lt;/code&gt; then export to the requirements file before you deploy. The Cloud Build step when deploying a Python based Cloud Functions depends on a &lt;em&gt;requirements.txt&lt;/em&gt; file in the source directory.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;sh&quot;&gt;&lt;pre class=&quot;language-sh&quot;&gt;&lt;code class=&quot;language-sh&quot;&gt;poetry &lt;span class=&quot;token builtin class-name&quot;&gt;export&lt;/span&gt; --without-hashes &lt;span class=&quot;token parameter variable&quot;&gt;--format&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;requirements.txt &lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; ./src/requirements.txt&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;To deploy the funciton run the deploy script passing the target environment.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;sh&quot;&gt;&lt;pre class=&quot;language-sh&quot;&gt;&lt;code class=&quot;language-sh&quot;&gt;./deploy.sh &lt;span class=&quot;token parameter variable&quot;&gt;--env&lt;/span&gt; dev&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;log&quot;&gt;&lt;pre class=&quot;language-log&quot;&gt;&lt;code class=&quot;language-log&quot;&gt;Preparing function&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;done&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;
✓ Deploying function&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;
  ✓ &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;Build&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; Logs are available at &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token url&quot;&gt;https://console.cloud.google.com/cloud-build/builds&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;;&lt;/span&gt;region&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;australia&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;southeast1&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;xyz&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
  ✓ &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;Service&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
  ✓ &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;ArtifactRegistry&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
  ✓ &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;Healthcheck&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
  ✓ &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;Triggercheck&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
Done&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2 id=&quot;Calling&quot; style=&quot;position:relative;&quot;&gt;Calling&lt;a href=&quot;#Calling&quot; aria-label=&quot;Calling permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;To test calling the Cloud Function we can use a basic curl command. Ensure you are logged in as a user that has invoke permissions.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;sh&quot;&gt;&lt;pre class=&quot;language-sh&quot;&gt;&lt;code class=&quot;language-sh&quot;&gt;&lt;span class=&quot;token function&quot;&gt;curl&lt;/span&gt; https://australia-southeast1-prj-xyz-dev-fruit-0.cloudfunctions.net/fn-afl-teams-0 &lt;span class=&quot;token parameter variable&quot;&gt;-H&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Authorization: Bearer &lt;span class=&quot;token variable&quot;&gt;&lt;span class=&quot;token variable&quot;&gt;$(&lt;/span&gt;gcloud auth print-identity-token&lt;span class=&quot;token variable&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&quot;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;If you check the target table, it should now include data from the scraped API.
&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 2000px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/2dd8e059b2730a89a331651c8870cb41/6eb05/gcp-fn-vscode-big-table.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 46.2%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAJCAIAAAC9o5sfAAAACXBIWXMAAAsSAAALEgHS3X78AAABWElEQVQozz2Ry47kIAxF8///WD2tSTQJCQRsMA9jQ6sqoz4Lr3x1ruzFOee9Hx9ENFHnrvQm2bvdoaWUjaXWWu+dmX9n733JmVIEhIAIRO+l6zqP4zDGAKRc9LhqbVqquMC5qsdeml43B+TlON26nYcJlUVVP35prRGRv90Y6r0vpQTg/SzMEnyw1iLGMeaCsZ4WjY21ypxTREopKSVEBIBSq3WOKJvTAVLOBT+oKjMvmejP12s/9jHGnFNVEbHWEgKEEJjZGDPndM4NVek9BPj+/u5d3uZa29frtW5bIpofcs6PnIh674g4xogxqqrI+5b3fY8xVHVh5svadV33fReR3zAAICIzW2vHGNZaEWFm7/22bU/HpbW6rX+vy1zXRR85IuZcYoxP+F14jBCCiNRaAcBa+z9cqqz/7tOW1mfrMxIf5s65EKWUUu8dAJ7aj5mIzvN8/vIDkYUEbcC8qC0AAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;gcp fn vscode big table&quot;
        title=&quot;gcp fn vscode big table&quot;
        src=&quot;/static/2dd8e059b2730a89a331651c8870cb41/f97d7/gcp-fn-vscode-big-table.png&quot;
        srcset=&quot;/static/2dd8e059b2730a89a331651c8870cb41/0eb09/gcp-fn-vscode-big-table.png 500w,
/static/2dd8e059b2730a89a331651c8870cb41/1263b/gcp-fn-vscode-big-table.png 1000w,
/static/2dd8e059b2730a89a331651c8870cb41/f97d7/gcp-fn-vscode-big-table.png 2000w,
/static/2dd8e059b2730a89a331651c8870cb41/6eb05/gcp-fn-vscode-big-table.png 2037w&quot;
        sizes=&quot;(max-width: 2000px) 100vw, 2000px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;h2 id=&quot;Testing&quot; style=&quot;position:relative;&quot;&gt;Testing&lt;a href=&quot;#Testing&quot; aria-label=&quot;Testing permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;To run the unit tests during development we can run this:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;sh&quot;&gt;&lt;pre class=&quot;language-sh&quot;&gt;&lt;code class=&quot;language-sh&quot;&gt;pytest ./tests/test_fn-afl-teams.py::test_webscrape_teams&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;log&quot;&gt;&lt;pre class=&quot;language-log&quot;&gt;&lt;code class=&quot;language-log&quot;&gt;&lt;span class=&quot;token separator comment&quot;&gt;============================&lt;/span&gt; test session starts &lt;span class=&quot;token separator comment&quot;&gt;=============================&lt;/span&gt;
platform linux &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt; Python &lt;span class=&quot;token number&quot;&gt;3.10.7&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; pytest&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;8.3.2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; pluggy&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1.5.0&lt;/span&gt;
&lt;span class=&quot;token property&quot;&gt;rootdir:&lt;/span&gt; &lt;span class=&quot;token file-path string&quot;&gt;/mnt/c/git/github/mortie23/ml/eng/afl/fn-afl-teams&lt;/span&gt;
&lt;span class=&quot;token property&quot;&gt;configfile:&lt;/span&gt; &lt;span class=&quot;token domain constant&quot;&gt;pytest.ini&lt;/span&gt;
&lt;span class=&quot;token property&quot;&gt;plugins:&lt;/span&gt; env&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1.1.3&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; hydra&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;core&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1.3.2&lt;/span&gt;
collected &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt; item

tests&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;test_fn&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;afl&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;teams&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;py &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;                                            &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;100&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;%&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;

&lt;span class=&quot;token separator comment&quot;&gt;=============================&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt; passed in &lt;span class=&quot;token number&quot;&gt;1.24s&lt;/span&gt; &lt;span class=&quot;token separator comment&quot;&gt;==============================&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2 id=&quot;IAM-and-other-infrastructure&quot; style=&quot;position:relative;&quot;&gt;IAM and other infrastructure&lt;a href=&quot;#IAM-and-other-infrastructure&quot; aria-label=&quot;IAM and other infrastructure permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The following is some of the prerequisites required for BigQuery, Storage Buckets and IAM permissions for developers (members of the &lt;code class=&quot;language-text&quot;&gt;gcp-developers&lt;/code&gt; group) and service account (&lt;code class=&quot;language-text&quot;&gt;serviceAccount:sa-xyz-dev-fruit-cf@prj-xyz-dev-fruit-0.iam.gserviceaccount.com&lt;/code&gt;).&lt;/p&gt;
&lt;h3 id=&quot;Creation-of-BigQuery-dataset-for-Horizon-Scanning&quot; style=&quot;position:relative;&quot;&gt;Creation of BigQuery dataset for Horizon Scanning&lt;a href=&quot;#Creation-of-BigQuery-dataset-for-Horizon-Scanning&quot; aria-label=&quot;Creation of BigQuery dataset for Horizon Scanning permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;sh&quot;&gt;&lt;pre class=&quot;language-sh&quot;&gt;&lt;code class=&quot;language-sh&quot;&gt;bq mk &lt;span class=&quot;token parameter variable&quot;&gt;--location&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;australia-southeast1 &lt;span class=&quot;token parameter variable&quot;&gt;--project_id&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;prj-xyz-dev-fruit-0 afl&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;Setup-of-the-Cloud-Build-things&quot; style=&quot;position:relative;&quot;&gt;Setup of the Cloud Build things&lt;a href=&quot;#Setup-of-the-Cloud-Build-things&quot; aria-label=&quot;Setup of the Cloud Build things permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Create a custom bucket for the source code&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;sh&quot;&gt;&lt;pre class=&quot;language-sh&quot;&gt;&lt;code class=&quot;language-sh&quot;&gt;gcloud storage buckets create &lt;span class=&quot;token parameter variable&quot;&gt;--location&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;australia-southeast1 &lt;span class=&quot;token parameter variable&quot;&gt;--project&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;prj-xyz-dev-fruit-0 gs://bkt-xyz-dev-afl-fn-0&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2 id=&quot;Create-service-accounts-for-Cloud-Functions-and-Cloud-Build&quot; style=&quot;position:relative;&quot;&gt;Create service accounts for Cloud Functions and Cloud Build&lt;a href=&quot;#Create-service-accounts-for-Cloud-Functions-and-Cloud-Build&quot; aria-label=&quot;Create service accounts for Cloud Functions and Cloud Build permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;sh&quot;&gt;&lt;pre class=&quot;language-sh&quot;&gt;&lt;code class=&quot;language-sh&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;# Create a service account&lt;/span&gt;
gcloud iam service-accounts create sa-xyz-dev-fruit-cf --display-name &lt;span class=&quot;token string&quot;&gt;&quot;Cloud Function Service Account&quot;&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;# Allow gcp-developers to act as the Cloud Function service account for deployment&lt;/span&gt;
gcloud iam service-accounts add-iam-policy-binding sa-xyz-dev-fruit-cf@prj-xyz-dev-fruit-0.iam.gserviceaccount.com &lt;span class=&quot;token parameter variable&quot;&gt;--member&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;group:gcp-developers@tremendousdomain.xyz&apos;&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;--role&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;roles/iam.serviceAccountUser&apos;&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;# Allow developers to invoke functions&lt;/span&gt;
gcloud projects add-iam-policy-binding prj-xyz-dev-fruit-0 &lt;span class=&quot;token parameter variable&quot;&gt;--member&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;group:gcp-developers@tremendousdomain.xyz&apos;&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;--role&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;roles/run.invoker&apos;&lt;/span&gt;
gcloud projects add-iam-policy-binding prj-xyz-dev-fruit-0 &lt;span class=&quot;token parameter variable&quot;&gt;--member&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;group:gcp-developers@tremendousdomain.xyz&quot;&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;--role&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;roles/cloudfunctions.invoker&quot;&lt;/span&gt;
gcloud projects add-iam-policy-binding prj-xyz-dev-fruit-0 &lt;span class=&quot;token parameter variable&quot;&gt;--member&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;group:gcp-developers@tremendousdomain.xyz&quot;&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;--role&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;roles/cloudfunctions.developer&quot;&lt;/span&gt;
gcloud projects add-iam-policy-binding prj-xyz-dev-fruit-0 &lt;span class=&quot;token parameter variable&quot;&gt;--member&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;group:gcp-developers@tremendousdomain.xyz&quot;&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;--role&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;roles/cloudfunctions.viewer&quot;&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;# Give service account permissions to all the things needed (might be too many permissions)&lt;/span&gt;
gcloud projects add-iam-policy-binding prj-xyz-dev-fruit-0 &lt;span class=&quot;token parameter variable&quot;&gt;--member&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;serviceAccount:sa-xyz-dev-fruit-cf@prj-xyz-dev-fruit-0.iam.gserviceaccount.com&quot;&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;--role&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;roles/cloudfunctions.invoker&quot;&lt;/span&gt;
gcloud projects add-iam-policy-binding prj-xyz-dev-fruit-0 &lt;span class=&quot;token parameter variable&quot;&gt;--member&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;serviceAccount:sa-xyz-dev-fruit-cf@prj-xyz-dev-fruit-0.iam.gserviceaccount.com&quot;&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;--role&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;roles/cloudfunctions.developer&quot;&lt;/span&gt;
gcloud projects add-iam-policy-binding prj-xyz-dev-fruit-0 &lt;span class=&quot;token parameter variable&quot;&gt;--member&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;serviceAccount:sa-xyz-dev-fruit-cf@prj-xyz-dev-fruit-0.iam.gserviceaccount.com&quot;&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;--role&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;roles/cloudfunctions.viewer&quot;&lt;/span&gt;
gcloud projects add-iam-policy-binding prj-xyz-dev-fruit-0 &lt;span class=&quot;token parameter variable&quot;&gt;--member&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;serviceAccount:sa-xyz-dev-fruit-cf@prj-xyz-dev-fruit-0.iam.gserviceaccount.com&quot;&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;--role&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;roles/logging.logWriter&quot;&lt;/span&gt;
gcloud projects add-iam-policy-binding prj-xyz-dev-fruit-0 &lt;span class=&quot;token parameter variable&quot;&gt;--member&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;serviceAccount:sa-xyz-dev-fruit-cf@prj-xyz-dev-fruit-0.iam.gserviceaccount.com&quot;&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;--role&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;roles/artifactregistry.writer&quot;&lt;/span&gt;
gcloud projects add-iam-policy-binding prj-xyz-dev-fruit-0 &lt;span class=&quot;token parameter variable&quot;&gt;--member&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;serviceAccount:sa-xyz-dev-fruit-cf@prj-xyz-dev-fruit-0.iam.gserviceaccount.com&quot;&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;--role&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;roles/storage.objectAdmin&quot;&lt;/span&gt;
gcloud projects add-iam-policy-binding prj-xyz-dev-fruit-0 &lt;span class=&quot;token parameter variable&quot;&gt;--member&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;serviceAccount:sa-xyz-dev-fruit-cf@prj-xyz-dev-fruit-0.iam.gserviceaccount.com&quot;&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;--role&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;roles/cloudbuild.builds.builder&quot;&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;# Cloud Functions use Cloud Run&lt;/span&gt;
gcloud projects add-iam-policy-binding prj-xyz-dev-fruit-0 &lt;span class=&quot;token parameter variable&quot;&gt;--member&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;serviceAccount:sa-xyz-dev-fruit-cf@prj-xyz-dev-fruit-0.iam.gserviceaccount.com&quot;&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;--role&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;roles/run.admin&quot;&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;# Needed for the BigQuery part after getting bigquery&lt;/span&gt;
gcloud projects add-iam-policy-binding prj-xyz-dev-fruit-0 &lt;span class=&quot;token parameter variable&quot;&gt;--member&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;serviceAccount:sa-xyz-dev-fruit-cf@prj-xyz-dev-fruit-0.iam.gserviceaccount.com&quot;&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;--role&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;roles/bigquery.dataEditor&quot;&lt;/span&gt;
gcloud projects add-iam-policy-binding prj-xyz-dev-fruit-0 &lt;span class=&quot;token parameter variable&quot;&gt;--member&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;serviceAccount:sa-xyz-dev-fruit-cf@prj-xyz-dev-fruit-0.iam.gserviceaccount.com&quot;&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;--role&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;roles/bigquery.jobUser&quot;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;</content:encoded></item><item><title><![CDATA[Flask application with custom package deployed to GCP Artifact Registry and Cloud Run]]></title><description><![CDATA[Flask application with custom package deployed to GCP Artifact Registry and Cloud Run What we are looking to do in this example is…]]></description><link>https://mortimer.xyz/flask-poetry-mono-repo-docker-gcp-artifact-registry/</link><guid isPermaLink="false">https://mortimer.xyz/flask-poetry-mono-repo-docker-gcp-artifact-registry/</guid><pubDate>Thu, 18 Jul 2024 12:00:00 GMT</pubDate><content:encoded>&lt;h1 id=&quot;Flask-application-with-custom-package-deployed-to-GCP-Artifact-Registry-and-Cloud-Run&quot; style=&quot;position:relative;&quot;&gt;Flask application with custom package deployed to GCP Artifact Registry and Cloud Run&lt;a href=&quot;#Flask-application-with-custom-package-deployed-to-GCP-Artifact-Registry-and-Cloud-Run&quot; aria-label=&quot;Flask application with custom package deployed to GCP Artifact Registry and Cloud Run permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h1&gt;
&lt;p&gt;What we are looking to do in this example is demonstrate how you might develop a Python based web application that uses modules from a custom Python package that is contained within the same mono repo as the source code for the application.&lt;/p&gt;
&lt;p&gt;The source code (&lt;a href=&quot;https://github.com/mortie23/ml/&quot;&gt;https://github.com/mortie23/ml/&lt;/a&gt;) is same as from my previous post about &lt;a href=&quot;https://mortimer.xyz/python-mono-repo-for-data-science-with-poetry/&quot;&gt;Python mono repos for Data Science with Poetry&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Some of the benefits are:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Module reuse across exploratory data analysis (EDA), and many applications&lt;/li&gt;
&lt;li&gt;Changes can be tested across the complete code base&lt;/li&gt;
&lt;li&gt;Changes and associated deployment can be orchestrated for both the custom modules and applications with one feature branch&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 1303px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/7908b0d0c1cbc2efa2f34775de85109b/bf4cc/flask-poetry-architecture.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 105.4%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAVCAYAAABG1c6oAAAACXBIWXMAAAsTAAALEwEAmpwYAAAEqElEQVQ4y32V24scRRTGy+wy0agPQkb/Av8JH5SIqAj6LvrksyFLTMA3QV8NRDaJm5iLL2bYYFyRgGgQom42bjIhs8neZ+c+fe/qru66dnd1H6nZXUNQbPj4ThfdP06dr5pGCCF0/JOTTx87evQQAExZXjDt0Wraaf045Z3/7NlUwLTIi2muimnxHzLrMiumLZJNHfvBRujVI28Y5iGE0Au3Fu8gVQEKwyFqAKCP3n/rZYAI8RxQpiskM41krnd9v841UrlGFsnQ0esWQjeXHjx3b3VnbmXHaqx3x5fXOiOjxp8r7Q8XO8kXW568uunJyxuuvLLhiH9p3RGXN1zxXXPIPz/5kzONvvm5+ZJlu1XebUKW5VCWFeR5ASs79qVhpNbDmIKLE8AkBSoySLmCVCigE+3eM5lBJ5C9dy4MDqLGjd8P953Q33BxVWidAYAqy9IAT1tELbWHMYy8RHGZ54SJnHI5UcpFPvBojhOhuFQG2Hz7wqiG5m/crPfdGD8MKiirsgQArbU2wNlRnC27fgjjMNZb7hCEVDDwMQyDGCiXMHIC2O4NdZwy6IaqdeRMp4a+XmjWHd/FIDagAvgH+LDjzA6wXBYqg4BQvdluAZcZ7IxsGLo+GIgTRLDVHWjjfZy1Xjm1UUNz39+qO0GE83QVCjF+3GHHnh1itUy5gJHj65XNLaCMw621HixvDYAkFAgVEMSJDuMEeqbD2Z0aOnN1oe5GHOssARbcfwI4irJlQhn0LVfbfgAhSU0NlhdCyuVECRPaBGa2/PrZbg2dm/+lbgcxBgDouWk58iKtlII7j7rnt23SdHwMlo9LkyQmFKKUTRJOmJgACeU63AeaGc5dX6zbfoCh8IByVXKp9CTljj2ZIRMS/Iho87IB4oTud7YHfNzhJJS5heW67fs4j/+CqkieSNkAqZDg4f8BMqHdAEPHl633rrg1dK6xUHcwx5IHwIOlCZALActrvbltJ2kGEQEXE7PlChM6UcpllTBRJVxWpkPzTDeQrTfnBjV0dv7XuotJbGZoYwYeTipGmenwQsdN70ckgYikVZxQMGmakMwYTPrGUyYqc4T6WK289tX2QXRp4bfD3bHnSJUVXTsQ2wOHj20P7qz2vly36B9ezMGNuXBjLj0iZEiVDNM9USWDVPKRG8KmK+6++61fQ5s96/me5V+0fXzN8sKG7ePG0A2vWX50eojFxQGW1zbH0fVByBv9gDd6Pmv0A9boBbve9Vnj/lp7pW3HpxYDmEJelNSFyqhLGOiygqKsHrsuoCoyUFKC+SonqvY0qSsodQFSKRAqe3T7wVoNxUwdjhPqd0d2lQqV3+6RfGvkFt3AyWxrNdf9Vu6Md3ImihwAnlBV6Dxu3VUqE8Bl3ry51KwhBycvMiEzc5jzojBDhpikwDkDKSUolUFMGcgsh0KXUBR6V1pDlhfAUzZZp1xu31vv1lDf8p7xo+RjnLCTYy/8FJP0REz5ccLUTMzkDKFihnE1QyifiVI2E6fG92rKZ4hQx6KUncAJ+6A9dA+gvespANj/FRwwdZRyRGWOEq6QUDlKuUAx5YjsaVIzgdKsQIQKFKUMtUcu+htOo4VrJWzI/gAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;flask poetry architecture&quot;
        title=&quot;flask poetry architecture&quot;
        src=&quot;/static/7908b0d0c1cbc2efa2f34775de85109b/bf4cc/flask-poetry-architecture.png&quot;
        srcset=&quot;/static/7908b0d0c1cbc2efa2f34775de85109b/0eb09/flask-poetry-architecture.png 500w,
/static/7908b0d0c1cbc2efa2f34775de85109b/1263b/flask-poetry-architecture.png 1000w,
/static/7908b0d0c1cbc2efa2f34775de85109b/bf4cc/flask-poetry-architecture.png 1303w&quot;
        sizes=&quot;(max-width: 1303px) 100vw, 1303px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;h2 id=&quot;Our-custom-Python-package-Fruitbowl&quot; style=&quot;position:relative;&quot;&gt;Our custom Python package Fruitbowl&lt;a href=&quot;#Our-custom-Python-package-Fruitbowl&quot; aria-label=&quot;Our custom Python package Fruitbowl permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;First we will start with the &lt;em&gt;super useful&lt;/em&gt; custom package we have called Fruit bowl, which has a Fruit module and function that returns a random name of a fruit. Just for demonstration purposes.&lt;/p&gt;
&lt;h3 id=&quot;Publish-to-a-GCP-Artifact-Registry-Python-package-repository&quot; style=&quot;position:relative;&quot;&gt;Publish to a GCP Artifact Registry Python package repository&lt;a href=&quot;#Publish-to-a-GCP-Artifact-Registry-Python-package-repository&quot; aria-label=&quot;Publish to a GCP Artifact Registry Python package repository permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Credit goes to Rodrigo Agundez who wrote this blog post:&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://medium.com/google-cloud/python-packages-via-gcps-artifact-registry-ce1714f8e7c1#5f79&quot;&gt;GCP Python packages via GCPS Artifact Registry&lt;/a&gt;&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;sh&quot;&gt;&lt;pre class=&quot;language-sh&quot;&gt;&lt;code class=&quot;language-sh&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;# Update poetry&lt;/span&gt;
poetry self update
&lt;span class=&quot;token comment&quot;&gt;# Add Google artifact registry keyring to poetry&lt;/span&gt;
poetry self &lt;span class=&quot;token function&quot;&gt;add&lt;/span&gt; keyrings.google-artifactregistry-auth
&lt;span class=&quot;token comment&quot;&gt;# Check the python repository on GCP&lt;/span&gt;
gcloud artifacts print-settings python &lt;span class=&quot;token parameter variable&quot;&gt;--project&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;prj-xyz-dev-fruit-0 &lt;span class=&quot;token parameter variable&quot;&gt;--repository&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;rpo-xyz-dev-fruit-pyn-0 &lt;span class=&quot;token parameter variable&quot;&gt;--location&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;australia-southeast1
&lt;span class=&quot;token comment&quot;&gt;# Configure a poetry repository to point to the python artifact registry&lt;/span&gt;
poetry config repositories.gcp https://australia-southeast1-python.pkg.dev/prj-xyz-dev-fruit-0/rpo-xyz-dev-fruit-pyn-0/
&lt;span class=&quot;token comment&quot;&gt;# publish the package to the repository&lt;/span&gt;
poetry publish &lt;span class=&quot;token parameter variable&quot;&gt;--build&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;--repository&lt;/span&gt; gcp&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The publish step should look like this if successful.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;log&quot;&gt;&lt;pre class=&quot;language-log&quot;&gt;&lt;code class=&quot;language-log&quot;&gt;Using python3&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;12 &lt;span class=&quot;token operator&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;3.12.3&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;)&lt;/span&gt;
Building fruitbowl &lt;span class=&quot;token operator&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0.1.0&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt; Building sdist
  &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt; Built fruitbowl&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;0&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;1&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;0&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;tar&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;gz
  &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt; Building wheel
  &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt; Built fruitbowl&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0.1.0&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;py3&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;none&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;any&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;whl

Publishing fruitbowl &lt;span class=&quot;token operator&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0.1.0&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;)&lt;/span&gt; to gcp
 &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt; Uploading fruitbowl&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0.1.0&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;py3&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;none&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;any&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;whl &lt;span class=&quot;token number&quot;&gt;100&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;%&lt;/span&gt;
 &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt; Uploading fruitbowl&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;0&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;1&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;0&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;tar&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;gz &lt;span class=&quot;token number&quot;&gt;100&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;%&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 638px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/6b74f6fc79669f67f5df07f19a3e7221/d376a/flask-poetry-gcp-artifact-repo-publish.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 54%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAALCAIAAADwazoUAAAACXBIWXMAAAsSAAALEgHS3X78AAABIklEQVQoz42SW1PCMBCF8///mqWM+gIVsYrYtLmQS8km22xwgNEXUPhez57dOTuHrVarcRytddZaANBavzSNc17/kHOe1/VisdBad5x3/GsYhrZ9533PUkqYMMU0TTlGAIDD3bCqqp6fHqtZvWyW83r2UM1COPpLKZfT5cSvytq23RlbyiFnOmvOOWNtuedy+/ZKlM/LiKgUMsZw3qWUbpu1AamT1NE4PByOfiKa8kREt80hknGTcbgP+WrCf2Db7cZ7O2GKEfw4QggxQjhy++1MCCnEIKVSSgHAbqd533vnAcB7L4QYxxER0zWYVmrz2X9sOiE14iSlfF2vrXWIKIRomkYpFc8FuICFELQlLhIXMSFhSta5cKoKInrn9yH8lf8btrF8pdov0ecAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;flask poetry gcp artifact repo publish&quot;
        title=&quot;flask poetry gcp artifact repo publish&quot;
        src=&quot;/static/6b74f6fc79669f67f5df07f19a3e7221/d376a/flask-poetry-gcp-artifact-repo-publish.png&quot;
        srcset=&quot;/static/6b74f6fc79669f67f5df07f19a3e7221/0eb09/flask-poetry-gcp-artifact-repo-publish.png 500w,
/static/6b74f6fc79669f67f5df07f19a3e7221/d376a/flask-poetry-gcp-artifact-repo-publish.png 638w&quot;
        sizes=&quot;(max-width: 638px) 100vw, 638px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;h2 id=&quot;The-Hello-Fruit-Flask-application&quot; style=&quot;position:relative;&quot;&gt;The Hello Fruit Flask application&lt;a href=&quot;#The-Hello-Fruit-Flask-application&quot; aria-label=&quot;The Hello Fruit Flask application permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Now for the simple example of a Flask application that uses this Fruit bowl function from the custom Python module.&lt;/p&gt;
&lt;h3 id=&quot;Setup&quot; style=&quot;position:relative;&quot;&gt;Setup&lt;a href=&quot;#Setup&quot; aria-label=&quot;Setup permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Firstly let’s just test if we can run it locally in development mode. In this mode we could test changing code from either the module or the application and see the changes.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;sh&quot;&gt;&lt;pre class=&quot;language-sh&quot;&gt;&lt;code class=&quot;language-sh&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;# Change to the root of the application&lt;/span&gt;
&lt;span class=&quot;token builtin class-name&quot;&gt;cd&lt;/span&gt; app/flask-hellofruit/
&lt;span class=&quot;token comment&quot;&gt;# Install application dependencies using poetry&lt;/span&gt;
poetry &lt;span class=&quot;token function&quot;&gt;install&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;Running-the-application&quot; style=&quot;position:relative;&quot;&gt;Running the application&lt;a href=&quot;#Running-the-application&quot; aria-label=&quot;Running the application permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;sh&quot;&gt;&lt;pre class=&quot;language-sh&quot;&gt;&lt;code class=&quot;language-sh&quot;&gt;poetry run flask run&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;log&quot;&gt;&lt;pre class=&quot;language-log&quot;&gt;&lt;code class=&quot;language-log&quot;&gt; &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; Debug mode&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; off
&lt;span class=&quot;token level warning important&quot;&gt;WARNING&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; This is a development server&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt; Do not use it in a production deployment&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt; Use a production WSGI server instead&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;
 &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; Running on &lt;span class=&quot;token url&quot;&gt;http://127.0.0.1:5000&lt;/span&gt;
Press CTRL&lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt;C to quit
&lt;span class=&quot;token ip-address constant&quot;&gt;127.0.0.1&lt;/span&gt; &lt;span class=&quot;token separator comment&quot;&gt;- - &lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token date number&quot;&gt;17/Jul/2024&lt;/span&gt; &lt;span class=&quot;token time number&quot;&gt;21:03:36&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;GET / HTTP/1.1&quot;&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;200&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;
&lt;span class=&quot;token ip-address constant&quot;&gt;127.0.0.1&lt;/span&gt; &lt;span class=&quot;token separator comment&quot;&gt;- - &lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token date number&quot;&gt;17/Jul/2024&lt;/span&gt; &lt;span class=&quot;token time number&quot;&gt;21:03:36&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;GET /favicon.ico HTTP/1.1&quot;&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;404&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 480px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/93214ad4e09e0f71d9740020fd1cb27f/9aebd/flask-poetry-hellofruit.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 37.916666666666664%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAICAIAAAB2/0i6AAAACXBIWXMAAAsSAAALEgHS3X78AAABTUlEQVQY043NXUvDMBgF4P5+9RdMEOfFpoI4HOxqXk2mV4qMtmMfbt2atGuaJk3bpM1HI2OXovjyXB0453WgsLMdckG2iOkSpEuIFwB5QRQSDlkNWR3mR/u8/kqYD+kciRU1a2ZXzDqAWxdkLiDrpNgk+SrKlhB7QRxSERUSFjIqFSgUKM06Lb24mB24T8ycWpdYB3GD6xY3NkjZ5kBj1kR5HbMGEB6SCpAqRAQguo1QRIqYVpnQKdeoUploHUCqpJRJpVYAbeKMaUualkhLpSXKFtoeEoQzEkKY4mwb7HYhSDFOUqx06xS1OikbXUnDhMx5c8KELGsllBHK1MoIqYU0/EjzRleNdux/r/0ZOb3+7eDx4eq6G+z2NzfdTufy7XU6eZlMp1PXm/+954xGo/HzeDh88ucL3/fu726HT4N+v3d2fvH+8fnbz1P5G54TuxgRelaIAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;flask poetry hellofruit&quot;
        title=&quot;flask poetry hellofruit&quot;
        src=&quot;/static/93214ad4e09e0f71d9740020fd1cb27f/9aebd/flask-poetry-hellofruit.png&quot;
        srcset=&quot;/static/93214ad4e09e0f71d9740020fd1cb27f/9aebd/flask-poetry-hellofruit.png 480w&quot;
        sizes=&quot;(max-width: 480px) 100vw, 480px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;h3 id=&quot;Hosting-on-GCP-with-Cloud-Run&quot; style=&quot;position:relative;&quot;&gt;Hosting on GCP with Cloud Run&lt;a href=&quot;#Hosting-on-GCP-with-Cloud-Run&quot; aria-label=&quot;Hosting on GCP with Cloud Run permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Hosting this on GCP has many options, but we are going to use Cloud Run to host this in a serverless way.&lt;/p&gt;
&lt;h4 id=&quot;Build-a-Docker-container-image&quot; style=&quot;position:relative;&quot;&gt;Build a Docker container image&lt;a href=&quot;#Build-a-Docker-container-image&quot; aria-label=&quot;Build a Docker container image permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;We need the Docker build process to have access to the Artifact registry repository. For this we create a service account and associated key file that can be temporarily copied during the Docker build process.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;sh&quot;&gt;&lt;pre class=&quot;language-sh&quot;&gt;&lt;code class=&quot;language-sh&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;# Create a service account&lt;/span&gt;
gcloud iam service-accounts create gsvc-xyz-dev-fruit-rpo --display-name &lt;span class=&quot;token string&quot;&gt;&quot;Artifact Registry Service Account&quot;&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;# Give service account permissions to artifact registry in project&lt;/span&gt;
gcloud projects add-iam-policy-binding prj-xyz-dev-fruit-0 &lt;span class=&quot;token parameter variable&quot;&gt;--member&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;serviceAccount:gsvc-xyz-dev-fruit-rpo@prj-xyz-dev-fruit-0.iam.gserviceaccount.com &lt;span class=&quot;token parameter variable&quot;&gt;--role&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;roles/artifactregistry.reader
&lt;span class=&quot;token comment&quot;&gt;# Create application keys JSON file for service account&lt;/span&gt;
gcloud iam service-accounts keys create gsvc-xyz-dev-fruit-rpo.json --iam-account gsvc-xyz-dev-fruit-rpo@prj-xyz-dev-fruit-0.iam.gserviceaccount.com&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Put the JSON keys file in this location on your developer client machine &lt;code class=&quot;language-text&quot;&gt;~\.config\gcloud\gsvc-xyz-dev-fruit-rpo.json&lt;/code&gt;.
In this case it is the home path on the WSL2 Windows client laptop.
It will be used later in the &lt;code class=&quot;language-text&quot;&gt;hellofruit&lt;/code&gt; app Docker build step.&lt;/p&gt;
&lt;p&gt;Now we can build the container image.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;sh&quot;&gt;&lt;pre class=&quot;language-sh&quot;&gt;&lt;code class=&quot;language-sh&quot;&gt;&lt;span class=&quot;token function&quot;&gt;docker&lt;/span&gt; build &lt;span class=&quot;token builtin class-name&quot;&gt;.&lt;/span&gt; --build-arg &lt;span class=&quot;token assign-left variable&quot;&gt;SA_KEY_BASE64&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;&lt;span class=&quot;token variable&quot;&gt;`&lt;/span&gt;base64 &lt;span class=&quot;token parameter variable&quot;&gt;-w0&lt;/span&gt; ~/.config/gcloud/gsvc-xyz-dev-fruit-rpo.json&lt;span class=&quot;token variable&quot;&gt;`&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-t&lt;/span&gt; australia-southeast1-docker.pkg.dev/prj-xyz-dev-fruit-0/rpo-xyz-dev-fruit-dkr-0/app/hellofruit:0.1&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Run the container locally.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;sh&quot;&gt;&lt;pre class=&quot;language-sh&quot;&gt;&lt;code class=&quot;language-sh&quot;&gt;&lt;span class=&quot;token function&quot;&gt;docker&lt;/span&gt; run &lt;span class=&quot;token parameter variable&quot;&gt;-p&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;5000&lt;/span&gt;:5000 australia-southeast1-docker.pkg.dev/prj-xyz-dev-fruit-0/rpo-xyz-dev-fruit-dkr-0/app/hellofruit:0.1&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;log&quot;&gt;&lt;pre class=&quot;language-log&quot;&gt;&lt;code class=&quot;language-log&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token date number&quot;&gt;2024-07-17&lt;/span&gt; &lt;span class=&quot;token time number&quot;&gt;11:34:23 +0000&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token level info keyword&quot;&gt;INFO&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; Starting gunicorn &lt;span class=&quot;token number&quot;&gt;22.0.0&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token date number&quot;&gt;2024-07-17&lt;/span&gt; &lt;span class=&quot;token time number&quot;&gt;11:34:23 +0000&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token level info keyword&quot;&gt;INFO&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token property&quot;&gt;Listening at:&lt;/span&gt; &lt;span class=&quot;token url&quot;&gt;http://0.0.0.0:5000&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token date number&quot;&gt;2024-07-17&lt;/span&gt; &lt;span class=&quot;token time number&quot;&gt;11:34:23 +0000&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token level info keyword&quot;&gt;INFO&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token property&quot;&gt;Using worker:&lt;/span&gt; gthread
&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token date number&quot;&gt;2024-07-17&lt;/span&gt; &lt;span class=&quot;token time number&quot;&gt;11:34:23 +0000&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;7&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token level info keyword&quot;&gt;INFO&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token property&quot;&gt;Booting worker with pid:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;7&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token date number&quot;&gt;2024-07-17&lt;/span&gt; &lt;span class=&quot;token time number&quot;&gt;11:34:23 +0000&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;8&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token level info keyword&quot;&gt;INFO&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token property&quot;&gt;Booting worker with pid:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;8&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Once we have tested that it works locally we can push it to the Docker registry.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;sh&quot;&gt;&lt;pre class=&quot;language-sh&quot;&gt;&lt;code class=&quot;language-sh&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;# First auth to the docker artifact registry&lt;/span&gt;
gcloud auth configure-docker australia-southeast1-docker.pkg.dev
&lt;span class=&quot;token comment&quot;&gt;# Push the local image to the artifact registry&lt;/span&gt;
&lt;span class=&quot;token function&quot;&gt;docker&lt;/span&gt; push australia-southeast1-docker.pkg.dev/prj-xyz-dev-fruit-0/rpo-xyz-dev-fruit-dkr-0/app/hellofruit:0.1&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 762px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/093bf0f658fc5f198418b08d846a3a08/98085/flask-poetry-docker-registry.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 48.199999999999996%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAKCAIAAAA7N+mxAAAACXBIWXMAAAsSAAALEgHS3X78AAABE0lEQVQoz42Ry3KDMAxF+f//ayYT8nBDw0AwsWzJll9Ax24XLDkbWYszlq6aYRyIiJAQkawtT8QQgpTzPM/sLCGFEMZxFOJhEEEVZimnaWqstSmlFFOMtcYYvM85u0rpY8w5ExEAeO+Z2Xt2rnzTnM/na3u93W7tpT19naxz27at63aE5tJeQQEz2woiuuofkl8/3b7XWksp87IckkHHWaXpE3Ne68DrcswssjaWObIvgf1ls1QOyd3z+/ORWoMxGhGNMVJKJFqWJcbIjlNKOeeScb3CngYAuq7v+0GBCSEqpYQQ4zh670Gpx/0OUOIUQjyfHTO7HQ0iDjK9ZejfbCjVrf9JKSEa7/22bdY6quOsO34Bw7FD7UqZnvUAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;flask poetry docker registry&quot;
        title=&quot;flask poetry docker registry&quot;
        src=&quot;/static/093bf0f658fc5f198418b08d846a3a08/98085/flask-poetry-docker-registry.png&quot;
        srcset=&quot;/static/093bf0f658fc5f198418b08d846a3a08/0eb09/flask-poetry-docker-registry.png 500w,
/static/093bf0f658fc5f198418b08d846a3a08/98085/flask-poetry-docker-registry.png 762w&quot;
        sizes=&quot;(max-width: 762px) 100vw, 762px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;h4 id=&quot;Cloud-Run-the-service&quot; style=&quot;position:relative;&quot;&gt;Cloud Run the service&lt;a href=&quot;#Cloud-Run-the-service&quot; aria-label=&quot;Cloud Run the service permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;Using the GCP web console, it is simple to select the container from the Artifact Registry and set the Container port to 5000. The remaining settings are left as the default settings.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 671px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/c7dbf08514d19ef30b2a7a92d1e5089f/b5f03/flask-poetry-cloud-run-container-settings.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 56.8%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAALCAIAAADwazoUAAAACXBIWXMAAAsSAAALEgHS3X78AAABRElEQVQoz6VR7Y6DIBDs+z/gtU2u9TRK5aMKyyIIghfANrnkkvtxE9xMcBdmhtM8Tefzx/lyYYzv+x5jTCntf6H2nPZ/4LRtm/e+67qmabquu16vUspydpVQ117JW9Q0Td77U0oJEYdh6Pu+aZq2ba21L22lPf1QW3zFZbExxiybMX67fRJC7vc7pXSeJeecMSaEeD4nIQRjrOzweX6iCQO1xvqUUh7WoPMvSseRPh4jISTXgQzDQAgB0IioNQJoY5Z1DWi899sxjGYZ6SQEAGgAkFKCzkQpBQDLYpbyGYMSwqzCJEMsXvIwaPfVzYxDCKG8VjYW04FYsG1HDdvBj5sJIW3bKCWdW2sqNaf0G2qD9+EITClQCpxb0RhENLmaWrHAvEg176wL29szYt/345gDYzW4AiG4tdY5Z6tvu1T49RD4DaMyeq2/B3RUAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;flask poetry cloud run container settings&quot;
        title=&quot;flask poetry cloud run container settings&quot;
        src=&quot;/static/c7dbf08514d19ef30b2a7a92d1e5089f/b5f03/flask-poetry-cloud-run-container-settings.png&quot;
        srcset=&quot;/static/c7dbf08514d19ef30b2a7a92d1e5089f/0eb09/flask-poetry-cloud-run-container-settings.png 500w,
/static/c7dbf08514d19ef30b2a7a92d1e5089f/b5f03/flask-poetry-cloud-run-container-settings.png 671w&quot;
        sizes=&quot;(max-width: 671px) 100vw, 671px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;Once the Cloud Run service has completed, you should see the service running and be given a URL to access the service.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 791px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/49a191c53b905b9c74279dd7df951a0b/17459/flask-poetry-cloud-run-service.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 50%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAKCAIAAAA7N+mxAAAACXBIWXMAAAsSAAALEgHS3X78AAABfUlEQVQoz3WRy27jMAxF/f9/2DSNk9pILFuS9aAkiw9p4HQWncUc3AUJXJK44PB5+Xy9XtM8j+MIACI9F7HWzNM8T9NtHBGxSTc7aoubqfos6qqrcXUAAESkN+0vnZkRqdZaEc++d5b2W8KNpQ/TNumgt1V/Xb/UqpBo3/cYo3eemWOIwRVvc9xzsBl8SeFI8dB6C8EPFSsSIuJRCiKyyPseMmHvjYlqwVoID66FTh2ElX88QynFWsvMROS9670DxHPV0R7PMqtDWk8JluVVa2VmbbRSKufcex9CCEot1trWJBXePfpILiAkQpIDhUjafxgiwLauznkWKYVDJEgU35P9F63/w5mLeQjBreuaU4ox5gytydvZWhMiEpGfX4iwCDOfaXtvz+fTWjtEKLf7Y7w/FrUB5BAgxhxiMma/3e7ex8vlOt6/tdm3zSxq+7hcH4/JuaBWPcwKb6/FuqS2sCinDUCWzcTVRG0hFbEurzoEoN0fAahSP7DvvkTAP6ClPbFHuJh1AAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;flask poetry cloud run service&quot;
        title=&quot;flask poetry cloud run service&quot;
        src=&quot;/static/49a191c53b905b9c74279dd7df951a0b/17459/flask-poetry-cloud-run-service.png&quot;
        srcset=&quot;/static/49a191c53b905b9c74279dd7df951a0b/0eb09/flask-poetry-cloud-run-service.png 500w,
/static/49a191c53b905b9c74279dd7df951a0b/17459/flask-poetry-cloud-run-service.png 791w&quot;
        sizes=&quot;(max-width: 791px) 100vw, 791px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 672px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/ae97fa3c3cb28c29608022c3fd2feed3/00e42/flask-poetry-cloud-run-instance.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 27%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAFCAIAAADKYVtkAAAACXBIWXMAAAsSAAALEgHS3X78AAAA30lEQVQY033I207CQBSF4b7/86ih3gAJhjtjDLbUEzTTOdChnUMLzql7GyWGO1e+rIs/Yw6rdnxhatvIsuZFLYpaVE1bkkNJ5CvXG24rld4sbjUWGjcKn3t86vHxiBm1vjGeqPNOGmbclRqINFSNzDqiHTFhr/xO+b1Nnya9q/ihUybMqT/H7hRoZ7WHP6jsILteD6O2Q0M5F4eGcUKZaKWQR0J5p0325ZwPMabkQgDEizRNCeDHBHGCBJguDxh/S5wgJMjubm7zfPawWt3ns/l8sVwu1uu1cx4RAQD/3Tcr3Rd54odY6gAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;flask poetry cloud run instance&quot;
        title=&quot;flask poetry cloud run instance&quot;
        src=&quot;/static/ae97fa3c3cb28c29608022c3fd2feed3/00e42/flask-poetry-cloud-run-instance.png&quot;
        srcset=&quot;/static/ae97fa3c3cb28c29608022c3fd2feed3/0eb09/flask-poetry-cloud-run-instance.png 500w,
/static/ae97fa3c3cb28c29608022c3fd2feed3/00e42/flask-poetry-cloud-run-instance.png 672w&quot;
        sizes=&quot;(max-width: 672px) 100vw, 672px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;h2 id=&quot;Next-steps&quot; style=&quot;position:relative;&quot;&gt;Next steps&lt;a href=&quot;#Next-steps&quot; aria-label=&quot;Next steps permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Set up CICD for the deployment of both the package and the application.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Converting table schemas from any database to BigQuery for Dataflow]]></title><description><![CDATA[Why do this? When using Dataflow Google-provided templates for any RDBMS to BigQuery (such as PostgreSQL to BigQuery) the target table in…]]></description><link>https://mortimer.xyz/bigquery-ddl-helper/</link><guid isPermaLink="false">https://mortimer.xyz/bigquery-ddl-helper/</guid><pubDate>Wed, 01 May 2024 00:00:00 GMT</pubDate><content:encoded>&lt;h1 id=&quot;Why-do-this&quot; style=&quot;position:relative;&quot;&gt;Why do this?&lt;a href=&quot;#Why-do-this&quot; aria-label=&quot;Why do this permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h1&gt;
&lt;p&gt;When using Dataflow &lt;a href=&quot;https://cloud.google.com/dataflow/docs/guides/templates/provided-templates&quot;&gt;Google-provided templates&lt;/a&gt; for any &lt;strong&gt;RDBMS to BigQuery&lt;/strong&gt; (such as PostgreSQL to BigQuery) the target table in BigQuery has to already exist. It logically must have the correct column names and data types.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 767px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/7ec38bf9b950c6d18e60e1c2e7436757/dff2b/bigquery-ddl-helper-flex.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 52.6%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAALCAIAAADwazoUAAAACXBIWXMAAAsSAAALEgHS3X78AAABC0lEQVQoz42P3U7DMAxG8/7vxwVQBC1rU7IkjuP8NWkCbN0mhibY0bmxpU/2xx468/iGL+/UDe55oJs+9TSJYGwCPCkhhFRYvxPTLF6HUSp0fiGXboqU0MaTGCYOHzKwYcRh3D92u5ED+YpUbuuuNJSdT8w5klJwPiGaWkur6z3Wuua8MKV03/fjbuy6DgCWJceYUlr+doN577XW0zRxzltrn/fRWjuEQwgAoI9Ya5VS1tp7wjlnFmMEACICAKXUPM9SSvsDIrLWGmNijJdwKcV7f7islEJErfUWFkLIM+pICKGUsq7rpVeM0RjDtrldc9n8+rasDV2trZWcU0rMGIP/Yc5oALGHje/OX4qJdrh4p0Q6AAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;bigquery ddl helper flex&quot;
        title=&quot;bigquery ddl helper flex&quot;
        src=&quot;/static/7ec38bf9b950c6d18e60e1c2e7436757/dff2b/bigquery-ddl-helper-flex.png&quot;
        srcset=&quot;/static/7ec38bf9b950c6d18e60e1c2e7436757/0eb09/bigquery-ddl-helper-flex.png 500w,
/static/7ec38bf9b950c6d18e60e1c2e7436757/dff2b/bigquery-ddl-helper-flex.png 767w&quot;
        sizes=&quot;(max-width: 767px) 100vw, 767px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;We want a way to automate the process of reading a source table schema and converting it to a BigQuery schema and then deploying it.&lt;/p&gt;
&lt;h2 id=&quot;Example-source-system&quot; style=&quot;position:relative;&quot;&gt;Example source system&lt;a href=&quot;#Example-source-system&quot; aria-label=&quot;Example source system permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;For demonstration purposes we will use a Postgres database. It is hosted on a Cloud VM and the firewall rules have been set so that my local machine can access it as well as the Dataflow worker node.&lt;/p&gt;
&lt;p&gt;I am using the HeidiSQL IDE to connect to one of the databases on the instance.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 1670px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/5a9f035206fa8450dc02147ff2d6499f/941a4/bigquery-ddl-helper-postgres-heidi.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 50.2%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAKCAIAAAA7N+mxAAAACXBIWXMAAAsSAAALEgHS3X78AAABr0lEQVQoz02PC46bQAyGuUkJzAMGhhl73sAQCCTRZjdSD9D7n6NiW6mVPlmWfn+yXYzPVzhe6flOj08RMsXAMTCM3KTWjdzNjYmNicwkhvEEIrdeLbkfU2GW3e4v3F8qheG6D9Oul7vKh4wTzEu3vKR1HXpmIoNAwTMYxQ3WX5P9csV8f+fnp0Bf95pjxOXehaWSWPWmFFAKVXb60sHlrLoUimivpkcLqRRYUDfr5SCDodqzb9lu7y4efbrqeWXgLkL9Mb9lTSWsjwXyVkld1Cba7cm1I8pRjF3IejqGdGutl8mR4Xv5f1CJ8eun2R4NhoKaZNY7GQyDINeH3T9g2XE91LS2ZmxMEm7swtyF3JrEMfYhCzcziBR8UUPA68HBlUI3Icp16vMor5MYk5him0ItkSpn804kEjDM+jaF84seChGvdn1Una6l4T602fPkmtnz6JrJN8n9aIeqR5O3WkINWAO2k68VVhIKOx922akyZauosc3omXc8euYsD5Y5WwpV9SjHTMEQwFrhOWNtNWDBtKfanWcIRZQfpk3PN+FnMliqPTnTvxD1r6dwRr8BhpJ1ohyiH7gAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;bigquery ddl helper postgres heidi&quot;
        title=&quot;bigquery ddl helper postgres heidi&quot;
        src=&quot;/static/5a9f035206fa8450dc02147ff2d6499f/941a4/bigquery-ddl-helper-postgres-heidi.png&quot;
        srcset=&quot;/static/5a9f035206fa8450dc02147ff2d6499f/0eb09/bigquery-ddl-helper-postgres-heidi.png 500w,
/static/5a9f035206fa8450dc02147ff2d6499f/1263b/bigquery-ddl-helper-postgres-heidi.png 1000w,
/static/5a9f035206fa8450dc02147ff2d6499f/941a4/bigquery-ddl-helper-postgres-heidi.png 1670w&quot;
        sizes=&quot;(max-width: 1670px) 100vw, 1670px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;The table schema can be obtained from the &lt;em&gt;information_schema.columns&lt;/em&gt; view.&lt;/p&gt;
&lt;h2 id=&quot;BeamMeUp&quot; style=&quot;position:relative;&quot;&gt;BeamMeUp&lt;a href=&quot;#BeamMeUp&quot; aria-label=&quot;BeamMeUp permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Managing many table loads that use Dataflow &lt;a href=&quot;https://cloud.google.com/dataflow/docs/guides/templates/provided-templates&quot;&gt;Google-provided templates&lt;/a&gt; across many environments is not straight forward. One such issue is the one outlined in &lt;a href=&quot;https://mortimer.xyz/gcp-kms-dataflow/&quot;&gt;this blog post&lt;/a&gt; regarding the need to encrypt and encode credentials.&lt;/p&gt;
&lt;p&gt;In an attempt to not repeat myself &lt;a href=&quot;https://en.wikipedia.org/wiki/Don%27t_repeat_yourself&quot;&gt;DRY&lt;/a&gt; while also trying to not over engineer things &lt;a href=&quot;https://en.wikipedia.org/wiki/You_aren%27t_gonna_need_it&quot;&gt;YAGNI&lt;/a&gt;, I developed a Python package with some reuse helper functions for working with Dataflow. I called it &lt;a href=&quot;https://github.com/mortie23/beam-poetry-mono/tree/master/eng/lib/beammeup&quot;&gt;BeamMeUp&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;One of the functions within BeamMeUp that this process relies on is the &lt;code class=&quot;language-text&quot;&gt;get_db_type_encoder&lt;/code&gt; function.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;py&quot;&gt;&lt;pre class=&quot;language-py&quot;&gt;&lt;code class=&quot;language-py&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;get_db_type_encoder&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
    db_type&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; pd&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;DataFrame&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token triple-quoted-string string&quot;&gt;&quot;&quot;&quot;Returns an encoder dataframe for source system to target system (BigQuery in this case)

    Args:
        db_type (str): The type of database (postgres or mssql)

    Returns:
        pd.DataFrame: a dataframe of the data type mappings
    &quot;&quot;&quot;&lt;/span&gt;
    columns &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;db_type&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;source_type&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;target_type&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
    data &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;mssql&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;char&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;string&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;mssql&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;date&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;date&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;mssql&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;varchar&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;string&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;postgres&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;character&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;string&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;postgres&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;date&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;date&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
    db_type_encoder &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; pd&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;DataFrame&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;data&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; columns&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;columns&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; db_type_encoder&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;db_type_encoder&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;db_type&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;==&lt;/span&gt; db_type&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2 id=&quot;DDL-Helper&quot; style=&quot;position:relative;&quot;&gt;DDL Helper&lt;a href=&quot;#DDL-Helper&quot; aria-label=&quot;DDL Helper permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;I developed a helper script that can be used for both PostgreSQL and Microsoft SQL Server source systems.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/mortie23/beam-poetry-mono/blob/master/eng/tools/ddlhelper/bqddl-postgres-from-source.py&quot;&gt;bqddl-postgres-from-source.py&lt;/a&gt;&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;sh&quot;&gt;&lt;pre class=&quot;language-sh&quot;&gt;&lt;code class=&quot;language-sh&quot;&gt;python3 bqddl-postgres-from-source.py &lt;span class=&quot;token parameter variable&quot;&gt;--env&lt;/span&gt; dev&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;log&quot;&gt;&lt;pre class=&quot;language-log&quot;&gt;&lt;code class=&quot;language-log&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token time number&quot;&gt;11:25:21&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; Creating table DDL from source 🚀  mortimer_nfl&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;weather       dbtools&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;py&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;409&lt;/span&gt;
               writing DDL to repo                                       dbtools&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;py&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;435&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;This will generate a script in appropriate folder within the repo.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;📁 ddl/
│   ├── 📁 bigquery/
│   │   ├── 📁 fruit-0/
│   │   │   │   📄 nfl.weather.sql&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;sql&quot;&gt;&lt;pre class=&quot;language-sql&quot;&gt;&lt;code class=&quot;language-sql&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;create&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;table&lt;/span&gt; nfl&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;weather &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
&lt;span class=&quot;token identifier&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;`&lt;/span&gt;weather_game_id&lt;span class=&quot;token punctuation&quot;&gt;`&lt;/span&gt;&lt;/span&gt; int64
  &lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;token identifier&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;`&lt;/span&gt;game_id&lt;span class=&quot;token punctuation&quot;&gt;`&lt;/span&gt;&lt;/span&gt; int64
  &lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;token identifier&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;`&lt;/span&gt;temperature&lt;span class=&quot;token punctuation&quot;&gt;`&lt;/span&gt;&lt;/span&gt; int64
  &lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;token identifier&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;`&lt;/span&gt;weather_condition&lt;span class=&quot;token punctuation&quot;&gt;`&lt;/span&gt;&lt;/span&gt; string
  &lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;token identifier&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;`&lt;/span&gt;wind_speed&lt;span class=&quot;token punctuation&quot;&gt;`&lt;/span&gt;&lt;/span&gt; int64
  &lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;token identifier&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;`&lt;/span&gt;humidity&lt;span class=&quot;token punctuation&quot;&gt;`&lt;/span&gt;&lt;/span&gt; int64
  &lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;token identifier&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;`&lt;/span&gt;wind_direction&lt;span class=&quot;token punctuation&quot;&gt;`&lt;/span&gt;&lt;/span&gt; string
  &lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;token identifier&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;`&lt;/span&gt;created_date&lt;span class=&quot;token punctuation&quot;&gt;`&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;timestamp&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;token identifier&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;`&lt;/span&gt;create_user&lt;span class=&quot;token punctuation&quot;&gt;`&lt;/span&gt;&lt;/span&gt; string
&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;We can now deploy this to the appropriate project in GCP using the &lt;code class=&quot;language-text&quot;&gt;--run_flag&lt;/code&gt;.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;sh&quot;&gt;&lt;pre class=&quot;language-sh&quot;&gt;&lt;code class=&quot;language-sh&quot;&gt;python3 bqddl-postgres-from-source.py &lt;span class=&quot;token parameter variable&quot;&gt;--env&lt;/span&gt; dev &lt;span class=&quot;token parameter variable&quot;&gt;--run_flag&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;log&quot;&gt;&lt;pre class=&quot;language-log&quot;&gt;&lt;code class=&quot;language-log&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token time number&quot;&gt;11:25:21&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; Creating table DDL from source 🚀  mortimer_nfl&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;weather       dbtools&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;py&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;409&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token time number&quot;&gt;11:25:22&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;     writing DDL to repo                                       dbtools&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;py&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;435&lt;/span&gt;
               Deploying TABLE DDL to BigQuery                           dbtools&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;py&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;451&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;This will have dropped the table if it existed and created the new one ready to be loaded by Dataflow.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 540px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/17242bba6366ec8e2f615c75c4e324d5/03ec9/bigquery-ddl-helper-weather.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 71.6%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAOCAIAAACgpqunAAAACXBIWXMAAAsSAAALEgHS3X78AAABiElEQVQoz4WR3WrjMBSE/f4vsHd9iQ29KLTsloWUEGjZLE3rn8RSYus3tiTLsusgqcQ2paVxdu4E+mbmnBMopaQUcZKk2802BUkcbzabruv8OTnnvPdZlhVFQSkNnHNt0xRFASEohWCcVVU1fJrSWy8hROC9b9tWa/368pLnWZqmxjT+f7LWnpIHJ4zxbPYToTw+1d5WVfVRcgrmnJ/gruuEEMvl8sA5QjkhROvaWncZHmsbY4QQq9Uqy/bZfg9SECeJkOpCuLWWEBIMD2NMGL5SSjFCnDNCKMaEENy27RR8OBxGuGmaKIowQhBClOc7uAMQQgDr2vT50zMbYxhj8/lclKW9dKMvdx6TrbXH4xFjvF6vRSmUqmwv587vTEppjFFKBR9+lVJ3d7dxFOUI9YT7Xnf4SSmVUo7JI6z1r/t7kAJKSQoA5Xxq24yxT3BvqnH2cHON/y758xML/2m088e3s5NLKeu67u88mBslFrezqx/x4+Lpz28cPnu8dTD0zp5dWFmWjLF3Yb0iFjWk25IAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;bigquery ddl helper weather&quot;
        title=&quot;bigquery ddl helper weather&quot;
        src=&quot;/static/17242bba6366ec8e2f615c75c4e324d5/03ec9/bigquery-ddl-helper-weather.png&quot;
        srcset=&quot;/static/17242bba6366ec8e2f615c75c4e324d5/0eb09/bigquery-ddl-helper-weather.png 500w,
/static/17242bba6366ec8e2f615c75c4e324d5/03ec9/bigquery-ddl-helper-weather.png 540w&quot;
        sizes=&quot;(max-width: 540px) 100vw, 540px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;</content:encoded></item><item><title><![CDATA[A way to build CRUD apps with R Shiny and SQLite]]></title><description><![CDATA[Background Often as software engineers working within organisations we are coerced into using a software stack that we may not have chosen…]]></description><link>https://mortimer.xyz/shinelite/</link><guid isPermaLink="false">https://mortimer.xyz/shinelite/</guid><pubDate>Wed, 01 May 2024 00:00:00 GMT</pubDate><content:encoded>&lt;h1 id=&quot;Background&quot; style=&quot;position:relative;&quot;&gt;Background&lt;a href=&quot;#Background&quot; aria-label=&quot;Background permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h1&gt;
&lt;p&gt;Often as software engineers working within organisations we are coerced into using a software stack that we may not have chosen if we were tasked with designing it from the start. This is part of the fun and part of what keeps our jobs interesting. Also we may be able to come to the conclusion that it makes sense to utilise a component in a system design that is less than optimal for it’s specific use case, but makes sense overall.&lt;/p&gt;
&lt;p&gt;Take R Shiny for example. An R centric framework for full stack web applications. From my limited exposure, common use cases for R Shiny are read only dashboards, but often we require mutations to data such as create, update and delete operations.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Shiny helps you turn your analyses into interactive web applications without requiring HTML, CSS, or JavaScript knowledge.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;There is some really great work that has been done in this space including but not limited to:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://shiny.posit.co/r/articles/build/persistent-data-storage/&quot;&gt;Persistent data storage in Shiny apps&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://forum.posit.co/u/barbara/summary&quot;&gt;Barbara Borges Ribeiro&lt;/a&gt;’s presentation on
&lt;a href=&quot;https://rawgit.com/bborgesr/wsds2017/master/presentation.html#(1)&quot;&gt;Interacting with Databases from R and Shiny&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Although there is a fair bit of content out there already on this, none of it exactly met my use case, so I decided to give a go at a basic scaffold of a R package for a CRUD application using R Shiny and SQLite.&lt;/p&gt;
&lt;p&gt;Welcome &lt;a href=&quot;https://github.com/mortie23/shinelite&quot;&gt;ShineLite&lt;/a&gt;.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;A basic lite CRUD application framework for RShiny application development with a SQlite back end.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;The &lt;em&gt;extra&lt;/em&gt; things I wanted to build into it were:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Environment life cycle to connect to different DBs based on development/production etc&lt;/li&gt;
&lt;li&gt;Package dependencies with &lt;a href=&quot;https://github.com/rstudio/renv/&quot;&gt;renv&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h1 id=&quot;Setup&quot; style=&quot;position:relative;&quot;&gt;Setup&lt;a href=&quot;#Setup&quot; aria-label=&quot;Setup permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h1&gt;
&lt;h2 id=&quot;OS-binaries&quot; style=&quot;position:relative;&quot;&gt;OS binaries&lt;a href=&quot;#OS-binaries&quot; aria-label=&quot;OS binaries permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;I started developing this on a vanilla Ubuntu VM and tried to capture all the things needed to get it up and running.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 1808px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/f1c129b2f7c8bf28722ff75fe1b266a5/f766a/shinelite-vm.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 43.2%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAJCAIAAAC9o5sfAAAACXBIWXMAAAsSAAALEgHS3X78AAACD0lEQVQoz2Ows7a0tbO1sLQ0t7QwtzQ3t7AAMSzMdfR0JqzYcPXz/7OP3l9/+fX2m+/3Pv59/uX7jf7MpuXb9x2a9/d5D4OaopqaopqitLyKnJKWsoa8hIyClJyGkpqavFJGRdvcXRcmLlwza9mGaUvWzVq+YfH6HbM6WzILa3t66pcsbmVwUrZ2VrF2VbZyVrJ0lDOzlTKyENE14lM34VH18Iz3Satw8Auz9Y20D4hzD09xDYl3Ck/zjUm39Y+38Eli8NKw89F28tZy9FS3t5MyshLTM+ZVN+BS0WdX8veJCE7JjkrOD07OdwtNcfGK9/COc/WMsHMNtHHwsXb0Y3BVsvRUt3NXsXFRtDTmU9fnVDbgVNHnUjHiVnW29bVw8TezdDM0c9QzcTbQczTQddDVsdPWttbVsdE1dGZwUrRyU7P1ULN1UbKyENYx4VM34FY14FIx4lHzcQl2dPXX0rZUUTVU07FU1zZT1zLR0DLV0DLT0DTV1Ldh0OfVMOLXNOHTMOBSMeBWNeRRAzG4VMz4tfx9oiMyS3wjU73CEi2c/dU0wdogCKTZjkGTWV6HVVGXVcGQS9WQUwWEuFQNuFSNOFWcbXyc/CJcvMKcvUMtHHzV0TXbMgQYmTnrmFhomshxSyjxyyjySSvySSvwSqmLKOrpWOroWxsY2ekbWKvpWmvoWGpoW0CRlrmmgT0A6OONxR+MUKAAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;shinelite vm&quot;
        title=&quot;shinelite vm&quot;
        src=&quot;/static/f1c129b2f7c8bf28722ff75fe1b266a5/f766a/shinelite-vm.png&quot;
        srcset=&quot;/static/f1c129b2f7c8bf28722ff75fe1b266a5/0eb09/shinelite-vm.png 500w,
/static/f1c129b2f7c8bf28722ff75fe1b266a5/1263b/shinelite-vm.png 1000w,
/static/f1c129b2f7c8bf28722ff75fe1b266a5/f766a/shinelite-vm.png 1808w&quot;
        sizes=&quot;(max-width: 1808px) 100vw, 1808px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;sh&quot;&gt;&lt;pre class=&quot;language-sh&quot;&gt;&lt;code class=&quot;language-sh&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;# Run this from your terminal (not console)&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;# Install all these OS binaries or you will get caught during the renv restore&lt;/span&gt;
&lt;span class=&quot;token function&quot;&gt;sudo&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;apt&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;install&lt;/span&gt; build-essential
&lt;span class=&quot;token function&quot;&gt;sudo&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;apt&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;install&lt;/span&gt; libcurl4-openssl-dev
&lt;span class=&quot;token function&quot;&gt;sudo&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;apt&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;install&lt;/span&gt; libfontconfig1-dev
&lt;span class=&quot;token function&quot;&gt;sudo&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;apt&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;install&lt;/span&gt; libharfbuzz-dev libfribidi-dev
&lt;span class=&quot;token function&quot;&gt;sudo&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;apt&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;install&lt;/span&gt; libfreetype6-dev libpng-dev libtiff5-dev libjpeg-dev
&lt;span class=&quot;token function&quot;&gt;sudo&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;apt&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;install&lt;/span&gt; libxml2-dev&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2 id=&quot;The-SQLite-DB&quot; style=&quot;position:relative;&quot;&gt;The SQLite DB&lt;a href=&quot;#The-SQLite-DB&quot; aria-label=&quot;The SQLite DB permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The package has a configuration function within &lt;code class=&quot;language-text&quot;&gt;R/config.R&lt;/code&gt;. It will return the path for where your SQLite DB file is hosted. In this case the path is &lt;code class=&quot;language-text&quot;&gt;/u01/data/shinelite/&amp;lt;env&gt;/todo_app.db&lt;/code&gt;. This would be changed per use case. There is probably a better way to do this, but it works for now.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;r&quot;&gt;&lt;pre class=&quot;language-r&quot;&gt;&lt;code class=&quot;language-r&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;#&apos; Set environment&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;#&apos;&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;#&apos; @return config&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;#&apos; @export&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;#&apos;&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;#&apos; @examples set_environment()&lt;/span&gt;
set_environment &lt;span class=&quot;token operator&quot;&gt;&amp;lt;-&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token comment&quot;&gt;# Get the environment&lt;/span&gt;
  env &lt;span class=&quot;token operator&quot;&gt;&amp;lt;-&lt;/span&gt; Sys.getenv&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;env&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

  &lt;span class=&quot;token comment&quot;&gt;# Define configurations for each environment&lt;/span&gt;
  dev_config &lt;span class=&quot;token operator&quot;&gt;&amp;lt;-&lt;/span&gt; list&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
    db_path &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;/u01/data/shinelite/dev/todo_app.db&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    other_config_variable &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;value&quot;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

  prd_config &lt;span class=&quot;token operator&quot;&gt;&amp;lt;-&lt;/span&gt; list&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
    db_path &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;/u01/data/shinelite/prd/todo_app.db&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    other_config_variable &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;value&quot;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

  &lt;span class=&quot;token comment&quot;&gt;# Set configuration based on environment&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;env &lt;span class=&quot;token operator&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;dev&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    return&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;dev_config&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;env &lt;span class=&quot;token operator&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;prd&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    return&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;prd_config&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    stop&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Unknown environment specified&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The next setup step is to initialise the SQLite DB in the development environment.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;sh&quot;&gt;&lt;pre class=&quot;language-sh&quot;&gt;&lt;code class=&quot;language-sh&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;# Run this from your terminal (not console)&lt;/span&gt;
&lt;span class=&quot;token builtin class-name&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token assign-left variable&quot;&gt;env&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;dev
&lt;span class=&quot;token assign-left variable&quot;&gt;db_path&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;/u01/data/shinelite/dev/todo_app.db&quot;&lt;/span&gt;
sqlite3 &lt;span class=&quot;token variable&quot;&gt;$db_path&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;EOF
CREATE TABLE IF NOT EXISTS todos (
    id INTEGER PRIMARY KEY,
    todo TEXT,
    done BOOLEAN DEFAULT FALSE
);
EOF&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2 id=&quot;The-R-environment&quot; style=&quot;position:relative;&quot;&gt;The R environment&lt;a href=&quot;#The-R-environment&quot; aria-label=&quot;The R environment permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Create a &lt;code class=&quot;language-text&quot;&gt;.Renviron&lt;/code&gt; file in the root of the cloned repository and populate it with this:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;ini&quot;&gt;&lt;pre class=&quot;language-ini&quot;&gt;&lt;code class=&quot;language-ini&quot;&gt;&lt;span class=&quot;token key attr-name&quot;&gt;env&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token value attr-value&quot;&gt;dev&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Start with the initial install of &lt;strong&gt;renv&lt;/strong&gt;.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;r&quot;&gt;&lt;pre class=&quot;language-r&quot;&gt;&lt;code class=&quot;language-r&quot;&gt;install.packages&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;renv&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Now install everything else with a renv restore&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;r&quot;&gt;&lt;pre class=&quot;language-r&quot;&gt;&lt;code class=&quot;language-r&quot;&gt;renv&lt;span class=&quot;token operator&quot;&gt;::&lt;/span&gt;restore&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;We also need the &lt;a href=&quot;https://github.com/r-lib/devtools&quot;&gt;devtools&lt;/a&gt; package to make all functions available.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;r&quot;&gt;&lt;pre class=&quot;language-r&quot;&gt;&lt;code class=&quot;language-r&quot;&gt;devtools&lt;span class=&quot;token operator&quot;&gt;::&lt;/span&gt;load_all&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;.&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h1 id=&quot;Run-the-RShiny-application-in-dev-mode&quot; style=&quot;position:relative;&quot;&gt;Run the RShiny application in dev mode&lt;a href=&quot;#Run-the-RShiny-application-in-dev-mode&quot; aria-label=&quot;Run the RShiny application in dev mode permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h1&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;r&quot;&gt;&lt;pre class=&quot;language-r&quot;&gt;&lt;code class=&quot;language-r&quot;&gt;runShineLite&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Remember to add the two most important things to your new todo list:&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 668px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/9e4eb2b9a3cb1634ad3802edc812d18d/1d36f/shinelite-app.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 155.6%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAfCAYAAADnTu3OAAAACXBIWXMAAAsSAAALEgHS3X78AAAEXUlEQVRIx62Vy48UVRTGa+ffIYkkjgxs3EkiO4zjH8HGSKJmaAETcK2JOxMSEzfGgDPMgCCvuNKA0zC8gjAO/aKrH9XVj+npV3VPVVfdulX1M1X9mB5oBiXe5Mu5fe/pL6fO47vKu2fPizeWr6Cc+zVQzl1GWbyG8vMVlIWrKGcvoSzdQDl/DWX5BsridZSfLqJc/QPl+k2U67dQLvwW2kC5chPl6i1TWVR1Luf1YFnVWM7rLGaLLOU0lnIlLhTKXNQqXNJrLCWe8v39Jywk0yxX6vxYrHHuWYZL+QK/lApcVleD71JxlEKzF2w1OhjVRtButKmWKjRrDZq1TYxGi353C6drILKPsZ/eY6uQplJvo5ZabOglgtYzrM1kIPUF1nMLgXI/UyCT10mGyGlkihXShTJJtThGKquS+ushWibF2toaNx+uc+vBOr+v3EXNrlMo6zzK5bj4II7StPr0TRPHcXBsB+EIbNvGsqzIhmeuKxFegOuDKz2kEHjSRUqJcH1s4eFJWGubKIYQaMUiqXSGcrlMsajRMQxKJY1cLoeqqmRVld1WMLRps49iuC4EAcEQvu8PnIb7MArXdcf30+D5A8rkloXSdWX0I/yTECKyjhCR444odiH0h76pScJqrYZW0qhUq+QLxYh4kmi3tZNQypfnZkwUTL2bGmHHcaiWy5Sr1agom5uNHcl+2WdOEu8gbAlJvWNSaXbYaPcidCyHZq9P13bpOfIFdB2JYcvtokwSNoVE3zTIFjS0ygZpNU/iWY5soUTXcui7Ppbw6LteZEeob4koskGV/W1Cw5U4Htg+2N4AfQ8cf3DWlwFWSOr60fnIT/gv5jwibJgm337zNZ9/9inHYzG+iB0bI3Zsni9PnuCr06c4feoUJ08cj84Gd8eYn58nFovxydGj/HDmDCnTQqkZXT6am+OtvXuZ3b+fffv2DTA7y8zMO7x/6BAfHD7Mh3NzHDz4Hm/PzDA7Ozv2O3DgAG/u2cPHR46MqixYe/yY+O3brK6uvoA7d+4Qj8cjhPtpPn+uxMkmE4MIe9Lj/1rjxg48D1fKaG5fB2KoBxOTEoxb4HWwo21Go/d8979qfif9ps7ytKl9VWTP6+EOtWk0m3Q6nUhlEokkrVZ718hCrQxnP5FMUN3YeC7CUEg9b5xk0zQj+R+J60gnw+RHT4Uz0EvTtOh2e1h9e5swbBvpOEORFViWSaVSQddD5alTKZfRdR1dL1Gv1ynrJQpDvazoFeK371EslScUOyr7YPgjCIkt/chaw70jfWzpRdbxgoGVPj3Tptnp0e5ZUSKHn+xRaxo0LUFzy6FUb/F3Jk8ifEJzRZ6kVOqGRceWtCyXtuXSsgSG45PJFlhZuUta1XDlRB+6Xqgooyj9cbR2JF2Svpj4gtFdGKFl0za2MEwb/O2iBIPS/4vGm/p4RrtgmMNAMT1v+qPxn7gHMWXCd1nr26LhCOq2E9Rth9fBhu0EHeHyqNPr/QPd+OzySuzPRwAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;shinelite app&quot;
        title=&quot;shinelite app&quot;
        src=&quot;/static/9e4eb2b9a3cb1634ad3802edc812d18d/1d36f/shinelite-app.png&quot;
        srcset=&quot;/static/9e4eb2b9a3cb1634ad3802edc812d18d/0eb09/shinelite-app.png 500w,
/static/9e4eb2b9a3cb1634ad3802edc812d18d/1d36f/shinelite-app.png 668w&quot;
        sizes=&quot;(max-width: 668px) 100vw, 668px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;h1 id=&quot;Checking-the-DB&quot; style=&quot;position:relative;&quot;&gt;Checking the DB&lt;a href=&quot;#Checking-the-DB&quot; aria-label=&quot;Checking the DB permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h1&gt;
&lt;p&gt;Just to show how to confirm that the DB was written to using the CLI.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 451px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/d621df8e48185d0c680d61d920e85853/99c16/shinelite-sqlite.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 62.08425720620843%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAMCAYAAABiDJ37AAAACXBIWXMAAA7DAAAOwwHHb6hkAAACoElEQVQoz22R627iBhCF/SJtIISQdNNCsI0xAXzBNsYYm4vBBttAIaRo2W5XrdSu2of/KlxplR/9cTQzGs2nOTPC0DiwnHxh0Muwu6+M9QtGJ6fbirC6Rww1Re+kWL0cXfcRFZX50mG5cZmtHMLIIckdpvOQbs9DsEchb5cL1ihAUvu0ezpqz6TTt4r6RbN5fJK4rda5u29Se5S5KTf5vtSkVBYp34p8d9OkdPtMpfqMEHqvxLNfSePP7DZfOaRfSZMvxIszefwXh/xPLH2NKgd0Xlx+bCgMPQPXN3E8A8s18AIDe+jR7jgIprZmZG2pP9nIjZAXaYXSDBEbLvKzj9zwaEsBHWWK3DJpiG1MW2c4NjEdvdBoYmAMLETZRBhPV4SLmOEowHLHheXy3RPVh2cqtTp3tUYRK/d1qjWRak0ubF5tl27FQjelJuVKs+gLvnNmFfxBMvuNPPqHTfg3rn7C6KzxjI94xjXPGHRT+v0xdVFhEdvEmcs8tplGDvnBZR5N6esThIG1IM3PLKI9/iRjER2Jlgey7I3d/oI3XiFKA2TZpilpiK0+rbZGS9VQVA1J6fFTQ6He6PD4QUGINwey/S84oxlRsiPbHfGnMX6wxHFnWE7IMt6SpAcWyy1+kDCwA6zhjGCaoHZsKlWx+P5VQhRv2WxPqF2HYJaQ5Uc0c4w3WRLOU86X3wnnG3bHcwG/3uk6eP8gUX2Qivzhh9Y3CfMoI92eCGYbhqMF+e6EHyYYxoSBHaKoNvog+A92L1KuNArQe8h7qOD7CWn+ymq9w/UivPGCw+kTb6dPKOqgAHX7Q/q6x9CbM3TntNVBsel76DdgFO/Zv34kXv/M2/kzoqzT7lh0+y5Sy0A3/eIcPW2EZnj0NA9R0grL/wf8F8hRrK4qXEF0AAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;shinelite sqlite&quot;
        title=&quot;shinelite sqlite&quot;
        src=&quot;/static/d621df8e48185d0c680d61d920e85853/99c16/shinelite-sqlite.png&quot;
        srcset=&quot;/static/d621df8e48185d0c680d61d920e85853/99c16/shinelite-sqlite.png 451w&quot;
        sizes=&quot;(max-width: 451px) 100vw, 451px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;h1 id=&quot;Adding-documentation-optional&quot; style=&quot;position:relative;&quot;&gt;Adding documentation (optional)&lt;a href=&quot;#Adding-documentation-optional&quot; aria-label=&quot;Adding documentation optional permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h1&gt;
&lt;p&gt;If you want to render out a site for your package you can use package down.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;r&quot;&gt;&lt;pre class=&quot;language-r&quot;&gt;&lt;code class=&quot;language-r&quot;&gt;pkgdown&lt;span class=&quot;token operator&quot;&gt;::&lt;/span&gt;build_site&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;</content:encoded></item><item><title><![CDATA[KMS encrypted credentials with Dataflow on GCP]]></title><description><![CDATA[Encrypting credentials Google KMS Cloud Key Management Service (Cloud KMS) lets you create and manage encryption keys for use in compatible…]]></description><link>https://mortimer.xyz/gcp-kms-dataflow/</link><guid isPermaLink="false">https://mortimer.xyz/gcp-kms-dataflow/</guid><pubDate>Fri, 19 Apr 2024 00:00:00 GMT</pubDate><content:encoded>&lt;h1 id=&quot;Encrypting-credentials&quot; style=&quot;position:relative;&quot;&gt;Encrypting credentials&lt;a href=&quot;#Encrypting-credentials&quot; aria-label=&quot;Encrypting credentials permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h1&gt;
&lt;h2 id=&quot;Google-KMS&quot; style=&quot;position:relative;&quot;&gt;Google KMS&lt;a href=&quot;#Google-KMS&quot; aria-label=&quot;Google KMS permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Cloud Key Management Service (Cloud KMS) lets you create and manage encryption keys for use in compatible Google Cloud services and in your own applications. So first things first, we have to create a Google managed KMS keyring and associated key.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 772px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/74d6f60c8077e19d39f78f3210a3f555/a546b/gcp-kms-dataflow-key.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 75.4%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAPCAIAAABr+ngCAAAACXBIWXMAAAsSAAALEgHS3X78AAAB00lEQVQoz41Si66cIBD1/7/vpunuKqur3ooiDxWG10Djstk2aXrbk3AyEA4zc5hKKql12DiTjNNp0sbkvwMR9ycAIIRQLQsTnAt+UggBEdOXKBcQMcZYDcNAKeWcL8uSUsr/h5fYuux9mCkVQozDMM9LzrkkySn5gCHiwt1IwUA0EKfFMm7n1YaYKgNhP5zWms4zpZQx9q6tBIjJeXQeY0wRk/fnc85HxFRxFZgIETOAMcbAb4alf6GyFvZ92/dDa30cJxc/tdaI+HXnFef8++XSdR25k8vlQsi9bdu6rtu23fe9eFPsiTGW+Jf4OA5CSNM0j0c/DEPbdY+u6/v+x/jZ9/29vTeEPLoHuRNC7l3XUkpfduZcSSmb85wQQh4D6wY+fooyBvuzmZOfKB1Za98/WuWcAWDbNkzJ++h8KLWFEP5sMqWklEJEzvkpRsRpmmZKpRRSnjk55+u6whMloRACALz3WuumaaSU0zSdYgC43W5CqIb0+67Zunx8+yAN8d4zxuq6HsexrmspJQCM43i9XhljrwlDRCG480kdmFIGb9ShnHUppRijMeZtb0pJCLGu6/sXK+ecUkpJea6T1bZt8hmXrZRSPBFC8N5bawGgjOBPcTRdcfs1a1AAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;gcp kms dataflow key&quot;
        title=&quot;gcp kms dataflow key&quot;
        src=&quot;/static/74d6f60c8077e19d39f78f3210a3f555/a546b/gcp-kms-dataflow-key.png&quot;
        srcset=&quot;/static/74d6f60c8077e19d39f78f3210a3f555/0eb09/gcp-kms-dataflow-key.png 500w,
/static/74d6f60c8077e19d39f78f3210a3f555/a546b/gcp-kms-dataflow-key.png 772w&quot;
        sizes=&quot;(max-width: 772px) 100vw, 772px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;h2 id=&quot;Google-provided-Dataflow-templates&quot; style=&quot;position:relative;&quot;&gt;Google-provided Dataflow templates&lt;a href=&quot;#Google-provided-Dataflow-templates&quot; aria-label=&quot;Google provided Dataflow templates permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Some background on why we are doing this. Google provides open source &lt;a href=&quot;https://cloud.google.com/dataflow/docs/guides/templates/provided-templates&quot;&gt;Dataflow templates&lt;/a&gt; that you can use instead of writing pipeline code.&lt;/p&gt;
&lt;p&gt;If we were to use the PostgeSQL to BigQuery one for example, we find that the only way to provide the credentials in a secure way is to encrypt them with a Google managed KMS key.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://cloud.google.com/dataflow/docs/guides/templates/provided/postgresql-to-bigquery#template-parameters&quot;&gt;https://cloud.google.com/dataflow/docs/guides/templates/provided/postgresql-to-bigquery#template-parameters&lt;/a&gt;&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Parameter&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;username&lt;/td&gt;
&lt;td&gt;Optional: The username to use for the JDBC connection. You can pass in this value encrypted by a Cloud KMS key as a Base64-encoded string.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;password&lt;/td&gt;
&lt;td&gt;Optional: The password to use for the JDBC connection. You can pass in this value encrypted by a Cloud KMS key as a Base64-encoded string.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;We have created a template shell script that will execute the GCloud CLI command for running a Dataflow job.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/mortie23/beam-poetry-mono/blob/master/eng/dataflow-run/nfl-postgres-tandl/dtf-gcloud-template.sh&quot;&gt;https://github.com/mortie23/beam-poetry-mono/blob/master/eng/dataflow-run/nfl-postgres-tandl/dtf-gcloud-template.sh&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;In this shell script, we have the credentials as templated parameters:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;sh&quot;&gt;&lt;pre class=&quot;language-sh&quot;&gt;&lt;code class=&quot;language-sh&quot;&gt;gcloud dataflow flex-template run dtf-postgres-nfl-tandl-&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt;job_name&lt;span class=&quot;token operator&quot;&gt;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;..&lt;/span&gt;.
    &lt;span class=&quot;token parameter variable&quot;&gt;--parameters&lt;/span&gt; &lt;span class=&quot;token assign-left variable&quot;&gt;connectionURL&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;&amp;lt;&amp;lt;connectionURL&gt;&gt;&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;
    &lt;span class=&quot;token parameter variable&quot;&gt;--parameters&lt;/span&gt; &lt;span class=&quot;token assign-left variable&quot;&gt;username&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;&amp;lt;&amp;lt;username&gt;&gt;&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;
    &lt;span class=&quot;token parameter variable&quot;&gt;--parameters&lt;/span&gt; &lt;span class=&quot;token assign-left variable&quot;&gt;password&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;&amp;lt;&amp;lt;password&gt;&gt;&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;But wow do we get the encrypted versions of these into the template.&lt;/p&gt;
&lt;h2 id=&quot;KMS-helper-function&quot; style=&quot;position:relative;&quot;&gt;KMS helper function&lt;a href=&quot;#KMS-helper-function&quot; aria-label=&quot;KMS helper function permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;We have written a series of helper functions to make this easier for us. Firstly a custom Beam Me Up package has been written with a KMS module.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/mortie23/beam-poetry-mono/blob/master/eng/lib/beammeup/beammeup/kms.py&quot;&gt;https://github.com/mortie23/beam-poetry-mono/blob/master/eng/lib/beammeup/beammeup/kms.py&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;The function within the module that is used to create the Base64 encoded encrypted credentials is the &lt;code class=&quot;language-text&quot;&gt;encode_dataflow_parameter&lt;/code&gt; function. Let’s look at what it does step by step.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;py&quot;&gt;&lt;pre class=&quot;language-py&quot;&gt;&lt;code class=&quot;language-py&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;# Uses another Beam Me Up function to encrypt any string value&lt;/span&gt;
encrypted_parameter &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; encrypt_symmetric&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
    project_id&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;project_id&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    location_id&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;location_id&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    key_ring_id&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;key_ring_id&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    key_id&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;key_id&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    plaintext&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;parameter&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;# Base64 encodes the returned ciphertext (which is in bytes) and then decodes the bytes to a string&lt;/span&gt;
base64_encrypted_parameter &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; base64&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;b64encode&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
    encrypted_parameter&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;ciphertext
&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;decode&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;utf-8&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Let’s look at each of the parts of this to figure out what is happening.
Dataflow jobs require a KMS encoded&lt;/p&gt;
&lt;h3 id=&quot;Encrypted-parameter&quot; style=&quot;position:relative;&quot;&gt;Encrypted parameter&lt;a href=&quot;#Encrypted-parameter&quot; aria-label=&quot;Encrypted parameter permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;The result from the call to &lt;code class=&quot;language-text&quot;&gt;encrypt_symmetric&lt;/code&gt; is an object with multiple parts. We just need the ciphertext.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;yaml&quot;&gt;&lt;pre class=&quot;language-yaml&quot;&gt;&lt;code class=&quot;language-yaml&quot;&gt;&lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;projects/prj-xyz-prd-fruit/locations/australia-southeast1/keyRings/keyring-fruit/cryptoKeys/key-fruit/cryptoKeyVersions/1&quot;&lt;/span&gt;
&lt;span class=&quot;token key atrule&quot;&gt;ciphertext&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;\n$\000\304\227|\301\324\343z\227\227\206I\214^\310\326\301\234O`\243|A\n\342\275v\332|\314|(&gt;\252;\243\022J\000\254\004\343\003\375[\3760\333\027&gt;(\311|k\&quot;\302\01426\334\207/\361\\\226-\001MP\375R0\017\270v\310\323\351\331\2140\217v2P9\242\333Y\262\307\225G\200\345a\373;)\257\260\263\344]1\323\322W\241\270J\t&quot;&lt;/span&gt;
ciphertext_crc32c &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;2931620773&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token key atrule&quot;&gt;verified_plaintext_crc32c&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean important&quot;&gt;true&lt;/span&gt;
&lt;span class=&quot;token key atrule&quot;&gt;protection_level&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; SOFTWARE&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;Cipher-text&quot; style=&quot;position:relative;&quot;&gt;Cipher text&lt;a href=&quot;#Cipher-text&quot; aria-label=&quot;Cipher text permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;The ciphertext itself is in bytes:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;b&apos;\n$\x00\xc4\x97|\xc1\xd4\xe3z\x97\x97\x86I\x8c^\xc8\xd6\xc1\x9cO`\xa3|A\n\xe2\xbdv\xda|\xcc|(&gt;\xaa;\xa3\x12J\x00\xac\x04\xe3\x03\xfd[\xfe0\xdb\x17&gt;(\xc9|k&quot;\xc2\x0c26\xdc\x87/\xf1\\\x96-\x01MP\xfdR0\x0f\xb8v\xc8\xd3\xe9\xd9\x8c0\x8fv2P9\xa2\xdbY\xb2\xc7\x95G\x80\xe5a\xfb;)\xaf\xb0\xb3\xe4]1\xd3\xd2W\xa1\xb8J\t&apos;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;Base64-encoded&quot; style=&quot;position:relative;&quot;&gt;Base64 encoded&lt;a href=&quot;#Base64-encoded&quot; aria-label=&quot;Base64 encoded permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;The result of Base64 encoding these bytes are bytes again.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;b&apos;CiQAxJd8wdTjepeXhkmMXsjWwZxPYKN8QQrivXbafMx8KD6qO6MSSgCsBOMD/Vv+MNsXPijJfGsiwgwyNtyHL/Fcli0BTVD9UjAPuHbI0+nZjDCPdjJQOaLbWbLHlUeA5WH7OymvsLPkXTHT0lehuEoJ&apos;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;Decoded&quot; style=&quot;position:relative;&quot;&gt;Decoded&lt;a href=&quot;#Decoded&quot; aria-label=&quot;Decoded permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Decoding these bytes leaves us with a string we can pass into the GCloud CLI Dataflow template parameter.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;&apos;CiQAxJd8wdTjepeXhkmMXsjWwZxPYKN8QQrivXbafMx8KD6qO6MSSgCsBOMD/Vv+MNsXPijJfGsiwgwyNtyHL/Fcli0BTVD9UjAPuHbI0+nZjDCPdjJQOaLbWbLHlUeA5WH7OymvsLPkXTHT0lehuEoJ&apos;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2 id=&quot;Filling-in-the-parameters&quot; style=&quot;position:relative;&quot;&gt;Filling in the parameters&lt;a href=&quot;#Filling-in-the-parameters&quot; aria-label=&quot;Filling in the parameters permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;A Python script is used that can source the Beam Me Up package and use our helper functions to encrypt a given credential from a &lt;code class=&quot;language-text&quot;&gt;.dotenv&lt;/code&gt; file and then replace the values in the shell script.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/mortie23/beam-poetry-mono/blob/master/eng/dataflow-run/nfl-postgres-tandl/dataflow-generate-run.py#L174&quot;&gt;https://github.com/mortie23/beam-poetry-mono/blob/master/eng/dataflow-run/nfl-postgres-tandl/dataflow-generate-run.py#L174&lt;/a&gt;&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;py&quot;&gt;&lt;pre class=&quot;language-py&quot;&gt;&lt;code class=&quot;language-py&quot;&gt;encoded_username &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; encode_dataflow_parameter&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
    project_id&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;cfg&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;project_id&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    location_id&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;cfg&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;location&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    key_ring_id&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;cfg&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;key_ring_id&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    key_id&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;cfg&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;key_id&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    parameter&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;username&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;</content:encoded></item><item><title><![CDATA[Python Beam and DataFlow to write data to BigQuery with a custom Docker image]]></title><description><![CDATA[Dataflow build and run Basic example of how to develop an Apache Beam pipeline written in Python that writes data to BigQuery. To then build…]]></description><link>https://mortimer.xyz/dataflow-beam-docker/</link><guid isPermaLink="false">https://mortimer.xyz/dataflow-beam-docker/</guid><pubDate>Fri, 22 Dec 2023 00:00:00 GMT</pubDate><content:encoded>&lt;h1 id=&quot;Dataflow-build-and-run&quot; style=&quot;position:relative;&quot;&gt;Dataflow build and run&lt;a href=&quot;#Dataflow-build-and-run&quot; aria-label=&quot;Dataflow build and run permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h1&gt;
&lt;p&gt;Basic example of how to develop an Apache Beam pipeline written in Python that writes data to BigQuery. To then build it as a &lt;em&gt;flex template&lt;/em&gt; and then run it as a Dataflow job.&lt;/p&gt;
&lt;p&gt;This example avoids complexities of reading from source systems or storage and simply creates synthetic data.&lt;/p&gt;
&lt;p&gt;Working from these examples:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://cloud.google.com/dataflow/docs/guides/templates/using-flex-templates#local-shell&quot;&gt;Using Flex Templates&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/GoogleCloudPlatform/python-docs-samples/blob/main/dataflow/flex-templates/streaming_beam/streaming_beam.py&quot;&gt;streaming_beam.py&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;Setup&quot; style=&quot;position:relative;&quot;&gt;Setup&lt;a href=&quot;#Setup&quot; aria-label=&quot;Setup permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Reference code is in the following Github repo &lt;a href=&quot;https://github.com/mortie23/beam-jdbc-testing/tree/master/docker&quot;&gt;https://github.com/mortie23/beam-jdbc-testing/tree/master/docker&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The GCP prerequisites are in the following sections.&lt;/p&gt;
&lt;h3 id=&quot;Project&quot; style=&quot;position:relative;&quot;&gt;Project&lt;a href=&quot;#Project&quot; aria-label=&quot;Project permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Firstly the basic GCP project naming convention we will have will include an environment.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;prj-&amp;lt;org&gt;-&amp;lt;env&gt;-&amp;lt;business-unit&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 464px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/632916a9a944f13aaace7ebffd3ad4cc/0359b/dataflow-beam-docker-welcome.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 35.12931034482759%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAHCAIAAACHqfpvAAAACXBIWXMAAAsSAAALEgHS3X78AAABBElEQVQY042PT2uDQBDF/eQ996skUCS3LrSpKwEPoXiRttKo0VETNZv1T5q4rt0JFgK2XvrjHYaZ94YZDREFO9bu+1AI0bZt3/f4PzSFXfV85zzMbR/D4HM2m+u6TgihlDqOs16vTdOklK5WK8YYIiqlRuFz8vF2/+gUT69Y5Oly+UII0XXdMAzLsgghtm1TSheLRZ7nf8OIyE483FeiG0bjq8a+W+eXNJw4JqibJj93XRdsgyTdxXGSZRkAbDYbAIA4LvmxPsldIXgtD7zjtQzTC69kdhC7QjRfvaaUAoAoitIffN8HAM/zXNdtmipnIslaVso0H1Zsk0vZSF7JeN+eL99XWa2FaoYGcK8AAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;dataflow beam docker welcome&quot;
        title=&quot;dataflow beam docker welcome&quot;
        src=&quot;/static/632916a9a944f13aaace7ebffd3ad4cc/0359b/dataflow-beam-docker-welcome.png&quot;
        srcset=&quot;/static/632916a9a944f13aaace7ebffd3ad4cc/0359b/dataflow-beam-docker-welcome.png 464w&quot;
        sizes=&quot;(max-width: 464px) 100vw, 464px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;In this example, the Organisation (&lt;code class=&quot;language-text&quot;&gt;&amp;lt;org&gt;&lt;/code&gt;) is &lt;strong&gt;xyz&lt;/strong&gt;, and the Business unit (&lt;code class=&quot;language-text&quot;&gt;&amp;lt;business-unit&gt;&lt;/code&gt;) is &lt;strong&gt;fruit&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;The project requires:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;GCP resource&lt;/th&gt;
&lt;th&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href=&quot;https://console.cloud.google.com/artifacts&quot;&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 24px; &quot;
    &gt;
      &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 100%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAUCAYAAACNiR0NAAAACXBIWXMAAAsSAAALEgHS3X78AAAEoElEQVQ4y2WUb4wTRRTAX++OOw9DiIqGRGP8ggmao91t7xKw/AmgwUAOOa/S7m7b49pOr6cccCRygetB292tBiTGP4jwAQJ6+kGQSFRMSEiM5hIjnAc7U9ruzPYOg5oo/vlg/LbmLQ0YnOQlk9n3+83b2Z0HcX0W4nsFJMYboJmzbZtLFGKmgKjBIWLU4N6xtcwhVhaglB14sVz1GGTRgS5Img7ECo4vZtptmjkHW0qsPWYI31YU6v8XRk3ubaiYji9i1tqRUUy7DR3oAk13WmIHbsBWnYFSrK9Rdf6RZjbaVUOAqnNQDQeUou2FNy/ZoHjrol3R7UlkkEWHposWiJsCXtovHlV0flTV+T+qzl2lZHegrHe8CmpJgFLiXuC8N18DVbchVqxjjosMsuiImxwgWnLCmiF+0gyBD/+IlepD6fIctPVOwX3PXoV9x2chVrSXYuw9/iN0rv8eFqz/Bgb0Om6SRQZZdERLdhjiZaFEi9yN7K9/nizXFuUONyB7sOH7+8pf8PaHN+fFDXFQwap17uL8xKe/zvtyyoXMoTlf7g0HkEEWHeiCTWP1/vD2ituVssZ6cgx6cqzDdV0A+My3cqQS68vXzyRMcQWjb8L+ODzC1M5VF9ugcxJ6crQDGWTRgS7oStGIRKgbzLK8TBjIhHYsSU5DV3qmTSL0nD9FZ1aNsJ2rd1Rzy1LWd1LGuhAi0/dL6cteLjLIogNdIBMWCWaZKxM6LhPqJfXv4bDj2CwE0tapYJa6gQw9G8jQU5gXSFufALzlWzdyDeQMCj1m/LaDRUDK0KaQecKeYdYOS675lBLv2TRW2ykRel4m9EwwS0/LxPpq41h1t1YUyxdu4C2hl632/wrRdUeIZYeGGKwbnZufMISOv4SmCzdu8Mk1u6vB8M7r/oQpTjb/BjdhioMb8pUFyCB7V3j3lSeCWQZBwqLP76m+oxn8UtzgU5v31d4P5VgqNMSSvXurRzWDT2mGuLRxrPZukNBEMOtVOOEJ8ZX96TsV7r9dvvWBP01/7x5iB5YPsx3+tOUEMta3EqEX/Wl6Y/kw29U9xCb8aeu3AKHnkEEWHeiCFdsr/d5hZ2i+eR5vNs+USYReaFZ/Vib0dLOKCzKhrLl+HBlkg4S5K16p9INq8ujm8bq7drRaWjXC4LHI9HwpYxVkws7LhE1KhJ6UUpcfCWash2RCj0kEPw79QiaW+UTf1QXIIIsOdOEl3+JdHbyXBj+SKIvF4V01kLI/LFyWmlkUHr8KIVIBf+Y6PDf6NTylWg8uS888sHqYwrby3GJV50c0g7vexzLEFngh35ivFOujqs5trzHo9i9Ro9a6bncFulLXoGuAQih73SdlGCxNVuHppAVrRyugmqJV0fnPzQZhowNdsGZwBqsEpcAfV0r2YVXntzTD6Rx4zYGVIxQCyQo8nH8dXtV1eHLgBjwzXIGBMrY20amU+C1kkEUHuqBvXw0UXcyLFWwYNGYhVhDdasFpVQoOYNw7lKIDqheNVqXodN9mbM+BLki89ycoh25iA23ZVm60KIYNmmmDatqA83uH6j3joJkcOzUMIlO0W9CBrn8B5TDgYa81NKsAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;gcp artifact registry&quot;
        title=&quot;gcp artifact registry&quot;
        src=&quot;/static/7a54109243387627a7f93a1ff6355439/22424/gcp-artifact_registry.png&quot;
        srcset=&quot;/static/7a54109243387627a7f93a1ff6355439/22424/gcp-artifact_registry.png 24w&quot;
        sizes=&quot;(max-width: 24px) 100vw, 24px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
    &lt;/span&gt;&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;GCP Artifact registry &lt;/br&gt; Docker repository&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href=&quot;https://console.cloud.google.com/dataflow&quot;&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 24px; &quot;
    &gt;
      &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 100%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAUCAYAAACNiR0NAAAACXBIWXMAAAsSAAALEgHS3X78AAAEqElEQVQ4y3XU/28TZRwH8PcVBJEYfh4EQrJg4kgMOjRkxnXdIBp+UQloUIFh77aC+4JEQINx0HaTYIiYsbG2dx1bGGzr3cYXEWIAA2xr77oyRRMMRo3yxW3A1vbueXrPdeRMhzHC5vMHvJ7P83nenw/w77GxpZ3A00pRxk+gQiIQRAMV7WlHpaQ7ivakOac/PdPlN+D0pYBdQPHeDFxeC3adjWnOQ7D6OMXrOyyOD5ECd8Ao3CylUdaQBJaOotiXhstnLHb6UkuwAnhlEsxCnBbkbAhhCj5sYeWHdg5sFkRiCyJpfHVfeiHWj84t8RvbXH59vMSXPv/SnvvzSr1plPl0FNSNTPV64xae+5hC1ij2fc0c5UGaVx6gNXyQjPl66fV3m4xoiT+dKfbqzS6vXlhWb80p2nEbzrq7ePMrayqo9Nu48pOFs6qN04MEDWey+GYo83xHv/lLZ5TZW1uJvSlgDLuD9OU1B1J4p5mhvMniirYnsat9mid3fmvjdJzg1Pf3EFHNfEVlnYrK7BMJ1hdR2XB1OzUEkVzItYEXSXhTkM6vkhiqpAxqW7PTVKgyTlYZZJV5FM1MKhobljX2qU+5iaN9bLcnTA5cvHAPQojUCaKREkRyS5DIW0IuDRLhHsG6YxkomjlD0Rhkzfxd1li6q4/Ojww+wJlrFHLcqu2Mst13aBs8R3QIIsmvEAmtEMk4H8rFi8yIJOhjoGpyimqiR2OrlThzRWImZM16UokSKBq7qWgs03T515lVbZlZ5QEDG1sM16aA/kbtUQNV7ToXGXwMPHKF4NgAfVpRzZiimT1nNYreKHUEzxJ09Vl5XTFrkfanjd4h0/HjvdzFZlvvIDvfkUjObh76DY9UKMcNHL3dhMjVkScUjfUomtV47gbDmR8mHORnG7KaXRQZyC5ou0zQk2COxB0LJwatjhOD7FJbQn+q8ep9dP8XjGgEssY4ebKH1ipFy66IxBh6NTbLTtq5/uqKxkaPxVLY2Ulnvx+icIsZZ0XYXLWr28RHnSbX/XiFisYmP0XR2LiiMdIdzeTLahr+PhvdMfOTrqi5PXB+HHw4A3eILhBEQgWR/OEOEfAhMuMRcDI2GuOUOIMSt9YpmnlLVs2UrJp1LacncOSSedAdNHbbtg0+RD8TRDIqiGSMDxGBfwhyU3LYMzQB5aoFOZFFZ585P6IySdGYfSrBLhwfYGRLKxkRRGOAnww2PcaLNL+2bQSe1hSWfzHNpJy89gAv1hsIfGdy4kUK70krF/aijn7zr/YrZm5J2OUtxo3KVrps7YEUKlsp1jfb+KCd4b3DD6aCh85lsbYpiULvMLa1ZeYIEl1eHiSHBdEwG07S/jVf6tedPn28eK9eU7xHz3v7EHFskHQU1BjgJToVXHfIRqWURqWUwsZAZt4/czvGh4xteVtTcwt2pha6/Eajy6/bJX695QUPdZR+rqO0QZ/cpVPX65CNaklHjZQGEIIQ0p9xB43FfNAAVqawZPsYXqvX4fTqhSU+femyqrvcqoPjKNuf/h/QfghW58AdtyCEDLhDBipEY6YnTLhib8qxep/uKKnX4arX8eyGMZTuT04B/wZ0Q/x+SXbYVgAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;gcp dataflow&quot;
        title=&quot;gcp dataflow&quot;
        src=&quot;/static/f805ff5a4628a4f4212cfde2f6f210dd/22424/gcp-dataflow.png&quot;
        srcset=&quot;/static/f805ff5a4628a4f4212cfde2f6f210dd/22424/gcp-dataflow.png 24w&quot;
        sizes=&quot;(max-width: 24px) 100vw, 24px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
    &lt;/span&gt;&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;DataFlow API&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href=&quot;https://console.cloud.google.com/storage&quot;&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 24px; &quot;
    &gt;
      &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 100%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAUCAYAAACNiR0NAAAACXBIWXMAAArwAAAK8AFCrDSYAAADC0lEQVQ4y6WVy09TQRTGTxdlwf9AE/buXJGakGhDogsjpiQm1eArlUC1QjSusBqioiii8kagCAWx4IVSqoDxVhQKCLRo6gp3RgUN9HEv985MO8fcWgIaoklZ/DLJ5JsvM9+cOQP5DhFMDhHyHCLYHCJUOUSodIjg+A+VlSJUVYlgs4mQlyeCySRCfr4IUNqNcN4p6063kaxWf0I/+pHrhWBSPxT6N5rmZZjra8eZ3nhLyTpaL+kKHiJAWQ/qLjg34EwbhbbJJPjCCENLHIaX8B9wEEIcxj4h3J9IgLFahcJ6GUyPUAdWJ4XS9qixuFnyN04o7uF5VRiYVYXBvxjYhntGFZ4FVMG7qAp3Rzbcxmsx/5F7EaPpNgGwtlModUYtxS0yNr1S0bNA8GmAYP/MnwzObSG8JzgwR3A0SPGOV8H8qjha6mOWwocKgPVxytBc3JwylEcWKXsdpkz8tI0wZcI8ZU8DhM19ZiyZ5GwlmmS+EGXVHkUuqJbQ1imZrc4tw6K0oeoNUv51LcFXIgn+PfJ7/Lae4MMLlLumCA9/YRwROU1w/mKJ8mqPoh6+K+Fll1RU3rfDDj2LlPW8I8w19SfP31PmniVs/ANl4S8JNrvMmDdIWa1Pkc11ElZ0SeYL3ZphJ4XSjs0MlVSGz9M5bWczP/cswd4pgn3TJKWtH1fQ0qgdOWYp6SIAp9rjYG35mW1pXMt94I3kuN/GDL3+qKFvB7T5gXdRw4u5mMETiBnck1FDgy+aU9Kxnlve9SPb3iNlbjgciBmE6ZjhRv96zh77z9z9V1ez916RMj9y7zRBb5BgjVfBfdfjaK6LWg7eUzO/lJllxkZDlN0eUeQDN+N4tjVuPt6ysfuyueVR1EN3JKzolorOu3ZR2Kvpwq7xKnJhrYSXuiXzxZRhhk9PG0cWKT4YU/DYIwntzril7ImWYabNYUYVNG3DuOI+0ST5yzoixnNOmnn7GgrxlLb1TRJOt1KwP9kAmwt1u2qwvo9c3ywm9CdbSJa9S9bZehAg4y8grbWl12oemtcvS34++1g+vskAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;gcp cloud storage&quot;
        title=&quot;gcp cloud storage&quot;
        src=&quot;/static/be82553f3a2a6df3cfdfa77b6b1d8d9c/22424/gcp-cloud_storage.png&quot;
        srcset=&quot;/static/be82553f3a2a6df3cfdfa77b6b1d8d9c/22424/gcp-cloud_storage.png 24w&quot;
        sizes=&quot;(max-width: 24px) 100vw, 24px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
    &lt;/span&gt;&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Cloud storage&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href=&quot;https://console.cloud.google.com/bigquery&quot;&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 24px; &quot;
    &gt;
      &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 100%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAUCAYAAACNiR0NAAAACXBIWXMAAAsSAAALEgHS3X78AAAEjElEQVQ4y3WUe4hUVRzHf3NnXRSjSCRJrAwq6B+RaoUonZ3RfEQPKd+JunPPOeP4oAT/iCSo3TurUWu47crOPefOzPpgfayrZa60BUFQuQqlq/0RQZhBS2nhzDm/c+fOajfOzFhtjwOHy/3dcz/n+/ud7/kB1EdS+GBzhCTXQAU2rOsuAnEVUKGAeAiEKyBcQjpXtmhOWVsLPrzZF0KLV4J/DeppIJ4PROjIq/tGDRDWdxfvIK5qokItJB4uIVzNJVzel86VgeZLwDw/uq1QgdW8WBUD45R5fvVpoPtzJ4FylaQCP6YCK1RgSD0Mq0+BI1TgzpasvJtmA9jMg8jT2V9gS94AwxrQpGmUJT0faN6fTDj2sr8gXxOOXVTgO1Tgh9TTIRPaxC8lhX6EeQjpvIpsKihI98q6Qq5hR+4qDO0bAAOrKdHf29ykWbpt7XvXGlfvLTVQLicRjg8RjkdrqvVlJvz7Ux5CytOwqVCsAYnQDUalXUuzCkty+YDZPelKsLMSbFcDNWtETQUVtzbGD7Ye1pA+oCOr2lUNyLIKmKtuZwKHTJo2x8W2UNB6vGzBnO1AXJxBuH6whesIEcra0CNhfY+6kwi8zASOMSGfZEICE8qCMASg3AfKdRMVOqACz5s0N+cRntmlrdd6b9xS820LL1oruyRs6cUo4Qj1uhqVGSKq7xOqwJq/1MJ6ul3P7lTRoYu/T/A+Daa82HbVWOoz5mHIcr9Zb5/04ZvRG2DKYbtqaR14KOUipDg2/h24pA7sOP/DDRg4W1nePxyMDF0cm2FzPGWAL72rrVPnKlP6hsvT13RcB1voRSZOPXVso6tgI1fjgHMNkAk8FYYhnDgX2P3DQXB6pDLT5jhofjTxgbNBrn84+Pyx180h6pdNnAjVTcxtEmpcyvdSgReMz57rwHsGvwqajp4pf3f64tjDSRffZx6O7ei/Hj12NsgfORPs6fyoDNTDL6nxJMdVtFbTaO1QPBmlogRMYKa6I8fDse0nIgPD/hNDl8rTkxy/MOp3DfqTDw0HTQAubMhi1WLMwwtrd4cTF28NIdkTQHNGgrGMlcpKYD3FaZSrkXqhe6elr0165WDZNIdHqcCF5jZ0DJYh6SJlntZm3YpO2bq8U8GyPSoK8SsQd0oAzCsbc0dt4ZvFs42x69AfqcDdtqvm20LPIUJvox6eqX8Ll+6WYXObvBJ35JKEIyHejpFEWxGAHbhp7rCZESok0Jw/kwp1/M/G8I/JPH1hRad6w8ASjgzjGTkad0oLYu0SmtulBSxbArtbQstbP8PGnI4YxZuPhKbjPE64aiMC+wjHfipUFxG48vld4cRlnQgJBxcnHDWayKgw7shf405pVjXlasdxfUjvr0C6twIsV4b0wdCitdMHlkdIFeqNViDctSyEF/ZgNOEoiLep+VVYRn0yL4NT52X0+EabLtyELYWbsMYZMQotwlUDy2FjKo+NVKgJRGB03d4A4KmfYNFOP7LA8SHmyFmxDE5NOCVzKBb8/wj/c8ZbEZrbJMRaSzA/o62YU4JYRkPCKVkJpwh/ALm5RbRg2fVvAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;gcp bigquery&quot;
        title=&quot;gcp bigquery&quot;
        src=&quot;/static/c2577331b9dc6492d69d72685aa5e242/22424/gcp-bigquery.png&quot;
        srcset=&quot;/static/c2577331b9dc6492d69d72685aa5e242/22424/gcp-bigquery.png 24w&quot;
        sizes=&quot;(max-width: 24px) 100vw, 24px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
    &lt;/span&gt;&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;BigQuery&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3 id=&quot;IAM&quot; style=&quot;position:relative;&quot;&gt;IAM&lt;a href=&quot;#IAM&quot; aria-label=&quot;IAM permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Service account (&lt;code class=&quot;language-text&quot;&gt;gsvc-xyz-&amp;lt;env&gt;-fruit@&amp;lt;project_id&gt;.iam.gserviceaccount.com&lt;/code&gt;) with roles
&lt;ul&gt;
&lt;li&gt;Artifact Registry Repository Administrator&lt;/li&gt;
&lt;li&gt;Storage Object Admin&lt;/li&gt;
&lt;li&gt;Dataflow Worker&lt;/li&gt;
&lt;li&gt;Workflows Invoker&lt;/li&gt;
&lt;li&gt;BigQuery custom role (permissions)
&lt;ul&gt;
&lt;li&gt;bigquery.tables.create&lt;/li&gt;
&lt;li&gt;bigquery.tables.get&lt;/li&gt;
&lt;li&gt;bigquery.tables.update&lt;/li&gt;
&lt;li&gt;bigquery.tables.updateData&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 866px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/8fc66c8abd2e8484033afd10c19e8782/4219d/dataflow-beam-docker-custom-role.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 60.4%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAMCAIAAADtbgqsAAAACXBIWXMAAAsSAAALEgHS3X78AAABLUlEQVQoz5WR3W6DMAyFef9n23axdjcrFYWEQkkYENPg/Hoi3dAmTVv32TeRfGSfkwyu3rpQ15wzLqUsEk1zbtqWiKwN8s10EhU4NKHpsJNG9KYVOCqbhRBjJGutMQYRtdZo0BjjvCeiGMmHtWOkEMn59PTRuuB9zLTWPs1t4LIAwDzP8Mn8AQAoADDW3iYzIWSeHxljRXHSWhORkGIYBvzCkhoRr1rjgtuyLN0WicgnAzfoPrK+7/P8kCq3ztF/yKZxOpUlr+uqqoQQfd8PwyCl3Iz9JvbeI2IIwTq7rM6MtVZr7b6n+LNYKVVz/no4HPOccXZMMMbuMb6Kq7IsiqKqqk4IABjHEZS6J7bs0rZPjw8v+/1u99xe2v8FBqBqXjcrZ8bYNE3b5/0pfgf7KLfpiqXYTAAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;dataflow beam docker custom role&quot;
        title=&quot;dataflow beam docker custom role&quot;
        src=&quot;/static/8fc66c8abd2e8484033afd10c19e8782/4219d/dataflow-beam-docker-custom-role.png&quot;
        srcset=&quot;/static/8fc66c8abd2e8484033afd10c19e8782/0eb09/dataflow-beam-docker-custom-role.png 500w,
/static/8fc66c8abd2e8484033afd10c19e8782/4219d/dataflow-beam-docker-custom-role.png 866w&quot;
        sizes=&quot;(max-width: 866px) 100vw, 866px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;h3 id=&quot;Cloud-storage&quot; style=&quot;position:relative;&quot;&gt;Cloud storage&lt;a href=&quot;#Cloud-storage&quot; aria-label=&quot;Cloud storage permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Cloud storage bucket (&lt;code class=&quot;language-text&quot;&gt;bkt-xyz-&amp;lt;env&gt;-fruit&lt;/code&gt;) with sub folders&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;📁 dataflow/
│   ├── 📁 flex-template/
│   ├── 📁 staging/
│   ├── 📁 logs/
│   ├── 📁 temp/&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 793px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/b54177e8ce2e742aea6bd03fe44cf956/94411/dataflow-beam-docker-storage-folders.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 49.199999999999996%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAKCAIAAAA7N+mxAAAACXBIWXMAAAsSAAALEgHS3X78AAABVElEQVQoz3WQbW/bMAyE/f9/Xz5kwdoss5PSlizLkqgXkpIHx0XQYtsDQjjcicCBXSnFrTbnlCI654Pfh6iYWQHAMPRVZPsPXaEGmkDTNJPUZp2ApnEmmEXa5lDuYwFd7lMhbqvn2yMNkD9UwVQ7ZiYS5spca60ilbjlwj4kkVqIifaIeI+kNnpqIpFaO2vtNI3TNI4AAOM0qYhYRfr+9+Px8N6H4EJwiMG5/f1W+3q9GmNmrYN3VFLOKT5J6VOEEJRSiKi1RkTvvVLqdDoBQHc+n+d5vt36xSZt4mwSEX+llJJSYmZjTIyRiFJK1toYY9f3/braH5ef2tDqyXlu7dtJ25Nt26y1iHg4n7WHYVitfXu7TjqnzK/fL2Q/TW2tLcuCiF+d7nK5GGPe33+BKmYpf+22+qS1dlQ9nCPqACCEcL9/mJVJ6j+XDxFCyDlv2/Za/gPYk0CWVVkeSwAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;dataflow beam docker storage folders&quot;
        title=&quot;dataflow beam docker storage folders&quot;
        src=&quot;/static/b54177e8ce2e742aea6bd03fe44cf956/94411/dataflow-beam-docker-storage-folders.png&quot;
        srcset=&quot;/static/b54177e8ce2e742aea6bd03fe44cf956/0eb09/dataflow-beam-docker-storage-folders.png 500w,
/static/b54177e8ce2e742aea6bd03fe44cf956/94411/dataflow-beam-docker-storage-folders.png 793w&quot;
        sizes=&quot;(max-width: 793px) 100vw, 793px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;h3 id=&quot;BigQuery&quot; style=&quot;position:relative;&quot;&gt;BigQuery&lt;a href=&quot;#BigQuery&quot; aria-label=&quot;BigQuery permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Make sure the target table within the appropriate dataset (&lt;code class=&quot;language-text&quot;&gt;fruit&lt;/code&gt;) exists.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;sql&quot;&gt;&lt;pre class=&quot;language-sql&quot;&gt;&lt;code class=&quot;language-sql&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;create&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;table&lt;/span&gt; fruit&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;hellofruit &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
  name string
  &lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; test_number int64
&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 1025px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/8ec1ffc563c9150ae959ba3fb1090b1b/cf562/dataflow-beam-docker-bq-fruit.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 68.19999999999999%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAOCAIAAACgpqunAAAACXBIWXMAAAsSAAALEgHS3X78AAABtElEQVQoz32S647cIAyF8/5P2EmiKNmZ0kkYCBcbMNcqSbvSVtP9xA+QfPDxpXvcHz6EdlJrUzoGKjFVAEAEZSLbgpA+BMdW3BWWHHOOF51zjohijKXknEtKBdEhOiGENur+2Lj0FjKXftcx5+J8ApdKqaWUbt+llALRtlZTSq1VzrmUgnO+beu2rSnFVhtgVvrIYSCJnbynWms3Tmyanx8/BeAf8xelZEBstWqb5odzoRAFIUQ+SFKIGFP33BRbdy4sUT7LrogYY0TE7SQEOv+qtdYr4Lq01rp1fc7zJATXWiFia81a65yzFpZlud/vIXxx9MlhmzG2zB/eB0RHFGupSmnnPQAwxkopVygiSimVUvLEGHNkVjb9GNXjl9M2h9jAFfaUgMEYPY4j5zznoxzvvRBCKWWMUUoBwJH58l/PIV/nsrTv++12W5blelprX69XCMGfxBgPcfsPQoi+7+d5JjoaRkTn2qBz7rNz34mHYRjHcZ5nACAirTWcWGuNMceGvVUGIinlMAx932/bFmMEAK01EYW/5Jzfi0spUspxHJ1z/8z2y6i+sT1NE2Psqrm+4zffLCizcsYPuwAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;dataflow beam docker bq fruit&quot;
        title=&quot;dataflow beam docker bq fruit&quot;
        src=&quot;/static/8ec1ffc563c9150ae959ba3fb1090b1b/cf562/dataflow-beam-docker-bq-fruit.png&quot;
        srcset=&quot;/static/8ec1ffc563c9150ae959ba3fb1090b1b/0eb09/dataflow-beam-docker-bq-fruit.png 500w,
/static/8ec1ffc563c9150ae959ba3fb1090b1b/1263b/dataflow-beam-docker-bq-fruit.png 1000w,
/static/8ec1ffc563c9150ae959ba3fb1090b1b/cf562/dataflow-beam-docker-bq-fruit.png 1025w&quot;
        sizes=&quot;(max-width: 1025px) 100vw, 1025px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;h2 id=&quot;Build&quot; style=&quot;position:relative;&quot;&gt;Build&lt;a href=&quot;#Build&quot; aria-label=&quot;Build permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The build step involves building a Docker container image, and associated Flex template file.&lt;/p&gt;
&lt;p&gt;The &lt;a href=&quot;https://raw.githubusercontent.com/mortie23/beam-jdbc-testing/master/docker/Dockerfile&quot;&gt;Docker file&lt;/a&gt;:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;starts &lt;strong&gt;FROM&lt;/strong&gt; the base image&lt;/li&gt;
&lt;li&gt;sets the required environment (&lt;strong&gt;ENV&lt;/strong&gt;) variables&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;COPY&lt;/strong&gt; the source files to the Docker image&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;RUN&lt;/strong&gt; operating system updates and installs of packages as well as the Python packages&lt;/li&gt;
&lt;li&gt;defines the &lt;strong&gt;ENTRYPOINT&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;dockerfile&quot;&gt;&lt;pre class=&quot;language-dockerfile&quot;&gt;&lt;code class=&quot;language-dockerfile&quot;&gt;&lt;span class=&quot;token instruction&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;FROM&lt;/span&gt; gcr.io/dataflow-templates-base/python3-template-launcher-base&lt;/span&gt;

&lt;span class=&quot;token instruction&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;ENV&lt;/span&gt; FLEX_TEMPLATE_PYTHON_REQUIREMENTS_FILE=&lt;span class=&quot;token string&quot;&gt;&quot;/template/requirements.txt&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token instruction&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;ENV&lt;/span&gt; FLEX_TEMPLATE_PYTHON_PY_FILE=&lt;span class=&quot;token string&quot;&gt;&quot;/template/hellofruit.py&quot;&lt;/span&gt;&lt;/span&gt;

&lt;span class=&quot;token instruction&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;COPY&lt;/span&gt; . /template&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;# We could get rid of installing libffi-dev and git, or we could leave them.&lt;/span&gt;
&lt;span class=&quot;token instruction&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;RUN&lt;/span&gt; apt-get update &lt;span class=&quot;token operator&quot;&gt;\&lt;/span&gt;
    &amp;amp;&amp;amp; apt-get install -y libffi-dev git &lt;span class=&quot;token operator&quot;&gt;\&lt;/span&gt;
    &amp;amp;&amp;amp; rm -rf /var/lib/apt/lists/* &lt;span class=&quot;token operator&quot;&gt;\&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;# Upgrade pip and install the requirements.&lt;/span&gt;
    &amp;amp;&amp;amp; pip install --no-cache-dir --upgrade pip &lt;span class=&quot;token operator&quot;&gt;\&lt;/span&gt;
    &amp;amp;&amp;amp; pip install --no-cache-dir -r &lt;span class=&quot;token variable&quot;&gt;$FLEX_TEMPLATE_PYTHON_REQUIREMENTS_FILE&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;\&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;# Download the requirements to speed up launching the Dataflow job.&lt;/span&gt;
    &amp;amp;&amp;amp; pip download --no-cache-dir --dest /tmp/dataflow-requirements-cache -r &lt;span class=&quot;token variable&quot;&gt;$FLEX_TEMPLATE_PYTHON_REQUIREMENTS_FILE&lt;/span&gt;&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;# Since we already downloaded all the dependencies, there&apos;s no need to rebuild everything.&lt;/span&gt;
&lt;span class=&quot;token instruction&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;ENV&lt;/span&gt; PIP_NO_DEPS=True&lt;/span&gt;

&lt;span class=&quot;token instruction&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;ENTRYPOINT&lt;/span&gt; [&lt;span class=&quot;token string&quot;&gt;&quot;/opt/google/dataflow/python_template_launcher&quot;&lt;/span&gt;]&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;Options&quot; style=&quot;position:relative;&quot;&gt;Options&lt;a href=&quot;#Options&quot; aria-label=&quot;Options permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;h4 id=&quot;Using-the-build-script&quot; style=&quot;position:relative;&quot;&gt;Using the build script&lt;a href=&quot;#Using-the-build-script&quot; aria-label=&quot;Using the build script permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;The &lt;a href=&quot;https://github.com/mortie23/beam-jdbc-testing/blob/master/docker/dataflow-build.sh&quot;&gt;build script&lt;/a&gt;, will run all steps for you and do parameter replacement from the &lt;a href=&quot;https://github.com/mortie23/beam-jdbc-testing/blob/master/docker/config.yml&quot;&gt;config file&lt;/a&gt;:&lt;/p&gt;
&lt;p&gt;The &lt;a href=&quot;https://github.com/mortie23/beam-jdbc-testing/blob/master/docker/config.yml&quot;&gt;config file&lt;/a&gt; sets out the parameters for the DataFlow job.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;yaml&quot;&gt;&lt;pre class=&quot;language-yaml&quot;&gt;&lt;code class=&quot;language-yaml&quot;&gt;&lt;span class=&quot;token key atrule&quot;&gt;project_id&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;prj-xyz-&amp;lt;env&gt;-fruit&apos;&lt;/span&gt;
&lt;span class=&quot;token key atrule&quot;&gt;artifact_registry&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;australia-southeast1-docker.pkg.dev&apos;&lt;/span&gt;
&lt;span class=&quot;token key atrule&quot;&gt;artifact_repo_docker&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;rpo-xyz-&amp;lt;env&gt;-fruit-dkr&apos;&lt;/span&gt;
&lt;span class=&quot;token key atrule&quot;&gt;storage_bucket&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;bkt-xyz-&amp;lt;env&gt;-fruit&apos;&lt;/span&gt;
&lt;span class=&quot;token key atrule&quot;&gt;service_account&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;gsvc-xyz-&amp;lt;env&gt;-fruit&apos;&lt;/span&gt;
&lt;span class=&quot;token key atrule&quot;&gt;location&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;australia-southeast1&apos;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;sh&quot;&gt;&lt;pre class=&quot;language-sh&quot;&gt;&lt;code class=&quot;language-sh&quot;&gt;./dataflow-build.sh &lt;span class=&quot;token parameter variable&quot;&gt;--env&lt;/span&gt; dev&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h4 id=&quot;Manual-step-by-step&quot; style=&quot;position:relative;&quot;&gt;Manual step by step&lt;a href=&quot;#Manual-step-by-step&quot; aria-label=&quot;Manual step by step permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;This is manually performing the steps from the bash script previously mentioned.&lt;/p&gt;
&lt;p&gt;Build the Docker image on your local client development machine, tagging the image with the full reference to the GCP Artifact registry repository.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;sh&quot;&gt;&lt;pre class=&quot;language-sh&quot;&gt;&lt;code class=&quot;language-sh&quot;&gt;&lt;span class=&quot;token function&quot;&gt;docker&lt;/span&gt; build &lt;span class=&quot;token builtin class-name&quot;&gt;.&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-t&lt;/span&gt; australia-southeast1-docker.pkg.dev/&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;project_id&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;/&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;artifact_repository_docker&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;/dataflow/hellofruit:0.1&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The build step will take quite a bit of time, it has to pull the base image, copy the scripts and then install the dependencies etc.&lt;/p&gt;
&lt;p&gt;Before you try to push the container image to the Artifact registry repository you need to authenticate your &lt;code class=&quot;language-text&quot;&gt;gcloud&lt;/code&gt; CLI with the artifact registry.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;sh&quot;&gt;&lt;pre class=&quot;language-sh&quot;&gt;&lt;code class=&quot;language-sh&quot;&gt;gcloud auth configure-docker australia-southeast1-docker.pkg.dev&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Push the image to the registry.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;sh&quot;&gt;&lt;pre class=&quot;language-sh&quot;&gt;&lt;code class=&quot;language-sh&quot;&gt;&lt;span class=&quot;token function&quot;&gt;docker&lt;/span&gt; push australia-southeast1-docker.pkg.dev/&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;project_id&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;/&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;artifact_repository_docker&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;/dataflow/hellofruit:0.1&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Now we can build a flex template.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;sh&quot;&gt;&lt;pre class=&quot;language-sh&quot;&gt;&lt;code class=&quot;language-sh&quot;&gt;gcloud dataflow flex-template build &lt;span class=&quot;token string&quot;&gt;&quot;gs://&amp;lt;storage_bucket&gt;/dataflow/flex-template/hellofruit.json&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;
     &lt;span class=&quot;token parameter variable&quot;&gt;--image&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;australia-southeast1-docker.pkg.dev/&amp;lt;project-id&gt;/&amp;lt;artifact_repository_docker&gt;/dataflow/hellofruit:0.1&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;
     --sdk-language &lt;span class=&quot;token string&quot;&gt;&quot;PYTHON&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;
     --metadata-file &lt;span class=&quot;token string&quot;&gt;&quot;metadata.json&quot;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;This should run quite quickly and result in a JSON file in the bucket location you requested.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;log&quot;&gt;&lt;pre class=&quot;language-log&quot;&gt;&lt;code class=&quot;language-log&quot;&gt;Successfully saved container spec in flex template file&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;
&lt;span class=&quot;token property&quot;&gt;Template File GCS Location:&lt;/span&gt; gs&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;storage_bucket&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token file-path string&quot;&gt;/dataflow/flex-template/hellofruit.json&lt;/span&gt;
&lt;span class=&quot;token property&quot;&gt;Container Spec:&lt;/span&gt;

&lt;span class=&quot;token operator&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token string&quot;&gt;&quot;defaultEnvironment&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token string&quot;&gt;&quot;image&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;australia-southeast1-docker.pkg.dev/&amp;lt;project-id&gt;/&amp;lt;artifact_repository_docker&gt;/dataflow/hellofruit:0.1&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token string&quot;&gt;&quot;metadata&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token string&quot;&gt;&quot;description&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Hello fruit Python flex template.&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token string&quot;&gt;&quot;name&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Hello fruit&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token string&quot;&gt;&quot;parameters&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;
            &lt;span class=&quot;token operator&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;token string&quot;&gt;&quot;helpText&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Name of the BigQuery output table name.&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
                &lt;span class=&quot;token string&quot;&gt;&quot;isOptional&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
                &lt;span class=&quot;token string&quot;&gt;&quot;label&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;BigQuery output table name.&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
                &lt;span class=&quot;token string&quot;&gt;&quot;name&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;output_table&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
                &lt;span class=&quot;token string&quot;&gt;&quot;regexes&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;
                    &lt;span class=&quot;token string&quot;&gt;&quot;([^:]+:)?[^.]+[.].+&quot;&lt;/span&gt;
                &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
            &lt;span class=&quot;token operator&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;token operator&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token string&quot;&gt;&quot;sdkInfo&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token string&quot;&gt;&quot;language&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;PYTHON&quot;&lt;/span&gt;
    &lt;span class=&quot;token operator&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token operator&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h4 id=&quot;Using-the-gcloud-CLI&quot; style=&quot;position:relative;&quot;&gt;Using the gcloud CLI&lt;a href=&quot;#Using-the-gcloud-CLI&quot; aria-label=&quot;Using the gcloud CLI permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;This method will do the build and push as well as create the flex template JSON file. However in certain GCP setups it may not run. Some of the magic it does requires certain permissions that may not be granted.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;sh&quot;&gt;&lt;pre class=&quot;language-sh&quot;&gt;&lt;code class=&quot;language-sh&quot;&gt;gcloud dataflow flex-template build &lt;span class=&quot;token string&quot;&gt;&quot;gs://&amp;lt;storage_bucket&gt;/dataflow/flex/hellofruit.json&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;
     --image-gcr-path &lt;span class=&quot;token string&quot;&gt;&quot;australia-southeast1-docker.pkg.dev/&amp;lt;project_id&gt;/&amp;lt;artifact_repository_docker&gt;/dataflow/hellofruit:latest&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;
     --staging-location &lt;span class=&quot;token string&quot;&gt;&quot;gs://&amp;lt;storage_bucket&gt;/dataflow/staging&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;
     --temp-location &lt;span class=&quot;token string&quot;&gt;&quot;gs://&amp;lt;storage_bucket&gt;/dataflow/temp&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;
     --gcs-log-dir &lt;span class=&quot;token string&quot;&gt;&quot;gs://&amp;lt;storage_bucket&gt;/dataflow/logs&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;
     --sdk-language &lt;span class=&quot;token string&quot;&gt;&quot;PYTHON&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;
     --flex-template-base-image &lt;span class=&quot;token string&quot;&gt;&quot;PYTHON3&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;
     --metadata-file &lt;span class=&quot;token string&quot;&gt;&quot;metadata.json&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;
     --py-path &lt;span class=&quot;token string&quot;&gt;&quot;.&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;
     &lt;span class=&quot;token parameter variable&quot;&gt;--env&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;FLEX_TEMPLATE_PYTHON_PY_FILE=hellofruit.py&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;
     &lt;span class=&quot;token parameter variable&quot;&gt;--env&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;FLEX_TEMPLATE_PYTHON_REQUIREMENTS_FILE=requirements.txt&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;
     --log-http &lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;
     &lt;span class=&quot;token parameter variable&quot;&gt;--verbosity&lt;/span&gt; debug&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h5&gt;Example error log&lt;/h5&gt;
&lt;p&gt;These are example logs that you might see if this fails for the reason such as you do not have storage bucket create access. Building it this way uses GCP &lt;em&gt;Cloud Run&lt;/em&gt; which will attempt to create a temporary bucket with the naming convention &lt;code class=&quot;language-text&quot;&gt;&amp;lt;project_id&gt;_cloudbuild&lt;/code&gt;&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;log&quot;&gt;&lt;pre class=&quot;language-log&quot;&gt;&lt;code class=&quot;language-log&quot;&gt;&lt;span class=&quot;token level debug keyword&quot;&gt;DEBUG&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; Running &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;gcloud&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;dataflow&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;flex&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;template&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;build&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;

&lt;span class=&quot;token property&quot;&gt;uri:&lt;/span&gt; &lt;span class=&quot;token url&quot;&gt;https://storage.googleapis.com/storage/v1/b/&amp;lt;project_id&gt;_cloudbuild?alt=json&lt;/span&gt;
&lt;span class=&quot;token level debug keyword&quot;&gt;DEBUG&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; Starting new HTTPS connection &lt;span class=&quot;token operator&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; storage&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;googleapis&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;com&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;443&lt;/span&gt;
&lt;span class=&quot;token level debug keyword&quot;&gt;DEBUG&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token url&quot;&gt;https://storage.googleapis.com:443&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;GET /storage/v1/b/&amp;lt;project_id&gt;_cloudbuild?alt=json HTTP/1.1&quot;&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;404&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;247&lt;/span&gt;

&lt;span class=&quot;token operator&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token string&quot;&gt;&quot;error&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token string&quot;&gt;&quot;code&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;404&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token string&quot;&gt;&quot;message&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;The specified bucket does not exist.&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token string&quot;&gt;&quot;errors&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;
      &lt;span class=&quot;token operator&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token string&quot;&gt;&quot;message&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;The specified bucket does not exist.&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token string&quot;&gt;&quot;domain&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;global&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token string&quot;&gt;&quot;reason&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;notFound&quot;&lt;/span&gt;
      &lt;span class=&quot;token operator&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
  &lt;span class=&quot;token operator&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token operator&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token property&quot;&gt;uri:&lt;/span&gt; &lt;span class=&quot;token url&quot;&gt;https://storage.googleapis.com/storage/v1/b?alt=json&amp;amp;enableObjectRetention=False&amp;amp;project=&amp;lt;project_id&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
&lt;span class=&quot;token level debug keyword&quot;&gt;DEBUG&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; Starting new HTTPS connection &lt;span class=&quot;token operator&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; storage&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;googleapis&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;com&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;443&lt;/span&gt;
&lt;span class=&quot;token level debug keyword&quot;&gt;DEBUG&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token url&quot;&gt;https://storage.googleapis.com:443&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;POST /storage/v1/b?alt=json&amp;amp;enableObjectRetention=False&amp;amp;project=&amp;lt;project_id&gt; HTTP/1.1&quot;&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;403&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;546&lt;/span&gt;
&lt;span class=&quot;token operator&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token string&quot;&gt;&quot;error&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token string&quot;&gt;&quot;code&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;403&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token string&quot;&gt;&quot;message&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;&amp;lt;user&gt;@&amp;lt;domain&gt; does not have storage.buckets.create access to the Google Cloud project. Permission &apos;storage.buckets.create&apos; denied on resource (or it may not exist).&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token string&quot;&gt;&quot;errors&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;
      &lt;span class=&quot;token operator&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token string&quot;&gt;&quot;message&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;&amp;lt;user&gt;@&amp;lt;domain&gt; does not have storage.buckets.create access to the Google Cloud project. Permission &apos;storage.buckets.create&apos; denied on resource (or it may not exist).&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token string&quot;&gt;&quot;domain&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;global&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token string&quot;&gt;&quot;reason&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;forbidden&quot;&lt;/span&gt;
      &lt;span class=&quot;token operator&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
  &lt;span class=&quot;token operator&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token operator&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token level debug keyword&quot;&gt;DEBUG&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;(&lt;/span&gt;gcloud&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;dataflow&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;flex&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;template&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;build&lt;span class=&quot;token operator&quot;&gt;)&lt;/span&gt; The user is forbidden from accessing the bucket &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;project_id&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;_cloudbuild&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt; Please check your organization&apos;s policy or if the user has the &lt;span class=&quot;token string&quot;&gt;&quot;serviceusage.services.use&quot;&lt;/span&gt; permission&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt; Giving the user Owner&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; Editor&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; or Viewer roles may also fix this issue&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt; Alternatively&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; use the &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;no&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;source option and access your source code via a different method&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;

&lt;span class=&quot;token property&quot;&gt;googlecloudsdk.command_lib.builds.submit_util.BucketForbiddenError:&lt;/span&gt; The user is forbidden from accessing the bucket &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;project_id&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;_cloudbuild&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt; Please check your organization&apos;s policy or if the user has the &lt;span class=&quot;token string&quot;&gt;&quot;serviceusage.services.use&quot;&lt;/span&gt; permission&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt; Giving the user Owner&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; Editor&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; or Viewer roles may also fix this issue&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt; Alternatively&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; use the &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;no&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;source option and access your source code via a different method&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;
&lt;span class=&quot;token level error important&quot;&gt;ERROR&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;(&lt;/span&gt;gcloud&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;dataflow&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;flex&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;template&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;build&lt;span class=&quot;token operator&quot;&gt;)&lt;/span&gt; The user is forbidden from accessing the bucket &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;project_id&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;_cloudbuild&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt; Please check your organization&apos;s policy or if the user has the &lt;span class=&quot;token string&quot;&gt;&quot;serviceusage.services.use&quot;&lt;/span&gt; permission&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt; Giving the user Owner&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; Editor&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; or Viewer roles may also fix this issue&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt; Alternatively&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; use the &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;no&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;source option and access your source code via a different method&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2 id=&quot;Run-your-custom-flex-template&quot; style=&quot;position:relative;&quot;&gt;Run your custom flex template&lt;a href=&quot;#Run-your-custom-flex-template&quot; aria-label=&quot;Run your custom flex template permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Running a Dataflow job using a built flex template and Docker container&lt;/p&gt;
&lt;h3 id=&quot;Options-1&quot; style=&quot;position:relative;&quot;&gt;Options&lt;a href=&quot;#Options-1&quot; aria-label=&quot;Options 1 permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;h4 id=&quot;Using-the-Run-script&quot; style=&quot;position:relative;&quot;&gt;Using the Run script&lt;a href=&quot;#Using-the-Run-script&quot; aria-label=&quot;Using the Run script permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;Like the build script, the &lt;a href=&quot;https://github.com/mortie23/beam-jdbc-testing/blob/master/docker/dataflow-run.sh&quot;&gt;run script&lt;/a&gt; will run all steps for you and do parameter replacement from the &lt;a href=&quot;https://github.com/mortie23/beam-jdbc-testing/blob/master/docker/config.yml&quot;&gt;config file&lt;/a&gt;.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;sh&quot;&gt;&lt;pre class=&quot;language-sh&quot;&gt;&lt;code class=&quot;language-sh&quot;&gt;./dataflow-run.sh &lt;span class=&quot;token parameter variable&quot;&gt;--env&lt;/span&gt; dev &lt;span class=&quot;token parameter variable&quot;&gt;--name&lt;/span&gt; hellofruit&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h4 id=&quot;Dataflow-UI&quot; style=&quot;position:relative;&quot;&gt;Dataflow UI&lt;a href=&quot;#Dataflow-UI&quot; aria-label=&quot;Dataflow UI permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;Using the Dataflow user interface you can use the &lt;em&gt;Create a job from template&lt;/em&gt; and browse to Custom template JSON that was created in the build step.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 803px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/07f6086a6997cae28dddedba5a91fcf2/476b8/dataflow-beam-docker-run-gui.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 62.4%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAMCAIAAADtbgqsAAAACXBIWXMAAAsSAAALEgHS3X78AAABTUlEQVQoz4XS227kIAwA0Pz//6VPSDNKOtlmuBiMSQIOsApsq6rSdM8DQkjY8mVAxHEchRDjOK7rWkph5vM1Zo4xPZ/PEMJwxGiMsdZKKbdtq6+VUtqRSznneXLODSEEItr3PYSAiMYYALDWIqLWut8BQGuNnjQkcEmauB1nrXUgovf5fVmWdV0R0VrrGyICgH4hoiucJ6QzbOzDecRcaxk+9J83Mc7TPE0TETnnoHHOee/NJ2stM9da88mPx7wsj5TSgIQKFBEZY6SUWmul1LNZ11Up5b3HJsaYc2bmq5IWa9j33eh/4bXW1lpjTEqJmY/j6NleGbZt64UhIhF57wEg5/y9yd2Pl+vzsixCiNvtNk3T/X4XQkgp6/8w877vV2YA6H221jrnQgi5Ka9xc81ZKdWH2aFzqVX7i75qQ4ypr8TXbjjnev3lVznnv1G2slPQ8FwkAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;dataflow beam docker run gui&quot;
        title=&quot;dataflow beam docker run gui&quot;
        src=&quot;/static/07f6086a6997cae28dddedba5a91fcf2/476b8/dataflow-beam-docker-run-gui.png&quot;
        srcset=&quot;/static/07f6086a6997cae28dddedba5a91fcf2/0eb09/dataflow-beam-docker-run-gui.png 500w,
/static/07f6086a6997cae28dddedba5a91fcf2/476b8/dataflow-beam-docker-run-gui.png 803w&quot;
        sizes=&quot;(max-width: 803px) 100vw, 803px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;Given that our &lt;code class=&quot;language-text&quot;&gt;metadata.json&lt;/code&gt; configuration contained a parameter of &lt;code class=&quot;language-text&quot;&gt;output_table&lt;/code&gt; the first optional parameter is Output table.&lt;/p&gt;
&lt;h2 id=&quot;Results&quot; style=&quot;position:relative;&quot;&gt;Results&lt;a href=&quot;#Results&quot; aria-label=&quot;Results permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;When the DataFlow job runs successfully you should see a Job Graph like this:&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 2000px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/706453b893b5607543c167fafacd4c38/6bde6/dataflow-beam-docker-df-graph.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 55.00000000000001%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAALCAIAAADwazoUAAAACXBIWXMAAAsSAAALEgHS3X78AAABe0lEQVQoz22R2Y7bMAxF8///1k5RBEkDIw/JYGJbsq3d5iKJhRdMZ4DeJwLioS55T7lICL5rWyIWkVoFqVrPkyPiCrAMw5Bzlv/p5AL3A4ZIy5zcJmIGwHE0AEBE3vtSiogQESLWLzrNC3589M4F5gyARJRz3vtExBijlNocVdq01weMiH3fL8siIplzLZJzTinFGEVEKdU0Ta1VRPSkx3EEgJwzM+ecT1XWh7hEZfrBam314AYg2Ley1t5ut3Ec19rZrutijPM8p5QQ8YDDHGxY+y5/Li44Ytph7/3j8Xg+n8aY1VFM3w72WZVctNKDHtZtqxxWtb5cLtfrtW1bRHTOxRgRsWw6YGICAhfcggsSfmZjrX29XsxrigBgjPHexxhDCJvt7YfJT+/q/df57efvH93Qzcu8w23Xnc/ntm1LKSklpVQI4d+1D9NVmFgrrXqNAHse69Bpaprmfr8zs/feGPMt54OtlZh4ExHt5G77uclaCwAppa85/wVD9XpWij6gtQAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;dataflow beam docker df graph&quot;
        title=&quot;dataflow beam docker df graph&quot;
        src=&quot;/static/706453b893b5607543c167fafacd4c38/f97d7/dataflow-beam-docker-df-graph.png&quot;
        srcset=&quot;/static/706453b893b5607543c167fafacd4c38/0eb09/dataflow-beam-docker-df-graph.png 500w,
/static/706453b893b5607543c167fafacd4c38/1263b/dataflow-beam-docker-df-graph.png 1000w,
/static/706453b893b5607543c167fafacd4c38/f97d7/dataflow-beam-docker-df-graph.png 2000w,
/static/706453b893b5607543c167fafacd4c38/6bde6/dataflow-beam-docker-df-graph.png 2100w&quot;
        sizes=&quot;(max-width: 2000px) 100vw, 2000px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;Checking the target BigQuery table shows successful rows written to the table (this is a result of multiple successful runs).&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 962px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/c90dbb9bd174f14b7b4eff52b7332d3f/ef0cc/dataflow-beam-docker-bq-restuls.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 145.79999999999998%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAdCAIAAAAl5NuSAAAACXBIWXMAAAsSAAALEgHS3X78AAAClElEQVQ4y5WUS2/TQBSF/bP5C6zYsUNixw7WCIkVEttGbUFUSTy2UWOPPZ73w/NEHoc0DW0TzsKSLX9z7rkzd4q7u7umrjsIV1dX19erbQmcs1Katx/q1+/Aqzfrr99hP+qfG1Y2fMR6muxBBYRwGHqMSVnWANR93zvnlJ4+f9t9+vL7/Udw8wuPxDQ7SagW8gi1tgghqFkTpo4JH1OKWekCFc45zrmUmgvr/CMmxuT9/JxXfA5mjGk9YSx2O4jxCDvYZSmp8hLxZWcmlekH1UHaD3xeSyljjHf+fNmUUqUUoWy3a9GIy7Lkgr/s+QBLKbXWS59CCOlvz86qsNaGEGKMPivG6Jy39tGWPKcipYjHkRAipRSCIzRwztNlKlJKoCxXq1VZluv1+vb2pmmaSwLP8CGqy4oheO+89ylF74OZfI4VUpr/OdacOaXU97Dcbuum3m63TdPUVaWNSSlBNP1Yi02tRmJPyshsLCilI8YhBK01pVRKaczkvSfMQKT7UfdId4MasSIEjxhDCAkhQohpmmZ4QIhzTjCumxpCyDl3znnvQnAhP52bpBSEEIQQhJBSaoxhjM1bZYwRWU0WQmjpwkHLLh6X7b3XWs+w1ppzrpRqswghJ4dh4U++GGOKaZqUUowxKWVVVQCAYRjOwt77PXxwXoaJUnqps7V2norcQABAVVUIofCPvPfHr865R5mFEG3bYoyfnIHlVByXrZTaO9OszWZzf3/vnHvZOcZoreWcP2QWQnRdRwg5MXky8zLIe1hKyRgDALRteyF8VHYeybZtx3G8BN5nXpyXfS7LkjG2HKCzmYUQszNjjBCilGqaRkqZhyZclHmBlztwvvryNfJ/sBBCSgkAWOCXyw4hLGX/AWvPkV6cstAIAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;dataflow beam docker bq restuls&quot;
        title=&quot;dataflow beam docker bq restuls&quot;
        src=&quot;/static/c90dbb9bd174f14b7b4eff52b7332d3f/ef0cc/dataflow-beam-docker-bq-restuls.png&quot;
        srcset=&quot;/static/c90dbb9bd174f14b7b4eff52b7332d3f/0eb09/dataflow-beam-docker-bq-restuls.png 500w,
/static/c90dbb9bd174f14b7b4eff52b7332d3f/ef0cc/dataflow-beam-docker-bq-restuls.png 962w&quot;
        sizes=&quot;(max-width: 962px) 100vw, 962px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;h2 id=&quot;Notes&quot; style=&quot;position:relative;&quot;&gt;Notes&lt;a href=&quot;#Notes&quot; aria-label=&quot;Notes permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;I personally found the development and debugging experience for this process very atypical. Nothing seemed to run smoothly and there were many error messages that were very difficult to debug. Lack of community support and lack of stack overflow questions and answers was definitely noticeable compared to other coding frameworks.&lt;/p&gt;
&lt;p&gt;One example I had was that I had it all working one day, and the next day it stopped working with the following error message. I found a post that suggested this could be many reasons, and one suggestion was to change the location. I changed the location to a US location and it worked. However this might not be an option for all users.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 1887px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/251abd30369e42ec6fbe9b7f888a4d38/53dfd/dataflow-beam-docker-location.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 41.4%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAICAIAAAB2/0i6AAAACXBIWXMAAAsSAAALEgHS3X78AAABG0lEQVQY03WQQXLDMAhFff8jdiZuOxMncWxhC5AQIFud2G12fQtYwOcDHQDknBGRmbat3adC7CvairZEU9ta2/+jEymI1F96RJyeU9/3Ma5MtK4rAKSUSiln6/bHvrcjtU615Jw+v78CRFWFAMw8z9OyLCIyjiNAYOZpmnLOACGEALfhfvnA+dn1V6FkEGNE37ZdS3F3RCSifd9VlYhqdeakqmZmpfDjivNIj2s3L6ai6TGufKxyEA9ae60nItWriByVV0PJ6Xkb3GvnbtVUwpJJX+ccEBEzn2J3r7Wqajte11oz8yVGM+uyiErG4QZQ3s5w0FqrteaU3P2cdSIiwzCoaqeq1awgpbLtv9r2ejjT2/mMb7GqzvNsaj/u685pDS3L/gAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;dataflow beam docker location&quot;
        title=&quot;dataflow beam docker location&quot;
        src=&quot;/static/251abd30369e42ec6fbe9b7f888a4d38/53dfd/dataflow-beam-docker-location.png&quot;
        srcset=&quot;/static/251abd30369e42ec6fbe9b7f888a4d38/0eb09/dataflow-beam-docker-location.png 500w,
/static/251abd30369e42ec6fbe9b7f888a4d38/1263b/dataflow-beam-docker-location.png 1000w,
/static/251abd30369e42ec6fbe9b7f888a4d38/53dfd/dataflow-beam-docker-location.png 1887w&quot;
        sizes=&quot;(max-width: 1887px) 100vw, 1887px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;log&quot;&gt;&lt;pre class=&quot;language-log&quot;&gt;&lt;code class=&quot;language-log&quot;&gt;Failed to start the VM&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; launcher&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; used for launching because of
&lt;span class=&quot;token property&quot;&gt;status code:&lt;/span&gt; UNAVAILABLE&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;token property&quot;&gt;reason:&lt;/span&gt; &lt;span class=&quot;token property&quot;&gt;One or more operations had an error:&lt;/span&gt;
  &lt;span class=&quot;token string&quot;&gt;&apos;operation----&apos;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;UNAVAILABLE&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;HTTP_503&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;</content:encoded></item><item><title><![CDATA[Copying a table from Teradata to Google BigQuery]]></title><description><![CDATA[Copying a table from Teradata to Google BigQuery For this demo I will be using a Windows PC including: Teradata Studio Teradata ClearScape…]]></description><link>https://mortimer.xyz/google-bigquery-teradata/</link><guid isPermaLink="false">https://mortimer.xyz/google-bigquery-teradata/</guid><pubDate>Tue, 03 Oct 2023 00:00:00 GMT</pubDate><content:encoded>&lt;h1 id=&quot;Copying-a-table-from-Teradata-to-Google-BigQuery&quot; style=&quot;position:relative;&quot;&gt;Copying a table from Teradata to Google BigQuery&lt;a href=&quot;#Copying-a-table-from-Teradata-to-Google-BigQuery&quot; aria-label=&quot;Copying a table from Teradata to Google BigQuery permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h1&gt;
&lt;p&gt;For this demo I will be using a Windows PC including:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Teradata Studio&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://clearscape.teradata.com/sign-in&quot;&gt;Teradata ClearScape&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Google BigQuery&lt;/li&gt;
&lt;li&gt;Google DataFlow&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This will demonstrate the most basic copy of a table from a Teradata ClearScape instance to a Google Cloud BigQuery table.&lt;/p&gt;
&lt;p&gt;It will not discuss things such as:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Automation of schema creation for the target table in BigQuery&lt;/li&gt;
&lt;li&gt;Scheduling the pipeline&lt;/li&gt;
&lt;li&gt;Handling drop and recreate or truncate and load of the target table&lt;/li&gt;
&lt;li&gt;Other transformations&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;Sign-up-for-Teradata-ClearScape&quot; style=&quot;position:relative;&quot;&gt;Sign up for Teradata ClearScape&lt;a href=&quot;#Sign-up-for-Teradata-ClearScape&quot; aria-label=&quot;Sign up for Teradata ClearScape permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Getting a cloud hosted Teradata instance for testing these days is easy using the &lt;a href=&quot;https://clearscape.teradata.com/sign-in&quot;&gt;free trial of Teradata ClearScape&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 699px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/cbf3aacb1b2c9949af1313595feb44ac/6a959/tdbq-create-environment.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 69.19999999999999%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAOCAIAAACgpqunAAAACXBIWXMAAAsSAAALEgHS3X78AAABEklEQVQoz6WRPW7DMAyFfeHcox2TQyRjD1CgSwt0CbK1VoD8eI6NurZikRRJubAcN0EHx0C/QSBFPfDhKQGAuq6ttU3EOWetPQ+0oyRVVRljttvt4XBI0/R4PBpjdrudMSbLsjviEIIfEJE/4zBKwsz56XTK86IomDmEoKq/45G1nbh/LSJBdeLC6+YQAhF6T4BIiM45Zj9RnohIWZblVwcS3nXbtq2q7vd7EUlUtRkAAEQkov6MBdANANDNEAHgYvtsbR9VjJyY2XvPkXjDN/Wlv9quIk3j2slIpLNN1Nkgoikh9V/DzKKatP9gqpiZv6sqqLxsNg/L1eNy9f75MVVMhHlRCMvT69tsvpjNF8/r9Q+m+TBrfkZBwQAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;tdbq create environment&quot;
        title=&quot;tdbq create environment&quot;
        src=&quot;/static/cbf3aacb1b2c9949af1313595feb44ac/6a959/tdbq-create-environment.png&quot;
        srcset=&quot;/static/cbf3aacb1b2c9949af1313595feb44ac/0eb09/tdbq-create-environment.png 500w,
/static/cbf3aacb1b2c9949af1313595feb44ac/6a959/tdbq-create-environment.png 699w&quot;
        sizes=&quot;(max-width: 699px) 100vw, 699px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 550px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/b257086bdf5a9a05682397ace2886f62/8c3ad/tdbq-initializing-environment.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 40.599999999999994%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAICAIAAAB2/0i6AAAACXBIWXMAAAsSAAALEgHS3X78AAAA2UlEQVQY05XOzWqEMBSG4bn/OxIMonQUhQ6TYnQ3gYFqchLzY2aSuBuqULuRtu/i7B6+c3oe5Jwzxhhrn8edjiQAfI6jANBa/w/P1o6M0ftdSimE+BN+rH3heQYAIQQAKKV+xyGEGON2fQjOOWutMcZ7H9ZijMuyhBC2gR1774uiqKoKY4wQIoQ0TZPnedu2dV1nWZYkSZqmCCFKaYxx8/vyMAyMMXq7lWV5xRh/4Lfzue/7rusul3dCCOecUso5/x7f8faV914pJaWcpkmtaa2lFNbaZe3n2y9u0L0uNWVGEwAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;tdbq initializing environment&quot;
        title=&quot;tdbq initializing environment&quot;
        src=&quot;/static/b257086bdf5a9a05682397ace2886f62/8c3ad/tdbq-initializing-environment.png&quot;
        srcset=&quot;/static/b257086bdf5a9a05682397ace2886f62/0eb09/tdbq-initializing-environment.png 500w,
/static/b257086bdf5a9a05682397ace2886f62/8c3ad/tdbq-initializing-environment.png 550w&quot;
        sizes=&quot;(max-width: 550px) 100vw, 550px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;h2 id=&quot;Check-environment&quot; style=&quot;position:relative;&quot;&gt;Check environment&lt;a href=&quot;#Check-environment&quot; aria-label=&quot;Check environment permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Once the environment is created, let’s use the API to check a few things available with the free trail. Click on Copy API Token.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 222px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/d82d86d38585aba5cc35b15cef1c19df/1ff5d/tdbq-api-token.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 31.98198198198198%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAGCAIAAABM9SnKAAAACXBIWXMAAAsSAAALEgHS3X78AAABB0lEQVQY012Qb0uDUBjF/f5fJAj2okW4Bi02Yklr2ubSko2c3j9edbalca+P3muobC/6cV4cDs+Bh6OVZYkpQyRCJApxq4vvDOvDThRTRqIYUxZiKoTQ8rzAhDWNklI2Haq1ra87mjNKKQDI86Kqqu/jiSWHtpykGULk1dpsnE9r7e4D5LjeaDy13e2b/WFa9my+NFfO+t1L0uz65t4PMBeCRHFXPmT7IHx6Xt6NHq4GurFYGS/WrT7Z+cjbfj1O54PheKhPZoZVFL8L0975IeeC9mVCmVKyqmsAEKIEgP5tWdey+1ydqQA45wBwPP2wONWEECGm/UiYsl7tWm14GfL/QYAI5+IPPrtF6oXxhjoAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;tdbq api token&quot;
        title=&quot;tdbq api token&quot;
        src=&quot;/static/d82d86d38585aba5cc35b15cef1c19df/1ff5d/tdbq-api-token.png&quot;
        srcset=&quot;/static/d82d86d38585aba5cc35b15cef1c19df/1ff5d/tdbq-api-token.png 222w&quot;
        sizes=&quot;(max-width: 222px) 100vw, 222px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;Create a file called &lt;code class=&quot;language-text&quot;&gt;.env&lt;/code&gt; and paste it in that file like this.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;ini&quot;&gt;&lt;pre class=&quot;language-ini&quot;&gt;&lt;code class=&quot;language-ini&quot;&gt;&lt;span class=&quot;token key attr-name&quot;&gt;API_TOKEN&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token value attr-value&quot;&gt;&amp;lt;token&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Create the following Python script and install the required packages with pip and then run it.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;py&quot;&gt;&lt;pre class=&quot;language-py&quot;&gt;&lt;code class=&quot;language-py&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; requests
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; os
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; json
&lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; dotenv &lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; load_dotenv

&lt;span class=&quot;token comment&quot;&gt;# Load environment variables from .env file&lt;/span&gt;
load_dotenv&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;# Get the bearer token from the environment variables&lt;/span&gt;
API_TOKEN &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; os&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;getenv&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;API_TOKEN&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

url &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;https://api.clearscape.teradata.com/environments&apos;&lt;/span&gt;
headers &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token string&quot;&gt;&apos;accept&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;application/json&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token string&quot;&gt;&apos;Authorization&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string-interpolation&quot;&gt;&lt;span class=&quot;token string&quot;&gt;f&apos;Bearer &lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;API_TOKEN&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;# Handle response&lt;/span&gt;
response &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; requests&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;get&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;url&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; headers&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;headers&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
data &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; response&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;json&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
formatted_data &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; json&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;dumps&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;data&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; indent&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;formatted_data&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The response should look something like this.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;json&quot;&gt;&lt;pre class=&quot;language-json&quot;&gt;&lt;code class=&quot;language-json&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&quot;state&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;RUNNING&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&quot;region&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;us-central&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&quot;services&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token property&quot;&gt;&quot;name&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Jupyterlab&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token property&quot;&gt;&quot;state&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;RUNNING&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token property&quot;&gt;&quot;url&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;https://dev-gcp-copy-&amp;lt;id&gt;.env.clearscape.teradata.com:8443/lab/tree/README.md?token=&amp;lt;token&gt;&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token property&quot;&gt;&quot;credentials&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token property&quot;&gt;&quot;name&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Vantage&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token property&quot;&gt;&quot;state&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;RUNNING&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token property&quot;&gt;&quot;url&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;bteq .logon dev-gcp-copy-&amp;lt;id&gt;.env.clearscape.teradata.com/demo_user&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token property&quot;&gt;&quot;credentials&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;
          &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token property&quot;&gt;&quot;name&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;username&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;token property&quot;&gt;&quot;value&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;demo_user&quot;&lt;/span&gt;
          &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token property&quot;&gt;&quot;name&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Query Service&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token property&quot;&gt;&quot;state&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;RUNNING&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token property&quot;&gt;&quot;url&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;https://dev-gcp-copy-&amp;lt;id&gt;.env.clearscape.teradata.com:1443/&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token property&quot;&gt;&quot;credentials&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;
          &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token property&quot;&gt;&quot;name&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;username&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;token property&quot;&gt;&quot;value&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;demo_user&quot;&lt;/span&gt;
          &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token property&quot;&gt;&quot;name&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;ModelOps&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token property&quot;&gt;&quot;state&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;RUNNING&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token property&quot;&gt;&quot;url&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;https://dev-gcp-copy-&amp;lt;id&gt;.env.clearscape.teradata.com:8443/modelops&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token property&quot;&gt;&quot;credentials&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;
          &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token property&quot;&gt;&quot;name&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;username&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;token property&quot;&gt;&quot;value&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;demo_user&quot;&lt;/span&gt;
          &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&quot;name&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;dev-gcp-copy&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&quot;ip&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;&amp;lt;ip-address&gt;&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&quot;dnsName&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;dev-gcp-copy-&amp;lt;id&gt;.env.clearscape.teradata.com&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&quot;owner&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;christopher@mortimer.xyz&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&quot;type&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;clearscape&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&quot;createDate&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;2023-10-03T09:44:10.710Z&quot;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2 id=&quot;Connect-with-Teradata-Studio&quot; style=&quot;position:relative;&quot;&gt;Connect with Teradata Studio&lt;a href=&quot;#Connect-with-Teradata-Studio&quot; aria-label=&quot;Connect with Teradata Studio permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;To test we can connect to the Teradata Database we will use Teradata Studio installed on our Windows client machine.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 1946px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/a9469cace02ac9dd1960cda0422ad16a/204ec/tdbq-studio-connection.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 73.00000000000001%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAPCAIAAABr+ngCAAAACXBIWXMAAAsSAAALEgHS3X78AAACUUlEQVQoz22RbU/aUBiG+3+odGnx06SogHRYbEtPT09bDn0/fZHotn+xjUGFuS3Zb1n2zZe4CbpI3b9ZCotx6JMr95eTK+fJ/VCXP76fXVyeX1yenV/c/r7L7/8s8vxukS/yfFHk/d0in9/cXs/m1/N5kbP5r9ns6urn/OaWOlDUjiS32mKrLTZb7Y6kOAEJkjRIDv049aMkGRwdHr9xwsgjsRcV+HE6OD6yHI8SBKFR3+X5ao3nazV+q1oFyLQDgj0fez60cBgfvh+OPo6zD6PxcJwNx9n09Mvnr9+wH1Asy9E0TW8UUyrRLMcBw8SebzkutHqKbgKj1+rIe/tSQxB3mu2dZnu7+Wq7IbREqZAZhnmxHIZhuEoFmr0gSU3bgRYevH6bTaajbJKdTLPJpxXjyfRkevpuOCrkcrnMLKdcLrMcZ9kuSQcGtoFh6VYf2z52Ctwg8sK4gCQeSdwwXpcrlQpAJjAsgEwFIlnTZagXqekSgCsOVK3TBQeq9oysQKQZWNGQIEp7+9K+rD5GVECxjusjbFMsty6ryNTMvqwZLVEuunlErSHUBRFh2yORZbtPft7clADcbrZrdWHNfJCdkKRHx0GcPiMrmi4qYG3bBzoKwG7gBMQOwnWZWxbmkdghsUtiN4xcEiNsq6sWlygQKcsWKYZh6FJpg6Y3aJoulViWlTWoIlPVjYf8V7umy0CXgK5AVDwZFrVTb27x/IqX1Wptd7e70p4ADEsze8Xxe7YdpQ5Jqb5Husj8D914igIRtLAbRn0/NPqO5XhG3/kLv5E6/yEAZwoAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;tdbq studio connection&quot;
        title=&quot;tdbq studio connection&quot;
        src=&quot;/static/a9469cace02ac9dd1960cda0422ad16a/204ec/tdbq-studio-connection.png&quot;
        srcset=&quot;/static/a9469cace02ac9dd1960cda0422ad16a/0eb09/tdbq-studio-connection.png 500w,
/static/a9469cace02ac9dd1960cda0422ad16a/1263b/tdbq-studio-connection.png 1000w,
/static/a9469cace02ac9dd1960cda0422ad16a/204ec/tdbq-studio-connection.png 1946w&quot;
        sizes=&quot;(max-width: 1946px) 100vw, 1946px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;h3 id=&quot;Create-source-table&quot; style=&quot;position:relative;&quot;&gt;Create source table&lt;a href=&quot;#Create-source-table&quot; aria-label=&quot;Create source table permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Let’s create a very simple demo hello word table for copying to BigQuery.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;sql&quot;&gt;&lt;pre class=&quot;language-sql&quot;&gt;&lt;code class=&quot;language-sql&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;create&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;table&lt;/span&gt; demo_user&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;testing &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
    col1 &lt;span class=&quot;token keyword&quot;&gt;integer&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; col2 &lt;span class=&quot;token keyword&quot;&gt;varchar&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;100&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;insert&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;into&lt;/span&gt; demo_user&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;testing &lt;span class=&quot;token keyword&quot;&gt;values&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;hello&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;insert&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;into&lt;/span&gt; demo_user&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;testing &lt;span class=&quot;token keyword&quot;&gt;values&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;world&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 1137px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/f7c6af0cf3b18198df017de503833ab6/7ec87/tdbq-create-table.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 45.599999999999994%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAJCAIAAAC9o5sfAAAACXBIWXMAAAsSAAALEgHS3X78AAABlElEQVQoz23Pf2vTQBjA8bwXM5O7S+5HftyluTRpejdNly6GKSssTVSs+J9DUJjMdYtDfY3qCykVbKUUZIx9+D5/PvA8xviJjtORTLPpcTWtT2ZNmyuNGdPF5MPHT+/Pzy8uPl9efinLaSQGkYiEiMKQyySVSWZkWVoUT9V4LATH2O267nTWHmAyHKmTrDieHI2V0kq5GFt3AAAghAbGpK7rJEk8Rh2Ezpqmmb+GEDLXJQc2dl3LskzTBLYN7rN3y9Wz+lBrQikA9rxtm1fvTJ8/5pHM83Q4zEejZJRbIg65EEESBBxCgCBCCBmY0IHwq/IwCDkEoO1eTusXFoQuZZR5nscYY5Qxh1JCCMEUY+I47j6DUMZDr5zoNM2CIGjbTillmo8QhHdPRHD/5m7+MyhlvkcnhdJaZ1m2WLwtq9ohNBSRHwoexVwMwvtF+4xYykTK0+dVWR5JKd8sFt++/1gul8vrvu9vr66ub26+9v3tgxmMMdfFPPTTYRwwND+b/fz1e7vdbjab1Wq12fm7Xq//POQf/QWlS2KjRuUAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;tdbq create table&quot;
        title=&quot;tdbq create table&quot;
        src=&quot;/static/f7c6af0cf3b18198df017de503833ab6/7ec87/tdbq-create-table.png&quot;
        srcset=&quot;/static/f7c6af0cf3b18198df017de503833ab6/0eb09/tdbq-create-table.png 500w,
/static/f7c6af0cf3b18198df017de503833ab6/1263b/tdbq-create-table.png 1000w,
/static/f7c6af0cf3b18198df017de503833ab6/7ec87/tdbq-create-table.png 1137w&quot;
        sizes=&quot;(max-width: 1137px) 100vw, 1137px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;h2 id=&quot;BigQuery-Data-Flow&quot; style=&quot;position:relative;&quot;&gt;BigQuery Data Flow&lt;a href=&quot;#BigQuery-Data-Flow&quot; aria-label=&quot;BigQuery Data Flow permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;h3 id=&quot;Target-table&quot; style=&quot;position:relative;&quot;&gt;Target table&lt;a href=&quot;#Target-table&quot; aria-label=&quot;Target table permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;For this example I have created a target table with the same schema as the source table in BigQuery using the GUI. For simplicity, I have also created a BigQuery Dataset with the same name as the source Teradata database.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 953px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/9756a1299aca2caa0b21364660bfbcbb/9187f/tdbq-create-table-bq.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 95.60000000000001%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAATCAIAAAAf7rriAAAACXBIWXMAAAsSAAALEgHS3X78AAABkklEQVQ4y5WS3Y7bIBCFeX+pfYXuq/Q6ykUt5SJSFVn1T5M4BvPrGWDMytCt3Ha7ir8LC0Ye4Jw5zDn3eDzGcQwhpD3MQGyapqqqhmEgomUnbJ7ny+XStq33Pj4HUQSAEAJ72++7NqV8M+f8eDyez2etdUrpmVOIlhgpPxug67phGIQQRPTMm7XBn3cXY1w1t21rjM3nPad5/YRIkQHAMAyISHsIYbWJuXnuug4RIVMWmCmL8B5lNCyEwDk3xjjnrLUuYzOl+NGcAaCuayFEsfpf3pV9u92MMb9uttbmGfzd9j/N3vtV8/V6PZ1OnPO0nzUkWutxHJVS25AQEQBsIpW894iYSriK5pQSANzv93meN0kgRGyaRkqZBxMQUUrZNI1zrjwbAJgPwVrXdb3Sep39Zhh93zu3JslnlFJ93wNAqazNyxIpQvBu+TNg3nvOOQAU5xDXv4UQRUuuIPvRjodv10N1+14/lJyEmOQbWmullJRymiZrbTFp6wKrzvrzS/3pS/31wJUcpdIf2Ft6frv9CgoRVF3FsiDVAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;tdbq create table bq&quot;
        title=&quot;tdbq create table bq&quot;
        src=&quot;/static/9756a1299aca2caa0b21364660bfbcbb/9187f/tdbq-create-table-bq.png&quot;
        srcset=&quot;/static/9756a1299aca2caa0b21364660bfbcbb/0eb09/tdbq-create-table-bq.png 500w,
/static/9756a1299aca2caa0b21364660bfbcbb/9187f/tdbq-create-table-bq.png 953w&quot;
        sizes=&quot;(max-width: 953px) 100vw, 953px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;h3 id=&quot;Cloud-Storage-for-driver&quot; style=&quot;position:relative;&quot;&gt;Cloud Storage for driver&lt;a href=&quot;#Cloud-Storage-for-driver&quot; aria-label=&quot;Cloud Storage for driver permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;I have created a Google Cloud Storage bucket and downloaded the latest JDBC driver jar file and copied it up to cloud storage.&lt;/p&gt;
&lt;p&gt;I have also added a &lt;code class=&quot;language-text&quot;&gt;temp&lt;/code&gt; folder for later use for the DataFlow job.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 486px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/146e377e0002220a22ce17b9b5890ae0/c42ee/tdbq-gcs-td-driver.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 117.6954732510288%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAYCAIAAAB1KUohAAAACXBIWXMAAAsSAAALEgHS3X78AAADLklEQVQ4y4WT6W7cNhSF9f6vkndwCthG7cauO56xJzMjS9RGLdy0kKK4iYWkpkFiGP7AH5fgPZfk5WEwCCvkPE0jAAAkSVFAAGIAAITQf0YwCMOl1WpKknTTpOkSlCWcPyOY5/mjwp+LlVLNSp7nfd9zztM0JStKqd9K/DYNOOd3d3f39/dXV1dPT0+73e729vb6+vrm5oZSumV/eGdrLcG4LMtxlO+XnbNFUWRZVpZlkiTGWErJWxhijCEsAqWmKHo7n89Ng+QoOefjKDjnfd8rpZyzWZqdz+c0TSGE1jrGaHgJszTL8zywxjDGWNsyxjDGCKG2bRFGZVUx1jrnuq6jlHHOt+MPnLddSymVUgYf3+gTloYZM7NWdb3iwnjvvJ+FEAgjgjGlhDHKOffeL60hBCFEGaOUWmuXhjVUH2N5ziZQ6V762TkEYQpAjVFDcI0a1raL2BjUNGvnqqqqtNaznwM56h7WLALkLSFRQvZ7HseyKJYBoRPi/7d9758A7f6pLhfSINF1omVNVSHK2q7Pk7TO8/3NzeHrH1u2c7Nzzm3BWisYMS4Pz/CwGyoocFNGl/jwUoaXGkT58RDe/5kfnj+ySuC9J0wV5ZicYXRI8jO0lNRRGR2yHFBCVUVtQ7X3HjGdVVNDVYmmtJSDsIt4GC3ubDfOvfT9arNRe8o96W3HLWnNINySJixmehCW9QZRPSkXSCnHkVujtJJayUkKwYWapDOT97O1RvCeD4vbrNXOaWv07Mw8G6NVMAxDHINsJQFJDECx2riuaudmKWWW5a+vrwihYRgIpYTQzXOUsaBpGoyQtrbteymnoa4FIcYYY62UUim1hlZrrVaGYej7fpsGx+Px+P07y/PX6+v44Rs6n+rTqdjtaFFIpcQK51z8ACFUluUWBzmE4X7fnE5Pj383CW3yxU/OGBaGcvvPy9P+h1v56W1YVW/7PY2iUxS1ZMTlwDuprVdDz8Jw/dK/iLXW20EWMYjjNMvw5fL1y5f0kIBDkb7C+PGMXl6UEO7Hbht2bQQhhFK6iOu6rqqqrBuYgPDx8e3hW/jwV7R/ztMUVhUsCviOcgVC+C9WE0ypdqIJrAAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;tdbq gcs td driver&quot;
        title=&quot;tdbq gcs td driver&quot;
        src=&quot;/static/146e377e0002220a22ce17b9b5890ae0/c42ee/tdbq-gcs-td-driver.png&quot;
        srcset=&quot;/static/146e377e0002220a22ce17b9b5890ae0/c42ee/tdbq-gcs-td-driver.png 486w&quot;
        sizes=&quot;(max-width: 486px) 100vw, 486px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;h3 id=&quot;DataFlow-job&quot; style=&quot;position:relative;&quot;&gt;DataFlow job&lt;a href=&quot;#DataFlow-job&quot; aria-label=&quot;DataFlow job permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Create a new Dataflow job with the following configuration options.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 669px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/b3740c6ad3fbdf015dd1e015cc4a912c/66b6b/tdbq-dataflow-jdbc-to-bigquery.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 49%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAKCAIAAAA7N+mxAAAACXBIWXMAAAsSAAALEgHS3X78AAABNklEQVQoz42SUW+DIBSF+f//zrVJ0760glTRe0GggKDoonZLtyzbvhxO7gNcyOESIcTpdKqqSmttjOl7HcKQcx7H6S+NBACu11tZUkrZ/X6nlALgMMTxV1JKMUaSXgghTNMUQvDeL/+AMMaKojifz4fDoW1bpXopVc7TNK3rqz9JKdV1o1RPpFSc87quGauEEJzzivOHc96HXSEMu4fwrK21ZUkRkTjntNZSSgBARKWUlBIBADqArus68UG7AQBNI4ZhWJ+ttaaU8o2yLBljdd0w3jLesWrdurcTQqxNETeHGLfDzjlE3C9/PB7ee+dDb1JvkvPjvJHz/Mler3HNM+GcH4/Hy+Xy9lYYY5ZlyTkvc543bVHll9jW/48x3W5l23bEe2+tNcZovY7Ht0n4kRgjIlpr3wGRwDqXzD30hwAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;tdbq dataflow jdbc to bigquery&quot;
        title=&quot;tdbq dataflow jdbc to bigquery&quot;
        src=&quot;/static/b3740c6ad3fbdf015dd1e015cc4a912c/66b6b/tdbq-dataflow-jdbc-to-bigquery.png&quot;
        srcset=&quot;/static/b3740c6ad3fbdf015dd1e015cc4a912c/0eb09/tdbq-dataflow-jdbc-to-bigquery.png 500w,
/static/b3740c6ad3fbdf015dd1e015cc4a912c/66b6b/tdbq-dataflow-jdbc-to-bigquery.png 669w&quot;
        sizes=&quot;(max-width: 669px) 100vw, 669px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;We will be using the Dataflow template of JDBC to BigQuery.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 697px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/0ff50dc8e1c2429f31140f3e25dad4da/11663/tdbq-required-params.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 106.80000000000001%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAVCAIAAADJt1n/AAAACXBIWXMAAAsSAAALEgHS3X78AAACaElEQVQ4y4WU25KjIBCG8/6vtlV7M1XGYzRBxIjACEE5BXe1J5nM7G7td0GV2k3//HR7mKbp7e2tLMuiKLIsS5IEY7z+D611COHgnEvTtCgKWPM8r6oq3cnzLMsy+FRVVZIkWZadTqfj8VhVFSHkYK3t+54Qcr1e+weEkLZtCSEY45ExQrY3fd9fh2GaJs65UirGeIgxwjOszjljjLXWGLMsy2NddkwI4VX84X6/I4QIId2OECKEEGNc1zX+jfsOxBysc2VZ5XmRJAkcGyG0LAsUt9Z6751z/ivW2g/ZSqllWeZ5Xh4opYQQ7+/vUsr7P9hkhxAulwvGuG1bhBCl1BjjnPum86kW8N5vlZ1zCKG+7zHGsAtCaBgG5xwodzvWWufcM9nuu2+yGWOUUliFEPM8a63lzjRNSqnb7aaU0lqDlk+3vfen5tR1Xdu2TdNIKZ9S//QZBBtjvPdbstLy5/EHuqDL5VzXddu2jLFxHDHGrxtB5rqunPM0TSmle+UQGN+i2Q7nvO/7cRy11n/eEFwbsCUba+u6hqkoiqJpmizL8jyXUq7r+mryM38zDO45hEApHYbher0OO9Dk4w6lFDybpmme5xjj0/CtcoxRCDGOI3/AGBuGgVIKL6FUCOFp4afby7LkeY4xLsuyaZq6rpum4ZxDPkzPaz9LKc/nsxDi48yEECklIYRS2nUdYwy61Rjzrb1ijFpriN+S13UdxxEOyTmH3r7dblJK+F18A7oaxB+01kmSEELSNK3rGgxjjEEEnPN1GDjnSZI87tl7xpiUknMhhHh6Nk1felPKzXAppbEWBvZ38i8n9ajX7cvqmwAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;tdbq required params&quot;
        title=&quot;tdbq required params&quot;
        src=&quot;/static/0ff50dc8e1c2429f31140f3e25dad4da/11663/tdbq-required-params.png&quot;
        srcset=&quot;/static/0ff50dc8e1c2429f31140f3e25dad4da/0eb09/tdbq-required-params.png 500w,
/static/0ff50dc8e1c2429f31140f3e25dad4da/11663/tdbq-required-params.png 697w&quot;
        sizes=&quot;(max-width: 697px) 100vw, 697px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;ini&quot;&gt;&lt;pre class=&quot;language-ini&quot;&gt;&lt;code class=&quot;language-ini&quot;&gt;&lt;span class=&quot;token key attr-name&quot;&gt;JDBCConnectionURL&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token value attr-value&quot;&gt;jdbc:teradata://dev-gcp-copy-&amp;lt;id&gt;.env.clearscape.teradata.com/demo_user&lt;/span&gt;
&lt;span class=&quot;token key attr-name&quot;&gt;JDBCDriverClassName&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token value attr-value&quot;&gt;com.teradata.jdbc.TeraDriver&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;For the optional parameters we will need to add the username and password for the Teradata database.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;This blog post does not consider how you might store the password as a secret or in a key vault of some sort. Or at a minimal, base64 encode it so it is not visible in plain text.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 684px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/730eb1843fe2742e9e109e2c54824181/544f7/tdbq-dataflow-optional.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 56.400000000000006%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAALCAIAAADwazoUAAAACXBIWXMAAAsSAAALEgHS3X78AAABMElEQVQoz3WR25KDIAyGef/3q9OqN50WFdDIRE7KwU5hdLrb3e8ikwmQ5Ocn8zxXVdVmmqa5XC6c833fU0r7/1hrt20j67pWVVVn+r5/PB5d1ymlUkrxL0LGex9CIM45Sunz+WSMGWOUUlprpZQxRmv9Hbdt896/YwgkhCClnOdZSgkHjDEAEEIAAOdcZDjn4zjGGM/l348ppeM4ljPOuda69D6HnEkI4Vg+ppSIMaau67Zt+75flgUArLVF26dU/5PSjnjvpZRCiOmgzGeMlYVLPmcQsWj03r8np5TkASICACJ+/1MIoej8tJBYa2/XW9s219u1ruv7/b6ua7n0yS/D8trZqu6AUgoASilEXA4Q8awopUrRORdjJDHG0xVxMAzDKXgYhmmabMY5V5Ji2AsFsHNqF4DhRwAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;tdbq dataflow optional&quot;
        title=&quot;tdbq dataflow optional&quot;
        src=&quot;/static/730eb1843fe2742e9e109e2c54824181/544f7/tdbq-dataflow-optional.png&quot;
        srcset=&quot;/static/730eb1843fe2742e9e109e2c54824181/0eb09/tdbq-dataflow-optional.png 500w,
/static/730eb1843fe2742e9e109e2c54824181/544f7/tdbq-dataflow-optional.png 684w&quot;
        sizes=&quot;(max-width: 684px) 100vw, 684px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 1454px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/4f4af55bcdbc046c4610187469b088ae/2a660/tdbq-dataflow-jobs.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 27%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAFCAIAAADKYVtkAAAACXBIWXMAAAsSAAALEgHS3X78AAAAwElEQVQY03WOy26EQAwE+f+fjLJKhCDAGNvj8WuYLLu5pg+tupS6p33fVPVE4apV3D2vf9Is1dL81ZZNc/p8PI5CiAgAtVZ3IyJmRsTWGhGJCCKKSGti1iK8idTKETGtGx67RXR3N7Pee2aqKgC8wd1778w8z3MpMMZQ1eMo13VN68f3NpObq2kpEBEeUaXCefNbzkxmXpblhHOMYaZQyhhjgq+1/Eh4ujvRfSYiWlPiP85XRGTfdyK6ZXdEfC7/Al9EISlqEK1lAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;tdbq dataflow jobs&quot;
        title=&quot;tdbq dataflow jobs&quot;
        src=&quot;/static/4f4af55bcdbc046c4610187469b088ae/2a660/tdbq-dataflow-jobs.png&quot;
        srcset=&quot;/static/4f4af55bcdbc046c4610187469b088ae/0eb09/tdbq-dataflow-jobs.png 500w,
/static/4f4af55bcdbc046c4610187469b088ae/1263b/tdbq-dataflow-jobs.png 1000w,
/static/4f4af55bcdbc046c4610187469b088ae/2a660/tdbq-dataflow-jobs.png 1454w&quot;
        sizes=&quot;(max-width: 1454px) 100vw, 1454px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Note: The two erroneous jobs were before the username and password were added to the optional parameters. The error message can be seen below.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;log&quot;&gt;&lt;pre class=&quot;language-log&quot;&gt;&lt;code class=&quot;language-log&quot;&gt;&lt;span class=&quot;token property&quot;&gt;java.sql.SQLException:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;Teradata JDBC Driver&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;TeraJDBC &lt;span class=&quot;token ip-address constant&quot;&gt;17.20.00.15&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;Error &lt;span class=&quot;token number&quot;&gt;1032&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;SQLState HY000&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; Single Sign&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;On NOT supported for Mechanism TD2&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 303px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/1f5f5213e00f8d7a4bbcfa7993ae9997/771d6/tdbq-dataflow-almost-success.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 103.96039603960396%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAVCAIAAADJt1n/AAAACXBIWXMAAAsSAAALEgHS3X78AAACYElEQVQ4y6VTXXOiMBTt//8d+9Snfe1uu/VbwWrVpVQEJKDEEJEAihgIyQ6yq7a104c9w2RuJvfMudx77o34D9wIIfZ0Xxs/1iaPTaXRVlt9Q+4bsqT1ZF2StK406/WNvqxL3Wmn8bveUdtNpbFcL0sy54UQ4rb+7XvnVghhL8Gv2n1X7jbbrU6vM1HGg+fBj/u7Wv1R1aZcCIRXYAFYwY5kwYUQXrBah7isIklMw9B13UPYATaE0HWhruuWBcIwKhP2SRzH57L5EdW9CnbbrYdxFEW+70PoEkJOCRWKojiTGWOXfFawXbJDCK3QKt5uszy/fOWcvyFXF855nmU0yxhjlFIIoWka+zTlnGdZRinN8/y6cvXDuqEDACzLGo1HiqJACI2yBbOXF3U4HLquWzGr/LPyqZ7qZIwlSdmbKj6d/B/+kj9DlmVpmn5hkrISXrz7uOCUHvZpIgS/8vqlcp6zjLIvlLM8BXgI1k8Gkk2vvwie7c3Q3gxMTzaRVMb+AKwHFu6bnjRfP82xRHbeWVme1Sagu6M+JvZY7c3mEwdppqOo+nBmjQ1HcZDmETtOPRQAF+sp3Z3t6a6XKFgJIVjOOs12/aH1+jIdjZR2q/fz7qHdkhq1luOUy0CCCEGvmtF7e55sFJASISEY42qqn9rz5LAqLoqCEAIh3Pg+xh6lWdnziyG/Ib9TPrY6h7DcpzQ9fOzzFWXseZZlua4LbMc0TW2qzefzxWJh22BuWVNVVV9fk2RftuajPQ+HQxzHaXqojIlWKAiCON5GYRhG0WazIYTkxw07K19d6c9wmfkH5w2ppZAWcAoAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;tdbq dataflow almost success&quot;
        title=&quot;tdbq dataflow almost success&quot;
        src=&quot;/static/1f5f5213e00f8d7a4bbcfa7993ae9997/771d6/tdbq-dataflow-almost-success.png&quot;
        srcset=&quot;/static/1f5f5213e00f8d7a4bbcfa7993ae9997/771d6/tdbq-dataflow-almost-success.png 303w&quot;
        sizes=&quot;(max-width: 303px) 100vw, 303px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 310px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/b03b747d76b29e8235db218693e50715/56e1b/tdbq-dataflow-success.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 104.19354838709678%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAVCAIAAADJt1n/AAAACXBIWXMAAAsSAAALEgHS3X78AAACQklEQVQ4y6VU23aiMBT1/9/nbf5jXme1KnewyE0gINCiVtGCGFNCOLOQXpDp2IfZi7VCss/OOTnZMIL/wAgAXsqXiTnmrOnYuOcdTkGy5ImSJypIEha87EuKL0meMDHHE3PMO1PNVxljrbhpGgD4+evH2PgNAAvfnvJTwzIkWdQetDAOOYGTVIkT+fyYk4oES1SURacaAbRD/BzuiwwADof9TNNc142j2Jgb6/XasR3TtBAKKaUAkO0ySumHGLoaAKC5AAC2222WZUVRbLfbNE3x+dyxXRhj7ErMGvbBtRuwpijydLVK07SqKsbqjya15EDcEZRSQgjGmFJaFHmSPEZRVFFaVdX5fMYYd5FX4rquu9V9li2XyzhJgiBQFQUhlKYr13U9zzcMY67r+Ey6+E9xc4238hjL85wQ0r/YQczoBocxrut60MuhSVoOmsEDACd8ojX9FzvMPEA/8y17Co4gLUTOmooLQQs0LVBVpAgOryJFRUo35W2OtznJFfXg4Wt7xk/R3eROUWXHs2fz2f3kTtLk2Xxm2MYRlydy8gIvP+Zv3e4OEKz9Xf4MAMdjoT/oC8sJw9BHSOB5WVZMY27bDqXtEbLslj3bl90F+xbZKn0qT7jf8L/syeq+PRlj+cthvd5sNhtCCKV1P8HX9sQn/Pr6WpYlpfRYFHEU+wi1G9f1xaGkqqp+CRfx+33EUbRcRkmLR8u057oeJYmPfNd1LctWFTWOk/dK2ac9u/mVPZuGtt8T6xurH/mNPb/9h/0BpuyqD+ySWekAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;tdbq dataflow success&quot;
        title=&quot;tdbq dataflow success&quot;
        src=&quot;/static/b03b747d76b29e8235db218693e50715/56e1b/tdbq-dataflow-success.png&quot;
        srcset=&quot;/static/b03b747d76b29e8235db218693e50715/56e1b/tdbq-dataflow-success.png 310w&quot;
        sizes=&quot;(max-width: 310px) 100vw, 310px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;h3 id=&quot;Testing-the-results&quot; style=&quot;position:relative;&quot;&gt;Testing the results&lt;a href=&quot;#Testing-the-results&quot; aria-label=&quot;Testing the results permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;After running the DataFlow job twice, and querying the target table we can see that the data has successfully been moved from the Teradata Database to the BigQuery Dataset.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 415px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/607b069a3fc3c8fc11c9d5bafe3e32b2/80028/tdbq-dataflow-bq-results.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 128.91566265060243%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAaCAIAAAA44esqAAAACXBIWXMAAAsSAAALEgHS3X78AAACwklEQVQ4y42UyW4UMRRF61/Z8QWs2LBjy68gWJIgoSgpAZEg3UnoqdI12F1dnuehjKqLhA5JJ9yl7eOnd9+1s7quAQBt285mM631FjsmPKKuRZYwZ41BCHmnT8/BdNZyRhDCd8rKndq2vb6+UtotK1tCO12In79EBQzjsqxAH82Xr83k10ZJgjEm5BaGEDLGOGcY4+KmuSm3YEMpt5Sbz3nz4ah6/6n+eHSjtKRMbZHARDKu9E7Z8fHxarmEG5DneX52cnLypQFN30djzJt3+au3py9ff37x6qiCJP9Bvl2QywWroFTKOucy7/3uFoOpRdQp5WMMzvsQfFHiiyt4cb25mrfa2BC8D8N68H+UOeeE4EppQjWlxtiQUj8qPacB5pxJqbnwSseHJ/q+j7E/CEsppFRdxy+vlxh3EG4wxl2HNhA2dW2MOVjZe991HefcOYMxopQQQqSUnHNKKeo6/QTsnFNSGmP6+HyTj8CjdfGw+gMaYGutMcbel/sPZTHGEMJ9f0eHn1cWvJtMJnmezxfz6eX0/Pz87Oy0qutxSM/0HGOUUlJKhRBKa865lNJam26zsj/w/ZWh577vx5SLnUZYCLHrZThKuRPSSeVDiH0/mKeUopSGELKUEgBgsVisVquiKObzeVmWy+UyDCFOlIfvU3Gx0JO5QsyPRauqms1mg2Fd1zHOU0oxRs75aHtKKQRPudl0Gm7ZYgVgy1dltyyaGEOMQ4qHhwGaBgAohKCUQggRQoSQ3fxc8E5JzhmFoFFSYLRdr4sY490sszBcFAgh3vt9Y1JKSmnvPaV0VRT7Fv4NiTHGaM0YG0O2b6m1NsYohGiaxjk3tnYPHuM1/HLe72/cwYyx9Xo9wv9WfgKWUg7PRqmiKPitqY/ABOOHsFJq/KTqupZSPlU5hPCwsvdeCFEUhRDikcr9Ye07/+iT/A2V/Nf2xeJTjQAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;tdbq dataflow bq results&quot;
        title=&quot;tdbq dataflow bq results&quot;
        src=&quot;/static/607b069a3fc3c8fc11c9d5bafe3e32b2/80028/tdbq-dataflow-bq-results.png&quot;
        srcset=&quot;/static/607b069a3fc3c8fc11c9d5bafe3e32b2/80028/tdbq-dataflow-bq-results.png 415w&quot;
        sizes=&quot;(max-width: 415px) 100vw, 415px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Connecting Nifi to Teradata]]></title><description><![CDATA[Connecting Nifi to Teradata For this demo I will be using a Windows PC including: Docker desktop PowerShell Core 7.3.6 VMWare Workstation…]]></description><link>https://mortimer.xyz/nifi-teradata/</link><guid isPermaLink="false">https://mortimer.xyz/nifi-teradata/</guid><pubDate>Wed, 13 Sep 2023 00:00:00 GMT</pubDate><content:encoded>&lt;h1 id=&quot;Connecting-Nifi-to-Teradata&quot; style=&quot;position:relative;&quot;&gt;Connecting Nifi to Teradata&lt;a href=&quot;#Connecting-Nifi-to-Teradata&quot; aria-label=&quot;Connecting Nifi to Teradata permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h1&gt;
&lt;p&gt;For this demo I will be using a Windows PC including:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Docker desktop&lt;/li&gt;
&lt;li&gt;PowerShell Core 7.3.6&lt;/li&gt;
&lt;li&gt;VMWare Workstation Player 16&lt;/li&gt;
&lt;li&gt;Teradata Express VM&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;Installing-Nifi&quot; style=&quot;position:relative;&quot;&gt;Installing Nifi&lt;a href=&quot;#Installing-Nifi&quot; aria-label=&quot;Installing Nifi permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The easiest way I found to get an instance of Nifi running is using Docker.&lt;/p&gt;
&lt;p&gt;To get Docker working on Windows there is a little bit of extra setup required. Firstly install Docker desktop using the following installation.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://docs.docker.com/desktop/install/windows-install/&quot;&gt;https://docs.docker.com/desktop/install/windows-install/&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Also, getting WSL2 setup is required for better performance.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 1179px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/4d016d7cbf483ec977f36b4c2eee790f/c4688/nifitd-docker-desktop-wsl.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 10.6%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAACCAYAAABYBvyLAAAACXBIWXMAACToAAAk6AGCYwUcAAAAfklEQVQI1z2KUQ6CMBTAuIn4IcOEESYwJgPeBDajMd7/NjXGxI+m/Wh2dm9aH/EhMobEvD3o/Q3Zn4wSCelFqTsOp4qjqskL/feP6t/fJ8vLFjME7LzT+xUnCTttuCXilsRV7hgn6M5TdxPNIOjW09iFxs4oIxSXFWUCuar5AE/4QHTkz++iAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;nifitd docker desktop wsl&quot;
        title=&quot;nifitd docker desktop wsl&quot;
        src=&quot;/static/4d016d7cbf483ec977f36b4c2eee790f/c4688/nifitd-docker-desktop-wsl.png&quot;
        srcset=&quot;/static/4d016d7cbf483ec977f36b4c2eee790f/0eb09/nifitd-docker-desktop-wsl.png 500w,
/static/4d016d7cbf483ec977f36b4c2eee790f/1263b/nifitd-docker-desktop-wsl.png 1000w,
/static/4d016d7cbf483ec977f36b4c2eee790f/c4688/nifitd-docker-desktop-wsl.png 1179w&quot;
        sizes=&quot;(max-width: 1179px) 100vw, 1179px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://hub.docker.com/r/apache/nifi&quot;&gt;https://hub.docker.com/r/apache/nifi&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;In your PowerShell terminal, pull the image.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;powershell&quot;&gt;&lt;pre class=&quot;language-powershell&quot;&gt;&lt;code class=&quot;language-powershell&quot;&gt;docker pull apache/nifi&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;To run the Docker image (as a container), use the follow command, or run using the Docker desktop application.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 2000px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/bfb317e0f4143bd041285d1fcbcfb271/0734c/nifitd-docker-container.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 12.6%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAADCAYAAACTWi8uAAAACXBIWXMAACToAAAk6AGCYwUcAAAAqElEQVQI1yWO7ZLCIAxF+x6uurWlX9BYoCDt1tX3f6njgD8yuXOTk9zq3ExM98gogax/uxkbD5S2/NxG+nlliUeZGbehbeJUf/11f5f97C3h4NJqqkZ7XPrHPZ60k6UZF3qJtDpQjx6lPcYmrsowSEDWnc54bsMdpV05mLnOfHXVZsBthP2NT6+iG5NQ8oeSg26OiN+4qplBVmx8lkQZzk9yzyHqXkp9AAZXW1CjDFALAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;nifitd docker container&quot;
        title=&quot;nifitd docker container&quot;
        src=&quot;/static/bfb317e0f4143bd041285d1fcbcfb271/f97d7/nifitd-docker-container.png&quot;
        srcset=&quot;/static/bfb317e0f4143bd041285d1fcbcfb271/0eb09/nifitd-docker-container.png 500w,
/static/bfb317e0f4143bd041285d1fcbcfb271/1263b/nifitd-docker-container.png 1000w,
/static/bfb317e0f4143bd041285d1fcbcfb271/f97d7/nifitd-docker-container.png 2000w,
/static/bfb317e0f4143bd041285d1fcbcfb271/0734c/nifitd-docker-container.png 2183w&quot;
        sizes=&quot;(max-width: 2000px) 100vw, 2000px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;powershell&quot;&gt;&lt;pre class=&quot;language-powershell&quot;&gt;&lt;code class=&quot;language-powershell&quot;&gt;docker run &lt;span class=&quot;token operator&quot;&gt;--&lt;/span&gt;name nifi &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;p 8443:8443 &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;d apache/nifi:latest&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Running the container will generate a username and password.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;powershell&quot;&gt;&lt;pre class=&quot;language-powershell&quot;&gt;&lt;code class=&quot;language-powershell&quot;&gt;docker logs nifi &lt;span class=&quot;token punctuation&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;Select-String&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Pattern &lt;span class=&quot;token string&quot;&gt;&quot;Generated&quot;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;log&quot;&gt;&lt;pre class=&quot;language-log&quot;&gt;&lt;code class=&quot;language-log&quot;&gt;Generated Username &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;username&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
Generated Password &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;password&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Browse to the service in your browser of choice and login with the Generated credentials.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://localhost:8443/nifi/&quot;&gt;https://localhost:8443/nifi/&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 1330px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/3feef84cb94d55f7f546c5d09757e6fe/9bae8/nifitd-login.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 64.8%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAANCAYAAACpUE5eAAAACXBIWXMAACToAAAk6AGCYwUcAAAB9UlEQVQ4y62S32/SUBTH+y8tvpFtOMZgzokaNWpijK/+AT76qi8atCtQYICL7kF9wieNPohPzpjMxB/RJRKIUzcChba09/a25WvugT5odGLiST45be73fHvOuVXSpy4gcfwckifO4+jZS0ifvoj5VAbxhWUcXjyC+cQyYvEUFpMriKczSGXOILFyEnNLq5hdWsVc6hgOzSYxE1vATCwBJVuoQC3dgVbeQL5yF4XaPWjFKtRcEbe1ItR8icjpZWh6BflyDblSFWv6OlR9HWt6BTfVAm6oeWQ1HYrLBd7tNMGFj/8RSmv3G65cv4X60+cYjUbwPIEgCOAJAcY4hPDp/XfIMwnjHhjn8IMAyuu3H3D56jVUH9TJMIrx84jyr8gIw5CMuCd+7tAYWHj0rIGdZhvC97HfNWAMzL+ORtMIH74fkHnXMKheEb5sPyQR5xx7+x36qiwIp4B0YQiXMQRhCGU8v0cHjuvi6/c9uJyTwGGM8kFEGlkrsxK1L5dr2UP0DAOWbVO308AmWZrRyNGiI8Nmq43Wl12Y9hBDh8F23AORmqE71tEt9/omJNGv0O2bk4uxyHRg/RnTdiYamzJ12On1IZFm/xqPX7yEuvEQm/UnyNbu41OzDcVx5UI53VS0z2mQ8fFzG41X29h68x6NrW10egZ+AJawrYZMy+ZnAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;nifitd login&quot;
        title=&quot;nifitd login&quot;
        src=&quot;/static/3feef84cb94d55f7f546c5d09757e6fe/9bae8/nifitd-login.png&quot;
        srcset=&quot;/static/3feef84cb94d55f7f546c5d09757e6fe/0eb09/nifitd-login.png 500w,
/static/3feef84cb94d55f7f546c5d09757e6fe/1263b/nifitd-login.png 1000w,
/static/3feef84cb94d55f7f546c5d09757e6fe/9bae8/nifitd-login.png 1330w&quot;
        sizes=&quot;(max-width: 1330px) 100vw, 1330px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;h2 id=&quot;Setting-up-Teradata-VM&quot; style=&quot;position:relative;&quot;&gt;Setting up Teradata VM&lt;a href=&quot;#Setting-up-Teradata-VM&quot; aria-label=&quot;Setting up Teradata VM permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;This is something for another time, and if you are reading this you probably have a large enterprise Teradata instance you want to connect to.&lt;/p&gt;
&lt;p&gt;For this demo, the important thing is the IP address of the VM is &lt;em&gt;192.168.190.128&lt;/em&gt;, you will probably have a hostname for your Teradata instance.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 1497px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/e46fee85bc704c3129e53ad29e981d93/7a0e8/nifitd-td-vmware.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 75%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAPCAIAAABr+ngCAAAACXBIWXMAAAsSAAALEgHS3X78AAAC6klEQVQoz2WP20/TUBzHz7ayldl1TdduvZ3Ty9qdyi4yYLAhbKylLCCiiQlC1PhHiC8kGOWRaARBgyYIAR8MRuKVRB+MjxqNJgQvwft/YsYIkfjJ9+GXk/PN7/cBo8Ntxwa9muvUPNfpq7jVPq/fHeh3PddxnarnOk61/tjvVF2nWin3dLTlC+1txc7C0e4S8AaPV2rDQ9Wy5w3UTpwqe0Pdff2lPrdYcYtlp7vilspuqeJ276ZUdhop9lZLZQdcuHZr4uqdydmVSwtrk9eXJmZuX5xZnJhZnJxbubywOn1zdXph9dKNlam55Xpml6dm705dr+fK/Aoo9BQ5NoxNvasjX8jnzp8ZOzc+ev7seGd7aws2bcvItmBs6lKCg1JCSnCywCtCPEqFkRwHCaQDAGIcl8nmWlvzx0dGTo+Nj5w4mc0dMYykaVpWCqcwViAim5sDASJANBEEAQCgqDAQNQMAwLJsNpvVNA0hpKpqPB5HCGGMFUVRVVXaBSEUDAYBAD6fDwAQpSkg6UkAAMMwpmnKsmxZlm3bEEJVVWmaZhiGZVme5yORSGNurN0r64fT9Ska1XVdURRhF13XOY6jKCoYDPr9foIg/LsQBNFYu1dWDAsAwPN8Pp/P5XKmaWYyGdu20+m0pmksy5Ik2WgGAgHwD42zzYZzJpMxDANjnEqlEEKyLGOMIYSapgmCIIqipmkkSR5wFrW6c5RhkmZSgXVnyzQRQjzP0zTdEBYEgabpWCwWCoUOlFMFm0MhaCbkJMcpEZojfX7f/g+/3+/z+fbP3hfeK5ecXqjyelJOWhDbuqTwkUPhaIQKBpvCZCjURETpSLiZBP9BhZtBfqCno9Zmd2WkFqjmDJg2BN3gFY1JyDERRXmRE2FCVvmEyIsyJ8OYpMQkyIqyYtlg+c3W+qef995t3//w+cHW943tX4+//lr/uDP/5M3C07ezD19PL22sPXr28tXrF++3n3z5sfntz+ZOPc93fv8FOKyl/NkPbHYAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;nifitd td vmware&quot;
        title=&quot;nifitd td vmware&quot;
        src=&quot;/static/e46fee85bc704c3129e53ad29e981d93/7a0e8/nifitd-td-vmware.png&quot;
        srcset=&quot;/static/e46fee85bc704c3129e53ad29e981d93/0eb09/nifitd-td-vmware.png 500w,
/static/e46fee85bc704c3129e53ad29e981d93/1263b/nifitd-td-vmware.png 1000w,
/static/e46fee85bc704c3129e53ad29e981d93/7a0e8/nifitd-td-vmware.png 1497w&quot;
        sizes=&quot;(max-width: 1497px) 100vw, 1497px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;h2 id=&quot;Adding-the-Teradata-JDBC-driver-to-the-Docker-container&quot; style=&quot;position:relative;&quot;&gt;Adding the Teradata JDBC driver to the Docker container&lt;a href=&quot;#Adding-the-Teradata-JDBC-driver-to-the-Docker-container&quot; aria-label=&quot;Adding the Teradata JDBC driver to the Docker container permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;We need to copy the Teradata JDBC driver to the Docker container and then restart it. Firstly download the JDBC driver and change to the directory in PowerShell.&lt;/p&gt;
&lt;p&gt;You’ll need to sign up/login to download the driver and then extract it from the Zip archive.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://downloads.teradata.com/download/connectivity/jdbc-driver&quot;&gt;https://downloads.teradata.com/download/connectivity/jdbc-driver&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 1482px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/f059a8452c9d0576eb9950f77914df29/4585a/nifitd-teradata-jdbc-driver-download.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 6.6000000000000005%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAABCAYAAADeko4lAAAACXBIWXMAACToAAAk6AGCYwUcAAAAQ0lEQVQI1x2LOQ7AMAgE8/9HhkMY2W5ogHajUKxGmtE++2wQvWBmEBHMDO4OVcVaBhaBiEw/96K7kZmzqhr+LiLm9wGoXkqJA+oOfwAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;nifitd teradata jdbc driver download&quot;
        title=&quot;nifitd teradata jdbc driver download&quot;
        src=&quot;/static/f059a8452c9d0576eb9950f77914df29/4585a/nifitd-teradata-jdbc-driver-download.png&quot;
        srcset=&quot;/static/f059a8452c9d0576eb9950f77914df29/0eb09/nifitd-teradata-jdbc-driver-download.png 500w,
/static/f059a8452c9d0576eb9950f77914df29/1263b/nifitd-teradata-jdbc-driver-download.png 1000w,
/static/f059a8452c9d0576eb9950f77914df29/4585a/nifitd-teradata-jdbc-driver-download.png 1482w&quot;
        sizes=&quot;(max-width: 1482px) 100vw, 1482px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 985px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/50f91568d36197555ba2c31a049e38e9/2d7cc/nifitd-teradata-jdbc-driver-jar.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 29.599999999999998%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAGCAYAAADDl76dAAAACXBIWXMAACToAAAk6AGCYwUcAAABFUlEQVQY022MW2+CQBCF+f//qi+mTzVqtS1pIwICArouewEW9Wt2bZrUdJIz55IzEw0yZrCSum5omhpjDFVVsd/vKYoi+KZpSJKEtm2p65qyLAO8PxwOQXsWQhC5QTCOPVobtNY457DWhkfej+MYvFIqeJ973XXqt6O0Dtz3PZEQkk5bRGd4HNOPCKn5b6SyHEXH6awClOlDHvk1TY7BaqyWgblO96uLww2Wqxu4jD23i/t5d3uAPxmZBktUCcM2r5h/ljwtvpitUzaFIlXwURlWmWCxO7JMBetcshUXEskf+GyV3ntR1ires5b5tmX2VvAcH3jZnYkFrKuBZd6xyCSvhWGZ65DFJ/g43gK83tSORdaFzjchasjHOEW6sAAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;nifitd teradata jdbc driver jar&quot;
        title=&quot;nifitd teradata jdbc driver jar&quot;
        src=&quot;/static/50f91568d36197555ba2c31a049e38e9/2d7cc/nifitd-teradata-jdbc-driver-jar.png&quot;
        srcset=&quot;/static/50f91568d36197555ba2c31a049e38e9/0eb09/nifitd-teradata-jdbc-driver-jar.png 500w,
/static/50f91568d36197555ba2c31a049e38e9/2d7cc/nifitd-teradata-jdbc-driver-jar.png 985w&quot;
        sizes=&quot;(max-width: 985px) 100vw, 985px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;Then list the containers running.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;powershell&quot;&gt;&lt;pre class=&quot;language-powershell&quot;&gt;&lt;code class=&quot;language-powershell&quot;&gt;docker &lt;span class=&quot;token function&quot;&gt;ps&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Get the &lt;strong&gt;CONTAINER ID&lt;/strong&gt; from the output.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;log&quot;&gt;&lt;pre class=&quot;language-log&quot;&gt;&lt;code class=&quot;language-log&quot;&gt;CONTAINER ID IMAGE              PORTS                  NAMES
&lt;span class=&quot;token number&quot;&gt;81b55e074d9a&lt;/span&gt; apache&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;nifi&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;latest &lt;span class=&quot;token ip-address constant&quot;&gt;0.0.0.0&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;8443&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;8443&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;tcp nifi&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Replace the &lt;strong&gt;CONTAINER ID&lt;/strong&gt; in the following command to copy the driver from your local Windows host to the Docker container.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;powershell&quot;&gt;&lt;pre class=&quot;language-powershell&quot;&gt;&lt;code class=&quot;language-powershell&quot;&gt;docker &lt;span class=&quot;token function&quot;&gt;cp&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;\terajdbc4&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;jar 81b55e074d9a:&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;opt/nifi/nifi-current/lib&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2 id=&quot;Setting-up-the-FlowFile&quot; style=&quot;position:relative;&quot;&gt;Setting up the FlowFile&lt;a href=&quot;#Setting-up-the-FlowFile&quot; aria-label=&quot;Setting up the FlowFile permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Drag a new Processor onto the workbench.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://localhost:8443/nifi/&quot;&gt;https://localhost:8443/nifi/&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 523px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/d3a01bc499ddf00b4018f708d3f386cd/c53f8/nifitd-drag-new.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 58.20000000000001%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAMCAIAAADtbgqsAAAACXBIWXMAAAsSAAALEgHS3X78AAACSUlEQVQoz3WO60taARjGz/+2m1mp7JhmR1JTxFwMxtbnYJ822JcgGFJeak0TL4mXU+al4w01TWXUmrPSyml6tFOp56pzuMh9cS8PLw/v8/zgBXiQYnJaxoXkAplaIJ0fF0qfsDgsNpc9zhtjc5+PcZ+yuDwOyJoAJ8AZjkA8AYpYPMGzSf4LzhQgUizwJSqhfEGifjs7/wZSvhZLlUJINiWSCCEZOC3hCWZnxHPgzJxIphLL1QKJEoTkfLHiJTQHrNs8nv2YKxBdNTnsu0gqdxxJZ+PZo68Or97qNrl8RqdXa3FZPAGD1bWsNxqd3nWbZ2XD/GV7B/io2Qglc/uJzNKyBkYSSCK16XAeZr7pbJ5Pa5tbbr/O6v6gMVh3EYMdXlr+bIaDWovr/cqqwQ4DMBJHklkkmfUEY7HM0cn5Rfb4R+4gs7cTgENxfyy9FzmAQ/FgIrMbTm77Qr5oaieccPhD3kgSKJR+5X+eXpar2H1HozUolarFd4sq9Suzfj0biZ7kC7UGVq7W82el8+JlvYFdVeqnpati8aJSbwLVWqNab1zXG03sLhBE1rQ6k2lLp9WnM7ni0fdyuVJr3lyjg8J1DR1stFkdGLRSQ4G7VpuiaYpmCJLu9Xr9x/nd72OVaqfVppguSQ0KFM2QFE3+LVMM08YJoIY2CZJ6UAcnhiJp5mwfaWG3JNMdFoYiKbrVwYHGzS1BjMgKpYuAxdq+b5GDp/4Do03s8UQ/GJykut1eOJUOpg5JagT5D8ZxcmTWwYm7VnskOYT/AJUNK5ReSCEuAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;nifitd drag new&quot;
        title=&quot;nifitd drag new&quot;
        src=&quot;/static/d3a01bc499ddf00b4018f708d3f386cd/c53f8/nifitd-drag-new.png&quot;
        srcset=&quot;/static/d3a01bc499ddf00b4018f708d3f386cd/0eb09/nifitd-drag-new.png 500w,
/static/d3a01bc499ddf00b4018f708d3f386cd/c53f8/nifitd-drag-new.png 523w&quot;
        sizes=&quot;(max-width: 523px) 100vw, 523px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;We will select the ExecuteSQL type.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 973px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/bf53e11c9570e65e25091196ec1fc271/08a5e/nifitd-add-processor.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 24.8%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAFCAIAAADKYVtkAAAACXBIWXMAAAsSAAALEgHS3X78AAABFklEQVQY0z3JXUvCUADG8X3u7SLIG0HPRTfhBC+6MEkCKWwtWOrc1HBrRCZBEL5sOy97Ocxtp0jaTqAQ/C7+PI8AWp2jk7OWWJcl0JSALAJZrDcO/qMhgePbPIyyBGRh7aEtJFtI1j7a+NhDGGKCgjCIaRBTHMbHCGLqk2Dj+S7EkISQhC4iAqU0y1lRlJzznH2NprY6mmmGNZ4vTHtpLT4Mezm23sb28nHiKIOpMpzp81dt4ry8fwpbiDauj8PIReHKXWmD8/bVae+2qqg1RQU3/drdfV1RDx7Ada/a6VY63cpFW9LNS8G0HW3yZFjP/aEeRFGxTxMKswQVvynn7JtFZZFxzjhnZZmnCcp2OE9xvsP7n/QPnAbqog9O2RkAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;nifitd add processor&quot;
        title=&quot;nifitd add processor&quot;
        src=&quot;/static/bf53e11c9570e65e25091196ec1fc271/08a5e/nifitd-add-processor.png&quot;
        srcset=&quot;/static/bf53e11c9570e65e25091196ec1fc271/0eb09/nifitd-add-processor.png 500w,
/static/bf53e11c9570e65e25091196ec1fc271/08a5e/nifitd-add-processor.png 973w&quot;
        sizes=&quot;(max-width: 973px) 100vw, 973px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;Now, lets configure this new Processor.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 471px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/2ea5bcf4fb416f840677caee23eaeeca/27c0d/nifitd-processor-configure.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 40.76433121019109%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAICAIAAAB2/0i6AAAACXBIWXMAAAsSAAALEgHS3X78AAABc0lEQVQY002Q64oTQRCF5/0fxd+KoKggK+iGxVU32Zhkrn2tvkz39FySdM90y0SRLT7Or6rzQWWt7bhUoDSolklNAZiQlAMFAUpzqV4CSot184ZUGeEAlDJMzoPRvCjKKs+LX09bjHGY52VZUvw3KcZ5noMPV+9jjBQgo0K2UlolXWcPp+P+VG5/n3bHokTkWDYUxH8VE3K6XFJa21JKoFSGGVdap5RqTF+9ef/p7tvrdx/efvz89fvT5ufz/eP2L5sfuy+bR8x4mIPp3HS+3Mwgq7puENofjmWD6gaf8kJI3bnBWGed61zv+qEfRt0a3RrvAxPKWMeFzAiIar1BhHGpNOeAMRmnc4xpnuMtF+9DCPM4TrI10/nKQGnbEYD1YYxxggnC5G7zgBAhhOZ5UddNVVZFXlLKr1fvfeiH0bp+iVG1dhjPqxmUzqtq97zf7Q9Caed6Y6y1Xdc5aztjrOuHqw8+zMM4lQ0inFcI1Zg0hP4BPPm2SGPdGA8AAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;nifitd processor configure&quot;
        title=&quot;nifitd processor configure&quot;
        src=&quot;/static/2ea5bcf4fb416f840677caee23eaeeca/27c0d/nifitd-processor-configure.png&quot;
        srcset=&quot;/static/2ea5bcf4fb416f840677caee23eaeeca/27c0d/nifitd-processor-configure.png 471w&quot;
        sizes=&quot;(max-width: 471px) 100vw, 471px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;Browse to the Properties tab, and add the SQL to select from a table in your Teradata instance. In this case my demo selects from a previous loaded table from another blog post.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;sql&quot;&gt;&lt;pre class=&quot;language-sql&quot;&gt;&lt;code class=&quot;language-sql&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;select&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; PRD_ADS_PYTHON_NFL_DB&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;GAME&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Then click the Arrow to configure the &lt;strong&gt;Database Connection Pooling Service&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 989px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/2979893ec8af2c0b4b4a179347cc6576/acc8c/nifitd-processor-configure-props.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 35.4%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAHCAIAAACHqfpvAAAACXBIWXMAAAsSAAALEgHS3X78AAABZUlEQVQY012P20vCYBjG95d7JwVBBooYzcOmpnNT6qKbKL1ITdOSPGxzn4dNUrc5dR42vx0NLejw43fx8sADz4uchhJ+nDoPp3yR9NlV0hvETkJxbxDz+FCP7+qPF+g/kdd6463ZYXo82xe6/RENeKbH04CnezzTExhwuNm+QINhh/vtoM0NEGhAcaao6408X4gzZatp6nqtafpcVSfybL5UF+pqIsn6brc/4O7do0cQXYfjqbTZaNC0tZ3huK5tu46zh6a91SE0LGhYWx3aznfhi9VG0/QdUn9voNfxCJHCqAxGZWJkGqOIGJlO3mTJu1ssS+I5Mp6j8GzmkFNELJXAk1gggj6Wq4jAV/IP/lIxXHnGyqVovYZzLAG6RPUlVnoKF+4DxXyoWAjRrSTgyC5LtKpos3zZrkV5gUagYSrKUl1tfzYdX5LHkzHDSUNBFj6k4WjKgjEDRDAwdcOElmXZoih9ApMSVdcgl/ilAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;nifitd processor configure props&quot;
        title=&quot;nifitd processor configure props&quot;
        src=&quot;/static/2979893ec8af2c0b4b4a179347cc6576/acc8c/nifitd-processor-configure-props.png&quot;
        srcset=&quot;/static/2979893ec8af2c0b4b4a179347cc6576/0eb09/nifitd-processor-configure-props.png 500w,
/static/2979893ec8af2c0b4b4a179347cc6576/acc8c/nifitd-processor-configure-props.png 989w&quot;
        sizes=&quot;(max-width: 989px) 100vw, 989px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;Now, we have a list of Database connections. We need to add or configure one for our Teradata instance (in this case the VM).&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 773px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/3c8f480c6b57ed207e795be57074e99b/5b101/nifitd-db-pooling-cog.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 38.6%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAICAIAAAB2/0i6AAAACXBIWXMAAAsSAAALEgHS3X78AAABLklEQVQY052L3UvCUADF7z/dW5AkM01zfUyIGSbUezVRSoJegyh0TnHOtNzd1r439+ntbosKpEfp93D4nQMHeEEYRHEYxUEYBWEUxqtsAxBCmmkBw3Js13OWvqpbhunYSy/bjDRNQfpDkiTrFScJxniTPxhNZ+PZu2G7mmGIsgKVD1lVXc9H6HOF0G/+lXXFGIPsX+AkieMIvIwWvbHITqThVO6PRU6QWR72eTh6VVgecoLECVKfh8OpPJh8CytIA0F64uaPPQHkyW2SLlCNMtWoVE4JqlEpUbtkfY++IMu1fK1ZPTrbLxznaucHJF0sHe4QxFaRzFVpon55AhyNQV43tO/kt2tH7VhKW4OMZ95qIqOLjG/fW0pHhy1Laetyy17ciM9Nc36F/G7kPnwBVhKtN5Z03ysAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;nifitd db pooling cog&quot;
        title=&quot;nifitd db pooling cog&quot;
        src=&quot;/static/3c8f480c6b57ed207e795be57074e99b/5b101/nifitd-db-pooling-cog.png&quot;
        srcset=&quot;/static/3c8f480c6b57ed207e795be57074e99b/0eb09/nifitd-db-pooling-cog.png 500w,
/static/3c8f480c6b57ed207e795be57074e99b/5b101/nifitd-db-pooling-cog.png 773w&quot;
        sizes=&quot;(max-width: 773px) 100vw, 773px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 842px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/92513b1f43c280a9e78ceaebaaa89a0b/26003/nifitd-db-connection-props.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 81.8%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAQCAIAAACZeshMAAAACXBIWXMAAAsSAAALEgHS3X78AAACcUlEQVQoz5WS208TQRjF99/WRzS+CGoCQRNLC8SItBYTKPDCo1HutwilBdrudmcvnb3OzM7MN7NtzRaNeInGL7/My5cz58zJGE/ml18sVp+V380srMyUVp6XV6dLb6dmK0/mlx7PLT6aq0zNlh9Ov3rw9OXvGNe9fvOm1+qY7a45Oa1Wx7y6NVsdq9NH3b7zFwxQGvmDKE211pQxUEAYsxw3StLxv8bwMf5y2bztmQmhhGWEZXFK7oiSNLxHdI8wTjIujIPz5srGzmJ9q/y+sVBrlKrrperG69X1pbXt1c2dSn2rUt+s1DfLtUZpdX2hulGqbpRrjfk3a7unFwZyvI+f9w5PznYPjo5Ozx0fB2Hs+PiifXNwfPpp72D/8GT/8Pj47PyydT0IIh+HOIxt10eOazDKTNNyi/EQcrgQw9FIAARRFEaR7w+iOA6jiFKm86HOc6V1PhyyLEMIGSkhnW7PRsi0LBwEXAgJirAMeT4hNEkJKK10DkoLCUICF1KCSlJi28iglPVt5DiFczypQSmdZdwPwkKcpEKCnACgAFQhVZoQWogJIaZlWX3b83zQWoICpWnGHX+QpiROUqmUhB8ICaB0SmgRm1LWM00chMWtAHdilnHXH2AcUMYyLv4otm3bIJR2ul3X84ScRANVxObCwwFCDsYB/7ZQ92NTSl3XNeIkaTavWu22afUpy0DpO2fk+TgICKWg9C/OEhQXknNuUDYpzHWDMEwIEQCgNROFMxeCcyEAimd/pyhcyqxYcSPjfDDAQRhiHBBCtM7zfCglhHEsAXSea/0TSufj8Xg0GuVaG+P/HFBq+cN28Z1rja8c4V5QNT+O7wAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;nifitd db connection props&quot;
        title=&quot;nifitd db connection props&quot;
        src=&quot;/static/92513b1f43c280a9e78ceaebaaa89a0b/26003/nifitd-db-connection-props.png&quot;
        srcset=&quot;/static/92513b1f43c280a9e78ceaebaaa89a0b/0eb09/nifitd-db-connection-props.png 500w,
/static/92513b1f43c280a9e78ceaebaaa89a0b/26003/nifitd-db-connection-props.png 842w&quot;
        sizes=&quot;(max-width: 842px) 100vw, 842px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;ini&quot;&gt;&lt;pre class=&quot;language-ini&quot;&gt;&lt;code class=&quot;language-ini&quot;&gt;&lt;span class=&quot;token key attr-name&quot;&gt;Name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token value attr-value&quot;&gt;TeradataVM-Connection-Test&lt;/span&gt;
&lt;span class=&quot;token key attr-name&quot;&gt;Id&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token value attr-value&quot;&gt;87974be0-018a-1000-26db-a45f03ce9a88&lt;/span&gt;
&lt;span class=&quot;token key attr-name&quot;&gt;Type&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token value attr-value&quot;&gt;DBCPConnectionPool 1.23.2&lt;/span&gt;
&lt;span class=&quot;token key attr-name&quot;&gt;Bundle&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token value attr-value&quot;&gt;org.apache.nifi - nifi-dbcp-service-nar&lt;/span&gt;
&lt;span class=&quot;token key attr-name&quot;&gt;SupportsControllerService&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token value attr-value&quot;&gt;DBCPService 1.23.2 from org.apache.nifi - nifi-standard-services-api-nar&lt;/span&gt;
&lt;span class=&quot;token key attr-name&quot;&gt;BulletinLevel&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token value attr-value&quot;&gt;WARN&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;ini&quot;&gt;&lt;pre class=&quot;language-ini&quot;&gt;&lt;code class=&quot;language-ini&quot;&gt;&lt;span class=&quot;token key attr-name&quot;&gt;DatabaseConnectionURL&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token value attr-value&quot;&gt;jdbc:teradata://192.168.190.128/dbc&lt;/span&gt;
&lt;span class=&quot;token key attr-name&quot;&gt;DatabaseDriverClassName&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token value attr-value&quot;&gt;com.teradata.jdbc.TeraDriver&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Then click enable, and if everything is good, you should get an enabled connection.&lt;/p&gt;
&lt;p&gt;Now you can run the Flow. I have added a PutFile Processor as well and connected them.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 772px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/09097358746656c30ebf69d1044e764c/a546b/nifitd-flow-run.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 57.8%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAMCAIAAADtbgqsAAAACXBIWXMAAAsSAAALEgHS3X78AAABXElEQVQoz32RW47WMAyF//0vhkcWwANbAHUmnar3OM394jhNB9SOQAj6c3Te7M9OfB7fvn42Vs7L2ve9MbZe2n+p1hpjwky3fnz/8qkQTvM6DJPSxjmHiD8uIZELISE+hbdt08byZVnnWYCwxv6uGRc2ZUKMT+H2rQMQrGna5oU1jdaGyp6p7GW3zgupw3+eDQBCbBsAcA6ca61TSsYYITbrvFTahxNPmG9gKvu56nKph9Lm5fW177qWMe8DZkqYQ0y3+x8fI9PVlDNZ54dxFJeMNtM8K6Wc986f+J9OmB//zqOyO2sVABIyxoZx3PeP8P6O8AZGKtY6K2U9qtIahDDOxxRDCPU4jvf3ehwxJh/CDZypKG2WdZVKL9PcMda1rQDA84M5EyXEBc5zPoW7vgex8ZV37G0aRiVlprOUqSTMQpp7GDP5EK/g4AwSxLosnK9XZimmFGLUJxt+AsoBq0tndpsOAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;nifitd flow run&quot;
        title=&quot;nifitd flow run&quot;
        src=&quot;/static/09097358746656c30ebf69d1044e764c/a546b/nifitd-flow-run.png&quot;
        srcset=&quot;/static/09097358746656c30ebf69d1044e764c/0eb09/nifitd-flow-run.png 500w,
/static/09097358746656c30ebf69d1044e764c/a546b/nifitd-flow-run.png 772w&quot;
        sizes=&quot;(max-width: 772px) 100vw, 772px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;Right clicking on the List queue we can see the queue of resulting SQL queries.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 474px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/4f39b87e161601dc5e25779d8c07fba7/823f8/nifitd-list-que.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 33.33333333333333%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAHCAIAAACHqfpvAAAACXBIWXMAAAsSAAALEgHS3X78AAABVklEQVQY0y3LwW7bIAAAUP//p+w8qZu0HFZNWzUpXdImCsSxjQFDbAwGDBiw1VVaJ73rK+SI9NRZzXl34fQc/Ohs7+fBzb2zd2d7Z2gIdklrTDnm9UNat5hycfjzVJYQVRC8HmhbW6OsUVINSgmtpTbTpMhkRhuicU4Z+0Eaa5wvUAOr6sYZoaghqL53uGdkfzzCW1NVDaZdWdYAQAAAYzylHMISY3LOD6Ms9ESsZmaixjCj6WyZnXlLmlFOQohRKiHGfhDDILS2Ka8p5ZzXEJZejMWStpg2H9ceIS0nP/sRk5cLOJwuqMWUMoQwxuR6LQntYswx5pRW5xchVRFiWmLyS+wJ7loUrNW4/fbj56fPX3ePvx52j192338fL88nuH8F+xPcn+Dz+fp0OGPG/+WUZx/c7Pw8b9v29+0N3hpQoRqRsm4rRNqO4+6O2X+E9zWm90G8A7pNfTkOa6FpAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;nifitd list que&quot;
        title=&quot;nifitd list que&quot;
        src=&quot;/static/4f39b87e161601dc5e25779d8c07fba7/823f8/nifitd-list-que.png&quot;
        srcset=&quot;/static/4f39b87e161601dc5e25779d8c07fba7/823f8/nifitd-list-que.png 474w&quot;
        sizes=&quot;(max-width: 474px) 100vw, 474px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 788px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/e1b7d0d491f17e36c3d23f823c0bbeb7/c3dd4/nifitd-view-queue.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 35.8%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAHCAIAAACHqfpvAAAACXBIWXMAAAsSAAALEgHS3X78AAABL0lEQVQY05WNSUvDQACF8/8v4kEPQlNtLYptLdIVEUSPVREL3aykTTOTzkxmJrOkTaZLIvHkUR8fj+/yeFb2kzRND38mzbI4SbiQVmIMEzIxJvtP8rPDwUIIv71/DMeT8eRzOnOGo+loPIWIQBxAlAMQ+d25YApwQLmw+oPRkX1Vubu/qHfsaqtw0yzW2+f1dqnRLd12So2uXW1eNnp2rXXdeijW2ifl+mmhcnxWrvUerVBKFFCpo/UmZjxcuEuEAw9AEtDlErgeyMUDmASEsthshZCvT8+D/guEvqWiiEu1iePtbqejNcYkDAWlTCqNMcGYSKUwJkJIpfR+v0+M+ZovFh5wnLkltSaMax1t4kQqDf0VZRwhzDgH0AfQz8VfBZTxUCRmq6P1zHXn+dj5BlEBeYNC7CdZAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;nifitd view queue&quot;
        title=&quot;nifitd view queue&quot;
        src=&quot;/static/e1b7d0d491f17e36c3d23f823c0bbeb7/c3dd4/nifitd-view-queue.png&quot;
        srcset=&quot;/static/e1b7d0d491f17e36c3d23f823c0bbeb7/0eb09/nifitd-view-queue.png 500w,
/static/e1b7d0d491f17e36c3d23f823c0bbeb7/c3dd4/nifitd-view-queue.png 788w&quot;
        sizes=&quot;(max-width: 788px) 100vw, 788px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;We can see the successful query of the Teradata data in Nifi.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 814px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/c51925250b42a4893cc7687b9acba042/1745e/nifitd-td-data.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 55.400000000000006%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAALCAIAAADwazoUAAAACXBIWXMAAAsSAAALEgHS3X78AAABWElEQVQoz42QO48UMRCE58fzB5AIkcgISYgIICA/HhIcJzgQu+zu+LXjtqdn2m63bTQQAYvuKumWWp+6qoY3H2/ef/rce6+1tdYyF6mt/yOpNXNp7Y/T8PDJ0wePHnMpIpKY44wrJS7bTimFEOYYrTZGa6OU1dpq7YyxShXm4dmLV89fviYiADgp/XW3V+bsI2p3DnFm5pSyc2ejtZ8mZ43VahvjyJwHLvLrqyDiOse4/54AFoyt1X5Jv53HGBFxSJlFhJkjIgVQ767s7Rc4HlYiEblI995zSjnnofdeiiwrIeIMEE/H1Tn0Exfu/xczb/C8rJkLZ/YhUAzj2yu/3016pJz+6vYCrNxEOVcRD4AAsN+F44/FmjUnqfUOeFm3bCLivUcAd3ON1vjJZb6H7ZxLrbWU4j1QCPb6g/t2C4fDQmutcge8FSZCRB58DCGOJ5qmBfE+hf0EHsJ50UGylJgAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;nifitd td data&quot;
        title=&quot;nifitd td data&quot;
        src=&quot;/static/c51925250b42a4893cc7687b9acba042/1745e/nifitd-td-data.png&quot;
        srcset=&quot;/static/c51925250b42a4893cc7687b9acba042/0eb09/nifitd-td-data.png 500w,
/static/c51925250b42a4893cc7687b9acba042/1745e/nifitd-td-data.png 814w&quot;
        sizes=&quot;(max-width: 814px) 100vw, 814px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Hip-hop collaboration network graph]]></title><description><![CDATA[Introduction As a Hip-hop fan (and other genres of music) it is obvious that certain artists are more alike than others.
I was interested to…]]></description><link>https://mortimer.xyz/hiphop-collaboration-network/</link><guid isPermaLink="false">https://mortimer.xyz/hiphop-collaboration-network/</guid><pubDate>Mon, 08 May 2023 00:00:00 GMT</pubDate><content:encoded>&lt;h1 id=&quot;Introduction&quot; style=&quot;position:relative;&quot;&gt;Introduction&lt;a href=&quot;#Introduction&quot; aria-label=&quot;Introduction permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h1&gt;
&lt;p&gt;As a Hip-hop fan (and other genres of music) it is obvious that certain artists are more alike than others.
I was interested to see if a network of Hip-hop artists based on their collaborations with others would be able to highlight some interesting insights.&lt;/p&gt;
&lt;p&gt;The things I needed to solve this problem were:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Data on artists, their tracks (songs) and the artists featuring on the tracks&lt;/li&gt;
&lt;li&gt;Network graph analysis and visualisation software&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This was done on a Windows 10 machine with:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Python 3&lt;/li&gt;
&lt;li&gt;Gephi&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;Blog-Collaboration-with-Herd-Mentality&quot; style=&quot;position:relative;&quot;&gt;Blog Collaboration with Herd Mentality&lt;a href=&quot;#Blog-Collaboration-with-Herd-Mentality&quot; aria-label=&quot;Blog Collaboration with Herd Mentality permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Speaking of collaborations, this post was done as a collaboration with a passionate group of technical data experts who are putting out their own collection of posts for the data community. See &lt;a href=&quot;https://herdmentality.xyz/blog/hiphop-collaboration-graph&quot;&gt;Herd Mentality&lt;/a&gt;&lt;/p&gt;
&lt;h1 id=&quot;Getting-the-data&quot; style=&quot;position:relative;&quot;&gt;Getting the data&lt;a href=&quot;#Getting-the-data&quot; aria-label=&quot;Getting the data permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h1&gt;
&lt;p&gt;To have a look at the source code for getting the collaboration data, have a look at the following Github repo &lt;a href=&quot;https://github.com/mortie23/musicbrainz-get&quot;&gt;https://github.com/mortie23/musicbrainz-get&lt;/a&gt;.
I looked for existing dataset but counldn’t find one, but found a fantastic free API from MusicBrainz &lt;a href=&quot;https://musicbrainz.org/doc/MusicBrainz_API/Examples&quot;&gt;https://musicbrainz.org/doc/MusicBrainz_API/Examples&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;To get started, since it is 2023, I started with a prompt to ChatGPT that was something like this:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Write me a Python script that calls that Music Brainz API (documentaiton here &lt;a href=&quot;https://musicbrainz.org/doc/MusicBrainz_API/Examples&quot;&gt;https://musicbrainz.org/doc/MusicBrainz_API/Examples&lt;/a&gt;) that searches for an artist and returns the artists ID.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Of course it oblidged politely:
&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 1025px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/c2290c82727e51bd9a41eb1b0f9f1d58/cf562/hiphop-graph-chatgpt.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 25.8%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAFCAYAAABFA8wzAAAACXBIWXMAABJ0AAASdAHeZh94AAAAuklEQVQY051Qyw7CIBDk/3/F7/Du2WvLq7Riy4JpQxmzYBu9OslkYHZImBVkLvAPB6kMtGZaSKUhpULfKyhlTmVfKV3vwzCCKGLbNuS8Y993MASQ4f0TXSdrUEqNeV5qeFkCYoxYQkAIBKLGQISUUj2v64pvCBSgFPyNUgpyzvWXrOJ6v8GNE4wZYK07lSsdyt7hWztWzzl+4zBN/tOGEEKEML4NW+W2H14BDznUglTrs1b+eBEpvU6+AUCqfsikHEdCAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;hiphop graph chatgpt&quot;
        title=&quot;hiphop graph chatgpt&quot;
        src=&quot;/static/c2290c82727e51bd9a41eb1b0f9f1d58/cf562/hiphop-graph-chatgpt.png&quot;
        srcset=&quot;/static/c2290c82727e51bd9a41eb1b0f9f1d58/0eb09/hiphop-graph-chatgpt.png 500w,
/static/c2290c82727e51bd9a41eb1b0f9f1d58/1263b/hiphop-graph-chatgpt.png 1000w,
/static/c2290c82727e51bd9a41eb1b0f9f1d58/cf562/hiphop-graph-chatgpt.png 1025w&quot;
        sizes=&quot;(max-width: 1025px) 100vw, 1025px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;Then, with a bit of refactoring and a few more prompts to ChatGPT, I ended up with a script that could loop through a list of my favourite artists and produce a CSV file of the collaboration graph.
The analysis was a bit of a throw back to the 1990’s of Hip-hop and I wanted a few East coast and West coast rappers in the list to see if the network analysis clusters them well.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;py&quot;&gt;&lt;pre class=&quot;language-py&quot;&gt;&lt;code class=&quot;language-py&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;# %%&lt;/span&gt;
artist_list &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;
    &lt;span class=&quot;token string&quot;&gt;&quot;Jay-Z&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token string&quot;&gt;&quot;Nas&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token string&quot;&gt;&quot;Wu-Tang Clan&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h1 id=&quot;Analysing-and-Visualizing-the-data&quot; style=&quot;position:relative;&quot;&gt;Analysing and Visualizing the data&lt;a href=&quot;#Analysing-and-Visualizing-the-data&quot; aria-label=&quot;Analysing and Visualizing the data permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h1&gt;
&lt;p&gt;After quickly trying some network and charting packages in Python, and then trying a PowerBI markplace viz with minimal success, I remembered a desktop appliction I used years ago that could render beautiful network graphs.
So I started working with Gephi &lt;a href=&quot;https://gephi.org/users/download/&quot;&gt;https://gephi.org/users/download/&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/static/images/hiphop-graph-gephi-screenshot.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;h1 id=&quot;Analysis&quot; style=&quot;position:relative;&quot;&gt;Analysis&lt;a href=&quot;#Analysis&quot; aria-label=&quot;Analysis permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h1&gt;
&lt;p&gt;Here is the first look of the network graph with labels.
The size of the nodes (and labels) is the number of edges in the graph (the number of collaboration artists).
The color is:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Orange: Calfornia&lt;/li&gt;
&lt;li&gt;Green: New york&lt;/li&gt;
&lt;li&gt;Blue: Chicago&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 2000px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/69c8962090800db0f980abb5fd77e664/2eb59/hiphop-graph-label.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 100%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAUCAYAAACNiR0NAAAACXBIWXMAAAsTAAALEwEAmpwYAAAC7klEQVQ4y42U2W7bRhSG/VhNYnEdDsnhTkryQtWSbNmWFXmRWxu2k1YFCtcGCqQw0PYmSBcUvewL9LZ9r68gHStSrSwXB4PhzHz8z/nPzIphGDyErusL4//XNK2BaZp1LNtTxcr7YDVA19E1Da3RwLZtXD9AOOLTgPNgIQRxlhOFIVGSEUQR7eEJW8dneMp5BJufLwUmRYv9q2/ZffUb3eMJ3Z0+G0dDuuddgizBsuy3JdDqUTfeZbWyTLZtC9Z2Dtj742/K03PSZkrYTlC5iwwT/DCos3Bdt07f0B9UvgXO163aEGQS1dRIeiXNvQHNrYKoHePHAtVShHlCnKdEa038QC3WcJmz0hcUuyHlpMvnx32CwmHjKKC1HWI7NlGS4PgSJ/DwY4kbiNq0hZSrSfWxiqwdM5pucjodMPhqncF3Hu19l9aOIulIwlyilIunfIS0sGxrVssFYJVuBVSBQ38nYjLN6H+pKC90WiPJ/huN578Kyl67VmY7FpZloWn645RN06iBjrBp5y6bLcn20KXTlaSJw/q2z8GbVbamFmmSI9V9P96D9Nk4M+WzJ0958nSVZ88arGWCViE5HGj0SoEfNChyQdFxiQsP4Zs4gYlpmQuG1jWs5FaqxsMuB4OSk1GP26uS7y8zfrzucXmywXi0Tpq7OErDMjVUbLE2VBSdEEcuNvpK1ZyeJ5lejrl+Oeb1Dxf88+ch//7e4e5mxN3NmL9eT7g6SlChSRAKbOf+sOMKhLAXgQ9mVEoruPIEh/2Au5cF12cpZdPm9tTh9jyjyMPatIdGrs48SnnmsGHQ0HQiZTP9ouSnb9Z58TyiFen0NxR5Wj0McuGqLb3L839wXclkN+F4N+OXm5Kfv0652BM0U79ujyo+9MTNPQ6VXIM49CgSH+VLpidNzg4KXr1o09tM0XTjo7AZ8N1Gg9VVDWGbKN9FSpdQeQS+nK2/D/SBu6xjW1bd6PeGGUhHfFTZUoXzV3DerNrZTwT+B+9U6c73iXH4AAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;hiphop graph label&quot;
        title=&quot;hiphop graph label&quot;
        src=&quot;/static/69c8962090800db0f980abb5fd77e664/f97d7/hiphop-graph-label.png&quot;
        srcset=&quot;/static/69c8962090800db0f980abb5fd77e664/0eb09/hiphop-graph-label.png 500w,
/static/69c8962090800db0f980abb5fd77e664/1263b/hiphop-graph-label.png 1000w,
/static/69c8962090800db0f980abb5fd77e664/f97d7/hiphop-graph-label.png 2000w,
/static/69c8962090800db0f980abb5fd77e664/2eb59/hiphop-graph-label.png 2048w&quot;
        sizes=&quot;(max-width: 2000px) 100vw, 2000px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;h2 id=&quot;Most-collaborations&quot; style=&quot;position:relative;&quot;&gt;Most collaborations&lt;a href=&quot;#Most-collaborations&quot; aria-label=&quot;Most collaborations permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 218px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/fbafe60e2fcb41ba61f275379ac2f5b0/82dd2/hiphop-graph-snoop.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 101.37614678899082%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAUCAYAAACNiR0NAAAACXBIWXMAAAsSAAALEgHS3X78AAAFMUlEQVQ4yz2Ve0wUVxTGBzBBaNGUYInpH7akUKqAKLs7s8uCLA9BClogFTDg+iiiKaBYIIiSGmNBIwktpQmJjY+2MS00Ra2FnZ0FFhVNsMYHYK1pUx80VZEVWPbBzsz5mhmof9zJzSTzO98957vfMLByDASOIYHzI8HgR4Keob6UELIllZOgvyT3xj+AJW4SdvY57IZR2cr94LMYCnBnh9/CdwEkzDOU9QoGSyxDtiSGbEYzrOwT9MZC6l0DXMsHXd+KB99xGD8XB/RpgT4NZJ69BZvRRLZEhgSD//9QhhSFllWM3J/BkJD4Nfi1ICsL763DIr0cEe/cHJKbjx0js9lMn1bulA9XZYtj57JFDHCQLQkgW2KlKmQBypCg95cFowJrg3UtJHuOOPXoijTtktHa1kGbPsyn+vp6am1tpdYv22nv/gNUtrsK9+1fiRhMBc1Dy0gwqMdXHgwJxkJYE+AVMsWJ8TFy+4DGQwdoSchrMJlSUFxchLNnz+D8+W6cOvUN7a/eSzUNR/Hyb7sEWyLIqpPJZoxTFYrCuiAI3F8yr8HDW93SlEvG2OhtCgsLQ3p6Ok6cOIHy8nIcOXIEdrsdzc3NyMxcj6h336Gz5y4Aj7t8ck8MSDD8AivLKIMwwxIHz4168bnDBcgiHT9+HOHh4cjMzMTGjRtRXV2Nuro6KO93756Ha7VaFBcXk8fjobnLhQRLPKgvJV7p4U/g12D2yWVxcspDkjiHqqoqJCYmoqWlBR0dHWhsbFT3bW1tqtqCggJoNBpkZWaQY0aE6/eTouIKshkPMuATxsieBadjXJ6ecZFzZgZ79uxBVlYWmpqa0N7ejg0bNiAvL09VW1NTo6qNi4tDXW0N3HNEkw+vieA1IIHrZmBZPSEPFcM5M01uj4fGRkdhNpthMBhUaFJSklpAAaWlpal7RalSoKvzR7i9Ek08GZGoLwWwaq8qwEl5aAtmZ50kiiLdGB5GRUUFTCYTdDqdCi4pKUFtbS1yc3ORkZGB0tJSJCQk4MKF83B5RHr+eESS+0wKcIgBv/YBDebC65yQnbNuIpJx+vRp6PV6dcrbtm1DcnKy2rOYmBhERUUhOjpaVX737m1MOb00+WhYhFUHCNwlxYe/wqqB++lNcXLKTXNeD7q6urBy5cpXahoaGlBUVASWZbFixQpERkaioqIS98ZGyDHjg/P+96LiFLIZP1dsU6VMyHv3mDjtIgCgzs5OLF68WLXO8uXLVVXK1JUWREREqMNyOBx49vRfckw5yXd9pwzLasU2RiVZlhGvncBACh7eG5SvXPsNnzUepEWLFiEwMBABAQHqCgoKUgsEBwejsrISjhfPyOkFZv68KCq2I0E/hJ731bvM+CzcPgzocP9bvW9Z2BsUGR0L0zojLQl5XVWogEJDQ7F06VLExqzCkpBgSkrNhuOfERmDWfL8fTamqVdPvvoRM8cnM6KFu4jLegy2vDf3QbqOdpTvgzE5lfLz88i8tQTpaamUk5NDm4tLKTT8bWSwb0kvLyaLEDSQrIaj8zHG+c9nocAysHOBEs/xCtTVY0T3F5t9lbsK5Z0f76ItW8uwPjsP27dvp9pPCqWTdat9Es8C/SwkXv+lj09i5IH1atDMB6yV9VcTFxGMr5dtgk3nw4AGnh49xn/Owh+dm+j2mXR63GWCxHPAFQ6wcS9EXl/m7eEY3IhfELYAXID6waZlZCvHzPXqo0Ve3wobO4o+jRf9CYBdC/RrnRDY4bke9pD7ku5NiWcZ0cL6kZV99Qv4Dw3H3XpwaPjVAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;hiphop graph snoop&quot;
        title=&quot;hiphop graph snoop&quot;
        src=&quot;/static/fbafe60e2fcb41ba61f275379ac2f5b0/82dd2/hiphop-graph-snoop.png&quot;
        srcset=&quot;/static/fbafe60e2fcb41ba61f275379ac2f5b0/82dd2/hiphop-graph-snoop.png 218w&quot;
        sizes=&quot;(max-width: 218px) 100vw, 218px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;The winner of this title goes to Snoop Dogg.&lt;/p&gt;
&lt;h2 id=&quot;Clusters&quot; style=&quot;position:relative;&quot;&gt;Clusters&lt;a href=&quot;#Clusters&quot; aria-label=&quot;Clusters permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 2000px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/8e9887bf1a5f3497a765b6cbd1978a62/2eb59/hiphop-graph-clusters.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 100%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAUCAIAAAAC64paAAAACXBIWXMAAAsSAAALEgHS3X78AAACuklEQVQ4y31UTW8bNxDV3ynQXuLGlqNdzgzJJZfcFffDXFleyVr5I1Ycw5as1G6qpqjdpjXQQy899tR/WqxdNDGqakDyQODNe8S8xw7+t4AeFyISAGK711dnzR0xpBB5iBTySIIQxAnZU7vNYCBV6siruDI8I1dki/e32iO3PYoYErZd1oKBQdRP3NmhdpUtK1vkRbW//O4blVmSSKaHZpd00D5qHTNEJXITD3wlkzjZy9xwz+RxZJ1UKpLKWqszzm3vCd/5jBalRW6Cs9PXy/sfzWg/qzMeS2Uo9rEw6ujk6O7uvvIDpnZ5zBCegyPHetC9nl9f/HCdz7zKqCzLvHQyJqGjOIknzcQ4xQ0TaYCMPoEJSVkR2q0vv/hKO+FP3eJqOW7q+nW5093Jy1QqIk5cIhfIbfBMNgPUUtgy8Idyr6iqgZ9dTK/mVz/9fL9arc4vz0hBpCUhkQCRBJ9kE2Fmwfd5U4nLi2hxO5w0JivgZNZUwzJJkn6ZCN0yA0OKWMv8JJsBGgXO4KseDEs6HpnVh3d//vVxPOmOakbRq60XL3Z7Oy+3v2aMIZDoByjgH2Yi9I7SmI5r+9vD/eJqdnhQ3CwvByMaTcNqEuicmbQdlkhDmQekwye3dRig1bjnZJHQw42/Wy3mb6fvL+2kZs10enp6PKkhLYDbYLv7UvZDLuFfn3ZChkWCOuIA6Kyo8+CXbw/uFun0QA/3/cWbZuxJS2KMOOfA8HOHt8z9GOIIAEV3e6sZV0dHJ9/Pa293pgPuUukMKolrk9UBRM6xsGHu0roeLa/nDw+/3izO/vj9YzMe9Hqh7xPn/wPGx8DqCA9yZhQNKv9hdTt7c35+/nbg874O0rgdx6Y8M0DBMbPMp6zKlNXcyt2hYxuQz4MBjyaLKNHgDHOWKyk2INd8BvCoImTtScRxY/0N679UpMbiLkEAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;hiphop graph clusters&quot;
        title=&quot;hiphop graph clusters&quot;
        src=&quot;/static/8e9887bf1a5f3497a765b6cbd1978a62/f97d7/hiphop-graph-clusters.png&quot;
        srcset=&quot;/static/8e9887bf1a5f3497a765b6cbd1978a62/0eb09/hiphop-graph-clusters.png 500w,
/static/8e9887bf1a5f3497a765b6cbd1978a62/1263b/hiphop-graph-clusters.png 1000w,
/static/8e9887bf1a5f3497a765b6cbd1978a62/f97d7/hiphop-graph-clusters.png 2000w,
/static/8e9887bf1a5f3497a765b6cbd1978a62/2eb59/hiphop-graph-clusters.png 2048w&quot;
        sizes=&quot;(max-width: 2000px) 100vw, 2000px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;The east coast and west coast artists are clustered well together based on their collaborations (as expected).&lt;/p&gt;
&lt;h2 id=&quot;Key-collaborations-between-clusters&quot; style=&quot;position:relative;&quot;&gt;Key collaborations between clusters&lt;a href=&quot;#Key-collaborations-between-clusters&quot; aria-label=&quot;Key collaborations between clusters permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 464px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/29d4418ed581277ee995785b58ae6464/0359b/hiphop-graph-drdre-50cent.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 48.060344827586206%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAKCAYAAAC0VX7mAAAACXBIWXMAAAsSAAALEgHS3X78AAADKElEQVQozxXMbVDTBRwH8B9evKgLTS+v81AgnVCpQcOY7G9sOGHNKeqAmLQHXcA2YMNOBziydifcUd0SuhDROG7U0IWeBtgBeiSlEdLN9OLI0qTueoJKhOjYw//37Xjxefsh2w9PUsm/lGQfS2o6cCHllrFbcvu1a6tbrLO03jq+kiqublzSM3yWxBvWJRjcTDhLG3HLfEa8WT0VDbr+RtDUg4vL5RhWEU92xZBl4vFcc0fanOqVF6HYthUva3OgNWbB0Pb8Qsn48t3271bRp8NCLK5kkNibvIfvNEf7zn0Ig9EE834L+nv8CE98AO5/wYov1ETO7uzZbdnbEUuPhpbGLROVymxRm7s7tCN3J0yt6QuOqdgNF78h+rOVknHvxHxf/xDSpLKQ1VrGHs9b/HFXIPzzbzMIf98KnF+aSSrdFkgSU0JrEuKhUGSxTqfjIn0hdHmFoX2le2C//GzbwFAR/ff5Xi/wAEc9DaF0aRpqamo4EAjw/Z/uYmr6n8jMX79AvP7qZXrzqAf2cjtrNBpubGxkp9PJBoOBzWZz1GI5gLLOl8ZAoFDw2MAnAT927cqLymQyVqvVXF9fD5/Px3/8/itm5sIcDrpFOnWiHe66I6zVatnj8aChoQGVlZVss9nEyooK2HzZ3wIUM9ikHly3NgGpqWnRxSw/P5+9Xi+am5t49uEDzMxFOBysY+rwn4LrUHVEIlmHxMREFgSBTSYjjtS6wy73QTj6Ms75vDqa6BTel0sleGrVmlBGxmYspn6/n0e/HsHIyGhkfnYaka/2X6N3+spx8ngH6/X66BMrlkG+NRNZiqxogdYg1rQXo/R2nObqZAphOie16aA0GrciHqsTksKCfAtOtrbA4aiKFBSX8cKPnUBvvIrKb6ytrW7fB32RHoV5xWLdYQ8fev0wlCXrUfrl08ec3Ur6qCX3kYWBHMJ1pfm9Kik2pCRBodrBJoudNz2XjLY3tgMjmlrccxG5BlVkGHuswOh/5m5FlwKmtnRUXRImHXcSDNWBvUQgOr3IrYqZX0zvq4TxjsyhS+8Kkd63M/nmadkognLtwyuFdPwz0P+4n7aPOrRm0gAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;hiphop graph drdre 50cent&quot;
        title=&quot;hiphop graph drdre 50cent&quot;
        src=&quot;/static/29d4418ed581277ee995785b58ae6464/0359b/hiphop-graph-drdre-50cent.png&quot;
        srcset=&quot;/static/29d4418ed581277ee995785b58ae6464/0359b/hiphop-graph-drdre-50cent.png 464w&quot;
        sizes=&quot;(max-width: 464px) 100vw, 464px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;It seems from the network graph that Dr Dre (from the west coast) signing 50 Cent (from the east coast) to his label became a key collaboration connection between the clusters.&lt;/p&gt;
&lt;h1 id=&quot;Wrap-up&quot; style=&quot;position:relative;&quot;&gt;Wrap up&lt;a href=&quot;#Wrap-up&quot; aria-label=&quot;Wrap up permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h1&gt;
&lt;p&gt;I think there is great insights that can be gained from collaborations between artists that confirm some suspicions of fans and highlight some unknown insights.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Flask application with Postgres containerized across multiple clouds]]></title><description><![CDATA[Flask app with a Postgres database containerized across multiple clouds Background We are building out an application that has a decoupled…]]></description><link>https://mortimer.xyz/flask-postgres-docker-multi-cloud/</link><guid isPermaLink="false">https://mortimer.xyz/flask-postgres-docker-multi-cloud/</guid><pubDate>Sun, 02 Apr 2023 12:00:00 GMT</pubDate><content:encoded>&lt;h1 id=&quot;Flask-app-with-a-Postgres-database-containerized-across-multiple-clouds&quot; style=&quot;position:relative;&quot;&gt;Flask app with a Postgres database containerized across multiple clouds&lt;a href=&quot;#Flask-app-with-a-Postgres-database-containerized-across-multiple-clouds&quot; aria-label=&quot;Flask app with a Postgres database containerized across multiple clouds permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h1&gt;
&lt;h2 id=&quot;Background&quot; style=&quot;position:relative;&quot;&gt;Background&lt;a href=&quot;#Background&quot; aria-label=&quot;Background permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;We are building out an application that has a decoupled RDBMS from a Front end in a multi cloud environment. We also want the front end to be containerized so that all the code and all its dependencies are packaged up and the application runs quickly and reliably regardless of the computing environment (development on client machine and on remote cloud hosting in production).&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 1093px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/475942d29b62460259c7c416d2aab2a4/994d6/flask-postgres-architecture.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 99.80000000000001%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAUCAYAAACNiR0NAAAACXBIWXMAAAsSAAALEgHS3X78AAAFHElEQVQ4y02UWYhk5RXHr8TYGcRoTNxCMJEwihJb4vbiiyFOh0TIgwh5CYE8iIEgiDozBqIYpRVRURFMHkLEhcAgaMQNp53Y3VNWd1d1V/e9tU11rbdu3X3f7/3ud7+/VPdkyIHzcfh/8ONsHG7HoNyGQm/YPjfb7khBd2SR1tTJmpKTDdpq8sHyWn6x262+bfa2R/ms2cxHX7eJo7QLR25n9rQdGEM+M0XZG9R/T5Uex21OI070yY2V9pgM1Agzh+F/pvmk/eO/46J8vF1J5Q7iYQ3+5lsX/s3QQkNuIYgi7PHrTwPguPnz1qZ91+4sjUQjgCVPaZzmRZrlmNi58N3jre+r3XpFH/DIo6DIo7CkjJUFLUvF9EpZNfIijeGKzccOgNzNKwvvbpl38moeyooJVD5gg6lSbgj7cBPscc++f/Xw3NbWbNpFPGpTWlvB0HAw1B10ZAFxEpI8iWGO+ScOgbesLHzc8m4VlDQUrQSe65ZeTKgTZZDdvHHdv9/5QTZrVpnWR+YbNAt1RGGIKPARmCJIGhGWhjAGjScuZPih4C025SQcWxlmAUpd0ag8HkDyWeOHr75xVSgKVWr0YAxUKgohomkHvtQDCoIiCQmNHJgXgD87vfAfwVsU5MSd2SnTvJJYk14+2V2HnaF+xUOPXx1N+LOw+5h0zKyxnhR6b6eQenwBoGBAgrKAMdw938OfrF3yUdO/vammGOkBNE2FbDjI6MGUJ3d8+Mm1kdTsEnUfSeQgyQIUtMR8F0pagLESjKQwR3tPHQIfOHXkzZXxj3al7Jm2nLzYU+Lne3K0PDLSVzoy+RPHcReF/Y2HMeVfTsXOcij138zU/utE6y/nWv+FVOkvE6X32pj/770cx13MLRz99eXH//Gvn1ZM5dFVTT5ZtbTjVVM7cdZQTtRt/eSXqvTSqqH+tWoZx6uW9pff9atHYj9ctF3/b3FK5n2b+1P7onwPx3FXcdy3Ti2cls07zoxH+GIwQNt3wDsmeNdCy7PxmSKiZusH8Vz7TJXuivzgWVlRkcUBCpIijGOYnnu+5Ctrl3w0NW5bHexnX+2fg+Ba4F0Tu5YOwTbwzw6PdVU6iOf6KX7vl7qqPscLLcgjAbo8wkybwXD1E4fAe2rfea+v3rQ+kzbXJKlVsy2+ZpnCuiQJq6LIn+kPOmdGo+ZXk4mwZZnt51c3F/MMf8hIOStKNChDA4BohvEfD4CXPjm87O21je8h2f5F6tV+hXR3CXl3KTQqS8yv3ff18MsHZ9rZ+4m7dSz3t471uyu/hV9fAhGWEru6RJzNYwjqv9nrfnKUu/vnRzjuWm9hON5YLP2dCLkEVZeZqu0DyIEigBsYyDMXrAjAiIvUaWGqTmA5OsAilEVcIJ3A19cePyz5Gm9hMN66jfo7IaiF5kBm23vrgD4+uChTL0RM6Pn7wpB6XTZVNSZaITSSgZQgWTzDVDz95CHwemdhOt28FWEjYJmKokRJooAFlc8ZSsr6ksySNJ3v8fzGMEcXGECYGuTs096ARXlGXHeE/vDzk4dALv12tVW/M3UbYRopIAyMkBi+3T3IqatFsJPiIC7LAiTooiwz0KIASbO5TFimAMHGnx2twnHUOctttOu3u0Yj8XwVQQ4WxClksYOyBHanHozwEMgYAw17KEuC/zNCUxnErjwCv85xhVPhEFeOItyNWDIGCrdkuQ1KfNDcAsih07mWmaBhBzTTQXMHNLNAiUdoPAR1Nx6h/g73DRl4LdXm8aQLAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;flask postgres architecture&quot;
        title=&quot;flask postgres architecture&quot;
        src=&quot;/static/475942d29b62460259c7c416d2aab2a4/994d6/flask-postgres-architecture.png&quot;
        srcset=&quot;/static/475942d29b62460259c7c416d2aab2a4/0eb09/flask-postgres-architecture.png 500w,
/static/475942d29b62460259c7c416d2aab2a4/1263b/flask-postgres-architecture.png 1000w,
/static/475942d29b62460259c7c416d2aab2a4/994d6/flask-postgres-architecture.png 1093w&quot;
        sizes=&quot;(max-width: 1093px) 100vw, 1093px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;The choice architecture for this demonstration will be:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;A self managed Postgres database on one cloud platform (GCP)&lt;/li&gt;
&lt;li&gt;A self managed web server on a different cloud platform (AWS)&lt;/li&gt;
&lt;li&gt;A local Windows client development environment&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id=&quot;The-Postgres-Database-IaaS&quot; style=&quot;position:relative;&quot;&gt;The Postgres Database (IaaS)&lt;a href=&quot;#The-Postgres-Database-IaaS&quot; aria-label=&quot;The Postgres Database IaaS permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;In this example I will be using a self managed Postgres database on a compute engine (Infrastructure as a Service). The main reason for this was that I already had this setup. It is a cheap way of testing out architectures. However, using a PaaS Postgres offering through something like these would be beneficial for a production solution as won’t need the overhead of managing upgrades and patching of the server OS:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;AWS RDS &lt;a href=&quot;https://aws.amazon.com/rds/&quot;&gt;https://aws.amazon.com/rds/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;GCP Cloud SQL &lt;a href=&quot;https://cloud.google.com/sql&quot;&gt;https://cloud.google.com/sql&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Azure Database &lt;a href=&quot;https://azure.microsoft.com/en-us/products/postgresql/&quot;&gt;https://azure.microsoft.com/en-us/products/postgresql/&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;Firewall&quot; style=&quot;position:relative;&quot;&gt;Firewall&lt;a href=&quot;#Firewall&quot; aria-label=&quot;Firewall permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;To get started with local development, we need to add a firewall rule such that our client machine can connect to the remote Postgres service running on the GCP cloud compute service.&lt;/p&gt;
&lt;p&gt;In the GCP console, we create a new firewall rule.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 200px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/f9b399660ae784667e81363503116a1a/56d15/flask-postgres-gcp-vpc-firewall-create.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 18.5%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAECAYAAACOXx+WAAAACXBIWXMAAA7DAAAOwwHHb6hkAAAAnElEQVQY062PXQrCMBCEe1zP4oPn8ByCYEXFnza1UdvYilXRl5psWtGMZPUIXVhYltmZbwN0WM45BOWtRX9YYbk3KK4tQqGRFhbJkZCWFnnVYCEN5tIgVoQoI97V5s2aOCe+qx4vNg1kadEbKIxWNdSlwTh6YpZqrA+E3cmy2IeFicZ0+2s/exChCBOhsckI5/vf0GPa9sO4Xbz8BR3dMWSjJ1yuAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;flask postgres gcp vpc firewall create&quot;
        title=&quot;flask postgres gcp vpc firewall create&quot;
        src=&quot;/static/f9b399660ae784667e81363503116a1a/56d15/flask-postgres-gcp-vpc-firewall-create.png&quot;
        srcset=&quot;/static/f9b399660ae784667e81363503116a1a/56d15/flask-postgres-gcp-vpc-firewall-create.png 200w&quot;
        sizes=&quot;(max-width: 200px) 100vw, 200px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;In this example we name it &lt;em&gt;postgres&lt;/em&gt; and the protocol/port is &lt;code class=&quot;language-text&quot;&gt;tcp:5432&lt;/code&gt; (the default port for Postgres). The Target (tag) will be the tag we have used for the Compute engine.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 793px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/80e0e928f040ef7694b9797e7bdb5616/94411/flask-postgres-gcp-vpc-firewall-postgres.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 39.6%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAICAIAAAB2/0i6AAAACXBIWXMAAAsSAAALEgHS3X78AAABB0lEQVQY04WOu27DMAxF/f/f06Ff0KFTl3ZJgSBJDcuWZFEi9SAtKYVTIEORogeXHAgc8A56mc+X8+l0mSbFN3LOLMxcSsnlhrBcHzEYoxetlxtKqWkaL19qVGExIUWKe2LO+bFMSODAOQg+YAgAHpEsbGuoLjTA6mOr7fpYLkF7Dz6EfRAB/DzP/Td/yCFWA3WrnTOVPVFkAwBmvv7HILIR+bfP+PQCz6/wfoy9Ss6FRYS5tb3xz+v7vpcZrLXj+HU4zscRPw5KaVqW2VhLhMaYlFLOGcBprYnIGIOIzLzadV3d4JwD50ljrzUn7G0jolJKaw0RiYhZYoxEtF+IRKT3XkpJMX0D/ivNRCdoB/cAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;flask postgres gcp vpc firewall postgres&quot;
        title=&quot;flask postgres gcp vpc firewall postgres&quot;
        src=&quot;/static/80e0e928f040ef7694b9797e7bdb5616/94411/flask-postgres-gcp-vpc-firewall-postgres.png&quot;
        srcset=&quot;/static/80e0e928f040ef7694b9797e7bdb5616/0eb09/flask-postgres-gcp-vpc-firewall-postgres.png 500w,
/static/80e0e928f040ef7694b9797e7bdb5616/94411/flask-postgres-gcp-vpc-firewall-postgres.png 793w&quot;
        sizes=&quot;(max-width: 793px) 100vw, 793px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;Then, after finding out what our public IP address is (you can just ask Google &lt;em&gt;what is my ip address&lt;/em&gt;) we allow our IP address through the firewall.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 540px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/c1d1e1558725cb8d658576262ea23cce/03ec9/flask-postgres-gcp-vpc-firewall-postgres-tag-ip.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 34%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAHCAYAAAAIy204AAAACXBIWXMAAAsSAAALEgHS3X78AAAA4klEQVQoz52RW4uDMBCF/f8/zgfLRoVKal8sarwk1uamp8xQSrsLC23ge0lmTs6cSaqqghACTdNg2zb4EBC+wHuPGCOS+nxmQSlP/OCcg/P+I0iM+qg/UUqh6zoYYx6CHwp5D+scbjfLEybN5YK6rkHCdEKIbP03VBxfiRvfkYlhGPAjBAKNrI2B1ppZluWRybvQM4p/oBp2WJYlsizD4ZAhTVMURYG2bXE8VpBSIs9zdrDvOze9ug784Y51XbmOM6Ts5nnGrDVDzeR0HEdM0wSlBlyv63OTfwgB1lr0fc9bvgPSSh1Hdjz4nQAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;flask postgres gcp vpc firewall postgres tag ip&quot;
        title=&quot;flask postgres gcp vpc firewall postgres tag ip&quot;
        src=&quot;/static/c1d1e1558725cb8d658576262ea23cce/03ec9/flask-postgres-gcp-vpc-firewall-postgres-tag-ip.png&quot;
        srcset=&quot;/static/c1d1e1558725cb8d658576262ea23cce/0eb09/flask-postgres-gcp-vpc-firewall-postgres-tag-ip.png 500w,
/static/c1d1e1558725cb8d658576262ea23cce/03ec9/flask-postgres-gcp-vpc-firewall-postgres-tag-ip.png 540w&quot;
        sizes=&quot;(max-width: 540px) 100vw, 540px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;You will notice there is a second IP address allowed through the firewall. This is the web server machine’s IP address which will be discussed later in the blog post.&lt;/p&gt;
&lt;h2 id=&quot;Local-development-environment&quot; style=&quot;position:relative;&quot;&gt;Local development environment&lt;a href=&quot;#Local-development-environment&quot; aria-label=&quot;Local development environment permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;I am using a Windows machine with:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;WSL 2 (Ubuntu 20.04)&lt;/li&gt;
&lt;li&gt;Python 3.8.10&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 936px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/e3a043bdcd4309abdd09a08f22326034/6d612/flask-postgres-machine-windows-wsl.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 50%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAKCAIAAAA7N+mxAAAACXBIWXMAAAsSAAALEgHS3X78AAABwklEQVQoz12Sz47TMBCH8wS0Uu0kTWPH8Tixm8TOX9sJoUt3qcQd3oMDFyQQe9sH4HG48GxQumIlpE9zmd8nzWgmiKlMaMl5A1RCWtA9x5iEIcUhzRIAUkBakDhHOEXohVsmwChNY2bY0XHteGNFW7FjHGUIkzJTUphSaMGbJIEsU4wdKVUsr5IEMCZBErERtBd6UtNUezuefeMbKneYDLxpwUDZSTUqNZayZ6ziXAPoZ1mS0oN2ZrHdqevvBvvgau+gSfd5n9eTMEKYUvY5rxE67HbPIJSGIQkGrud2te7ByeE6duPt+HYRrSSyy+tW9lKN2ixSDiyvOG+EaJMEkgTimAVD2U3unZ3OszCON7Mc1v40i7bKVE1VwSpR9lcHDIiWkJJSlaZFSor9Pg9aVk1FZ+39omcrBz/cuXZdhKEJXMz946dvX5++PH7/9fnHT//6/XYbXQ+ByY0ADmItOlv2vnvj/WXyF197DzqOMkGP83jy6zr7D+78URTm76pZGNIbQRjSOlOLaJ1oR9CTXqya4AA4pDuUbrbxZhNvN2j7Cu12h3/ai4wx5Qcx8MaBMeyYXl+C3NpRlEXRrWb/mX/k3/RjWj6Ny7p8AAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;flask postgres machine windows wsl&quot;
        title=&quot;flask postgres machine windows wsl&quot;
        src=&quot;/static/e3a043bdcd4309abdd09a08f22326034/6d612/flask-postgres-machine-windows-wsl.png&quot;
        srcset=&quot;/static/e3a043bdcd4309abdd09a08f22326034/0eb09/flask-postgres-machine-windows-wsl.png 500w,
/static/e3a043bdcd4309abdd09a08f22326034/6d612/flask-postgres-machine-windows-wsl.png 936w&quot;
        sizes=&quot;(max-width: 936px) 100vw, 936px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;To get started there is an OS dependency we need to install:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;sh&quot;&gt;&lt;pre class=&quot;language-sh&quot;&gt;&lt;code class=&quot;language-sh&quot;&gt;&lt;span class=&quot;token function&quot;&gt;sudo&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;apt&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;install&lt;/span&gt; libpq-dev&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Now we will create a Python virtual environment for the development.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;sh&quot;&gt;&lt;pre class=&quot;language-sh&quot;&gt;&lt;code class=&quot;language-sh&quot;&gt;&lt;span class=&quot;token builtin class-name&quot;&gt;cd&lt;/span&gt; ~/venv
python3 &lt;span class=&quot;token parameter variable&quot;&gt;-m&lt;/span&gt; venv fpg
&lt;span class=&quot;token builtin class-name&quot;&gt;source&lt;/span&gt; fpg/bin/activate&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;Clone-the-git-repository&quot; style=&quot;position:relative;&quot;&gt;Clone the git repository&lt;a href=&quot;#Clone-the-git-repository&quot; aria-label=&quot;Clone the git repository permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;The Github repository to follow is &lt;a href=&quot;https://github.com/mortie23/flask-postgres&quot;&gt;https://github.com/mortie23/flask-postgres&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id=&quot;Create-a-code-classlanguage-textenvcode-in-the-root-of-the-repository&quot; style=&quot;position:relative;&quot;&gt;Create a &lt;code class=&quot;language-text&quot;&gt;.env&lt;/code&gt; in the root of the repository&lt;a href=&quot;#Create-a-code-classlanguage-textenvcode-in-the-root-of-the-repository&quot; aria-label=&quot;Create a code classlanguage textenvcode in the root of the repository permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;ini&quot;&gt;&lt;pre class=&quot;language-ini&quot;&gt;&lt;code class=&quot;language-ini&quot;&gt;&lt;span class=&quot;token key attr-name&quot;&gt;APP_SETTINGS&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token value attr-value&quot;&gt;config.Config&lt;/span&gt;
&lt;span class=&quot;token key attr-name&quot;&gt;DATABASE_URL&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token value attr-value&quot;&gt;&quot;&lt;span class=&quot;token inner-value&quot;&gt;postgresql://&amp;lt;username&gt;:&amp;lt;password&gt;@&amp;lt;hostname&gt;:&amp;lt;port&gt;/&amp;lt;database&gt;&lt;/span&gt;&quot;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2 id=&quot;Initiate-the-database&quot; style=&quot;position:relative;&quot;&gt;Initiate the database&lt;a href=&quot;#Initiate-the-database&quot; aria-label=&quot;Initiate the database permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;If you have configured everything else correctly, and have the correct connection string in your &lt;code class=&quot;language-text&quot;&gt;.env&lt;/code&gt; file, you should be able to initialise the database.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;sh&quot;&gt;&lt;pre class=&quot;language-sh&quot;&gt;&lt;code class=&quot;language-sh&quot;&gt;flask db init&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;log&quot;&gt;&lt;pre class=&quot;language-log&quot;&gt;&lt;code class=&quot;language-log&quot;&gt;  Creating directory &lt;span class=&quot;token file-path string&quot;&gt;/flask-postgres/migrations&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;  done
  Creating directory &lt;span class=&quot;token file-path string&quot;&gt;/flask-postgres/migrations/versions&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;  done
  Generating &lt;span class=&quot;token file-path string&quot;&gt;/flask-postgres/migrations/script.py.mako&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;  done
  Generating &lt;span class=&quot;token file-path string&quot;&gt;/flask-postgres/migrations/env.py&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;  done
  Generating &lt;span class=&quot;token file-path string&quot;&gt;/flask-postgres/migrations/alembic.ini&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;  done
  Generating &lt;span class=&quot;token file-path string&quot;&gt;/flask-postgres/migrations/README&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;  done
  Please edit configuration&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;connection&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;logging settings in &lt;span class=&quot;token string&quot;&gt;&apos;/flask-postgres/migrations/alembic.ini&apos;&lt;/span&gt; before proceeding&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;sh&quot;&gt;&lt;pre class=&quot;language-sh&quot;&gt;&lt;code class=&quot;language-sh&quot;&gt;flask db migrate&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;log&quot;&gt;&lt;pre class=&quot;language-log&quot;&gt;&lt;code class=&quot;language-log&quot;&gt;&lt;span class=&quot;token level info keyword&quot;&gt;INFO&lt;/span&gt;  &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;alembic&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;runtime&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;migration&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; Context impl PostgresqlImpl&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;
&lt;span class=&quot;token level info keyword&quot;&gt;INFO&lt;/span&gt;  &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;alembic&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;runtime&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;migration&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; Will assume transactional DDL&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;
&lt;span class=&quot;token level info keyword&quot;&gt;INFO&lt;/span&gt;  &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;alembic&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;autogenerate&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;compare&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; Detected added table &lt;span class=&quot;token string&quot;&gt;&apos;todo&apos;&lt;/span&gt;
  Generating &lt;span class=&quot;token file-path string&quot;&gt;/flask-postgres/migrations/versions/bc12dad5cf64_.py&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;  done&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;sh&quot;&gt;&lt;pre class=&quot;language-sh&quot;&gt;&lt;code class=&quot;language-sh&quot;&gt;flask db upgrade&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;log&quot;&gt;&lt;pre class=&quot;language-log&quot;&gt;&lt;code class=&quot;language-log&quot;&gt;&lt;span class=&quot;token level info keyword&quot;&gt;INFO&lt;/span&gt;  &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;alembic&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;runtime&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;migration&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; Context impl PostgresqlImpl&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;
&lt;span class=&quot;token level info keyword&quot;&gt;INFO&lt;/span&gt;  &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;alembic&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;runtime&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;migration&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; Will assume transactional DDL&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;
&lt;span class=&quot;token level info keyword&quot;&gt;INFO&lt;/span&gt;  &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;alembic&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;runtime&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;migration&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; Running upgrade &lt;span class=&quot;token number&quot;&gt;812916de9ad7&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; bc12dad5cf64&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; empty message&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 627px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/01612401e781efa2da950b989b9b940f/5986b/flask-postgres-postgres-todo-table.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 54%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAALCAIAAADwazoUAAAACXBIWXMAAAsSAAALEgHS3X78AAAB00lEQVQoz22Q2W6bQABF+ZAqGiCDYfaNWRgWA97j2HXapFVe8v9/UdG4UlVVOg/n5T7ckxAbWbNubx/t4WLGJ7VpTWvk6Vo0G+Y6bGO3fTb9RnXTSoZ6N0yH3q0Put9l1CT99edwfHbjzEPLfJcSwxQup5PYv1DfQe3Xx++2PfF21uNRXN+b84u/vpbDHtUxmc9vw+kyHC8iDKWyAPOHkq+aMV5fWRxJWBPfrkiodMDNRF1XafsYxqLbIxOScX+z4053U05lRiRA4qGkqG7i9on6HkqL6gAIB5iDigHEABZpxdKSAsST29vH5vKtHmZiI0D8kSlim0J7EgbieygsQDzFYoHIu2D5G5HErz/C+SabnrgIuZHDvjlcSx1yXkPloXAALbNsQWWfQu+SfKlbtTtX0qSIZURiP0DlVqahzRpKVxjPh2ElQ6EcFLYQFgqbM1NID6VLMixSxMHfVKyqm3ra62Fb+UbtxpX02EY7HVAdKxNVN/O4hsImf27cybB4QNI4brpW9Zts+alSKgvp28M7DTMUtexmbNucmX/Hn0BhsGvj7lRptwRDImOSbmJR10s2ojKqM6r+P86YzqkulYPcLMHwkicXCiCeEZUznTOdEvkLIzCC3gtcsYQAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;flask postgres postgres todo table&quot;
        title=&quot;flask postgres postgres todo table&quot;
        src=&quot;/static/01612401e781efa2da950b989b9b940f/5986b/flask-postgres-postgres-todo-table.png&quot;
        srcset=&quot;/static/01612401e781efa2da950b989b9b940f/0eb09/flask-postgres-postgres-todo-table.png 500w,
/static/01612401e781efa2da950b989b9b940f/5986b/flask-postgres-postgres-todo-table.png 627w&quot;
        sizes=&quot;(max-width: 627px) 100vw, 627px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;h2 id=&quot;Run-the-Flask-application-in-development&quot; style=&quot;position:relative;&quot;&gt;Run the Flask application in development&lt;a href=&quot;#Run-the-Flask-application-in-development&quot; aria-label=&quot;Run the Flask application in development permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;sh&quot;&gt;&lt;pre class=&quot;language-sh&quot;&gt;&lt;code class=&quot;language-sh&quot;&gt;python3 app.py&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;log&quot;&gt;&lt;pre class=&quot;language-log&quot;&gt;&lt;code class=&quot;language-log&quot;&gt;&lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; Serving Flask app &lt;span class=&quot;token string&quot;&gt;&apos;app&apos;&lt;/span&gt;
 &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; Debug mode&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; off
&lt;span class=&quot;token level warning important&quot;&gt;WARNING&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; This is a development server&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt; Do not use it in a production deployment&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt; Use a production WSGI server instead&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;
 &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; Running on &lt;span class=&quot;token url&quot;&gt;http://127.0.0.1:5000&lt;/span&gt;
Press CTRL&lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt;C to quit
&lt;span class=&quot;token ip-address constant&quot;&gt;127.0.0.1&lt;/span&gt; &lt;span class=&quot;token separator comment&quot;&gt;- - &lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token date number&quot;&gt;02/Apr/2023&lt;/span&gt; &lt;span class=&quot;token time number&quot;&gt;09:28:57&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;GET / HTTP/1.1&quot;&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;200&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;
&lt;span class=&quot;token ip-address constant&quot;&gt;127.0.0.1&lt;/span&gt; &lt;span class=&quot;token separator comment&quot;&gt;- - &lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token date number&quot;&gt;02/Apr/2023&lt;/span&gt; &lt;span class=&quot;token time number&quot;&gt;09:29:10&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;GET / HTTP/1.1&quot;&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;200&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;
&lt;span class=&quot;token ip-address constant&quot;&gt;127.0.0.1&lt;/span&gt; &lt;span class=&quot;token separator comment&quot;&gt;- - &lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token date number&quot;&gt;02/Apr/2023&lt;/span&gt; &lt;span class=&quot;token time number&quot;&gt;09:29:11&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;GET /favicon.ico HTTP/1.1&quot;&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;404&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;
&lt;span class=&quot;token ip-address constant&quot;&gt;127.0.0.1&lt;/span&gt; &lt;span class=&quot;token separator comment&quot;&gt;- - &lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token date number&quot;&gt;02/Apr/2023&lt;/span&gt; &lt;span class=&quot;token time number&quot;&gt;09:30:02&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;POST /add HTTP/1.1&quot;&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;302&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;
&lt;span class=&quot;token ip-address constant&quot;&gt;127.0.0.1&lt;/span&gt; &lt;span class=&quot;token separator comment&quot;&gt;- - &lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token date number&quot;&gt;02/Apr/2023&lt;/span&gt; &lt;span class=&quot;token time number&quot;&gt;09:30:03&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;GET / HTTP/1.1&quot;&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;200&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;Test-the-appliction-works&quot; style=&quot;position:relative;&quot;&gt;Test the appliction works&lt;a href=&quot;#Test-the-appliction-works&quot; aria-label=&quot;Test the appliction works permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;With a sample item in the todo, we can test that the appliction is writing to the cloud IaaS Postgres instance.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 356px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/bb158111a752332623d9e26698c3faa7/323b2/flask-postgres-flask-app-1-todo.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 76.12359550561798%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAPCAYAAADkmO9VAAAACXBIWXMAAA7DAAAOwwHHb6hkAAACP0lEQVQ4y62S72/SQBjH+a/nGzeVvdF/QNEYjZvxrTMaYzZN9oK5TKdYNlYGjPKjUFraUkqB9lo+pqc0m7hojE/yyT3Xu/vet89zuVLPp9T3Ud0FJ8OIL7rPvqqjjqE6hpblYQwdzrU2fcuha1ho+oCeaWO6HqY7ZhaTkVMNnzPDp+6EVIczKsaEsu5RcwU1R9BxAgzXpzv0MNyJHNumiz706Dk+fXeCPRUZOZEkRCLOCEWMiBdZLknnyYIwTogSEAlyjOKFXJuLOCPHtbHgXyJ3Ua/x6vUbhIhRvn1l7/0HgulULo69EbV6XeZhGErkVYvFteTazTr5zU0KhQL5O7dZ37jFjbU1iodHNGoqN9c3ePrkMXfv3SOfz9PR+1I0SX7/B7kgmFCpVCiVShwfH6OqVU7KChdNDc8bcVY55eGD+7zc2eHZ1jZhJFZcLudSkP8QSzEpGEWCJWEYEUVRNp/PQ2azufye5hnhjz2zWUiSJFcdDq0h51UVrdXCtCwMo0+j0aDd7crNl0kPpeKVU5WtrRfs7u3LZl4RDIIA27YZ2jbuyMN1XUzTwvPGiDiRxZ/P55m71LFhDFCUUy6abeL4F4eG0aNQeEQqvIym1mT33Vu2t59zpp7/7Gryd+9wOg3QtBbJpcKmdex2O3Q6nexNXm5AyrIEK132PE8KWOaAo0+fOTgoomkaqqpSLBYplxUUReHg4yGtVnulqysORRQhhJCio9EIx7FJL/H9CY7joOs6xmCAbTtMp7M/Cn4HaFNyqPkuo+IAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;flask postgres flask app 1 todo&quot;
        title=&quot;flask postgres flask app 1 todo&quot;
        src=&quot;/static/bb158111a752332623d9e26698c3faa7/323b2/flask-postgres-flask-app-1-todo.png&quot;
        srcset=&quot;/static/bb158111a752332623d9e26698c3faa7/323b2/flask-postgres-flask-app-1-todo.png 356w&quot;
        sizes=&quot;(max-width: 356px) 100vw, 356px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 509px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/9877335e2c301791074b30f24e7dbb57/98235/flask-postgres-postgres-todo-table-data.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 44.99999999999999%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAJCAIAAAC9o5sfAAAACXBIWXMAAAsSAAALEgHS3X78AAABL0lEQVQoz43PyU7DMAAEUH9JSWLXsZN4jxM7S9N0oQu0qEVAb0gc+AV+HyWIIxHSO4xGmsMAsr2Jpqe2hsL61cP55YPXvWw30nf4cOX7i6p7Xvfnzy+12NLdzZ1e6+OpenxOXAuwqbDxsll3x4uqV1GmIRtEmYKjaASZGQLTkBvITcSGEmBVElsz1xX9bkayGWEzKu4SEaYyTMVIhqkMfppEBL/CVAKsHbENc93m/Nbcn4RbCJlTroNxMw1gWRBTUeMQM7pZJ8YLaf47Rn5J2k3mWlJU0XBVh8wEmUbMIJ7DSSA7XNvb+/ry4o9PrOrnsqS24n5JbTNXJTZ+AqDaYWF1v6KujF0Z2yLWzu33oltgWyBhkcj/AoJURsxApbHN49LOtQkSCZVCRiOtpz9/A44uZpFws2RDAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;flask postgres postgres todo table data&quot;
        title=&quot;flask postgres postgres todo table data&quot;
        src=&quot;/static/9877335e2c301791074b30f24e7dbb57/98235/flask-postgres-postgres-todo-table-data.png&quot;
        srcset=&quot;/static/9877335e2c301791074b30f24e7dbb57/0eb09/flask-postgres-postgres-todo-table-data.png 500w,
/static/9877335e2c301791074b30f24e7dbb57/98235/flask-postgres-postgres-todo-table-data.png 509w&quot;
        sizes=&quot;(max-width: 509px) 100vw, 509px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;h2 id=&quot;Deploy-the-application&quot; style=&quot;position:relative;&quot;&gt;Deploy the application&lt;a href=&quot;#Deploy-the-application&quot; aria-label=&quot;Deploy the application permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;h3 id=&quot;Create-a-docker-image-of-the-Flask-application&quot; style=&quot;position:relative;&quot;&gt;Create a docker image of the Flask application&lt;a href=&quot;#Create-a-docker-image-of-the-Flask-application&quot; aria-label=&quot;Create a docker image of the Flask application permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;sh&quot;&gt;&lt;pre class=&quot;language-sh&quot;&gt;&lt;code class=&quot;language-sh&quot;&gt;&lt;span class=&quot;token function&quot;&gt;docker&lt;/span&gt; build &lt;span class=&quot;token parameter variable&quot;&gt;-t&lt;/span&gt; mortimerxyz/flaskpostgrestodo:0.1 &lt;span class=&quot;token builtin class-name&quot;&gt;.&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;log&quot;&gt;&lt;pre class=&quot;language-log&quot;&gt;&lt;code class=&quot;language-log&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; Building &lt;span class=&quot;token number&quot;&gt;2.3s&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;)&lt;/span&gt; FINISHED
 &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;internal&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; load build definition from Dockerfile                                   &lt;span class=&quot;token number&quot;&gt;0.0s&lt;/span&gt;
 &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; transferring dockerfile&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;326B&lt;/span&gt;                                                   &lt;span class=&quot;token number&quot;&gt;0.0s&lt;/span&gt;
 &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;internal&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; load &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;dockerignore                                                      &lt;span class=&quot;token number&quot;&gt;0.0s&lt;/span&gt;
 &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; transferring context&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;2B&lt;/span&gt;                                                        &lt;span class=&quot;token number&quot;&gt;0.0s&lt;/span&gt;
 &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;internal&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; load metadata for docker&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;io&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;library&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;python&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;3.8&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;buster                   &lt;span class=&quot;token number&quot;&gt;2.2s&lt;/span&gt;
 &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;auth&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; library&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;python&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;pull token for &lt;span class=&quot;token domain constant&quot;&gt;registry-1.docker.io&lt;/span&gt;                          &lt;span class=&quot;token number&quot;&gt;0.0s&lt;/span&gt;
 &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; FROM docker&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;io&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;library&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;python&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;3.8&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;buster&lt;span class=&quot;token operator&quot;&gt;@&lt;/span&gt;sha256&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0e41815385213a2be1c61f182f7&lt;/span&gt;  &lt;span class=&quot;token number&quot;&gt;0.0s&lt;/span&gt;
 &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;internal&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; load build context                                                      &lt;span class=&quot;token number&quot;&gt;0.0s&lt;/span&gt;
 &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; transferring context&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;220B&lt;/span&gt;                                                      &lt;span class=&quot;token number&quot;&gt;0.0s&lt;/span&gt;
 &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; CACHED &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; COPY &lt;span class=&quot;token domain constant&quot;&gt;app.py&lt;/span&gt; &lt;span class=&quot;token domain constant&quot;&gt;config.py&lt;/span&gt; &lt;span class=&quot;token domain constant&quot;&gt;database.py&lt;/span&gt; &lt;span class=&quot;token domain constant&quot;&gt;models.py&lt;/span&gt; &lt;span class=&quot;token domain constant&quot;&gt;requirements.txt&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;        &lt;span class=&quot;token number&quot;&gt;0.0s&lt;/span&gt;
 &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; CACHED &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; COPY templates templates&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;                                             &lt;span class=&quot;token number&quot;&gt;0.0s&lt;/span&gt;
 &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; CACHED &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; RUN pip install &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;r &lt;span class=&quot;token domain constant&quot;&gt;requirements.txt&lt;/span&gt;                                   &lt;span class=&quot;token number&quot;&gt;0.0s&lt;/span&gt;
 &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; exporting to image                                                                 &lt;span class=&quot;token number&quot;&gt;0.0s&lt;/span&gt;
 &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; exporting layers                                                                &lt;span class=&quot;token number&quot;&gt;0.0s&lt;/span&gt;
 &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; writing image sha256&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;bd1fffc6a1154c85937d98c320e186afeb8e6a0e39bac848fb1151bdd  &lt;span class=&quot;token number&quot;&gt;0.0s&lt;/span&gt;
 &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; naming to docker&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;io&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;mortimerxyz&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;flaskpostgrestodo&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0.1&lt;/span&gt;                           &lt;span class=&quot;token number&quot;&gt;0.0s&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;To see the built image:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;sh&quot;&gt;&lt;pre class=&quot;language-sh&quot;&gt;&lt;code class=&quot;language-sh&quot;&gt;&lt;span class=&quot;token function&quot;&gt;docker&lt;/span&gt; images &lt;span class=&quot;token parameter variable&quot;&gt;-a&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;log&quot;&gt;&lt;pre class=&quot;language-log&quot;&gt;&lt;code class=&quot;language-log&quot;&gt;REPOSITORY                      TAG       IMAGE ID       CREATED             SIZE
mortimerxyz&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;flaskpostgrestodo   &lt;span class=&quot;token number&quot;&gt;0.1&lt;/span&gt;       bd1fffc6a115   About an hour ago   &lt;span class=&quot;token number&quot;&gt;986MB&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;This run statement passes the host machines environment variables for &lt;code class=&quot;language-text&quot;&gt;APP_SETTINGS&lt;/code&gt; and &lt;code class=&quot;language-text&quot;&gt;DATABASE_URL&lt;/code&gt; to the running Docker container.
This means that to get it running, you will not be able to depend on the &lt;code class=&quot;language-text&quot;&gt;.env&lt;/code&gt; file you have for development.
Since I am using a Windows host, the following PowerShell commands need to run to set the environment variables:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;powershell&quot;&gt;&lt;pre class=&quot;language-powershell&quot;&gt;&lt;code class=&quot;language-powershell&quot;&gt;&lt;span class=&quot;token variable&quot;&gt;$Env&lt;/span&gt;:APP_SETTINGS=&lt;span class=&quot;token string&quot;&gt;&apos;config.Config&apos;&lt;/span&gt;
&lt;span class=&quot;token variable&quot;&gt;$Env&lt;/span&gt;:DATABASE_URL=&lt;span class=&quot;token string&quot;&gt;&quot;postgresql://&amp;lt;username&gt;:&amp;lt;password&gt;@&amp;lt;hostname&gt;:&amp;lt;port&gt;/&amp;lt;database&gt;&quot;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;sh&quot;&gt;&lt;pre class=&quot;language-sh&quot;&gt;&lt;code class=&quot;language-sh&quot;&gt;&lt;span class=&quot;token function&quot;&gt;docker&lt;/span&gt; run &lt;span class=&quot;token parameter variable&quot;&gt;--env&lt;/span&gt; APP_SETTINGS &lt;span class=&quot;token parameter variable&quot;&gt;--env&lt;/span&gt; DATABASE_URL &lt;span class=&quot;token parameter variable&quot;&gt;-p&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;5000&lt;/span&gt;:5000 mortimerxyz/flaskpostgrestodo:0.1&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;log&quot;&gt;&lt;pre class=&quot;language-log&quot;&gt;&lt;code class=&quot;language-log&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token date number&quot;&gt;2023-04-02&lt;/span&gt; &lt;span class=&quot;token time number&quot;&gt;01:40:51 +0000&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token level info keyword&quot;&gt;INFO&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; Starting gunicorn &lt;span class=&quot;token number&quot;&gt;20.1.0&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token date number&quot;&gt;2023-04-02&lt;/span&gt; &lt;span class=&quot;token time number&quot;&gt;01:40:51 +0000&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token level info keyword&quot;&gt;INFO&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token property&quot;&gt;Listening at:&lt;/span&gt; &lt;span class=&quot;token url&quot;&gt;http://0.0.0.0:5000&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token date number&quot;&gt;2023-04-02&lt;/span&gt; &lt;span class=&quot;token time number&quot;&gt;01:40:51 +0000&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token level info keyword&quot;&gt;INFO&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token property&quot;&gt;Using worker:&lt;/span&gt; sync
&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token date number&quot;&gt;2023-04-02&lt;/span&gt; &lt;span class=&quot;token time number&quot;&gt;01:40:51 +0000&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;8&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token level info keyword&quot;&gt;INFO&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token property&quot;&gt;Booting worker with pid:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;8&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 506px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/d57be1474c3285b40f607eb892eca942/61db1/flask-postgres-flask-app-from-docker.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 73.2%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAPCAYAAADkmO9VAAAACXBIWXMAAA7DAAAOwwHHb6hkAAACPUlEQVQ4y6WSy27TQBSG/Qi8CG8BT8CSJQJWbBAIsYEVLGBVVV0WkIAFCyqyqRAIqS0SrUTiJm1D4sSOHV8S2yFpWl/j2P6Q3SK1EoIARzo6M5qjb/5/5ghfxyDqHnXZZKtlstPsIfZspFFAx/XousfUun0a0xz5w3Mqly9QuXOFNWvAjS9TllYfYIorfB+/Z6gsI2zYOVtayOfuIR/bDltti+3ukF1zyp41pTmYUlcHNI/naG+f0L95kd2n17i11uDSyj6Plh8zqd1mKK9gH1xH2FYn1JwZNSdho+NSN4/oD0copo1mOUiqgeGMsSdT/KHBUXePerXK0uobrt59RuXFPfRP99EaqwwaDxGOE/DmEGRwGOcEKSRAOM/x4jleVGSCl2T44zHhoEdkStjNKtt1hXFvh3jUwg8nBL6F0FNkVEXmYL/BLAqxTAPXtomjiH8JoVqtsrG5SaXyDlXrs76+zutXL9F0s2zITxvzPD/JLCszyzLStFin5Hl2ep4h/OqWNE3JsryscTxjPp+fg/5WoeO4uO4Ix3GYTA6ZzWanmRCGEaLYQNetxYGtVgvP88uNqqqI4m4JLBSe2ErL+hP4xzdMkhkDy0TVNPR+v4Tqul6CzsYisBIYhgGmadDpdOmpKlK7TUfqoChy+X5nYQtZLoB9Xaf17RuGYeK6bqmu+IizoIXHJgiC0mJhVZbl8oP+J4QoDEuYJEn0FAVV1VAUBd/3F7Z5Dlg0FyrDMCSOI6IoIvD9c7P3Nwp/AAU4bIyNdmWmAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;flask postgres flask app from docker&quot;
        title=&quot;flask postgres flask app from docker&quot;
        src=&quot;/static/d57be1474c3285b40f607eb892eca942/61db1/flask-postgres-flask-app-from-docker.png&quot;
        srcset=&quot;/static/d57be1474c3285b40f607eb892eca942/0eb09/flask-postgres-flask-app-from-docker.png 500w,
/static/d57be1474c3285b40f607eb892eca942/61db1/flask-postgres-flask-app-from-docker.png 506w&quot;
        sizes=&quot;(max-width: 506px) 100vw, 506px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;h3 id=&quot;Stop-the-running-container&quot; style=&quot;position:relative;&quot;&gt;Stop the running container&lt;a href=&quot;#Stop-the-running-container&quot; aria-label=&quot;Stop the running container permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;sh&quot;&gt;&lt;pre class=&quot;language-sh&quot;&gt;&lt;code class=&quot;language-sh&quot;&gt;&lt;span class=&quot;token function&quot;&gt;docker&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;ps&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;log&quot;&gt;&lt;pre class=&quot;language-log&quot;&gt;&lt;code class=&quot;language-log&quot;&gt;CONTAINER ID   IMAGE                               COMMAND                  CREATED
&lt;span class=&quot;token level info keyword&quot;&gt;STATUS&lt;/span&gt;         PORTS                    NAMES
fd611c4ff1ff   mortimerxyz&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;flaskpostgrestodo&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0.1&lt;/span&gt;   &lt;span class=&quot;token string&quot;&gt;&quot;gunicorn -b 0.0.0.0…&quot;&lt;/span&gt;   &lt;span class=&quot;token number&quot;&gt;6&lt;/span&gt; minutes ago   Up &lt;span class=&quot;token number&quot;&gt;6&lt;/span&gt; minutes   &lt;span class=&quot;token ip-address constant&quot;&gt;0.0.0.0&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;5000&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;5000&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;tcp   nice_mclaren&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;sh&quot;&gt;&lt;pre class=&quot;language-sh&quot;&gt;&lt;code class=&quot;language-sh&quot;&gt;&lt;span class=&quot;token function&quot;&gt;docker&lt;/span&gt; stop fd611c4ff1ff&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;Publish-the-image-to-a-container-registry&quot; style=&quot;position:relative;&quot;&gt;Publish the image to a container registry&lt;a href=&quot;#Publish-the-image-to-a-container-registry&quot; aria-label=&quot;Publish the image to a container registry permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;We now need to push the image to a container registry so we can pull it from our deployment service.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;sh&quot;&gt;&lt;pre class=&quot;language-sh&quot;&gt;&lt;code class=&quot;language-sh&quot;&gt;&lt;span class=&quot;token function&quot;&gt;docker&lt;/span&gt; push mortimerxyz/flaskpostgrestodo:0.1&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 466px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/635a8d965666fb44f718ec8a5bd172f5/68f5c/flask-postgres-dockerhub-publish-image.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 37.553648068669524%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAICAYAAAD5nd/tAAAACXBIWXMAAA7DAAAOwwHHb6hkAAAA/0lEQVQoz51Ri06EMBDk///PmFNPRL3yKlBaWvoe03oguWg0TjLZpruZncwWIQQciQjEiIy4PW6Q5rz3Nwyw1qFITSlVHtJ6RdO2aJo217brQAjB5UpS15nG2m8XJuGi6Xo8PD3j9HjOoj2lKMsSfGZ4qSpQSiGlhFQqV6XUj86d8yg4F7i7P+HtnSB4h2mWaEaFUXgQusC4CGsN0tyqdRbcxMUiIISAMfZLcMskwZoVI9eo54Cq13ilFsoECM7RtR3YPGNiExhjmK6kw5AX7IJH+8451JcaixD4D3aHm2gKdaAjxCI/rxljOnru/8ZdMJ16Y3KYeimC4/9fmbL8ADdrcBfFmsUsAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;flask postgres dockerhub publish image&quot;
        title=&quot;flask postgres dockerhub publish image&quot;
        src=&quot;/static/635a8d965666fb44f718ec8a5bd172f5/68f5c/flask-postgres-dockerhub-publish-image.png&quot;
        srcset=&quot;/static/635a8d965666fb44f718ec8a5bd172f5/68f5c/flask-postgres-dockerhub-publish-image.png 466w&quot;
        sizes=&quot;(max-width: 466px) 100vw, 466px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;h3 id=&quot;Use-the-Docker-image-to-deploy-application&quot; style=&quot;position:relative;&quot;&gt;Use the Docker image to deploy application&lt;a href=&quot;#Use-the-Docker-image-to-deploy-application&quot; aria-label=&quot;Use the Docker image to deploy application permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;We are going to use a basic EC2 machine to host the web application.
To enable us to SSH to the machine we will need to add a firewall rule to allow SSH over port 22 to our public IP address:&lt;br&gt;
&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 804px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/d9f6927f21155c47abffd8267eef99c8/0c082/flask-postgres-aws-ec2-sgr-localip.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 22.2%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAECAIAAAABPYjBAAAACXBIWXMAAAsSAAALEgHS3X78AAAAmElEQVQI103O2woDMQgE0P3/v2yhUNhcjDExRs2WTSn0MC8yDHgQUUgJMgCAql7X5e6IdYiu5V+qxswxpUK9MfcdHuMoiGcIGRsPMbPvOMSIiJVF1cxsztlaP2PKtfX/MRJFAKiERJWoIA4ZAFAKqrmZubtMacy1sZiru7mv/dXhP7ZzW2td27r5fS0SfYTyyvV5whu77+oDRvPoAmtbBTkAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;flask postgres aws ec2 sgr localip&quot;
        title=&quot;flask postgres aws ec2 sgr localip&quot;
        src=&quot;/static/d9f6927f21155c47abffd8267eef99c8/0c082/flask-postgres-aws-ec2-sgr-localip.png&quot;
        srcset=&quot;/static/d9f6927f21155c47abffd8267eef99c8/0eb09/flask-postgres-aws-ec2-sgr-localip.png 500w,
/static/d9f6927f21155c47abffd8267eef99c8/0c082/flask-postgres-aws-ec2-sgr-localip.png 804w&quot;
        sizes=&quot;(max-width: 804px) 100vw, 804px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;We will also need to add the public IP address of the EC2 machine to the firewall for the GCP machine that is hosting the Postgres database service:&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 675px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/8a869cb906928d3a836e9a2875b13a81/a0bb6/flask-postgres-aws-ec2-public-ip.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 34.800000000000004%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAHCAIAAACHqfpvAAAACXBIWXMAAAsSAAALEgHS3X78AAABLklEQVQY0y3Q0W6EIBAFUP//27pPbR/WjeuigCDgKjMMiFoabZN5uMnk5kymGtwyLZ47L90yvedeSDkoOSguB0AkokCEIQSiEMJ/IAIAD1ABQF0/GGOcC8Y6zgXnvK4fTfNkrLvXj2vbPZ8tY52QQ/Ns29eLiGKMFQB+3G7fj1a7ZVkWN02nhpcRCLxHOH1EDIh4ih4Acs5XGZELoUcj5NALORo7GqtGY4yz1vlAbl70aGYPad+2n59ztv2/fPIxpXVNKcUYj/2IKZ0wxUAxKg0vNjdN5OKwtjhb3tOe834c65qqQCQHNSjtAQIG7+F6TYg5F63N55dmzCo19nxW9q2su7cb60opOeeKKI6jUUpb5+KF/52Q1rUgpr5PnK+cr0KS1EEo1PbIWyll27Zf2AaH/fpT/nMAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;flask postgres aws ec2 public ip&quot;
        title=&quot;flask postgres aws ec2 public ip&quot;
        src=&quot;/static/8a869cb906928d3a836e9a2875b13a81/a0bb6/flask-postgres-aws-ec2-public-ip.png&quot;
        srcset=&quot;/static/8a869cb906928d3a836e9a2875b13a81/0eb09/flask-postgres-aws-ec2-public-ip.png 500w,
/static/8a869cb906928d3a836e9a2875b13a81/a0bb6/flask-postgres-aws-ec2-public-ip.png 675w&quot;
        sizes=&quot;(max-width: 675px) 100vw, 675px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;First we will start with pulling the Docker image down from where we published it to Docker hub.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;sh&quot;&gt;&lt;pre class=&quot;language-sh&quot;&gt;&lt;code class=&quot;language-sh&quot;&gt;&lt;span class=&quot;token function&quot;&gt;docker&lt;/span&gt; pull mortimerxyz/flaskpostgrestodo:0.1&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;log&quot;&gt;&lt;pre class=&quot;language-log&quot;&gt;&lt;code class=&quot;language-log&quot;&gt;&lt;span class=&quot;token number&quot;&gt;0.1&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; Pulling from mortimerxyz&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;flaskpostgrestodo
&lt;span class=&quot;token number&quot;&gt;4e2befb7f5d1&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; Pull complete
&lt;span class=&quot;token number&quot;&gt;792af667f626&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; Pull complete
&lt;span class=&quot;token number&quot;&gt;3e37868ebf66&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; Pull complete
&lt;span class=&quot;token number&quot;&gt;591fe17e35dd&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; Pull complete
&lt;span class=&quot;token property&quot;&gt;b9cba6e3073a:&lt;/span&gt; Pull complete
&lt;span class=&quot;token number&quot;&gt;8d8bcef47b1e&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; Pull complete
&lt;span class=&quot;token number&quot;&gt;920891d7496a&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; Pull complete
&lt;span class=&quot;token property&quot;&gt;a2b9eb6d3f8d:&lt;/span&gt; Pull complete
&lt;span class=&quot;token property&quot;&gt;eb27f9541dc2:&lt;/span&gt; Pull complete
&lt;span class=&quot;token number&quot;&gt;579045be23fb&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; Pull complete
&lt;span class=&quot;token number&quot;&gt;77f0ef020355&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; Pull complete
&lt;span class=&quot;token number&quot;&gt;96bf4b83abf5&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; Pull complete
&lt;span class=&quot;token property&quot;&gt;Digest:&lt;/span&gt; sha256&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token hash constant&quot;&gt;b020a3438a47ee535bb78afec1f9ab590cf73b66f74b2b94c469d84dfca93b14&lt;/span&gt;
&lt;span class=&quot;token property&quot;&gt;Status:&lt;/span&gt; Downloaded newer image for mortimerxyz&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;flaskpostgrestodo&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0.1&lt;/span&gt;
docker&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;io&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;mortimerxyz&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;flaskpostgrestodo&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0.1&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Since we are now using a Linux host, the following Bash commands need to run to set the environment variables:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;sh&quot;&gt;&lt;pre class=&quot;language-sh&quot;&gt;&lt;code class=&quot;language-sh&quot;&gt;&lt;span class=&quot;token builtin class-name&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token assign-left variable&quot;&gt;APP_SETTINGS&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;config.Config
&lt;span class=&quot;token builtin class-name&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token assign-left variable&quot;&gt;DATABASE_URL&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;postgresql://&amp;lt;username&gt;:&amp;lt;password&gt;@&amp;lt;hostname&gt;:&amp;lt;port&gt;/&amp;lt;database&gt;&quot;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;We need to run the Docker container in detached mode:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;sh&quot;&gt;&lt;pre class=&quot;language-sh&quot;&gt;&lt;code class=&quot;language-sh&quot;&gt;&lt;span class=&quot;token function&quot;&gt;docker&lt;/span&gt; run &lt;span class=&quot;token parameter variable&quot;&gt;--detach&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;--env&lt;/span&gt; APP_SETTINGS &lt;span class=&quot;token parameter variable&quot;&gt;--env&lt;/span&gt; DATABASE_URL &lt;span class=&quot;token parameter variable&quot;&gt;-p&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;5000&lt;/span&gt;:5000 mortimerxyz/flaskpostgrestodo:0.1&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;This EC2 machine already has Nginx installed and an associated SSL certificate and a domain name (this is outside the scope of this blog post).&lt;/p&gt;
&lt;p&gt;We need to configure a server rule to proxy pass the service running on port 5000 to the SSL port 443 in the default Nginx configuration file (&lt;code class=&quot;language-text&quot;&gt;/etc/nginx/sites-available/default&lt;/code&gt;).&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;ini&quot;&gt;&lt;pre class=&quot;language-ini&quot;&gt;&lt;code class=&quot;language-ini&quot;&gt;location / {
  proxy_pass http://localhost:5000;
  proxy_redirect http://localhost:5000/ $scheme://$http_host/;
  proxy_http_version 1.1;
  proxy_set_header Upgrade $http_upgrade;
  proxy_set_header Connection $connection_upgrade;
  proxy_read_timeout 20d;
}&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Now when we browse to the domain name we get the running Flask application.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 629px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/440f424816ff549f308e87c391544710/7e5f2/flask-postgres-aws-docker-flask.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 72.8%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAPCAIAAABr+ngCAAAACXBIWXMAAAsSAAALEgHS3X78AAABx0lEQVQoz6WQT2vUQBjG8xH8AH4jb3oSRJDevHrzsh48FLxY6UkK0pOgYKFFaJGlVWvLhma3UnfzZzabzUz+TDJJNtm6yc4kMyPbevKUxYeH9/T8eJ/3Vc4xv9D0k3HUM+GZPv0VFmZSgmQxRHEPpjpy+4/uHt+78+nj244rH777ou09ifwDDLfj8QvlNGzOUHXsFD+cdIDSn142wnMrvrbCzMxKOLlEm4/R1sab95/v75oPOh+M7tPo6jWxnqfDDUXz5ldJrbq56s68dO6GcZDkOCsgJjb0HWfqDy+Dsf716LCzufPy1TY+eWae70bmDjG3FCFlIyUVqymkXDaiZLxkzYI2C9aUS1ZGHkt9GoynLsYBamY2pb8pW1B6rRzs759+/0biSLtQB4M+Y0y2lqL1+xYAMUlUtdftduuG/xsR4tZcCMG5FPym4soK55xSyur6NpllMwh9IUSrzXFMllWJEHIh5JxnWe77YdvaeT7L8xwhOByNimIu15ESBIGu664LAbB0wyCEcM5bwyEGlgWhSwihlFZV1fLgFYw837ZtACwcxTevFWvURggZpjGZTJypYwHAWL0GHOEIeQhBSNfB/sLyP/QHCqVHaZZy0NUAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;flask postgres aws docker flask&quot;
        title=&quot;flask postgres aws docker flask&quot;
        src=&quot;/static/440f424816ff549f308e87c391544710/7e5f2/flask-postgres-aws-docker-flask.png&quot;
        srcset=&quot;/static/440f424816ff549f308e87c391544710/0eb09/flask-postgres-aws-docker-flask.png 500w,
/static/440f424816ff549f308e87c391544710/7e5f2/flask-postgres-aws-docker-flask.png 629w&quot;
        sizes=&quot;(max-width: 629px) 100vw, 629px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Connecting to Teradata from Python]]></title><description><![CDATA[Reading and Writing data between Python and Teradata This blog post is paired with the associated Github repository https://github.com…]]></description><link>https://mortimer.xyz/python-teradata/</link><guid isPermaLink="false">https://mortimer.xyz/python-teradata/</guid><pubDate>Sat, 25 Mar 2023 12:00:00 GMT</pubDate><content:encoded>&lt;h1 id=&quot;Reading-and-Writing-data-between-Python-and-Teradata&quot; style=&quot;position:relative;&quot;&gt;Reading and Writing data between Python and Teradata&lt;a href=&quot;#Reading-and-Writing-data-between-Python-and-Teradata&quot; aria-label=&quot;Reading and Writing data between Python and Teradata permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h1&gt;
&lt;p&gt;This blog post is paired with the associated Github repository &lt;a href=&quot;https://github.com/mortie23/python-teradata&quot;&gt;https://github.com/mortie23/python-teradata&lt;/a&gt;&lt;/p&gt;
&lt;h2 id=&quot;Local-Python-environment-setup&quot; style=&quot;position:relative;&quot;&gt;Local Python environment setup&lt;a href=&quot;#Local-Python-environment-setup&quot; aria-label=&quot;Local Python environment setup permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;For this run through I am using:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Python 3.8.10&lt;/li&gt;
&lt;li&gt;installed on Windows Subsystem for Linux (Ubuntu 20.04)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Create a new virtual environment and install the requirements.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;sh&quot;&gt;&lt;pre class=&quot;language-sh&quot;&gt;&lt;code class=&quot;language-sh&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;# Create a new virtual environment&lt;/span&gt;
python3 &lt;span class=&quot;token parameter variable&quot;&gt;-m&lt;/span&gt; venv tdsql
&lt;span class=&quot;token comment&quot;&gt;# Activate the environment&lt;/span&gt;
&lt;span class=&quot;token builtin class-name&quot;&gt;source&lt;/span&gt; tdsql/bin/activate
&lt;span class=&quot;token comment&quot;&gt;# Install the packages required&lt;/span&gt;
pip &lt;span class=&quot;token function&quot;&gt;install&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-r&lt;/span&gt; requirements.txt
&lt;span class=&quot;token comment&quot;&gt;# Register the IPython Kernel for use (with VScode)&lt;/span&gt;
python3 &lt;span class=&quot;token parameter variable&quot;&gt;-m&lt;/span&gt; ipykernel &lt;span class=&quot;token function&quot;&gt;install&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;--user&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;--name&lt;/span&gt; tdsql --display-name tdsql&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2 id=&quot;Teradata&quot; style=&quot;position:relative;&quot;&gt;Teradata&lt;a href=&quot;#Teradata&quot; aria-label=&quot;Teradata permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;I am using a Teradata Express 16.20 virtual machine running with VMWare Workstation Player 16.2.5.
Our hosts file is mapping the virtual machine’s IP address to &lt;code class=&quot;language-text&quot;&gt;tdvm&lt;/code&gt;&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;sh&quot;&gt;&lt;pre class=&quot;language-sh&quot;&gt;&lt;code class=&quot;language-sh&quot;&gt;&lt;span class=&quot;token function&quot;&gt;cat&lt;/span&gt; /etc/hosts
&lt;span class=&quot;token comment&quot;&gt;# This file was automatically generated by WSL. To stop automatic generation of this file, add the following entry to /etc/wsl.conf:&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;# [network]&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;# generateHosts = false&lt;/span&gt;
&lt;span class=&quot;token number&quot;&gt;127.0&lt;/span&gt;.0.1       localhost
&lt;span class=&quot;token number&quot;&gt;192.168&lt;/span&gt;.190.128 tdvm&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 707px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/6df1fd32cf025235eb84d474869a6481/e9ffe/teradata-vm.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 74.2%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAPCAIAAABr+ngCAAAACXBIWXMAAAsSAAALEgHS3X78AAADEElEQVQoz3WQWW8SYRSGzwwwnQGHWWBWBgaGlrq1WktjFRCoCwqltFC8aTWxi4nLjQaIFzbxD3jhnYlN1LhEGzXd0Fq01cZabbGhi/HGpDap/g4jo8Ybn5ycfBffk7zvgWQsEIuE4tGDbeFg7HCkLRyKHgpEQwejocChQGskeCASPBAOtvr3NjRXx69PU+P+Fj+kLxROnLnU2X8pde5q+uJQYqDQ3p8/3pdLDuZSZ/Odg4XUYCE5kO8YLHQM5JP9uWR/rr3vctfZ/PkrQ3B7au7B6/V7M58fzqzff7U6MvdlbHFjqvL9zfrW3PrW9MqPl5XvxfLmZHmzWN6cWPw29nFj7MPX8cWNpx9WId6dbWnwxdpC2c5EKn6kM3Es25XIdBzPpOKpRCzTcaIrGUsnY6n40eD+plDrvnCgxb9357WhXHFxGVw7GgAAwwlJdtg53qE4BUmmKJphbXaOpxnWWn2TpLUGJwAANaAAkM0mi58q4N61BwAYhtGq1FVRVdXr9fI8z7IMx9lFUdQ3TdM4jgNAb2/3i+UV8O7ZBwA4QVAUxVYhSZKmabPZjKIoXoUgCBzHsSomk+mX3JMpVdZ+ywzLut1uj8cjiqKiKJqmOZ1OhmH0r/+ix+7tyUwsr/yNzXq9Xj2t2+0WRVHTNJfLJcsywzDmKhaLxWg0ougvuacn83jpT2eaomu9tR6PVu+r1zsLgqAoCs/zkiTJssxxnCRJepffcrkC7t2NAECYCRvPCpKdF+w2m42mGZIkMQyjKMpqtZIkieO4xWLBMAxBEF0eKa9AY7SF5MFZx3MOKyUQ21gMEPgfCIIYjQYAOH2q+8mnCvgjEc6+TZIEnrcpimi3MyajEcdrDAbUZDSgCELgNdg/Z9Njx7vS9xYqED7ZXtes1jX7PI2ar7ne17TdVVsvqxqvqLKqCQ6XQ9WcqkeUHYLs5B0u0alaGTbdd/HOwhoUZ2efz7yaX1oqvXl998Gt0fHHpZnxl6XR6dLk2/cLxanp4fsjw8M3io+uTz67Oftudr689HZhfnmtMjH9/CeP5ta/cY1aoQAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;teradata vm&quot;
        title=&quot;teradata vm&quot;
        src=&quot;/static/6df1fd32cf025235eb84d474869a6481/e9ffe/teradata-vm.png&quot;
        srcset=&quot;/static/6df1fd32cf025235eb84d474869a6481/0eb09/teradata-vm.png 500w,
/static/6df1fd32cf025235eb84d474869a6481/e9ffe/teradata-vm.png 707w&quot;
        sizes=&quot;(max-width: 707px) 100vw, 707px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;h3 id=&quot;Setup-database-structures&quot; style=&quot;position:relative;&quot;&gt;Setup database structures&lt;a href=&quot;#Setup-database-structures&quot; aria-label=&quot;Setup database structures permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;This step is obviously not required, however it is here for completeness sake.
Running the &lt;code class=&quot;language-text&quot;&gt;teradata-setup.sql&lt;/code&gt; script to create the required database and provide access to the appropriate user.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;sql&quot;&gt;&lt;pre class=&quot;language-sql&quot;&gt;&lt;code class=&quot;language-sql&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;-- Create Database&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;-- Assumes you have a production analytical datastore (ADS)&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;CREATE&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;DATABASE&lt;/span&gt; PRD_ADS_PYTHON &lt;span class=&quot;token keyword&quot;&gt;FROM&lt;/span&gt; PRD_ADS &lt;span class=&quot;token keyword&quot;&gt;AS&lt;/span&gt;
   PERM &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;
   &lt;span class=&quot;token keyword&quot;&gt;NO&lt;/span&gt; FALLBACK
   &lt;span class=&quot;token keyword&quot;&gt;NO&lt;/span&gt; BEFORE JOURNAL
   &lt;span class=&quot;token keyword&quot;&gt;NO&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;AFTER&lt;/span&gt; JOURNAL
&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;COMMENT&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;ON&lt;/span&gt; PRD_ADS_PYTHON &lt;span class=&quot;token keyword&quot;&gt;AS&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;Python development&apos;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;GRANT&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;ALL&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;ON&lt;/span&gt; PRD_ADS_PYTHON &lt;span class=&quot;token keyword&quot;&gt;TO&lt;/span&gt; PRDDBA &lt;span class=&quot;token keyword&quot;&gt;WITH&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;GRANT&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;OPTION&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;CREATE&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;DATABASE&lt;/span&gt; PRD_ADS_PYTHON_NFL_DB &lt;span class=&quot;token keyword&quot;&gt;FROM&lt;/span&gt; PRD_ADS_PYTHON &lt;span class=&quot;token keyword&quot;&gt;AS&lt;/span&gt;
   PERM &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;
   &lt;span class=&quot;token keyword&quot;&gt;NO&lt;/span&gt; FALLBACK
   &lt;span class=&quot;token keyword&quot;&gt;NO&lt;/span&gt; BEFORE JOURNAL
   &lt;span class=&quot;token keyword&quot;&gt;NO&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;AFTER&lt;/span&gt; JOURNAL
&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;COMMENT&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;ON&lt;/span&gt; PRD_ADS_PYTHON_NFL_DB &lt;span class=&quot;token keyword&quot;&gt;AS&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;NFL Data loaded using Python&apos;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;GRANT&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;ALL&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;ON&lt;/span&gt; PRD_ADS_PYTHON_NFL_DB &lt;span class=&quot;token keyword&quot;&gt;TO&lt;/span&gt; PRDDBA &lt;span class=&quot;token keyword&quot;&gt;WITH&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;GRANT&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;OPTION&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;-- Give Space&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;-- Python&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;CREATE&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;DATABASE&lt;/span&gt; GS_TEMP &lt;span class=&quot;token keyword&quot;&gt;FROM&lt;/span&gt; PRD_ADS &lt;span class=&quot;token keyword&quot;&gt;AS&lt;/span&gt;
PERM &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;E9
&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
GIVE GS_TEMP &lt;span class=&quot;token keyword&quot;&gt;TO&lt;/span&gt; PRD_ADS_PYTHON
&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;DROP&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;DATABASE&lt;/span&gt; GS_TEMP
&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;-- Python NFL&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;CREATE&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;DATABASE&lt;/span&gt; GS_TEMP &lt;span class=&quot;token keyword&quot;&gt;FROM&lt;/span&gt; PRD_ADS_PYTHON &lt;span class=&quot;token keyword&quot;&gt;AS&lt;/span&gt;
PERM &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;5&lt;/span&gt;E6
&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
GIVE GS_TEMP &lt;span class=&quot;token keyword&quot;&gt;TO&lt;/span&gt; PRD_ADS_PYTHON_NFL_DB
&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;DROP&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;DATABASE&lt;/span&gt; GS_TEMP
&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;-- Users&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;CREATE&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;USER&lt;/span&gt; MORTCH &lt;span class=&quot;token keyword&quot;&gt;FROM&lt;/span&gt; PRD_USERS &lt;span class=&quot;token keyword&quot;&gt;AS&lt;/span&gt;
  PASSWORD &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; MORTCH
  PERM &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;NO&lt;/span&gt; Fallback
  &lt;span class=&quot;token keyword&quot;&gt;NO&lt;/span&gt; Before Journal &lt;span class=&quot;token keyword&quot;&gt;NO&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;After&lt;/span&gt; Journal
  &lt;span class=&quot;token keyword&quot;&gt;DEFAULT&lt;/span&gt; ROLE &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;ALL&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;-- Roles&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;CREATE&lt;/span&gt; ROLE R_PYTHON_PRD&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;GRANT&lt;/span&gt; R_PYTHON_PRD &lt;span class=&quot;token keyword&quot;&gt;TO&lt;/span&gt; MORTCH&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;-- Python&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;GRANT&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;SELECT&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;INSERT&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;UPDATE&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;DELETE&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;CREATE&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;TABLE&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;DROP&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;TABLE&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;ON&lt;/span&gt; PRD_ADS_PYTHON_NFL_DB &lt;span class=&quot;token keyword&quot;&gt;TO&lt;/span&gt; R_PYTHON_PRD &lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;GRANT&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;SELECT&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;CREATE&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;VIEW&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;DROP&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;VIEW&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;ON&lt;/span&gt; PRD_ADS_PYTHON_NFL_DB &lt;span class=&quot;token keyword&quot;&gt;TO&lt;/span&gt; R_PYTHON_PRD &lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;-- Test&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;CREATE&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;TABLE&lt;/span&gt; PRD_ADS_PYTHON_NFL_DB&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;TEST &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
  COL1 &lt;span class=&quot;token keyword&quot;&gt;INTEGER&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;PRIMARY&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;INDEX&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;COL1&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;INSERT&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;INTO&lt;/span&gt; PRD_ADS_PYTHON_NFL_DB&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;TEST &lt;span class=&quot;token keyword&quot;&gt;VALUES&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;Setup-the-table-structures&quot; style=&quot;position:relative;&quot;&gt;Setup the table structures&lt;a href=&quot;#Setup-the-table-structures&quot; aria-label=&quot;Setup the table structures permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Firstly test connecting to it from Teradata Studio IDE:&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 585px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/379ac6ab4ff910bc938baf77135d5435/51d84/teradata-studio-connection.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 102.8%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAVCAIAAADJt1n/AAAACXBIWXMAAAsSAAALEgHS3X78AAADZklEQVQ4y2WSy1IbRxSG9SyIla0CAes4lmQYSdaMpLl29/T03HruGiEFsMsWIGGZQITATvkBHOIk+zi4clvzDMQ4VCps7A1Q+AlSkhxTglPf4lR1f/X35SRO3x69O/nn9N/Td8d/n5yc/HV0dPz2+Oz8/OLi4uz8qi4H9fHy8uPZ2fn7Dx/OLy4ODw8T8Nmfcv83u9FaXll5tNZ5vNZudTba3aet9ka7u7m68WS18+TRWtuLYtP1Ldf3ojhuLi0uPbBcP8GwfLEsZrK5bDYDMEGGhXQT6QbQdKSbCia2H273+pvb33S3trqbW1/3djpPN0Wk8gAlymyRu1/IZjPz8/cUVSM21UwbD1ENk1iOG9S2ejs7e896u7u9/u7u82+3e31F03mAE6lUKnU7dfvWrVQqJQBoOJ41PJ7heCJUDeph3S5wfIGrFji+yPEFtppnq6KCRagmpqam0sOamZkZ7HZ9YlMJYRGgiqTYfhjU6opKZJXISJMgliAWIVaG/UCeHlY6nRYA1CxKLIpNa9Q8f/Fi/9UPL7/bf7n//asff3p98Ob1wZuffzn49fc/osXmmCxB1Ytig7oSwgJAAkC67XzGsF2Teib1DNulXqhgciVPp9Mywl4Uq4ZFLCpCtSwpZVHmBJETJE4QWUFk+SsqknI9mROk0dpQkESoQmICzfgffQQkhgjQWLKiajp1NZMiYglAZUplplTJD5+XKVVYXtKpY3sh9UPDcSExrsvYpCLAIsB5tppZKN6dL4z48l5+vshZrk/9gPqBE4RoTJ6eBpiwvPxFjrm7MBAyC8Usc39EZqHIlCqm4wVxPVps0CCEN2VOkO/kmE9pg8BP3MkxuXwpqDfi5ldxc6m5vAKJfl2uypAplvOlyhjsgFJVdMPYjWpeVA/ixpicHn4VNm3tBtiwFEwUTCSER8iqNhjP0WDOzs7Ozc2JAFE/8qPYDWteWAviuhNGXhQ7Qcgr8BpVGSSSyeTk5OTExEQymeRlIAB0cx+vwIoMKjKojpPodDrr6+v9fn9vb8+wnYqkCDdDJGUELwPqh36tHtTqbhQlVjvdxvKDh621h63Vwd1UDWAyjk6cUHdruhfpXkT9cHiRyKBuQoSYKXF5tpxny2VRrkjKNcqSAi2PuJFKA2T7SDdNx1MNS0L4P2Na1cZhekGQAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;teradata studio connection&quot;
        title=&quot;teradata studio connection&quot;
        src=&quot;/static/379ac6ab4ff910bc938baf77135d5435/51d84/teradata-studio-connection.png&quot;
        srcset=&quot;/static/379ac6ab4ff910bc938baf77135d5435/0eb09/teradata-studio-connection.png 500w,
/static/379ac6ab4ff910bc938baf77135d5435/51d84/teradata-studio-connection.png 585w&quot;
        sizes=&quot;(max-width: 585px) 100vw, 585px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;Running this (&lt;code class=&quot;language-text&quot;&gt;python-nfl-ddl.sql&lt;/code&gt;) SQL script in you favourite SQL IDE (for example Teradata Studio).&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 226px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/6c980dab7900f91610b16d0d163888d0/9e144/teradata-tables.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 164.60176991150442%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAhCAYAAADZPosTAAAACXBIWXMAAA7DAAAOwwHHb6hkAAAEVUlEQVRIx51Wa3PaVhDlz9RPEOiBJISkK+nqgRBQbEPSuHZrOxDsYFzTuDOd6eRr//bp7MpgO8EO0w87F/Q4d8+e3XNV0Y0mZODhy3SMvJOh0+nAcRzESYogEBBBgDSl3+VqWRZqtRrq9frGqCj1OjzHxvzT7wjCCFLG8DwPkZQQQkAIH+IROI7jLQCVGhqqir2DGr7eDfHhOEEkU3S7XXSyDE1Dx8HBAarVKq8rMEVRXgNUoKkqdN3A5DRFPw/giwC+78F1Xei6xg82Go0yHl98G1DToGo67icF3h2liJMMaZIgSVI06goODw9fxFtZrgE13cBy0sdJXyIIJeJYQkrJQoRhiCRJEMe0SQzTbL5aRwZUVRWqquHjLzF6mYAnAlbV932mTSIRaBCEiMIQzaaxzu7bLF9k+Pd8iPHPEmnWxWDQh9tuY3d3h8XY399fR0m7+oL6CngtCtVwdpah3xFw2h48111n2Gq10GrZaLfbsEwTtm1zGIb+eoYEuLjs4mQQc9uEgWCavV4fRVGg280h45jbKc87fD0KAyi1Gqu/BqT+KltCw8P1gEXx/IBFoFrt7u4yzb29/We0yxKs+nOlfLVaQ8V1Hfi+y5MwP09xPu5geHSE4XCANIkRRQHCMGB1oyjkEIHPazmaPmfabjtw3TYq/X6PBRiPRvj3nyWWy3vMbuaYTq4wu77G3WKO2WyK+z8W+Dz/jOX9HW4XCzw8LHF9/Ymv3d3ecCkIp2LbJmzLhGU14QmB8/c5bi6HXK+iyJGmCdfTbBrgZ20LjmOj1bJYKMdp8bXynokKcacgcQ6rdRx1PUx/zdB2fWRZxo3tth2u1epZqlV1tT67TlFZqaOSMKqB38YJvsyGiOKU7Yymo2Vb/OKq35733XeTsmpMHvqGhl7m4vRYouW4bF80JVRsXdc5DMPgoFZ7AlU2ADbqUOoazk4kltM+RCDZvsgkyHQpqB8HgwH6/T6kjDjrjRmWoDVousl+ODmlGgoWhAx2NWabKH83KU9/FCh1FUeFwMU4Qij8cjqk5NHcVK9NoJUXu5FS9dJsv/55ijSNmepoNIJlmevJeNMPv92RCm9bTZyNMwjhsUFQlmQGZBIk0nP7+iGgpulo2SbuZqN1hjRNQRiWv3s9eJ7LNd8KkOgS4O3VAFKGfNLleQ7P9/kkpE1WAv1AlPImuTdZ/OX5EGFYWhhPi+ui7brs5LqmoU613y5DHU7Lwl+375FlCfJOh3vP8wWKoovh0TGvNFlbiUI1tMwmLj7kiEI66AMWhfxRVRuP54+6vco0UqTy7eQYSRIx3fLEi/k+tc1qrreqIYlClBeTIWQkkKQZ0/Y8n0evKHooegUC4b89ek+iaEz56mzATk50oyhiMeiQol6kVdPULSnrOtvV8uYdkkRyy2ScoYudnZ/+D+VyUqZnOeI45M8Rap2YXSd7cXRulSEpSJQ/XpywRZFrM+UwYvchwLc+5/4D59lPEkk4pp4AAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;teradata tables&quot;
        title=&quot;teradata tables&quot;
        src=&quot;/static/6c980dab7900f91610b16d0d163888d0/9e144/teradata-tables.png&quot;
        srcset=&quot;/static/6c980dab7900f91610b16d0d163888d0/9e144/teradata-tables.png 226w&quot;
        sizes=&quot;(max-width: 226px) 100vw, 226px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;h2 id=&quot;Python-execution-in-VSCode&quot; style=&quot;position:relative;&quot;&gt;Python execution in VSCode&lt;a href=&quot;#Python-execution-in-VSCode&quot; aria-label=&quot;Python execution in VSCode permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;I’m going to using Python interactive in VSCode to run this. In this mode we can run the “Cells” (blocks of code between &lt;code class=&quot;language-text&quot;&gt;# %%&lt;/code&gt;) separately and view tabular results in a more visual way.&lt;/p&gt;
&lt;p&gt;For example, running the &lt;code class=&quot;language-text&quot;&gt;python-teradata.sql&lt;/code&gt; script up until this point and then in the interactive window typing &lt;code class=&quot;language-text&quot;&gt;tables&lt;/code&gt; and hitting &lt;code class=&quot;language-text&quot;&gt;shift-enter&lt;/code&gt;.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;py&quot;&gt;&lt;pre class=&quot;language-py&quot;&gt;&lt;code class=&quot;language-py&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;# %%&lt;/span&gt;
query &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string-interpolation&quot;&gt;&lt;span class=&quot;token string&quot;&gt;f&quot;&quot;&quot;
select
  TableName
  , count(*) as ColumnCount
from
  dbc.columnsv
where
  databasename=&apos;&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;database&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;
group by
  TableName
order by
  TableName
&quot;&quot;&quot;&lt;/span&gt;&lt;/span&gt;
tables &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; pd&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;read_sql&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;query&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; con&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;# %%&lt;/span&gt;
tables&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 317px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/9643732789a2393ab4acd85495fbdd33/66d24/python-nfl-tables.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 108.83280757097793%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAWCAYAAADAQbwGAAAACXBIWXMAAA7DAAAOwwHHb6hkAAACvUlEQVQ4y41V2W7TUBDtTyABTZrdW7zEjrPatZs0adIkJWoRiAdUCqUVFYj/fzvojO3UWZDyMLrXnnvPzJk5Y5+c1RyU6q2NlRvuxvLvj7WT3RenFSuxsoXdYEcBZpe4MqsgvkYQzxFGU1TVNoo5/zF2UspRKysuzFYI0w1huQEqind0lhvAut5Bue6ionuoNj3UtC5qehfvzpooVG0Uq6/Ri7ln7vOZbSiTJl80jA6UZles6Qxh2AMx3eqjoSc+PmtmD3Xdl/dqer6m+a+AGS2CuJ1IqE5ma7z8/ovnX3/w8OMZX+8fEUQzrG+/4PHpBd++P+P+4SfuH57EbC9EoWK/NoXpMyKjVxUPNbWNmtaWDPz+CP1wKnsGZzbMkH6eYyLcZ7RFNgTUUgqMVKhYeFs0UFU9DKMZovFCQHbrxrOK0RU17AEyUlaXMJ5hvrzD8PxKLL5cwmwNoRgd8Uvt0n14MZeVGBtAoqtmTxwN3Reag3AKtxMnlINJ0gytLf56atzzDBta3KNs9sTxvmRCt/u4mKwQjRZw2ucYT2/Ex1JsU7YkkQOUnZRGF3XNF5B2byRry49kpTxYU2lGanxmmQ42hZdMZygAN+vPWH74hDCe43r1EVfXt7BawVYNs/JQamS3VcOkywnlvOoZhJbJJpv3ciPxVxQXlheinPZhC1AmgsWV8Uqk4/iRUB+cX0mtEsCWACSAnjSldAiQE0JpcCJmi1uZFkpmOl9jNFnBdgOhuEuZDNR9yo7UIZtXRvU6saydwRh+byQANdUT6bAJsqoeDGew3xQqng5eomxI982pJmK/nK1xcbkU3fHcrmx0e3Coy0mGmXhppMLs2EVmKPJQ24moc/Ns2H3Z79WQ9WNGlAZrE48XWKw+Sv3mizsBJlC+hjwrGs1PytnOJz6TTEH+K+Zm/d+XO3+PX+x/IWSga7osbhMAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;python nfl tables&quot;
        title=&quot;python nfl tables&quot;
        src=&quot;/static/9643732789a2393ab4acd85495fbdd33/66d24/python-nfl-tables.png&quot;
        srcset=&quot;/static/9643732789a2393ab4acd85495fbdd33/66d24/python-nfl-tables.png 317w&quot;
        sizes=&quot;(max-width: 317px) 100vw, 317px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;h2 id=&quot;Loading-data-from-CSV&quot; style=&quot;position:relative;&quot;&gt;Loading data from CSV&lt;a href=&quot;#Loading-data-from-CSV&quot; aria-label=&quot;Loading data from CSV permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The key function that loads data to a table from a CSV file is here:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;py&quot;&gt;&lt;pre class=&quot;language-py&quot;&gt;&lt;code class=&quot;language-py&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;insert_rows_from_csv&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
    tablename&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    columncount&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token triple-quoted-string string&quot;&gt;&quot;&quot;&quot;Insert rows from a CSV into a Teradat table

    Args:
        tablename (str): the name of the table
        columncount (int): the number of columns in the table
    &quot;&quot;&quot;&lt;/span&gt;
    columncount_questionmark &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;, ?&quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;columncount &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    exec_string &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string-interpolation&quot;&gt;&lt;span class=&quot;token string&quot;&gt;f&quot;{{fn teradata_read_csv(./data/&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;tablename&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;lower&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;.csv)}}insert into &lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;tablename&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt; (?&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;columncount_questionmark&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;)&quot;&lt;/span&gt;&lt;/span&gt;
    cur&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;execute&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;exec_string&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The script then has a function that loops over all the tables and runs the &lt;code class=&quot;language-text&quot;&gt;insert_rows_from_csv&lt;/code&gt; function. After the loads have completed, running a select using the Pandas &lt;code class=&quot;language-text&quot;&gt;read_sql&lt;/code&gt; method demonstrates that we have successfully loaded the tables from the CSV files.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 1005px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/2f1b55a145e13d1425bfc7141ed36dbc/3d463/python-player-table.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 51.6%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAKCAYAAAC0VX7mAAAACXBIWXMAAA7DAAAOwwHHb6hkAAABgUlEQVQoz1VSWXaDMBDjJGkIO4TVC0sgQNL2pfc/kPo0kDT98Bt5LGs0Yzt+omH6O2o7QfczzLDCjgtqO6M2M0o1odJXWdwzMkdcPM+Ed0WQGDhBatCNX5JIiwFJ3iM+dwhTCz8xQmIMMwtyvVjBj/WOtfA2jsaHX8FxwwZZOWBcHpjWH4zzA+3lE7q7QbUrVLu89izcT9+Ss8Mdulth+puck8tzh6qsRuG07JHXF6mYFnTaIjq3EotmgrILGrvgFDXiOMk7wUGiEWVWsHP0K4R7KxTMqkEIJG9taxFkgaIZxZkXKbnDvLcLikak/hxSkCK8SEwySZzXU5CjYWtPV+Huyn8XPLHVogfjq2qspAgvbctI6yyYFJ1wnyLEwk+0YIcvxad3wxpZ2eNcXV5tci7RPqu8HlGbKzQdvhV338U5Q274pzhLivEi20yLTloipiv+PT4IX9XdXXHGNOLHm6C7OVTIm1Fa5IxOkZL/xMNjUAtmZJ7FGQ9eJXmug1f+w7/8QDXDOq5UhQAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;python player table&quot;
        title=&quot;python player table&quot;
        src=&quot;/static/2f1b55a145e13d1425bfc7141ed36dbc/3d463/python-player-table.png&quot;
        srcset=&quot;/static/2f1b55a145e13d1425bfc7141ed36dbc/0eb09/python-player-table.png 500w,
/static/2f1b55a145e13d1425bfc7141ed36dbc/1263b/python-player-table.png 1000w,
/static/2f1b55a145e13d1425bfc7141ed36dbc/3d463/python-player-table.png 1005w&quot;
        sizes=&quot;(max-width: 1005px) 100vw, 1005px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;h2 id=&quot;Cleaning-up&quot; style=&quot;position:relative;&quot;&gt;Cleaning up&lt;a href=&quot;#Cleaning-up&quot; aria-label=&quot;Cleaning up permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;We also have a script to truncate all the rows from the tables.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;py&quot;&gt;&lt;pre class=&quot;language-py&quot;&gt;&lt;code class=&quot;language-py&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;# Truncate all tables&lt;/span&gt;
table_load &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; table_load&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;sort_values&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;by&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;order&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; ascending&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token boolean&quot;&gt;False&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; index&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; row &lt;span class=&quot;token keyword&quot;&gt;in&lt;/span&gt; table_load&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;iterrows&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;row&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;TableName&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    cur&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;execute&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-interpolation&quot;&gt;&lt;span class=&quot;token string&quot;&gt;f&quot;delete from &lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;row&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;TableName&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;</content:encoded></item><item><title><![CDATA[Python mono repo for Data Science with Poetry]]></title><description><![CDATA[Mono repo with Python Poetry What we are looking to do is create a code repository that can be used as the source for many Python…]]></description><link>https://mortimer.xyz/python-mono-repo-for-data-science-with-poetry/</link><guid isPermaLink="false">https://mortimer.xyz/python-mono-repo-for-data-science-with-poetry/</guid><pubDate>Fri, 17 Feb 2023 12:00:00 GMT</pubDate><content:encoded>&lt;h1 id=&quot;Mono-repo-with-Python-Poetry&quot; style=&quot;position:relative;&quot;&gt;Mono repo with Python Poetry&lt;a href=&quot;#Mono-repo-with-Python-Poetry&quot; aria-label=&quot;Mono repo with Python Poetry permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h1&gt;
&lt;p&gt;What we are looking to do is create a code repository that can be used as the source for many Python applications (Flask deployed with Docker) and exporitory data analysis that can share a common library of functions.
Pretty much all the credit for this comes from the following article, where Dan Hipschman goes through the process that Opendoor went through to create a mono repo.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://medium.com/opendoor-labs/our-python-monorepo-d34028f2b6fa&quot;&gt;OpenDoor Labs, Our Python Monorepo&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;The Github repo that the remainder of the blog post is referencing is here &lt;a href=&quot;https://github.com/mortie23/ml&quot;&gt;Github&lt;/a&gt;&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;txt&quot;&gt;&lt;pre class=&quot;language-txt&quot;&gt;&lt;code class=&quot;language-txt&quot;&gt;📁ml
├── 📁eda  # Each exploratory data analysis task has a directory here
│   ├── 📁eda-project-1
│   │   ├── eda-project-1.py  # Each eda project has Python scripts
│   │   ├── pyproject.toml  # Each eda project has its own dependencies
│   └── 📁eda-project-2 ...
├── 📁app  # Each application has a directory here
│   ├── 📁flask-app-1
│   │   ├── app.py  # Each application has an app entry point
│   │   ├── Dockerfile  # Each application has a Dockerfile
│   │   ├── pyproject.toml  # Each application has its own dependencies
│   └── 📁flask-app-2 ...
├── 📁lib  # Each lib is a Python package that you can install using poetry (or pip)
│   ├── 📁helloworld  #
│   │   ├── pyproject.toml  # Each lib specifies its dependencies
│   │   ├── 📁helloworld/
│   │   │   ├── __init__.py
│   │   │   ├── helloworld.py
│   │   └── 📁tests/
│   │   │   ├── test_helloworld.py
│   └── 📁lib-2 ...
└── 📁tools
    ├── 📁build  # Application build script&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;For this, I have tested on both ChromeOS Linux (Debian) and Windows WSL2 (Ubuntu 20.04).
Docs from here are from the WSL2 run through.&lt;/p&gt;
&lt;h2 id=&quot;Fresh-Python-Virtual-Environment-venv&quot; style=&quot;position:relative;&quot;&gt;Fresh Python Virtual Environment (venv)&lt;a href=&quot;#Fresh-Python-Virtual-Environment-venv&quot; aria-label=&quot;Fresh Python Virtual Environment venv permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;sh&quot;&gt;&lt;pre class=&quot;language-sh&quot;&gt;&lt;code class=&quot;language-sh&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;# Make new directory in your home for virtual environments&lt;/span&gt;
&lt;span class=&quot;token function&quot;&gt;mkdir&lt;/span&gt; ~/venv
&lt;span class=&quot;token comment&quot;&gt;# change into directory&lt;/span&gt;
&lt;span class=&quot;token builtin class-name&quot;&gt;cd&lt;/span&gt; ~/venv
&lt;span class=&quot;token comment&quot;&gt;# create a virtual environment named poetry (or any other name you choose)&lt;/span&gt;
python3 &lt;span class=&quot;token parameter variable&quot;&gt;-m&lt;/span&gt; venv poetry
&lt;span class=&quot;token comment&quot;&gt;# activate the vitrual environment&lt;/span&gt;
&lt;span class=&quot;token builtin class-name&quot;&gt;source&lt;/span&gt; poetry/bin/activate
&lt;span class=&quot;token comment&quot;&gt;# confirm it activated&lt;/span&gt;
&lt;span class=&quot;token function&quot;&gt;which&lt;/span&gt; python3&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Clone this Git repo&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;sh&quot;&gt;&lt;pre class=&quot;language-sh&quot;&gt;&lt;code class=&quot;language-sh&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;# Make new directory in your home for git repos&lt;/span&gt;
&lt;span class=&quot;token function&quot;&gt;mkdir&lt;/span&gt; ~/git
&lt;span class=&quot;token comment&quot;&gt;# change into directory&lt;/span&gt;
&lt;span class=&quot;token builtin class-name&quot;&gt;cd&lt;/span&gt; ~/git
&lt;span class=&quot;token comment&quot;&gt;# clone the repository&lt;/span&gt;
&lt;span class=&quot;token function&quot;&gt;git&lt;/span&gt; clone https://github.com/mortie23/ml.git
&lt;span class=&quot;token comment&quot;&gt;# change into the clone repo&lt;/span&gt;
&lt;span class=&quot;token builtin class-name&quot;&gt;cd&lt;/span&gt; ml&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2 id=&quot;Getting-started-with-Poetry&quot; style=&quot;position:relative;&quot;&gt;Getting started with Poetry&lt;a href=&quot;#Getting-started-with-Poetry&quot; aria-label=&quot;Getting started with Poetry permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Install this repositories requirements (which is just poetry). The remaining packages poetry will handle.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;sh&quot;&gt;&lt;pre class=&quot;language-sh&quot;&gt;&lt;code class=&quot;language-sh&quot;&gt;pip &lt;span class=&quot;token function&quot;&gt;install&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-r&lt;/span&gt; requirements.txt&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2 id=&quot;Using-Poetry-to-install-Python-packages&quot; style=&quot;position:relative;&quot;&gt;Using Poetry to install Python packages&lt;a href=&quot;#Using-Poetry-to-install-Python-packages&quot; aria-label=&quot;Using Poetry to install Python packages permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Now using poetry to install the packages (including the internal one)&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;sh&quot;&gt;&lt;pre class=&quot;language-sh&quot;&gt;&lt;code class=&quot;language-sh&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;# change into directory&lt;/span&gt;
&lt;span class=&quot;token builtin class-name&quot;&gt;cd&lt;/span&gt; app/flask-app-1
&lt;span class=&quot;token comment&quot;&gt;# poetry to create/update lock file and install python package dependencies&lt;/span&gt;
poetry lock &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; poetry &lt;span class=&quot;token function&quot;&gt;install&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;log&quot;&gt;&lt;pre class=&quot;language-log&quot;&gt;&lt;code class=&quot;language-log&quot;&gt;Updating dependencies
Resolving dependencies&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1.8s&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;)&lt;/span&gt;

Writing lock file
Installing dependencies from lock file

&lt;span class=&quot;token property&quot;&gt;Package operations:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;12&lt;/span&gt; installs&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt; updates&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt; removals

  • Installing markupsafe &lt;span class=&quot;token operator&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;2.1.2&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;)&lt;/span&gt;
  • Installing click &lt;span class=&quot;token operator&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;8.1.3&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;)&lt;/span&gt;
  • Installing exceptiongroup &lt;span class=&quot;token operator&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1.1.0&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;)&lt;/span&gt;
  • Updating importlib&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;metadata &lt;span class=&quot;token operator&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;4.13.0&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;6.0.0&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;)&lt;/span&gt;
  • Installing iniconfig &lt;span class=&quot;token operator&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;2.0.0&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;)&lt;/span&gt;
  • Installing itsdangerous &lt;span class=&quot;token operator&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;2.1.2&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;)&lt;/span&gt;
  • Installing jinja2 &lt;span class=&quot;token operator&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;3.1.2&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;)&lt;/span&gt;
  • Installing pluggy &lt;span class=&quot;token operator&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1.0.0&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;)&lt;/span&gt;
  • Updating setuptools &lt;span class=&quot;token operator&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;44.0.0&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;67.3.2&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;)&lt;/span&gt;
  • Installing werkzeug &lt;span class=&quot;token operator&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;2.2.3&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;)&lt;/span&gt;
  • Installing flask &lt;span class=&quot;token operator&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;2.2.3&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;)&lt;/span&gt;
  • Installing gunicorn &lt;span class=&quot;token operator&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;20.1.0&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;)&lt;/span&gt;
  • Installing pytest &lt;span class=&quot;token operator&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;7.2.1&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;)&lt;/span&gt;
  • Installing helloworld &lt;span class=&quot;token operator&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0.1.0&lt;/span&gt; &lt;span class=&quot;token file-path string&quot;&gt;/mnt/c/git/github/mortie23/ml/lib/helloworld&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Run the app with poetry. This app sources from the internal package in dev mode, so changes made to the source files are reflected as they change.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;sh&quot;&gt;&lt;pre class=&quot;language-sh&quot;&gt;&lt;code class=&quot;language-sh&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;# use poetry to run the app with the venv python3 binary&lt;/span&gt;
poetry run python3 app.py&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;sh&quot;&gt;&lt;pre class=&quot;language-sh&quot;&gt;&lt;code class=&quot;language-sh&quot;&gt; * Serving Flask app &lt;span class=&quot;token string&quot;&gt;&apos;app&apos;&lt;/span&gt;
 * Debug mode: off
WARNING: This is a development server. Do not use it &lt;span class=&quot;token keyword&quot;&gt;in&lt;/span&gt; a production deployment. Use a production WSGI server instead.
 * Running on http://127.0.0.1:5000
Press CTRL+C to quit
&lt;span class=&quot;token number&quot;&gt;127.0&lt;/span&gt;.0.1 - - &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;15&lt;/span&gt;/Feb/2023 &lt;span class=&quot;token number&quot;&gt;19&lt;/span&gt;:33:46&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;GET / HTTP/1.1&quot;&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;200&lt;/span&gt; -&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 568px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/f8c64a38afbf235192ed747a01e9b5f2/d63a8/python-mono-browser-poetry-flask.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 49.4%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAKCAYAAAC0VX7mAAAACXBIWXMAAAsTAAALEwEAmpwYAAABR0lEQVQoz62Ry27CMBBF8/+/wYYFIBA7EN/AEonwEisQiV8hb8e3upNQVaWLSu1IV2OPZ45n7MhaG6y1oHfOwRjzJsYp5vX+pX6vtQ5KKdxuty56wViotcbz+USe55/i/jv4/QILY61AI2OMwJRS4X6/o21b9BYQQoD3Hmma4vFIkGWZgAUwXDBAw3BJiMqyRF3XIhZQ7DRJUhFBaaok5lyGsqzAGnqlOFEuYE5TFAUi7zvpous65HkhYJdxTIts8FobeTMWM79tPZqmhTFOwGyiqmrUTUOg7wcMAX81MqLRaITpdIrxeIz5fI44jrFcLrHZbCTGs8PhgMVigdlshvV6LevVaiXnk8lEcvmWtOh6vUrB5XLB8XiUD+CeYPrz+YwkSeRst9vhdDqJttutaL/fS25VVT0Q/2gyMj/jqxj8TeynHHb4AW1i9zI1C3gHAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;python mono browser poetry flask&quot;
        title=&quot;python mono browser poetry flask&quot;
        src=&quot;/static/f8c64a38afbf235192ed747a01e9b5f2/d63a8/python-mono-browser-poetry-flask.png&quot;
        srcset=&quot;/static/f8c64a38afbf235192ed747a01e9b5f2/0eb09/python-mono-browser-poetry-flask.png 500w,
/static/f8c64a38afbf235192ed747a01e9b5f2/d63a8/python-mono-browser-poetry-flask.png 568w&quot;
        sizes=&quot;(max-width: 568px) 100vw, 568px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;h2 id=&quot;Testing-internal-package-for-EDA&quot; style=&quot;position:relative;&quot;&gt;Testing internal package for EDA&lt;a href=&quot;#Testing-internal-package-for-EDA&quot; aria-label=&quot;Testing internal package for EDA permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;sh&quot;&gt;&lt;pre class=&quot;language-sh&quot;&gt;&lt;code class=&quot;language-sh&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;# change into directory&lt;/span&gt;
&lt;span class=&quot;token builtin class-name&quot;&gt;cd&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;..&lt;/span&gt;/&lt;span class=&quot;token punctuation&quot;&gt;..&lt;/span&gt;/eda/eda-project-1/
&lt;span class=&quot;token comment&quot;&gt;# poetry to create/update lock file and install python package dependencies&lt;/span&gt;
poetry lock &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; poetry &lt;span class=&quot;token function&quot;&gt;install&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;log&quot;&gt;&lt;pre class=&quot;language-log&quot;&gt;&lt;code class=&quot;language-log&quot;&gt;Updating dependencies
Resolving dependencies&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;8.2s&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;)&lt;/span&gt;

Writing lock file
Installing dependencies from lock file

&lt;span class=&quot;token property&quot;&gt;Package operations:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;25&lt;/span&gt; installs&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt; update&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt; removals

  • Installing asttokens &lt;span class=&quot;token operator&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;2.2.1&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;)&lt;/span&gt;
  • Installing executing &lt;span class=&quot;token operator&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1.2.0&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;)&lt;/span&gt;
  • Installing parso &lt;span class=&quot;token operator&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0.8.3&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;)&lt;/span&gt;
  • Updating platformdirs &lt;span class=&quot;token operator&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;2.6.2&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;3.0.0&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;)&lt;/span&gt;
  • Installing pure&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;eval &lt;span class=&quot;token operator&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0.2.2&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;)&lt;/span&gt;
  • Installing traitlets &lt;span class=&quot;token operator&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;5.9.0&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;)&lt;/span&gt;
  • Installing wcwidth &lt;span class=&quot;token operator&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0.2.6&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;)&lt;/span&gt;
  • Installing backcall &lt;span class=&quot;token operator&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0.2.0&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;)&lt;/span&gt;
  • Installing decorator &lt;span class=&quot;token operator&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;5.1.1&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;)&lt;/span&gt;
  • Installing jedi &lt;span class=&quot;token operator&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0.18.2&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;)&lt;/span&gt;
  • Installing jupyter&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;core &lt;span class=&quot;token operator&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;5.2.0&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;)&lt;/span&gt;
  • Installing matplotlib&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;inline &lt;span class=&quot;token operator&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0.1.6&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;)&lt;/span&gt;
  • Installing pickleshare &lt;span class=&quot;token operator&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0.7.5&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;)&lt;/span&gt;
  • Installing prompt&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;toolkit &lt;span class=&quot;token operator&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;3.0.36&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;)&lt;/span&gt;
  • Installing pygments &lt;span class=&quot;token operator&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;2.14.0&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;)&lt;/span&gt;
  • Installing python&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;dateutil &lt;span class=&quot;token operator&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;2.8.2&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;)&lt;/span&gt;
  • Installing pyzmq &lt;span class=&quot;token operator&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;25.0.0&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;)&lt;/span&gt;
  • Installing stack&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;data &lt;span class=&quot;token operator&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0.6.2&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;)&lt;/span&gt;
  • Installing tornado &lt;span class=&quot;token operator&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;6.2&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;)&lt;/span&gt;
  • Installing comm &lt;span class=&quot;token operator&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0.1.2&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;)&lt;/span&gt;
  • Installing debugpy &lt;span class=&quot;token operator&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1.6.6&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;)&lt;/span&gt;
  • Installing ipython &lt;span class=&quot;token operator&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;8.10.0&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;)&lt;/span&gt;
  • Installing jupyter&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;client &lt;span class=&quot;token operator&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;8.0.3&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;)&lt;/span&gt;
  • Installing nest&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;asyncio &lt;span class=&quot;token operator&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1.5.6&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;)&lt;/span&gt;
  • Installing psutil &lt;span class=&quot;token operator&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;5.9.4&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;)&lt;/span&gt;
  • Installing ipykernel &lt;span class=&quot;token operator&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;6.21.2&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Register the ipykernel&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;sh&quot;&gt;&lt;pre class=&quot;language-sh&quot;&gt;&lt;code class=&quot;language-sh&quot;&gt;python3 &lt;span class=&quot;token parameter variable&quot;&gt;-m&lt;/span&gt; ipykernel &lt;span class=&quot;token function&quot;&gt;install&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;--user&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;--name&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;poetry&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Now to test running the EDA code within VScode, you need to have installed the VSCode Python extension from Microsoft.
This will activate the &lt;strong&gt;Run Cell&lt;/strong&gt; links above each of the &lt;code class=&quot;language-text&quot;&gt;# %%&lt;/code&gt; comment blocks.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 471px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/5007f2286426f10de01be006a197f576/27c0d/python-mono-vscode-jupyter-interactive-code.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 42.25053078556263%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAICAIAAAB2/0i6AAAACXBIWXMAAAsSAAALEgHS3X78AAABU0lEQVQY02WP22rkMBBE8xlLMuOxPZJ1cevSuliSLc842SVhIZCXPC3sc/7/DxYPIQQWiir64VDVd+u25mVb1l+5Pk1lM670zDXEnohtiD3e/HA7/9cdhyhUYioZm0eduS7zdRO6ULUgpGepfgqog2wG3w+uHVxLXUvxEz4RPBD8zcSr9u8ARsZeTlTmFpY64l/g7+P4R/KTqsNYmJ6FqXTMn3BL7JH6N8E+avhwsmBSYQv5UdkFXV1ivU5rcMUob/3aUtv0pjmbr2bb7WMwgJUCB0jSFGlmDlmozPQ86JlBktIxSN2ALcXvs/foWCD6coal41Mnpl5EMpZe5uPZ/WjUfafvO/Nw0ofdzaE1zflbMxEx1RfjL2AXjFcOSehsXXEueB/RBvRe+ggh2pIgRMpN8wX3zIOdpc7arThdBSQOyWA2xscd9grdDZ6wJvCRsP3tf0kTVhLTFIvOAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;python mono vscode jupyter interactive code&quot;
        title=&quot;python mono vscode jupyter interactive code&quot;
        src=&quot;/static/5007f2286426f10de01be006a197f576/27c0d/python-mono-vscode-jupyter-interactive-code.png&quot;
        srcset=&quot;/static/5007f2286426f10de01be006a197f576/27c0d/python-mono-vscode-jupyter-interactive-code.png 471w&quot;
        sizes=&quot;(max-width: 471px) 100vw, 471px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;When you do, an interactive windows will open the right side. Since you have registered your ipykernel, you will need to select it.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 534px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/ae1ae61db3918d6eedc7d07c841a6174/f1de1/python-mono-vscode-jupyter-interactive-kernel.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 16.400000000000002%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAADCAIAAAAcOLh5AAAACXBIWXMAAAsSAAALEgHS3X78AAAAlElEQVQI1yWIQQ7CIBAA+xFboCy7sAgF2mBtjamaePbg/79iapPJZDINp5XOC4VFuyoxC0idHg7aPnZ9PPq0OwodxX9KHVs9NDbMLq4UFhtXGxf0V6BRQBKQJBaFo8LSQrqBe6PbkB/IT+I78gdNIyADV0UT+mr8rGgymBUWaTK4/Rh/6Ux+GdrCWF2ZuVw5Z5u/jn6+rB7/bU5n5wAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;python mono vscode jupyter interactive kernel&quot;
        title=&quot;python mono vscode jupyter interactive kernel&quot;
        src=&quot;/static/ae1ae61db3918d6eedc7d07c841a6174/f1de1/python-mono-vscode-jupyter-interactive-kernel.png&quot;
        srcset=&quot;/static/ae1ae61db3918d6eedc7d07c841a6174/0eb09/python-mono-vscode-jupyter-interactive-kernel.png 500w,
/static/ae1ae61db3918d6eedc7d07c841a6174/f1de1/python-mono-vscode-jupyter-interactive-kernel.png 534w&quot;
        sizes=&quot;(max-width: 534px) 100vw, 534px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;Now, when you run the cells, you should see that the internal package can be successfully run from the EDA project code.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 776px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/cb2d1896e1680def9d88663c76fbfa9b/cfc60/python-mono-vscode-jupyter-interactive.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 60.4%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAMCAIAAADtbgqsAAAACXBIWXMAAAsTAAALEwEAmpwYAAABd0lEQVQoz42SiW7bMAyG8yiNrcs6KFKHLdtpm3Y5umLL8v5PMyjenAADhgI/BErER/4gtdG+5zQwyMom2RGXnisUCqUOoiNxO1sBi2qqI6nDkt10NgKOykQfJuOylCA66mzCOAGNBjLQyBW2ApiA5RHD7GnSLm9aAZ5GwGL9ENMzxZ2nkdIOsBjoMUzKxCdmG+62zG6ZbYRr+B9VeP/2eT5fzx/Xb4efh+PleLocjpfT+fr2/nk8/Xo//Hjdf3/dfzgszpfFxaIKWxh8ddgrE7XNnkbni8MCNRh8mADLaoQrbLi7w60EUDPoidJMcQYcMUwYZ6MHY/vqltvtzeeWWSb9qgoz5ZWKUoV1jFIHpYNUQShkyj8Cq+dbZw5cI6U55F1f9gAD0lBKKiWF3NuccM6+9ELRY4mHzn8v/F5+0b3Jv2SFmfQNdxgminPqX5wfnlqzjuT/2oiO6g+hOkypQ2eScTX4EtwKUCbWHd7WCFRCfu5s+krz34pMf33Tx6ylAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;python mono vscode jupyter interactive&quot;
        title=&quot;python mono vscode jupyter interactive&quot;
        src=&quot;/static/cb2d1896e1680def9d88663c76fbfa9b/cfc60/python-mono-vscode-jupyter-interactive.png&quot;
        srcset=&quot;/static/cb2d1896e1680def9d88663c76fbfa9b/0eb09/python-mono-vscode-jupyter-interactive.png 500w,
/static/cb2d1896e1680def9d88663c76fbfa9b/cfc60/python-mono-vscode-jupyter-interactive.png 776w&quot;
        sizes=&quot;(max-width: 776px) 100vw, 776px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;h2 id=&quot;Internal-package-unit-testing&quot; style=&quot;position:relative;&quot;&gt;Internal package unit testing&lt;a href=&quot;#Internal-package-unit-testing&quot; aria-label=&quot;Internal package unit testing permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;To test each function in the internal package we have included pytest and an example unit test.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;sh&quot;&gt;&lt;pre class=&quot;language-sh&quot;&gt;&lt;code class=&quot;language-sh&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;# change into the package directory&lt;/span&gt;
&lt;span class=&quot;token builtin class-name&quot;&gt;cd&lt;/span&gt; lib/helloworld
&lt;span class=&quot;token comment&quot;&gt;# run all tests with pytest&lt;/span&gt;
pytest&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;log&quot;&gt;&lt;pre class=&quot;language-log&quot;&gt;&lt;code class=&quot;language-log&quot;&gt;&lt;span class=&quot;token separator comment&quot;&gt;===================================&lt;/span&gt; test session starts &lt;span class=&quot;token separator comment&quot;&gt;====================================&lt;/span&gt;
platform linux &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt; Python &lt;span class=&quot;token number&quot;&gt;3.8.10&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; pytest&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;7.2.1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; pluggy&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1.0.0&lt;/span&gt;
&lt;span class=&quot;token property&quot;&gt;rootdir:&lt;/span&gt; &lt;span class=&quot;token file-path string&quot;&gt;/mnt/c/git/github/mortie23/ml/lib/helloworld&lt;/span&gt;
collected &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt; item

tests&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;test_helloworld&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;py &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;                                                           &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;100&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;%&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;

&lt;span class=&quot;token separator comment&quot;&gt;====================================&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt; passed in &lt;span class=&quot;token number&quot;&gt;0.09s&lt;/span&gt; &lt;span class=&quot;token separator comment&quot;&gt;=====================================&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2 id=&quot;Internal-package-dependency&quot; style=&quot;position:relative;&quot;&gt;Internal package dependency&lt;a href=&quot;#Internal-package-dependency&quot; aria-label=&quot;Internal package dependency permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Since we are using poetry to manage the internal package as a dependency in the mono repo, it works in development mode on own local client machine, however when deploying the application using a Docker container, it will not have access to the internal package.&lt;/p&gt;
&lt;p&gt;We can either:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;deploy the package to an internal package server and then update the &lt;code class=&quot;language-text&quot;&gt;pyproject.toml&lt;/code&gt; file to source from the internal package server&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;copy the internal package from the lib directory it is currently in within the mono repo temporarily for the building of the docker image&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;There are probably other options, but the simplest option for me for this small example was the second option. the build step does this.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;Build-the-docker-container&quot; style=&quot;position:relative;&quot;&gt;Build the docker container&lt;a href=&quot;#Build-the-docker-container&quot; aria-label=&quot;Build the docker container permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;If also testing this on Windows WSL2, you will need to install Docker desktop for windows and then configure it to use WSL2 as the kernel.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;sh&quot;&gt;&lt;pre class=&quot;language-sh&quot;&gt;&lt;code class=&quot;language-sh&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;# change into build tools directory&lt;/span&gt;
&lt;span class=&quot;token builtin class-name&quot;&gt;cd&lt;/span&gt; tools/build/
&lt;span class=&quot;token comment&quot;&gt;# ensure the shell script has execute permissions&lt;/span&gt;
&lt;span class=&quot;token function&quot;&gt;chmod&lt;/span&gt; +x build.sh
&lt;span class=&quot;token comment&quot;&gt;# run the build script with the parameter as the name of the application directory&lt;/span&gt;
./build.sh flask-app-1&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;log&quot;&gt;&lt;pre class=&quot;language-log&quot;&gt;&lt;code class=&quot;language-log&quot;&gt;flask&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;app&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;
sending incremental file list

sent &lt;span class=&quot;token number&quot;&gt;630&lt;/span&gt; bytes  received &lt;span class=&quot;token number&quot;&gt;17&lt;/span&gt; bytes  &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;294.00&lt;/span&gt; bytes&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;sec
total size is &lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;597&lt;/span&gt;  speedup is &lt;span class=&quot;token number&quot;&gt;4.01&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; Building &lt;span class=&quot;token number&quot;&gt;10.3s&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;12&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;12&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;)&lt;/span&gt; FINISHED
 &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;internal&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; load build definition from Dockerfile                                    &lt;span class=&quot;token number&quot;&gt;0.1s&lt;/span&gt;
 &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; transferring dockerfile&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;436B&lt;/span&gt;                                                    &lt;span class=&quot;token number&quot;&gt;0.0s&lt;/span&gt;
 &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;internal&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; load &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;dockerignore                                                       &lt;span class=&quot;token number&quot;&gt;0.0s&lt;/span&gt;
 &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; transferring context&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;2B&lt;/span&gt;                                                         &lt;span class=&quot;token number&quot;&gt;0.0s&lt;/span&gt;
 &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;internal&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; load metadata for docker&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;io&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;library&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;python&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;3.8&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;buster                    &lt;span class=&quot;token number&quot;&gt;2.9s&lt;/span&gt;
 &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;auth&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; library&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;python&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;pull token for &lt;span class=&quot;token domain constant&quot;&gt;registry-1.docker.io&lt;/span&gt;                           &lt;span class=&quot;token number&quot;&gt;0.0s&lt;/span&gt;
 &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;7&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; FROM docker&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;io&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;library&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;python&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;3.8&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;buster&lt;span class=&quot;token operator&quot;&gt;@&lt;/span&gt;sha256&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;                            &lt;span class=&quot;token number&quot;&gt;0.0s&lt;/span&gt;
 &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;internal&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; load build context                                                       &lt;span class=&quot;token number&quot;&gt;0.1s&lt;/span&gt;
 &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; transferring context&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;24.27kB&lt;/span&gt;                                                    &lt;span class=&quot;token number&quot;&gt;0.1s&lt;/span&gt;
 &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; CACHED &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;7&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; RUN pip install &lt;span class=&quot;token string&quot;&gt;&quot;poetry==1.3.2&quot;&lt;/span&gt;                                        &lt;span class=&quot;token number&quot;&gt;0.0s&lt;/span&gt;
 &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;7&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; COPY &lt;span class=&quot;token domain constant&quot;&gt;poetry.lock&lt;/span&gt; &lt;span class=&quot;token domain constant&quot;&gt;pyproject.toml&lt;/span&gt; &lt;span class=&quot;token domain constant&quot;&gt;app.py&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;                                      &lt;span class=&quot;token number&quot;&gt;0.1s&lt;/span&gt;
 &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;7&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; COPY lib lib&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;                                                                 &lt;span class=&quot;token number&quot;&gt;0.1s&lt;/span&gt;
 &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;7&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; COPY templates templates&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;                                                     &lt;span class=&quot;token number&quot;&gt;0.0s&lt;/span&gt;
 &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;6&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;7&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; RUN poetry config &lt;span class=&quot;token domain constant&quot;&gt;virtualenvs.create&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;false&lt;/span&gt;   &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;amp;&lt;/span&gt; poetry install &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;no&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;dev       &lt;span class=&quot;token number&quot;&gt;6.6s&lt;/span&gt;
 &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; exporting to image                                                                  &lt;span class=&quot;token number&quot;&gt;0.2s&lt;/span&gt;
 &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; exporting layers                                                                 &lt;span class=&quot;token number&quot;&gt;0.2s&lt;/span&gt;
 &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; writing image sha256&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;                                                          &lt;span class=&quot;token number&quot;&gt;0.0s&lt;/span&gt;
 &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; naming to docker&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;io&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;mortimerxyz&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;flaskapp1&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0.0.1&lt;/span&gt;                                  &lt;span class=&quot;token number&quot;&gt;0.0s&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2 id=&quot;Run-the-docker-container&quot; style=&quot;position:relative;&quot;&gt;Run the docker container&lt;a href=&quot;#Run-the-docker-container&quot; aria-label=&quot;Run the docker container permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;sh&quot;&gt;&lt;pre class=&quot;language-sh&quot;&gt;&lt;code class=&quot;language-sh&quot;&gt;&lt;span class=&quot;token function&quot;&gt;docker&lt;/span&gt; run &lt;span class=&quot;token parameter variable&quot;&gt;-p&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;5000&lt;/span&gt;:5000 mortimerxyz/flaskapp1:0.0.1&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;log&quot;&gt;&lt;pre class=&quot;language-log&quot;&gt;&lt;code class=&quot;language-log&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token date number&quot;&gt;2023-02-15&lt;/span&gt; &lt;span class=&quot;token time number&quot;&gt;08:59:29 +0000&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token level info keyword&quot;&gt;INFO&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; Starting gunicorn &lt;span class=&quot;token number&quot;&gt;20.1.0&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token date number&quot;&gt;2023-02-15&lt;/span&gt; &lt;span class=&quot;token time number&quot;&gt;08:59:29 +0000&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token level info keyword&quot;&gt;INFO&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token property&quot;&gt;Listening at:&lt;/span&gt; &lt;span class=&quot;token url&quot;&gt;http://0.0.0.0:5000&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token date number&quot;&gt;2023-02-15&lt;/span&gt; &lt;span class=&quot;token time number&quot;&gt;08:59:29 +0000&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token level info keyword&quot;&gt;INFO&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token property&quot;&gt;Using worker:&lt;/span&gt; sync
&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token date number&quot;&gt;2023-02-15&lt;/span&gt; &lt;span class=&quot;token time number&quot;&gt;08:59:29 +0000&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;8&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token level info keyword&quot;&gt;INFO&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token property&quot;&gt;Booting worker with pid:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;8&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 568px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/1e2ba5c72b33a3dc771490300bf901b5/d63a8/python-mono-browser-docker-flask.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 49.4%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAKCAYAAAC0VX7mAAAACXBIWXMAAAsTAAALEwEAmpwYAAABSUlEQVQoz61RSW4CQQzs/3+DCwcQIG7AvIEjEru4gJjpdfampyuyWZKIHCIllkrl9lK2Z4S1Nlpr4ZxjNsa8wTnHeNTB2ifub611VErhcrl0ghpIVGvNybIsv6Eoijfh9wEWxloWFUopds7ncyQBshgjIjGAEAKklEjTlJtZ3H4OeF73GBJFXddo2xbe+8fkHEprqDSDzCSu1xRSKtoAVVWjrhtQD7FSGkVRvi6rqgoihI63IKYAibu8gMkUrDKQSkNrw4O8v+F2CwzyjXEsnOc5mqZF6z0JhteZfzXSEL1eD8PhEP1+H+PxGJvNBtPpFMliwTHKbbdbTCYTjEYjzOdz9mezGecHgwGSJOFvSSZOpxM3HI9H7HY7/gH0JmHiw+GALMs4t1qtsN/vGcvlkrFer7m2aZq7IP7R+OSu6/AVFPxN7Kca2vADnR73RktzAIwAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;python mono browser docker flask&quot;
        title=&quot;python mono browser docker flask&quot;
        src=&quot;/static/1e2ba5c72b33a3dc771490300bf901b5/d63a8/python-mono-browser-docker-flask.png&quot;
        srcset=&quot;/static/1e2ba5c72b33a3dc771490300bf901b5/0eb09/python-mono-browser-docker-flask.png 500w,
/static/1e2ba5c72b33a3dc771490300bf901b5/d63a8/python-mono-browser-docker-flask.png 568w&quot;
        sizes=&quot;(max-width: 568px) 100vw, 568px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Install Posit Connect on Ubuntu]]></title><description><![CDATA[Overview We want to test out the installation and management of Posit Connect (previously RStudio Connect). Prerequisites A machine running…]]></description><link>https://mortimer.xyz/install-posit-connect-ubuntu/</link><guid isPermaLink="false">https://mortimer.xyz/install-posit-connect-ubuntu/</guid><pubDate>Wed, 04 Jan 2023 12:00:00 GMT</pubDate><content:encoded>&lt;h2 id=&quot;Overview&quot; style=&quot;position:relative;&quot;&gt;Overview&lt;a href=&quot;#Overview&quot; aria-label=&quot;Overview permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;We want to test out the installation and management of Posit Connect (previously RStudio Connect).&lt;/p&gt;
&lt;h2 id=&quot;Prerequisites&quot; style=&quot;position:relative;&quot;&gt;Prerequisites&lt;a href=&quot;#Prerequisites&quot; aria-label=&quot;Prerequisites permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;A machine running Ubuntu
&lt;ul&gt;
&lt;li&gt;I used a Windows machine with VMWare workstation 16 player and a clean install of Ubuntu 20.04 from ISO&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;RStudio server installation (basically because this installation process includes the installation of &lt;strong&gt;r-base&lt;/strong&gt; and I was hoping to test a single server with dual installation of RStudio server and Posit Connect)
&lt;ul&gt;
&lt;li&gt;I followed the steps on this documentation page &lt;a href=&quot;https://posit.co/download/rstudio-server/&quot;&gt;Download RStudio server&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;Update: 2025-April
Specific versions of &lt;strong&gt;r-base&lt;/strong&gt; and RStudio&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;sh&quot;&gt;&lt;pre class=&quot;language-sh&quot;&gt;&lt;code class=&quot;language-sh&quot;&gt;&lt;span class=&quot;token function&quot;&gt;sudo&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;apt-get&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;install&lt;/span&gt; r-base
&lt;span class=&quot;token function&quot;&gt;sudo&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;apt-get&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;install&lt;/span&gt; gdebi-core
&lt;span class=&quot;token function&quot;&gt;wget&lt;/span&gt; https://download2.rstudio.org/server/jammy/amd64/rstudio-server-2024.12.1-563-amd64.deb
&lt;span class=&quot;token function&quot;&gt;sudo&lt;/span&gt; gdebi rstudio-server-2024.12.1-563-amd64.deb&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;Infrastructure-attempted&quot; style=&quot;position:relative;&quot;&gt;Infrastructure attempted&lt;a href=&quot;#Infrastructure-attempted&quot; aria-label=&quot;Infrastructure attempted permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;Windows with a WSL
&lt;ul&gt;
&lt;li&gt;I got issues when Posit Connect used &lt;code class=&quot;language-text&quot;&gt;systemctl&lt;/code&gt; with &lt;code class=&quot;language-text&quot;&gt;System has not been booted with systemd as init system (PID 1). Can&apos;t operate.&lt;/code&gt; I’m thinking that my release of WSL didn’t support Systemd so I moved on to other options&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Virtual machine running Ubuntu
&lt;ul&gt;
&lt;li&gt;I got this to work and the remaining blog post starts with this as a basis&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;ChromeOS with Debian
&lt;ul&gt;
&lt;li&gt;I actually tried this first and got issues with the service failing to start&lt;/li&gt;
&lt;li&gt;After trying the install on the virtual machine running Ubuntu and getting the same issue (which I resolved with a configuration change), I tried the configuration fix on ChromeOS debian and it worked&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id=&quot;Installation&quot; style=&quot;position:relative;&quot;&gt;Installation&lt;a href=&quot;#Installation&quot; aria-label=&quot;Installation permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Following on this Posit documentation page &lt;a href=&quot;https://docs.posit.co/rsc/manual-install/#&quot;&gt;Manually install Posit Connect&lt;/a&gt;. In my case, the Ubuntu installation I had was 20.04. (This same installation worked for Debian on ChromeOS)&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;sh&quot;&gt;&lt;pre class=&quot;language-sh&quot;&gt;&lt;code class=&quot;language-sh&quot;&gt;&lt;span class=&quot;token function&quot;&gt;sudo&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;apt-get&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;install&lt;/span&gt; gdebi-core
&lt;span class=&quot;token function&quot;&gt;curl&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-O&lt;/span&gt; https://cdn.rstudio.com/connect/2022.12/rstudio-connect_2022.12.0~ubuntu20_amd64.deb
&lt;span class=&quot;token function&quot;&gt;sudo&lt;/span&gt; gdebi rstudio-connect_2022.12.0~ubuntu20_amd64.deb&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;Update 2025-April
I needed to build another VM recently with newer versions and to test Python applications&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;sh&quot;&gt;&lt;pre class=&quot;language-sh&quot;&gt;&lt;code class=&quot;language-sh&quot;&gt;&lt;span class=&quot;token function&quot;&gt;curl&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-O&lt;/span&gt; https://cdn.posit.co/connect/2024.09/rstudio-connect_2024.09.0~ubuntu24_amd64.deb
&lt;span class=&quot;token function&quot;&gt;sudo&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;apt&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;install&lt;/span&gt; ./rstudio-connect_2024.09.0~ubuntu24_amd64.deb&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;Initially-this-failed-to-start-the-service&quot; style=&quot;position:relative;&quot;&gt;Initially this failed to start the service&lt;a href=&quot;#Initially-this-failed-to-start-the-service&quot; aria-label=&quot;Initially this failed to start the service permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;After running the installation, test that the service is running.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;sh&quot;&gt;&lt;pre class=&quot;language-sh&quot;&gt;&lt;code class=&quot;language-sh&quot;&gt;&lt;span class=&quot;token function&quot;&gt;sudo&lt;/span&gt; systemctl status rstudio-connect&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;For me, unfortunately, the service failed to start.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;log&quot;&gt;&lt;pre class=&quot;language-log&quot;&gt;&lt;code class=&quot;language-log&quot;&gt;● &lt;span class=&quot;token domain constant&quot;&gt;rstudio-connect.service&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt; Posit Connect
     &lt;span class=&quot;token property&quot;&gt;Loaded:&lt;/span&gt; loaded &lt;span class=&quot;token operator&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token file-path string&quot;&gt;/lib/systemd/system/rstudio-connect.service&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;;&lt;/span&gt; enabled&lt;span class=&quot;token operator&quot;&gt;;&lt;/span&gt; vendor preset&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; enabled&lt;span class=&quot;token operator&quot;&gt;)&lt;/span&gt;
     &lt;span class=&quot;token property&quot;&gt;Active:&lt;/span&gt; &lt;span class=&quot;token property&quot;&gt;activating (auto-restart) (Result:&lt;/span&gt; exit&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;code&lt;span class=&quot;token operator&quot;&gt;)&lt;/span&gt; since Wed &lt;span class=&quot;token date number&quot;&gt;2023-01-04&lt;/span&gt; &lt;span class=&quot;token time number&quot;&gt;01:15:02&lt;/span&gt; PST&lt;span class=&quot;token operator&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;116ms&lt;/span&gt; ago
    &lt;span class=&quot;token property&quot;&gt;Process:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;20215&lt;/span&gt; ExecStart&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;opt&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;rstudio&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;connect&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;bin&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;connect &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;config &lt;span class=&quot;token file-path string&quot;&gt;/etc/rstudio-connect/rstudio-connect.gcfg&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;(&lt;/span&gt;code&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;exited&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; status&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token level error important&quot;&gt;FAILURE&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;)&lt;/span&gt;
   &lt;span class=&quot;token property&quot;&gt;Main PID:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;20215&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;(&lt;/span&gt;code&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;exited&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; status&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token level error important&quot;&gt;FAILURE&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;token date number&quot;&gt;Jan 04&lt;/span&gt; &lt;span class=&quot;token time number&quot;&gt;01:15:02&lt;/span&gt; ubuntu systemd&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; rstudio&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;connect&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;service&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; Scheduled restart job&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; restart counter is at &lt;span class=&quot;token number&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;
&lt;span class=&quot;token date number&quot;&gt;Jan 04&lt;/span&gt; &lt;span class=&quot;token time number&quot;&gt;01:15:02&lt;/span&gt; ubuntu systemd&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; Stopped Posit Connect&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;
&lt;span class=&quot;token date number&quot;&gt;Jan 04&lt;/span&gt; &lt;span class=&quot;token time number&quot;&gt;01:15:02&lt;/span&gt; ubuntu systemd&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; rstudio&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;connect&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;service&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; Start request repeated too quickly&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;
&lt;span class=&quot;token date number&quot;&gt;Jan 04&lt;/span&gt; &lt;span class=&quot;token time number&quot;&gt;01:15:02&lt;/span&gt; ubuntu systemd&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; rstudio&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;connect&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;service&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; Failed with result &lt;span class=&quot;token string&quot;&gt;&apos;exit-code&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;
&lt;span class=&quot;token date number&quot;&gt;Jan 04&lt;/span&gt; &lt;span class=&quot;token time number&quot;&gt;01:15:02&lt;/span&gt; ubuntu systemd&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; Failed to start Posit Connect&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;After checking the the log:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;sh&quot;&gt;&lt;pre class=&quot;language-sh&quot;&gt;&lt;code class=&quot;language-sh&quot;&gt;&lt;span class=&quot;token function&quot;&gt;sudo&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;cat&lt;/span&gt; /var/log/rstudio/rstudio-connect/rstudio-connect.log&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;I found the last log line showing that the reason the service cannot start is the Posit Connect cannot find the installation of R.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;log&quot;&gt;&lt;pre class=&quot;language-log&quot;&gt;&lt;code class=&quot;language-log&quot;&gt;time&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;2023-01-04T09:15:02.118Z&quot;&lt;/span&gt; level&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;fatal msg&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Error: Unable to initialize local environment: Unable to use R on this system: Could not locate an R installation&quot;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;I’m not sure what default location for R that Posit Connect searches for. Let’s find out where R is installed on our system:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;sh&quot;&gt;&lt;pre class=&quot;language-sh&quot;&gt;&lt;code class=&quot;language-sh&quot;&gt;&lt;span class=&quot;token function&quot;&gt;which&lt;/span&gt; R&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Looks like it is installed in &lt;code class=&quot;language-text&quot;&gt;/usr/bin&lt;/code&gt;.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;log&quot;&gt;&lt;pre class=&quot;language-log&quot;&gt;&lt;code class=&quot;language-log&quot;&gt;&lt;span class=&quot;token file-path string&quot;&gt;/usr/bin/R&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;Change-the-configuration-for-the-R-install-location&quot; style=&quot;position:relative;&quot;&gt;Change the configuration for the R install location&lt;a href=&quot;#Change-the-configuration-for-the-R-install-location&quot; aria-label=&quot;Change the configuration for the R install location permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;sh&quot;&gt;&lt;pre class=&quot;language-sh&quot;&gt;&lt;code class=&quot;language-sh&quot;&gt;&lt;span class=&quot;token function&quot;&gt;sudo&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;vim&lt;/span&gt; /etc/rstudio-connect/rstudio-connect.gcfg&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Initially I tried a configuration of &lt;code class=&quot;language-text&quot;&gt;/usr/bin/&lt;/code&gt; but found that Posit Connect was by default adding &lt;code class=&quot;language-text&quot;&gt;bin&lt;/code&gt; to the path and looking in &lt;code class=&quot;language-text&quot;&gt;/usr/bin/bin/&lt;/code&gt;. This was the configuration that worked for me:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;ini&quot;&gt;&lt;pre class=&quot;language-ini&quot;&gt;&lt;code class=&quot;language-ini&quot;&gt;&lt;span class=&quot;token section&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token section-name selector&quot;&gt;Server&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;; No longer working in newer version&lt;/span&gt;
&lt;span class=&quot;token key attr-name&quot;&gt;RVersion&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token value attr-value&quot;&gt;&quot;&lt;span class=&quot;token inner-value&quot;&gt;/usr/&lt;/span&gt;&quot;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;Update: 2025-April
With the latest version of Posit Connect I used the automatic scanning config instead&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;ini&quot;&gt;&lt;pre class=&quot;language-ini&quot;&gt;&lt;code class=&quot;language-ini&quot;&gt;&lt;span class=&quot;token section&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token section-name selector&quot;&gt;R&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token key attr-name&quot;&gt;ExecutableScanning&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token value attr-value&quot;&gt;true&lt;/span&gt;
&lt;span class=&quot;token key attr-name&quot;&gt;ExecutableSystemScanning&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token value attr-value&quot;&gt;true&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;Update: 2025-April
I wanted to test deploying Python based apps&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;ini&quot;&gt;&lt;pre class=&quot;language-ini&quot;&gt;&lt;code class=&quot;language-ini&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;; Installing Python the Posit way: https://docs.posit.co/resources/install-python.html&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;; Configuring Posit Connect for Python apps: https://docs.posit.co/connect/admin/python/&lt;/span&gt;
&lt;span class=&quot;token section&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token section-name selector&quot;&gt;Python&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token key attr-name&quot;&gt;Enabled&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token value attr-value&quot;&gt;true&lt;/span&gt;
&lt;span class=&quot;token key attr-name&quot;&gt;Executable&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token value attr-value&quot;&gt;/opt/python/3.12.4/bin/python3&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;Service-working&quot; style=&quot;position:relative;&quot;&gt;Service working&lt;a href=&quot;#Service-working&quot; aria-label=&quot;Service working permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Restart the service and check the status:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;sh&quot;&gt;&lt;pre class=&quot;language-sh&quot;&gt;&lt;code class=&quot;language-sh&quot;&gt;&lt;span class=&quot;token function&quot;&gt;sudo&lt;/span&gt; systemctl restart rstudio-connect
&lt;span class=&quot;token function&quot;&gt;sudo&lt;/span&gt; systemctl status rstudio-connect&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;log&quot;&gt;&lt;pre class=&quot;language-log&quot;&gt;&lt;code class=&quot;language-log&quot;&gt;● &lt;span class=&quot;token domain constant&quot;&gt;rstudio-connect.service&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt; Posit Connect
     &lt;span class=&quot;token property&quot;&gt;Loaded:&lt;/span&gt; loaded &lt;span class=&quot;token operator&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token file-path string&quot;&gt;/lib/systemd/system/rstudio-connect.service&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;;&lt;/span&gt; enabled&lt;span class=&quot;token operator&quot;&gt;;&lt;/span&gt; vendor preset&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; enabled&lt;span class=&quot;token operator&quot;&gt;)&lt;/span&gt;
     &lt;span class=&quot;token property&quot;&gt;Active:&lt;/span&gt; active &lt;span class=&quot;token operator&quot;&gt;(&lt;/span&gt;running&lt;span class=&quot;token operator&quot;&gt;)&lt;/span&gt; since Wed &lt;span class=&quot;token date number&quot;&gt;2023-01-04&lt;/span&gt; &lt;span class=&quot;token time number&quot;&gt;01:18:51&lt;/span&gt; PST&lt;span class=&quot;token operator&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;11s&lt;/span&gt; ago
   &lt;span class=&quot;token property&quot;&gt;Main PID:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;20580&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;(&lt;/span&gt;connect&lt;span class=&quot;token operator&quot;&gt;)&lt;/span&gt;
      &lt;span class=&quot;token property&quot;&gt;Tasks:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;22&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;(&lt;/span&gt;limit&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;9408&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;)&lt;/span&gt;
     &lt;span class=&quot;token property&quot;&gt;Memory:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;26.1M&lt;/span&gt;
     &lt;span class=&quot;token property&quot;&gt;CGroup:&lt;/span&gt; &lt;span class=&quot;token file-path string&quot;&gt;/system.slice/rstudio-connect.service&lt;/span&gt;
             ├─&lt;span class=&quot;token number&quot;&gt;20580&lt;/span&gt; &lt;span class=&quot;token file-path string&quot;&gt;/opt/rstudio-connect/bin/connect&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;config &lt;span class=&quot;token file-path string&quot;&gt;/etc/rstudio-connect/rstudio-connect.gcfg&lt;/span&gt;
             ├─&lt;span class=&quot;token number&quot;&gt;20622&lt;/span&gt; &lt;span class=&quot;token file-path string&quot;&gt;/opt/rstudio-connect/ext/rserver-monitor&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;monitor&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;data&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;path &lt;span class=&quot;token file-path string&quot;&gt;/var/lib/rstudio-connect/metrics&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;monitor&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;interval&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;seconds &lt;span class=&quot;token number&quot;&gt;60&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;monitor&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;domain&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;socket &lt;span class=&quot;token file-path string&quot;&gt;/tmp/connect-server/rserver-monitor.socke&gt;&lt;/span&gt;
             ├─&lt;span class=&quot;token number&quot;&gt;20628&lt;/span&gt; &lt;span class=&quot;token file-path string&quot;&gt;/bin/sh&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;c &lt;span class=&quot;token string&quot;&gt;&apos;/usr/bin/rrdtool&apos;&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;-&apos;&lt;/span&gt;
             └─&lt;span class=&quot;token number&quot;&gt;20632&lt;/span&gt; &lt;span class=&quot;token file-path string&quot;&gt;/usr/bin/rrdtool&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;

&lt;span class=&quot;token date number&quot;&gt;Jan 04&lt;/span&gt; &lt;span class=&quot;token time number&quot;&gt;01:18:51&lt;/span&gt; ubuntu systemd&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; Started Posit Connect&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;
&lt;span class=&quot;token date number&quot;&gt;Jan 04&lt;/span&gt; &lt;span class=&quot;token time number&quot;&gt;01:18:52&lt;/span&gt; ubuntu rstudio&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;connect&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;20580&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; time&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;2023-01-04T09:18:52.476Z&quot;&lt;/span&gt; level&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;info msg&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Loaded server migration from &apos;/etc/rstudio-connect/rstudio-connect-migration.gcfg&apos;&quot;&lt;/span&gt;
&lt;span class=&quot;token date number&quot;&gt;Jan 04&lt;/span&gt; &lt;span class=&quot;token time number&quot;&gt;01:18:52&lt;/span&gt; ubuntu rstudio&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;connect&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;20580&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; time&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;2023-01-04T09:18:52.480Z&quot;&lt;/span&gt; level&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;info msg&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;The migration file does not affect the configuration&quot;&lt;/span&gt;
&lt;span class=&quot;token date number&quot;&gt;Jan 04&lt;/span&gt; &lt;span class=&quot;token time number&quot;&gt;01:18:52&lt;/span&gt; ubuntu rstudio&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;connect&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;20580&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; time&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;2023-01-04T09:18:52.480Z&quot;&lt;/span&gt; level&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;info msg&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Found 0 environment variables starting with &apos;CONNECT_&apos; that may affect configuration.\n&quot;&lt;/span&gt;
&lt;span class=&quot;token date number&quot;&gt;Jan 04&lt;/span&gt; &lt;span class=&quot;token time number&quot;&gt;01:18:52&lt;/span&gt; ubuntu rstudio&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;connect&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;20580&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; time&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;2023-01-04T09:18:52.482Z&quot;&lt;/span&gt; level&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;info msg&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Using file /var/log/rstudio/rstudio-connect/rstudio-connect.log to store Server Logs.&quot;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Success, we have a installation of Posit Connect up and running.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 639px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/e89d7dab0852f728e67de78999e88f29/3dbb9/posit-connect-signup.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 85.2%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAARCAYAAADdRIy+AAAACXBIWXMAABJ0AAASdAHeZh94AAACi0lEQVQ4y62TTU8TYRDH95ugEdpijIlRv4KJHj170Is3NdFo9GCCMTERLxTwZCJ4oHT7IoYoHIRyIPFsMUGa1lZWYJG2+9p22+7Lz+wuVAiYqGGS/z4z88w8mZn9jxCLxjh/6gSPHz2kVPlOPp/ny8oKpdI3JEmiWq3yZGiIk319DEajRAcGiPT3czoaDexYJMJgLMaFM4NcPRdBEEWRmZTIUi5HuVyhsLbG19VVCoUCxWKR9fV1lpeXSU5Pk81meTczEyCbyQQ+MZkkkUiQEUVm0yICePjiOg52p0PHsmi3Wpi6jqoomIZO27JwbZvDEubiOuCFuuB/zK7LWkWiuCFTb3aQVZP2EakuYO+eDQf0jkMXqDbavXjBmpugMfsKc/4NzUWR1sI0zY8JWotJrFwKa1HEyolYvr04HfjaudDXWkhiLaVozk3QmpukNT+JYEw9x4eZGsF8+xIzPYqZjodndhwjPYqRGcVIxzFSIz3dzIyFMZkxjMQLjKlhjMRw2PJxiuC5Dp7jEJzHAMHzPLzdP7Sn/yv25/Za3rv4H9mfe6DCcrlMPv+ZYrEUkLpSLgfELpVKu0QvIUmh7eu+T1W1wxXuGaZpoCgKdR/1OrquoakqDdNE17TgzjB06vUaqqoGtmVZhx/sdrthNZKELG8hyzLy9jY71Rpb8jbVuoL8c4edWi3wVWs1fkjrbGxuBh34+7736IEKXdftwWlbOE0Tp2mEaOjYDT3wud0Otm3jOE4Q+3tsXjjDfdPt7aQ5fhfl5kW0B1fQ7l1Cu385gHLjLK0Pr8N4xz6wnH6qcBRdfGlm4ujPrmOM3sEYuYURvx3o2tNrtD+9D5/w+dvLCwsSjqTAX1DoTzT7BT9R11DtvmQ2AAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Posit Connect&quot;
        title=&quot;Posit Connect&quot;
        src=&quot;/static/e89d7dab0852f728e67de78999e88f29/3dbb9/posit-connect-signup.png&quot;
        srcset=&quot;/static/e89d7dab0852f728e67de78999e88f29/0eb09/posit-connect-signup.png 500w,
/static/e89d7dab0852f728e67de78999e88f29/3dbb9/posit-connect-signup.png 639w&quot;
        sizes=&quot;(max-width: 639px) 100vw, 639px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Note: it is a 45 day free trial license.&lt;/p&gt;
&lt;/blockquote&gt;</content:encoded></item><item><title><![CDATA[Publish an R Package to a private repository and use]]></title><description><![CDATA[Overview Let’s say we want to manage an R code base as a package. The benefits might include: a good way to distribute R code for other…]]></description><link>https://mortimer.xyz/r-package-publish-private/</link><guid isPermaLink="false">https://mortimer.xyz/r-package-publish-private/</guid><pubDate>Mon, 05 Dec 2022 12:00:00 GMT</pubDate><content:encoded>&lt;h2 id=&quot;Overview&quot; style=&quot;position:relative;&quot;&gt;Overview&lt;a href=&quot;#Overview&quot; aria-label=&quot;Overview permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Let’s say we want to manage an R code base as a package. The benefits might include:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;a good way to distribute R code for other people&lt;/li&gt;
&lt;li&gt;a good way to document R code&lt;/li&gt;
&lt;li&gt;it keeps track R functions for reuse&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;What if we don’t want to share the package on the public internet (for corporate reasons), and have an R Studio server (installed on Linux) as our infrastructure.&lt;/p&gt;
&lt;h3 id=&quot;R-Studio-Server&quot; style=&quot;position:relative;&quot;&gt;R Studio Server&lt;a href=&quot;#R-Studio-Server&quot; aria-label=&quot;R Studio Server permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;For demo purposes I have installed R Studio server on my Chromebook using the Ubuntu/Debian instructions &lt;a href=&quot;https://posit.co/download/rstudio-server/&quot;&gt;Download RStudio Server&lt;/a&gt;. If you have a Windows machine this will also work &lt;a href=&quot;https://support.posit.co/hc/en-us/articles/360049776974-Using-RStudio-Server-in-Windows-WSL2&quot;&gt;Using RStudio Server in Windows WSL2&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Now let’s check that the service is running.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;sh&quot;&gt;&lt;pre class=&quot;language-sh&quot;&gt;&lt;code class=&quot;language-sh&quot;&gt;&lt;span class=&quot;token function&quot;&gt;service&lt;/span&gt; rstudio-server status&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 943px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/77592a0c0b4c1460500286f0dd1f24d4/a6711/rstudio-server-status.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 39.2%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAICAYAAAD5nd/tAAAACXBIWXMAAAsTAAALEwEAmpwYAAABs0lEQVQoz62QSW/TUBSFvaApoghUWmeem9F2bNdJ7CS2YxLSikSFgoRAEQjYsGGJhMSmoUPaItJFu+HXkn7ILwtW7HjSp3t1daR3zpH4T+/2dsly+RtpfnHJydk5J6cXzGZnzL7POTo65fj4nMsfV/xcXAsWixsWV/8m1Fzf/EKq6gYVzSKVrZFKN0imVRJJBTlaJpFS2bgf595GjPW7W6xFNomsP/pLJJyr+521TR48TCA1DJfhaIrbP8QfrOh6Y3r9CeODDwTDVwxGr2nae+w2B+hmH90M0A0f03qMprvYnafU1Q5+8Bxpp2zRcQ9oOftYjk+rG2B7Q2w/oFJvoqg9IdZ0D0UL9y6a7qM2XExrSKXWxrQG1NUexq6PVCjqwoHTndB2xvS8Q6o1h7rqks4oxOJFEsmymPFECTmaZ2s7gxwtiD0aKyDLObblPNlsDSk8VKptiiVT/OQFL2i293B6E1FDGLnrPRMpvOAlZnMoHOYLDTJZhVxeIzSVTFWoqzbS9P1X3n38xpu3X/j0ec6T/SmFHUNECuM1jD41pSPiCtSe6LBUtgjrWml8MjmVlj3iD+60PRDOPDN7AAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;rstudio server status&quot;
        title=&quot;rstudio server status&quot;
        src=&quot;/static/77592a0c0b4c1460500286f0dd1f24d4/a6711/rstudio-server-status.png&quot;
        srcset=&quot;/static/77592a0c0b4c1460500286f0dd1f24d4/0eb09/rstudio-server-status.png 500w,
/static/77592a0c0b4c1460500286f0dd1f24d4/a6711/rstudio-server-status.png 943w&quot;
        sizes=&quot;(max-width: 943px) 100vw, 943px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;Since the service is running on our local machine we can access it on port 8787 like this: &lt;a href=&quot;http://127.0.0.1:8787&quot;&gt;http://127.0.0.1:8787&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 1078px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/9b5a9343e762b8a3f18d97b6fd618d17/cf75f/rstudio-server-repo.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 68%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAOCAYAAAAvxDzwAAAACXBIWXMAAAsTAAALEwEAmpwYAAADMklEQVQ4y3WSS08bZxSG5+eA5/6Nx1dsA3F8GRvfPdgG34O5xHEIaSO1+y76Q/oTusymVAFLwUlJ00V3lRJRgpK0EkWweioPKKt28eh9dfSeo6OjI/31zw1XV1dcX19zeXnJzc0Nnz7/zfuzC84vPvPnh0//wUfOzj9yfnbBuzven33gj3dnSK/e/s7J6W+8fP2Wn6cznh8e8dPxCUcnp1+YvvqV6ewNx7M3nn/x8hcOpzNeHx7z4+ERz6cnvJjOmM5OkYbf/UDn2fcUujtUB2Pc0VMaoyc0tw/Y2PmK1vZTyt09qv0x9cGEUuchTnNErrWNs7lLvrVNfeuAzqNvaQ4nSIX+PpnGkHS9S7LaYbXcJllpk671uF/tEsutk3If4G4dUOpNiOebhNO1O6pEnToRxyXquCyXNpA6exM2th+zNXlGY+dryv1HVLoPqfTG5FsjKsN9Cpu7dHaesD4cU26PKLaG5Bp9svU2xdaAnNvDcbsU2gOk1FqN7FqFRNIhGE0QiMQJx1eJ30sTji1zP1dEC8QRliASixKMhBC2HytoYwiBqulohoGm6aiqhlSouOScPMK0kH0+fIsL6JqK7fejqQrRWBwtEEPVFCKJKIFwANMyMSwT4Reen6uqqiiKguSU6qSzBZaWVrAsG1mRMU2TcCSCYRgkVpOYkRV0TSMQtPHbFoawMCwbXdgYhokQwhs2HyrlK1UymQy2HUIIC0WWvWbLsrwNQ5EoZjiBYRrYkQjCtrFCQUTARtVUL68osqeyLCPliiXSqSS6GUDTTRR1fhOBIfzopkDYIXwizMKiD1UXqPOMdsvcz7Ne3rzdXMpv7lJo77Ba2vBI17qsFFvcK7ZYytaxV9dYLncp9x+Tdvukaj2yjSFO4wErpU3vXcKZOvG1Jul6H8ndmtCffENzax93MKba26OwMaLaGVHr7VEfjElVezilJulcFafgki81yBXXya7VSWZKJJJ5Qon7hJdTSLoVRDcMTMuP3w4gLP8tfhtrTiCE6o8iLyx4X/CFxUUUnw9VllG9G87ri0iqvYRi2qhWECsUwwrfooogsmGjiCCKHUPRdBTNuNP/w+Bf1eU6f0VNz7EAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;rstudio server repo&quot;
        title=&quot;rstudio server repo&quot;
        src=&quot;/static/9b5a9343e762b8a3f18d97b6fd618d17/cf75f/rstudio-server-repo.png&quot;
        srcset=&quot;/static/9b5a9343e762b8a3f18d97b6fd618d17/0eb09/rstudio-server-repo.png 500w,
/static/9b5a9343e762b8a3f18d97b6fd618d17/1263b/rstudio-server-repo.png 1000w,
/static/9b5a9343e762b8a3f18d97b6fd618d17/cf75f/rstudio-server-repo.png 1078w&quot;
        sizes=&quot;(max-width: 1078px) 100vw, 1078px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;h2 id=&quot;Example&quot; style=&quot;position:relative;&quot;&gt;Example&lt;a href=&quot;#Example&quot; aria-label=&quot;Example permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;For this example we are going to create the most basic package we can think of. A package that contains a single function that prints out &lt;code class=&quot;language-text&quot;&gt;hello world&lt;/code&gt;. Let’s call the package &lt;strong&gt;helloworldr&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;We want a documentation site as well.&lt;/p&gt;
&lt;h3 id=&quot;Initial-install&quot; style=&quot;position:relative;&quot;&gt;Initial install&lt;a href=&quot;#Initial-install&quot; aria-label=&quot;Initial install permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;We will use the GUI in R Studio Server to start the template for our &lt;strong&gt;Hello World R&lt;/strong&gt; package.&lt;/p&gt;
&lt;p&gt;Select the parent directory (the New Project Wizard will create a directory with the name of the project).&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 632px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/11633fc600f5278c20548555fc48eb68/afa26/rstudio-new-r-package.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 71.2%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAOCAYAAAAvxDzwAAAACXBIWXMAAAsTAAALEwEAmpwYAAACAklEQVQ4y5VSXYvTQBTN//fBZ5/0F4hoX3wsgoLYXbrb5qvJTNJM2nw1mWQmqVLKWeZmG7CuRQMHLpfJueecey3PD+G4Plxvg9XaQcgi5EWFvDwgy0uqi7LGPisg0gzp7jasYRiw2+0R8Ri67wkhC7G2bXi+GeRRnRcF+mGA0vomrE7pkTCKINsOnVIQaYogDJEIAR5xuJ6LPM+htUanOiilJnRXsJRWNLnrj5NCo8qQmj6LtvACBm/DEIs9Wn1E3WrUUhFke0VY1B2++wU+zFf4OL/DoZGI45jsZnmOlRfh28LGjwcfPG2QFBqi6JE1v7Cvf6KsFSmfLPuiwuvPa7x5/wWv3s2QZiWESIg0CAIwxglCpOCcg3FOER0ONcqyIlW/ZZhVDWaLEG8/fcVsvkAtJc7nM259p9MJIkmxjbdou26yS4R9rzH0PapaYjgOFDqPIlJoFG2TBJxHCIKQ1O2zjEjqpsGhrtF24w5M9pSh2bLB2BjrkDHc3d9juVzSyTw8PsJ2HFRVRUSybSdlxnYYMCRbAaW02fLoncio7mkZhsicziYIYNs2WBQjjMccoziGv9mQYrM84+IywHrpOC+ZmEeXh8Za03ZT7wKj1vxjHD4f9p/Heb25f8Vzhi8T/g1GkZRywkWllGOu1v9MN7ZMXq7rEhzHQdM0SMUOLGRE+gSiRhiXwz8EowAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;rstudio new r package&quot;
        title=&quot;rstudio new r package&quot;
        src=&quot;/static/11633fc600f5278c20548555fc48eb68/afa26/rstudio-new-r-package.png&quot;
        srcset=&quot;/static/11633fc600f5278c20548555fc48eb68/0eb09/rstudio-new-r-package.png 500w,
/static/11633fc600f5278c20548555fc48eb68/afa26/rstudio-new-r-package.png 632w&quot;
        sizes=&quot;(max-width: 632px) 100vw, 632px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;In the directory it creates you will see files like this:&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 1097px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/7985ddcaf60958ed990a164697069f14/c37c4/rstudio-default-pacakge.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 50.6%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAKCAYAAAC0VX7mAAAACXBIWXMAAAsTAAALEwEAmpwYAAAB5UlEQVQoz4WR3U7bMBiGczkjiZvEdn6apn+Bkv7TDOgorMBoETC0jU3TDna8m9jVPpNdxKZp0g4e+fVr+/0+fXbOv/9k+fSDw9WGycUd9btPLK4eqa8/Ul9/YL5+z2h1y+ztg+VwuWVwurEY/TdOvf3M0eUd9fqW8WpDtbyhOr7k4PWas7uvnD98o6zXlIsLevMVxeiUzvQN7cmSZnVMPjqhNTolf8Y5u33kbPvI1f0XTjZPzNcPTFdbjm6eaB1MaPdK+gcV5WBIu7dPs+hQdPvk7R5JsyBuFug035G1cPKyorM/IM4KpE6IdIJOMpKiz16giYIGSkmUlGil0HpHFIaWIGgghE9DCITv4YxmNbPpHCk1vufhui5h0EDGGV6UomSEUpo4jkmSmDhO0FoTBCYsIAxDSxRFdu8MpgsOh1My06FUeJ6PjELSvINQGbHtyIQlZGlqw6SUFqNNIaWUDfN9H2c4mzMeD9E6tZWMGTQEoU4Rumk7NGFFq6CZ5wgh7B0TZvw0/f3OnDnTxYJxVeKLYHdZCDuPhkxsYJokxGlGkhlSoli9hL5g5veMk7e7FHlzN1TPxfdcAuHZz3EDje++st6OvT/0v3Fkd4LuDkn6Y+LeDqPDoiLqTsjKCe1hbVfj/49fEkVX5s77Wf8AAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;rstudio default pacakge&quot;
        title=&quot;rstudio default pacakge&quot;
        src=&quot;/static/7985ddcaf60958ed990a164697069f14/c37c4/rstudio-default-pacakge.png&quot;
        srcset=&quot;/static/7985ddcaf60958ed990a164697069f14/0eb09/rstudio-default-pacakge.png 500w,
/static/7985ddcaf60958ed990a164697069f14/1263b/rstudio-default-pacakge.png 1000w,
/static/7985ddcaf60958ed990a164697069f14/c37c4/rstudio-default-pacakge.png 1097w&quot;
        sizes=&quot;(max-width: 1097px) 100vw, 1097px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;Now we need package down for the documentation.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;r&quot;&gt;&lt;pre class=&quot;language-r&quot;&gt;&lt;code class=&quot;language-r&quot;&gt;usethis&lt;span class=&quot;token operator&quot;&gt;::&lt;/span&gt;use_pkgdown&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Of course &lt;em&gt;Yes&lt;/em&gt; is the wrong answer to &lt;em&gt;1, 2 or 3&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 715px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/b945c8a6b0cc7e88a1784e96c174c15b/277d6/rstudio-install-packagedown.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 20%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAECAYAAACOXx+WAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAjElEQVQY04WOSwqDQBBEvY893fP/aJJJQ8SF9z9PhbgQhMQsHlQt6lFDv3UszwV97tCHYtUV22uD3hUxRrAL8HmGDRk2JDAziAjGmK8MpRRM04TWGqyzIENgYYgViAhYLMQFsLg9f0aXwlorVHUnlwzv/T44MY4gGo9+JR1qqmi5IcWEVtoh/PXg38M3VSl9uWq2qv0AAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;rstudio install packagedown&quot;
        title=&quot;rstudio install packagedown&quot;
        src=&quot;/static/b945c8a6b0cc7e88a1784e96c174c15b/277d6/rstudio-install-packagedown.png&quot;
        srcset=&quot;/static/b945c8a6b0cc7e88a1784e96c174c15b/0eb09/rstudio-install-packagedown.png 500w,
/static/b945c8a6b0cc7e88a1784e96c174c15b/277d6/rstudio-install-packagedown.png 715w&quot;
        sizes=&quot;(max-width: 715px) 100vw, 715px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;If you have installed Node.js modules in the past, or have seen memes about installing Node.js modules, you haven’t seen nothing yet, this will take a long time.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 715px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/8795811fcfe01b7bf0a45ef3cc3648d1/277d6/rstudio-install-packagedown-done.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 17.6%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAECAYAAACOXx+WAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAvUlEQVQY032Py26DMBRE+Z7ENtjGmGv7hrbqQ2pCu8n//8qJACmbSl0cjTSbmdN9iedHI78a97y2yLUG7i/jnrcW+ZRAHXtKHAi9xRiDtfYPW981vZDTiBRFdUFmYZ4LTV+RPFFEqFWRFFHJ5LIQ5gs+JmIIDDHjUyHkRpgq3bremKsyLt+U95XU3hj1A58bQyoY12Os42wsJ2Oxrj94PnNY5559l1I61mTZV/xU6AePOZ927Ka3Kx5sWv/xAImlgLOeLudiAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;rstudio install packagedown done&quot;
        title=&quot;rstudio install packagedown done&quot;
        src=&quot;/static/8795811fcfe01b7bf0a45ef3cc3648d1/277d6/rstudio-install-packagedown-done.png&quot;
        srcset=&quot;/static/8795811fcfe01b7bf0a45ef3cc3648d1/0eb09/rstudio-install-packagedown-done.png 500w,
/static/8795811fcfe01b7bf0a45ef3cc3648d1/277d6/rstudio-install-packagedown-done.png 715w&quot;
        sizes=&quot;(max-width: 715px) 100vw, 715px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;h3 id=&quot;R-Environment&quot; style=&quot;position:relative;&quot;&gt;R Environment&lt;a href=&quot;#R-Environment&quot; aria-label=&quot;R Environment permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Since we are using &lt;strong&gt;renv&lt;/strong&gt; to manage our package dependencies and versions, and we have installed a lot of package dependencies through using packagedown, we need to update the lock file.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;r&quot;&gt;&lt;pre class=&quot;language-r&quot;&gt;&lt;code class=&quot;language-r&quot;&gt;renv&lt;span class=&quot;token operator&quot;&gt;::&lt;/span&gt;snapshot&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 602px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/3f65ec514ddfa1e0eeef5255729b3615/d1b94/rstudio-renv-snapshot-01.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 24.400000000000002%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAFCAYAAABFA8wzAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAyklEQVQY03VQ2W7DMAzz7+RCEh+xLR9Nk3YLkP//HhZSl4cN6wNBk4IoySrlG9ZyR8krnts39vULa92RcsXiA6xb4BYvzIiU4EMUj+vMIZLAWAfFRkoE7z1yySCKiDGiloJEhBCCaAbrUgqsMZimCfM8C4zW0FqLp3jatm0Yx1FMYwwoEojoHVwrUkow1sJaK/W+79F13Zt/3pdWzgec54njOGTLpmngnJNABgey37btr8ZhGP6F4n95PHZpuqbzJnwqe1fw35BPoS9qwaG7gFIE5QAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;rstudio renv snapshot 01&quot;
        title=&quot;rstudio renv snapshot 01&quot;
        src=&quot;/static/3f65ec514ddfa1e0eeef5255729b3615/d1b94/rstudio-renv-snapshot-01.png&quot;
        srcset=&quot;/static/3f65ec514ddfa1e0eeef5255729b3615/0eb09/rstudio-renv-snapshot-01.png 500w,
/static/3f65ec514ddfa1e0eeef5255729b3615/d1b94/rstudio-renv-snapshot-01.png 602w&quot;
        sizes=&quot;(max-width: 602px) 100vw, 602px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;
&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 602px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/9aaf380febcdff599630ad67db2d01ac/d1b94/rstudio-renv-snapshot-03.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 16%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAADCAYAAACTWi8uAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAhElEQVQI112OXQ7DMAiDe5+k0/IDAdJE6v0P5Qmm7mEP1idshDle74Z1XSilIqeMlBJKKWitgTqBiCAiyPmb/evxned54hg8oSTYtrF0wYZh2Yp58oSQ4L5umBh671FUa/3Ryx/60YPYsPeGioKJ4xvXGCOWHs9zVQ3fzIIuZg56gR/8ABU4YF85eeC9AAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;rstudio renv snapshot 03&quot;
        title=&quot;rstudio renv snapshot 03&quot;
        src=&quot;/static/9aaf380febcdff599630ad67db2d01ac/d1b94/rstudio-renv-snapshot-03.png&quot;
        srcset=&quot;/static/9aaf380febcdff599630ad67db2d01ac/0eb09/rstudio-renv-snapshot-03.png 500w,
/static/9aaf380febcdff599630ad67db2d01ac/d1b94/rstudio-renv-snapshot-03.png 602w&quot;
        sizes=&quot;(max-width: 602px) 100vw, 602px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;You should see the JSON in your &lt;code class=&quot;language-text&quot;&gt;renv.lock&lt;/code&gt; file now contains a list of all the package dependencies (if you checked it before hand you would have seen not much at all).&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 602px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/b9e70dc2668bc38720901696b0405f33/d1b94/rstudio-renv-snapshot-04.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 65.2%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAANCAYAAACpUE5eAAAACXBIWXMAAAsTAAALEwEAmpwYAAABvklEQVQ4y51Sy47TQBD0x7CExJ6x5+3Hxk4MJLtaIGiFBBsRLjmAEIgLBw7774W67UyMWEDiUGr3eKqm+pHsv93j8PUH9p++Y/fhM3R7BbXcorjc/BeS28NHvN4fcXf8gt3dEdvb97h+c0D/8i2e7d7h6asJdv9GcvFkASkFtDYw3kJbA6UUZC4hhIAscsg8h5BDLoT8K5KL2Rw5EYicS3TbDu2mRb2qERqPqqtgvYWUElmWjaK/gs5PGB1KFk3TFNpouNLBlhba6QFW82X6H7E4xyw7i0fB+FKaIVwGdkmlt887dJsVQl2iWpLrcvhua/gqcCxUEd0nj2ZzaK2ZzA6VjuIU8yJn9wQiRmiFvBji9H7yeJ7Ce8cEX3rul7KKSeScHiHXJ/dnpONZGsW4h7N5Bucck0mESm3WDYsXxSDKAxlFHxrKFCwYgh+avkh5AN1Vh/XNGqEJKJclmr6BMgpCPjzZaZ7MqGTnYkkkyC77Br72qNqKv1fXK86lkL+JTJ2zQ2ttXAty0r/ouWzawVML+pvhzDeeXdNDFDlvSxhnILJxKHVdx9WxziIsA+8iXSZXSisYY3jx/4TTUH4CTbaki4OepskAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;rstudio renv snapshot 04&quot;
        title=&quot;rstudio renv snapshot 04&quot;
        src=&quot;/static/b9e70dc2668bc38720901696b0405f33/d1b94/rstudio-renv-snapshot-04.png&quot;
        srcset=&quot;/static/b9e70dc2668bc38720901696b0405f33/0eb09/rstudio-renv-snapshot-04.png 500w,
/static/b9e70dc2668bc38720901696b0405f33/d1b94/rstudio-renv-snapshot-04.png 602w&quot;
        sizes=&quot;(max-width: 602px) 100vw, 602px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;So this is how we got this far. But now, to follow the rest we are going to continue with something I have prepared earlier.&lt;/p&gt;
&lt;h2 id=&quot;Hello-World-R&quot; style=&quot;position:relative;&quot;&gt;Hello World R&lt;a href=&quot;#Hello-World-R&quot; aria-label=&quot;Hello World R permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;After following these steps, and making a few modifications to the function, we end up with this:&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/mortie23/helloworldr&quot;&gt;https://github.com/mortie23/helloworldr&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;If you were following, from here it might be easier to delete what you have and clone this one. Otherwise, the differences you will need to at least continue functionally are just the &lt;code class=&quot;language-text&quot;&gt;build-deploy.R&lt;/code&gt; script.&lt;/p&gt;
&lt;h3 id=&quot;Deploy-to-private-repository&quot; style=&quot;position:relative;&quot;&gt;Deploy to private repository&lt;a href=&quot;#Deploy-to-private-repository&quot; aria-label=&quot;Deploy to private repository permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Before we build and deploy the package to a private repository, we need to know what a “private repository” is and how to create one.&lt;/p&gt;
&lt;p&gt;All it is a directory on a file system! So it can be created like this:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;sh&quot;&gt;&lt;pre class=&quot;language-sh&quot;&gt;&lt;code class=&quot;language-sh&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;# Create complete path and parents as needed&lt;/span&gt;
&lt;span class=&quot;token function&quot;&gt;mkdir&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-p&lt;/span&gt; /opt/privatePkgs/src/contrib/&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Now just source the &lt;code class=&quot;language-text&quot;&gt;build-deploy.R&lt;/code&gt; script.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 783px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/f961776d685d7910836a95a844aef162/e19e2/rstudio-build-deploy.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 21.999999999999996%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAECAYAAACOXx+WAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAsElEQVQY0z2NS47EIAwFc6BE4eMEsDEw02pN3/9Cb2RQZ1EqvYXL282Kz/sPvf0g1xekf1DkF8oVQwdabeDEiD6CAi375eACjuPAfuzL+47NJ0Frij4GsiiyNKTCuK4LdBFCDIgUEeMihLWnYwQRPdz3jc1RApeC1hpU68Qe2BYRpJRQSkHOeR5ZxHv/YGHn3PR5nthOT6giUNUZ+GKBMQZ676i1Tpj5ObSIYVHbX/8DxveCvYLdUsMAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;rstudio build deploy&quot;
        title=&quot;rstudio build deploy&quot;
        src=&quot;/static/f961776d685d7910836a95a844aef162/e19e2/rstudio-build-deploy.png&quot;
        srcset=&quot;/static/f961776d685d7910836a95a844aef162/0eb09/rstudio-build-deploy.png 500w,
/static/f961776d685d7910836a95a844aef162/e19e2/rstudio-build-deploy.png 783w&quot;
        sizes=&quot;(max-width: 783px) 100vw, 783px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;Check the package was built and deployed:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;sh&quot;&gt;&lt;pre class=&quot;language-sh&quot;&gt;&lt;code class=&quot;language-sh&quot;&gt;&lt;span class=&quot;token builtin class-name&quot;&gt;cd&lt;/span&gt; /opt/privatePkgs/src/contrib/
ll&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Yep, there is the tar gzipped up pacakge and the PACKAGE repo metadata.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;log&quot;&gt;&lt;pre class=&quot;language-log&quot;&gt;&lt;code class=&quot;language-log&quot;&gt;Permissions Size User     Date Modified Name
&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;rwxrwx&lt;span class=&quot;token separator comment&quot;&gt;---&lt;/span&gt;  &lt;span class=&quot;token number&quot;&gt;1.2k&lt;/span&gt; mortimer &lt;span class=&quot;token date number&quot;&gt;30 Nov 10&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;42&lt;/span&gt;  helloworldr_0&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;0&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;0&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;9000&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;tar&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;gz
&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;rw&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;r&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;r&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;   &lt;span class=&quot;token number&quot;&gt;210&lt;/span&gt; mortimer &lt;span class=&quot;token date number&quot;&gt;30 Nov 10&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;42&lt;/span&gt;  PACKAGES
&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;rw&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;r&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;r&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;   &lt;span class=&quot;token number&quot;&gt;157&lt;/span&gt; mortimer &lt;span class=&quot;token date number&quot;&gt;30 Nov 10&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;42&lt;/span&gt;  PACKAGES&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;gz
&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;rw&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;r&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;r&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;   &lt;span class=&quot;token number&quot;&gt;384&lt;/span&gt; mortimer &lt;span class=&quot;token date number&quot;&gt;30 Nov 10&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;42&lt;/span&gt;  PACKAGES&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;rds&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;Build-the-docs-page&quot; style=&quot;position:relative;&quot;&gt;Build the docs page&lt;a href=&quot;#Build-the-docs-page&quot; aria-label=&quot;Build the docs page permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;r&quot;&gt;&lt;pre class=&quot;language-r&quot;&gt;&lt;code class=&quot;language-r&quot;&gt;pkgdown&lt;span class=&quot;token operator&quot;&gt;::&lt;/span&gt;build_site&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 994px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/16a65c74f4cd00e8a7638641fcfaf6b4/d75ff/rstudio-packagedown-build-site.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 36.8%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAHCAYAAAAIy204AAAACXBIWXMAAAsTAAALEwEAmpwYAAABLUlEQVQoz32RWY7bMBBEfR9rJHHTQlkkRS3WMnASwPe/ygsoI0EymJmPh6pGkwUU+uJ7z+P9wbZu/Hr8ZLuvhBBYloUYI9M40rYtdVWhtUYrhdYKYzTGGKSUlKJECHFySR+P4zgDln0jzBMuDoRpxI/xpB8Ct+Cxrsf6HlM3CCGRQpLnOdfrleyanVzmeSZhuwbb9zT9jS567OCwoT99619qgzt92t1iCreY1lDfalrf0rjmFXhfN4Z5o3MDSqcqElNpdKVfatTf+Y+v24qqqZBKIpRAaonQqfI8s+0HcftBXN9xcSbECaU0RVFQluXnFC/ytw+V933HeY9xI21caIYJ3TkKVfGWF+fj78iy7D8u6YrP55N1O7ivO+M4Ya3F2u7UdOGvSPt0+X+b/AadN+kIFK1trwAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;rstudio packagedown build site&quot;
        title=&quot;rstudio packagedown build site&quot;
        src=&quot;/static/16a65c74f4cd00e8a7638641fcfaf6b4/d75ff/rstudio-packagedown-build-site.png&quot;
        srcset=&quot;/static/16a65c74f4cd00e8a7638641fcfaf6b4/0eb09/rstudio-packagedown-build-site.png 500w,
/static/16a65c74f4cd00e8a7638641fcfaf6b4/d75ff/rstudio-packagedown-build-site.png 994w&quot;
        sizes=&quot;(max-width: 994px) 100vw, 994px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;Preview the site built in the &lt;code class=&quot;language-text&quot;&gt;docs&lt;/code&gt; directory.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 397px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/e05d2b722cde02fc66817232537da4b6/b323d/rstudio-packagedown-docs.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 104.03022670025189%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAVCAYAAABG1c6oAAAACXBIWXMAAAsTAAALEwEAmpwYAAADqUlEQVQ4y22Ta1PiVhzG+ThdSbgmEMiFkBAWwkVECJAAKqJEXafuVMbttp1Ox85OO33d6cf9dU50FR1e/Oec5GSePLeTagaXTC7vsXoRdndC42hBvtahMZzjDiIaRxHFehenN8U9DFHqPbzhHO8oQnX7GO1jCjWfcqNPzu6ROlzfEm+/MjiL6YfnDMMlvUnIcHLCycUnzm5+wu1P8KM1vekZdndMf7ZiEJ5T7wY0Dqd4gxnd4AR3MCcV3F1z+7Bm8eVXbn/7xmi+4I+/Hlkurvjz8W+2//zH7GqLG3+ht7yhF23wpxfJdKMN3XBDO9xw8ukL85ufSf349RvL6wcGi2vC+J5Z/MDh4obR+o7JZpvYEazvmK7viK4fCOMts3jL8dkt0809o7NbVo//Mrjc4gyWpPwwpr+4wg8u6IYxwfozm/vfWd480I3E2TW9+RXdefy0RjGdKOb87heCi890wk3CXHznjlakCrpDsayj9Fsolk3FdLDcjxh1j5Ju7x1Vr2E6TTTTSfYlvUbZdCk6fVI5RaOsKpQ7DgVNJSPJpA8OkNJppPQB6YMPe+bpPJORkaU0Bx9+IC3JSNUGKbfdJ1osGZ+c0fJ76LqNLhhYDrIsI0kSspxJ9smzLJPL5ajVariui6qqpAV4Lk9Gb5Ky3BaDoyG96BTHa1EuVzEMG00zsCyHYlFFktKvgJJEPp/HMAzq9TqKoiaM5WxuB3A4Yrno0PGbaBUrAc3niyhKiWw298zyFTCTyVAsinOFbC6XyH8BrDVaHI+nrBYOrY82htGgUCiSFv4JH5/BxPp+/zTPtnwHtD2fcTChIyQ3WlQrJrpuYdsemqa/kbsr2TRNHMehVColkjPZ/Cvg8TggvjjH9zuUyzrVqomqagnDXSCxindCrghF+JjNZp9++iLZ8xmNA4LTFV6zlYQhQimXK+Ry+Tdguwx1XU9Yin1618OnlI8Zrs5ptn1Mw0kqU6kYVKvWXkDBqlKpYFlWEozw+w3gcDiiuRrS6PmYupPURTAUfRSMd6V/lyw6qGkaB6Iy8ptQ2oyDkNPIpv1RlNpJJAt2ojqiIu8ZilWwfHneTbnmtRkFM5anfZpePemhadoJqKjPvpTl59uyv4deO0m5Mz/H9dpUKmYiUzAUae/zUICJUERtFPXdTak3O0ynM4aXt7QPjzGqNSzLTfwzjHqS9PurJwCFh6I6T5bsMMwqGopSJF/SKBRV8vlCIlWM2O9jKFZRFzEv754B/wcQnN+WRiar9wAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;rstudio packagedown docs&quot;
        title=&quot;rstudio packagedown docs&quot;
        src=&quot;/static/e05d2b722cde02fc66817232537da4b6/b323d/rstudio-packagedown-docs.png&quot;
        srcset=&quot;/static/e05d2b722cde02fc66817232537da4b6/b323d/rstudio-packagedown-docs.png 397w&quot;
        sizes=&quot;(max-width: 397px) 100vw, 397px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;sh&quot;&gt;&lt;pre class=&quot;language-sh&quot;&gt;&lt;code class=&quot;language-sh&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;# Sorry, I don&apos;t know how to run a basic HTTP server in R, let&apos;s use Python&lt;/span&gt;
&lt;span class=&quot;token builtin class-name&quot;&gt;cd&lt;/span&gt; docs
python &lt;span class=&quot;token parameter variable&quot;&gt;-m&lt;/span&gt; http.server&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 1197px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/8e3b3c3f29bc5de0c3878eaba5a9de97/7e04b/rstudio-packagedown-preview.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 54.800000000000004%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAALCAYAAAB/Ca1DAAAACXBIWXMAAAsTAAALEwEAmpwYAAABwUlEQVQoz42Ry24TMRSG52VZd1uRotCLECoIsUPlAVAlViyQ2LApUQt5AChFhCYl7Uwnc7XH9ow9/tBMQgAJWix9OvY59u/fx0GZpGRRSBlHLObfycJLMAKaCmoJToORyCRGZwlqhUxjTJ4jK01eabSqkWlCsPM+4+E4Y/ckYfPtvGc4irk3Shh0vFv0bI0W6/ngKGZzlHL3+Ss2hvvc2X7Cxu5TBq8/EuydpDwepzwapzw4junW+/0F6Q0k7H0o2Hp5zPbBITsHhwyfveD+m1MCgC9ZQ6wcl3LJXFjOC8u0sNw0DND+kWkJWu9xrcd2uJb257z1NG5Z+xdRFHIxmxFdXyOEoBSCoG4sWSEwjcV3d3hW0eO97+PvdEJ+VYuEoVA1vl36NHVNIGrHWVzxLan4FAlOryWTVPM5bfiaW6alZZJbZqXt1117jPO9QKg8oQLj+CWobUsoaq4KzVUmexZCc9voHF6kkklcIivVJag7QWksZ2HGNBGcL0oi2VDUnYPuWfwFvxYsS0GW5xQi7vf3gq1zGFVRG41RitbZ9YHbWLtd9d4YQ+CcQ2uDNqaPSmu63P8IWmtRSvUfUxqLMZofQj05/TDgRnMAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;rstudio packagedown preview&quot;
        title=&quot;rstudio packagedown preview&quot;
        src=&quot;/static/8e3b3c3f29bc5de0c3878eaba5a9de97/7e04b/rstudio-packagedown-preview.png&quot;
        srcset=&quot;/static/8e3b3c3f29bc5de0c3878eaba5a9de97/0eb09/rstudio-packagedown-preview.png 500w,
/static/8e3b3c3f29bc5de0c3878eaba5a9de97/1263b/rstudio-packagedown-preview.png 1000w,
/static/8e3b3c3f29bc5de0c3878eaba5a9de97/7e04b/rstudio-packagedown-preview.png 1197w&quot;
        sizes=&quot;(max-width: 1197px) 100vw, 1197px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;h2 id=&quot;Hello-World-R-function-use&quot; style=&quot;position:relative;&quot;&gt;Hello World R function use&lt;a href=&quot;#Hello-World-R-function-use&quot; aria-label=&quot;Hello World R function use permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;So we have a package deployed to a private repository, we can now use the functions in other downstream projects.&lt;/p&gt;
&lt;p&gt;Start by cloning this Git repository down to your machine.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/mortie23/helloworldr-fun&quot;&gt;https://github.com/mortie23/helloworldr-fun&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;In the &lt;code class=&quot;language-text&quot;&gt;renv.lock&lt;/code&gt; file note the refeerence to the Repository:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;json&quot;&gt;&lt;pre class=&quot;language-json&quot;&gt;&lt;code class=&quot;language-json&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;&quot;Name&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;private&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;&quot;URL&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;file:///opt/privatePkgs&quot;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;and the reference to our package:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;json&quot;&gt;&lt;pre class=&quot;language-json&quot;&gt;&lt;code class=&quot;language-json&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;&quot;Package&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;helloworldr&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;&quot;Version&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;0.0.0.9000&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;&quot;Source&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Repository&quot;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;If you have all the previous section complete correctly, you should be able to run:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;r&quot;&gt;&lt;pre class=&quot;language-r&quot;&gt;&lt;code class=&quot;language-r&quot;&gt;renv&lt;span class=&quot;token operator&quot;&gt;::&lt;/span&gt;restore&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;and our &lt;strong&gt;helloworldr&lt;/strong&gt; package should be installed into the new R projects &lt;em&gt;renv&lt;/em&gt; and the function should now be available.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 477px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/e52a778fcb15192e8d0d97ecddf24861/9e707/rstudio-hellowroldr-fun.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 38.36477987421384%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAICAYAAAD5nd/tAAAACXBIWXMAAAsTAAALEwEAmpwYAAABn0lEQVQoz2XOS2/aYBCFYf8bfKeJ8R0bSLAJ5h4DBtsllIRGqFWQKnXZXf/3W/HRpIsuHp2Zo1mMNK5PTPdn2pOa9qjgbl4TZmua8YhovKE3r4knW+Lxls60xO7PUYMBqp9eBSl6+PBBOv78zfHHLzYvZxb1kdX+JGTrJzbP36lfz1THN4rDN6qvb8yrFwZ5TbIoSRcl/dmWaJgTZ0tBStYn7sZLpsVnkllBN3tktnnisTowLXYM85Lxqmay3jEpdsw2exblQdxc5iyv6GY5vdFSkGyziWu5uDcOQcvHtzxaTUtwb6/dhWe5gqooyI0Gsiwjyw0UWUZTFdS/pF6nQzuIyAYZD8mQ+949aT8lTQa4totjOYJ1awmmaWIYBoZhYnzM/0iB4xF5Md2gSz/q0wk6RH6EY9uomoqiKIKqqoKm62jaO+2a+pWu60iXd3VRaFeXI10nyUtWuxe2+yPll1eK3bMwmK2YrEpm64p5UTFdlcKla3ltJENT/3vb0A1aYYwbRnhhhN+OcYMIN2hjuT62F2B7IY4finzfzU83/AHULhmvnSVh6wAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;rstudio hellowroldr fun&quot;
        title=&quot;rstudio hellowroldr fun&quot;
        src=&quot;/static/e52a778fcb15192e8d0d97ecddf24861/9e707/rstudio-hellowroldr-fun.png&quot;
        srcset=&quot;/static/e52a778fcb15192e8d0d97ecddf24861/9e707/rstudio-hellowroldr-fun.png 477w&quot;
        sizes=&quot;(max-width: 477px) 100vw, 477px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;h2 id=&quot;Conclusion&quot; style=&quot;position:relative;&quot;&gt;Conclusion&lt;a href=&quot;#Conclusion&quot; aria-label=&quot;Conclusion permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Well, that took longer than I thought!&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Databricks security model architecture]]></title><description><![CDATA[Overview Let’s say we have a brand new Databricks resource that we have just created from Azure. It comes as an empty shell ready for use…]]></description><link>https://mortimer.xyz/databricks-security-model-architecture/</link><guid isPermaLink="false">https://mortimer.xyz/databricks-security-model-architecture/</guid><pubDate>Mon, 31 Oct 2022 12:00:00 GMT</pubDate><content:encoded>&lt;h2 id=&quot;Overview&quot; style=&quot;position:relative;&quot;&gt;Overview&lt;a href=&quot;#Overview&quot; aria-label=&quot;Overview permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Let’s say we have a brand new Databricks resource that we have just created from Azure. It comes as an empty shell ready for use. You add yourself as the system administrator and you need to onboard your organisation in such as way that teams within the organisation can collaborate. To illustrate this, let’s take the example of a large group of Law Firms.&lt;/p&gt;
&lt;p&gt;Organisations can have a structure where multiple “business units” are supported by a common set of &lt;em&gt;enabling services&lt;/em&gt;. In this example the &lt;em&gt;People&lt;/em&gt; (HR), &lt;em&gt;Finance&lt;/em&gt; and &lt;em&gt;IT&lt;/em&gt; teams are central and support the different groups of legal teams, &lt;em&gt;Family&lt;/em&gt;, &lt;em&gt;Criminal&lt;/em&gt; and &lt;em&gt;Consumer&lt;/em&gt; law.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 660px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/5632d93466222a6292f6fadf6b7d3e9b/7c811/databricks-security-model-org.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 99.2%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAUCAYAAACNiR0NAAAACXBIWXMAAAsTAAALEwEAmpwYAAAErElEQVQ4y41VXWhcVRCeu02tRZ8q7VMVUQpFCGIRBF/8Q/BBBPVJ9M2iQn3wQUFbLFQRofWXgr7Y+oNI09qatllqExM3SZPsZrM3u0n2//cmm2x2s3v37/6fc2bk3KSKb71wOPfOmfnmm5kzcwHu4CEicKsrYBQXlH5mBipXv95r5mZetLK3nkAi6I+dBTN6Gazlsf8bTi1m/ltq9t93CegUwgppYaDcxP5+UVXz0UnaSEZIrCVOy3MUqFjLNwEQEWzHBcNyoNM3QSAG9E53YL22OdDp9ga4wEDftIBv5nZhIw92KXaSb+ZoLDjspiIhwnqWOvHRR+zcjHQa8L1zLsB2PMX3hASNZgsKpQo09TYIgRBaSEKnXr1LLyeBqkuf9bUVev+HX82fLl9itJWn5tLEEbcUBacUDfgMPcYDfdP2wRkXz3T7xumtlv5L3zC/9Rh/6YV3PlFSWgOOHD1zdyj4x7OOlqhp5RRtFhPEtcUhaSeXnZ0GYJwHXI/JcPczzkeIiFrtDpXKGjVbuvwkgRgurm489vxrxw74RSpM30+r6nu0qr6RvnZ2jwQzc3OBtpYC0LsG1PXuvVyIOPrmxDnnrG+5HmOMERHzhUJscaQHlst1IHUIqKsBrsahEB6F9Nw46Ilx6BRUZSdM/pUPhuggEeZrPRxLlDFf7++I0dn2JK5L/XpW3d2cOA+N+ZGDtfjkyao6caql/nmwnQkDdPrWPiGwidv0hOUhRspdHP19mOaX1tBghD4kEgpEYowNppYTQMXQPrsSz2hLYSrHZ8ldXSrb8ZED4Lje0772ziM4YqJuU/DvEpXybXKI5KE859Jjr2+8K1k20+G3qJ6jyPWLdnDoZ8fbyJBbjr0NrsdewW16QkYnlxBIbY+I7SR1W74NaJjWSQmop2ZfdddTdObEB2Jq6EfhbaTJKUZeBsvxnpSKjAvMVNu4rLXRdPw6kMOQyg0D13WbmJDFQan3erZQBoDDYBai3zUy80S1DLkV9bzfbum11j1EuLbR5RSKp8X04gKla7bPrdIw8cMrKn5xM4lNQ8hUunrPOiQZVkzaFZP3b2tlsJ25NShl3eiV7cYn5B9tmUhzybKbrqyKUsvzQ680bRwKF3B4oew0DS6TcU5vdyB16UtoVdKg56JA6zHwqnEfx0zcAOjqDbh6bURxGR+RRtUOI5f7+WJybxoc25YgLlAN/jV1CODBPVuxmw93s+HjvWz4zeqN7/dWQ79Bf2VcsdMhANfzFMt2oK23Bgj5N0To7ty92zWSVbrAXfs+ySI9O/ZUKzOvr8yOk5w4Zik2fEFGCQB2ehJAcAae5ymubfi0O4b9kOuxo4yLj12PHetZzuDtXu0UE8C0xc/ltFlLRk29tMx4LUNmavJxuxABpzgfAI8xf3yZtiOnTcB2Pdk5wIXwd9nnRKR4+vqA2MyBVYqdoM0snQ5G3cuzS0T1NNXj44ed/Cw4hYjiV9q0XX8WtnumBFAY57u4EANyl5Ooa1jg5GYUOzkOTiK4z9Hic+vZODXyi8QqsU+pVwXWaSh2cgLgTn8BTjHqT20nOw36xeO7SQs/R+WZR6m3AdroObDUa2BnpuAf0zebDPViqGcAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;databricks security model org&quot;
        title=&quot;databricks security model org&quot;
        src=&quot;/static/5632d93466222a6292f6fadf6b7d3e9b/7c811/databricks-security-model-org.png&quot;
        srcset=&quot;/static/5632d93466222a6292f6fadf6b7d3e9b/0eb09/databricks-security-model-org.png 500w,
/static/5632d93466222a6292f6fadf6b7d3e9b/7c811/databricks-security-model-org.png 660w&quot;
        sizes=&quot;(max-width: 660px) 100vw, 660px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;One of the organizations has put their hand up to be the enabling service of IT.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 846px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/b9f8b89e5889cbbf501f141bf380226f/4ec94/databricks-security-model-law-firm-members.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 72.39999999999999%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAOCAYAAAAvxDzwAAAACXBIWXMAAAsTAAALEwEAmpwYAAADlElEQVQ4y33Ta0yTZxQH8P/blwKt9LKJZGWzjCLYUrmkK9AilkJpaTbLbdiOi2MEGSrKxTEwEceGgzZOcFOHLJsuG8qHJfuwJftAsiyI4NwFylAIykVgUwm4cRE2ZM6zlCKJifokJ3k+nOeXc5L/AzzlXLg2jbHbc8zI5CKIyJeIvGYWCXN/E7Mw2svMDfdg4fqPcVP9l1pnBy8XXe4fx1NPx9Asp+/mEvpu3Ytv6RqZ+sZ56wci4hMR6EY77l9r9/9n4upMd0cb/TnUTXSzL/uJmCLGiM7hWdY5fhe9vy8Wnr80TN/2/rE8vUSS2y5w8hfQ+M8RCxNX6WjrV/8N9HQRTQ2881hMaR+E8r1f0dx5B9+P3UdR2zI7Or2cPTi5HDc3t4SBsb+Y9nnC/APC0g1n4+TolYXZUecFGul67hHooP3jlWpw1CJx9wdQl3yJbQAjC5Y/A4AHgK/S6J51d/fgixN1K7evm49FpL9qEc5fuYgngEeQ6fgOYQ3jbGTtT94gYh72BHxGHmFHnN7yuiEWOIX+0hrP9wHPi1qr10xrC+exYIX9E85RRx0ntLKNRRuxlvwSWaLBZN4gDdVrd9WJ5WfIM7K6g60qO4/eV97gXs/bzxtIK/ByatIZKDTGtTLbClHfdA4HHU38Svtpbm4gONaM7chJ1b+dp1eTUrrxnshfEuwbmQgJwJQXn0OPaSf6krLX6hFQGhaL9PwSVNaf4tccP8tu8fPxCJWIEBX8fLVNpSC1VELgeStd22wJ8uccsBpwel8WZ0dMJFueksj+1lixBnooNEZhqNYkWh8Yzq05fpZfWH7YI0QejohNUtf7jVmxqj1JypAsajNDHeSH1EghSjNimMZSK0xRSuxOT0D351XuvClijGKFxihTaIyBweoEvwPvNohdiq/POmm0clOxTqUoAliRQMSI5S+Ii+M2b9hrUooCilOjUL8nPW6nOfat/TsMr7dU53Dd4OrKAasrF+yrFLhAHpebYtO/RGVWE4l465KD1sOcu3Uz1ebqaXu4MO1QTjzOHC74NFenovxk7d07nSeFDydcAZNtuxh7cyveLDskdIGegDnNEEd7czLJhyeIlwmQUJGhoY9KbQ/iAzkvW6KDkGXa+qFFG0VpuuiJ0td0Avc3W52wyhUbRxPKao55/0vkypQwKcWqS87M0wOMj5iFwJZhic/LL9wmBETusLEvhqu0BllIRBQA5n+p/GV7HJYEtgAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;databricks security model law firm members&quot;
        title=&quot;databricks security model law firm members&quot;
        src=&quot;/static/b9f8b89e5889cbbf501f141bf380226f/4ec94/databricks-security-model-law-firm-members.png&quot;
        srcset=&quot;/static/b9f8b89e5889cbbf501f141bf380226f/0eb09/databricks-security-model-law-firm-members.png 500w,
/static/b9f8b89e5889cbbf501f141bf380226f/4ec94/databricks-security-model-law-firm-members.png 846w&quot;
        sizes=&quot;(max-width: 846px) 100vw, 846px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;Databricks will be used by both the business units and the IT teams (to do bespoke analysis of IT systems, such as cost monitoring of cloud resources and monitoring usage of systems). The teams may have the need to keep their work separated and inaccessible from each other, and may also have the need to share.&lt;/p&gt;
&lt;h3 id=&quot;Bi-modal&quot; style=&quot;position:relative;&quot;&gt;Bi-modal&lt;a href=&quot;#Bi-modal&quot; aria-label=&quot;Bi modal permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Gartner coined the term Bi-model. See this article: &lt;a href=&quot;https://www.gartner.com/en/information-technology/glossary/bimodal&quot;&gt;Gartner Glossary, Bimodal&lt;/a&gt;.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Bimodal is the practice of managing two separate but coherent styles of work: one focused on predictability; the other on exploration. Mode 1 is optimized for areas that are more predictable and well-understood. It focuses on exploiting what is known, while renovating the legacy environment into a state that is fit for a digital world. Mode 2 is exploratory, experimenting to solve new problems and optimized for areas of uncertainty&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;There are many words used to separate these two modes, I have listed a few I have seen:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Mode 1
&lt;ul&gt;
&lt;li&gt;Operational&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Mode 2
&lt;ul&gt;
&lt;li&gt;Discovery, Laboratory, Exploratory&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Here we are focusing on mode 2 (some mode 1 thrown in here too) and will denote mode 2 as Discovery&lt;/p&gt;
&lt;h3 id=&quot;Components&quot; style=&quot;position:relative;&quot;&gt;Components&lt;a href=&quot;#Components&quot; aria-label=&quot;Components permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;There are many components of a Databricks workspace that need to be considered in the security model. We will try to cover off a lot of what is included and have a default position on each component. Having this upfront will attempt to avoid business users having to ask down the track for a component that hasn’t been considered.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;a href=&quot;#Workspace-Settings&quot;&gt;Workspace Settings&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#Users-and-Groups&quot;&gt;Users and Groups&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#Workspace-and-Repos&quot;&gt;Workspace and Repos&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#Compute&quot;&gt;Compute&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#Storage&quot;&gt;Storage&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#Secrets&quot;&gt;Secrets&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#Machine-Learning&quot;&gt;Machine Learning&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 1048px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/26ae70089a07964ea0c47053596e1c45/f0257/databricks-security-model.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 96.2%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAATCAYAAACQjC21AAAACXBIWXMAAAsTAAALEwEAmpwYAAAE3klEQVQ4y42RezAUCBzHv7vtJqw3peucudCWXUKykdWJcGlyIY9jw50VsYZJYj3SJbY7DSVndi660gPJpfKqqzYK5/3clMmku+rq6jiW7tH43ejun5umuu/M59/PfGa+kIy8gEjxnNHWcpA5OngQvd0FXt29pW70AzR/vWFjpLrONZxRWBjMNJsZqG4sN5xssp9/d1gI3CeYJ7UAgY2YbbqMkdZO3CMCEn8ihN2aAqCC/FIdyuTJS1PLa/jezapgogamQlwLAtCz8xBoCPP+VDB16BKAPILx7g4u/BVLZvvbLO50920ZnRwHEh+SVliriguvZrZz/gAHNsTCVoWGQ+RBU4gq1e5FJRjQp+s5D4tj0VVXaPyoNcfi6WA6yG2DvmWY3FLTR/7hYPlJ8/rS08Gjc4Whl7qMfc90CfVTutRXFj/REnw1qGV/aEzLJvxrDYjL1PMi4rkNPu6LbhbsRXZ6yoojX2ZZXbNw12sNkKh/Im0Hwp6Bum6Cnj5Hw+1+BgAwI4hgV6m0W1Y65GQVM7KAJ7utxz88ZmROUzCpHHSxzq+3y87KNAGM1NxMVhnf9BAZSsKkWCw9r79QdFQHGQUgIiSmJgP8yj6sbpv6YPW5fldtgOVGxHC42MfYRATb+kfuQfkVvMOOXouT/D/nVmYe4HTGpBt5x23XXZIVY6Trt0N39fV8IZeOL06LThTsSt1tBdszt7DyhMJ0TUWLzapjV+Fw4iojk7bCZOZ78Mt61kvTjtjQQksOvb9ObX9cikV85gF744QAnYXRm1kWWbsg2BdjjOxt6rExEmF0XLwV1k4SHCcJG18QPrr7EnuIsLJoDC4FyvnYplQnDW1NuTiHVRgrhThT9t7O5D3WzlW3zFyrWjjrTrUCRIjbL8UOSfz6uPgEawhnCU6zBO+/CJ4vCbZHH4GXrYQwo41tIP9Nd9zJS/ucOFVzf2GJdnhMkn5UgnSZj+KxxsbyXrZn8zDsqB2Skpy5L+YBC5hY7ujxihX/wstRgi+7A5esDrb+oZ91yJSvkShJWxKRstcrL2UvEtMLEbDvOUJ3PYEltSMPOciQfgFADYAOXhs/984rhFkdbN3Dz3Qeu/pon/EUGRWzTDk5SRmWCRlFa4LXkJpY9IQTvWUcUX4ziA0hLBe4v+K18WTD4MnmpHfZfJlSz17WADK0Zo+GJGpFB4Sb+0Vk8jZvaNbaHvyH8WcBz1jiwGnM8cb9UzgM19waNrJJK/LAbqH0eC63sOYk6pw3IjqtAJH+LxEZOK0rDpxmRQapMMebhbJh2KSeRuyF7wAJMZ3TSzzE35xdJx/r4aGJsC25nhnhOwVxkEpfHKT6H4VFSnh3TMDh8kOm9YUHMCsfcPL4tlaYWl3lb5Y3wqF2MIBriApW6UcGqliRgSqIA99WeHYAguv3mYIbP0KgeLDO9vRVKwCsj8u6V0Q1TYQ/6ItH0XmCyHdGNyZ0mrU9ZBqRQW8ptKruh2NJI8PxaCMcS65wrU40mli2y7UBwpXjuoLpTustFYPkGxI4tihX9DvTj//iHYXV/XCpG8La2gGGc+0QbOsHYVFZrI3aDr28Y7ylnTWeglPds6JNoX1qfMkvyK2YeLdQeLEfThd652A4nb8H2+ojoJ55GOzYgSQiVlXvRDCBIDipRGnv+H+EfwMAcyM6zkp6nQAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;databricks security model&quot;
        title=&quot;databricks security model&quot;
        src=&quot;/static/26ae70089a07964ea0c47053596e1c45/f0257/databricks-security-model.png&quot;
        srcset=&quot;/static/26ae70089a07964ea0c47053596e1c45/0eb09/databricks-security-model.png 500w,
/static/26ae70089a07964ea0c47053596e1c45/1263b/databricks-security-model.png 1000w,
/static/26ae70089a07964ea0c47053596e1c45/f0257/databricks-security-model.png 1048w&quot;
        sizes=&quot;(max-width: 1048px) 100vw, 1048px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;h2 id=&quot;Workspace-Settings&quot; style=&quot;position:relative;&quot;&gt;Workspace Settings&lt;a href=&quot;#Workspace-Settings&quot; aria-label=&quot;Workspace Settings permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;There are many workspace settings that can be changed depending on what your requirements for the platform are. It is difficult to prescribe a default set of modifications to make, however one recommendation would be to Enable the Web Terminal. Users are able to submit shell commands from a notebook regardless of this setting, it may just make it easier if they are comfortable using a command line.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 832px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/234e700aded9bb6987df427b5aa273c3/6a350/databricks-security-model-workspace-settings.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 67.60000000000001%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAOCAIAAACgpqunAAAACXBIWXMAAAsSAAALEgHS3X78AAAA3ElEQVQoz52RsVLEMAxE/f+fxUfQMtBAgn0+25IlR5YMSSi5m3HebKFmR6uV+1rWGG/rsojImMQBYgFMOZcClQiJfUw+JqxsY6g91G72CSJQ3TTXVmgLpX18w7sHX1rdFNv/AtYm6jIS0Eaim45D1m1024fWH4rFRM3VigiAtdokY5hDRFUdY9g8DgFFpPd+xQwApUDOWVWnzcx8HHAFR0Q2v/Nvc8V6ra0zNvWuZ4yZPx2xQwjpnpiZGp+1P0e1AzWfaf9zSukWgg9hjZ6Z21OYm3V5W+4vr5+/5h8jVzk98nAduAAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;databricks security model workspace settings&quot;
        title=&quot;databricks security model workspace settings&quot;
        src=&quot;/static/234e700aded9bb6987df427b5aa273c3/6a350/databricks-security-model-workspace-settings.png&quot;
        srcset=&quot;/static/234e700aded9bb6987df427b5aa273c3/0eb09/databricks-security-model-workspace-settings.png 500w,
/static/234e700aded9bb6987df427b5aa273c3/6a350/databricks-security-model-workspace-settings.png 832w&quot;
        sizes=&quot;(max-width: 832px) 100vw, 832px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;h2 id=&quot;Users-and-Groups&quot; style=&quot;position:relative;&quot;&gt;Users and Groups&lt;a href=&quot;#Users-and-Groups&quot; aria-label=&quot;Users and Groups permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;h3 id=&quot;Azure-Active-Directory&quot; style=&quot;position:relative;&quot;&gt;Azure Active Directory&lt;a href=&quot;#Azure-Active-Directory&quot; aria-label=&quot;Azure Active Directory permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Our Azure administrators have some nice rules and naming conventions which will help us down the track. Let’s create the organization structure in Azure Active Directory.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;LawFirms-&amp;lt;Environment&gt;-Databricks-&amp;lt;BusinessUnit&gt;-&amp;lt;Role&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Component&lt;/th&gt;
&lt;th&gt;Values&lt;/th&gt;
&lt;th&gt;description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Organisation&lt;/td&gt;
&lt;td&gt;LawFirms&lt;/td&gt;
&lt;td&gt;The name of the organisation&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Environment&lt;/td&gt;
&lt;td&gt;Dev, PreProd, Prod&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Resource&lt;/td&gt;
&lt;td&gt;Databricks&lt;/td&gt;
&lt;td&gt;The name of the resource&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Business unit&lt;/td&gt;
&lt;td&gt;Family, Consumer, Criminal etc&lt;/td&gt;
&lt;td&gt;The name of the business unit&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Role&lt;/td&gt;
&lt;td&gt;DataScientist, DataEngineer etc&lt;/td&gt;
&lt;td&gt;The role within the business unit&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Firstly we create a series of groups that assist us in setting permissions at the group level (not user level).&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 421px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/bcfae8c8af8ff7e6157a93b716965482/edfbf/databricks-security-model-aad-groups.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 54.156769596199524%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAALCAIAAADwazoUAAAACXBIWXMAAAsSAAALEgHS3X78AAABhklEQVQoz5WSzWoUURCF+/nyEq7EF0iQ2fsIbsSNCEJgxD8QF7oXNFtdKZqETGY60z3d96/vrVM/0t2OE7LzLC4fBYdzqLoVM4cQQFRv6m63MzNV09vvDBPfUTWbVRWEkrP9j0bzMGlxcvJyuWSzJpDL0g/sMjeR28S+SJ95l1h032VqUQFIKfXOPTg+Pn37rpjVnrqB24g28cajiXCTcxtwp3nFQExJieJiQa9eF7M4YIAkkgyJRQYaORTOkFDGRj5zPzCLjmafkjjXHh3FJ08bs7ovc842YtVTHdAkXnts/Nhl7VB7XDvCaBamUgro8afTj9+/mFkiLqwZUiADxnziiekvz51VtSIg+NBFd+/Zw+ef3wSyq77cRN54rgMuO7p2Y+BNwEVHlx2tPWQ63mHbAH9Y/vh2tjUzgrAoi0AUIyhYiBWs81z3qkDwwfk+P7r/9f2L3y3reVvWHlc9rRx+NnTR0U3A2uNXW853ZeXosG1VZWZVdV0uhffz+VPd5sOV/iX/AbrQc+hsga/yAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;databricks security model aad groups&quot;
        title=&quot;databricks security model aad groups&quot;
        src=&quot;/static/bcfae8c8af8ff7e6157a93b716965482/edfbf/databricks-security-model-aad-groups.png&quot;
        srcset=&quot;/static/bcfae8c8af8ff7e6157a93b716965482/edfbf/databricks-security-model-aad-groups.png 421w&quot;
        sizes=&quot;(max-width: 421px) 100vw, 421px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;Now we add the users to the groups.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 580px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/38e6112c9c75e0bd54b4180e5b0df78e/12f38/databricks-security-model-aad-group-member.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 53.2%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAALCAIAAADwazoUAAAACXBIWXMAAAsSAAALEgHS3X78AAABiElEQVQoz42QwW7bMAyG/e59k75NgfbSQxpntpMicdbYsizJoiRSolTY6YptwIB9B15I8CP/qpRCKQtgCWkCVi5izIOm2bNyebQ8mjgDTZZNyOVPqieB5/7n/m1XHw6X86XrurZtu7bd7Xb1/u14PL6/n/r+cjjU9X5/uw3eh5wzMyNi9SyCVEZrTUTe+3uNhN67sOIRkXNGxBACETFzKSVvVMzZWqu0MsaklAJiIhSQRg12Mcx5dtEYrbTWSscYedN+nR2QgncObPAuxhjuOooBaXUyU0yrdjOnlHjjy3wd5eljavuxHxXFaJZFKYWI5T+oFo9qAWOdsRAwxBWKkX6B2icJKDQYH/+Ku/IBpZSTEFJKC5bWqHwI/r4iRRoNnj7m800I7UopnNJ3bFUpxRujh+GeITN75yzA9/rgrDW6bO112PtpmsDBdnYp4vFRPjzMROI2zPNsFmPBMnP6je2dyMwAdhhGRColV8ba9vW1fXlpumPT/Giapt04/YOu6+r6cL1eAeATgn90A7QTtDoAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;databricks security model aad group member&quot;
        title=&quot;databricks security model aad group member&quot;
        src=&quot;/static/38e6112c9c75e0bd54b4180e5b0df78e/12f38/databricks-security-model-aad-group-member.png&quot;
        srcset=&quot;/static/38e6112c9c75e0bd54b4180e5b0df78e/0eb09/databricks-security-model-aad-group-member.png 500w,
/static/38e6112c9c75e0bd54b4180e5b0df78e/12f38/databricks-security-model-aad-group-member.png 580w&quot;
        sizes=&quot;(max-width: 580px) 100vw, 580px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;This is repeated for all the users and groups in the New York law firms organizational structure.&lt;/p&gt;
&lt;h3 id=&quot;Databricks-users-and-groups&quot; style=&quot;position:relative;&quot;&gt;Databricks users and groups&lt;a href=&quot;#Databricks-users-and-groups&quot; aria-label=&quot;Databricks users and groups permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;We will sync these users and groups from Azure Active Directory to our Databricks workspace using the following process &lt;a href=&quot;https://learn.microsoft.com/en-us/azure/databricks/administration-guide/users-groups/scim/&quot;&gt;Sync users and groups from Azure Active Directory&lt;/a&gt;. If that is too much overhead (which it may well be) then syncing the users manually and creating Databricks local groups manually may suffice.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Note: if we are going to do this manually, we should put careful thought on how we might migrate to the SCIM method later down the track. Following a naming convention that will make the migration easy would be beneficial. The manual method will also become cumbersome as more Law firms in New York start to see the benefits of the platform and want to jump on, or as Lawyers move between firms, which we know they do, and we need to change their membership in multiple places (Azure AD and Databricks).&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 733px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/8e9b79e78f059183126a3ec5897d76c8/6e70f/databricks-security-model-databricks-groups.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 36.199999999999996%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAHCAIAAACHqfpvAAAACXBIWXMAAAsSAAALEgHS3X78AAAA9UlEQVQY033Q2VLFIBBF0fz/V6qlV4YQwhDoEawQfXU9c7p2sZ0pG+tyznVhImEWJmYSEVUREQCwxsQjikgIwd8cIm4XcMjgjvPlgtlj7QiknRRZa71SLldrYwxVJaIxhogQEYuK6NaQ9wyxkkt4VPQZQro+7X3IOH+mXGrVhZl1lTBRQyaWrQGb1PeCr9h9BpfAnd368Pb+8fVtSqlzzrGIyJ0wVJgbCrJsHfkocJTuUjtKjxfuBcsFL2O/rQsxPss55+/4TiAgoTWW0sA6Z/0ez/N5qqrjzxMMAK01WYjo+cht/muM0VpLOXnvaynMnBeArqo/SeqU9Jqxda0AAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;databricks security model databricks groups&quot;
        title=&quot;databricks security model databricks groups&quot;
        src=&quot;/static/8e9b79e78f059183126a3ec5897d76c8/6e70f/databricks-security-model-databricks-groups.png&quot;
        srcset=&quot;/static/8e9b79e78f059183126a3ec5897d76c8/0eb09/databricks-security-model-databricks-groups.png 500w,
/static/8e9b79e78f059183126a3ec5897d76c8/6e70f/databricks-security-model-databricks-groups.png 733w&quot;
        sizes=&quot;(max-width: 733px) 100vw, 733px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;By default, all users are in the &lt;strong&gt;users&lt;/strong&gt; group and cannot be removed. By default the users group may have settings that you want to control separately, so lets remove all default entitlements from the &lt;strong&gt;users&lt;/strong&gt; group.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 339px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/b041a73d5a1885ec7b0b4c2e93575d00/527f2/databricks-security-model-users-group-defaults.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 84.36578171091445%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAARCAIAAABSJhvpAAAACXBIWXMAAAsSAAALEgHS3X78AAABtklEQVQ4y6WSbXObMAzH+f5fb+vapElogRj8AMFPwk8yO6DZlax3226/Fz7ZsvyXLBWTjzU3Qk69cnycyADSOi6nbgAJcbjdSNf2w8AZ987Pe4qYUFpvXbAumikoCD4k6+JoPPg0OaeU0lorqUKIj8Hzf1B45+Q4ctEDTJ8d+W+Cb0KcT6fL+3vf9/lflXPeheScM6aMmDNu8mmxc97W/eWCcUqupGkapTTGdCqrb09Px+Pp+8vpJtWcsW6unDHW0bZtuRCPypgWEDGmVDJzIPq10weiBuPnOa+ehRhjivfNSvHHOh9S3SmHEIQQlFJjbIzR7wgPeO8Zo13bdpQ55wrvPSHX4/E4DGPOea0gLRl+MuLK9l5dV88/nl8OrwCw1Nz3vRBCyrGqqqZp6rruuo4Q0rYtY6wsS0opIWSeZ0QEAET8SDulZFYAwNwBAK21McZaO47jdoi4/CwAhBC2Dy4QkXP+9vZWVVW30vd9Xddi35UNRNRa75QBQEq56RhjtNbWWucc/kZKyVqb7t1baqaUlmV5Pp/rlcvlYoz5sjcpJaVUSulDGde5e+jnOo5fE2P8NSQ/AWoY2r5BXEQrAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;databricks security model users group defaults&quot;
        title=&quot;databricks security model users group defaults&quot;
        src=&quot;/static/b041a73d5a1885ec7b0b4c2e93575d00/527f2/databricks-security-model-users-group-defaults.png&quot;
        srcset=&quot;/static/b041a73d5a1885ec7b0b4c2e93575d00/527f2/databricks-security-model-users-group-defaults.png 339w&quot;
        sizes=&quot;(max-width: 339px) 100vw, 339px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;Now, lets add at least &lt;em&gt;Workspace access&lt;/em&gt; back for each of our custom groups from Azure Active Directory.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 678px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/493e4452895e75aaa72254afefa416c6/dc057/databricks-security-model-workspace-access.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 25%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAFCAIAAADKYVtkAAAACXBIWXMAAAsSAAALEgHS3X78AAAAmklEQVQY032Nyw7DIAwE+f+vjFRVELDNswkBm1Q0vfTQzGlXGu0qH4ID5z3lFAMRAq7raqzz3gPhCgAISOScNWY2a8227dpoAFAppVJKKq/loS2GWmuMMafELGMMEWFm+WWMwR/U1Y/OVGre22XzNL7eDSrn7JxrrXHvwtz6pPX+1IaIWKT/R9VaEZGIYozX3nmOo8mCM9xfvwHYzCG51YUdfAAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;databricks security model workspace access&quot;
        title=&quot;databricks security model workspace access&quot;
        src=&quot;/static/493e4452895e75aaa72254afefa416c6/dc057/databricks-security-model-workspace-access.png&quot;
        srcset=&quot;/static/493e4452895e75aaa72254afefa416c6/0eb09/databricks-security-model-workspace-access.png 500w,
/static/493e4452895e75aaa72254afefa416c6/dc057/databricks-security-model-workspace-access.png 678w&quot;
        sizes=&quot;(max-width: 678px) 100vw, 678px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;So now we have a Databricks workspace with the users and groups from our organisation, we need to setups some things to enable them to work both siloed and collaboratively.&lt;/p&gt;
&lt;h2 id=&quot;Workspace-and-Repos&quot; style=&quot;position:relative;&quot;&gt;Workspace and Repos&lt;a href=&quot;#Workspace-and-Repos&quot; aria-label=&quot;Workspace and Repos permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;h3 id=&quot;Workspace&quot; style=&quot;position:relative;&quot;&gt;Workspace&lt;a href=&quot;#Workspace&quot; aria-label=&quot;Workspace permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Using source control is going to be the recommended way to manage notebooks (code). But in some instances, your business units data scientists may not be comfortable using Git, or maybe even the IT team hasn’t provided them with access to a remote Git service (such as Github, Gitlab, Bitbucket or Azure DevOps repos).&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 525px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/183fc51122337b5f9c94123192b97136/635dd/databricks-security-model-workspace.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 53.400000000000006%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAALCAIAAADwazoUAAAACXBIWXMAAAsSAAALEgHS3X78AAABBElEQVQoz4WR61KEMAxGef+X1BFZFoTe0qQtaaIDDLp2HPf86zRnki/phuE2TfdpmgHg8xlbKe/923gbnfMi0nnvvXPW2hhjFakXIqKyo6rnU1S3bfPeQwCitMv3cQcAcs50gYjGGGae79Mw3IgoXhBRSskYg0Rd3/fjOC7LYt0P1tplWbjWiAgASFRK4Ytaa0rEzN3Ly+s8fyAiPYCHIyIpZ+f9akyt9TF8rVVVu3VdiahZjB7xdjkl5xxiW7DxVkU6RIxHkz9lShRCYOZGZuZ9YTFG52z4fSdVPb9zzs3AJ3KODQDGmFJKI5dSVPX/s++dQwhN3SFvIs9kiLEJ/J35aecvdbGDGMHDmdsAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;databricks security model workspace&quot;
        title=&quot;databricks security model workspace&quot;
        src=&quot;/static/183fc51122337b5f9c94123192b97136/635dd/databricks-security-model-workspace.png&quot;
        srcset=&quot;/static/183fc51122337b5f9c94123192b97136/0eb09/databricks-security-model-workspace.png 500w,
/static/183fc51122337b5f9c94123192b97136/635dd/databricks-security-model-workspace.png 525w&quot;
        sizes=&quot;(max-width: 525px) 100vw, 525px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;We create a top level folder for each business unit at both the Workspace level and the Shared level. The Shared folders are optional, and should be communicated to business that they are &lt;em&gt;use at your own risk&lt;/em&gt;, as other teams can read/write content in them.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 626px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/28d08506d87ecf0adb368291b995d5a7/ce661/databricks-security-model-workspace-permissions-family-law.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 46.6%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAJCAIAAAC9o5sfAAAACXBIWXMAAAsSAAALEgHS3X78AAAAzklEQVQoz42Q3Y6EIAxGef931DslMAZoK1h+ym5Wdswk44XnsuXkK5+ik/0EABAxpfTzgN67Wtd1nuch9DdPTBFRcvJE+EY557TWtztr7TRNy7LcJv/Jr9fLe8/M32fXWhERAETk0mqtzPwvO+e2bbPWhhBGYQMRKaXs+05ErbXxSRFprZVSRpiKMY62mflajxZKKSklIso511rbG2ZGxFqrYmYiijEi4sgBgOM4eu/XU+99znlMcs5EpLUGAHXbc//gs6GcszEGAIwxIYRflCcO/5liPigAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;databricks security model workspace permissions family law&quot;
        title=&quot;databricks security model workspace permissions family law&quot;
        src=&quot;/static/28d08506d87ecf0adb368291b995d5a7/ce661/databricks-security-model-workspace-permissions-family-law.png&quot;
        srcset=&quot;/static/28d08506d87ecf0adb368291b995d5a7/0eb09/databricks-security-model-workspace-permissions-family-law.png 500w,
/static/28d08506d87ecf0adb368291b995d5a7/ce661/databricks-security-model-workspace-permissions-family-law.png 626w&quot;
        sizes=&quot;(max-width: 626px) 100vw, 626px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;So now, logging in as Robert Zane from the Family Law business unit, we will only see the Family workspace folder.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 697px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/9c46fe5e258bfee78f76c0595125e7ed/11663/databricks-security-model-workspace-robert-zane.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 46.4%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAJCAIAAAC9o5sfAAAACXBIWXMAAAsSAAALEgHS3X78AAAB00lEQVQoz3XNW2vTYAAG4PwEQYqKSm0OXdsl3ZI23bKkhzRrHcwDE+90DFT8AbvQn+A8DNbVdLQiMhERJl554Y9ZHbsozZo269d8aZp+XxJxNw5kD+/t+76ENC8sFzW1XOUFkRdyvCCKOSmZ4kgqfitGn0+MZEiKiUYpkknQTGJmJkWo6rKSV6u3VzVtJStKklwolLSSWpHkfFZcWJSUQr4kK0Wez6bnhMrK6qP1jXK+tLQoZxdkIp5TSEG6wWWuJuavs0JclG+mxeicWL6zpt19wC0VL9NshOEuxZJX6NkklZplM1ScI2k2zbDENU5U7j18pze3avp2o9X8/LX24dOb+t7+t4MvBz/0j/uv3zff6q2t3b1XO3q9prd2Gw292dKb9Z06kSpUnm2+gPYI2rY9Gvm+700mp5aFEOoPBhN3El7A9Txiu9H6/vNXp9PpdrtDAKDj2BBCxxmeDsfjcXAGY4zwPwihMAx7pklUC+Xnmy8BdAAA0+kUI/Q3GOMgcBwHng1hjM9/BkEQhqFx0iMiDHf/8ROzfdg/bE9NE1kWsizPMFzDGJz0TLPvuhPf9/8vDwEgIqnM2vpT53cbHB8jCPFohABAAIyPjjwIw4u5rvsHyxdUBOoVyd0AAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;databricks security model workspace robert zane&quot;
        title=&quot;databricks security model workspace robert zane&quot;
        src=&quot;/static/9c46fe5e258bfee78f76c0595125e7ed/11663/databricks-security-model-workspace-robert-zane.png&quot;
        srcset=&quot;/static/9c46fe5e258bfee78f76c0595125e7ed/0eb09/databricks-security-model-workspace-robert-zane.png 500w,
/static/9c46fe5e258bfee78f76c0595125e7ed/11663/databricks-security-model-workspace-robert-zane.png 697w&quot;
        sizes=&quot;(max-width: 697px) 100vw, 697px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;h3 id=&quot;Repos&quot; style=&quot;position:relative;&quot;&gt;Repos&lt;a href=&quot;#Repos&quot; aria-label=&quot;Repos permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;h4 id=&quot;What-is-a-distributed-version-control-system&quot; style=&quot;position:relative;&quot;&gt;What is a distributed version control system?&lt;a href=&quot;#What-is-a-distributed-version-control-system&quot; aria-label=&quot;What is a distributed version control system permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h4&gt;
&lt;blockquote&gt;
&lt;p&gt;A distributed version control system (DVCS) brings a local copy of the complete repository to every team member’s computer, so they can commit, branch, and merge locally.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;When it comes to &lt;em&gt;Repos&lt;/em&gt;, we do not need a folder for each business unit for feature development and collaboration. Git is a DVCS, and that means that each users has a folder for themselves.&lt;/p&gt;
&lt;p&gt;However, for running scheduled jobs or CI/CD, a location that always has the &lt;strong&gt;production&lt;/strong&gt; code is required. This article discusses this topic well &lt;a href=&quot;https://learn.microsoft.com/en-us/azure/databricks/repos/ci-cd-best-practices-with-repos&quot;&gt;CI/CD workflows with Git integration and Databricks Repos&lt;/a&gt;. To enable this we will still create a top level folder for each business unit to enable them to operationalise their pipelines.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 522px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/6784651c8908e61ce6f7b08286558691/d15a3/databricks-security-model-repos.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 32.4%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAGCAIAAABM9SnKAAAACXBIWXMAAAsSAAALEgHS3X78AAAAt0lEQVQY04WQy4qEQAxF/f+v7NGFlK15VCqVelkOtvQguJizCCHcwOEOi3PjODLzcRy99+M/AOD1+nm/11rrYDESUXoQo1pKpRQiijHe7icagqoOQcQ5p6rxhpmJiH4W55z33szugRCC934A2JZlQcRtXdd1AwBELKW01kopOWdmYmaic6aUCAkRa2unNhFN0zTPM3+5cgAgIpe2iCBS+zwwk6r23mstQ8750jCz9mDf97+qnnX+AvjFWf34CTkrAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;databricks security model repos&quot;
        title=&quot;databricks security model repos&quot;
        src=&quot;/static/6784651c8908e61ce6f7b08286558691/d15a3/databricks-security-model-repos.png&quot;
        srcset=&quot;/static/6784651c8908e61ce6f7b08286558691/0eb09/databricks-security-model-repos.png 500w,
/static/6784651c8908e61ce6f7b08286558691/d15a3/databricks-security-model-repos.png 522w&quot;
        sizes=&quot;(max-width: 522px) 100vw, 522px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;We have set the same &lt;strong&gt;Can Manage&lt;/strong&gt; permissions to the top level &lt;em&gt;Repo&lt;/em&gt; folder (Family), and Robert was able to log in a clone his teams repo twice:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;once checked out to to the &lt;em&gt;preprod&lt;/em&gt; branch&lt;/li&gt;
&lt;li&gt;the next checked out to the &lt;em&gt;master&lt;/em&gt; branch&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;This way, his team can create jobs that source code from either of these clones and ensure the job runs against the correct version of the code base.&lt;/p&gt;
&lt;p&gt;He also has a personal folder that he can clone his teams repository and check out his feature branches that he creates for analysis and development.&lt;/p&gt;
&lt;h2 id=&quot;Compute&quot; style=&quot;position:relative;&quot;&gt;Compute&lt;a href=&quot;#Compute&quot; aria-label=&quot;Compute permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;We may/may not want to allow business units to create any cluster they want. This example assumes we do not.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Note: Going down this path will require more administration burden down the track as business units requirements change. Some restrictions appear to have more advantages than not, such as limiting the machine spec (and therefore limiting cost), however limiting, for example the Databricks runtime version, does not appear to have many advantages.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;This can be done using cluster policies.&lt;/p&gt;
&lt;h3 id=&quot;Cluster-policies&quot; style=&quot;position:relative;&quot;&gt;Cluster policies&lt;a href=&quot;#Cluster-policies&quot; aria-label=&quot;Cluster policies permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;A cluster policy definition is a JSON string that defines restrictions or sets parameters on cluster creation.&lt;/p&gt;
&lt;p&gt;Cluster policies themselves require a name. The name will contain the core components required to delineate between use of the policies. They will line up with the components used to name the Active Directory Groups.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;&amp;lt;BusinessUnit&gt;-&amp;lt;Role&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Component&lt;/th&gt;
&lt;th&gt;Values&lt;/th&gt;
&lt;th&gt;description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Business unit&lt;/td&gt;
&lt;td&gt;Family, Consumer, Criminal etc&lt;/td&gt;
&lt;td&gt;The name of the business unit&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Role&lt;/td&gt;
&lt;td&gt;DataScientist, DataEngineer etc&lt;/td&gt;
&lt;td&gt;The role within the business unit&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;We will create a very light touch cluster policy for our IT team members and a more restrictive policy for the members of the &lt;em&gt;Family&lt;/em&gt; Law business unit (see &lt;a href=&quot;#Restrictive-cluster-policy&quot;&gt;Restrictive cluster policy&lt;/a&gt;).&lt;/p&gt;
&lt;p&gt;The light touch cluster policy is below:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;json&quot;&gt;&lt;pre class=&quot;language-json&quot;&gt;&lt;code class=&quot;language-json&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;&quot;cluster_name&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&quot;type&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;regex&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&quot;pattern&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;(Dev|PreProd|Prod)-(Discovery|Operational)-IT-[0-9.]*?ML?-[a-zA-Z]*-?[0-9]*?&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&quot;hidden&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;false&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;&quot;custom_tags.Team&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&quot;type&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;fixed&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&quot;value&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;IT&quot;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;&quot;autotermination_minutes&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&quot;type&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;unlimited&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&quot;isOptional&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&quot;defaultValue&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;60&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Our naming convention for the clusters is:&lt;/p&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;(Dev|PreProd|Prod)-(Discovery|Operational)-&amp;lt;BusinessUnit&gt;-[0-9.]*?ML?-[a-zA-Z]*-?[0-9]*?&lt;/code&gt;&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Component&lt;/th&gt;
&lt;th&gt;Values&lt;/th&gt;
&lt;th&gt;description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Environment&lt;/td&gt;
&lt;td&gt;Dev, PreProd, Prod&lt;/td&gt;
&lt;td&gt;The environment of the clusters use&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Mode&lt;/td&gt;
&lt;td&gt;Discovery, Operational&lt;/td&gt;
&lt;td&gt;What mode the cluster is used for&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Business unit&lt;/td&gt;
&lt;td&gt;Family, Consumer, Criminal etc&lt;/td&gt;
&lt;td&gt;The name of the business unit&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Databricks runtime&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;The Databricks runtime version&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Name&lt;/td&gt;
&lt;td&gt;Such as a project&lt;/td&gt;
&lt;td&gt;A name to denote the use of the cluster&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Cluster number&lt;/td&gt;
&lt;td&gt;If more than one per project&lt;/td&gt;
&lt;td&gt;A number (optional)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Example:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Prod-Discovery-Family-11.3ML-project-01&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;After creation of the policy, we need to assign the policy permission to the correct group.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 556px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/02dcccd630cac6e5f771cf719554b586/68b51/databricks-security-model-cluster-policy.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 84%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAARCAIAAABSJhvpAAAACXBIWXMAAAsSAAALEgHS3X78AAABhElEQVQ4y5WTzbLTMAyF84rseC02vA9LYNjBhc50KLQ3iePYcuz4V7Zjpu2lpKkHhm/hhUdHOpbkhsn5OAplPGgUOkwG1axBCKN1+RdNwOh8yDkHjBgTxphiRMQUYwghIs4ufjqID98FaCylLMtKLCZBBsI5p4ScjkdKKSFd23fWWsbZBOPn0/T67cdXb96/+8ZKKSn/UTezUsGHqqvlhRLzEvOyrvkippQCCO+9Mcb7kJclX6jEPr7ZWavmM1JOzrnlv8R913HG730utxRrHtM1iCgnGWP6O+fOx7gVMzY+P5+mSW4qV9mKhYC2ba21VasbtuLHfNWaOeeKbWvtPCtEdM5KqbQx1bbVbVtrtdYRIwH19edwIPA78i666uW8YYcfh5TSxnkpxWr99OVpt9v1hNwu7yp3bbvf7yml4zgCwGXhgHMOAN75gQzDMGhjcs63sSFiCME51wAAucAYSyndXng9nXPeu3XDrzPHCw0hpO97rXV1H6WUSqn1kK4ZQzj/pV9ARN/rUmutjQAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;databricks security model cluster policy&quot;
        title=&quot;databricks security model cluster policy&quot;
        src=&quot;/static/02dcccd630cac6e5f771cf719554b586/68b51/databricks-security-model-cluster-policy.png&quot;
        srcset=&quot;/static/02dcccd630cac6e5f771cf719554b586/0eb09/databricks-security-model-cluster-policy.png 500w,
/static/02dcccd630cac6e5f771cf719554b586/68b51/databricks-security-model-cluster-policy.png 556w&quot;
        sizes=&quot;(max-width: 556px) 100vw, 556px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;h3 id=&quot;Testing-the-policy&quot; style=&quot;position:relative;&quot;&gt;Testing the policy&lt;a href=&quot;#Testing-the-policy&quot; aria-label=&quot;Testing the policy permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;When Robert creates a cluster he only had the option to create a cluster using the &lt;strong&gt;Family-DataScientist&lt;/strong&gt; cluster policy.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 726px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/309ede35855f1fe1486ecfe46f3b3e4b/c04c8/databricks-security-model-family-create-cluster.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 63%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAANCAIAAAAmMtkJAAAACXBIWXMAAAsSAAALEgHS3X78AAACpUlEQVQoz42QWWsTYRSG5yd4IRW0Js3aJU1tY2qSZpKxKS3aUupS1AuhoNQfIFIX0JomXdygCoJ4oeCV1Bstir/AJAU3cGtjlzTJZGKSzkySmczM933zjUwCguCFD+ficOB9Oe9LBH2kP9hPUSE/SXUdcB10e9raO01mm7HFYmyxGIxmg9FsNFlN9TG2WJoNJout3WJttdnbCIoaOHJ0dHhkrM8fJEmqPzQYCg36+gLuXo/XRwaDh/0k5XZ72h3OHrd3bOzEmZOnKE+f1xfo6fUSVpdvX0dPk925x+60dB9qdrgMnS5yaCQwNNLpCey2OJpsjl2mtr1Gu32/tcPhcnb1Gs3tNlOr3WAlugeGL0dvXYksXJu7Pbv48OadxavRhQePnzx6+ixy7/6l8PxUZOHidHTqejhyIzIbnpsOz0dm5qMzc5PnLxDHJiZ5jq2W+TLHYoy1/0BVVU3T3n/4SLx4/TZFM4WdMlNki1wFQiiIogKhoigQQt1Od2yMhjFWMYYIaZoWTySI42fPZZLr6dWfW19X02sb5UyG39qkU3SlKopiTZYVXpDoUiVbLDNsVajJEEJZUXRxPEGcnpisJJPldKaSz1fzufT69ubaBv1tjauIgiDWxFqBE36ki99ThSTNshURACDLsqZp72JxYmn5jaZpsqLfkCjkOWHpc16CSFFkjuerQhWrqopgmWOFakWR5Z1SqVgsIoQSKyvE85fLulhWGkm22dqrL79YQUYQqKouxBgjpCoKAADAOgAADeNYPE5E7i5CAERRFEQRyrVPqVKS4bDeF/hTr95T3UVV1frbigyQLu4fHc8XCgyTy+rQhXyOydGZbJZhGJ7nAQDK30iSLOpVSLFYnCBHxyVJwhhDCBFCjccaC0II/wtUD5hIrPwGjuIzPpU8gtoAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;databricks security model family create cluster&quot;
        title=&quot;databricks security model family create cluster&quot;
        src=&quot;/static/309ede35855f1fe1486ecfe46f3b3e4b/c04c8/databricks-security-model-family-create-cluster.png&quot;
        srcset=&quot;/static/309ede35855f1fe1486ecfe46f3b3e4b/0eb09/databricks-security-model-family-create-cluster.png 500w,
/static/309ede35855f1fe1486ecfe46f3b3e4b/c04c8/databricks-security-model-family-create-cluster.png 726w&quot;
        sizes=&quot;(max-width: 726px) 100vw, 726px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;When he attempted to create a cluster that breached the naming convention, he is notified at the bottom of the page what the naming convention he needs to follow is (as a regex).&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 726px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/320bf1836261b744c907f72aa46feae3/c04c8/databricks-security-model-cluster-naming.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 59.4%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAMCAIAAADtbgqsAAAACXBIWXMAAAsSAAALEgHS3X78AAACo0lEQVQoz2WR20/TABTG+09gNDI2Nsad4tiNXVpKQQZxYAyIBEQfZAzGbYrBMC5DcIZEND4QY4KJxjf/CBINBq+AUYYkmmUXBuu6lq5j0ttanUhi9PdyTk7Ol3zfOYCxUmMxQxZLjdkMQzB6RqOrADXKgkK5QiVXqGRypSxPmSdXKvILjia5MrlSVZivLARBDYAiaB3aYG++UFdvgyAErkFRtAGCa3V6o1ZnNJshq7VGb6gGQY3JZD1b33ixvQOGEYPBBMO1QIkRkpVV5ZVrTxWBsjKNuqr6dEllpRlBGptNqE1Rrj1ZBOYUlOWoSrMLFbpiI5xbqjmhLleAOkCph5w3Pbfm5ocnvJ67924vPByd8U3P31988mxh8fGo987I5OyQZ2ZwfHrQM+OemnNPzd3w+q5P+1xjE4Cx8fymf0uSJJ4XeI5jWZZlGJqmD1khzYk/eDEjSn8QxeOabUiSBB49fR4MhmiajsfxRIKg6RRBknE8QVHJ6O5egiBwnNjd9IdeviK/fc8qM5kjMUEQANTStrbxiSSIUDgcCUcikZ2daDQYDGFYnKIoan8/QRB7H9ewz1/SJClJUkYQGIbhBAnDMCBfZ3n7/gPHMjgeT6VSHMdRFEWSZJJOchwv/YcoihzHZyQpEAgA6mrk3frGQTq9G40myX3u8PCAovC9GIFhZALnGEaUJP7Y6t+ZY7EYoNaZ11de+1ffjPX1TQ4PTwwNjQ8MjPX3e0dGvG73qMPxYmlJpGmOYYRjeD7rKBwOA1q9NbD1dWV5+XJ3t9Plcva7enp7exy9PU5ne2enranJNzsrpVLJUIgVBJZhfn+D4Xk+EokAxQYouL3tf7Pafc7uaL90rbXtaldXa0tLh812xW63I8gDr1fieZ5h/rH969o/AZy0x3Z+2dx5AAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;databricks security model cluster naming&quot;
        title=&quot;databricks security model cluster naming&quot;
        src=&quot;/static/320bf1836261b744c907f72aa46feae3/c04c8/databricks-security-model-cluster-naming.png&quot;
        srcset=&quot;/static/320bf1836261b744c907f72aa46feae3/0eb09/databricks-security-model-cluster-naming.png 500w,
/static/320bf1836261b744c907f72aa46feae3/c04c8/databricks-security-model-cluster-naming.png 726w&quot;
        sizes=&quot;(max-width: 726px) 100vw, 726px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;h3 id=&quot;Cluster-use&quot; style=&quot;position:relative;&quot;&gt;Cluster use&lt;a href=&quot;#Cluster-use&quot; aria-label=&quot;Cluster use permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;By default, the permissions for using the cluster will be exclusive to the user that created the cluster. This is not a great idea, since the cluster might need to be used (in the case of a shared cluster), monitored, stopped or deleted by someone else within the team. We will communicate with the business units that they can self manage the permissions of their clusters and should add their team as &lt;em&gt;Can Manage&lt;/em&gt; after the creation of any new cluster.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 628px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/42a1323645290c20d6a2a1429283b4bc/5e4bf/databricks-security-model-cluster-default-permissions.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 52%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAKCAIAAAA7N+mxAAAACXBIWXMAAAsSAAALEgHS3X78AAABBklEQVQoz5WQQY7DIAxFc/9bpoimTSFygBjHjumouBNV6mzmLZCxefDFcLvdnHOXy8V7f71enXPjOIYQXGeaJuec956Inh+01lR1UFURYeZ936VzFta0bWvt07RiKKU8Ho/nXwCA9z6E8D0yfwghxBhF5PsEES3LEmM8juNsWrS3HGM0P3dKKVa01kQkpQQAIqK/MDN1XnJKKYQwzzMA1A4z21P7vuecAcA6hv1FrfUlI2KtFRG3DiKqqiVk5pxzSuk4DlX9lBHxJRPRmRYRSykWwWKLSK11WRYAsCtUFRHv93vOeTivPGdG69jU1tYaM8cY13Wdpmld1+H5H1Q150xE27YR0Q/YTUWbW+SuhAAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;databricks security model cluster default permissions&quot;
        title=&quot;databricks security model cluster default permissions&quot;
        src=&quot;/static/42a1323645290c20d6a2a1429283b4bc/5e4bf/databricks-security-model-cluster-default-permissions.png&quot;
        srcset=&quot;/static/42a1323645290c20d6a2a1429283b4bc/0eb09/databricks-security-model-cluster-default-permissions.png 500w,
/static/42a1323645290c20d6a2a1429283b4bc/5e4bf/databricks-security-model-cluster-default-permissions.png 628w&quot;
        sizes=&quot;(max-width: 628px) 100vw, 628px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;h3 id=&quot;Package-management&quot; style=&quot;position:relative;&quot;&gt;Package management&lt;a href=&quot;#Package-management&quot; aria-label=&quot;Package management permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;There are a few ways to install required packages on clusters. Databricks provides a GUI for installing packages, but this can also be done:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;within notebook code using, for example, &lt;code class=&quot;language-text&quot;&gt;%pip install &amp;lt;package&gt;&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;within an init script run while the cluster is starting up (see &lt;a href=&quot;#Sample-init_script-framework&quot;&gt;Sample init_script framework&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id=&quot;Storage&quot; style=&quot;position:relative;&quot;&gt;Storage&lt;a href=&quot;#Storage&quot; aria-label=&quot;Storage permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;h3 id=&quot;Data-lake&quot; style=&quot;position:relative;&quot;&gt;Data lake&lt;a href=&quot;#Data-lake&quot; aria-label=&quot;Data lake permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;All storage will be kept in an Azure Data Lake Storage (ADLS) Gen 2 storage account. A single container will we created for Discovery use. A sub folder will be created for each business unit and permissions set accordingly.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 730px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/ea9202fbb14c81313f8d68feb079432c/a6a70/databricks-security-model-adls-discovery.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 61.199999999999996%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAMCAIAAADtbgqsAAAACXBIWXMAAAsSAAALEgHS3X78AAABcUlEQVQoz42Ry27lIBBE/f8fN5rNLKJZJOPIvjZcaKCheTTgCPvmockmRyyQUFFd1dPmi9BmlXpX1kaurR8/oJ1MTzbN67atC2jFzMfPeIhzzt77VlvvnQuXUlqt7ThCTCmXVBidI4o+MaJvrXIpOefex4DTPyHn+UWK3YAyoEDfg0cAeHl+vm23fd8BjA8BffCISBkj21B8qkOsjZFCgNYWQCmF6Hpv1ph5nqUQGjTFGEIgImbOhbl2buMMMeWGOP51DmutVyQDcJdSKSWkEPtuAIa4jFTXwBcTGEDE3ntrI/b1NkzyyMajhMLMhfkS1VpL4Udmn5IxxjlnrWXm/uAIuAfvzu4OIjLWtnbeQ0B09XSaKBIAOOe+boIoJfjt9OaRem+XT//GlIvXWp89fYYJFKP5g1bmUo+jX1v9FL3fp5tnKe+R4n/OQf2ioJlbPWnfOMcuLaf00fO7OC6vf9fldV1vy7IopVJK8QtElHN+AyPJuRgsT5i5AAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;databricks security model adls discovery&quot;
        title=&quot;databricks security model adls discovery&quot;
        src=&quot;/static/ea9202fbb14c81313f8d68feb079432c/a6a70/databricks-security-model-adls-discovery.png&quot;
        srcset=&quot;/static/ea9202fbb14c81313f8d68feb079432c/0eb09/databricks-security-model-adls-discovery.png 500w,
/static/ea9202fbb14c81313f8d68feb079432c/a6a70/databricks-security-model-adls-discovery.png 730w&quot;
        sizes=&quot;(max-width: 730px) 100vw, 730px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;To enable users from Azure Active Directory to read and write from the ADLS we need a couple of things first. We will use the group to manage the permissions:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;The group will need the role of &lt;em&gt;Reader&lt;/em&gt; at the storage account level&lt;/li&gt;
&lt;li&gt;The ADLS folder’s ACLs will need to have the group set appropriately.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 654px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/79d780415646e32b65996e7bc8a8f216/ba4a2/databricks-security-model-adls-role.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 5.6000000000000005%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAABCAIAAABR8BlyAAAACXBIWXMAAAsSAAALEgHS3X78AAAAP0lEQVQI1xXK2w2AMAwDQBZmMlZigiokSLGhxOUh7vum5jkv+9oq+wjKshwKyCmDDIM8SdzPayhLBf+2QUe/Pp1KOHLvxT+FAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;databricks security model adls role&quot;
        title=&quot;databricks security model adls role&quot;
        src=&quot;/static/79d780415646e32b65996e7bc8a8f216/ba4a2/databricks-security-model-adls-role.png&quot;
        srcset=&quot;/static/79d780415646e32b65996e7bc8a8f216/0eb09/databricks-security-model-adls-role.png 500w,
/static/79d780415646e32b65996e7bc8a8f216/ba4a2/databricks-security-model-adls-role.png 654w&quot;
        sizes=&quot;(max-width: 654px) 100vw, 654px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 995px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/bdfe5a50f68f0ff11ec32689ab1351b2/aa4b8/databricks-security-model-adls-folder-permissions.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 44.2%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAJCAIAAAC9o5sfAAAACXBIWXMAAAsSAAALEgHS3X78AAABRUlEQVQoz21Qy27cMAz0/39Rj7nl0gIBcsgiLRJkN7HWkiVZlEw9LJKB10WBAjsnYsB5kIP8D2ZOMU3TZIyptfJOsNwDEQ3ETMy999basQYhKDVqPXnvSsnU+10xMw+TNuqqzWy9DzkXZo4JHeBae8wt5C23LnI/fLhcLuevq3YQsfQ9hBOWcQ5XCynXEFNakYjui5clAIC1cwQ4ymyd4oorZr6dc5DHkUfff15Day0XJP7bjIhaq87OzlrELCIaiscmIiuiiCiPS9yHrdPQkk72XLHKzW//3LbVbduN9kC2yxojUilLiCJi1g0biewFh6fHh/efP8bTb/16MmpcFo9au7e3pFQcx+bmP2erlGsQf70oWezzy+f0qYUplj648X01HznE7H0tpdTaAMo8Z+fQmOocaJshtRCCB4IAxpUAwtxZvgEu0QgJJkHbUAAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;databricks security model adls folder permissions&quot;
        title=&quot;databricks security model adls folder permissions&quot;
        src=&quot;/static/bdfe5a50f68f0ff11ec32689ab1351b2/aa4b8/databricks-security-model-adls-folder-permissions.png&quot;
        srcset=&quot;/static/bdfe5a50f68f0ff11ec32689ab1351b2/0eb09/databricks-security-model-adls-folder-permissions.png 500w,
/static/bdfe5a50f68f0ff11ec32689ab1351b2/aa4b8/databricks-security-model-adls-folder-permissions.png 995w&quot;
        sizes=&quot;(max-width: 995px) 100vw, 995px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;Make sure you set the default permissions too so that child items (files) in the folders inherit the permissions of the parent folder.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 951px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/fd2bdaed2c11e3600eb576229e2ee8ee/79e31/databricks-security-model-adls-folder-permissions-default.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 48%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAKCAIAAAA7N+mxAAAACXBIWXMAAAsSAAALEgHS3X78AAABHElEQVQoz5WSy24DIQxF5/9/rpt2l0WmSppMM5nh4QcYDBVMUkVppapHXiDL2PcaBgDw3sfAxsF4+Dyc5tN0TSnX8jfDsizOgbGwWriaFh4IEIgIEZkZEaGBiEidLRNCGKbL1Ri7Gr8YZx1GKar1iVJK/Y0Bkbx3MQYRiTHU/zBYa9Z1JaI2occGAOSca61EFENrKiLW2kcVAxGVoqr6uImUdfeBqq1uPLOnVGudlnheWpfv2sE5h4hPrlIub3u4S2BJTcJxDsdL6Cu4T2amn2ZUiwfezkicun4OwiFul7umm+ySc9Y7RVWSvuzcNuF1D6tvsseJx6l1jEmB87bthvce4Pac7c9Eeb+E7rkc54C9dLYyW+mmNIrWWr4A3UpIisMG39EAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;databricks security model adls folder permissions default&quot;
        title=&quot;databricks security model adls folder permissions default&quot;
        src=&quot;/static/fd2bdaed2c11e3600eb576229e2ee8ee/79e31/databricks-security-model-adls-folder-permissions-default.png&quot;
        srcset=&quot;/static/fd2bdaed2c11e3600eb576229e2ee8ee/0eb09/databricks-security-model-adls-folder-permissions-default.png 500w,
/static/fd2bdaed2c11e3600eb576229e2ee8ee/79e31/databricks-security-model-adls-folder-permissions-default.png 951w&quot;
        sizes=&quot;(max-width: 951px) 100vw, 951px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;This is a very siloed security model and does not allow for sharing of data between business units. It is suggested to also create a shared folder within the discovery container and give each business unit access on the occasion that they need to share data.&lt;/p&gt;
&lt;h3 id=&quot;Hive-metastore&quot; style=&quot;position:relative;&quot;&gt;Hive metastore&lt;a href=&quot;#Hive-metastore&quot; aria-label=&quot;Hive metastore permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;In Databricks, data can be registered to the Hive metastore (think of it as an information schema or data dictionary not the data itself). This enables SQL queries on the data (schema on read).&lt;/p&gt;
&lt;p&gt;By default there is no security across the Hive metastore, which means that all users have permissions to create, read and drop registrations (not the data) from the metastore.&lt;/p&gt;
&lt;p&gt;We will create a Hive metastore schema (the name schema and database are interchangeable in a Databricks metastore) for each business unit.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;sql&quot;&gt;&lt;pre class=&quot;language-sql&quot;&gt;&lt;code class=&quot;language-sql&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;CREATE&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;SCHEMA&lt;/span&gt; admin&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;CREATE&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;SCHEMA&lt;/span&gt; family&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;CREATE&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;SCHEMA&lt;/span&gt; consumer&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;CREATE&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;SCHEMA&lt;/span&gt; criminal&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;CREATE&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;SCHEMA&lt;/span&gt; it&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;Note: Hive metastore objects (schemas, tables etc) can only be lowercase.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 783px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/0168f05a4766519cf5cc6d0711d68894/e19e2/databricks-security-model-hive.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 34%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAHCAIAAACHqfpvAAAACXBIWXMAAAsSAAALEgHS3X78AAAAy0lEQVQY042QUW7EMAhEc//bbXOFaJvIrowxBoKxWyVRo5W6H32fzDAamOZ5fjw+lmVBxO//kTPEGM1s2rYNsRCRsCRMSMi1xq+oKqoqIsxsZkSEWMxMRGqth19kAoBCtO+7u6vqbjszp5RyziLSWjMzd6+1QkpE1H5x9ymEsK5rzplOVLUUBAAzG2PcVZmZiHrv9+RYdvcrpp+M3nPOANBau31jjEt+vXyMMf39B0AKMTb3Qz85Ql9a3LxZDiE8n5+I5QIRa61v3/4DRc2YC4o+u2IAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;databricks security model hive&quot;
        title=&quot;databricks security model hive&quot;
        src=&quot;/static/0168f05a4766519cf5cc6d0711d68894/e19e2/databricks-security-model-hive.png&quot;
        srcset=&quot;/static/0168f05a4766519cf5cc6d0711d68894/0eb09/databricks-security-model-hive.png 500w,
/static/0168f05a4766519cf5cc6d0711d68894/e19e2/databricks-security-model-hive.png 783w&quot;
        sizes=&quot;(max-width: 783px) 100vw, 783px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;This will become a suggestion for business users to use, however there is nothing restricting them from creating more schemas, and dropping already existing schemas.&lt;/p&gt;
&lt;p&gt;To make a more secure metastore layer without going down the path of Table Access Controls we could implement multiple metastores using Databricks Unity Catalog (not for this discussion).&lt;/p&gt;
&lt;h4 id=&quot;Table-Access-Controls&quot; style=&quot;position:relative;&quot;&gt;Table Access Controls&lt;a href=&quot;#Table-Access-Controls&quot; aria-label=&quot;Table Access Controls permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;Table access control lets you programmatically grant and revoke access to your data from Python and SQL.&lt;/p&gt;
&lt;p&gt;By default, all users have access to all data stored in a cluster’s managed tables unless table access control is enabled for that cluster. Once table access control is enabled, users can set permissions for data objects on that cluster.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://docs.databricks.com/security/access-control/table-acls/index.html&quot;&gt;Table ACLs&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;This means that unless Table ACLs are enforced on all clusters and applied to all objects in the Hive metastore, then it is not an option. Since we have architected permissions at the storage level we will not go down this path.&lt;/p&gt;
&lt;h5&gt;Sample of how this may work&lt;/h5&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;sql&quot;&gt;&lt;pre class=&quot;language-sql&quot;&gt;&lt;code class=&quot;language-sql&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;GRANT&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;USAGE&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;ON&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;SCHEMA&lt;/span&gt; family &lt;span class=&quot;token keyword&quot;&gt;TO&lt;/span&gt; &lt;span class=&quot;token identifier&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;`&lt;/span&gt;LawFirms-Prod-Databricks-FamilyLaw-DataScientist&lt;span class=&quot;token punctuation&quot;&gt;`&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Implementing a data governance model using Table ACLs requires clusters with Table ACLs enabled. If we attempt to run any &lt;code class=&quot;language-text&quot;&gt;GRANT&lt;/code&gt; statements with clusters that do not have Table ACLs enabled we will get the following error.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;Error in SQL statement: SparkException: Trying to perform permission action on Hive Metastore /CATALOG/`hive_metastore`/DATABASE/`family` but Table Access Control is not enabled on this cluster.&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;After adding the following Spark config to the cluster, the Table ACL was successfully set.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;spark.databricks.acl.sqlOnly true&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Michael Ross in the IT team logs in tries to select from a table in the &lt;em&gt;family&lt;/em&gt; schema and gets the following error.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;Error in SQL statement: SecurityException: User does not have permission SELECT on table `family`.`game`.
User does not have permission USAGE on database `family`.&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;DBFS&quot; style=&quot;position:relative;&quot;&gt;DBFS&lt;a href=&quot;#DBFS&quot; aria-label=&quot;DBFS permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;The Databricks File System (DBFS) is a distributed file system mounted into a Databricks workspace and available on Databricks clusters.&lt;/p&gt;
&lt;p&gt;Databricks recommends against storing any production data or sensitive information in DBFS.&lt;/p&gt;
&lt;p&gt;In some cases it may be required, so the important thing to do is to set some standards and expectations to business units on the usage of DBFS.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;directory level&lt;/th&gt;
&lt;th&gt;directory&lt;/th&gt;
&lt;th&gt;description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;/dbfs/FileStore&lt;/td&gt;
&lt;td&gt;Top level DBFS folder for artifacts&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 50px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/872a116c2274b08175bbee49df42d4b1/77dd4/folder.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 42%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAICAYAAAD5nd/tAAAACXBIWXMAAArwAAAK8AFCrDSYAAABWElEQVQoz5XRv0tCYRTG8QNBUxAtNQf9CREStDa3NDZVpqlRGdVoGBUVLYEUUVNbS0tbDRFlpJD3fU3UbhoNNWjn7d5SrtcfTygObdqBL4ezfJZDP5lTKr6edOUvh3qLMWfPWx6kCiAAnQD6AHQDjbtRywHQnxN76Q8RMnL6uW5YGGULDjMbujafNytmZv8ifUAkg0TlQrY1WCm8jajkLj71Q/DLMVg/Aj+HwMIL1qbr+0GsE0XmiWxDawMsscNIr4EfJ6pKm6oqbbLW2NJjK+mFii/eyi2iqL8OynbAvMNIBcGaq6bkXE1JXzNvhYUHKr5wJzf/A1q5YSMVAMcmbRbuMgtXM3eJNReU9N3IDaLIYh0UrcFq+XvQ1Hegnvz4SqziK7Hyp2UYyYCIbxNFl4js71RbX+4oGdq4xeFZi++dFt/PNHNaHPbYZmLs/WqAsmdEtUqxJfgL4PIOs7y7UGQAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;folder&quot;
        title=&quot;folder&quot;
        src=&quot;/static/872a116c2274b08175bbee49df42d4b1/77dd4/folder.png&quot;
        srcset=&quot;/static/872a116c2274b08175bbee49df42d4b1/77dd4/folder.png 50w&quot;
        sizes=&quot;(max-width: 50px) 100vw, 50px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/td&gt;
&lt;td&gt;Admin&lt;/td&gt;
&lt;td&gt;Admin directory&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 50px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/bbae23db9683f6a9cf4659383b488f54/77dd4/folder-lvl1.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 42%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAICAYAAAD5nd/tAAAACXBIWXMAAArwAAAK8AFCrDSYAAABZ0lEQVQoz43Mz0vCYBjA8bd2qGsEXjtGf0L/jBE558jVZcykDoEQoR2j8CDVoVuHfhw6dU3J0P1gm8aKxECt9r7btKkb+oQVCB1aL3x5D8/zfBD64wEAAuh9/TKPkF1emzVvFkNgsFP4A1Dj+WY0mwGAEABMj/ZQEPhW2p58veNRu3bONcvpdks9bpv121MCMG+3CjHHSDccI9Pv1HJh28gEg6bIU+/3DOrUz45MfRfw0wmYRhbwYw5wZQewRAOWGLBUgTVL4WDQ0hOUpSeR27w6IAoLRFz2iBQZEGllSCRmQBTOI0ocLH0rikX6H6CWoIgmILdxeUiUdcAK5xOFG46L+9/gJoPFSDBIVJ4iKo/cxkUWy6uApVgXy6yP5dhPbA9LMbC0BG2WloJBu5qi7GoKdVvX+5aeBEsTRse/2gDH2OMsTQgGPUeb8JwK8t2XuT4psj2cj/ZwgRmXp/ukyPlubcFrP6BPdkIsIuOVaawAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;folder lvl1&quot;
        title=&quot;folder lvl1&quot;
        src=&quot;/static/bbae23db9683f6a9cf4659383b488f54/77dd4/folder-lvl1.png&quot;
        srcset=&quot;/static/bbae23db9683f6a9cf4659383b488f54/77dd4/folder-lvl1.png 50w&quot;
        sizes=&quot;(max-width: 50px) 100vw, 50px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/td&gt;
&lt;td&gt;init_scripts&lt;/td&gt;
&lt;td&gt;For admin init scripts&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 50px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/bbae23db9683f6a9cf4659383b488f54/77dd4/folder-lvl1.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 42%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAICAYAAAD5nd/tAAAACXBIWXMAAArwAAAK8AFCrDSYAAABZ0lEQVQoz43Mz0vCYBjA8bd2qGsEXjtGf0L/jBE558jVZcykDoEQoR2j8CDVoVuHfhw6dU3J0P1gm8aKxECt9r7btKkb+oQVCB1aL3x5D8/zfBD64wEAAuh9/TKPkF1emzVvFkNgsFP4A1Dj+WY0mwGAEABMj/ZQEPhW2p58veNRu3bONcvpdks9bpv121MCMG+3CjHHSDccI9Pv1HJh28gEg6bIU+/3DOrUz45MfRfw0wmYRhbwYw5wZQewRAOWGLBUgTVL4WDQ0hOUpSeR27w6IAoLRFz2iBQZEGllSCRmQBTOI0ocLH0rikX6H6CWoIgmILdxeUiUdcAK5xOFG46L+9/gJoPFSDBIVJ4iKo/cxkUWy6uApVgXy6yP5dhPbA9LMbC0BG2WloJBu5qi7GoKdVvX+5aeBEsTRse/2gDH2OMsTQgGPUeb8JwK8t2XuT4psj2cj/ZwgRmXp/ukyPlubcFrP6BPdkIsIuOVaawAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;folder lvl1&quot;
        title=&quot;folder lvl1&quot;
        src=&quot;/static/bbae23db9683f6a9cf4659383b488f54/77dd4/folder-lvl1.png&quot;
        srcset=&quot;/static/bbae23db9683f6a9cf4659383b488f54/77dd4/folder-lvl1.png 50w&quot;
        sizes=&quot;(max-width: 50px) 100vw, 50px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/td&gt;
&lt;td&gt;lib&lt;/td&gt;
&lt;td&gt;For file based libraries or packages&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 75px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/6affa36bdcb5f1824ed85a12fe326f5e/0d135/folder-lvl2.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 27.999999999999996%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAGCAYAAADDl76dAAAACXBIWXMAAAsSAAALEgHS3X78AAAA+ElEQVQY02NgIBL8//+f4cEWB4YHWx0ZXp/vln55stL4/5//zC8vlDL8/HhF69enq8qf701hIBr8/vYMbOj///9Z3l6fduXVjfn/3z3Zv/n9ow0zPlyv+v/hRv2R53uVSDDwyz2YgZyf7nQ9fnc57/+7K0X/310u+P/uYvr/95dzjzzZxkOCgV/hBnJ8vN1+992FlP/vLmV+e3cp88u7S1n/31/OP/B0hyApBj6AGcj+6U7P6/eXc/+/v1L0//2Vwv/vrxT8/3Ct7OLTnSLEG/j313uG+2sZGJ7uEmX4/mpX4Ldna4u/PV+XA8bP1hZ/f7kj5M25aAYAgBuop7jJcBIAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;folder lvl2&quot;
        title=&quot;folder lvl2&quot;
        src=&quot;/static/6affa36bdcb5f1824ed85a12fe326f5e/0d135/folder-lvl2.png&quot;
        srcset=&quot;/static/6affa36bdcb5f1824ed85a12fe326f5e/0d135/folder-lvl2.png 75w&quot;
        sizes=&quot;(max-width: 75px) 100vw, 75px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/td&gt;
&lt;td&gt;debs&lt;/td&gt;
&lt;td&gt;Debian packages&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 75px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/6affa36bdcb5f1824ed85a12fe326f5e/0d135/folder-lvl2.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 27.999999999999996%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAGCAYAAADDl76dAAAACXBIWXMAAAsSAAALEgHS3X78AAAA+ElEQVQY02NgIBL8//+f4cEWB4YHWx0ZXp/vln55stL4/5//zC8vlDL8/HhF69enq8qf701hIBr8/vYMbOj///9Z3l6fduXVjfn/3z3Zv/n9ow0zPlyv+v/hRv2R53uVSDDwyz2YgZyf7nQ9fnc57/+7K0X/310u+P/uYvr/95dzjzzZxkOCgV/hBnJ8vN1+992FlP/vLmV+e3cp88u7S1n/31/OP/B0hyApBj6AGcj+6U7P6/eXc/+/v1L0//2Vwv/vrxT8/3Ct7OLTnSLEG/j313uG+2sZGJ7uEmX4/mpX4Ldna4u/PV+XA8bP1hZ/f7kj5M25aAYAgBuop7jJcBIAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;folder lvl2&quot;
        title=&quot;folder lvl2&quot;
        src=&quot;/static/6affa36bdcb5f1824ed85a12fe326f5e/0d135/folder-lvl2.png&quot;
        srcset=&quot;/static/6affa36bdcb5f1824ed85a12fe326f5e/0d135/folder-lvl2.png 75w&quot;
        sizes=&quot;(max-width: 75px) 100vw, 75px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/td&gt;
&lt;td&gt;python&lt;/td&gt;
&lt;td&gt;Python packages&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 75px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/6affa36bdcb5f1824ed85a12fe326f5e/0d135/folder-lvl2.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 27.999999999999996%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAGCAYAAADDl76dAAAACXBIWXMAAAsSAAALEgHS3X78AAAA+ElEQVQY02NgIBL8//+f4cEWB4YHWx0ZXp/vln55stL4/5//zC8vlDL8/HhF69enq8qf701hIBr8/vYMbOj///9Z3l6fduXVjfn/3z3Zv/n9ow0zPlyv+v/hRv2R53uVSDDwyz2YgZyf7nQ9fnc57/+7K0X/310u+P/uYvr/95dzjzzZxkOCgV/hBnJ8vN1+992FlP/vLmV+e3cp88u7S1n/31/OP/B0hyApBj6AGcj+6U7P6/eXc/+/v1L0//2Vwv/vrxT8/3Ct7OLTnSLEG/j313uG+2sZGJ7uEmX4/mpX4Ldna4u/PV+XA8bP1hZ/f7kj5M25aAYAgBuop7jJcBIAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;folder lvl2&quot;
        title=&quot;folder lvl2&quot;
        src=&quot;/static/6affa36bdcb5f1824ed85a12fe326f5e/0d135/folder-lvl2.png&quot;
        srcset=&quot;/static/6affa36bdcb5f1824ed85a12fe326f5e/0d135/folder-lvl2.png 75w&quot;
        sizes=&quot;(max-width: 75px) 100vw, 75px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/td&gt;
&lt;td&gt;R&lt;/td&gt;
&lt;td&gt;R packages&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 50px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/872a116c2274b08175bbee49df42d4b1/77dd4/folder.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 42%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAICAYAAAD5nd/tAAAACXBIWXMAAArwAAAK8AFCrDSYAAABWElEQVQoz5XRv0tCYRTG8QNBUxAtNQf9CREStDa3NDZVpqlRGdVoGBUVLYEUUVNbS0tbDRFlpJD3fU3UbhoNNWjn7d5SrtcfTygObdqBL4ezfJZDP5lTKr6edOUvh3qLMWfPWx6kCiAAnQD6AHQDjbtRywHQnxN76Q8RMnL6uW5YGGULDjMbujafNytmZv8ifUAkg0TlQrY1WCm8jajkLj71Q/DLMVg/Aj+HwMIL1qbr+0GsE0XmiWxDawMsscNIr4EfJ6pKm6oqbbLW2NJjK+mFii/eyi2iqL8OynbAvMNIBcGaq6bkXE1JXzNvhYUHKr5wJzf/A1q5YSMVAMcmbRbuMgtXM3eJNReU9N3IDaLIYh0UrcFq+XvQ1Hegnvz4SqziK7Hyp2UYyYCIbxNFl4js71RbX+4oGdq4xeFZi++dFt/PNHNaHPbYZmLs/WqAsmdEtUqxJfgL4PIOs7y7UGQAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;folder&quot;
        title=&quot;folder&quot;
        src=&quot;/static/872a116c2274b08175bbee49df42d4b1/77dd4/folder.png&quot;
        srcset=&quot;/static/872a116c2274b08175bbee49df42d4b1/77dd4/folder.png 50w&quot;
        sizes=&quot;(max-width: 50px) 100vw, 50px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/td&gt;
&lt;td&gt;Family&lt;/td&gt;
&lt;td&gt;Business unit directory&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 50px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/bbae23db9683f6a9cf4659383b488f54/77dd4/folder-lvl1.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 42%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAICAYAAAD5nd/tAAAACXBIWXMAAArwAAAK8AFCrDSYAAABZ0lEQVQoz43Mz0vCYBjA8bd2qGsEXjtGf0L/jBE558jVZcykDoEQoR2j8CDVoVuHfhw6dU3J0P1gm8aKxECt9r7btKkb+oQVCB1aL3x5D8/zfBD64wEAAuh9/TKPkF1emzVvFkNgsFP4A1Dj+WY0mwGAEABMj/ZQEPhW2p58veNRu3bONcvpdks9bpv121MCMG+3CjHHSDccI9Pv1HJh28gEg6bIU+/3DOrUz45MfRfw0wmYRhbwYw5wZQewRAOWGLBUgTVL4WDQ0hOUpSeR27w6IAoLRFz2iBQZEGllSCRmQBTOI0ocLH0rikX6H6CWoIgmILdxeUiUdcAK5xOFG46L+9/gJoPFSDBIVJ4iKo/cxkUWy6uApVgXy6yP5dhPbA9LMbC0BG2WloJBu5qi7GoKdVvX+5aeBEsTRse/2gDH2OMsTQgGPUeb8JwK8t2XuT4psj2cj/ZwgRmXp/ukyPlubcFrP6BPdkIsIuOVaawAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;folder lvl1&quot;
        title=&quot;folder lvl1&quot;
        src=&quot;/static/bbae23db9683f6a9cf4659383b488f54/77dd4/folder-lvl1.png&quot;
        srcset=&quot;/static/bbae23db9683f6a9cf4659383b488f54/77dd4/folder-lvl1.png 50w&quot;
        sizes=&quot;(max-width: 50px) 100vw, 50px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/td&gt;
&lt;td&gt;init_scripts&lt;/td&gt;
&lt;td&gt;For business unit init scripts&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;We use the top level of the DBFS as the FileStore as a convention. At this level we create a folder for each business unit and provide them guidance on placing anything that is required there. There is nothing stopping them from creating more top level folders or deleting existing folders or files.&lt;/p&gt;
&lt;p&gt;For this reason it is suggested that any files that are stored in DBFS are not the source of truth. A suggested way to manage scripts that are required in DBFS is to use a process such as the one documented here in a previous post &lt;a href=&quot;https://mortimer.xyz/databricks-azure-devops-cicd-dbfs/&quot;&gt;Databricks and Azure DevOps CICD Pipeline for DBFS&lt;/a&gt;&lt;/p&gt;
&lt;h2 id=&quot;Secrets&quot; style=&quot;position:relative;&quot;&gt;Secrets&lt;a href=&quot;#Secrets&quot; aria-label=&quot;Secrets permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Users may/may not require secrets. However it is best to provide an Azure key vault backed secret scope upfront for each business unit and provide them a user guide on how to use it in the instance that they do.&lt;/p&gt;
&lt;p&gt;We are going to manage the secrets using the &lt;strong&gt;Databricks CLI&lt;/strong&gt; in a PowerShell terminal. &lt;a href=&quot;https://learn.microsoft.com/en-us/azure/databricks/security/secrets/secret-scopes#--create-an-azure-key-vault-backed-secret-scope-using-the-databricks-cli&quot;&gt;Create an Azure Key Vault-backed secret scope using the Databricks CLI&lt;/a&gt;&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;# Create each secret scope&lt;/span&gt;
databricks secrets create-scope &lt;span class=&quot;token parameter variable&quot;&gt;--scope&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;scope-name&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; --scope-backend-type AZURE_KEYVAULT --resource-id &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;azure-keyvault-resource-id&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; --dns-name &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;azure-keyvault-dns-name&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;# List all scopes&lt;/span&gt;
databricks secrets list-scopes&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;Scope        Backend         KeyVault URL
-----------  --------------  --------------------------------------------
admin        AZURE_KEYVAULT  https://&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;keyvault&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;.vault.azure.net/
consumer     AZURE_KEYVAULT  https://&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;keyvault&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;.vault.azure.net/
criminal     AZURE_KEYVAULT  https://&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;keyvault&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;.vault.azure.net/
family       AZURE_KEYVAULT  https://&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;keyvault&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;.vault.azure.net/
it           AZURE_KEYVAULT  https://&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;keyvault&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;.vault.azure.net/&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2 id=&quot;Machine-Learning&quot; style=&quot;position:relative;&quot;&gt;Machine Learning&lt;a href=&quot;#Machine-Learning&quot; aria-label=&quot;Machine Learning permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Our users are going to want to register models to the inbuilt model registry in Databricks (an instance of MLFlow).&lt;/p&gt;
&lt;p&gt;At this stage, the recommendation in Discovery is for users to self manage the permission of the model artifacts, however a user guide through an example is the best way to enable this.&lt;/p&gt;
&lt;p&gt;To demonstrate how this could work we are going to log in as one of our IT Data Scientists (Michael Ross), and he is going to run through our &lt;a href=&quot;https://mortimer.xyz/databrick-sample-nfl-mlflow/&quot;&gt;Databricks NFL MLFlow example&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;First he creates an appropriate cluster using the cluster policy we created for Data scientists in our IT team. He names it:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Prod-Discovery-IT-11.3ML-nfl-01&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;He knows the best practices because of the great users guides provided by the administrators so he goes into the cluster permissions and gives the rest of his team permissions to the shared cluster.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 625px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/6232c19e269b9794dcad63200096f635/b1b4f/databricks-security-model-cluster-permissions.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 64.6%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAANCAIAAAAmMtkJAAAACXBIWXMAAAsSAAALEgHS3X78AAABVElEQVQoz5WRW04DMQxFZ9FshiXwxRL4ZAkgoaq0U2XaSchrEifOq6hxO0IChDgfUaT43lzbgzHGWmuMcc5Za7XWzrmcc2uNzvMvtNYGxth+vx/H8XA4MMbmeT4ej9vtlnO+2+2WZaG678pa60APtdbSaZ3aOf/FIIQ4nU4/2s/zvNlsGGM/Zr6Ip2lSSqWUvlcgotZaKfU1Rc45hHAVA0AIwXfovpbmnAHAOVdKqTdKKSklRLyIOedkb4xRnTVFCEEIwTlHxHwjxoiINMhBSjlN0ziOQgjbIVeK7b2nzZUbq8V1YDQzrTXFXtcbY5RSCiG+/owd7/01tut475dloeadcyRWSkkpc861VhKnzlUcQgCAGGO4AQApJaoupcQYyXQdG2LMYFuKwypbLQAAEVtr1CQiAoD3nuxCgA9lH5/fnl+n4fwfUsryQxxmdffwcv/0/gmR6PHnb1WpagAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;databricks security model cluster permissions&quot;
        title=&quot;databricks security model cluster permissions&quot;
        src=&quot;/static/6232c19e269b9794dcad63200096f635/b1b4f/databricks-security-model-cluster-permissions.png&quot;
        srcset=&quot;/static/6232c19e269b9794dcad63200096f635/0eb09/databricks-security-model-cluster-permissions.png 500w,
/static/6232c19e269b9794dcad63200096f635/b1b4f/databricks-security-model-cluster-permissions.png 625w&quot;
        sizes=&quot;(max-width: 625px) 100vw, 625px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;He clones the Git repo to his personal repo folder using the following HTTPS clone URL &lt;a href=&quot;https://github.com/mortie23/databricks-nfl-data.git&quot;&gt;https://github.com/mortie23/databricks-nfl-data.git&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 519px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/044b625dd7f9eb17e29f0f693a9ce2f0/d6ae3/databricks-security-model-mlflow-repo.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 28.4%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAGCAIAAABM9SnKAAAACXBIWXMAAAsSAAALEgHS3X78AAAAp0lEQVQY043QzW4DIQwEYN7/LRsBxl7/AE3JAtptFXJplSjqd7I8mss43ri12/f/nOdZck4JVW2M4Zj5ev06V/DsuV4/K22bmc05HRHt+377pS2qMsc00xA8phRjBIAAwCJ77601M3URovxlZqUUVe29MzMApAURkSghqmrOWVXdx+USF+99WIio1ioij7IP9/+9Sygicd2lVDNzffSX28w5j+N4v98Pu/NXrcYHw+AAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;databricks security model mlflow repo&quot;
        title=&quot;databricks security model mlflow repo&quot;
        src=&quot;/static/044b625dd7f9eb17e29f0f693a9ce2f0/d6ae3/databricks-security-model-mlflow-repo.png&quot;
        srcset=&quot;/static/044b625dd7f9eb17e29f0f693a9ce2f0/0eb09/databricks-security-model-mlflow-repo.png 500w,
/static/044b625dd7f9eb17e29f0f693a9ce2f0/d6ae3/databricks-security-model-mlflow-repo.png 519w&quot;
        sizes=&quot;(max-width: 519px) 100vw, 519px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;Firstly Michael loads the CSV files to the ADLS folder that the administrators have setup for him on &lt;code class=&quot;language-text&quot;&gt;abfss://discovery@lawfirm.dfs.core.windows.net/IT/&lt;/code&gt; and then runs the &lt;code class=&quot;language-text&quot;&gt;db.sql&lt;/code&gt; notebook to load all the NFL data from ADLS and register them in the Hive metastore.&lt;/p&gt;
&lt;p&gt;Then he opens up the &lt;code class=&quot;language-text&quot;&gt;nfl.model&lt;/code&gt; notebook and trains a model. Since we are using MLFlow, Michael ends up with a experiment in the model registry.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;py&quot;&gt;&lt;pre class=&quot;language-py&quot;&gt;&lt;code class=&quot;language-py&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;with&lt;/span&gt; mlflow&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;start_run&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 944px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/a1ddff4246cebde05b265b3dec62bc09/26530/databricks-security-model-mlflow-experiment.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 38.6%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAICAIAAAB2/0i6AAAACXBIWXMAAAsSAAALEgHS3X78AAABJElEQVQY04XRTUsDQQwG4PnNngRBQc+eBU+K9GKphWJdBP+AICiloLaChVVKu8pO5zuTyUS2HyCC+pxCyAsJEe1eMXmfD8eTt0rWFgNS5kw58y+IMiYiyjlncdS52b143Dl/2O8Ot1r3ty+Vsv5TudVozpyXmNm7YIypajN4nd2NpmWlRLvXZ+YYARE3gW/WrXWfqKkw0WHxXAymotMvmNk5S5QQo7UmQMAN61xd10prYxsR0ViLiB9SS2VE57IJL7RBTIiolLLWAkDwAQCMMbP5TEqptV4oFWN0zqWUMDZE9+qamQMgYGJmAHDeuyXvQwihKUKIMXrvACIt90/UnCCOT1tlWUopV1fxn0JM2kdE1MYkIrG9d3By1noajZs3/Bf+4QuxisWyrgJQFQAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;databricks security model mlflow experiment&quot;
        title=&quot;databricks security model mlflow experiment&quot;
        src=&quot;/static/a1ddff4246cebde05b265b3dec62bc09/26530/databricks-security-model-mlflow-experiment.png&quot;
        srcset=&quot;/static/a1ddff4246cebde05b265b3dec62bc09/0eb09/databricks-security-model-mlflow-experiment.png 500w,
/static/a1ddff4246cebde05b265b3dec62bc09/26530/databricks-security-model-mlflow-experiment.png 944w&quot;
        sizes=&quot;(max-width: 944px) 100vw, 944px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;Notebooks and experiments in a folder inherit all permissions settings of that folder. So only Michael will be able to see the experiment he ran. This is fine in Discovery, but when moving into Mode 1 (Operational) we will want the experiments run from the top level PreProd and Prod clones.&lt;/p&gt;
&lt;p&gt;Michael now registers the experiment to a model for use by others in the team. He also needs to remember to set the permissions on the registered model.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 903px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/864ddf7970d6455e0d77793bda3ff184/8728b/databricks-security-model-model-permissions.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 53.800000000000004%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAALCAIAAADwazoUAAAACXBIWXMAAAsSAAALEgHS3X78AAABlklEQVQoz32Q2c6bMBCFef+3aP4QFuOFJfeRyAJvkcuIRkG4tvHYxlTgVpVaqZ+8SZ6ZM3OiwzH59nWKTylCKE3Tpmnu9/vtdrter+H8D9Ep2UmTLMsOh0Pbts459ZtlWbz36z9475dlieIkKzApMK7qusD48Xh476214TuELstijPmrivc+KooCIZRlWY7Q8Rh3Xfd+vznnADDvOOeEEM/nk3MeKoZazrkIY4KKje2BUN/3UkohhHMOAEIL3nullNY6ZFprAWBd14gxlud5gQkra0JI3/fTNA3DwDkXQoQga+00TUqpoOyc01pvypRSytj53JzPf5Jfr9c4jlJKY8y6rsaYcRzDCHZnnmdrbUQIyfP8mBZxVqRZ2nUdAEzTJISQUgKA9z6MLaV0zoXOAcAYsykzSuM0/4qTPEdt267rqrUGAK11UAuGz/McPDfGcM4/n09EGcUE421v1+VyAQAhhFJqnmellPghtiVEMH9WahiG7ztRWZWYEkwoYWVVVfQXpKqquq7LsgytMcqapjnv7B5t/AQRJDRMicLnbAAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;databricks security model model permissions&quot;
        title=&quot;databricks security model model permissions&quot;
        src=&quot;/static/864ddf7970d6455e0d77793bda3ff184/8728b/databricks-security-model-model-permissions.png&quot;
        srcset=&quot;/static/864ddf7970d6455e0d77793bda3ff184/0eb09/databricks-security-model-model-permissions.png 500w,
/static/864ddf7970d6455e0d77793bda3ff184/8728b/databricks-security-model-model-permissions.png 903w&quot;
        sizes=&quot;(max-width: 903px) 100vw, 903px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;Michael Ross now deicides to hand off the remainder of the work (integrating the model predictions into an operational pipeline) to the Data Engineer in his team.&lt;/p&gt;
&lt;p&gt;Harvey Specter now logs in and since Michael has set the permission correctly he can pick up the same notebook. Since Michael used a Shared cluster, Harvey can use the cluster that Michael created.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Note: If Michael did not set the model permissions, Harvey would get the following error: &lt;code class=&quot;language-text&quot;&gt; RestException: PERMISSION_DENIED: User does not have any permission level assigned to the registered model.&lt;/code&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Harvey is able to use the following code to load the latest trained model that Michael prepared for him.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;py&quot;&gt;&lt;pre class=&quot;language-py&quot;&gt;&lt;code class=&quot;language-py&quot;&gt;model_name &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;nfl-decisiontree-players&quot;&lt;/span&gt;
model_udf &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; mlflow&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;pyfunc&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;spark_udf&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
  spark&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token string-interpolation&quot;&gt;&lt;span class=&quot;token string&quot;&gt;f&quot;models:/&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;model_name&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;/latest&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2 id=&quot;Appendix&quot; style=&quot;position:relative;&quot;&gt;Appendix&lt;a href=&quot;#Appendix&quot; aria-label=&quot;Appendix permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;h3 id=&quot;Restrictive-cluster-policy&quot; style=&quot;position:relative;&quot;&gt;Restrictive cluster policy&lt;a href=&quot;#Restrictive-cluster-policy&quot; aria-label=&quot;Restrictive cluster policy permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;json&quot;&gt;&lt;pre class=&quot;language-json&quot;&gt;&lt;code class=&quot;language-json&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;&quot;spark_conf.spark.databricks.cluster.profile&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&quot;type&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;fixed&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&quot;value&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;singleNode&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&quot;hidden&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;&quot;num_workers&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&quot;type&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;fixed&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&quot;value&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&quot;hidden&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;&quot;runtime_engine&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&quot;type&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;fixed&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&quot;value&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;STANDARD&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&quot;hidden&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;&quot;cluster_type&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&quot;type&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;fixed&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&quot;value&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;all-purpose&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&quot;hidden&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;false&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;&quot;cluster_name&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&quot;type&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;regex&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&quot;pattern&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;(Dev|PreProd|Prod)-(Discovery|Operational)-Family-[0-9.]*?ML?-[a-z]*-?[0-9]*?&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&quot;hidden&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;false&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;&quot;data_security_mode&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&quot;type&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;fixed&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&quot;value&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;LEGACY_SINGLE_USER&quot;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;&quot;autotermination_minutes&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&quot;type&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;fixed&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&quot;value&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;60&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;&quot;custom_tags.Team&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&quot;type&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;fixed&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&quot;value&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Family&quot;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;&quot;spark_version&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&quot;type&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;allowlist&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&quot;values&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;11.3.x-cpu-ml-scala2.12&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;11.3.x-scala2.12&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&quot;defaultValue&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;11.3.x-scala2.12&quot;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;&quot;node_type_id&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&quot;type&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;allowlist&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&quot;values&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Standard_F4&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&quot;defaultValue&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Standard_F4&quot;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;Sample-init_script-framework&quot; style=&quot;position:relative;&quot;&gt;Sample init_script framework&lt;a href=&quot;#Sample-init_script-framework&quot; aria-label=&quot;Sample init_script framework permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;This sample framework has a parent script that calls subsequent scripts to install packages for:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;apt&lt;/li&gt;
&lt;li&gt;python&lt;/li&gt;
&lt;li&gt;R&lt;/li&gt;
&lt;/ol&gt;
&lt;h4 id=&quot;clusterConfigssh&quot; style=&quot;position:relative;&quot;&gt;clusterConfigs.sh&lt;a href=&quot;#clusterConfigssh&quot; aria-label=&quot;clusterConfigssh permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h4&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;sh&quot;&gt;&lt;pre class=&quot;language-sh&quot;&gt;&lt;code class=&quot;language-sh&quot;&gt;&lt;span class=&quot;token shebang important&quot;&gt;#!/bin/bash&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;## add custom prompt to bashrc&lt;/span&gt;
&lt;span class=&quot;token function&quot;&gt;cat&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;${script_dir}&lt;/span&gt;/bashPrompt.sh &lt;span class=&quot;token operator&quot;&gt;&gt;&gt;&lt;/span&gt;~/.bashrc
&lt;span class=&quot;token comment&quot;&gt;## add custom vimrc using vim so much on the terminal&lt;/span&gt;
&lt;span class=&quot;token function&quot;&gt;cat&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;${script_dir}&lt;/span&gt;/.vimrc &lt;span class=&quot;token operator&quot;&gt;&gt;&gt;&lt;/span&gt;~/.vimrc
&lt;span class=&quot;token builtin class-name&quot;&gt;source&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;${script_dir}&lt;/span&gt;/packageInstallTest.sh&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h4 id=&quot;packageInstallTestsh&quot; style=&quot;position:relative;&quot;&gt;packageInstallTest.sh&lt;a href=&quot;#packageInstallTestsh&quot; aria-label=&quot;packageInstallTestsh permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h4&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;sh&quot;&gt;&lt;pre class=&quot;language-sh&quot;&gt;&lt;code class=&quot;language-sh&quot;&gt;&lt;span class=&quot;token shebang important&quot;&gt;#!/bin/bash&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;## install python packages from a requirements file&lt;/span&gt;
/databricks/python/bin/pip &lt;span class=&quot;token function&quot;&gt;install&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-r&lt;/span&gt; /&lt;span class=&quot;token variable&quot;&gt;${script_dir}&lt;/span&gt;/packageInstallTest.txt
&lt;span class=&quot;token comment&quot;&gt;## test installing jq using apt and libsodium-dev (which is a dependency for our next R one)&lt;/span&gt;
&lt;span class=&quot;token function&quot;&gt;apt&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-y&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;install&lt;/span&gt; jq
&lt;span class=&quot;token function&quot;&gt;apt&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-y&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;install&lt;/span&gt; libsodium-dev
&lt;span class=&quot;token comment&quot;&gt;## install library from R library list&lt;/span&gt;
&lt;span class=&quot;token function&quot;&gt;cat&lt;/span&gt; /&lt;span class=&quot;token variable&quot;&gt;${script_dir}&lt;/span&gt;/packageInstallTest.R &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; R --no-save&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h4 id=&quot;packageInstallTesttxt&quot; style=&quot;position:relative;&quot;&gt;packageInstallTest.txt&lt;a href=&quot;#packageInstallTesttxt&quot; aria-label=&quot;packageInstallTesttxt permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h4&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;nltk
spacy
file:/dbfs/FileStore/Admin/lib/python-package/en_core_web_md-3.1.0-py3-none-any.whl
imblearn&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h4 id=&quot;packageInstallTestR&quot; style=&quot;position:relative;&quot;&gt;packageInstallTest.R&lt;a href=&quot;#packageInstallTestR&quot; aria-label=&quot;packageInstallTestR permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h4&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;r&quot;&gt;&lt;pre class=&quot;language-r&quot;&gt;&lt;code class=&quot;language-r&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;## set the correct library path for Databricks spark&lt;/span&gt;
.libPaths&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;/databricks/spark/R/lib&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
databricksLibPath&lt;span class=&quot;token operator&quot;&gt;&amp;lt;-&lt;/span&gt;.libPaths&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;## install two packages to this library path&lt;/span&gt;
install.packages&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;plumber&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; repos&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;https://cran.csiro.au/&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; lib&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;databricksLibPath&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;</content:encoded></item><item><title><![CDATA[Databricks and Azure DevOps CICD Pipeline for DBFS]]></title><description><![CDATA[DevOps CICD Pipeline for Databricks This is in reference to this Git repo https://dev.azure.com/mortimer-xyz/mortie23/_git/dbfs-cicd-pieline…]]></description><link>https://mortimer.xyz/databricks-azure-devops-cicd-dbfs/</link><guid isPermaLink="false">https://mortimer.xyz/databricks-azure-devops-cicd-dbfs/</guid><pubDate>Thu, 11 Aug 2022 12:00:00 GMT</pubDate><content:encoded>&lt;h2 id=&quot;DevOps-CICD-Pipeline-for-Databricks&quot; style=&quot;position:relative;&quot;&gt;DevOps CICD Pipeline for Databricks&lt;a href=&quot;#DevOps-CICD-Pipeline-for-Databricks&quot; aria-label=&quot;DevOps CICD Pipeline for Databricks permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;This is in reference to this Git repo &lt;a href=&quot;https://dev.azure.com/mortimer-xyz/mortie23/_git/dbfs-cicd-pieline&quot;&gt;https://dev.azure.com/mortimer-xyz/mortie23/_git/dbfs-cicd-pieline&lt;/a&gt;&lt;/p&gt;
&lt;h3 id=&quot;Much-credit-to-Adam-Paternostro&quot; style=&quot;position:relative;&quot;&gt;Much credit to Adam Paternostro&lt;a href=&quot;#Much-credit-to-Adam-Paternostro&quot; aria-label=&quot;Much credit to Adam Paternostro permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;This is based on a simplified version of the repo from Adam Paternostro. Read up on this for a much more feature rich version of this.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/AdamPaternostro/Azure-Databricks-Dev-Ops&quot;&gt;Adam Paternostro, Azure-Databricks-Dev-Ops&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;I only required a small subset of what Adam was doing so I created my own how to in the process. The following is specifically about syncing Databricks init scripts from a Git repo to DBFS.&lt;/p&gt;
&lt;p&gt;There is a whole lot of things you need to get this to work. Most of it is documented elsewhere, so instead of re-writing it, I’ll reference it.&lt;/p&gt;
&lt;h2 id=&quot;Create-a-repo-in-Azure-DevOps&quot; style=&quot;position:relative;&quot;&gt;Create a repo in Azure DevOps&lt;a href=&quot;#Create-a-repo-in-Azure-DevOps&quot; aria-label=&quot;Create a repo in Azure DevOps permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Add the content of this repo.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 300px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/fff007750ba8985b572ceddd58d55ef4/a8a0d/cicd-scripts.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 58.333333333333336%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAMCAIAAADtbgqsAAAACXBIWXMAAAsSAAALEgHS3X78AAABiElEQVQoz32R3Y7TMBCF+yQ0Sf0747GdxM6PnaSttC1aoNtl4YYbJMQbcMnToy5ISGxY6bv9dOac2ZTMH88f++nMRKOwF9BxFYSOXEeuWqGjhI7JpmTuJZuS+bcPX9Ldo4CxiUfyE9WLrRewCW0ybq7DQWH3pjQrcsFcWeLnvmkpaErGJomd0BFolNBpMyAlhQPaVHG/Ihcl3rfeYws2k5uAkoQOKGkzACVXL5oS0LgiV8xdPn1dzk9cRbBJQo82G5crXm8rKnZ2W9FvVju7cX8f0omrqKAXOj7nDBX3xe4mr0719+yqwm9TO/gO/OKaRWFvbLbNnuqFXH7Fv63NhD+FaKDlupMQhY7PP7s9SejwWvJ2Z7lw3/dd7ztwM9pEfipuJc22MkVF5c4W/2HDVeA6Bp80RGXGZwYmA1N/2MmWqcDX2KBN+fJjevoZhrtxf435XT9f8vExHa5pfx3mD8Py0Of3pj5Qc/yHjYSI7Um5A9CIbgKbjZ+pntFmsBndhG7SNErsX/ILe/+BUNg6LRkAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;cicd scripts&quot;
        title=&quot;cicd scripts&quot;
        src=&quot;/static/fff007750ba8985b572ceddd58d55ef4/a8a0d/cicd-scripts.png&quot;
        srcset=&quot;/static/fff007750ba8985b572ceddd58d55ef4/a8a0d/cicd-scripts.png 300w&quot;
        sizes=&quot;(max-width: 300px) 100vw, 300px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;h2 id=&quot;Create-a-DevOps-Azure-Resource-Manager-service-connection&quot; style=&quot;position:relative;&quot;&gt;Create a DevOps Azure Resource Manager service connection&lt;a href=&quot;#Create-a-DevOps-Azure-Resource-Manager-service-connection&quot; aria-label=&quot;Create a DevOps Azure Resource Manager service connection permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;This will create it’s own service principal which we will be using later.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://docs.microsoft.com/en-us/azure/devops/pipelines/library/connect-to-azure?view=azure-devops&quot;&gt;Azure Resource Manager service connection&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 923px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/902bd4352de19842afe03ef392256e6e/14077/cicd-service-principal-params.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 39.4%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAICAIAAAB2/0i6AAAACXBIWXMAAAsSAAALEgHS3X78AAABAElEQVQY042OSW4DMQwE5///ywNiYOxZtXOX5EQ2YNg5pcEDAaJYPX1d0rYf63I79m1Zbtu6XK9zSun+j0yx2ObgCOizxmIuqdWm1ohV1JBMlUkqi4mwqn7Al3n/vszzdd1Pf7q4nyHnZLUhMrEUFAQgNkQCACLqb5l2n499zSmasjA9p/dORMEH+1T9ra2cYww5Z0Rsrb0OpeTgAwDWamZGRMIjIkxEzDzMUBIi3Pu9ttZ7f8FQAAqoamuNibxzIYQ4Jp7n6Zwb8OaLcw4eEZEXD1CedWqtzOSc9z7EOPCxBW9qUwCOMXnvmei9NhGVUgDHU0SJRUOWDPboNxS/5h93os+qbsUvBgAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;cicd service principal params&quot;
        title=&quot;cicd service principal params&quot;
        src=&quot;/static/902bd4352de19842afe03ef392256e6e/14077/cicd-service-principal-params.png&quot;
        srcset=&quot;/static/902bd4352de19842afe03ef392256e6e/0eb09/cicd-service-principal-params.png 500w,
/static/902bd4352de19842afe03ef392256e6e/14077/cicd-service-principal-params.png 923w&quot;
        sizes=&quot;(max-width: 923px) 100vw, 923px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;h2 id=&quot;Create-a-Key-Vault&quot; style=&quot;position:relative;&quot;&gt;Create a Key Vault&lt;a href=&quot;#Create-a-Key-Vault&quot; aria-label=&quot;Create a Key Vault permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://docs.microsoft.com/en-us/azure/key-vault/general/quick-create-portal&quot;&gt;Quick Create in Azure Portal&lt;/a&gt;&lt;/p&gt;
&lt;h2 id=&quot;Add-the-secrets-to-the-KeyVault&quot; style=&quot;position:relative;&quot;&gt;Add the secrets to the KeyVault&lt;a href=&quot;#Add-the-secrets-to-the-KeyVault&quot; aria-label=&quot;Add the secrets to the KeyVault permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;You’ll need to generate a secret for the service principal.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 624px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/97272cfdd2402bfa6c7f95ea661a0eed/08d47/cicd-secrets-keyvault-add.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 63.6%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAANCAIAAAAmMtkJAAAACXBIWXMAAA50AAAOdAFrJLPWAAABcElEQVQoz42S247cIAxA+f+fm5d9GK06SdVVyA0CBidgTKBKthtt252qR8iyhA6+CGHc/Pj2uN/vTdO0bXu73ZqmAYB5msdxBIBaaynliu8ceSkimJd+mPpeAgAiKqW01kQUY1zXjZnr75RPubAuOAfeu5QSM+eTWmuulYi8cx4gRPKRHUDKu9+C984BUEoCvTbGEtG+7+WEc45hnf3+2qGyOGhULihH44ILJgVhXHBa0GAS6qh6lL2aIUq97BTW1z5NCw7LNmgc9Doo7BVK5Qe99grtVoTxwTsAgG1bmfMpk5R9ZlrR138irMEYw7GhEPK+f8gypWSMQcRrnL8RRgNRzDkzMxG9RyklJ7bGrttWniO8M1oviHiuENDjL5kZLORzkKcyehtC/DxJSknKnihO0+jRM/P+BDHPEzNfj33IMudsD1yMsZTytYxOEaXrtpQSY+y67o//+PW2vz9e2rZZ9Lyo4xitxl6+vf34H/knODnxDCep/WcAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;cicd secrets keyvault add&quot;
        title=&quot;cicd secrets keyvault add&quot;
        src=&quot;/static/97272cfdd2402bfa6c7f95ea661a0eed/08d47/cicd-secrets-keyvault-add.png&quot;
        srcset=&quot;/static/97272cfdd2402bfa6c7f95ea661a0eed/0eb09/cicd-secrets-keyvault-add.png 500w,
/static/97272cfdd2402bfa6c7f95ea661a0eed/08d47/cicd-secrets-keyvault-add.png 624w&quot;
        sizes=&quot;(max-width: 624px) 100vw, 624px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;h2 id=&quot;Create-an-Environment-in-Azure-DevOps-pipelines&quot; style=&quot;position:relative;&quot;&gt;Create an Environment in Azure DevOps pipelines&lt;a href=&quot;#Create-an-Environment-in-Azure-DevOps-pipelines&quot; aria-label=&quot;Create an Environment in Azure DevOps pipelines permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://docs.microsoft.com/en-us/azure/devops/pipelines/process/environments?view=azure-devops&quot;&gt;Environment in Azure DevOps pipelines&lt;/a&gt;&lt;/p&gt;
&lt;h2 id=&quot;Add-an-Access-Policy-for-the-KeyVault&quot; style=&quot;position:relative;&quot;&gt;Add an Access Policy for the KeyVault&lt;a href=&quot;#Add-an-Access-Policy-for-the-KeyVault&quot; aria-label=&quot;Add an Access Policy for the KeyVault permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Give the service principal associated to the Azure Resource Manager service connection (create previously) service principal Secret management on the Key Vault.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://docs.microsoft.com/en-us/azure/key-vault/general/assign-access-policy?tabs=azure-portal&quot;&gt;Access Policy for the KeyVault&lt;/a&gt;&lt;/p&gt;
&lt;h2 id=&quot;Create-a-Databricks-secret-scope-backed-by-KeyVault&quot; style=&quot;position:relative;&quot;&gt;Create a Databricks secret scope backed by KeyVault&lt;a href=&quot;#Create-a-Databricks-secret-scope-backed-by-KeyVault&quot; aria-label=&quot;Create a Databricks secret scope backed by KeyVault permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 984px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/ad536202c0088473ad1aa30c93a490be/61b91/cicd-secrets-keyvault-backed.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 32.2%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAGCAIAAABM9SnKAAAACXBIWXMAAA50AAAOdAFrJLPWAAAA10lEQVQY012PSW7DMBAE+QtzM7cZLqJF0qLkBLokp/z/S4FJBwgMFPoyXQ0MUaGa2CBtPnXwzUDB0CysjNsL1QMzUl2opsz8h4R8uNiUzbd6hrQ7rD40DC2kjr5ivCsdjV0mjNupzV2i04blA5YOcfNph9BnG7A6LEIC444LGLhpcuGERCGBWJsdrEI4/iw5zu04IBcg5ItpPnNsoa9x6egLWfKx1tPHO5cwhu3bY3+fv6DM9OPr8/zp+zcxefftcQ1F2nzVN+UKE0CZntrb0EBrk4zLSqdfA3Iu2mZ3//gAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;cicd secrets keyvault backed&quot;
        title=&quot;cicd secrets keyvault backed&quot;
        src=&quot;/static/ad536202c0088473ad1aa30c93a490be/61b91/cicd-secrets-keyvault-backed.png&quot;
        srcset=&quot;/static/ad536202c0088473ad1aa30c93a490be/0eb09/cicd-secrets-keyvault-backed.png 500w,
/static/ad536202c0088473ad1aa30c93a490be/61b91/cicd-secrets-keyvault-backed.png 984w&quot;
        sizes=&quot;(max-width: 984px) 100vw, 984px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;h2 id=&quot;Create-and-then-run-the-pipeline&quot; style=&quot;position:relative;&quot;&gt;Create and then run the pipeline&lt;a href=&quot;#Create-and-then-run-the-pipeline&quot; aria-label=&quot;Create and then run the pipeline permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 792px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/1a75c7ed250548ea2419c69345d574aa/59f54/cicd-devops-pipelines-new.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 31.2%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAGCAYAAADDl76dAAAACXBIWXMAABJ0AAASdAHeZh94AAAA3ElEQVQY05WR3UrEMBCF+/q+hq/TC3dVRLIU3aIX3Ta2zT9N009SqFaFBQcOJzNMzplJilFbTi9v9Mpj/Ix2Ea0N3ntSSl+w1qKUZp5nlmUhxohSauWc557MRZgSo0sMNqIDmABp4UdkkQ17k31t40LKjqfHe+rzK+2lQXYXcq1tW+q6xlrHf6Jomoby8MCzEFRVxfFwRAixoixLhnFcG/M6G19DwZKwpzsmZ/AhEIInThPOfU/2+9Le4M+Ek9WI2xv69zOyH/iQkqEf6LoOY+0qnE3yJ+Vzfqtrgp8oJNG3VgoYgwAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;cicd devops pipelines new&quot;
        title=&quot;cicd devops pipelines new&quot;
        src=&quot;/static/1a75c7ed250548ea2419c69345d574aa/59f54/cicd-devops-pipelines-new.png&quot;
        srcset=&quot;/static/1a75c7ed250548ea2419c69345d574aa/0eb09/cicd-devops-pipelines-new.png 500w,
/static/1a75c7ed250548ea2419c69345d574aa/59f54/cicd-devops-pipelines-new.png 792w&quot;
        sizes=&quot;(max-width: 792px) 100vw, 792px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 588px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/972722c1c45587c365ef484331b1b87f/f81fd/cicd-devops-pipelines-run.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 149.8%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAeCAIAAACjcKk8AAAACXBIWXMAAAsSAAALEgHS3X78AAACS0lEQVQ4y5WVyW7bMBCG9fJFzz30Fdpr0IdJDj40diDLXEQO901LIY3sCIq3fgeBgPRzfg5nRpWdUUoBQNd14xP0M+M4VqfTabfbvb6+vr29tW0bYwwh4NM5hwtcX7bOM5M452ytxS/QgjHGWqu1llJaa40xSikpJQrWVADAGKOUhhCumhyG4eK2m0HPIYTKOQcAQggAuLzuuq584fIqxlhK0VpX1lohBKWUMYZnCyF473PO5QYxxq7rJjFGG4ZhHTPGeHF7i0lsjBFCKKW01gCACcej3mERW2s5523bohLF5S45577vJ3HOGQA451przjmm7aF4SZj33hiDl4xlMDzBYlspVdc1pbRpGgDAqkop3cn2p+2UkpiRUrZtK4QopYzjuL7YDbjFYlsIobU2xgCAPtPd5fOqmqbBmIwxTDsAbKpqE3kYhsU2ncFUr/vucbZTSn4Guw/XbgYTc5XFttYaC5tSKqXkM0IIQohzbt0qV2yHEBhjnHPGGCGEUppzHobhqmyb7ZyTmVNtjNFaO+fWB7t15sU2aHNsTnK+asaYtXZ8jkn8wUvTBiWFBPg6aB6IvffyXBto/skZOomllIfDoWmauq4Ph8N+v3fOrUfXLZRSFc6k/zKM+5ZSpvKs65oQgs0UY/TeY+WklK6KP+c2RsbySjO4SwhhY2fdz/0cfIpMCMFsPTzneZt+RDFGvkxc77211jm38dz3/VS3jBBufv6pv/9+P7apKqVMtyUl/uuMMdiV3vuN+Hhs9u9/a6J+vNTffr1/sPgPqhPQLubvbqAAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;cicd devops pipelines run&quot;
        title=&quot;cicd devops pipelines run&quot;
        src=&quot;/static/972722c1c45587c365ef484331b1b87f/f81fd/cicd-devops-pipelines-run.png&quot;
        srcset=&quot;/static/972722c1c45587c365ef484331b1b87f/0eb09/cicd-devops-pipelines-run.png 500w,
/static/972722c1c45587c365ef484331b1b87f/f81fd/cicd-devops-pipelines-run.png 588w&quot;
        sizes=&quot;(max-width: 588px) 100vw, 588px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;h2 id=&quot;DevOps-pipelines-results&quot; style=&quot;position:relative;&quot;&gt;DevOps pipelines results&lt;a href=&quot;#DevOps-pipelines-results&quot; aria-label=&quot;DevOps pipelines results permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 1138px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/e4c93223b74a344f0cdff9438b35b00d/0ff2a/cicd-devops-pipelines.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 37.2%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAHCAIAAACHqfpvAAAACXBIWXMAAAsSAAALEgHS3X78AAABAUlEQVQY033Ry07DMBCFYb//Y9EuWi5SIKJIKDRXGnucOI7qcTx2HFBTNqiCb/+PjjQsrz5PvOOiR7SI6L2PMS7LQkQhhPgvdre7fzscjvkRJLRtq5SyFsuyLIqirhtjDBG5G9OKPR2aoihazgFACDEMWg2667q+76WUiOj+xp4/3suygpUQQmtt7WSnKYQ5hDDPc4wx3KAVOz5uqrpZQ8E511oTOaUUIhpjENFaG5fl6zfy5Ghiu/1rmr5kWZbneZZlAOA9DYMaxxEA2vZ0Pp+dc/N8GeK9X9ZDlnC0mu32D5vtNkmSNE2TJGmahohAgtZadlKIy5brC679T+zMiPobYi+K6rqPM+EAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;cicd devops pipelines&quot;
        title=&quot;cicd devops pipelines&quot;
        src=&quot;/static/e4c93223b74a344f0cdff9438b35b00d/0ff2a/cicd-devops-pipelines.png&quot;
        srcset=&quot;/static/e4c93223b74a344f0cdff9438b35b00d/0eb09/cicd-devops-pipelines.png 500w,
/static/e4c93223b74a344f0cdff9438b35b00d/1263b/cicd-devops-pipelines.png 1000w,
/static/e4c93223b74a344f0cdff9438b35b00d/0ff2a/cicd-devops-pipelines.png 1138w&quot;
        sizes=&quot;(max-width: 1138px) 100vw, 1138px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 1274px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/66b3f0f0a2d3c8a2d50a17f61f1f00f0/0ba95/cicd-devops-pipelines-success.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 55.00000000000001%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAALCAIAAADwazoUAAAACXBIWXMAAAsSAAALEgHS3X78AAAB4UlEQVQozz2RW27bMBREtZBYInl5+abeCYoC3f8WDFiF0ca1Az9lSY6UQIgLWUB+5u9czhwGXdftD/vdbtf3/Waz+fnrh9EqS+LEu9hZBBYunqJwMWcULkgUeu/SNCFRGPR9f76cz6fz5+fnZrMpixwoMUoqgc4a76x3NvHOO2uNVlIYrZADJdEEt217u936vv/6+np9fX15LoFRraRW0mjlnXVGGy2t0QiMRCEl0UxO8O29OxwPx+NxHMfValUUuUBADkoKRiLBwWolEBglNAq/y8/9g37oL/XlcDgMw/C7qvIsNVrNPeUDAkYRQCJHDoJPd5EDf7QI3vvbbr99e3v7+Pioquq5LKxWeZZmaZJnSZlnWRJnafxSFkns5y1GK60kMBoMw1Bf6+bajONYVVVZFByY4FwKRM6mBDY9zmGq/tg8D2aUBPX18vffn/V6fTqdlstllqUCuXc2TXz8UO2MdtbYSTIXHOR0jUbhQiAPmrbZ7be77bZpm+VymWcpiUIEEMin3zJaChTIjZKMkm9h4eJpgrvuVtfXtmnv93u1WhV5ZozyzqRp7JzxzkjkErnVSj0ECuSzSKNV0HZtfa3ruh7Hcb1el2UOSIXiXDAU005GCVAy5cM8MMqBMUoEwn+S7+NdDcAsaQAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;cicd devops pipelines success&quot;
        title=&quot;cicd devops pipelines success&quot;
        src=&quot;/static/66b3f0f0a2d3c8a2d50a17f61f1f00f0/0ba95/cicd-devops-pipelines-success.png&quot;
        srcset=&quot;/static/66b3f0f0a2d3c8a2d50a17f61f1f00f0/0eb09/cicd-devops-pipelines-success.png 500w,
/static/66b3f0f0a2d3c8a2d50a17f61f1f00f0/1263b/cicd-devops-pipelines-success.png 1000w,
/static/66b3f0f0a2d3c8a2d50a17f61f1f00f0/0ba95/cicd-devops-pipelines-success.png 1274w&quot;
        sizes=&quot;(max-width: 1274px) 100vw, 1274px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;h2 id=&quot;Databricks-DBFS-success&quot; style=&quot;position:relative;&quot;&gt;Databricks DBFS success&lt;a href=&quot;#Databricks-DBFS-success&quot; aria-label=&quot;Databricks DBFS success permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Now, checking in our Databricks DBFS we see the folder and the scripts we copied.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 730px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/adc178e81fccb3cacf934526ba3779ce/a6a70/cicd-databricks-dbfs-success.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 52.400000000000006%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAKCAIAAAA7N+mxAAAACXBIWXMAAA50AAAOdAFrJLPWAAABvklEQVQoz42QwUsUYRjGPxSkQ53UGB1MV1vcrW3GYWd3MTHdZkepDnnJooN4EQ9Kl/6Rzh2EhMDVBTVBW7xI4KVO3hKRwljRndyZYb9ZZ+Z7v/cLlwLFPfS7vYff8/A+pC8Wf6Al7ylqJNqfULWx8THTNHO5nGEY2WzWMAzzGrk6pmmS+NDjwScTIxOTI89fyEr6Y2EVAcply7bt/f3vtuMIIRBRXILSquM4iEg6lbSsZmQl3aUNNkuRxXyBA6vYdsiY7/uMMQBgAAwREAEAOAfEmudR6pGnr6dfzb6ZnJmbmn87/nJqfasokLuuGwQBInLOOSIEgVMqVX7+cA4OwpMTVq1eJDJG3r1fWNssrn/eLu58WVxZ3f36zfPo77OzIPzLue/XDg9/7e05p6c16vEwZEdHzLYBkbTGBtpiWmu/eqMrSm62f8gXkINlWZRSr06VUt91aankHx9zy4JyGSoVBLj4uTs5dDczKiX0jkSyRe7Lr20IIQDg8kL86mDi30lkJd2beiQl9LbYQJPUs9RQrvc0kDuVVEQflu4nb8e15o5I42bORSOIrj9UjWd3UsM9mdFbUXX50+b/y38AckaypcpHR4YAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;cicd databricks dbfs success&quot;
        title=&quot;cicd databricks dbfs success&quot;
        src=&quot;/static/adc178e81fccb3cacf934526ba3779ce/a6a70/cicd-databricks-dbfs-success.png&quot;
        srcset=&quot;/static/adc178e81fccb3cacf934526ba3779ce/0eb09/cicd-databricks-dbfs-success.png 500w,
/static/adc178e81fccb3cacf934526ba3779ce/a6a70/cicd-databricks-dbfs-success.png 730w&quot;
        sizes=&quot;(max-width: 730px) 100vw, 730px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;If we modify the scripts and then &lt;code class=&quot;language-text&quot;&gt;git add .&lt;/code&gt; &lt;code class=&quot;language-text&quot;&gt;git commit -m &apos;message&apos;&lt;/code&gt; &lt;code class=&quot;language-text&quot;&gt;git push&lt;/code&gt; etc. We can configure the pipeline to run on a pull request completion to &lt;code class=&quot;language-text&quot;&gt;master&lt;/code&gt;.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Databricks using Azure ML model registry]]></title><description><![CDATA[Registering a model in AzureML from Databricks What if we want to use AzureML as a central model repository for all models across many…]]></description><link>https://mortimer.xyz/databricks-using-azureml-model-registry/</link><guid isPermaLink="false">https://mortimer.xyz/databricks-using-azureml-model-registry/</guid><pubDate>Wed, 10 Aug 2022 12:00:00 GMT</pubDate><content:encoded>&lt;h2 id=&quot;Registering-a-model-in-AzureML-from-Databricks&quot; style=&quot;position:relative;&quot;&gt;Registering a model in AzureML from Databricks&lt;a href=&quot;#Registering-a-model-in-AzureML-from-Databricks&quot; aria-label=&quot;Registering a model in AzureML from Databricks permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;What if we want to use AzureML as a central model repository for all models across many platforms (in this case Databricks workspaces). The AzureML model registry also uses MLFlow. I will follow these docs and show it step by step with examples.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://docs.microsoft.com/en-us/azure/machine-learning/how-to-use-mlflow-azure-databricks?tabs=azuremlsdk&quot;&gt;Docs&lt;/a&gt;&lt;/p&gt;
&lt;h2 id=&quot;Get-things-ready&quot; style=&quot;position:relative;&quot;&gt;Get things ready&lt;a href=&quot;#Get-things-ready&quot; aria-label=&quot;Get things ready permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;There will be a few secrets we will need to maintain to get this going. The authentication we will use from Databricks to AzureML will be using an Azure Service Principal.&lt;/p&gt;
&lt;h3 id=&quot;Create-a-Service-Principal&quot; style=&quot;position:relative;&quot;&gt;Create a Service Principal&lt;a href=&quot;#Create-a-Service-Principal&quot; aria-label=&quot;Create a Service Principal permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Firstly lets create a Service Principal in our Azure Active Directory. Using the Azure PowerShell module:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;powershell&quot;&gt;&lt;pre class=&quot;language-powershell&quot;&gt;&lt;code class=&quot;language-powershell&quot;&gt;az ad &lt;span class=&quot;token function&quot;&gt;sp&lt;/span&gt; create-&lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;rbac &lt;span class=&quot;token operator&quot;&gt;--&lt;/span&gt;sdk-auth &lt;span class=&quot;token operator&quot;&gt;--&lt;/span&gt;name ml-auth &lt;span class=&quot;token operator&quot;&gt;--&lt;/span&gt;role Contributor &lt;span class=&quot;token operator&quot;&gt;--&lt;/span&gt;scopes &lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;subscriptions/&amp;lt;subid&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;You’ll see output like the following JSON:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;json&quot;&gt;&lt;pre class=&quot;language-json&quot;&gt;&lt;code class=&quot;language-json&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;&quot;clientId&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;&amp;lt;guid&gt;&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;&quot;clientSecret&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;&amp;lt;guid&gt;&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;&quot;subscriptionId&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;&amp;lt;guid&gt;&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;&quot;tenantId&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;&amp;lt;guid&gt;&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;&quot;activeDirectoryEndpointUrl&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;https://login.microsoftonline.com&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;&quot;resourceManagerEndpointUrl&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;https://management.azure.com/&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;&quot;activeDirectoryGraphResourceId&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;https://graph.windows.net/&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;&quot;sqlManagementEndpointUrl&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;https://management.core.windows.net:8443/&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;&quot;galleryEndpointUrl&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;https://gallery.azure.com/&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;&quot;managementEndpointUrl&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;https://management.core.windows.net/&quot;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;Create-the-secret-scope&quot; style=&quot;position:relative;&quot;&gt;Create the secret scope&lt;a href=&quot;#Create-the-secret-scope&quot; aria-label=&quot;Create the secret scope permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Using this output we will put the bits we will need in a Databricks secret scope. If you don’t know how to do this using the Databricks cli you can follow this &lt;a href=&quot;https://docs.microsoft.com/en-us/azure/databricks/dev-tools/cli/&quot;&gt;Databricks CLI&lt;/a&gt;. The main thing is to install it using &lt;code class=&quot;language-text&quot;&gt;pip&lt;/code&gt; and then configure it to authenticate to your workspace using &lt;code class=&quot;language-text&quot;&gt;databricks configure -t &amp;lt;databricks-pat&gt;&lt;/code&gt;. Once you have done this you need to create a secret scope (I called mine &lt;code class=&quot;language-text&quot;&gt;azureml&lt;/code&gt;).&lt;/p&gt;
&lt;h3 id=&quot;Store-the-secrets&quot; style=&quot;position:relative;&quot;&gt;Store the secrets&lt;a href=&quot;#Store-the-secrets&quot; aria-label=&quot;Store the secrets permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;databricks secrets put &lt;span class=&quot;token parameter variable&quot;&gt;--scope&lt;/span&gt; azureml &lt;span class=&quot;token parameter variable&quot;&gt;--key&lt;/span&gt; clientId
databricks secrets put &lt;span class=&quot;token parameter variable&quot;&gt;--scope&lt;/span&gt; azureml &lt;span class=&quot;token parameter variable&quot;&gt;--key&lt;/span&gt; clientSecret
databricks secrets put &lt;span class=&quot;token parameter variable&quot;&gt;--scope&lt;/span&gt; azureml &lt;span class=&quot;token parameter variable&quot;&gt;--key&lt;/span&gt; subscriptionId
databricks secrets put &lt;span class=&quot;token parameter variable&quot;&gt;--scope&lt;/span&gt; azureml &lt;span class=&quot;token parameter variable&quot;&gt;--key&lt;/span&gt; tenantId&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Let’s also store some of the other info we need in the same secret scope while we are at it.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;databricks secrets put &lt;span class=&quot;token parameter variable&quot;&gt;--scope&lt;/span&gt; azureml &lt;span class=&quot;token parameter variable&quot;&gt;--key&lt;/span&gt; subscriptionid
databricks secrets put &lt;span class=&quot;token parameter variable&quot;&gt;--scope&lt;/span&gt; azureml &lt;span class=&quot;token parameter variable&quot;&gt;--key&lt;/span&gt; resourcegroup
databricks secrets put &lt;span class=&quot;token parameter variable&quot;&gt;--scope&lt;/span&gt; azureml &lt;span class=&quot;token parameter variable&quot;&gt;--key&lt;/span&gt; workspacename&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;Configure-the-external-model-registry&quot; style=&quot;position:relative;&quot;&gt;Configure the external model registry&lt;a href=&quot;#Configure-the-external-model-registry&quot; aria-label=&quot;Configure the external model registry permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Now to configure your Databricks cluster to use the external MLFlow model registry you need to add this extra cell in your notebook:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;py&quot;&gt;&lt;pre class=&quot;language-py&quot;&gt;&lt;code class=&quot;language-py&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;# Change the MLFlow tracking URI to the AzureML one&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; os
&lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; azure&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;ai&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;ml &lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; MLClient
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; azure&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;identity &lt;span class=&quot;token keyword&quot;&gt;as&lt;/span&gt; azurei
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; mlflow

&lt;span class=&quot;token comment&quot;&gt;# set environment variables for the service principal authentication of DefaultAzureCredential() getting details from the secret scope&lt;/span&gt;
os&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;environ&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;AZURE_CLIENT_ID&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; dbutils&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;secrets&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;get&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;scope &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;azureml&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; key &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;clientId&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
os&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;environ&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;AZURE_TENANT_ID&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; dbutils&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;secrets&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;get&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;scope &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;azureml&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; key &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;tenantId&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
os&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;environ&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;AZURE_CLIENT_SECRET&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; dbutils&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;secrets&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;get&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;scope &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;azureml&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; key &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;clientSecret&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;# Enter details of your AzureML workspace getting details from the secret scope&lt;/span&gt;
subscription_id &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; dbutils&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;secrets&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;get&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;scope &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;azureml&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; key &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;subscriptionid&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
resource_group &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; dbutils&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;secrets&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;get&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;scope &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;azureml&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; key &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;resourcegroup&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
workspace_name &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; dbutils&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;secrets&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;get&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;scope &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;azureml&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; key &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;workspacename&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

ml_client &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; MLClient&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
    credential&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;azurei&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;DefaultAzureCredential&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;logging_enable&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token boolean&quot;&gt;True&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    subscription_id&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;subscription_id&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    resource_group_name&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;resource_group
&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

azureml_mlflow_uri &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; ml_client&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;workspaces&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;get&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;workspace_name&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;mlflow_tracking_uri
mlflow&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;set_tracking_uri&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;azureml_mlflow_uri&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;azureml_mlflow_uri&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;This should output the URI of your AzureML model registry.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;log&quot;&gt;&lt;pre class=&quot;language-log&quot;&gt;&lt;code class=&quot;language-log&quot;&gt;azureml&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;australiaeast&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;api&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;azureml&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;ms&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;mlflow&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;v1.0&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;subscriptions&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;REDACTED&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token file-path string&quot;&gt;/resourceGroups/&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;REDACTED&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token file-path string&quot;&gt;/providers/Microsoft.MachineLearningServices/workspaces/&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;REDACTED&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2 id=&quot;Train-you-model&quot; style=&quot;position:relative;&quot;&gt;Train you model&lt;a href=&quot;#Train-you-model&quot; aria-label=&quot;Train you model permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Now, when we log any artifacts using MLFlow within Databricks, it will be logged against the MLFlow registry in AzureML.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;mlflow.spark.log_model(spark_model=model, artifact_path=&quot;model&quot;)&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Experiment list:&lt;br&gt;
&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 1091px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/2b1bace9a444a77fd22a41c43f612090/acfc2/azureml-experiment.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 57.199999999999996%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAALCAYAAAB/Ca1DAAAACXBIWXMAABJ0AAASdAHeZh94AAABaUlEQVQoz3VTi26EIBD0/7+xbXqnJz0VXxwvBabZVYxn001GcNiXw1o0TYN7WaJpnmibBlVVQfwIlGUJUT/wfD55X9c1r9YakKWUGCAgHe8FdlvWtJ3tzmQvk7n0h0sp7khQOjIXY0RBDzLrI0JMh9OyLLhXHYxxfE5+xlhUdQ+lXuzHjSwrHmJACJELFxTsnMM4DlgWz8nWdUXbdrjdviGE4OTee5ajLO+8UgxxP0Lg6/ODZXLObx0eepy0Iedpmjkwxcgd0n6eZ1hr+Z2gtUbXSRhjEELYOsxGDkTm4L7vYU7BmbMXbhgGXlnDl1JQOyjZ1ahgDj5/xX9cYbSGlHLrxhiuRFqQbpfUbxNwqviWlD/ZGM8XQZdC2hEoYdbz2sWZP58fYzOOI6TsEeO7nsQTpmnkAlS0aVvIvod3HrLrDl3XELYOKdC6CKUDwy/bPEUan7DN5aRbzGrmzulPoUuh5CRRnuOs4S9jSluiC7bJxwAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;azureml experiment&quot;
        title=&quot;azureml experiment&quot;
        src=&quot;/static/2b1bace9a444a77fd22a41c43f612090/acfc2/azureml-experiment.png&quot;
        srcset=&quot;/static/2b1bace9a444a77fd22a41c43f612090/0eb09/azureml-experiment.png 500w,
/static/2b1bace9a444a77fd22a41c43f612090/1263b/azureml-experiment.png 1000w,
/static/2b1bace9a444a77fd22a41c43f612090/acfc2/azureml-experiment.png 1091w&quot;
        sizes=&quot;(max-width: 1091px) 100vw, 1091px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;Specific experiment:&lt;br&gt;
&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 736px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/bdd19b3f7dea531db38b5a63d60415f5/bc563/azureml-experiment-nfl.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 98%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAUCAYAAACNiR0NAAAACXBIWXMAABJ0AAASdAHeZh94AAAB1UlEQVQ4y6VU2Y7bMAz0/39af6BPBTaHLMWybieWJcuzIIMEwaLbbRICBHVY1HA4VreUDWlqSOcNGwCax/OGSwZ8rCi1YVmAvAClNNS6IQYPPQxQUmEcR0ilEGMCWZdiwmkYMOgRQghoPfJ8v98jhAklV9gg4IJGihPO5zPWdUWt9e6tNfZt29B576H1AGsNdh8ffJseNXohMOeCVhvSRSNNFt4FXC4z/mVdXVf08oTReL7FOYt1bZjnGc45RuCMg/eREdB+CB7eeSzLwmOKZIyQOBIqQg4BpVbkPKOUgpwzJ21r4zWaU6m0lpeMZSn37yjeS14bEOMEYzTfRJ5SwjRNiDGyhxDuh2l+5REwo4UxBtZaPsMJv3JAi9/54/7X8Z1DQqSUxH63gzH2x6Q/eUelnNQJ1G1qAC3e+Hgp4SUn/P7zC9Y6pDQx8TeUr1hX6gI9Skglkab033x+i5AQ6WHE4XDgjvkQOD5q6zmEzKFC3/dX3bXGeqT4UkJqBOlIKcWRNGesfQNhLTgcjxDiiFpXbgr5o9aeRkhPECn9byJ/usuUkPg7iiNHesLqg3SeRkjlWWMQYrw2hH/07X2EfS8g+h4++PeETQmllNzd21P0armU8BOmqCBZF1nhOQAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;azureml experiment nfl&quot;
        title=&quot;azureml experiment nfl&quot;
        src=&quot;/static/bdd19b3f7dea531db38b5a63d60415f5/bc563/azureml-experiment-nfl.png&quot;
        srcset=&quot;/static/bdd19b3f7dea531db38b5a63d60415f5/0eb09/azureml-experiment-nfl.png 500w,
/static/bdd19b3f7dea531db38b5a63d60415f5/bc563/azureml-experiment-nfl.png 736w&quot;
        sizes=&quot;(max-width: 736px) 100vw, 736px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;Model list:&lt;br&gt;
&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 858px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/743e948d741334f2605e1287c469dff1/fd891/azureml-model.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 44.60000000000001%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAJCAIAAAC9o5sfAAAACXBIWXMAABJcAAASXAFoxDaJAAABCklEQVQoz5WQzW7EIAyE8/5P2T1uEkgAY/wLLcl2VanqoXPBDBr7Mwux52IVTdWOJLlILsqi274B1DGGe/9LSwj7x+OxrauZjX9qEbHncw1hcx+l4JlOVQUAVSWi1io23wOkfEIFEUsp5ZK99xnuYxAJYjVzZiEiVWVmVRURm9v01uaNWYmup+nbxFa1nCHG2LDxlbSXVFXHGP0aci0/cgaAQkQxRmZeVDUeBxEh4m39DPzWcSmlhIjLy4qRie7azACAiErOInKecV9XYe6973s4j+NuPbHvA5FrU5b5Eb33N7i7Q+VUCGnWtdZSJvYN+JrMMgAdm715v9uPxh2pN3ZzZ2ZsjS59hT8B69sOWiVK7fcAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;azureml model&quot;
        title=&quot;azureml model&quot;
        src=&quot;/static/743e948d741334f2605e1287c469dff1/fd891/azureml-model.png&quot;
        srcset=&quot;/static/743e948d741334f2605e1287c469dff1/0eb09/azureml-model.png 500w,
/static/743e948d741334f2605e1287c469dff1/fd891/azureml-model.png 858w&quot;
        sizes=&quot;(max-width: 858px) 100vw, 858px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;
Specific model:&lt;br&gt;
&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 822px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/af34840dc164d1d290f6625ed99f72da/a6c2c/azureml-model-nfl.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 84.39999999999999%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAARCAIAAABSJhvpAAAACXBIWXMAABJcAAASXAFoxDaJAAABbElEQVQ4y52TW3MbIQxG/f9/Y8dxukntXVbLTSCBuCR4k0nSmbhxzwPDgw6IDzikVBxKpFprwyges/U55xpCdig5ZaMNABijSymttVH3zsEY/fvK4+NJrWu/hwNRnOczEcG6MnMMwVpbStm2LRIxMTMRkd60995YE2PsvbfWhsxJ/iw+S0mcSqkiwsy1ViLKknPOcoVoLMPMWeRj51K7dWitkdJWgBhja91aBwDOWRFBROc8ImIIiN57767UWg+9d2Zy3qeU2if2YPYOv2PIADA9PWEIX0pvavvqQ16WBQDoys+jfpMB4HK5IOKe5H2y9/4u7e+dT6eT1pqZEbHWeodsrZ2mSWtNRCGEPeSfysuyHI9HpdR+gdu2AcA8zwBQSvmHrJSydryHlBKnxMxjMoZ0o4s3eV3V+fycmP4nsFmZXw/n6Xl1QSwKxhJi2ez4kjeOP+TWGiUJJMQSo4QokYRIfMghZpHyHa/P8wVEguQRv5hEuQAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;azureml model nfl&quot;
        title=&quot;azureml model nfl&quot;
        src=&quot;/static/af34840dc164d1d290f6625ed99f72da/a6c2c/azureml-model-nfl.png&quot;
        srcset=&quot;/static/af34840dc164d1d290f6625ed99f72da/0eb09/azureml-model-nfl.png 500w,
/static/af34840dc164d1d290f6625ed99f72da/a6c2c/azureml-model-nfl.png 822w&quot;
        sizes=&quot;(max-width: 822px) 100vw, 822px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Model scoring API as a Docker container]]></title><description><![CDATA[Use Docker to host a Python FastAPI service In my preview blog post (Model scoring API) I went through the technical basics of a model…]]></description><link>https://mortimer.xyz/model-scoring-api-as-docker/</link><guid isPermaLink="false">https://mortimer.xyz/model-scoring-api-as-docker/</guid><pubDate>Thu, 04 Aug 2022 12:00:00 GMT</pubDate><content:encoded>&lt;h2 id=&quot;Use-Docker-to-host-a-Python-FastAPI-service&quot; style=&quot;position:relative;&quot;&gt;Use Docker to host a Python FastAPI service&lt;a href=&quot;#Use-Docker-to-host-a-Python-FastAPI-service&quot; aria-label=&quot;Use Docker to host a Python FastAPI service permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;In my preview blog post (&lt;a href=&quot;https://mortimer.xyz/model-scoring-api/&quot;&gt;Model scoring API&lt;/a&gt;) I went through the technical basics of a model scoring API. I finished it off at the step of having it running on you local client machine (laptop in my case). However to host such a service we need a way to deploy this somewhere. We have lots of options. The first option that comes to mind would be to:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;spin up a cloud Linux virtual machine&lt;/li&gt;
&lt;li&gt;go through the process of installing Python and the packages required&lt;/li&gt;
&lt;li&gt;run the uvicorn service in the background&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;However, let’s go through the process of hosting it using Docker. The benefits are the portability of the resulting service.&lt;/p&gt;
&lt;h3 id=&quot;Install-Docker-on-your-local-computer&quot; style=&quot;position:relative;&quot;&gt;Install Docker on your local computer&lt;a href=&quot;#Install-Docker-on-your-local-computer&quot; aria-label=&quot;Install Docker on your local computer permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;I’m using Windows now for this process (if you are you may need to install the Docker for Windows application). The default is for it to build Windows containers (which I wasn’t aware of at the time). This results in a Docker image that needs to be run on Windows. So if you are planning on hosting it on a Windows server, then go with this. Otherwise, like I did in the end, you will need to switch to Linux containers (assuming you have WSL2 on your machine).&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 446px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/7d2b89bb479f0f98921f240ae3641e86/6dbe6/docker-wsl.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 114.34977578475336%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAXCAIAAACEf/j0AAAACXBIWXMAAAsSAAALEgHS3X78AAACsUlEQVQ4y42SS4+kNhSF+f/bRMoyyjJKfsJssixNpE5BuqkqqnmDH5inbfzCOILqqZlMq0tzhBAGPt97fI/nbrIuYglVdGYz5/PMOWNcCCmE5PMspFzt99LaeM65dYcvJmWGsYlRxjhjXd9PlE7bwzBRuv32fxmzbPBOuzRK6ET5zCmls5itXd03+o50zi3LDt8WfhD0fQ8hrOsaYWTM4h5Ka+3dN86ybJ7nrmsxbhCEE2UPyHVdp4m+wdbaZVmMMUIIpfTtSN6ZNEoprTYtu77CzjklZVEURZZLpd7s3C/npJRD34/jSBoiZmGt9W4f7H7X2nTjNEx0oBQ2TQGR0uajzt8qL0JoSpeuo9dXeDwiP4C+T8KQxfGcpirPZZHbaVyFsEK8zWaDrceThF+vQxThS4TLKj6F8fkSn09JdMVlhcsK5iVIsyr4Fzw9wSDAQdBjPFE69IOn2naO46mqCEKgyEM/uL6cXs/nMAiej34UhqEfPP9zjMITwQ3Ki/TTJ5yk3TAS0m5tWylnCLctoggcj00YwiAgLy/45bl+emrPZ12WMst4/Cqqyu5n+dXz/cDsukpj1LIIraVZzLouW3A3l/e4rV/S9iWeztlly5OWEkFEmgYBBACUUt4z8e3M3lW+zVmpuq7btmtIgzGqALy9/3BUewtbvPY5KwBAS9oGY8a51npd10cwISQt67brbuth2jWO95B9pM1zBeBPf7WADKCuOOfjOGhj3A9oq4wQqsq673sAAGOsLMtxmn4UBgD9/jmpmq5ryUQphLBt23vDD2HrIYz/TgjuBgRA1/eka7uuzbIMgDrNsqIolVLmnZbFSCm9PM//+PO3JI6j/FqVie8H/vFY13We50VRUEr5B2KMeWma/vLrz5fL5ZTEsC4Oh8+Hw6FpSFlWAMDHtv8DEmAjxteSaHgAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;docker wsl&quot;
        title=&quot;docker wsl&quot;
        src=&quot;/static/7d2b89bb479f0f98921f240ae3641e86/6dbe6/docker-wsl.png&quot;
        srcset=&quot;/static/7d2b89bb479f0f98921f240ae3641e86/6dbe6/docker-wsl.png 446w&quot;
        sizes=&quot;(max-width: 446px) 100vw, 446px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;h3 id=&quot;Build-the-image&quot; style=&quot;position:relative;&quot;&gt;Build the image&lt;a href=&quot;#Build-the-image&quot; aria-label=&quot;Build the image permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;You’ll need to start by cloning the associated repo &lt;a href=&quot;https://github.com/mortie23/fastapi-linear-model-demo&quot;&gt;https://github.com/mortie23/fastapi-linear-model-demo&lt;/a&gt;.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token function&quot;&gt;git&lt;/span&gt; clone https://github.com/mortie23/fastapi-linear-model-demo.git&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Then lets build the Docker image.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token function&quot;&gt;docker&lt;/span&gt; build &lt;span class=&quot;token parameter variable&quot;&gt;-t&lt;/span&gt; mortimerxyz/linear:1.0 &lt;span class=&quot;token builtin class-name&quot;&gt;.&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Docker files define &lt;em&gt;layers&lt;/em&gt;. In this Docker file we start from a base image (which has Python3.9 on it) and then go through steps to build up the image by copying files we have in the repo. We also need to install the Python packages from the &lt;em&gt;requirements.txt&lt;/em&gt; files within the image and then define the command that will run the service when the container is run.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;dockerfile&quot;&gt;&lt;pre class=&quot;language-dockerfile&quot;&gt;&lt;code class=&quot;language-dockerfile&quot;&gt;&lt;span class=&quot;token instruction&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;FROM&lt;/span&gt; python:3.9&lt;/span&gt;

&lt;span class=&quot;token instruction&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;WORKDIR&lt;/span&gt; /&lt;/span&gt;

&lt;span class=&quot;token instruction&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;COPY&lt;/span&gt; ./requirements.txt ./&lt;/span&gt;

&lt;span class=&quot;token instruction&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;RUN&lt;/span&gt; pip install --no-cache-dir --upgrade -r requirements.txt&lt;/span&gt;

&lt;span class=&quot;token instruction&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;COPY&lt;/span&gt; ./fastapiApp /app&lt;/span&gt;

&lt;span class=&quot;token instruction&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;CMD&lt;/span&gt; [&lt;span class=&quot;token string&quot;&gt;&quot;uvicorn&quot;&lt;/span&gt;, &lt;span class=&quot;token string&quot;&gt;&quot;app.model:app&quot;&lt;/span&gt;, &lt;span class=&quot;token string&quot;&gt;&quot;--host&quot;&lt;/span&gt;, &lt;span class=&quot;token string&quot;&gt;&quot;0.0.0.0&quot;&lt;/span&gt;, &lt;span class=&quot;token string&quot;&gt;&quot;--port&quot;&lt;/span&gt;, &lt;span class=&quot;token string&quot;&gt;&quot;80&quot;&lt;/span&gt;]&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h4 id=&quot;Windows-using-PowerShell&quot; style=&quot;position:relative;&quot;&gt;Windows using PowerShell&lt;a href=&quot;#Windows-using-PowerShell&quot; aria-label=&quot;Windows using PowerShell permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;Since I first run this on Windows, I have the logs here. The Windows build took significantly longer than the Linux one below.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;log&quot;&gt;&lt;pre class=&quot;language-log&quot;&gt;&lt;code class=&quot;language-log&quot;&gt;Sending build context to Docker daemon  &lt;span class=&quot;token number&quot;&gt;3.322MB&lt;/span&gt;
Step &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;6&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; FROM python&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;3.9&lt;/span&gt;
 &lt;span class=&quot;token separator comment&quot;&gt;---&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;05cae63731aa&lt;/span&gt;
Step &lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;6&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; WORKDIR &lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;
 &lt;span class=&quot;token separator comment&quot;&gt;---&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; Running in &lt;span class=&quot;token number&quot;&gt;1e478e292609&lt;/span&gt;
Removing intermediate container &lt;span class=&quot;token number&quot;&gt;1e478e292609&lt;/span&gt;
 &lt;span class=&quot;token separator comment&quot;&gt;---&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; be030c7a42e0
Step &lt;span class=&quot;token number&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;6&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; COPY &lt;span class=&quot;token file-path string&quot;&gt;./requirements.txt&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;
 &lt;span class=&quot;token separator comment&quot;&gt;---&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;2487f2c14aec&lt;/span&gt;
Step &lt;span class=&quot;token number&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;6&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; RUN pip install &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;no&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;cache&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;dir &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;upgrade &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;r &lt;span class=&quot;token domain constant&quot;&gt;requirements.txt&lt;/span&gt;
 &lt;span class=&quot;token separator comment&quot;&gt;---&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; Running in f8b71525170b
Collecting fastapi&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;all&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
  Downloading fastapi&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0.79.0&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;py3&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;none&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;any&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;whl &lt;span class=&quot;token operator&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;54&lt;/span&gt; kB&lt;span class=&quot;token operator&quot;&gt;)&lt;/span&gt;
     &lt;span class=&quot;token separator comment&quot;&gt;----------------------------------------&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;54.6&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;54.6&lt;/span&gt; KB &lt;span class=&quot;token number&quot;&gt;1.4&lt;/span&gt; MB&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;s eta &lt;span class=&quot;token time number&quot;&gt;0:00:00&lt;/span&gt;
Collecting uvicorn
  Downloading uvicorn&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0.18.2&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;py3&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;none&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;any&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;whl &lt;span class=&quot;token operator&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;57&lt;/span&gt; kB&lt;span class=&quot;token operator&quot;&gt;)&lt;/span&gt;
     &lt;span class=&quot;token separator comment&quot;&gt;----------------------------------------&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;57.0&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;57.0&lt;/span&gt; KB &lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt; eta &lt;span class=&quot;token time number&quot;&gt;0:00:00&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
Successfully installed &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;

 &lt;span class=&quot;token separator comment&quot;&gt;---&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; b901eee1947f
Step &lt;span class=&quot;token number&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;6&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; COPY &lt;span class=&quot;token file-path string&quot;&gt;./fastapiApp&lt;/span&gt; &lt;span class=&quot;token file-path string&quot;&gt;/app&lt;/span&gt;
 &lt;span class=&quot;token separator comment&quot;&gt;---&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; b65d2074826b
Step &lt;span class=&quot;token number&quot;&gt;6&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;6&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; CMD &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;uvicorn&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;app.model:app&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;--host&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;0.0.0.0&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;--port&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;80&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
 &lt;span class=&quot;token separator comment&quot;&gt;---&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; Running in &lt;span class=&quot;token number&quot;&gt;8f20e0e45191&lt;/span&gt;
Removing intermediate container &lt;span class=&quot;token number&quot;&gt;8f20e0e45191&lt;/span&gt;
 &lt;span class=&quot;token separator comment&quot;&gt;---&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;24ba8c92979c&lt;/span&gt;
Successfully built &lt;span class=&quot;token number&quot;&gt;24ba8c92979c&lt;/span&gt;
Successfully tagged mortimerxyz&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;linear&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1.0&lt;/span&gt;

Use &lt;span class=&quot;token string&quot;&gt;&apos;docker scan&apos;&lt;/span&gt; to run Snyk tests against images to find vulnerabilities and learn how to fix them&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h4 id=&quot;WSL-on-bash&quot; style=&quot;position:relative;&quot;&gt;WSL on bash&lt;a href=&quot;#WSL-on-bash&quot; aria-label=&quot;WSL on bash permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;After switching Docker to Linux containers I ran the build again from WSL bash shell. I’m going to push this one so I can then pull it on my Chromebook (Debian) and test using it.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;log&quot;&gt;&lt;pre class=&quot;language-log&quot;&gt;&lt;code class=&quot;language-log&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; Building &lt;span class=&quot;token number&quot;&gt;74.9s&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;)&lt;/span&gt; FINISHED
 &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;internal&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; load build definition from Dockerfile                     &lt;span class=&quot;token number&quot;&gt;0.1s&lt;/span&gt;
 &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; transferring dockerfile&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;430B&lt;/span&gt;                                     &lt;span class=&quot;token number&quot;&gt;0.0s&lt;/span&gt;
 &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;internal&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; load &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;dockerignore                                        &lt;span class=&quot;token number&quot;&gt;0.1s&lt;/span&gt;
 &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; transferring context&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;2B&lt;/span&gt;                                          &lt;span class=&quot;token number&quot;&gt;0.0s&lt;/span&gt;
 &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;internal&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; load metadata for docker&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;io&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;library&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;python&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;3.9&lt;/span&gt;           &lt;span class=&quot;token number&quot;&gt;10.3s&lt;/span&gt;
 &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;auth&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; library&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;python&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;pull token for &lt;span class=&quot;token domain constant&quot;&gt;registry-1.docker.io&lt;/span&gt;            &lt;span class=&quot;token number&quot;&gt;0.0s&lt;/span&gt;
 &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; FROM docker&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;io&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;library&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;python&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;3.9&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;@&lt;/span&gt;sha256&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;                &lt;span class=&quot;token number&quot;&gt;51.3s&lt;/span&gt;
 &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; resolve docker&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;io&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;library&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;python&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;3.9&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;@&lt;/span&gt;sha256&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;f                &lt;span class=&quot;token number&quot;&gt;0.0s&lt;/span&gt;
 &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; sha256&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;2.22kB&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;2.22kB&lt;/span&gt;                                     &lt;span class=&quot;token number&quot;&gt;0.0s&lt;/span&gt;
 &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;internal&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; load build context                                        &lt;span class=&quot;token number&quot;&gt;0.1s&lt;/span&gt;
 &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; transferring context&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;832B&lt;/span&gt;                                        &lt;span class=&quot;token number&quot;&gt;0.1s&lt;/span&gt;
 &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; COPY &lt;span class=&quot;token file-path string&quot;&gt;./requirements.txt&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;                                     &lt;span class=&quot;token number&quot;&gt;0.6s&lt;/span&gt;
 &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; RUN pip install &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;no&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;cache&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;dir &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;upgrade &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;r &lt;span class=&quot;token domain constant&quot;&gt;requirements.txt&lt;/span&gt;  &lt;span class=&quot;token number&quot;&gt;11.8s&lt;/span&gt;
 &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; COPY &lt;span class=&quot;token file-path string&quot;&gt;./fastapiApp&lt;/span&gt; &lt;span class=&quot;token file-path string&quot;&gt;/app&lt;/span&gt;                                         &lt;span class=&quot;token number&quot;&gt;0.0s&lt;/span&gt;
 &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; exporting to image                                                   &lt;span class=&quot;token number&quot;&gt;0.6s&lt;/span&gt;
 &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; exporting layers                                                  &lt;span class=&quot;token number&quot;&gt;0.6s&lt;/span&gt;
 &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; writing image sha256&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;                                        &lt;span class=&quot;token number&quot;&gt;0.0s&lt;/span&gt;
 &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; naming to docker&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;io&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;mortimerxyz&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;linear&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1.0&lt;/span&gt;                        &lt;span class=&quot;token number&quot;&gt;0.0s&lt;/span&gt;

Use &lt;span class=&quot;token string&quot;&gt;&apos;docker scan&apos;&lt;/span&gt; to run Snyk tests against images to find vulnerabilities and learn how to fix them&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;Test-running-it&quot; style=&quot;position:relative;&quot;&gt;Test running it&lt;a href=&quot;#Test-running-it&quot; aria-label=&quot;Test running it permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Once it is built we need to test that it runs.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token function&quot;&gt;docker&lt;/span&gt; run &lt;span class=&quot;token parameter variable&quot;&gt;-p&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;80&lt;/span&gt;:80 mortimerxyz/linear:1.0&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;You should expect to see something like this in the terminal if you are successful.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;log&quot;&gt;&lt;pre class=&quot;language-log&quot;&gt;&lt;code class=&quot;language-log&quot;&gt;&lt;span class=&quot;token level info keyword&quot;&gt;INFO&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;     Started server process &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1324&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;token level info keyword&quot;&gt;INFO&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;     Waiting for application startup&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;
&lt;span class=&quot;token level info keyword&quot;&gt;INFO&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;     Application startup complete&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;
&lt;span class=&quot;token level info keyword&quot;&gt;INFO&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;     Uvicorn running on &lt;span class=&quot;token url&quot;&gt;http://0.0.0.0:80&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;(&lt;/span&gt;Press CTRL&lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt;C to quit&lt;span class=&quot;token operator&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token level info keyword&quot;&gt;INFO&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;     &lt;span class=&quot;token ip-address constant&quot;&gt;192.168.0.19&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;23413&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;GET /weight/150 HTTP/1.1&quot;&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;200&lt;/span&gt; OK&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;You should be able to call the service from your browser or programmatically now (on port 80).&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 438px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/5bbf334ccf8ff61dbce44a7c97bc6f1a/a3b87/mdlapi-height-weight-api.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 31.27853881278539%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAGCAYAAADDl76dAAAACXBIWXMAABJ0AAASdAHeZh94AAABCUlEQVQY032Q6W6DMBCEef8H7KmUkGADCb4wmHAkX+Wt0n/tSiOvtN6Z2SlCiLy8vjFNE957QghYa+h7g7UOYy3OeeZ5JteyLEwpoZsL759fNBeDbq8/6HqKZVmJMbLvO+u6CvJSxny7CdGyrjLPdb/fpfchcuktfhhxfsD6AeMCRUqzKOZP27aJm5SSvNu2o3RDrTTlseLj80BVVRzKirI8cq5rjtWJ6qw4nWucDxSZ5Kn+eDzEWXbxPFEEp8Q45kiCCNkM63DOcc3ROI8PgXFKFHkxD4wxDENkHKPkaIylbVqapqVtG7qu+xX5r4QwkymlGWKUPEX52qO1pq4VStcopWT+vOQvfAORB8wYwFAQxwAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;mdlapi height weight api&quot;
        title=&quot;mdlapi height weight api&quot;
        src=&quot;/static/5bbf334ccf8ff61dbce44a7c97bc6f1a/a3b87/mdlapi-height-weight-api.png&quot;
        srcset=&quot;/static/5bbf334ccf8ff61dbce44a7c97bc6f1a/a3b87/mdlapi-height-weight-api.png 438w&quot;
        sizes=&quot;(max-width: 438px) 100vw, 438px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;h3 id=&quot;Publish-it&quot; style=&quot;position:relative;&quot;&gt;Publish it&lt;a href=&quot;#Publish-it&quot; aria-label=&quot;Publish it permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;To follow this process, you don’t need this step, but if you do, you will want to sign up for Docker Hub.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Sign up to Docker Hub&lt;/li&gt;
&lt;li&gt;Create an access token&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 1294px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/a66f858bc389b847a3bc472eb2ea2abc/80132/docker-access-token.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 24%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAFCAIAAADKYVtkAAAACXBIWXMAAAsSAAALEgHS3X78AAAAnklEQVQY002QwQ4CMQhE9/+/SE8b49XLGj/CeNCLyjLDUEOrG1+aBqYwkE7PVyEpM7d7kH/0VKAOZ+1OsT/F9R6Tu6/uEdE6UrYf0ZPR+VWiHS8xLzkveXtoikK2+hjkwObSTUWGg61c6rSG1rIXaOpvbmYAM9Nsfb+NrBjdiICZDbFaGcpaKlSTBRIOkhWDAPs6AVCqyShKHOn4EVIf0XgkwToOgvcAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;docker access token&quot;
        title=&quot;docker access token&quot;
        src=&quot;/static/a66f858bc389b847a3bc472eb2ea2abc/80132/docker-access-token.png&quot;
        srcset=&quot;/static/a66f858bc389b847a3bc472eb2ea2abc/0eb09/docker-access-token.png 500w,
/static/a66f858bc389b847a3bc472eb2ea2abc/1263b/docker-access-token.png 1000w,
/static/a66f858bc389b847a3bc472eb2ea2abc/80132/docker-access-token.png 1294w&quot;
        sizes=&quot;(max-width: 1294px) 100vw, 1294px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;powershell&quot;&gt;&lt;pre class=&quot;language-powershell&quot;&gt;&lt;code class=&quot;language-powershell&quot;&gt;docker login &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;u mortimerxyz&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;powershell&quot;&gt;&lt;pre class=&quot;language-powershell&quot;&gt;&lt;code class=&quot;language-powershell&quot;&gt;docker push mortimerxyz/linear:1&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;0&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;log&quot;&gt;&lt;pre class=&quot;language-log&quot;&gt;&lt;code class=&quot;language-log&quot;&gt;The push refers to repository &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;docker&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;io&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;mortimerxyz&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;linear&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;token number&quot;&gt;849958a0eb7c&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; Preparing
&lt;span class=&quot;token property&quot;&gt;a7b3e42a4b41:&lt;/span&gt; Pushed
&lt;span class=&quot;token property&quot;&gt;c00d727af360:&lt;/span&gt; Pushed
&lt;span class=&quot;token number&quot;&gt;27a391641a6d&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; Pushed
&lt;span class=&quot;token number&quot;&gt;6bf06ec166f0&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; Pushed
&lt;span class=&quot;token property&quot;&gt;b2809f3ef892:&lt;/span&gt; Pushed
&lt;span class=&quot;token number&quot;&gt;1a31622a1cf9&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; Pushed
&lt;span class=&quot;token property&quot;&gt;b588d5e23fdf:&lt;/span&gt; Pushed
&lt;span class=&quot;token property&quot;&gt;f22fec08a08a:&lt;/span&gt; Pushed
&lt;span class=&quot;token number&quot;&gt;69c9ca396f64&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; Pushed
&lt;span class=&quot;token property&quot;&gt;b55de1dfa670:&lt;/span&gt; Pushed
&lt;span class=&quot;token number&quot;&gt;91896eadc954&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; Pushed
&lt;span class=&quot;token number&quot;&gt;8b5e3ef66306&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; Pushed
&lt;span class=&quot;token number&quot;&gt;63a0eaed8845&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; Pushed
&lt;span class=&quot;token property&quot;&gt;e673d0986f90:&lt;/span&gt; Pushed
&lt;span class=&quot;token number&quot;&gt;5e610cf6f254&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; Skipped foreign layer
&lt;span class=&quot;token property&quot;&gt;d6fdd6832d95:&lt;/span&gt; Skipped foreign layer
&lt;span class=&quot;token number&quot;&gt;1.0&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; digest&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; sha256&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token property&quot;&gt;size:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;4232&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 906px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/1888088d42a0e42b4c14edc8c6b983c8/8cb5d/docker-published-linux.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 36.6%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAHCAIAAACHqfpvAAAACXBIWXMAAAsSAAALEgHS3X78AAAAtklEQVQY032P227DMAxD8/9f2iZ9aWbdLJManKTD2gEjDgxBECV6ASBqOtEB1CHyDTOfeER093B3EQGwALiv233d1u3RmkT0iKh3tdZE5gl3NzM1u617TywkzcLce+9mNodUX/c5xmB9irx6S1X1zJ/AYwy86swUVfBTc7/5ZbZ0P6KS7JmZ4xw6O8cunrjZ/rU/96dIm2aAYoW/4X4pRzWtphQ9iwlQ8885iv+awcqsntd7QtY3B/ScknNQKI8AAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;docker published linux&quot;
        title=&quot;docker published linux&quot;
        src=&quot;/static/1888088d42a0e42b4c14edc8c6b983c8/8cb5d/docker-published-linux.png&quot;
        srcset=&quot;/static/1888088d42a0e42b4c14edc8c6b983c8/0eb09/docker-published-linux.png 500w,
/static/1888088d42a0e42b4c14edc8c6b983c8/8cb5d/docker-published-linux.png 906w&quot;
        sizes=&quot;(max-width: 906px) 100vw, 906px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;h3 id=&quot;Test-it-on-another-platform&quot; style=&quot;position:relative;&quot;&gt;Test it on another platform&lt;a href=&quot;#Test-it-on-another-platform&quot; aria-label=&quot;Test it on another platform permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Now we see the benefit of all of this (hopefully). To get the whole thing running on another platform (another local machine or even a cloud VM) we can just to the following:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token function&quot;&gt;docker&lt;/span&gt; pull mortimerxyz/liner:1.0
&lt;span class=&quot;token comment&quot;&gt;# run with the IMAGE ID or the REPOSITORY:TAG&lt;/span&gt;
&lt;span class=&quot;token function&quot;&gt;docker&lt;/span&gt; run &lt;span class=&quot;token parameter variable&quot;&gt;-p&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;80&lt;/span&gt;:80 mortimerxyz/liner:1.0&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 1045px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/75c83180186b2e7031833d24f0735a48/3fbc2/docker-run-chromeos.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 86.4%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAARCAYAAADdRIy+AAAACXBIWXMAAAsTAAALEwEAmpwYAAAC4UlEQVQ4y41T2W6bUBDlA5puUm3AG7uJwcaA8QZ4xZDNztItbdNKeemXho+JK7nJO+FU96ZNmqpK+3A0d4B75pyZgVlfrm8Ivq2/0XhJ8vX6ZrPZ3FxfX/8Xrq6ubjab7/QOs7c6QrK3RLy7RLK/ouf5Yh+z+QHi5AiL+PCfIN/NoyVOP5yDUY1GXhLEnK+JeU2V84ok5QW+lm895fOnz/j82fNy/vzF43jxspI/2eJyXXdzpu0O0fMXcLoT9IIIgzCB053C6URwOnNIsoVSWUO5oj8KllPguCGYYHyCKDlDMDpGp7uDgb/EePYWDXMISWmjXNkGx6vgS9qjKBQltG0fzN7yHKuTrxjNXsPrJQgnJ0j2v0BWbLwqCHdk/yK9I5zFp4iSj/D6O9TmIFhiEr2j6oiN/yF7QDiavsY0eo+BfwDbnaM72KUqJfme8G8gRX7hAeE8/kAthpNjuN0YwfgI8e5nqrBQFClpkZXBcvLPqPxVMSG0nQBMtPMJfngIopSAFJgtTqlK0gLXW9B2kNjpJeh0Yzowo+mjaY1gtgIYzYAWaRgeGMueQG/00bLHlIDkTSukkQxGkFpQVAeiZFHUBJMq+lMpybW6DaY/mqAXzmB1Bmh5XWimCVlrUguC2ES1ZlCrpYoOUWqB+3m5KhioVO9X6o5wGKwwDA/RdqZUPlGhaC7tGbFI+qrVPdS3e+j7+7f7KVvUBVHOsvJDQtNxYDoe9GYbhm3D8nrQttsQpTZVRIjvd1FBqVynf8bvE35AOI1Os7rezVxvkYXj42wYrrJhsMrGszfZwD/Ito1BVqroWZGVMpaTKciZ45WM41UKvqRlLKdkWt3OmHj3DJVqg9obz96g7x/QOAxXGE1O6OTJfjbbIwqzFVKQFpBd/b2Hdd0G44eHF8WimNYEI60JZipIrVRW7LRaM1JRtlK13kkVzU1VzU0V1bl9prn0PV/SUpZTUo5X0kJRSlXNuvgBRPcszIUEIVQAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;docker run chromeos&quot;
        title=&quot;docker run chromeos&quot;
        src=&quot;/static/75c83180186b2e7031833d24f0735a48/3fbc2/docker-run-chromeos.png&quot;
        srcset=&quot;/static/75c83180186b2e7031833d24f0735a48/0eb09/docker-run-chromeos.png 500w,
/static/75c83180186b2e7031833d24f0735a48/1263b/docker-run-chromeos.png 1000w,
/static/75c83180186b2e7031833d24f0735a48/3fbc2/docker-run-chromeos.png 1045w&quot;
        sizes=&quot;(max-width: 1045px) 100vw, 1045px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;Success and testing calling it is all good!&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token function&quot;&gt;curl&lt;/span&gt; localhost:80/weight/200&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;json&quot;&gt;&lt;pre class=&quot;language-json&quot;&gt;&lt;code class=&quot;language-json&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token property&quot;&gt;&quot;weight&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;90.275&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;</content:encoded></item><item><title><![CDATA[Databricks sample NFL MLFlow]]></title><description><![CDATA[Fit a decision tree model for predicting win or loss The code this post is relating to is here: https://github.com/mortie23/databricks-nfl…]]></description><link>https://mortimer.xyz/databrick-sample-nfl-mlflow/</link><guid isPermaLink="false">https://mortimer.xyz/databrick-sample-nfl-mlflow/</guid><pubDate>Wed, 03 Aug 2022 12:00:00 GMT</pubDate><content:encoded>&lt;h1 id=&quot;Fit-a-decision-tree-model-for-predicting-win-or-loss&quot; style=&quot;position:relative;&quot;&gt;Fit a decision tree model for predicting win or loss&lt;a href=&quot;#Fit-a-decision-tree-model-for-predicting-win-or-loss&quot; aria-label=&quot;Fit a decision tree model for predicting win or loss permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h1&gt;
&lt;p&gt;The code this post is relating to is here: &lt;a href=&quot;https://github.com/mortie23/databricks-nfl-data&quot;&gt;https://github.com/mortie23/databricks-nfl-data&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;This is using:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Azure Databricks&lt;/li&gt;
&lt;li&gt;Azure Data Lake Storage (ADLS) Gen2&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;We are again looking at the NFL season for 2014.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 946px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/31ed407e460cf22df989ed896d70efca/b2dbf/nfl-playoffs-2014.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 51.4%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAKCAYAAAC0VX7mAAAACXBIWXMAAA7DAAAOwwHHb6hkAAACBElEQVQoz3WS22sTYRDF94/2RfBJEAShT30ShbZYa0GoKNrUSGjoVTeXdrObpLlvc9tkd7OXJJt0bz/Jamkt9sB5mflm5ps5R+jtp7gulNGUBka1jtPpMGk20fpjJpqJpZmYmkkc8w/ih4G/ELobmzSzItaFjF0p09nZQXr6jFb2GOPwEPv4hOb3NI5lEUYRnufh+/6jTYUIWKWXMdymA+AmCHHniyS2iCLk9XWkrS3q6TT9TAaz3U7eRlGUNL6lUH75iuJeBuM8hymKqKkUtXfbtIplLKXGstFgKJXQSyUGikK3WkVvNDB1/f8re3KJQeWKUbXCdDhgms+jbn+gVZAZ5fJY0iWGLBM+KDS7XQxJYtDpYfVHjDsD3ImDUB9OUA2PnuUz8YJk9VWxPZ1j2C6L4O5eq/VWXCF39JPNtbecnikUck2KYoVBb4Tw4vUXvol9iuqMQnPCxt4hT9Y+klVMTmsOYtMhe6HeCREGCS3XxYthuoywvIBFGLMaJSRSxBFhuPpXzHS+oK9Psec+hj3D88F2Z+x+PeKHeIXU0hGrA9rd4a2B7psJYTd1zomscdl2yNV0pCuVYqVFR3O41j3MWcB44vI5I5I+V5BaYwq1YTIsUfmewonK7/d/cVoxqI+WtMZzPmVyPH9zQKHtcqFOybcdzpQ+cfzndv7NAqLwUR/+BoxC5TbudzDYAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;nfl playoffs 2014&quot;
        title=&quot;nfl playoffs 2014&quot;
        src=&quot;/static/31ed407e460cf22df989ed896d70efca/b2dbf/nfl-playoffs-2014.png&quot;
        srcset=&quot;/static/31ed407e460cf22df989ed896d70efca/0eb09/nfl-playoffs-2014.png 500w,
/static/31ed407e460cf22df989ed896d70efca/b2dbf/nfl-playoffs-2014.png 946w&quot;
        sizes=&quot;(max-width: 946px) 100vw, 946px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;In the previous blog post we looked at how to take a series of CSV files and load them into Databricks as delta tables backed by parquet files.&lt;/p&gt;
&lt;p&gt;This time we will demonstrate using Spark and MLFlow train a model and host an model serving REST end point.&lt;/p&gt;
&lt;h2 id=&quot;Visualizing-data&quot; style=&quot;position:relative;&quot;&gt;Visualizing data&lt;a href=&quot;#Visualizing-data&quot; aria-label=&quot;Visualizing data permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Before we try to fit a model it is always a good idea to look at the distribution of the features. This example shows the distribution of the total yards.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 718px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/59c11b9401a8e657e6e139c869641599/ab030/mlflow-histogram-total-yards.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 93.39999999999999%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAATCAIAAAAf7rriAAAACXBIWXMAAAsSAAALEgHS3X78AAABT0lEQVQ4y53SiWrDMAwG4Lz/U47YSdMsWST5knxlpC2la7PsEMYIzId+gRtmXpbF/1SW7DROSit9qbZtT6dTIyLW2nW36upnnyVvbakppvBQ1tpmXVdm/g6bwSSfdh9FpHHOEdF32J5t5nztd3DO2Xt/gP3sSyr7k49j27PFFu1oE6cdzMwAcICpI2iBkfcnhxCOsCZUKCT/iX2ED2KnkMxgqPv75For9USabtjIb3euuTLwhrsL1ug/fMnlGYvIa+wSC2q8yusBBQHCr2KXVO5j78eczGP4DddaX3/YLkaFfvL3r7rhGCMifpG5hCU8ydvm81f8GjuFhApNb25pL812a3rGtVbnXIopSiylMDEowA5BAWhAvTXXGxRQT2Y0bHlL50NTSnHOwQdIkHEY53EONqg35cgt09KrPobYtd0yLY6celNi5X14Pw/nFNMnqdtTF38m428AAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;mlflow histogram total yards&quot;
        title=&quot;mlflow histogram total yards&quot;
        src=&quot;/static/59c11b9401a8e657e6e139c869641599/ab030/mlflow-histogram-total-yards.png&quot;
        srcset=&quot;/static/59c11b9401a8e657e6e139c869641599/0eb09/mlflow-histogram-total-yards.png 500w,
/static/59c11b9401a8e657e6e139c869641599/ab030/mlflow-histogram-total-yards.png 718w&quot;
        sizes=&quot;(max-width: 718px) 100vw, 718px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;h2 id=&quot;Using-MLFlow-to-register-model&quot; style=&quot;position:relative;&quot;&gt;Using MLFlow to register model&lt;a href=&quot;#Using-MLFlow-to-register-model&quot; aria-label=&quot;Using MLFlow to register model permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;We will use MLFlow to track our “experiments”. Trained models and any other “artifacts” that you choose to log can be tracked using MLFlow. In this example we will track the RMSE and the trained model.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;py&quot;&gt;&lt;pre class=&quot;language-py&quot;&gt;&lt;code class=&quot;language-py&quot;&gt;mlflow&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;sklearn&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;autolog&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;trainModel&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token triple-quoted-string string&quot;&gt;&apos;&apos;&apos;
    Train a Descision tree model
    Return:
      run_id: an UUID representing the MLFLow model run
  &apos;&apos;&apos;&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;with&lt;/span&gt; mlflow&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;start_run&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;# The input features&lt;/span&gt;
    inputCols &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;TOTAL_FIRST_DOWNS&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;PASSING_FIRST_DOWNS&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;YARDS_PER_PLAY&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;FUMBLES&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;TOTAL_YARDS&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;INTERCEPTIONS&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;PUNTS&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
    va &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; VectorAssembler&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;inputCols&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;inputCols&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;outputCol&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;features&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;# Define the Decision tree regressor&lt;/span&gt;
    dt &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; DecisionTreeRegressor&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;labelCol&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;WIN_LOSS_BINARY&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; featuresCol&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;features&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; maxDepth&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    evaluator &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; RegressionEvaluator&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;metricName &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;rmse&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; labelCol&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;WIN_LOSS_BINARY&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

    grid &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; ParamGridBuilder&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;addGrid&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;dt&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;maxDepth&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;7&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;build&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    cv &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; CrossValidator&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;estimator&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;dt&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; estimatorParamMaps&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;grid&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; evaluator&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;evaluator&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; numFolds &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    pipeline &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; Pipeline&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;stages&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;va&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; dt&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

    &lt;span class=&quot;token comment&quot;&gt;# Now call fit to train the model and then use MLFlow to log the model&lt;/span&gt;
    model &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; pipeline&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;fit&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;df&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    mlflow&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;spark&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;log_model&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;spark_model&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;model&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; artifact_path&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;model&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

    &lt;span class=&quot;token comment&quot;&gt;# Log the RMSE to MLFlow&lt;/span&gt;
    predictions &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; model&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;transform&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;df&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    rmse &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; evaluator&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;evaluate&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;predictions&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;evaluator&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;metricName&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;rmse&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    mlflow&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;log_metric&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;rmse&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; rmse&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

    &lt;span class=&quot;token comment&quot;&gt;# Return the run&lt;/span&gt;
    run_id &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; mlflow&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;active_run&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;info&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;run_id
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; run_id

run_id &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; trainModel&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;# Register the model&lt;/span&gt;
model_name &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;nfl-decisiontree&quot;&lt;/span&gt;
artifact_path &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;model&quot;&lt;/span&gt;
model_uri &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string-interpolation&quot;&gt;&lt;span class=&quot;token string&quot;&gt;f&quot;runs:/&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;run_id&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;artifact_path&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;

model_details &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; mlflow&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;register_model&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;model_uri&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;model_uri&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; name&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;model_name&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2 id=&quot;MLFlow-user-interface&quot; style=&quot;position:relative;&quot;&gt;MLFlow user interface&lt;a href=&quot;#MLFlow-user-interface&quot; aria-label=&quot;MLFlow user interface permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;When this code has run, you will be able to see the experiment and the associated runs.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 792px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/5528b81961871fc4e480328d0690b880/59f54/mlflow-experiment.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 40.400000000000006%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAICAIAAAB2/0i6AAAACXBIWXMAAAsSAAALEgHS3X78AAABLUlEQVQY05WQTU7DMBCFcysO0CUqJ+EO7BASUrkD12CB+JP4WbCoKAgVSiFpkiZN7MT22J6xjZKWBaIbPj2N3ubpjV6Up0kafy4Xcfo1L/I0/Ido/J5djecPr+ntc3L/sniK2SThk4QLjeScIbJI5Nz28P7p4+DoYji62ju5GY6ud48vB4fnOwdnd9Oy5O0sqz7ymisdQvB/w/FKTrNmVsi3rJn2WhsJlpwj5y1S372FKASvZLsqltYAoSFruosGlOKMLfNcSoUWzW9sT4RITSuKspRKaW0AtOoFAEKIqqq01s45IkRE+gERu7D3HsmBddogaGsseb/5al0CAFJKpdTGSCmktNZ677uwRWLCtMpIMGAoBO98t07fXHPe1DXjvBFCsJo1TcsYB91N+A16hMOSqzc+UwAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;mlflow experiment&quot;
        title=&quot;mlflow experiment&quot;
        src=&quot;/static/5528b81961871fc4e480328d0690b880/59f54/mlflow-experiment.png&quot;
        srcset=&quot;/static/5528b81961871fc4e480328d0690b880/0eb09/mlflow-experiment.png 500w,
/static/5528b81961871fc4e480328d0690b880/59f54/mlflow-experiment.png 792w&quot;
        sizes=&quot;(max-width: 792px) 100vw, 792px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 1483px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/d1ce3dbf269c5a28fa679a106b042e4f/dabbe/mlflow-experiment-list.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 13.600000000000001%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAADCAIAAAAcOLh5AAAACXBIWXMAAAsSAAALEgHS3X78AAAAgklEQVQI1yXHSxKEIAwFQO9/y3EnSIB8XgKizsJNV/UmcDZYDPXBCjbvCoYrQj3Y8CkINv+qHgI/Ttpyx5652F1tprM01kytq1FnhadSBX5S64LSOFO3GOZDzFOhrcfaa/zKJJ0Ov5/3ed7rWillA6gr2xSfVYMxjxY+1v2881qi9gc/xquHgLhU6QAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;mlflow experiment list&quot;
        title=&quot;mlflow experiment list&quot;
        src=&quot;/static/d1ce3dbf269c5a28fa679a106b042e4f/dabbe/mlflow-experiment-list.png&quot;
        srcset=&quot;/static/d1ce3dbf269c5a28fa679a106b042e4f/0eb09/mlflow-experiment-list.png 500w,
/static/d1ce3dbf269c5a28fa679a106b042e4f/1263b/mlflow-experiment-list.png 1000w,
/static/d1ce3dbf269c5a28fa679a106b042e4f/dabbe/mlflow-experiment-list.png 1483w&quot;
        sizes=&quot;(max-width: 1483px) 100vw, 1483px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;h3 id=&quot;Adding-metadata&quot; style=&quot;position:relative;&quot;&gt;Adding metadata&lt;a href=&quot;#Adding-metadata&quot; aria-label=&quot;Adding metadata permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Once we click on the model we can see the RSME we logged and any other artifacts related to the trained model.
&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 1543px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/0883301852ece59a83cfd1587377f1af/aafd9/mlflow-experiment-logged-model.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 88.99999999999999%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAASCAIAAADUsmlHAAAACXBIWXMAAAsSAAALEgHS3X78AAABpElEQVQ4y52T627bMAyF/f6vNmD/Bgwr0G5xnMbyRVdSJHXJIBvJmqzA2n2wCInCgQ8hsjufx74/Hvr+OJyGtk7HYeiPAyJe/kXHzERUSnm4qLXmN8nCnAEyYs1/kh0RhRDchveeiFJKgI1IVHd9SnA+e6WcWmhe600MAbTWAIGZa215EdkykHO+/USICCNFCSiSyp3tXOqd4Y2/a3msORIBgKRaL5+mQ4AQQv0f7aXDiIS4u2TmcuVDYv06ktbJaNLae5+vfEhMxmbAAlA+b76Tkj9o8h1x//TcPz3r07gMozXNtjSS3OAt8LZLKeXM7Zhyzl3//ce3L1/Vy2H+2ZvVOOe01sYY730IwXu/955zzloLretoNXbRDhA7RDgeful1SUlSTvOyTNOkJiUi9R3aQ2yhFdtRjMZa5z1vOO9m7RcbYqS4gXufb5t4BRGZuUtXtsJYaz3Ni7F+f21AXLVZ1lVE7meudXEnb2Bm48A4v48Ui0Bw4+tpVLOxBiO2ftoiIEhKj2JADKHNU/C+lByCV0oZa7Vdp1VNWqm1ffM6pSS/Af3mGrgRbwpHAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;mlflow experiment logged model&quot;
        title=&quot;mlflow experiment logged model&quot;
        src=&quot;/static/0883301852ece59a83cfd1587377f1af/aafd9/mlflow-experiment-logged-model.png&quot;
        srcset=&quot;/static/0883301852ece59a83cfd1587377f1af/0eb09/mlflow-experiment-logged-model.png 500w,
/static/0883301852ece59a83cfd1587377f1af/1263b/mlflow-experiment-logged-model.png 1000w,
/static/0883301852ece59a83cfd1587377f1af/aafd9/mlflow-experiment-logged-model.png 1543w&quot;
        sizes=&quot;(max-width: 1543px) 100vw, 1543px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;h3 id=&quot;Model-lifecycle&quot; style=&quot;position:relative;&quot;&gt;Model lifecycle&lt;a href=&quot;#Model-lifecycle&quot; aria-label=&quot;Model lifecycle permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;MLFlow helps you to track the models and promote them through environments.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 1597px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/4f10564c7309e9ade35c4ccec6a761e7/23b09/mlflow-experiment-production.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 72.39999999999999%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAOCAIAAACgpqunAAAACXBIWXMAAAsSAAALEgHS3X78AAABPUlEQVQoz52Ry04cQQxF+/9/hKzZkygLBGIRdkAEYpPHRFHm0dXtsn39aDTVMwgIIwFHXrXKbZ/rbrlcrlZrwFQ1IqZpiggAUFERAFMDMAhU+Pzy/ujk+6cvt18v7rvKTLUSjeMwQLdPzYyI+oHHKsQqFqhMP3+dfju+vTqbWHPa0Vmb6R4Rmbn9npkRoRYyFwLmoVqGpQqFh7Vyj05ViUjU8vGHb6artZYyEI3epouImcUed5+zmMnnE7bNqpqZ7m5mfV+k5QTA3QGMI5mZN142v7pPZuYbNDp5co93OzMzEe2UWs2qAOI/Xq6tjVlYFAozM7QTKNT2vO78VA8esGe2uT/7gbWFN5uNMG+PNCesgjL0qz8/FtdgjsjDgbkNpWzW61IKEbFIHQr//bf4fXd585n7tZkfbPaYPswDdws2QXZlFIUAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;mlflow experiment production&quot;
        title=&quot;mlflow experiment production&quot;
        src=&quot;/static/4f10564c7309e9ade35c4ccec6a761e7/23b09/mlflow-experiment-production.png&quot;
        srcset=&quot;/static/4f10564c7309e9ade35c4ccec6a761e7/0eb09/mlflow-experiment-production.png 500w,
/static/4f10564c7309e9ade35c4ccec6a761e7/1263b/mlflow-experiment-production.png 1000w,
/static/4f10564c7309e9ade35c4ccec6a761e7/23b09/mlflow-experiment-production.png 1597w&quot;
        sizes=&quot;(max-width: 1597px) 100vw, 1597px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;h2 id=&quot;Checking-model-outputs&quot; style=&quot;position:relative;&quot;&gt;Checking model outputs&lt;a href=&quot;#Checking-model-outputs&quot; aria-label=&quot;Checking model outputs permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Vizualising the tree.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 1529px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/c4c898f58668964a122b74882c249ce4/ce240/mlflow-tree-diagram.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 52%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAKCAIAAAA7N+mxAAAACXBIWXMAAAsSAAALEgHS3X78AAAA90lEQVQoz4WR2W4DIQxF5///M40UqUrAOxi7gkknW6WeN/A9vmhmQ8Sv8/l0Ol0uFwDQ/2BmXBDRNsboC3cfY8QiM6x1QMp4cEwPtnzlGHQf3V/S+cGU92u1Rsy7/rYuM7s7IP8hZ6Z7N2vWJp8N7t5ab93N9Hn1lEmUiGOMlRtvDUCiqmNN97CZPWREMr2fzRoSu/vRSSz99zkRwaIisvdvEUmsBTAzWQ0RIwKWPyKupUYMYtW1vQC2Zu6jIt2bZ9s6EPM9VGH+wvmR5j0uOzMrQEa03vGQ1VqpwMyl1lJBRFfaKiASi2oFFFEW/b7ekAiQbqWyyA/3I0tEwwM6rAAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;mlflow tree diagram&quot;
        title=&quot;mlflow tree diagram&quot;
        src=&quot;/static/c4c898f58668964a122b74882c249ce4/ce240/mlflow-tree-diagram.png&quot;
        srcset=&quot;/static/c4c898f58668964a122b74882c249ce4/0eb09/mlflow-tree-diagram.png 500w,
/static/c4c898f58668964a122b74882c249ce4/1263b/mlflow-tree-diagram.png 1000w,
/static/c4c898f58668964a122b74882c249ce4/ce240/mlflow-tree-diagram.png 1529w&quot;
        sizes=&quot;(max-width: 1529px) 100vw, 1529px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;h2 id=&quot;Feature-importance&quot; style=&quot;position:relative;&quot;&gt;Feature importance&lt;a href=&quot;#Feature-importance&quot; aria-label=&quot;Feature importance permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Feature importance is a measure of information gain. It is scaled from 0.
Let’s map the features to their proper names to make them easier to read.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Feature&lt;/th&gt;
&lt;th&gt;Importance&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;INTERCEPTIONS&lt;/td&gt;
&lt;td&gt;0.392&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;TOTAL_YARDS&lt;/td&gt;
&lt;td&gt;0.289&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;FUMBLES&lt;/td&gt;
&lt;td&gt;0.131&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;YARDS_PER_PLAY&lt;/td&gt;
&lt;td&gt;0.085&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;PUNTS&lt;/td&gt;
&lt;td&gt;0.035&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;TOTAL_FIRST_DOWNS&lt;/td&gt;
&lt;td&gt;0.034&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;PASSING_FIRST_DOWNS&lt;/td&gt;
&lt;td&gt;0.030&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 id=&quot;Model-serving&quot; style=&quot;position:relative;&quot;&gt;Model serving&lt;a href=&quot;#Model-serving&quot; aria-label=&quot;Model serving permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 1570px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/dc2124c56f63243bb799bc2389ebd6f6/958fa/mlflow-model-serving.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 74%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAPCAIAAABr+ngCAAAACXBIWXMAAAsSAAALEgHS3X78AAABR0lEQVQoz42SzU7DMAyA+/7vwA3xENw4ABckkHZHoAqxsS5N09hx/JOgdjDGtGn7lEvifI6dpOn7vuu6jXMRIjPX07CIMi1af3X3dvPYXt+3zTjG9dr5IdRzqFkxXfr09Ope3v3zm29E2UMEklprKaXWamaUGUmRBEgw0al0zWfnVxufiAAxswAmVS2l2G6Ylf9MFcyjyTmrWqkl5QSAy9UXs9QLKKU0zvW+78MYluvVGELvfQhjjBExAUDCHwAQMbHaPxkAxpkQQqacM8cIIrKt8MzJtf7tENGPLx9xypFS2q0TEQCY2aG8n15Vh2EgOrxeBPDeExGLnJS3jzmOU88xxm3oVPFHZM7snJv6z3kXOuofkUVk020AYH/9UtlUVNhUbWb+MHaRrFYCMpDojP1yVDaz5vdWpvmAfPPQ3i5WwjkRnf1h39VvcOSoMynjAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;mlflow model serving&quot;
        title=&quot;mlflow model serving&quot;
        src=&quot;/static/dc2124c56f63243bb799bc2389ebd6f6/958fa/mlflow-model-serving.png&quot;
        srcset=&quot;/static/dc2124c56f63243bb799bc2389ebd6f6/0eb09/mlflow-model-serving.png 500w,
/static/dc2124c56f63243bb799bc2389ebd6f6/1263b/mlflow-model-serving.png 1000w,
/static/dc2124c56f63243bb799bc2389ebd6f6/958fa/mlflow-model-serving.png 1570w&quot;
        sizes=&quot;(max-width: 1570px) 100vw, 1570px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;h3 id=&quot;Testing-the-inference-REST-API&quot; style=&quot;position:relative;&quot;&gt;Testing the inference REST API&lt;a href=&quot;#Testing-the-inference-REST-API&quot; aria-label=&quot;Testing the inference REST API permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;json&quot;&gt;&lt;pre class=&quot;language-json&quot;&gt;&lt;code class=&quot;language-json&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;&quot;columns&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;
    &lt;span class=&quot;token string&quot;&gt;&quot;TOTAL_FIRST_DOWNS&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token string&quot;&gt;&quot;PASSING_FIRST_DOWNS&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token string&quot;&gt;&quot;YARDS_PER_PLAY&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token string&quot;&gt;&quot;FUMBLES&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token string&quot;&gt;&quot;TOTAL_YARDS&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token string&quot;&gt;&quot;INTERCEPTIONS&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token string&quot;&gt;&quot;PUNTS&quot;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;&quot;data&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;28&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;15&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;7.6&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;495&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;Example-of-calling&quot; style=&quot;position:relative;&quot;&gt;Example of calling&lt;a href=&quot;#Example-of-calling&quot; aria-label=&quot;Example of calling permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Databricks provides some light weight examples of how to call it with CURL or Python. The example doesn’t provide how to call with passing data.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;py&quot;&gt;&lt;pre class=&quot;language-py&quot;&gt;&lt;code class=&quot;language-py&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; os
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; requests
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; numpy &lt;span class=&quot;token keyword&quot;&gt;as&lt;/span&gt; np
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; pandas &lt;span class=&quot;token keyword&quot;&gt;as&lt;/span&gt; pd

&lt;span class=&quot;token keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;create_tf_serving_json&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;data&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;inputs&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;name&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; data&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;name&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;tolist&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; name &lt;span class=&quot;token keyword&quot;&gt;in&lt;/span&gt; data&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;keys&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;isinstance&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;data&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;dict&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt; data&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;tolist&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;score_model&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;dataset&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  url &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;https://&amp;lt;workspace&gt;.azuredatabricks.net/model/nfl-decisiontree/1/invocations&apos;&lt;/span&gt;
  headers &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;Authorization&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string-interpolation&quot;&gt;&lt;span class=&quot;token string&quot;&gt;f&apos;Bearer &lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;os&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;environ&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;get&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;DATABRICKS_TOKEN&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
  data_json &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; dataset&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;to_dict&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;orient&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;split&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;isinstance&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;dataset&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; pd&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;DataFrame&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt; create_tf_serving_json&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;dataset&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  response &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; requests&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;request&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;method&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;POST&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; headers&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;headers&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; url&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;url&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; json&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;data_json&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; response&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;status_code &lt;span class=&quot;token operator&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;200&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;raise&lt;/span&gt; Exception&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-interpolation&quot;&gt;&lt;span class=&quot;token string&quot;&gt;f&apos;Request failed with status &lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;response&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;status_code&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;, &lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;response&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;text&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; response&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;json&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;</content:encoded></item><item><title><![CDATA[Databricks sample NFL dataset]]></title><description><![CDATA[Create a Databricks database with sample NFL data The code this post is relating to is here: https://github.com/mortie23/databricks-nfl-data…]]></description><link>https://mortimer.xyz/databrick-sample-nfl-data/</link><guid isPermaLink="false">https://mortimer.xyz/databrick-sample-nfl-data/</guid><pubDate>Tue, 02 Aug 2022 12:00:00 GMT</pubDate><content:encoded>&lt;h2 id=&quot;Create-a-Databricks-database-with-sample-NFL-data&quot; style=&quot;position:relative;&quot;&gt;Create a Databricks database with sample NFL data&lt;a href=&quot;#Create-a-Databricks-database-with-sample-NFL-data&quot; aria-label=&quot;Create a Databricks database with sample NFL data permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The code this post is relating to is here: &lt;a href=&quot;https://github.com/mortie23/databricks-nfl-data&quot;&gt;https://github.com/mortie23/databricks-nfl-data&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;This is using:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Azure Databricks&lt;/li&gt;
&lt;li&gt;Azure Data Lake Storage (ADLS) Gen2&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;Create-your-storage-account-and-container&quot; style=&quot;position:relative;&quot;&gt;Create your storage account and container&lt;a href=&quot;#Create-your-storage-account-and-container&quot; aria-label=&quot;Create your storage account and container permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;In this demo I have created a storage account called &lt;code class=&quot;language-text&quot;&gt;nfl&lt;/code&gt; and a container within the account called &lt;code class=&quot;language-text&quot;&gt;nfldata&lt;/code&gt;
&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 901px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/5e4c3ebdab8e716db93e82d196ac2bfb/a54a1/dbnfl-adls-containers.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 47.800000000000004%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAKCAIAAAA7N+mxAAAACXBIWXMAAAsSAAALEgHS3X78AAABGUlEQVQoz5WRy24DIQxF5///r1I3XTTpkGiUDMYPwOYxbZgkyq7t2SCEjnyvmcQ65ZJL3f7P9Pbxfvg8nM/nr+PxMCCinHNr7Xf5eAUhhBCYuQxaa733P00WFggAAKpaB6WU3lrftqgVwItwrTXnHB/UWvtgIpH9vg/ciTGqZmZhZiLicaSUSilmVh5MK5JfrzHG1zzOOURMKeWcZcDMqra3aa3tGScfJKVoZq/yPM8ism0bEdV6/wgzIyLvvareOyOgqvbeSym11v3BOcfMZrYsi5nt+4sxLstyuVxCCDnnW+eA6L0PA0QEgNba6XRCRGYOITxlVQXwzLyuawjhJiNHQiQiAHg2d84RkZkh3nM9YqOI0Fjej/wNp49F1efqJgIAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;dbnfl adls containers&quot;
        title=&quot;dbnfl adls containers&quot;
        src=&quot;/static/5e4c3ebdab8e716db93e82d196ac2bfb/a54a1/dbnfl-adls-containers.png&quot;
        srcset=&quot;/static/5e4c3ebdab8e716db93e82d196ac2bfb/0eb09/dbnfl-adls-containers.png 500w,
/static/5e4c3ebdab8e716db93e82d196ac2bfb/a54a1/dbnfl-adls-containers.png 901w&quot;
        sizes=&quot;(max-width: 901px) 100vw, 901px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;
I have added my Azure Active Directory account as a &lt;em&gt;Storage Blob Data Contributor&lt;/em&gt;.
&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 1304px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/46f0af4c81d850fbe504788403ceaf1f/8cdd0/dbnfl-adls-container-roles.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 55.199999999999996%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAALCAIAAADwazoUAAAACXBIWXMAAAsSAAALEgHS3X78AAABdElEQVQoz4WS2XLTQBBF9f+/B6kEQqUCliVbsjRb77OIshyMUzxwH+Zhppd7uqcLVOfVjxfvEi4BEhAzp5RUdfufOgdlGMf5fIKUUgwAEGPyzjHLPajt2ratts/JwBIT5FLuQbU1q1WZ0TnyPjknVpBIWJCNiUopqtpa6wJLXBYFUACTazcxS6cTDsP49n788RaOAw2jEMUYmUhEdndRVbvx5Vv/9OXw9PX48nx5/U7zBEjL+bwM4+FXf+zHaRinvifE3dqN4sNj56eJQ6gimSkDFtXaWq31ka1tmzDfsD8xJzHnfQJQs9uV5oyIhFeZWWs1oEwuoZbzGhPbmqg/L7NPnSqFEFT/ztbMAFFVWYSZc861tVKvg8y1lf3MpeZSu8Dsvbc/bW/J0a9CmE2FGRDLvov9SbPpw56pIOIjTy3l+TAdZze7NK3hXrch8GXxs4sXF+eVLkv377+ptb7+fF9DchEisFn+KG2mzsMaOCRyUSP8BrqVfMsoSohYAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;dbnfl adls container roles&quot;
        title=&quot;dbnfl adls container roles&quot;
        src=&quot;/static/46f0af4c81d850fbe504788403ceaf1f/8cdd0/dbnfl-adls-container-roles.png&quot;
        srcset=&quot;/static/46f0af4c81d850fbe504788403ceaf1f/0eb09/dbnfl-adls-container-roles.png 500w,
/static/46f0af4c81d850fbe504788403ceaf1f/1263b/dbnfl-adls-container-roles.png 1000w,
/static/46f0af4c81d850fbe504788403ceaf1f/8cdd0/dbnfl-adls-container-roles.png 1304w&quot;
        sizes=&quot;(max-width: 1304px) 100vw, 1304px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;h2 id=&quot;Load-data-to-ADLS&quot; style=&quot;position:relative;&quot;&gt;Load data to ADLS&lt;a href=&quot;#Load-data-to-ADLS&quot; aria-label=&quot;Load data to ADLS permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;I am using Azure Storage Explorer to do this. You could also use the Azure portal, the azcopy cli or many other methods.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 909px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/6f68baeecb50501aefa3436ac3106031/2f7b1/dbnfl-adls-ase-upload.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 74.4%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAPCAIAAABr+ngCAAAACXBIWXMAAAsSAAALEgHS3X78AAACWklEQVQoz3WS607bQBCF/SKo4Qfwdig4DeKBKqjUvkWFIE0lShWoSaxg7832Xuz13nwLpnICFaLqp5l/ezRnZo9X2M5oLYTgnAshiqKo6/r5//Rv8M7Ozs7PL6QsGePGmLZt61eqqn7qmpC6TzPxdUbPr9jlstzqt9333t7e3nh8Yq2TZWmtM8Y6V+1KuUrJckXkxXXy5RJ+vsTf7rmtGmUb7RrjWu/o6Oj09HTz9Gyrpm7atu12bLpO1R1MuBBFTjNntGBZLgTCaUJSjBJCUu/g4GA6nYrSAkJFXiilrLXGGGtMLksqjWB5GoHkEcBwTWOUAIxwCgCKI+gdHh5Op1NlHEQYI4QxVko55+zQ1oBYAvh4d/fr6nrxff64WOAgqJO0rZuu27zYdlW9ShIAIQAAISSlVFqXUiqEiigK5j/uZrPVzU348zZbrlySOldVVT3Y/jidPrXNDc5jnCQEo+18QghCSGlttI6jOFyFEACCMYIIIgThUN5o9GF84m8qfg8iiMmgxhhuQQgxSpXSGOMgCMIwxBiLbSTEFm80Gp34/qZt1gQuYQziGAAghHDOaa1392OMYYyzLKOUVm8YxP7E7/tna03MxW+U0TRJs0xrvXvhnKOUxnGcZVme58NHvOLt7++Px2NjDGNc5WJB+DxYoziijO0yK6VkjEEI0zTlnL8XHx8fCyEIxjFE8zW6DR7C5TJJEkKG/TnnjDEAQJqmUkr7hsH2ZDLp+76uHBPqIeQpwdqYv4s55/I8p5S+G/si9n2/6zprdCaKICI0y3Yh26G1cVvsP/wBQswNA6XNEn0AAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;dbnfl adls ase upload&quot;
        title=&quot;dbnfl adls ase upload&quot;
        src=&quot;/static/6f68baeecb50501aefa3436ac3106031/2f7b1/dbnfl-adls-ase-upload.png&quot;
        srcset=&quot;/static/6f68baeecb50501aefa3436ac3106031/0eb09/dbnfl-adls-ase-upload.png 500w,
/static/6f68baeecb50501aefa3436ac3106031/2f7b1/dbnfl-adls-ase-upload.png 909w&quot;
        sizes=&quot;(max-width: 909px) 100vw, 909px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;h2 id=&quot;Clone-this-repo-to-your-Databricks&quot; style=&quot;position:relative;&quot;&gt;Clone this repo to your Databricks&lt;a href=&quot;#Clone-this-repo-to-your-Databricks&quot; aria-label=&quot;Clone this repo to your Databricks permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 692px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/3b4b05939183c70517a4703831161edb/51c99/dbnfl-databricks-clone-repo.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 56.60000000000001%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAALCAIAAADwazoUAAAACXBIWXMAAAsSAAALEgHS3X78AAACVUlEQVQoz22QTU8TURiFZ20DraKNCspApxTboaW0dLBQOg4gVIwudKMYMUR/gwsWpH4ENUGg0BpUEiGBBDXGnYk/wAUhRklMjA3VRVd0hrlz2/m+M9fQQuLCJ29OzubkvDmEp9UzwLEX2CTHsr3xONXawnTHUiPDDBNzOp0Oh6PuX1yuuvr6/asawt3SdrrN7ya9btJ7gvQ2nGl1k97jzdTY+MTy8nK2ymKNTGYunV6Ymsqm07OTk5mZGSIY6IyFuodv3Bkdm7hy+y537ebV8XtHfaHH81lT1wEAhmniGoqCoATLZUkCFkKY54lmX5Dyh0NcKsylwgOX6MRgdOiyi6IfPs/YCPE8X6lUdF3XDUOVJFgoiPn8Xj4vFQrq7i7RHE342VRjiGnq7DnV0d3U2UNG+xxk+/R8ztC0YrGoKLJSQ1X3VZZVRRH4EoSQICPxc32Dvjjni3PeHtYT6/cnhpwU/SST0zVVEPbK+39KEEJYVVmWTdPUNM00TeLNxvut79tfNre+bv/4tfP7Z34nX/iz+W3746fPAs+LolhNAQhhpVKW5YqqahhjQ9cRQsT9B9MvVtae5l7Ovl5ZWtvIra6/Wn/7LLu0+u6DBAAvCAe1VczD8RBClmURrvZwIx31BSKBIHPWE3A2NNafJIkjxx7NLZYlqVQqQSjxvCCKIgBAFIGiqBhjvdac7IicZ5Jd7IifSVKRXm+0j471ezz0QiaHMQYiQJZl27ZdLbTtA2sYBrIQMUKHk0xy9PqtSOJiW1dvezThZ9gWKpibz2KMNVU9DP6HvwLvjtKAw4k7AAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;dbnfl databricks clone repo&quot;
        title=&quot;dbnfl databricks clone repo&quot;
        src=&quot;/static/3b4b05939183c70517a4703831161edb/51c99/dbnfl-databricks-clone-repo.png&quot;
        srcset=&quot;/static/3b4b05939183c70517a4703831161edb/0eb09/dbnfl-databricks-clone-repo.png 500w,
/static/3b4b05939183c70517a4703831161edb/51c99/dbnfl-databricks-clone-repo.png 692w&quot;
        sizes=&quot;(max-width: 692px) 100vw, 692px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;Open the notebook called &lt;code class=&quot;language-text&quot;&gt;db.sql.py&lt;/code&gt;&lt;/p&gt;
&lt;h2 id=&quot;Run-the-Notebook&quot; style=&quot;position:relative;&quot;&gt;Run the Notebook&lt;a href=&quot;#Run-the-Notebook&quot; aria-label=&quot;Run the Notebook permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;First we will create the Hive database for the tables.&lt;/li&gt;
&lt;/ol&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;sql&quot;&gt;&lt;pre class=&quot;language-sql&quot;&gt;&lt;code class=&quot;language-sql&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;create&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;database&lt;/span&gt; nfl&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;ol start=&quot;2&quot;&gt;
&lt;li&gt;The notebook has two functions.
&lt;ul&gt;
&lt;li&gt;Reading a CSV file and creating a Delta table&lt;/li&gt;
&lt;li&gt;Reading all files in an ADLS container and running the aforementioned function for each.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 785px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/09272de5f695a40bfd57a1beb5f6fd58/18baa/dbnfl-hive-nfl-tables.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 70.2%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAOCAIAAACgpqunAAAACXBIWXMAAAsSAAALEgHS3X78AAABRUlEQVQoz5WSS2/CMBCE8/9/Gxy4ceCQB0KAcex4X8HPykG0EVVL+x18sDSzo9ltuq7bbDa73W673R4OB6WUtVMpJZdcvsHMSqnL9Toa0/d9M2rd9d1wHNquvVwu4BwR5VyVeaGUkhZyzvM8OzdN04SIbds2iHg+n7XWMcT7/c7MwjLacd/tiYiZhuMRAEQEEZcfFpHH2wiLtdZVAInCgvdeZomx2rklCyKGEGKM4UmMsWHmGgPQWguIaUXOmYhqC8aMo5nn2T+5LzSn02kYBucAAJxz9okxRqSGattWKQUA0wpT7cYm/UxtSOSmtVu8U/7qP+dcY5dfEam7maZaSV6JU0ohhDfiZbFXAHTOee/XYu/928mib5qIXv7/KGatNRIBwMvk97FFxJiRmUMIKaX/FfY4ZmNMjPF/sR/2j6tYV/159h/Bjy7BUlxbWwAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;dbnfl hive nfl tables&quot;
        title=&quot;dbnfl hive nfl tables&quot;
        src=&quot;/static/09272de5f695a40bfd57a1beb5f6fd58/18baa/dbnfl-hive-nfl-tables.png&quot;
        srcset=&quot;/static/09272de5f695a40bfd57a1beb5f6fd58/0eb09/dbnfl-hive-nfl-tables.png 500w,
/static/09272de5f695a40bfd57a1beb5f6fd58/18baa/dbnfl-hive-nfl-tables.png 785w&quot;
        sizes=&quot;(max-width: 785px) 100vw, 785px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;h2 id=&quot;Test-the-results&quot; style=&quot;position:relative;&quot;&gt;Test the results&lt;a href=&quot;#Test-the-results&quot; aria-label=&quot;Test the results permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The notebook has a sample query to test the results.
This will get all players from the SF 49ers and sort them by the number rushing yards they had in each game.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;sql&quot;&gt;&lt;pre class=&quot;language-sql&quot;&gt;&lt;code class=&quot;language-sql&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;select&lt;/span&gt;
  p&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;PLAYER_NAME
  &lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; p&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;FIELD_POSITION
  &lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; t&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;TEAM_SHORT
  &lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; p&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;RUSHING_YARDS
  &lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; p&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;PASSING_YARDS
&lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt;
  nfl&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;player p
  &lt;span class=&quot;token keyword&quot;&gt;inner&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;join&lt;/span&gt; nfl&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;team_lookup
&lt;span class=&quot;token keyword&quot;&gt;on&lt;/span&gt; p&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;team_id &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; t&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;team_id
  &lt;span class=&quot;token operator&quot;&gt;and&lt;/span&gt; t&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;team_short &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;SF&apos;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;order&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;by&lt;/span&gt; p&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;RUSHING_YARDS &lt;span class=&quot;token keyword&quot;&gt;desc&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;For the 2014 NFL season, for the 49ers, Frank Gore and Colin Kaepernick were racking up the rushing yards.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Top 20&lt;/strong&gt;&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;-&lt;/th&gt;
&lt;th&gt;PLAYER_NAME&lt;/th&gt;
&lt;th&gt;FIELD_POSITION&lt;/th&gt;
&lt;th&gt;TEAM_SHORT&lt;/th&gt;
&lt;th&gt;RUSHING_YARDS&lt;/th&gt;
&lt;th&gt;PASSING_YARDS&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;Frank Gore&lt;/td&gt;
&lt;td&gt;RB&lt;/td&gt;
&lt;td&gt;SF&lt;/td&gt;
&lt;td&gt;158&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;Colin Kaepernick&lt;/td&gt;
&lt;td&gt;QB&lt;/td&gt;
&lt;td&gt;SF&lt;/td&gt;
&lt;td&gt;151&lt;/td&gt;
&lt;td&gt;114&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;Frank Gore&lt;/td&gt;
&lt;td&gt;RB&lt;/td&gt;
&lt;td&gt;SF&lt;/td&gt;
&lt;td&gt;144&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;Frank Gore&lt;/td&gt;
&lt;td&gt;RB&lt;/td&gt;
&lt;td&gt;SF&lt;/td&gt;
&lt;td&gt;119&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;td&gt;Frank Gore&lt;/td&gt;
&lt;td&gt;RB&lt;/td&gt;
&lt;td&gt;SF&lt;/td&gt;
&lt;td&gt;107&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;6&lt;/td&gt;
&lt;td&gt;Frank Gore&lt;/td&gt;
&lt;td&gt;RB&lt;/td&gt;
&lt;td&gt;SF&lt;/td&gt;
&lt;td&gt;95&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;7&lt;/td&gt;
&lt;td&gt;Frank Gore&lt;/td&gt;
&lt;td&gt;RB&lt;/td&gt;
&lt;td&gt;SF&lt;/td&gt;
&lt;td&gt;81&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;8&lt;/td&gt;
&lt;td&gt;Frank Gore&lt;/td&gt;
&lt;td&gt;RB&lt;/td&gt;
&lt;td&gt;SF&lt;/td&gt;
&lt;td&gt;66&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;9&lt;/td&gt;
&lt;td&gt;Colin Kaepernick&lt;/td&gt;
&lt;td&gt;QB&lt;/td&gt;
&lt;td&gt;SF&lt;/td&gt;
&lt;td&gt;64&lt;/td&gt;
&lt;td&gt;248&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;10&lt;/td&gt;
&lt;td&gt;Frank Gore&lt;/td&gt;
&lt;td&gt;RB&lt;/td&gt;
&lt;td&gt;SF&lt;/td&gt;
&lt;td&gt;63&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;11&lt;/td&gt;
&lt;td&gt;Colin Kaepernick&lt;/td&gt;
&lt;td&gt;QB&lt;/td&gt;
&lt;td&gt;SF&lt;/td&gt;
&lt;td&gt;63&lt;/td&gt;
&lt;td&gt;204&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;12&lt;/td&gt;
&lt;td&gt;Frank Gore&lt;/td&gt;
&lt;td&gt;RB&lt;/td&gt;
&lt;td&gt;SF&lt;/td&gt;
&lt;td&gt;63&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;13&lt;/td&gt;
&lt;td&gt;Colin Kaepernick&lt;/td&gt;
&lt;td&gt;QB&lt;/td&gt;
&lt;td&gt;SF&lt;/td&gt;
&lt;td&gt;58&lt;/td&gt;
&lt;td&gt;218&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;14&lt;/td&gt;
&lt;td&gt;Carlos Hyde&lt;/td&gt;
&lt;td&gt;RB&lt;/td&gt;
&lt;td&gt;SF&lt;/td&gt;
&lt;td&gt;55&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;15&lt;/td&gt;
&lt;td&gt;Colin Kaepernick&lt;/td&gt;
&lt;td&gt;QB&lt;/td&gt;
&lt;td&gt;SF&lt;/td&gt;
&lt;td&gt;54&lt;/td&gt;
&lt;td&gt;245&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;16&lt;/td&gt;
&lt;td&gt;Carlos Hyde&lt;/td&gt;
&lt;td&gt;RB&lt;/td&gt;
&lt;td&gt;SF&lt;/td&gt;
&lt;td&gt;50&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;17&lt;/td&gt;
&lt;td&gt;Frank Gore&lt;/td&gt;
&lt;td&gt;RB&lt;/td&gt;
&lt;td&gt;SF&lt;/td&gt;
&lt;td&gt;49&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;18&lt;/td&gt;
&lt;td&gt;Colin Kaepernick&lt;/td&gt;
&lt;td&gt;QB&lt;/td&gt;
&lt;td&gt;SF&lt;/td&gt;
&lt;td&gt;46&lt;/td&gt;
&lt;td&gt;141&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;19&lt;/td&gt;
&lt;td&gt;Carlos Hyde&lt;/td&gt;
&lt;td&gt;RB&lt;/td&gt;
&lt;td&gt;SF&lt;/td&gt;
&lt;td&gt;43&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;20&lt;/td&gt;
&lt;td&gt;Frank Gore&lt;/td&gt;
&lt;td&gt;RB&lt;/td&gt;
&lt;td&gt;SF&lt;/td&gt;
&lt;td&gt;38&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;</content:encoded></item><item><title><![CDATA[Model scoring API examples]]></title><description><![CDATA[A basic example of a predictive model scoring API The associate Github repo for this blog post is https://github.com/mortie23/fastapi-linear…]]></description><link>https://mortimer.xyz/model-scoring-api/</link><guid isPermaLink="false">https://mortimer.xyz/model-scoring-api/</guid><pubDate>Sat, 09 Jul 2022 12:00:00 GMT</pubDate><content:encoded>&lt;h1 id=&quot;A-basic-example-of-a-predictive-model-scoring-API&quot; style=&quot;position:relative;&quot;&gt;A basic example of a predictive model scoring API&lt;a href=&quot;#A-basic-example-of-a-predictive-model-scoring-API&quot; aria-label=&quot;A basic example of a predictive model scoring API permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h1&gt;
&lt;p&gt;The associate Github repo for this blog post is &lt;a href=&quot;https://github.com/mortie23/fastapi-linear-model-demo&quot;&gt;https://github.com/mortie23/fastapi-linear-model-demo&lt;/a&gt;&lt;/p&gt;
&lt;h2 id=&quot;To-demonstrate-model-scoring-hosted-as-API&quot; style=&quot;position:relative;&quot;&gt;To demonstrate model scoring hosted as API&lt;a href=&quot;#To-demonstrate-model-scoring-hosted-as-API&quot; aria-label=&quot;To demonstrate model scoring hosted as API permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The reason for doing this is to create a very light weight and infrastructure free way to demonstrate calling a model inference API from a web front end. It doesn’t have many things you would potentially need for a real life situation like:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;authentication&lt;/li&gt;
&lt;li&gt;authorization&lt;/li&gt;
&lt;li&gt;auto scaling&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;It is not meant to be used for production, but simply to demonstrate the mechanics.
We are going to do this both with R (Plumber) and Python (FastAPI).
This should be all good to run on Windows or Linux. I have developed it on Debian (from ChromeOS).&lt;/p&gt;
&lt;h3 id=&quot;The-model&quot; style=&quot;position:relative;&quot;&gt;The model&lt;a href=&quot;#The-model&quot; aria-label=&quot;The model permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Before we even start we need a simple example of a &lt;em&gt;model&lt;/em&gt;. We will use linear regression to model the relationship between height and weight. No need to for an ML model framework or a model registry for this demonstration. We are demonstrating to REST API part of things.&lt;/p&gt;
&lt;p&gt;A small sample of heights and weights was used.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 692px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/62b5e52060c5dd4e3be889b891ac2fe0/51c99/mdlapi-height-weight-data.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 59.199999999999996%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAMCAYAAABiDJ37AAAACXBIWXMAABJ0AAASdAHeZh94AAABPUlEQVQoz42TiYrDMAxE8//f2VJ65058xLM8uQ12KXQFwrIljWYU0mzbprZtFUJQSknc/+uf9dwbSVrX1QAxHmuXFWPvJjwaSFLireirAL8x5C2EaLHzUdMS1I9O8xosjjHufQZIME2TvPevKTWglLS6oG70inHT6qJWn0F82CxHzQ54u900z3OWnJI1hLhZjCyaYMgJIANLRp9nc71eK8k+ZEDkMb2fvJaCReUMjbH+KM65SjIJmLSDM2kY+3IvmXhp7/vPj2LSXlMzi3xSC4Gu64wE6yKGGGaAy7LsgKWX+8FoGoZBp9PJ/Hg86nA46Hw+26AdkIk80Ahw6bDizKvI+wIU54NeLpdiVTEDQpsEoM/n04r7vtf9fjdWMEAWNeTGcbS/i9zj8TBC9NHTlMv9JvmtAHCAvq2j7PkDXauvPtDUy0oAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;mdlapi height weight data&quot;
        title=&quot;mdlapi height weight data&quot;
        src=&quot;/static/62b5e52060c5dd4e3be889b891ac2fe0/51c99/mdlapi-height-weight-data.png&quot;
        srcset=&quot;/static/62b5e52060c5dd4e3be889b891ac2fe0/0eb09/mdlapi-height-weight-data.png 500w,
/static/62b5e52060c5dd4e3be889b891ac2fe0/51c99/mdlapi-height-weight-data.png 692w&quot;
        sizes=&quot;(max-width: 692px) 100vw, 692px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;
The model in this case is a combination of the model function:&lt;/p&gt;
&lt;p&gt;&lt;em&gt;weight&lt;/em&gt;(&lt;em&gt;height&lt;/em&gt;) = &lt;strong&gt;m&lt;/strong&gt; &lt;em&gt;height&lt;/em&gt; + &lt;strong&gt;b&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;and the model parameters:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;parameter&lt;/th&gt;
&lt;th&gt;value&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;m&lt;/td&gt;
&lt;td&gt;0.5772&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;b&lt;/td&gt;
&lt;td&gt;-25.165&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 id=&quot;Python&quot; style=&quot;position:relative;&quot;&gt;Python&lt;a href=&quot;#Python&quot; aria-label=&quot;Python permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;h3 id=&quot;Coded-in-a-Python-function&quot; style=&quot;position:relative;&quot;&gt;Coded in a Python function&lt;a href=&quot;#Coded-in-a-Python-function&quot; aria-label=&quot;Coded in a Python function permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;This model is very simply to code in Python. Note the decorator code used by FastAPI before the function definition.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;py&quot;&gt;&lt;pre class=&quot;language-py&quot;&gt;&lt;code class=&quot;language-py&quot;&gt;&lt;span class=&quot;token decorator annotation punctuation&quot;&gt;@app&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;get&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;/weight/{height}&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;weight&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;height&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;dict&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token triple-quoted-string string&quot;&gt;&quot;&quot;&quot;
    This function takes a height and returns an estimated weight
        Args:
            height: user inputs the height
        Return:
            weight
        Model:
            y = mx + b
    &quot;&quot;&quot;&lt;/span&gt;
    weight &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0.5772&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; height &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;25.165&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;weight&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; weight&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Now, how do we host this as a scoring (predicting or inference) API?&lt;/p&gt;
&lt;h3 id=&quot;Using-FastAPI&quot; style=&quot;position:relative;&quot;&gt;Using FastAPI&lt;a href=&quot;#Using-FastAPI&quot; aria-label=&quot;Using FastAPI permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;To get this up and running I will assume you have Python3 already. To get started you might want to create a new virtual environment and then install the dependencies:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;sh&quot;&gt;&lt;pre class=&quot;language-sh&quot;&gt;&lt;code class=&quot;language-sh&quot;&gt;pip &lt;span class=&quot;token function&quot;&gt;install&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;fastapi[all]&quot;&lt;/span&gt;
pip &lt;span class=&quot;token function&quot;&gt;install&lt;/span&gt; uvicorn&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The python script in the associated repo creates the REST service.
To start it up use:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;sh&quot;&gt;&lt;pre class=&quot;language-sh&quot;&gt;&lt;code class=&quot;language-sh&quot;&gt;uvicorn model:app &lt;span class=&quot;token parameter variable&quot;&gt;--reload&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;You’ll see something like the below log if it is working and you can browse to the end point using &lt;a href=&quot;http://127.0.0.1:8000/weight/150&quot;&gt;http://127.0.0.1:8000/weight/150&lt;/a&gt; or for the Swagger docs using &lt;a href=&quot;http://127.0.0.1:8000/docs&quot;&gt;http://127.0.0.1:8000/docs&lt;/a&gt;:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;log&quot;&gt;&lt;pre class=&quot;language-log&quot;&gt;&lt;code class=&quot;language-log&quot;&gt;&lt;span class=&quot;token level info keyword&quot;&gt;INFO&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;     Will watch for changes in these directories&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;C:\\git\\fastapi&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;token level info keyword&quot;&gt;INFO&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;     Uvicorn running on &lt;span class=&quot;token url&quot;&gt;http://127.0.0.1:8000&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;(&lt;/span&gt;Press CTRL&lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt;C to quit&lt;span class=&quot;token operator&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token level info keyword&quot;&gt;INFO&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;     Started reloader process &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;36128&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; using watchgod
&lt;span class=&quot;token level info keyword&quot;&gt;INFO&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;     Started server process &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;24032&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;token level info keyword&quot;&gt;INFO&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;     Waiting for application startup&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;
&lt;span class=&quot;token level info keyword&quot;&gt;INFO&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;     Application startup complete&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 668px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/9c4eaa77d98b975a962a63eadb64ee9c/1d36f/mdlapi-fastapi-screenshot.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 56.199999999999996%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAALCAYAAAB/Ca1DAAAACXBIWXMAAAsTAAALEwEAmpwYAAABhUlEQVQoz62R647cIAyF8/7vta/Q3XZHSiaXbUICBEgI15zKmcnObKVW/VFL5oCxP2NRKKV2rTW01qdCKY15njHPCo/YY3+ezxgxhBAYGMsFFUk571wIcC5AF1JKOOdgrQXn/IA/Q57h93MmFUJ0hV2X3S4Gi9FHFwIQcFlWGGMwTfxedHs5xbQ2EEJ+NtJaZ4orpcpi1B7SJqg1IHgP5z2UNhjH6RiZwPcpsKwWKWXEmFDXzZGzrpYmydZu2LatKi4s4OXiwHTGR9fhWjdo2w7Oh6Po/XI5gATxPnx6CBHO+cO9D5li1m5VIaVCzySM1uj7AW37gfraQHCJoWe4Vg3YMEIrg9VaGLNQ4aEhhKNRCDGTOucIKI/P6BmHkDMmwdF0LZqfLQbB0PQtyrYC4yO2zWFZbkDSlBL2fSfPpDHGqsg7UM8ZpcgICTBBYdomlKrEG3/FD/Edr/wbhBP43e6wr0DaAPuZgb/ZEwD719xMS0rpBvxXp75/uHsA8R/sBMYYr78Afq9R1DKlbtEAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;mdlapi fastapi screenshot&quot;
        title=&quot;mdlapi fastapi screenshot&quot;
        src=&quot;/static/9c4eaa77d98b975a962a63eadb64ee9c/1d36f/mdlapi-fastapi-screenshot.png&quot;
        srcset=&quot;/static/9c4eaa77d98b975a962a63eadb64ee9c/0eb09/mdlapi-fastapi-screenshot.png 500w,
/static/9c4eaa77d98b975a962a63eadb64ee9c/1d36f/mdlapi-fastapi-screenshot.png 668w&quot;
        sizes=&quot;(max-width: 668px) 100vw, 668px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;The part that does the “scoring” in the Python script is:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;py&quot;&gt;&lt;pre class=&quot;language-py&quot;&gt;&lt;code class=&quot;language-py&quot;&gt;weight &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0.5772&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt;height &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;25.165&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2 id=&quot;R&quot; style=&quot;position:relative;&quot;&gt;R&lt;a href=&quot;#R&quot; aria-label=&quot;R permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;We will use Plumber for this.
I am on a (brilliant) Chromebook at the moment which has a Debian distro on it. So I need this to get going:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token function&quot;&gt;sudo&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;apt&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;install&lt;/span&gt; libsodium-dev
&lt;span class=&quot;token function&quot;&gt;sudo&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;apt&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;install&lt;/span&gt; libcurl4-openssl-dev&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;r&quot;&gt;&lt;pre class=&quot;language-r&quot;&gt;&lt;code class=&quot;language-r&quot;&gt;install.packages&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;plumber&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;Using-Plumber&quot; style=&quot;position:relative;&quot;&gt;Using Plumber&lt;a href=&quot;#Using-Plumber&quot; aria-label=&quot;Using Plumber permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;r&quot;&gt;&lt;pre class=&quot;language-r&quot;&gt;&lt;code class=&quot;language-r&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;#* Return the estimated weight&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;#* @param height the height&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;#* @get /weight&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;height&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token number&quot;&gt;0.5772&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; as.numeric&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;height&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;25.165&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;If you look at the &lt;code class=&quot;language-text&quot;&gt;./modelApi.R&lt;/code&gt; script in the associated repo, I have added a shebang (&lt;code class=&quot;language-text&quot;&gt;#!&lt;/code&gt;) to the script (pointing to the Rscript executable) and given the script execute permissions (&lt;code class=&quot;language-text&quot;&gt;chmod +x modelApi.R&lt;/code&gt;). We can start the Plumber REST service like this:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;sh&quot;&gt;&lt;pre class=&quot;language-sh&quot;&gt;&lt;code class=&quot;language-sh&quot;&gt;./modelApi.R&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;You will get something like this in the log if it has worked:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;log&quot;&gt;&lt;pre class=&quot;language-log&quot;&gt;&lt;code class=&quot;language-log&quot;&gt;Running plumber API at &lt;span class=&quot;token url&quot;&gt;http://127.0.0.1:8000&lt;/span&gt;
Running swagger Docs at &lt;span class=&quot;token url&quot;&gt;http://127.0.0.1:8000/__docs__/&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 673px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/54b554595270d39b75592ba8f238a01d/60e44/mdlapi-plumber-screenshot.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 49.8%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAKCAYAAAC0VX7mAAAACXBIWXMAAAsTAAALEwEAmpwYAAABZ0lEQVQoz41Qa2vCQBDM//9fhX6zWKhWW0yMMbk8753cxdyUW7TY4gcPhiGbnZndTYQQQUoJKeWNCUIIglEWRhsYo6GUwn3PXW/o+x5N04SEc4Fh4FToug43juJotNq/4u19hfX6HbvdDlmW4nA4ELIso95reOCcIxmNDlYraCWvpjGpRfwZ0z8Oa2w/N9hstmRwOp1QFAXSNEWe5zRA3C4OoJQKSSMdenMBNzOcc5ich5QKbdvHyTFaD60NpNIQQhI751GWFeq6gbUjxnGEsZb0yXfj8fI1gckF53OBLDsiz08kOh5z7PZ7xLPM8wXT5Kge4f1M3/e1KRryQaCqe0qvWYNzUZJp23YoS4Y0zYijqbUWSmmaKrL3noLmef5F0g8cbTfQcauWgXU1yqpC23S0erwpYzUZ0mrGEMczLMuCEMIfJH4BlAMuYUapSzDDEBDwzPtvRoYgccC9x6PGR3j0kmfFz+IHvjsByABrQcAAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;mdlapi plumber screenshot&quot;
        title=&quot;mdlapi plumber screenshot&quot;
        src=&quot;/static/54b554595270d39b75592ba8f238a01d/60e44/mdlapi-plumber-screenshot.png&quot;
        srcset=&quot;/static/54b554595270d39b75592ba8f238a01d/0eb09/mdlapi-plumber-screenshot.png 500w,
/static/54b554595270d39b75592ba8f238a01d/60e44/mdlapi-plumber-screenshot.png 673w&quot;
        sizes=&quot;(max-width: 673px) 100vw, 673px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;h3 id=&quot;Adding-an-interface-and-calling-the-API&quot; style=&quot;position:relative;&quot;&gt;Adding an interface and calling the API&lt;a href=&quot;#Adding-an-interface-and-calling-the-API&quot; aria-label=&quot;Adding an interface and calling the API permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;A basic HTML front end has been created to demonstrate calling the API with user input.&lt;/p&gt;
&lt;p&gt;In this example we are just using some JavaScript and Jquery to get the user input, pass it to the API and then populate the front end with the score.&lt;/p&gt;
&lt;p&gt;To test it I am just using the &lt;a href=&quot;https://marketplace.visualstudio.com/items?itemName=ritwickdey.LiveServer&quot;&gt;Live server&lt;/a&gt; VScode extension. If you have cloned this repo you, and have the extension, you can just right click on the &lt;code class=&quot;language-text&quot;&gt;index.html&lt;/code&gt; and click on &lt;em&gt;Open with Live Server&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 543px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/6649a8ee63ac984b49f1e3ffad5f3be9/10880/mdlapi-height-weight-frontend.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 53.400000000000006%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAALCAYAAAB/Ca1DAAAACXBIWXMAABJ0AAASdAHeZh94AAAB20lEQVQoz3WSvY7TQBSF/Xi8AR3vQIEooUG8wBZUVNQ8ACUUS7UdUpIVm83P+Hec2OOfZMYef+jOJlFA7JWOrj2eOfec44nyQtM0LbVpqGvDvjKY07sxbXhuu56qbtiojDgtSDL9LKI4SbDW0vc9fd9hTENV1TSNEJqwPo4j1jk2KiXNNXpXU5TVBVmxIyvK0KPRe6Zp4ng80nU9h8MB5wbSLKcoNDIwTjLSNEPFCUmaEScpKklPaylVbWi6HtN0RKJACI5HG8i6vsdaR7nbB6W6LNG6JMvyQCbI8uLS5ZsxdXDTdi3RfH6PUjHOOYZhuMB7H6xKiQMh2KqYQpdB0VaJ2pyyalk8pPyar1nFeyKlVCCTw9eEAslWohjGkcfVmoflks12S6E1upQ4Yu5/P6LSirXS1I0lWq+3yJ8eRo/3U1AjkAHnQaJWotC6Jo53LB8LHgTLPNwK5yzHw4FxHIjUZkmiVphKY+qSMmSmQ54X+9OEdZ6+F8UjbWtpO0fbysCJc4mQ6PZOcXu34dv3GT9+zlgs5sxmM3b7fbArxE+KPX7y/K/OrgLhizeOVx8GXr4feX3jma42+tOVOvd/D58BV4RvP1k+frG8+2y5+eoYxuuNfxNfEz5XfwA9v0WrM1LTqAAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;mdlapi height weight frontend&quot;
        title=&quot;mdlapi height weight frontend&quot;
        src=&quot;/static/6649a8ee63ac984b49f1e3ffad5f3be9/10880/mdlapi-height-weight-frontend.png&quot;
        srcset=&quot;/static/6649a8ee63ac984b49f1e3ffad5f3be9/0eb09/mdlapi-height-weight-frontend.png 500w,
/static/6649a8ee63ac984b49f1e3ffad5f3be9/10880/mdlapi-height-weight-frontend.png 543w&quot;
        sizes=&quot;(max-width: 543px) 100vw, 543px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;The JavaScript that does the calling of the Python FastAPI is:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;js&quot;&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; url &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;http://127.0.0.1:8000/weight/&apos;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; height&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
$&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;url&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;data&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;weight&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  weight&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;value &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; data&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;weight&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;If we want to test this for the R Plumber API we would just change it to:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;js&quot;&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; url &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;http://127.0.0.1:8000/weight?height=&apos;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; height&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
$&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;url&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;data&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;weight&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  weight&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;value &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; data&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;weight&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;</content:encoded></item><item><title><![CDATA[Power BI PBIX Git version control]]></title><description><![CDATA[How to commit a Power BI PBIX to Git NOTE: this blog post has been superseded by Power BI PBIP Git version control This is in reference to…]]></description><link>https://mortimer.xyz/pbix-git-version-control/</link><guid isPermaLink="false">https://mortimer.xyz/pbix-git-version-control/</guid><pubDate>Tue, 14 Jun 2022 12:00:00 GMT</pubDate><content:encoded>&lt;h1 id=&quot;How-to-commit-a-Power-BI-PBIX-to-Git&quot; style=&quot;position:relative;&quot;&gt;How to commit a Power BI PBIX to Git&lt;a href=&quot;#How-to-commit-a-Power-BI-PBIX-to-Git&quot; aria-label=&quot;How to commit a Power BI PBIX to Git permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h1&gt;
&lt;blockquote&gt;
&lt;p&gt;NOTE: this blog post has been superseded by &lt;a href=&quot;https://mortimer.xyz/pbip-git-version-control&quot;&gt;Power BI PBIP Git version control&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;This is in reference to the associated &lt;a href=&quot;https://github.com/mortie23/pbix-git-version-control&quot;&gt;https://github.com/mortie23/pbix-git-version-control&lt;/a&gt;&lt;/p&gt;
&lt;h2 id=&quot;What-is-the-problem&quot; style=&quot;position:relative;&quot;&gt;What is the problem&lt;a href=&quot;#What-is-the-problem&quot; aria-label=&quot;What is the problem permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;A PBIX file, under the hood, it just a series of text files archived into a single file.
For Power BI to read the file and display the dashboard in Power BI desktop or web it needs to uncompress the archive and read the files first.&lt;/p&gt;
&lt;p&gt;Version control systems like Git are terrific at tracking text files and not designed to track files that are not text. If the file is text, then incremental changes are efficiently tracked and stored. Otherwise Git will just track versions by keeping a full history of the file.&lt;/p&gt;
&lt;p&gt;Git will see a commit of a PBIX file and say “I can’t track the changes, so I will just keep another version of the file”. This results in:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;You cannot use it to see what the changes were between versions&lt;/li&gt;
&lt;li&gt;The size of the repo starts to blow out the more you commit&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;We want Git to track the underlying files in the archive and in the process we want the Power BI dashboard developers to not have to worry about any of these technical details.&lt;/p&gt;
&lt;h2 id=&quot;Git-Hooks&quot; style=&quot;position:relative;&quot;&gt;Git Hooks&lt;a href=&quot;#Git-Hooks&quot; aria-label=&quot;Git Hooks permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Git hooks are scripts that Git can run before or after most Git actions (like commit, receive, checkout etc). They are kept in the hidden &lt;code class=&quot;language-text&quot;&gt;.git&lt;/code&gt; directory and are local client files. This means they are not tracked by Git and are not synced between remotes when pushed or pulled. There are ways to get around this.&lt;/p&gt;
&lt;h2 id=&quot;Sharing-Git-Hooks&quot; style=&quot;position:relative;&quot;&gt;Sharing Git Hooks&lt;a href=&quot;#Sharing-Git-Hooks&quot; aria-label=&quot;Sharing Git Hooks permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;One way to do this is to create a &lt;code class=&quot;language-text&quot;&gt;hooks&lt;/code&gt; directory in the root of your repo and then run this configuration.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token function&quot;&gt;git&lt;/span&gt; config core.hooksPath &lt;span class=&quot;token string&quot;&gt;&apos;./hooks&apos;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;It tells Git to look for the hooks scripts in this directory instead of the default in the &lt;code class=&quot;language-text&quot;&gt;.git&lt;/code&gt; directory.&lt;/p&gt;
&lt;h2 id=&quot;How-we-solve-the-PBIX-problem&quot; style=&quot;position:relative;&quot;&gt;How we solve the PBIX problem&lt;a href=&quot;#How-we-solve-the-PBIX-problem&quot; aria-label=&quot;How we solve the PBIX problem permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;We want Git to do the following whenever a PBIX is committed:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Extract the PBIX into its underlying files&lt;/li&gt;
&lt;li&gt;Commit these files instead&lt;/li&gt;
&lt;li&gt;Do not commit the PBIX file&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;We want Git to do the following whenever a PBIX is pulled/merged/checked out&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Compress the PBIX&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id=&quot;Pre-requisite&quot; style=&quot;position:relative;&quot;&gt;Pre-requisite&lt;a href=&quot;#Pre-requisite&quot; aria-label=&quot;Pre requisite permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;This will only work for a PBIX file that sources its data from a remote Power BI XMLA source. If the data (DataModel) is contained within the PBIX then it will not work.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Running on client machine with:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Windows 10&lt;/li&gt;
&lt;li&gt;Git 2.27.0.windows.1&lt;/li&gt;
&lt;li&gt;GNU bash, version 4.4.23(1)-release (x86_64-pc-msys)&lt;/li&gt;
&lt;li&gt;Powershell Core 7.2.4&lt;/li&gt;
&lt;li&gt;7-Zip 21.07 (x64)
&lt;ul&gt;
&lt;li&gt;The 7z.exe cli needs to be in your environment path (you can do this be adding &lt;code class=&quot;language-text&quot;&gt;$env:Path += &quot;;C:\Program Files\7-Zip&quot;&lt;/code&gt; to your Powershell profile)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;Example&quot; style=&quot;position:relative;&quot;&gt;Example&lt;a href=&quot;#Example&quot; aria-label=&quot;Example permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;First let’s create the PBIX.&lt;/p&gt;
&lt;h3 id=&quot;Dataset&quot; style=&quot;position:relative;&quot;&gt;Dataset&lt;a href=&quot;#Dataset&quot; aria-label=&quot;Dataset permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;I have a cloud hosted PostGres database and have loaded some sample NFL data into it. I have subsequently loaded this to Power BI as a XMLA dataset.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 989px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/cdf7e466af5167db127e6fb557b892b0/acc8c/pbi-git-datasource.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 15.8%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAADCAIAAAAcOLh5AAAACXBIWXMAAAsSAAALEgHS3X78AAAAkklEQVQI1zXGywrCMBCF4b6nT+tK6E5Uelk0NrVNJ2mUgiWTZCYRWjx8HP6ivcO6vqV8OYfMiYiJOEZKKXdCXi/nzyj08lZqcs4hYgiB9xHnYvsicfQeU+Kc02Hv3HWibWqO0fuA6I8nosSZIsbtVqhRG7AGrJ6XgwE7yGkc5rpqquoBSljdL38GnqD6QZSiPP0A1nGnqEC2v8YAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;pbi git datasource&quot;
        title=&quot;pbi git datasource&quot;
        src=&quot;/static/cdf7e466af5167db127e6fb557b892b0/acc8c/pbi-git-datasource.png&quot;
        srcset=&quot;/static/cdf7e466af5167db127e6fb557b892b0/0eb09/pbi-git-datasource.png 500w,
/static/cdf7e466af5167db127e6fb557b892b0/acc8c/pbi-git-datasource.png 989w&quot;
        sizes=&quot;(max-width: 989px) 100vw, 989px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;Lets open up Power BI desktop and connect to this dataset.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 1341px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/ecddc151a135a2a5839da82d4bca6c2d/e5b55/pbi-git-datasource-connect.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 20%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAECAIAAAABPYjBAAAACXBIWXMAAAsSAAALEgHS3X78AAAAfklEQVQI133NMQ7CMAxA0dz/UByBkYW5S+vGiWMbB+EYKZUqlvLWP/xUK+Wc930nIkQspYwx4tpZ3T2ZKTP33n/DHzZ9pvTuJiLufjZmdncziwhVNXtFhLAcA1UVleOUlvtteT6wNplaa0QkIrBtpRQAqLWqKgBs64qIbeLpC6r16Gljz34iAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;pbi git datasource connect&quot;
        title=&quot;pbi git datasource connect&quot;
        src=&quot;/static/ecddc151a135a2a5839da82d4bca6c2d/e5b55/pbi-git-datasource-connect.png&quot;
        srcset=&quot;/static/ecddc151a135a2a5839da82d4bca6c2d/0eb09/pbi-git-datasource-connect.png 500w,
/static/ecddc151a135a2a5839da82d4bca6c2d/1263b/pbi-git-datasource-connect.png 1000w,
/static/ecddc151a135a2a5839da82d4bca6c2d/e5b55/pbi-git-datasource-connect.png 1341w&quot;
        sizes=&quot;(max-width: 1341px) 100vw, 1341px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;h3 id=&quot;Dashboard&quot; style=&quot;position:relative;&quot;&gt;Dashboard&lt;a href=&quot;#Dashboard&quot; aria-label=&quot;Dashboard permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;We will create something very simple that includes:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Text box&lt;/li&gt;
&lt;li&gt;Map viz&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 1160px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/8c618d6aaeadf0172e7fff040f8b137a/9708d/pbi-git-dashboard.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 55.400000000000006%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAALCAYAAAB/Ca1DAAAACXBIWXMAABJ0AAASdAHeZh94AAACD0lEQVQoz2WSa08aQRSG+f+/pOmXJpWbto3VKizQAlFgZS/snR32vipaEPZpdqGN1UnezMyZ8z4zJ2dqAEXxWkUZIo5jVFVFVRSiKKrWmqZhWxa2bXPwFa8EpbNW7HdQ7IB9pWoPZFnGdDJBlmWEEKiqgqIoGIaJ5zq8G8We/W5LzXRDNDtGNwMMM8RyItIoZL1+YLt/4eVlx/N6ze/nHdvNhu3mmfunDXH6yON9ThY/kCUJIshZxU/ULsY5J52URjeh0UlpShmyvsJf2gjhIXwX37PRTIHr+cShx8IWLCyB7/s47hJVNfg6CGn1sgOwfgSVqndzvvRD5nczVsKtgEK41QWm5TLTBGEYkiQhYbDi5s7j+iZGmiS0+/lbYPnSjJaUYDsOYeCxEh5R6BOsPBxTozcy+HVrs9AXzDUdaWxzKgU0uwlng4za92H2DtjupXiuWwGF7zGbjbi9GaGrc4Y/x/TPz2l/rPPhbE6z/1h5Sp3235Tc6B7mz1cJnZ6GWJq4joXrLDANDdvU0eYyjjqjPzQ5uc5pSenxIel7YKUj9KSb0RmWf84gjgSWoSNPJyiaynRmVea/+Y3uf8D7qsuvD0rVy4RBzlSLCFYOC2tJ4yrg02VE82JO/UrQkPJDbic9lpxTuxwF/+jt3lulVedk1UHR7Sr2Y5xy2nH41vM46ye0pIi2FNHqRnwZpPwBVacV08v3HkQAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;pbi git dashboard&quot;
        title=&quot;pbi git dashboard&quot;
        src=&quot;/static/8c618d6aaeadf0172e7fff040f8b137a/9708d/pbi-git-dashboard.png&quot;
        srcset=&quot;/static/8c618d6aaeadf0172e7fff040f8b137a/0eb09/pbi-git-dashboard.png 500w,
/static/8c618d6aaeadf0172e7fff040f8b137a/1263b/pbi-git-dashboard.png 1000w,
/static/8c618d6aaeadf0172e7fff040f8b137a/9708d/pbi-git-dashboard.png 1160w&quot;
        sizes=&quot;(max-width: 1160px) 100vw, 1160px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;h3 id=&quot;Use-PBIT-file-type&quot; style=&quot;position:relative;&quot;&gt;Use PBIT file type&lt;a href=&quot;#Use-PBIT-file-type&quot; aria-label=&quot;Use PBIT file type permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;We need to save this as a PBIT (Power BI Template) to ensure that one of the underlying files is created (&lt;code class=&quot;language-text&quot;&gt;DataMashup&lt;/code&gt;).&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 761px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/148381f597f44dcef04bb3deff9ce94f/fe1bc/pbi-git-dashboard-pbit.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 57.199999999999996%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAALCAIAAADwazoUAAAACXBIWXMAAAsSAAALEgHS3X78AAABn0lEQVQoz3WQC2vjMBCE/f//Wy+Fa0id+BHZsSVZlrx6W5EUH06gFMJ9LMsuwzAwxd30IHiMadvyI+fHf8hvUgihiDHlnFN63J3wVkupjDGMMaN1fhJC8N6nlJxzWu3qi90MSg+YaeMWuR8LKL+uznvnV+v2/Wu8895Yq5Ri0v5peZFTcGqk05xS8t5b55Vxr8yUUv6FsQ6UAWVyzjEmIU2xbdmZeeYihHVdQ7x7b/j9Hrc3vPcAYK01xqSYvHMFrLljSi5CaY0JTdFruHGxvJvXdRVi0VpLKWOMxpgiPraacljEY9tCCIazaeyGcVyegJSg1L4BGGPzPDvnnm2t1ppiDwSJ22YoS1rXpG7YbaBVRaoaV9XYtmPTYIQonQilnPOftgGggGURnDffp8vh8/jxcfr6uiJ0vbaoQ7dxnDAm1yvpelqWjGDGJvKEUjoMQwEAnPPXP7ZNdzpVh0N7PKLLua8b2vek73Fdd+equ+FuICOhP/6CThNCqENICKGNUfqJUpIxObEFY5hnkLIZ589T9ffSdWRum7osz9M0/QOB7Wt13ZcxewAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;pbi git dashboard pbit&quot;
        title=&quot;pbi git dashboard pbit&quot;
        src=&quot;/static/148381f597f44dcef04bb3deff9ce94f/fe1bc/pbi-git-dashboard-pbit.png&quot;
        srcset=&quot;/static/148381f597f44dcef04bb3deff9ce94f/0eb09/pbi-git-dashboard-pbit.png 500w,
/static/148381f597f44dcef04bb3deff9ce94f/fe1bc/pbi-git-dashboard-pbit.png 761w&quot;
        sizes=&quot;(max-width: 761px) 100vw, 761px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;But then we will rename the extension back to PBIX to make things easier (i.e. double clicking the file will open it in Power BI desktop and make it easier to edit).&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 776px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/58ad9a8e88648a34e05172c8fc4a510c/cfc60/pbi-git-dashboard-pbix.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 22.799999999999997%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAFCAIAAADKYVtkAAAACXBIWXMAAAsSAAALEgHS3X78AAAAzElEQVQY002N23LCMAxE8///R+qhEwyhaUhzsWTZcnwhYkhL2zP7tmdnK7A+lyIi2468CGsE6xawHKKIxJTI8XdVSnE+iEi1BkPOPcfynP4m5wyA5Jwjx8y5ZGuJmfebH6daqRvG6e/xRcnJzJNFQFiC9/eSCU1g/9+p2qs6qKMB/MR0RmntHhK9pEM7q96qDo83r5dUf4DqsPlibfKVpJlTVb839Zs6aT3aAFFg3WAVjDJROnXTZTCXwfSzHynpfjrflt54EzaM0ht+ALaWHgqFBZjiAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;pbi git dashboard pbix&quot;
        title=&quot;pbi git dashboard pbix&quot;
        src=&quot;/static/58ad9a8e88648a34e05172c8fc4a510c/cfc60/pbi-git-dashboard-pbix.png&quot;
        srcset=&quot;/static/58ad9a8e88648a34e05172c8fc4a510c/0eb09/pbi-git-dashboard-pbix.png 500w,
/static/58ad9a8e88648a34e05172c8fc4a510c/cfc60/pbi-git-dashboard-pbix.png 776w&quot;
        sizes=&quot;(max-width: 776px) 100vw, 776px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;h3 id=&quot;Ensure-our-configuration-is-set-to-run-hooks&quot; style=&quot;position:relative;&quot;&gt;Ensure our configuration is set to run hooks&lt;a href=&quot;#Ensure-our-configuration-is-set-to-run-hooks&quot; aria-label=&quot;Ensure our configuration is set to run hooks permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token function&quot;&gt;git&lt;/span&gt; config core.hooksPath &lt;span class=&quot;token string&quot;&gt;&apos;./hooks&apos;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;Lets-commit-the-file-and-see-what-happens&quot; style=&quot;position:relative;&quot;&gt;Lets commit the file and see what happens&lt;a href=&quot;#Lets-commit-the-file-and-see-what-happens&quot; aria-label=&quot;Lets commit the file and see what happens permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token function&quot;&gt;git&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;add&lt;/span&gt; nfl.pbix
&lt;span class=&quot;token function&quot;&gt;git&lt;/span&gt; commit &lt;span class=&quot;token parameter variable&quot;&gt;-m&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;pbix commit&quot;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;/c/git/github/mortie23/pbix-git-version-control/hooks
nfl.pbix

Expanding pbix files&lt;span class=&quot;token punctuation&quot;&gt;..&lt;/span&gt;.

nfl.pbix
Archive:  nfl.pbix
  inflating: ./nfl.pbix.expanded/Version
  inflating: ./nfl.pbix.expanded/&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;Content_Types&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;.xml
  inflating: ./nfl.pbix.expanded/DataMashup
  inflating: ./nfl.pbix.expanded/DiagramLayout
  inflating: ./nfl.pbix.expanded/Report/Layout
  inflating: ./nfl.pbix.expanded/Settings
  inflating: ./nfl.pbix.expanded/Metadata
  inflating: ./nfl.pbix.expanded/Connections
  inflating: ./nfl.pbix.expanded/Report/StaticResources/SharedResources/BaseThemes/CY22SU03.json
  inflating: ./nfl.pbix.expanded/SecurityBindings

&lt;span class=&quot;token number&quot;&gt;7&lt;/span&gt;-Zip &lt;span class=&quot;token number&quot;&gt;21.07&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;x64&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token builtin class-name&quot;&gt;:&lt;/span&gt; Copyright &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;c&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1999&lt;/span&gt;-2021 Igor Pavlov &lt;span class=&quot;token builtin class-name&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;2021&lt;/span&gt;-12-26

Scanning the drive &lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; archives:
&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt; file, &lt;span class=&quot;token number&quot;&gt;1775&lt;/span&gt; bytes &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt; KiB&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

Extracting archive: .&lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;nfl.pbix.expanded&lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;DataMashup

WARNINGS:
There are data after the end of archive

--
Path &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; .&lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;nfl.pbix.expanded&lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;DataMashup
Type &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;zip&lt;/span&gt;
WARNINGS:
There are data after the end of archive
Offset &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;8&lt;/span&gt;
Physical Size &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;792&lt;/span&gt;
Tail Size &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;975&lt;/span&gt;
Characteristics &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; Local Central

Everything is Ok

Archives with Warnings: &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;

Warnings: &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;
Files: &lt;span class=&quot;token number&quot;&gt;3&lt;/span&gt;
Size:       &lt;span class=&quot;token number&quot;&gt;499&lt;/span&gt;
Compressed: &lt;span class=&quot;token number&quot;&gt;1775&lt;/span&gt;
Unstaged changes after reset:
M       README.md
&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;master 8e7d696&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; pbix commit
 &lt;span class=&quot;token number&quot;&gt;11&lt;/span&gt; files changed, &lt;span class=&quot;token number&quot;&gt;6&lt;/span&gt; insertions&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;+&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
 create mode &lt;span class=&quot;token number&quot;&gt;100644&lt;/span&gt; nfl.pbix.expanded/Connections
 create mode &lt;span class=&quot;token number&quot;&gt;100644&lt;/span&gt; nfl.pbix.expanded/DataMashup.7zexpanded/Config/Package.xml
 create mode &lt;span class=&quot;token number&quot;&gt;100644&lt;/span&gt; nfl.pbix.expanded/DataMashup.7zexpanded/Formulas/Section1.m
 create mode &lt;span class=&quot;token number&quot;&gt;100644&lt;/span&gt; nfl.pbix.expanded/DataMashup.7zexpanded/&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;Content_Types&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;.xml
 create mode &lt;span class=&quot;token number&quot;&gt;100644&lt;/span&gt; nfl.pbix.expanded/DiagramLayout
 create mode &lt;span class=&quot;token number&quot;&gt;100644&lt;/span&gt; nfl.pbix.expanded/Metadata
 create mode &lt;span class=&quot;token number&quot;&gt;100644&lt;/span&gt; nfl.pbix.expanded/Report/Layout
 create mode &lt;span class=&quot;token number&quot;&gt;100644&lt;/span&gt; nfl.pbix.expanded/Report/StaticResources/SharedResources/BaseThemes/CY22SU03.json
 create mode &lt;span class=&quot;token number&quot;&gt;100644&lt;/span&gt; nfl.pbix.expanded/Settings
 create mode &lt;span class=&quot;token number&quot;&gt;100644&lt;/span&gt; nfl.pbix.expanded/Version
 create mode &lt;span class=&quot;token number&quot;&gt;100644&lt;/span&gt; nfl.pbix.expanded/&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;Content_Types&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;.xml&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;After-the-commit&quot; style=&quot;position:relative;&quot;&gt;After the commit&lt;a href=&quot;#After-the-commit&quot; aria-label=&quot;After the commit permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;The PBIX file itself was not committed, but instead the directory containing the files was committed.
&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 824px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/e3e1fbbe188309b6f46f10bfcb3044c0/a0e79/pbi-git-expanded.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 19.6%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAECAIAAAABPYjBAAAACXBIWXMAAAsSAAALEgHS3X78AAAAsklEQVQI1y3KSU7DQBBAUd/E7qpyjzW4Gw/BcRIkpGSBuP91EBjp7f7v2B5teUk9sl7LdGBoA+pANpD1qEVvbX5yvWXdz9r/1Z7MkXWlbNP8EXzzjsuQA4hHDagRZXQsZX3fPiXMETUCR8cJJIFkYETtdLQ11RY4uyLADFz+SXa55eVYH7O81dAm38o5OFZXEkk3yt32b6nXyDuQ4i87+dh82uzyJfWI+QKkQHbqaaK0/AC8lykQOjvrRQAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;pbi git expanded&quot;
        title=&quot;pbi git expanded&quot;
        src=&quot;/static/e3e1fbbe188309b6f46f10bfcb3044c0/a0e79/pbi-git-expanded.png&quot;
        srcset=&quot;/static/e3e1fbbe188309b6f46f10bfcb3044c0/0eb09/pbi-git-expanded.png 500w,
/static/e3e1fbbe188309b6f46f10bfcb3044c0/a0e79/pbi-git-expanded.png 824w&quot;
        sizes=&quot;(max-width: 824px) 100vw, 824px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;h3 id=&quot;On-checkout&quot; style=&quot;position:relative;&quot;&gt;On checkout&lt;a href=&quot;#On-checkout&quot; aria-label=&quot;On checkout permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;So now let’s delete the PBIX and then checkout and see what happens.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token function&quot;&gt;git&lt;/span&gt; checkout&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;/c/git/github/mortie23/pbix-git-version-control/hooks

Creating pbix files&lt;span class=&quot;token punctuation&quot;&gt;..&lt;/span&gt;.

nfl.pbix.expanded
Removing ./nfl.pbix
rm: cannot remove &lt;span class=&quot;token string&quot;&gt;&apos;./nfl.pbix&apos;&lt;/span&gt;&lt;span class=&quot;token builtin class-name&quot;&gt;:&lt;/span&gt; No such &lt;span class=&quot;token function&quot;&gt;file&lt;/span&gt; or directory

&lt;span class=&quot;token number&quot;&gt;7&lt;/span&gt;-Zip &lt;span class=&quot;token number&quot;&gt;21.07&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;x64&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token builtin class-name&quot;&gt;:&lt;/span&gt; Copyright &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;c&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1999&lt;/span&gt;-2021 Igor Pavlov &lt;span class=&quot;token builtin class-name&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;2021&lt;/span&gt;-12-26

Scanning the drive:
&lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt; folders, &lt;span class=&quot;token number&quot;&gt;3&lt;/span&gt; files, &lt;span class=&quot;token number&quot;&gt;499&lt;/span&gt; bytes &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt; KiB&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

Creating archive: DataMashup.7z

Add new data to archive: &lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt; folders, &lt;span class=&quot;token number&quot;&gt;3&lt;/span&gt; files, &lt;span class=&quot;token number&quot;&gt;499&lt;/span&gt; bytes &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt; KiB&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;


Files &lt;span class=&quot;token builtin class-name&quot;&gt;read&lt;/span&gt; from disk: &lt;span class=&quot;token number&quot;&gt;3&lt;/span&gt;
Archive size: &lt;span class=&quot;token number&quot;&gt;535&lt;/span&gt; bytes &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt; KiB&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
Everything is Ok

C:/git/github/mortie23/pbix-git-version-control nfl.pbix
Remove DataMashup C:/git/github/mortie23/pbix-git-version-control/nfl.pbix.expanded/DataMashup&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The PBIX was created and successfully opens in Power BI desktop.&lt;/p&gt;
&lt;h3 id=&quot;Testing-a-change&quot; style=&quot;position:relative;&quot;&gt;Testing a change&lt;a href=&quot;#Testing-a-change&quot; aria-label=&quot;Testing a change permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;We will make one small change and then look at the diff that is created.&lt;/p&gt;
&lt;p&gt;Let’s just change the text in the textbox.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 1157px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/8ad84a79812b0bc0a4ef32163f28ea19/03e15/pbi-git-dashboard-change.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 55.8%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAALCAYAAAB/Ca1DAAAACXBIWXMAABJ0AAASdAHeZh94AAACVklEQVQoz22Qa2/aWBCG/f//x35aqd0uDaFpl6RkAyYkTrgZGwLGJtgQfDkHfM3tWXGifqi0Iz16pbm8Mxrt+bni/f2dt7c3xTGqqsR1XTzPVRrudiyXS4IgYLvdKi2KQvW/vr7+hlaWJS8vL79RVRWz2QzLsrBtm81mo4wnkwmmaaJ3OlRlwf+FliQJSSJI4oQkjonCECkEVVmqK8qyIktTsjRTubIoyPKcpyhFJjEyShFCkh0kWZ6hlUXGy/PHcJ7n5FnGXkqkiImjkINM2IsIKSLSvSBPJXGcMDAdBpMl0/mjekO4C9jttmjbKCXYCoQQpOlBbS+KHLnP1QJ5KNglJWHySw/4m5i+FWCYx1c4yuxpGyjVvnUjPrdCjOGMwHcIAh/XfWS1CVn7a67uN3z6ueOv1gdfWlvO9RmmZWOOB9i2SRCsCfwV68cl2sWd5O/2gfaNw9af8zCfcPa9Q+2Pz/y4HPHDSDntCk67krouOdVDBlaAfreg0xtgDF1u7x/ojxdM7TFa05DUOpKzf9cMhybWZECreUb9z3M+NZ+odyW1q1hxcjRs+4ytBX075MIIuRl6mOYY+8HBHBkfhiedhFpb0LzZsXA8zi7nnFwuOOnEqva1/QuBMfLwHxdsfJfVymE295g7S0zboTdcHQ33qrmufwx874WcX3s0OgGNqxVf25HK13VBrZ1wN7CYWn2mtsnKm+O5DkPzgdbtmi9XEu3CiDjVE75dC0Xj+nix4PLGYzadcD+a0byNVf7IP70nfvZWdA0LezJkPOorw4Ye0+gm/AdibxyCAd+qEwAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;pbi git dashboard change&quot;
        title=&quot;pbi git dashboard change&quot;
        src=&quot;/static/8ad84a79812b0bc0a4ef32163f28ea19/03e15/pbi-git-dashboard-change.png&quot;
        srcset=&quot;/static/8ad84a79812b0bc0a4ef32163f28ea19/0eb09/pbi-git-dashboard-change.png 500w,
/static/8ad84a79812b0bc0a4ef32163f28ea19/1263b/pbi-git-dashboard-change.png 1000w,
/static/8ad84a79812b0bc0a4ef32163f28ea19/03e15/pbi-git-dashboard-change.png 1157w&quot;
        sizes=&quot;(max-width: 1157px) 100vw, 1157px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;Save the PBIX file and then commit. See the key info from the log is that only 1 file was changed:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;master e8f64a9&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; feat: textbox change
 &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;file&lt;/span&gt; changed, &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt; insertions&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;+&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;, &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt; deletions&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;-&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
 rewrite nfl.pbix.expanded/Report/Layout &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;87&lt;/span&gt;%&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 897px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/b2bb7a7b2de4cdca2fa9aad218c2d3ce/d3e70/pbi-git-commit.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 37%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAHCAYAAAAIy204AAAACXBIWXMAABJ0AAASdAHeZh94AAABIUlEQVQoz31Ry27CMBDMvZRSwI4Trx9xHjaBELiUnqr+/09NtaZBqoR6GM2uPJ7dsYsunnG+fmKab5jmDwxpRll5VDpA1c2/YB25iDh/IV2+YfwBhfURabzmw0UolIUoLaRykGrhOxZdWfvHYHIJ5BOUDii2gjCMF0yXG4bDjD6d0Q5Trhc2TYRgs9pjKzTW7+UfrN4EVus9XjcSxU6aLK5NC1m5jNp2GUo30LaHogBZ+aeGm12FnSSIymFfWhTkepAb4LsRYTiBe98e0HRjHsJs/ICX9T5vwlssZlyzmW9HxPGaExU82fiI3gT0FNBSgG0SXGDTI5r+lC9wTT7+bto8wD0nXLjgONr10BRgOLakvPpddEf+JOXAz8ORn4My/wDR0bsYmZCuIQAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;pbi git commit&quot;
        title=&quot;pbi git commit&quot;
        src=&quot;/static/b2bb7a7b2de4cdca2fa9aad218c2d3ce/d3e70/pbi-git-commit.png&quot;
        srcset=&quot;/static/b2bb7a7b2de4cdca2fa9aad218c2d3ce/0eb09/pbi-git-commit.png 500w,
/static/b2bb7a7b2de4cdca2fa9aad218c2d3ce/d3e70/pbi-git-commit.png 897w&quot;
        sizes=&quot;(max-width: 897px) 100vw, 897px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;We still have the issue of Git thinking this file is binary when it is actually a text file with JSON. This may be because the JSON is minified?&lt;/p&gt;
&lt;h2 id=&quot;Things-to-consider&quot; style=&quot;position:relative;&quot;&gt;Things to consider&lt;a href=&quot;#Things-to-consider&quot; aria-label=&quot;Things to consider permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;This doesn’t test many edge cases. It will/may:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Stop you from committing any file with a PBIX extension&lt;/li&gt;
&lt;li&gt;Crash if the structure of the expanded PBIX isn’t what I have tested&lt;/li&gt;
&lt;li&gt;Still track some of the text files as binary&lt;/li&gt;
&lt;/ol&gt;</content:encoded></item><item><title><![CDATA[Git post-receive hook for deployment of a static site]]></title><description><![CDATA[Using a post receive hook for website deployment Other deployment methods Firstly, let’s mention that there are other potentially easier…]]></description><link>https://mortimer.xyz/git-post-receive-hook-for-ssg-deployment/</link><guid isPermaLink="false">https://mortimer.xyz/git-post-receive-hook-for-ssg-deployment/</guid><pubDate>Fri, 06 May 2022 12:00:00 GMT</pubDate><content:encoded>&lt;h1 id=&quot;Using-a-post-receive-hook-for-website-deployment&quot; style=&quot;position:relative;&quot;&gt;Using a post receive hook for website deployment&lt;a href=&quot;#Using-a-post-receive-hook-for-website-deployment&quot; aria-label=&quot;Using a post receive hook for website deployment permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h1&gt;
&lt;h2 id=&quot;Other-deployment-methods&quot; style=&quot;position:relative;&quot;&gt;Other deployment methods&lt;a href=&quot;#Other-deployment-methods&quot; aria-label=&quot;Other deployment methods permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Firstly, let’s mention that there are other potentially easier ways to do this type of deployment. A Github action or using a service such a Netlify can do the build and deployment for you.&lt;/p&gt;
&lt;h2 id=&quot;How-to-do-this&quot; style=&quot;position:relative;&quot;&gt;How to do this&lt;a href=&quot;#How-to-do-this&quot; aria-label=&quot;How to do this permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;This approach is a lower level approach where all you need need is SSH access to the webserver you are hosting the site on.&lt;/p&gt;
&lt;p&gt;Let’s say that we use a static site generator to build our website. The build process creates the static site in a &lt;code class=&quot;language-text&quot;&gt;public&lt;/code&gt; directory in the root of the repo.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 317px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/5e39e48c3cff7631c67ea6a6978b6b3b/66d24/git-hooks-ssg-public.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 32.49211356466877%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAGCAIAAABM9SnKAAAACXBIWXMAAAsSAAALEgHS3X78AAAAsUlEQVQY043NTQrCMBQE4B7FvOQlbdLmJSmx/65UXIobLyDiITyB4CG8gjtvpyi40C4KA7OZj0kACXhc7+rb/Wz9Qpkqd73MYlY0Wd4Yag11SscZt0zQT5JPoampWgE6JgjQg/SA7hsm3L98Y/SIRbk5Hq4PbZs0r7XtUlONrkeeQZDQ0TVLrgKg5zKA9JMwIHEeh21/uuxNiL6dgyQm7NRnJhyaUHaD1EHlAZCmyBd+AuAcPdZGXicKAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;git hooks ssg public&quot;
        title=&quot;git hooks ssg public&quot;
        src=&quot;/static/5e39e48c3cff7631c67ea6a6978b6b3b/66d24/git-hooks-ssg-public.png&quot;
        srcset=&quot;/static/5e39e48c3cff7631c67ea6a6978b6b3b/66d24/git-hooks-ssg-public.png 317w&quot;
        sizes=&quot;(max-width: 317px) 100vw, 317px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;Usually the default would be to configure the &lt;code class=&quot;language-text&quot;&gt;public&lt;/code&gt; generated site to be ignored by git. For this process we would need to include the generated site.&lt;/p&gt;
&lt;h2 id=&quot;Directory-structure-on-your-webserver&quot; style=&quot;position:relative;&quot;&gt;Directory structure on your webserver&lt;a href=&quot;#Directory-structure-on-your-webserver&quot; aria-label=&quot;Directory structure on your webserver permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;We will use the home directory of the user account you are using to SSH (so it has permissions to access the required files).&lt;/p&gt;
&lt;p&gt;We will create a git directory and a bare repo for our site.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;# Change to home directory&lt;/span&gt;
&lt;span class=&quot;token builtin class-name&quot;&gt;cd&lt;/span&gt; ~
&lt;span class=&quot;token comment&quot;&gt;# Create a tmp directory to be used later&lt;/span&gt;
&lt;span class=&quot;token function&quot;&gt;mkdir&lt;/span&gt; tmp
&lt;span class=&quot;token comment&quot;&gt;# Create git directory and change into it&lt;/span&gt;
&lt;span class=&quot;token function&quot;&gt;mkdir&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;git&lt;/span&gt;
&lt;span class=&quot;token builtin class-name&quot;&gt;cd&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;git&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;# Initialise a bare repo&lt;/span&gt;
&lt;span class=&quot;token function&quot;&gt;git&lt;/span&gt; init &lt;span class=&quot;token parameter variable&quot;&gt;--bare&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;You will see a directory structure created which should contain a &lt;code class=&quot;language-text&quot;&gt;hooks&lt;/code&gt; directory.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 512px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/19c47e1771a607df8b5ba866c7dd6e72/06341/git-hooks-ssg-bare.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 35.6%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAHCAYAAAAIy204AAAACXBIWXMAAA7DAAAOwwHHb6hkAAABXUlEQVQoz12RzXKbQBAGeQcJjISAZVlgdkH8S7aUKqtys/3+79MpVkoOOcy1q3u+oJeJc9NjaiHXQhQr3o4loxPWs3AbW5rScFIjdvrieBJUdaGorhh7R/rf6OaDMMoJ9ycCqxs6LZTakKWaKMo42oWxc1w24PQCFhNu+SFJhaJ+R9dXmnrk3N8w1UCWCYdDQeC00BuHKStUZjww1i1TZ1m6J1CMIckH3PxNklqUWVFmYRjuiF1J0xqlHIeDJujt/EyuhKxoiI6at7T2ydfect+ApSF9GW7JW2Ipd+T8oOke7PcndruEMEwJnGkZ645BWvLcEOfCsR4YW8vSCh+j88D/DfNyppaVRi6E+8SXbRdM3Tu9mygbhzItYawIdyem9mn4a34a+h/OPx7of9jcMO6Tqn2QZM7beeDgVs52xGzA0hHFBWGYvVa2fmV5rfwX+Fz5QtV++vT4UP4D/gGZqMsJ3Ey3PwAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;git hooks ssg bare&quot;
        title=&quot;git hooks ssg bare&quot;
        src=&quot;/static/19c47e1771a607df8b5ba866c7dd6e72/06341/git-hooks-ssg-bare.png&quot;
        srcset=&quot;/static/19c47e1771a607df8b5ba866c7dd6e72/0eb09/git-hooks-ssg-bare.png 500w,
/static/19c47e1771a607df8b5ba866c7dd6e72/06341/git-hooks-ssg-bare.png 512w&quot;
        sizes=&quot;(max-width: 512px) 100vw, 512px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;h2 id=&quot;The-hooks-shell-script&quot; style=&quot;position:relative;&quot;&gt;The hook’s shell script&lt;a href=&quot;#The-hooks-shell-script&quot; aria-label=&quot;The hooks shell script permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;In the hooks directory we will create a &lt;code class=&quot;language-text&quot;&gt;post-receive&lt;/code&gt; hook script.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token builtin class-name&quot;&gt;cd&lt;/span&gt; hooks
&lt;span class=&quot;token function&quot;&gt;touch&lt;/span&gt; post-receive
&lt;span class=&quot;token function&quot;&gt;chmod&lt;/span&gt; +x post-receive&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Then paste the contents of the following script in there. Replace the &lt;code class=&quot;language-text&quot;&gt;&amp;lt;username&gt;&lt;/code&gt; with the username that you will SSH to the server.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token shebang important&quot;&gt;#!/bin/bash&lt;/span&gt;

&lt;span class=&quot;token builtin class-name&quot;&gt;unset&lt;/span&gt; GIT_DIR
&lt;span class=&quot;token builtin class-name&quot;&gt;set&lt;/span&gt; GIT_DIR &lt;span class=&quot;token builtin class-name&quot;&gt;.&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;# Change into your bare repo&lt;/span&gt;
&lt;span class=&quot;token builtin class-name&quot;&gt;cd&lt;/span&gt; /home/&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;username&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;/git/
&lt;span class=&quot;token comment&quot;&gt;# Set the work tree (the files in the repo) to the temp directory we created&lt;/span&gt;
&lt;span class=&quot;token function&quot;&gt;git&lt;/span&gt; --work-tree&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;/home/&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;username&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;/tmp checkout &lt;span class=&quot;token parameter variable&quot;&gt;-f&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;# Sync the files in the public directory of the repo to the root of the Nginx webserver&lt;/span&gt;
&lt;span class=&quot;token function&quot;&gt;rsync&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-r&lt;/span&gt; /home/&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;username&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;/tmp/public/ /var/www/html&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2 id=&quot;Add-your-webserver-as-a-git-remote&quot; style=&quot;position:relative;&quot;&gt;Add your webserver as a git remote&lt;a href=&quot;#Add-your-webserver-as-a-git-remote&quot; aria-label=&quot;Add your webserver as a git remote permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The repository on your local machine will have an origin remote (assuming you started by creating it on a remote git server like Github). You will need to add a remote which points to your webserver.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token function&quot;&gt;git&lt;/span&gt; remote &lt;span class=&quot;token function&quot;&gt;add&lt;/span&gt; webserver ssh://&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;username&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;@&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;server&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;:&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;ssh-port&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;//home/&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;username&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;/git&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2 id=&quot;Deploying-change-to-the-site&quot; style=&quot;position:relative;&quot;&gt;Deploying change to the site&lt;a href=&quot;#Deploying-change-to-the-site&quot; aria-label=&quot;Deploying change to the site permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;When you have changes to your site build to the &lt;code class=&quot;language-text&quot;&gt;public&lt;/code&gt; directory you can deploy them to the server using:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token function&quot;&gt;git&lt;/span&gt; push webserver master&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;</content:encoded></item><item><title><![CDATA[Raspberry Pi time lapse server]]></title><description><![CDATA[Building a time lapse server with a Raspberry Pi and a webcam I had a Raspberry Pi lying around that I use to use as a media server, and an…]]></description><link>https://mortimer.xyz/raspberrypi-timelapse/</link><guid isPermaLink="false">https://mortimer.xyz/raspberrypi-timelapse/</guid><pubDate>Fri, 18 Mar 2022 12:00:00 GMT</pubDate><content:encoded>&lt;h1 id=&quot;Building-a-time-lapse-server-with-a-Raspberry-Pi-and-a-webcam&quot; style=&quot;position:relative;&quot;&gt;Building a time lapse server with a Raspberry Pi and a webcam&lt;a href=&quot;#Building-a-time-lapse-server-with-a-Raspberry-Pi-and-a-webcam&quot; aria-label=&quot;Building a time lapse server with a Raspberry Pi and a webcam permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h1&gt;
&lt;p&gt;I had a Raspberry Pi lying around that I use to use as a media server, and an old webcam that I used before laptops came with webcams. I had an idea to create a time lapse server.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 700px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/65725a17c21e9fbcf75de064b9b0ca72/40619/timelapse-camera.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 75.2%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDAAEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQH/2wBDAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQH/wgARCAAPABQDASIAAhEBAxEB/8QAGAAAAgMAAAAAAAAAAAAAAAAAAAgGBwn/xAAWAQEBAQAAAAAAAAAAAAAAAAAFBAb/2gAMAwEAAhADEAAAAXlp1BWcOv09I6ZVf//EABwQAAEFAAMAAAAAAAAAAAAAAAUBAgMGBwAEFP/aAAgBAQABBQJ0qt4U0ietWL0xPQcO0C8RVnDCUXaECRQEf//EACERAAIABQQDAAAAAAAAAAAAAAERAAIDEiEEBRNBIjGB/9oACAEDAQE/AaUmq1VvFSIF4uqeXGB25wB9WV1A24oXTl4alYeGjcGPawI//8QAIREAAgEEAQUBAAAAAAAAAAAAAQIRAwQSIQAFEyMxQWL/2gAIAQIBAT8BuEsLBne5uA3hYpSxp95n+FaTM4P5yGMkSY3w9U2cKQKycS7wxWdZABgDHsBjBnZ5/8QAJhAAAgICAQIFBQAAAAAAAAAAAQIDBAUSEQATBhQhIlIxUXGSof/aAAgBAQAGPwLYqwT5FWC/sRx/esZj572FgwN/mBpFgvZHJV740YvbSvH24azB1ghijYzM7CcsIkm0DqysrDlWUhgR9wR6EfjpXhs5HI1JHaISX82kdcuOdlaF7Msnt+rHy2rEaqvB56qy+JcpCcbG7PJhcfYySpNvyx4swWqfabYg7qny9jBuOoMXi6i1aVfftQ9yeXUyO0kh3mklkOzszHlz6nr/xAAZEAEBAQEBAQAAAAAAAAAAAAABESEAMVH/2gAIAQEAAT8hIMIEhuxAeE31UJb7toAogGNiE3i6j6p26Sewy6BdPITtd8KeggkBJ4YGyJGpNGH3qshnf//aAAwDAQACAAMAAAAQAN//xAAYEQEBAQEBAAAAAAAAAAAAAAABESEAQf/aAAgBAwEBPxAQOARIYeBqJuCCSLkGyLjGklNFEBQc7//EABcRAQEBAQAAAAAAAAAAAAAAAAERMSH/2gAIAQIBAT8QHmnW8DOtRM2DQIMCTG4rHCRgIC//xAAYEAEBAQEBAAAAAAAAAAAAAAABEQAhQf/aAAgBAQABPxAckU16C1ILELGEKODwiSYp+vLTbK5Knpq3oxNLRDikMKynxJKN7VydbhakXcwPWTmfd7ysPPuqD//Z&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;timelapse camera&quot;
        title=&quot;timelapse camera&quot;
        src=&quot;/static/65725a17c21e9fbcf75de064b9b0ca72/40619/timelapse-camera.jpg&quot;
        srcset=&quot;/static/65725a17c21e9fbcf75de064b9b0ca72/953fe/timelapse-camera.jpg 500w,
/static/65725a17c21e9fbcf75de064b9b0ca72/40619/timelapse-camera.jpg 700w&quot;
        sizes=&quot;(max-width: 700px) 100vw, 700px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;h2 id=&quot;What-is-does&quot; style=&quot;position:relative;&quot;&gt;What is does&lt;a href=&quot;#What-is-does&quot; aria-label=&quot;What is does permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;It simply takes a photo every minute and saves it to a directory within the path of a webserver (Nginx).&lt;/p&gt;
&lt;p&gt;Then a basic webpage takes input from the user (me) a time stamp as a parameter and starts playing the images at 1 image per second (60 times faster than they were taken).&lt;/p&gt;
&lt;h2 id=&quot;Details-on-setting-it-up&quot; style=&quot;position:relative;&quot;&gt;Details on setting it up&lt;a href=&quot;#Details-on-setting-it-up&quot; aria-label=&quot;Details on setting it up permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Firstly I wiped the memory card of the Raspberry Pi that original had the OpenELEC (a Kodi distribution for Raspberry Pis) operating system and installed Rasbian.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;$&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; lsb_release &lt;span class=&quot;token parameter variable&quot;&gt;-a&lt;/span&gt;
Distributor ID: Raspbian
Description:    Raspbian GNU/Linux &lt;span class=&quot;token number&quot;&gt;11&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;bullseye&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
Release:        &lt;span class=&quot;token number&quot;&gt;11&lt;/span&gt;
Codename:       bullseye&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;I then installed&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;git&lt;/li&gt;
&lt;li&gt;Python 3&lt;/li&gt;
&lt;li&gt;Nginx&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I created a Github repository and cloned it down to the home directory of the logged in user (just the default &lt;code class=&quot;language-text&quot;&gt;pi&lt;/code&gt; user).
I created a virtual Python environment for development.&lt;/p&gt;
&lt;p&gt;In the Nginx webserver root directory I copied the &lt;code class=&quot;language-text&quot;&gt;webserver&lt;/code&gt; files.&lt;/p&gt;
&lt;h2 id=&quot;Running-the-service&quot; style=&quot;position:relative;&quot;&gt;Running the service&lt;a href=&quot;#Running-the-service&quot; aria-label=&quot;Running the service permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;To ensure the Python service is running always in the background I ran.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token function&quot;&gt;nohup&lt;/span&gt; python photo-service.py &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;This will take a photo every minute and save it to the Nginx hosted webserver at &lt;code class=&quot;language-text&quot;&gt;/var/www/html/webcam/webcam-yyyymmddhhmm.jpg&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;Then, knowing the IP address of the Raspberry Pi on our network, we can use a browser to see the photos.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 813px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/c150d49c1e6b04c942b605f31a28bb31/765bd/timelapse-webserver.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 93%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAATCAYAAACQjC21AAAACXBIWXMAABJ0AAASdAHeZh94AAAFA0lEQVQ4y22UW0zTBxTG/88+7MGRqbvHGZc5zdQ5ojMSr4ggWnRDwYFFBB3IRR276DAbmzh1WzBesDhQvKBDx1SuCgIVlIEg0FqgpRRoof33TgtFKchvEbfFh33Jl3znnJwvOTk5R3C53Hg8w4x4vbyIkZERzHYn+j4TvQYj/SYLZqtjgkbRSp/R/ELO/l9NGB31TjR7PB6Gh4cxmy3Y7XbsdgeiaKZXb0A0Wxgc8uAeHMI9OIjb7X7OZ/rf3D9asDlcmEQLzgEXo2NPcbmHJuLRsXGejsPY03HGxmEcJuIXMf4/FIqvZtOhaECvUzPsMuMZMOGyGRgZsuL12PB67Hg9DrxDdkaHnXgceszaB1h0D3nsMvJkUOSxuw+Ps5snQ2aEbX6TqL+ejq2znH7lDURVMdb2W1jabmFtL8fWUYVNLcemuYulrYz6y7uRnwpFfjIUY8vvmB7lY2jOorPmR4zKywg7VvpQeCoWc8c1+hT5iG1FWDpuY9NUYu+swdF1H4e2DqeuAVd3Hf3NeXTfO4WxOR+b+jai6ip9rdlo7uzH0HgWIXb5ZC6kSTAqLmBovYSouo61vQy7phKHtgaH9h7unkac2lo0NXnoHxbi6qnH0XUXa3sJoiqPnrojKApi6W04h7BzpQ+Ze5aifyCjr/U8ouoPrB2lzw077zKg+wujopTTX60jae0MDkZ+yKNbx3F2VWJWXcXQdAx1xVc8KtqDofkyQrz/KxzZPh9t7a8YlbkTI1g7irFrKrCrq3jc30R5VjJfBL2JLHo+BYm+1J+WYlXn09d0DE3lN6jvHKCtNAV9Qy5CQsBUvts8E2VZGkZlDiblJSztBVg7inBoKxCVhZxLDeZMnC/5CQuRSedRdXgjupp0Oqv2o674Fk3l92jlaVg6i58bfi15m7rLSfS3ZNLfegaz6iJm1RWc2puo5SfZ5j+dpMDp3EheRF7sR1xJWIJcFklb8V40dw7QU3cUq7qYQXMrwq6AqewJep3SE1vob8xA33QMoyITkyILS1sOPfUZpITPY8viaZyOmMP5mA/J3LqAqmObaM6LQludjk19E4dOzmNnP0Lc6qkkrp5GfroEUXGavuZMROVvmB9lIyqzEBWn6K0/xJ3jGzkaMp3U4BlkfDYfecYGGrLDMTyQ4ey+i1NXy4jb8mzLU4hfNQ1Z8kK6bn+OtiIOnTyB7ppEdPJddFbE0lsZRVPuJ8i2fsAvYbMpTl9Hy/lwHl6MQF3+A25DIy5DC08GTAgxy32I93+Vw9L3abkSQlvhZjQVW9FWRtEtl9JTLaX6zAYyEtawPeB99kpmk5MSRMHBEJRXY+ksS8Gtb8Rj0THiFhGil/oQ5/8a3216l/KT66g9F4SmPIquyhiaC2LITV1PasRykkIWEek/B8mSmUSumsO+MD9OJAdTcCQMk6oM7+Czm3ciRC+bwq6AN/ky+C2qz8WjKIhEXSZF+aeUtOhVhPrNImzFbKRr5hG1Zj5Ra+YRvmoOcRJfDkSs4PCOQEpk+3kyYGVsxIuw2XcS2/wms3PJS1w7EkptjoSOos0UZgSxdsHrLJrlw8L3XmbZ3GmsXvAGgb5vsf7j6YQsfoeYgLl8He5HVkoobbWlEy9N+H7Han5KDObnhEAKZSmUnE2gqzqVkuzdJEolxEYEsS0skB0Ra9kllZAUvZG9sZ+yL34LacmRpO+NIvfQHu4XXpx4iH8D/d95eu9vqhwAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;timelapse webserver&quot;
        title=&quot;timelapse webserver&quot;
        src=&quot;/static/c150d49c1e6b04c942b605f31a28bb31/765bd/timelapse-webserver.png&quot;
        srcset=&quot;/static/c150d49c1e6b04c942b605f31a28bb31/0eb09/timelapse-webserver.png 500w,
/static/c150d49c1e6b04c942b605f31a28bb31/765bd/timelapse-webserver.png 813w&quot;
        sizes=&quot;(max-width: 813px) 100vw, 813px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;h2 id=&quot;Time-lapse-results&quot; style=&quot;position:relative;&quot;&gt;Time lapse results&lt;a href=&quot;#Time-lapse-results&quot; aria-label=&quot;Time lapse results permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;This is a time lapse of me at my desk played back at 5 frames per second.
&lt;img src=&quot;/e9d15f4f45658617545f634808879e66/timelapse-fast.gif&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;This is a time lapse at me buring the midnight oil, coding away at 10 frames per second.
&lt;img src=&quot;/5939a1016aa42a7fc10e442a59f21f3d/timelapse-night-coding.gif&quot; alt=&quot;&quot;&gt;&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Dynamic Images in Power BI Dashboards]]></title><description><![CDATA[Dynamic images in Power BI Getting images to be dynamic, such as changing in reaction to an change in the selected value of a slicer seems…]]></description><link>https://mortimer.xyz/powerbi-dynamic-images/</link><guid isPermaLink="false">https://mortimer.xyz/powerbi-dynamic-images/</guid><pubDate>Thu, 17 Mar 2022 12:00:00 GMT</pubDate><content:encoded>&lt;h1 id=&quot;Dynamic-images-in-Power-BI&quot; style=&quot;position:relative;&quot;&gt;Dynamic images in Power BI&lt;a href=&quot;#Dynamic-images-in-Power-BI&quot; aria-label=&quot;Dynamic images in Power BI permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h1&gt;
&lt;p&gt;Getting images to be dynamic, such as changing in reaction to an change in the selected value of a slicer seems to have limited out of the box solutions in Power BI (at the time of this blog).&lt;/p&gt;
&lt;p&gt;The solutions I have seen involve sourcing the images from a URL (already hosted images). Which is fine if you have access to a webserver to host them, but if you don’t you may feel you are stuck.&lt;/p&gt;
&lt;h2 id=&quot;Another-way-using-HTML&quot; style=&quot;position:relative;&quot;&gt;Another way using HTML&lt;a href=&quot;#Another-way-using-HTML&quot; aria-label=&quot;Another way using HTML permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;If you don’t have access to a webserver to host your images, you can host the images as text in two ways.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 900px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/6d28da00461146a43306d9f86264dd68/e7448/pbi-html-content-powerbi-viz.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 41.4%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAICAYAAAD5nd/tAAAACXBIWXMAABJ0AAASdAHeZh94AAABfklEQVQoz3WST2sUQRDF55t5FPwEgkcPwWuIJ2/GgwRBFITowSUnD3r0IB5yzCHgnwQWd4XEbMSsO909m9mZ6Znunp6fTE0iREzBo141XY/iVSWtq5l/fM3Z++ekuyP8yuB8wGiNUillWQpP0xTvPX10XSf4XyTuPOXzg5v82Nng+NENqtkYF0Epg9aGPM/JskyEY4xXBP+FCIYqZ/z0LvPdEb9G92jUT3wHq9JT2Ya6tlcmuE7sr2DsOqYv1xlv3uLk1RqxbSmKgul0wunpjNiByiylDeSFo6g810UvmgzM4WYH4FZSeufQWnO+XFLXNU3TYK0VXls71FUluUcl3A0T7nw4Zv3FVx6/PWFj+5Bn76ZAFO+c9+Jbj7ZtadtACEF4lHpA/3bpb7L15hu3H+6x9mSfO5t73N8+IHjH7/lcNitYXOR0IZP3XGk1ZKUv/ixkeclklvNpYvjyPePwaMnRWUkIXj5pY1BqaOxPxxgjTZc2NE2Nc07Q1/1Z/QHluFbyINnEZwAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;HTML Content&quot;
        title=&quot;HTML Content&quot;
        src=&quot;/static/6d28da00461146a43306d9f86264dd68/e7448/pbi-html-content-powerbi-viz.png&quot;
        srcset=&quot;/static/6d28da00461146a43306d9f86264dd68/0eb09/pbi-html-content-powerbi-viz.png 500w,
/static/6d28da00461146a43306d9f86264dd68/e7448/pbi-html-content-powerbi-viz.png 900w&quot;
        sizes=&quot;(max-width: 900px) 100vw, 900px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;h3 id=&quot;The-images&quot; style=&quot;position:relative;&quot;&gt;The images&lt;a href=&quot;#The-images&quot; aria-label=&quot;The images permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;h4 id=&quot;SVG&quot; style=&quot;position:relative;&quot;&gt;SVG&lt;a href=&quot;#SVG&quot; aria-label=&quot;SVG permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;To demonstrate using SVGs I sourced some from &lt;a href=&quot;https://fontawesome.com/download&quot;&gt;Font Awesome&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 936px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/d85112a2c0c5d419b2631d91fdc88416/6d612/pbi-fontawesome-svg.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 54.199999999999996%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAALCAYAAAB/Ca1DAAAACXBIWXMAABJ0AAASdAHeZh94AAACGUlEQVQoz42Q3UtTcRjHz7/RVVQiSLPMMSpNEzVqm+k2XXOyTSNoogvP8mV0Fd1Ed5Hdlv4LWVddmGl3kTDoxdWghKhouZ2d7by/feP5HR0SJT3w4fs95/n9nvOcLzc+dQdDyXkMjs0hlJjH8HgOkWQOkVQO0YlbCCcX4L/CI8DI7sLvUx7BWBb94QwmZ++BI7O08RwDV+fgOTOKvnAG5wcm0RuaxqXoDLqDaRxpC6PJO4xj7REG+aOn3HdN3giafSM41BJg97izF6/hwsgNtJ4bg7cnBV/fBFpOx+DpjKPZF2Weem3dCaatnXGc7ErgeEeceU9HHO09KRw+MYSuYBpcIn0bvYNT8Ed5hBILuByfRSCWZb8RHL3J1B+dYdv6d3E933imSPpDGVzn74KTZAXVWh2qqqFWlyBJMlOiLskM07Jg2TYs6wBsG4ZhgpMkBT9LFdTEOgRBZNBwXTfYAeJ/y7ZtcHRRklWomg5F1aBqGkzTguM47IDtOGwD8u6Wbu9vUI8ryVUsF9ew/GkNS4UXePxhFY+KqyiWv0ORVOyUBVQEEaVfFeYpIioasFd7nj7M/ahW8GB9BQ9fPcPixlMsvlzB/fUnePd1G4XCF7zJbyH/9iM281t4vfken7e/NYbspzGQMhIrNQhlEaJQhyjUIOy4OSqKxjYiZFllqmn6PzN0B5omdF1n2VGeBHnDMA4M/09oS8r+N4l6ttUwsN+MAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Font Awesome SVG&quot;
        title=&quot;Font Awesome SVG&quot;
        src=&quot;/static/d85112a2c0c5d419b2631d91fdc88416/6d612/pbi-fontawesome-svg.png&quot;
        srcset=&quot;/static/d85112a2c0c5d419b2631d91fdc88416/0eb09/pbi-fontawesome-svg.png 500w,
/static/d85112a2c0c5d419b2631d91fdc88416/6d612/pbi-fontawesome-svg.png 936w&quot;
        sizes=&quot;(max-width: 936px) 100vw, 936px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;h4 id=&quot;Base64&quot; style=&quot;position:relative;&quot;&gt;Base64&lt;a href=&quot;#Base64&quot; aria-label=&quot;Base64 permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;To demonstrate using Base64 encoding I got a png of an emoji and used a &lt;a href=&quot;https://www.base64-image.de/&quot;&gt;Base64 Image converter&lt;/a&gt; to convert to Base64.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 972px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/1681c38ad63bfed3de4a9661e0c27654/84a58/pbi-base64-encode.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 71.2%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAOCAYAAAAvxDzwAAAACXBIWXMAABJ0AAASdAHeZh94AAACqElEQVQ4y42Ty28bVRSH5x/sf8CCJSzYdNl2Q8qqKhJSF0gti0qhCJKASJHbRBWlDysmtevxa172jMd22nnYM2N7HMg87Hxorp0QQCBG+nTPPefM71ydoyMFgY+qNomCMf/1rZYrFosF5yDOJElZLldkaUaapsznsciTHGfAYNjDDT386QRv/B7PHeF5J5e47khwYTvOEGfjK+yrp6TpMt1ui8b9z6l/eQetcYSmN9C0Opomb6iLnF6vjWG06F7QXaOL/HWuZFoK9V8OePbJh/z8wTVef3UPy1IwjOblD2uxDqbZwbJUbFvH7uviXhQp4hf5Um+g0/jxGw6vf0Tp+se8uruF2f+n4J/CbUxTuVJA2aCuBXVdxu2pyM8OOXpaIhpYGJpMt6hsNFHVOu12lXb7Da3WmmbzVxTlLeXyAY8ff83TJzuUSt/S6VSR9OpLqvs77P1UYvu7XSqHT6iXdtEtZSP4llbrmEajcoksHwnBg4NdHjy4w/b2PR4+/ELEpG6jQqW0x+3PbnPr5g22bt3g7tanHL8p07cUMZDilUUL/k4RU5SaoChc+CTdaNG3Nfb3d/h+7xHVWoVWp0mnU7uc3L/186KnVwcjFZeiqbJcoVYri9eY5l8n938QwsVQJoGH675nsYg5P1+RZQlpmpBlqbDzZUa68eV5Rp6nwr5KkXf6e4LjvkOKogDbHjKdzsmzFWmSkyQZv52ekZ3lzKKYPC1WbMl8FhPHp2RZTipWbk2xfmfJkvHER4pmDqr9En34mpOwJhj4x7Tt57zQdjhsPKLc/YFRWMUYvUIdvsCLuozDEX5k44cDvKBPFL8jDB2kIHTwQxtnbBLOR4TxiGA2xAsthmOVk8BgNFEJ50MRn0wHRHOHaOYTzT3CqUc0c5nFEzz/hD8ApCHh3p3zNuwAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Base64 Encode&quot;
        title=&quot;Base64 Encode&quot;
        src=&quot;/static/1681c38ad63bfed3de4a9661e0c27654/84a58/pbi-base64-encode.png&quot;
        srcset=&quot;/static/1681c38ad63bfed3de4a9661e0c27654/0eb09/pbi-base64-encode.png 500w,
/static/1681c38ad63bfed3de4a9661e0c27654/84a58/pbi-base64-encode.png 972w&quot;
        sizes=&quot;(max-width: 972px) 100vw, 972px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;h3 id=&quot;Database-table&quot; style=&quot;position:relative;&quot;&gt;Database table&lt;a href=&quot;#Database-table&quot; aria-label=&quot;Database table permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;I will demo this using a MySQL database.
&lt;a href=&quot;https://github.com/mortie23/powerbi-dynamic-image/blob/master/base-table.sql&quot;&gt;SQL code from Github repository&lt;/a&gt;&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;sql&quot;&gt;&lt;pre class=&quot;language-sql&quot;&gt;&lt;code class=&quot;language-sql&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;DROP&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;TABLE&lt;/span&gt; &lt;span class=&quot;token identifier&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;`&lt;/span&gt;powerbi_image&lt;span class=&quot;token punctuation&quot;&gt;`&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;CREATE&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;TABLE&lt;/span&gt; &lt;span class=&quot;token identifier&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;`&lt;/span&gt;powerbi_image&lt;span class=&quot;token punctuation&quot;&gt;`&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;token identifier&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;`&lt;/span&gt;picture_id&lt;span class=&quot;token punctuation&quot;&gt;`&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;NOT&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;NULL&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;AUTO_INCREMENT&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token identifier&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;`&lt;/span&gt;picture_name&lt;span class=&quot;token punctuation&quot;&gt;`&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;varchar&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;100&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;NULL&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;DEFAULT&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;NULL&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token identifier&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;`&lt;/span&gt;picture_type&lt;span class=&quot;token punctuation&quot;&gt;`&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;varchar&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;100&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;NULL&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;DEFAULT&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;NULL&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token identifier&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;`&lt;/span&gt;picture_source_code&lt;span class=&quot;token punctuation&quot;&gt;`&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;longtext&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;NULL&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;DEFAULT&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;NULL&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;PRIMARY&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;KEY&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token identifier&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;`&lt;/span&gt;picture_id&lt;span class=&quot;token punctuation&quot;&gt;`&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;COLLATE&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;latin1_swedish_ci&apos;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;ENGINE&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;MyISAM
&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;-- The full code containing the SVG and Base64 images&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;-- is in this associated repo which is linked above&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;INSERT&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;INTO&lt;/span&gt; &lt;span class=&quot;token identifier&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;`&lt;/span&gt;powerbi_image&lt;span class=&quot;token punctuation&quot;&gt;`&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token identifier&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;`&lt;/span&gt;picture_name&lt;span class=&quot;token punctuation&quot;&gt;`&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token identifier&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;`&lt;/span&gt;picture_type&lt;span class=&quot;token punctuation&quot;&gt;`&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token identifier&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;`&lt;/span&gt;picture_source_code&lt;span class=&quot;token punctuation&quot;&gt;`&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;values&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;smile&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;svg&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;&amp;lt;html&gt;&amp;lt;svg&gt;&amp;lt;path d=&quot;&quot;/&gt;&amp;lt;/svg&gt;&amp;lt;/html&gt;&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;icecream&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;svg&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;&amp;lt;html&gt;&amp;lt;svg&gt;&amp;lt;path d=&quot;&quot;/&gt;&amp;lt;/svg&gt;&amp;lt;/html&gt;&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;avocado&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;base64&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;&amp;lt;html&gt;&amp;lt;img src=&quot;data:image/png;base64,&quot;/&gt;&amp;lt;/html&gt;&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;The-Power-BI-dashboard&quot; style=&quot;position:relative;&quot;&gt;The Power BI dashboard&lt;a href=&quot;#The-Power-BI-dashboard&quot; aria-label=&quot;The Power BI dashboard permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;I connected Power BI to the MySQL table and on the canvas I have added three visualizations:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;slicer (sourcing from the picture_name)&lt;/li&gt;
&lt;li&gt;card (sourcing the picture_type)&lt;/li&gt;
&lt;li&gt;HTML content (sourcing the picture_source_code)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The result is an image which updates in reaction to the changing selected value in the slicer.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 202px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/9b9e728ecf8212d84ca7ad887292e277/37144/pbi-dashboard-avocado.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 40.5940594059406%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAICAYAAAD5nd/tAAAACXBIWXMAABJ0AAASdAHeZh94AAABM0lEQVQoz42SUW+CMBSF+cv7V3td4ov6MkPmAiy6CINsTBMwoCRUITCglXqWXgXdYrI1IS23p1/PuakGAIfDAXVdQwgBITjatqUa5xxN00BKqWQ4Ho/4a2hKtFx+wnXfMJvNYNs2wjCE4zgwDINqRVH0BxaLBV4sC57nkdayLNKZpgGWptCUKAgCJElCDpUj3jQoi4LW6kL1dS5938doOMR0+oTJ5BHj8Qiv8zlM00QURSfgYDCAruuoqi+EYYA4irBaLcFY+iPqvyMrZ6pnKtput8N+vwdjjObfsGu3LLGRpi6tpWxpjxxSTM6R5zniOCZn6/Ua2+32JvA0BB7u7/Dx/nwGyguwqiqCKqDq5Waz6SN0Pbz+7+ZG1Dib7esE7CKXZYk8z5BlGT2da9gt6K1LvgGquWT/XBWAmQAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Avocado&quot;
        title=&quot;Avocado&quot;
        src=&quot;/static/9b9e728ecf8212d84ca7ad887292e277/37144/pbi-dashboard-avocado.png&quot;
        srcset=&quot;/static/9b9e728ecf8212d84ca7ad887292e277/37144/pbi-dashboard-avocado.png 202w&quot;
        sizes=&quot;(max-width: 202px) 100vw, 202px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;br&gt;
&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 196px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/35aa81c672de672824c725f56f87deba/01ced/pbi-dashboard-icecream.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 41.83673469387755%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAICAYAAAD5nd/tAAAACXBIWXMAABJ0AAASdAHeZh94AAABCUlEQVQoz21Ry4qDQBD0M/wkPzV7T8CDdw8jigcVjE9QBB8XY9a3FXqCyaxrQ9lO2VPdXUoAEMcRDMbgeR5M04Tv+wiCAJZlgTGGKIqoDNu2cVB03QOO4yDPc3iui77vOS/Ro21bjmEY+Ieu6z4gjnAUbJoGmqbBMBh+LhdUVfUWFIvqukaSJLzr/e4jSWKs6/oRE0M8U8N5nr8TpmnK16UimnAcRwzDO4uCO3bOdV0oisLvUxDPBXVdx/V2wzRNiMIQRVFwD8Mw4GuLE1FelgX7PVmWYdv2V1DsTNORN2VZnvp2XJcEVFX9w0u7BwT6MeRflmX4fT7/TXaWj02lM5PPuLP3fX3x/AL2w2On89R4ugAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Icecream&quot;
        title=&quot;Icecream&quot;
        src=&quot;/static/35aa81c672de672824c725f56f87deba/01ced/pbi-dashboard-icecream.png&quot;
        srcset=&quot;/static/35aa81c672de672824c725f56f87deba/01ced/pbi-dashboard-icecream.png 196w&quot;
        sizes=&quot;(max-width: 196px) 100vw, 196px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;br&gt;
&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 188px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/518b098e88f5f7dbf25284d23f5589fe/f5101/pbi-dashboard-smile.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 43.08510638297872%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAJCAYAAAAywQxIAAAACXBIWXMAABJ0AAASdAHeZh94AAABQ0lEQVQoz3WSX4rCQAzG544eQvDVe3iIHmJ981VbsRQULKs7U1yLrBWdUVrtH/2WjJvdWt1ASTKQL78mEWVZ4nK5gHxRFLher6iqCvxO8e12A1nTN43ehed5mE6nmEwm8H0fYRhiuVxiOBzC81yMx+MHIY632y/MZjOs15+2/ng82ndxOBwsCVNmWYY8z63nvC5If0CmpES//wZ3NILjOIii6C7IQpvNBlJ+YLVaWcJwPkeapk9kbCxMdjqdcD6f74JURLMjKvL0cRMqqotxzGKu66LT6UBK+dvEEpJIkiRQSiKOYyilsFi8Q2v9tASKaVFkrVYLQgh0u12bE4TY7XYWlwiN0TDG2AGTp0avBJlwMBig3W4jCII/wl6vB9q00RpRdCcj4brAfyfyKhf7/d4SVj93yLfYnF29kHO+WSamO/wGw3qrMrcAtDsAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Smile&quot;
        title=&quot;Smile&quot;
        src=&quot;/static/518b098e88f5f7dbf25284d23f5589fe/f5101/pbi-dashboard-smile.png&quot;
        srcset=&quot;/static/518b098e88f5f7dbf25284d23f5589fe/f5101/pbi-dashboard-smile.png 188w&quot;
        sizes=&quot;(max-width: 188px) 100vw, 188px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Integrating Azure Cognitive with Retool]]></title><description><![CDATA[What is Retool Retool is a simple tool for creating a web application. One of the things that makes Retool great is you’re never limited by…]]></description><link>https://mortimer.xyz/retool-azure-cognitive/</link><guid isPermaLink="false">https://mortimer.xyz/retool-azure-cognitive/</guid><pubDate>Fri, 03 Dec 2021 12:00:00 GMT</pubDate><content:encoded>&lt;h2 id=&quot;What-is-Retool&quot; style=&quot;position:relative;&quot;&gt;What is Retool&lt;a href=&quot;#What-is-Retool&quot; aria-label=&quot;What is Retool permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://retool.com/&quot;&gt;Retool&lt;/a&gt; is a simple tool for creating a web application. One of the things that makes Retool great is you’re never limited by what’s available out of the box. If you can write it with JavaScript (the most popular language for web developer) and an API.&lt;/p&gt;
&lt;h2 id=&quot;What-is-Azure-Cognitive-Services&quot; style=&quot;position:relative;&quot;&gt;What is Azure Cognitive Services&lt;a href=&quot;#What-is-Azure-Cognitive-Services&quot; aria-label=&quot;What is Azure Cognitive Services permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://azure.microsoft.com/en-us/services/cognitive-services/&quot;&gt;AzureCognitive Services&lt;/a&gt; makes it easy to integrate AI with your web applications. All it takes is an API call to embed the ability to do cool things.&lt;/p&gt;
&lt;h2 id=&quot;What-you-need-to-get-started&quot; style=&quot;position:relative;&quot;&gt;What you need to get started&lt;a href=&quot;#What-you-need-to-get-started&quot; aria-label=&quot;What you need to get started permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;h3 id=&quot;A-Retool-subscription-and-an-Azure-subscription&quot; style=&quot;position:relative;&quot;&gt;A Retool subscription and an Azure subscription&lt;a href=&quot;#A-Retool-subscription-and-an-Azure-subscription&quot; aria-label=&quot;A Retool subscription and an Azure subscription permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;First step is to sign up for the Free trial of Retool (if you don’t already have a Retool subscription) and the Free trial of Azure (if you don’t already have an Azure subscription).&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 441px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/670923bc1daeda3ea5820ef9c37e057d/7cad8/retool-try-for-free.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 17.687074829931973%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAECAYAAACOXx+WAAAACXBIWXMAABJ0AAASdAHeZh94AAAA0klEQVQY042PwUrDQBRF+7EiuJLizpW2SrElCIJ/o1VbumpqaoISmpo0ZqNkommcKWaEHHGSlXThhcd9XB7n8lr8UVVVbJP3VHA9EtyMBcORMPvvXN2l3E4E2Yc2dy2lFL7vE8cxUkoTvr59sXrZkGYlIitN1r94pn34SGcQ0B0EnFgBp1btO/sujruugVprkiQhiiKkVCZcF5plKElFyXvTbF2G7B14dPoLjs4WHDfeO1+y23ZxvAb435fDlWI2z5k95NjzHNupfXqfG1jx+W2AP2wVIHlcNOi4AAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;retool try for free&quot;
        title=&quot;retool try for free&quot;
        src=&quot;/static/670923bc1daeda3ea5820ef9c37e057d/7cad8/retool-try-for-free.png&quot;
        srcset=&quot;/static/670923bc1daeda3ea5820ef9c37e057d/7cad8/retool-try-for-free.png 441w&quot;
        sizes=&quot;(max-width: 441px) 100vw, 441px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 805px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/be973053dc890b9db781d3f2a1a328d1/0469b/retool-azure-signup.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 34.199999999999996%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAHCAYAAAAIy204AAAACXBIWXMAABJ0AAASdAHeZh94AAABLElEQVQoz22ObU6DQBiEOUKrxQIlVK1C0mhsUj66sAssn4rGM6g3MNpewX/GG4/ZRWhp/LHZd2d25n2UMEyQpiXyvAalHEFAQQhDsGHgvASlqdTq+lHOrksgMoTEYHGGIIiQpAWa5hmeR6CsVh4imqIo7mVxllUoywes3Q2q+glV1cDzQ8RJLj3GOMqykcsEhFjWZaIwhjKdmhiPz2A7Nz0NZRwRTUAjDt8P4XsElCaIk0xSRCyF6xHpMZZjcelgNFKhqgYUw5hjNjuHuI9nXbegTixMTi1MTtpZaOK0/6w+0+UUYR4WdG9db7W3nYPt9xK7nyVeP51e78qGGaslPCzdl/8Vbm28f13L8/JhS31fOh/ASMJj49DUpibMi1tcrTkWdxSathjQ/Jf9BS2p1JnTRQpFAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;retool azure signup&quot;
        title=&quot;retool azure signup&quot;
        src=&quot;/static/be973053dc890b9db781d3f2a1a328d1/0469b/retool-azure-signup.png&quot;
        srcset=&quot;/static/be973053dc890b9db781d3f2a1a328d1/0eb09/retool-azure-signup.png 500w,
/static/be973053dc890b9db781d3f2a1a328d1/0469b/retool-azure-signup.png 805w&quot;
        sizes=&quot;(max-width: 805px) 100vw, 805px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;h2 id=&quot;Creating-the-App&quot; style=&quot;position:relative;&quot;&gt;Creating the App&lt;a href=&quot;#Creating-the-App&quot; aria-label=&quot;Creating the App permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;We are going to demo how to integrate the computer vision Azure Cognitive service. So we need a basic Retool application that loads up an image from your local PC. We also need some input boxes to put the response of the API call.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 432px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/e7d66b96e7f272597e3979e8cd2f96d8/dc3b1/retool-image-component.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 65.74074074074075%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAANCAIAAAAmMtkJAAAACXBIWXMAAAsSAAALEgHS3X78AAABgUlEQVQoz5WSy44cIQxF6/8/K0qkSEmWUWZWrXrQ9YKmugBjwMZRV2V6lcnj6C7sxQVsbhNCUEr1fa+U6roOAPZ911obYxBR/kgTgh+Goe26YRj6vg8h5Jx9CGdR30ekNj7SugXr8B6ydbhDCbnCQxKQ/TtyQB658VidB631j5fXcRxFhI9zH5LfSESI2NrNYW1ilpCqR7Y7bj7B0f5VPnJItWGinHMphZmJ6BjmX2mcc+1B13WXyyXG+B9mkZpLAYgJ8bw/5RIj8gERMVMp5Sg4JSwJEPF8YFMK6UUZMy56jQApl3W8LMtgt01qvbtod4gAOeeEUY3rt1dtb7czAg/zPA2fXz59efkaISKmaWo/fv8wLpOI3HacjI8QChEEv5qtXdBaCwAPMxE551ej7WZTSkys7W2YrjlnIsI3zqUao/UyGWOY+ZxZck7zPN3vu4jUWvWqlVLeexGJMY7jOM9zKUVEAOB6HVPC58LOfydiOmtrbdu1z2C/hfEXzPxsfwLRxfClEVz3JwAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;retool image component&quot;
        title=&quot;retool image component&quot;
        src=&quot;/static/e7d66b96e7f272597e3979e8cd2f96d8/dc3b1/retool-image-component.png&quot;
        srcset=&quot;/static/e7d66b96e7f272597e3979e8cd2f96d8/dc3b1/retool-image-component.png 432w&quot;
        sizes=&quot;(max-width: 432px) 100vw, 432px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 248px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/1917b0bdfcd9694ba9566ce68a52122b/6fa18/retool-basic-app.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 201.61290322580646%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAoCAYAAAD+MdrbAAAACXBIWXMAAA7DAAAOwwHHb6hkAAADkklEQVRIx9VVTY8URRhuY4Sbv8CLXJCEi4mSIBq8kCC4l71ywATCwWB01bMhIiRciAZClMge4cCBM8YEY1CMByUqQffDMDgzOzP9/TU93VU9j3nemert2Z0VUDBQyZOqrnrreb+rreFwCM/zYNsOHMeB7Yxn20YURYjjGL7vw/P9ajZy9oS8gzzPYRVFgV7PBomVUlBag3scN374Ede++VaIfD8QxUEQoCgUtNZQSsudsiyRpilc1xsRcjFtXP/uBq5+9TWWlpfxx8Iibt/+HUvLfwrB2kHrXM+DRU3NZmtkvm2L6QStNpaJq55fzetcdl10Ol2EYQTLaKBWul0H9+jBWkhopoDD4qXBYDDVZV5mbLIsuycoJ0mhJYwLE7Cy0qmsNYRMQqfTEYRhiHZ7RdwjAc9JwplGCSEvMo4sEZNdKjGESZJI6XAmSRTF8t3v9ycwYWF9mPiZNZXdLyi/jrA+GGhqp2bCuDVaT8aQcpS3dFlicXEJP/18E7/8+pusqc3UVhCG6Ek5jTqn2+tJqaTpqiKCpJXL1Mz6YmwYi3oMScIzc+66rnQM5XhvXZanubpah0Mh1dq0mqripdT6+mR1WPiHwYumLOolYtZ10FrKS2H/1WzhTuMuFhYWpSZb7RW02m0RYh2a9qL7bDv2fpKkVTL6WYZEQjCOYbfbk8JttVpoNBrodrvyrViHcSwx8zxXatGxbbiuI2SaLTduxTyvFTZzUOgSqhxCl+O1HiJJM/hhhCBKEMUpgihGGKcI4wR+GMMPoglQgcX3b/bYRbx89AvsmvsSr7y3il1zF/Da+/PY/cE8drxzHjvfnTwzeHXugpx/f6sBa1AoPHfgNKy9H2PTzEk88+aJCk/v+wTPzp7C/o8uYfb4ZTz/1hk89cZxbJo5MSG3eeYkrD3HcOX6LViF0nj9w3lsOfgZth0+i62HRnjBzIfP4sW3P8dLR89j+5Fz2HroTHVm5Hhvy8FPce3m8iiGdpCg2QvQsqfjbtcXbCTTtAO0nVB+CZLldFCgUCXyQmNQaFnX51xpSdTaPUL2dIksV6C30stxPx+/g0OUwwcHjSJ5kuWThGt/AfcLeZl0if6gGBHS5frD+iCjekg2svDfEoqF+aOykIuHYWFqYviwCJ+ApDw6Cx/XpKRPZlI2av57nRf/y2vzX4bwZMXoRx8kmZCmWY6kn4smmp+OwXW1N57NXjyWj9KBWPg3NLTYeUNtzSYAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;retool basic app&quot;
        title=&quot;retool basic app&quot;
        src=&quot;/static/1917b0bdfcd9694ba9566ce68a52122b/6fa18/retool-basic-app.png&quot;
        srcset=&quot;/static/1917b0bdfcd9694ba9566ce68a52122b/6fa18/retool-basic-app.png 248w&quot;
        sizes=&quot;(max-width: 248px) 100vw, 248px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;We need to give the input components the correct names so the the Javascript query we are going to write later will be able to access them.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;tagsInput&lt;/li&gt;
&lt;li&gt;captionInput&lt;/li&gt;
&lt;li&gt;confidenceInput&lt;/li&gt;
&lt;/ul&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;js&quot;&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// relevant js code to above input boxes&lt;/span&gt;
tagsInput&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;setValue&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
captionInput&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;setValue&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
confidenceInput&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;setValue&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Before we start to create the Javascript that will call the API, we need to include some libraries from Microsoft that will assist in the process. Retool has our back here, and has a nice way to load libraries.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 294px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/d06e7afef385ad824c04a99fe86bf789/b27a5/retool-script-style.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 172.44897959183675%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAiCAYAAABfqvm9AAAACXBIWXMAAAsSAAALEgHS3X78AAAEWElEQVRIx61Wu24rVRQdQhUJiQKJEokPoOYDEN9BmxQprXRQwDdACRRXulIEBSmQ6CKQbpPLFQjpxnEce562Z8bzcMbzOK+F1tiTTMizYKSTfY49WWfts9bex5YQAlJKKKVuDWPMnQEYrGqN2UqhaAyO/8nx1a9zfP3bHIfHAV68TmE1TYOqqpAkCZbLJYqigNYaDz3LUuMslMhqYPBLgM+/G+PgJw+ffTvGFy9sWHVdg6MD5uhY87P+EKLBqmywyCsUlcAbr2xZ/fx3hpd/pnhll7CSJFF5nmM2m5koisxiseAcYRji6uqqZbyJV93arNcF8iwzTVloqEpDVhqi1EW21FYURciyTA+HQ0yn03ZMJhM4joPVatUC3oBu5uuyNHEc42J8iantIIqXWIQRvv/hR1hpmp4yPQC6PfXew7OkQIwUhXMhhOH7RVGUcRwP8zwfrVarUZZlo9PT05FljDngi2EYyiRJDNOnOGmatkKRGec8hrIsuY9q/yj1B9Bq8F6aprvD4XB3b29v1xJCHJJBlmUyyzLDfyIggbo0syxrP6N4ACTZSilPtNYWQdM0tcbjsbW/v29ZUsrBNjWplDJUuPPiffYxxnQMTwimlHo3z3MrDEPr6OjIsrTWA1rCdV3p+77xfR9U2vM8zOfzDqRnbtwCBLAjhLCuHyHEYLu5/K8oPVYPMrwD2Ge4WCxaH1IM2uny8hIUqQ/6JCAZ8uWmaWTTNIbnRlvwLLlRD6gbjwNSFApQFIVkFZDRer1uLUKVuWbspf04oFJqQDZRFMnlctlWAEFoFc4pDH3YZ9jZ5l7Auq4H290fFKVfOVprxSiEODHGsDB2mqa5yzBJkjblfv1y3qW9NfXzU/Z9X4ZhaFhiTJUqU23OqXyn9pOA/3vKVJk2yfNclmVpmCoVZqOl2lwzg551HhdFa33YN3YQBNfqcjBdgvbM/aSxD54qvceaw32Ax0w5iiIVx7Fhm6IHKQKZ0oNdf2QBPEcUHrRuL6K6htxeUIyCcTtn3HbuW2d4R5S6rrXZ9D1zJ2WKQJDely3gpt6vGTZVdQPYkIkxcB0Hse8hsG345+eYjUaoR+fAdAIzCwD6sGluUtb6GlB98+UNYFWWULMA5dnbFqB2HTSug3I+h+Idws5dXEHFMZTrQNkTpeMIYr0+McO3ll7Md2D1nvJihHw+a+0xj2OEcYw4SbGIovZuZuUkWYayrlFwrFZqHUcoLkavCLQE3jEff9gHPEfGNCcT2I4Dz/fhBwFc34cXBLBdt92krCoUeY5iGas1f7IMz17Dst4nqPnog17KVQU9n0H6HrTvwXgOdOBBey6UPW3Xht+PLzZpR6HSgQ8xn/1uHNsyWu+YTz/pidI0raP5CyCczeA7DlzbRuC6sMdjJGHYniM2HrwRBdiIovXtM6QP+5fQgxXySC3Lv97cZsjipwAUhlXBtsU154zsi/3m8JxKaTsLm2n/Puli11yfA/gv/Y8F93VeXzIAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;retool script style&quot;
        title=&quot;retool script style&quot;
        src=&quot;/static/d06e7afef385ad824c04a99fe86bf789/b27a5/retool-script-style.png&quot;
        srcset=&quot;/static/d06e7afef385ad824c04a99fe86bf789/b27a5/retool-script-style.png 294w&quot;
        sizes=&quot;(max-width: 294px) 100vw, 294px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;We are going to use the browser libraries from the following NPM package &lt;a href=&quot;https://www.npmjs.com/package/@azure/cognitiveservices-computervision&quot;&gt;@azure/cognitiveservices-computervision&lt;/a&gt;. I simply NPM installed the package and then sourced the minified libraries from &lt;code class=&quot;language-text&quot;&gt;\node_modules\@azure\cognitiveservices-computervision\dist&lt;/code&gt; and then hosted themselves on my own webserver. You need to have them in this order because the second is dependant on the first.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 653px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/d65b4b924e06adbce449e282d788fe75/ffe7f/retool-libraries.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 66.19999999999999%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAANCAIAAAAmMtkJAAAACXBIWXMAAAsSAAALEgHS3X78AAAA4UlEQVQoz6WRa27DIBCEOWtPl7tUyiGSqlLcNLbZlxdYiIAkitwfrdNPgFag0c4OTjUMw8B3ZFlCCOU3zCzG6FT1+/wlwmULueFY9PB5Op0vM8kIfPE0As/AzCQi1YsIES0NuZNzLqW4aIVCkZhRrS9YTIKtmqzoT85SIgQEYKJa+FmYwM8AUC8Qzeyn7ZRStZ1Sgob33gNM0wSI0DY1ELFnqaoPLzHGm5ifQMQ+6i38NvBK3NOu4vIP3HMqvXicfxVv/ORiueTXxC927sMcR30/0MRpm9iaercf33bH/QdfAbYl/WCMtFV/AAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;retool libraries&quot;
        title=&quot;retool libraries&quot;
        src=&quot;/static/d65b4b924e06adbce449e282d788fe75/ffe7f/retool-libraries.png&quot;
        srcset=&quot;/static/d65b4b924e06adbce449e282d788fe75/0eb09/retool-libraries.png 500w,
/static/d65b4b924e06adbce449e282d788fe75/ffe7f/retool-libraries.png 653w&quot;
        sizes=&quot;(max-width: 653px) 100vw, 653px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;If you haven’t enables CORS on your webserver you will need to do this so that the libraries can be called by Retool. If you are using NGINX that would be something like adding the following to your NGINX config &lt;code class=&quot;language-text&quot;&gt;add_header &apos;Access-Control-Allow-Origin&apos; &apos;*&apos; always;&lt;/code&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;We need to create a Javascript query which will call the Azure Cognitive services API when we load the image.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 596px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/093ef6f372b57b4b9d7d1e41e9e79435/fe133/retool-javascript-query.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 49%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAKCAIAAAA7N+mxAAAACXBIWXMAAAsSAAALEgHS3X78AAABM0lEQVQoz4WS207DMAyG9/4vxhUIbhgqh21sS9cuaRInsZ1Ti7oh0ECwT7mwZH/5pcSLEAIAIOJ4yTRWT/VxH+9WuBTR0hQ8dF0HAEII7/00TYuUEhHFGGutl/KIsexUbE3qIWGsRDhozczWWmaeZe+RKOScpl8EivevTqnBaGWMUUql9DlWa51lIZ5fdqbZ9M37cdO53tUOyvkIxQ9rlIM5YbXWxhjnnLX2fMtCtG/NVj+t++W6Xx3gALW1pbVF2NIZGgwgEhExMxGFEIgIEfNZnv4mR8+2ATscj1JKaYyRJ5i5lDKO4+Likb6Za0KQ25tj3+3FTNu2UkpEDCFIKa8kR0aEnXcOwGEI3ntETCestXPyv7KH7lYPSmv9o1VKuZJcSo0x5ZxyzjFGZv7ahfNXfQBMCEJOXJ2l3wAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;retool javascript query&quot;
        title=&quot;retool javascript query&quot;
        src=&quot;/static/093ef6f372b57b4b9d7d1e41e9e79435/fe133/retool-javascript-query.png&quot;
        srcset=&quot;/static/093ef6f372b57b4b9d7d1e41e9e79435/0eb09/retool-javascript-query.png 500w,
/static/093ef6f372b57b4b9d7d1e41e9e79435/fe133/retool-javascript-query.png 596w&quot;
        sizes=&quot;(max-width: 596px) 100vw, 596px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;js&quot;&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// our secrets (for simplicity hard coded, I do not recommend this approach)&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; computerVisionKey &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;&amp;lt;key&gt;&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; computerVisionEndPoint &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;&amp;lt;endpoint&gt;&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;// this is needed because the cognitive API needs binary data not base64&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;_base64ToArrayBuffer&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;base64&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; binary_string &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; window&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;atob&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;base64&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; len &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; binary_string&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;length&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; bytes &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Uint8Array&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;len&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; i &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; i &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt; len&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; i&lt;span class=&quot;token operator&quot;&gt;++&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    bytes&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;i&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; binary_string&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;charCodeAt&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;i&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; bytes&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;buffer&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; cognitiveServiceCredentials &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;msRest&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;ApiKeyCredentials&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token literal-property property&quot;&gt;inHeader&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token string-property property&quot;&gt;&apos;Ocp-Apim-Subscription-Key&apos;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; computerVisionKey&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; client &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Azure&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;CognitiveservicesComputervision&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;ComputerVisionClient&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
  cognitiveServiceCredentials&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  computerVisionEndPoint&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;// get the base64 image provided by browse to file from retool&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; imageBase64 &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; cognativeImageButton&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;value&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; options &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token literal-property property&quot;&gt;maxCandidates&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token literal-property property&quot;&gt;language&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;en&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;// make the call and write results to input box&lt;/span&gt;
client
  &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;describeImageInStream&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;_base64ToArrayBuffer&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;imageBase64&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; options&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;then&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;result&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    tagsInput&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;setValue&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;result&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;tags&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;join&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    captionInput&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;setValue&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;result&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;captions&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;text&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    confidenceInput&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;setValue&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;result&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;captions&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;confidence&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;catch&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;err&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;An error occurred:&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;error&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;err&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Alright, this missing piece to all this now is the Azure cognitive services.&lt;/p&gt;
&lt;h2 id=&quot;Getting-the-Azure-services&quot; style=&quot;position:relative;&quot;&gt;Getting the Azure services&lt;a href=&quot;#Getting-the-Azure-services&quot; aria-label=&quot;Getting the Azure services permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Firstly, after you sign up for your Azure subscription, you will need to create a Computer Visions resource.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 407px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/2f440514b159ca1641822a9550830728/fc7bc/retool-cognitive-services-select.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 33.415233415233416%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAHCAIAAACHqfpvAAAACXBIWXMAAAsSAAALEgHS3X78AAAAxUlEQVQY05XO0U7CMBSA4b7/o5gQ8JIEEgnKFXABbranHQI9BTq2Zit0ZmWrBh9gyp/vAX4yoqY3x95cDpbqean6CxxBOea202UMxdNCkwiL11hN33dvEc5iOYvVSpZ/wHItiwk9E2Odsc5W9dctVLdQ+XCtG/cvLalKY7LMuev3g4W2JcNIgxAJUGAfggNnFOVW4b7bQaH3NXlhGSQbwSgH9pmIhMPxgGd96pal2ntPplufGpum2vyW57m19n4VQvd20zQ/QCSAE5KqipkAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;retool cognitive services select&quot;
        title=&quot;retool cognitive services select&quot;
        src=&quot;/static/2f440514b159ca1641822a9550830728/fc7bc/retool-cognitive-services-select.png&quot;
        srcset=&quot;/static/2f440514b159ca1641822a9550830728/fc7bc/retool-cognitive-services-select.png 407w&quot;
        sizes=&quot;(max-width: 407px) 100vw, 407px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;Now that the endpoint has been created, lets get a key and the enpoint and paste them into the JS query in Retool.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 728px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/07b58470360505c1e2eaa91289dda569/0b3a5/retool-cognitive-keys-endpoint.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 84.39999999999999%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAARCAIAAABSJhvpAAAACXBIWXMAAAsSAAALEgHS3X78AAACwklEQVQ4y1VSS44cRRQsxIYFO27AggtwFx8FDR9pLDYszIojIFk+wYCEN5bXCI/EwPR0d3VXd9enqyorvy//WZkouzz28BRS5iYUEe9F8dkP/3x+/e+XLzZf/bz+4sf7r3/ZPv+ju7qpv/+t/S6jubqpr26a69f99evhpzfjs5en4pu/P/327pOru2KL3Hqw29Gsev0w6B0yJdLlaLajXg+6HHXL7CB8L1zP3QC+4+5ILyC2SL7Rojk3TVufUH8mqO+77ng49Odz27ZoGBgegRGGMcUIj2cNLKWUYkyzL6IH4HgcESXEaG20tsZY65RxxmhjjLFe5ddaa51z8zzHtNBjAWYmlHPGjDFzCMF775yfo9AWGBOca+uF1JxRIUQIYSFmxFj0wiPClYT0OFJKSmlMyYY4pxRimmOaU0aYk5+Tm6Ofs/ECq4ApJ3gSAM7alJIQQMmEVXpXsv2xL0+oPA3lcSiPY8Zp2B76M5IxpUJBYFRkr84trgAA48nFVHXsbn1clc2qrB/KdrWt7zf1/fZ0v62PXbZWaKUZZVrnnQCAkmohZwtcntvhUI/ladyfUDOKbpLtCN0kqYphToVSapomjDFCSCsVYxQCBCN/te7lLX11y7YoZ3HhY/JlEVmZTnXXVPXpeKj2GCHgrOs6YOTtjv/6J3p1SzcX8uU+8QlSiLFQBigheMLGGH85I2MUY5ySjw7SrJ2RAKC1upw9N8Fbjbj6/UEUOkTKOKM0xuX4eWGEEG2MADC5KtZa45zzjzMHD9o9jKag2k+EKQmP3GXb2HsvBJcAWmVN56z3bkEMjin3ZicLE6yQQAnz3j9V5pxv1ptqv8cY89zA/w0hOM6+YDZiJrSUHxq2kEGIru2yuMyZJYB8MlxwH0LhQ9TavA8c35MppRNCq9XqUO2rqqp2+/3lsyt3+92uqqqmaZxz/wHG+7DO2gND8gAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;retool cognitive keys endpoint&quot;
        title=&quot;retool cognitive keys endpoint&quot;
        src=&quot;/static/07b58470360505c1e2eaa91289dda569/0b3a5/retool-cognitive-keys-endpoint.png&quot;
        srcset=&quot;/static/07b58470360505c1e2eaa91289dda569/0eb09/retool-cognitive-keys-endpoint.png 500w,
/static/07b58470360505c1e2eaa91289dda569/0b3a5/retool-cognitive-keys-endpoint.png 728w&quot;
        sizes=&quot;(max-width: 728px) 100vw, 728px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;js&quot;&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// our secrets (for simplicity hard coded, I do not recommend this approach)&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; computerVisionKey &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;&amp;lt;key&gt;&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; computerVisionEndPoint &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;&amp;lt;endpoint&gt;&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2 id=&quot;Testing-it&quot; style=&quot;position:relative;&quot;&gt;Testing it&lt;a href=&quot;#Testing-it&quot; aria-label=&quot;Testing it permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Uploading this close up picture of an Eagle, and the computer vision API returns with a confidence of around 50% that it is &lt;em&gt;a close up of an eagle&lt;/em&gt;. Not too bad.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 591px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/8b0943fa91e5d407af35560997bd4ab2/c1f10/retool-results.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 114.39999999999999%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAXCAYAAAALHW+jAAAACXBIWXMAAA7DAAAOwwHHb6hkAAAFVklEQVQ4y22R609bZRzHT3znG/8A4wvdCzfjPZuJxpgs3qNRN5U4b/Ma3dymiTd2U3GA25AIskHROVDGZVBWCtuhLfd7sWtxG22h68ZGL9DSDmh7ejmF8jHnAAajT/LJeZ7f5ft8f88R0uk0yWQKWZZJpdaSQslFIhGmAwHi8TjJZJKUvJxTa+RllN5kallDUIqUYgVJiiPFV5AkVSAcvoHLdRn/1BTzkYgaSyQSxBOJ5XpJUuujsZgaExYWFohJEplM5l8sLS2pl0SiUeLxhNoYjUZVx4qwslcdq25T6l7REhYXF9XG/1uKSDgcZnZ2doU51fFMKKSeFRF5ZWRFVNFSHd5YSSpLcaagLOUiJae6mp9XxZQ6pVF531Wh1e+Kw4w6ezq9wGImg3JWySwhxRPqeJFojGhMYm4+osYS6jsmSSRTa1gZeSmzyEIyRjoe/e/MGZlEdJZkbJ6UFEGWImTk1edZWsPqOYNwxX+DPaWtfFpmWMPyeU+pyK7jItm/tbHvZBuflJxl9/Fz7FrDbhWRj4qbsbp8CI7rM7z4XR1bD9Wv0MDW3AZ1/1LOaZ47eIp3Clv4oLiVV/N1ZP2gIytfp+5XUWIv5mgZsHsQ/rGdkVXLkIYlWR1CTkoMD3VTUnSAH4/s5kTZfip/PUhtRQ5nqvJpqT6MePoIrdoCmhvy8HudCMrPCM6E8Pl8jI07cLvs/GXuwjV2ifn5WfVH2C5a+S53J9vfe4BPdj/KF59v5tvsp8k/8CxHcp7nx7wXyM19CoejB0GWUwQCAbyeSdwuB5dsQ7gcViZcF/BfdzEbniGVSpCWF2kRNeTkPUlR8RbKj79ChWYbVb++RU3Fu1Se3MbEhBkhLaeYcI9x9co4LqcV56iVMfsFJifG8V67TCg0QzgwxWxwmriURG8opKT8Caqr30Bb8w5NdR/Q0vgxTdr38XktCJG5EKO2fmyWbmzD7Yi6ampLs+mtP8SFAT3eq6OEgh5CQS8JScIzaeXUmfeoqn8F7eksmhu3c1b3Pjrdm3h9ZoRp7zUK9++gNO8LTh3Lpezgy+R9lcXHWzayZ8t66ss+o63hKBeHdMx5bSxOFxHxFjN2qYS2jr3UNmylsWkbDbqX8fgGEGb8HooOfMr+D7P4Zud2Kg+/gf73vdT99DYt5TswNBylU1/M+GAN0vgxguL9BOpvg9DvLM6OMDKST+3ZLdQ0P891Xx9CwO+hpvQIhXt3ofn+czQ/7Ket7ijW9koc5404rWe4NlpH+HoPqclK5syPE7t8iHSom8ykBjwFmC37KG/azIS/GyEUnEJfVUph9g469XX0G/SYqr/lSvdBfLajhO1fEnN/TfxaAbK3GNl9CHnsMPLF11mwPczSyGNI49lUis9w2WtEmPZ70FYUYdCexNzXzphjFPd5PUF7OTeu6pm69Bte817ClteI/bmJSP8jBE1PE+m4l3jvLcgDN4HtIfQdz2D3nEMIh4Kc01YxYjVjswziHnfitNu5OGLF8ZcVu7WD8605WPQ7OKvZiLFyHR2l6xg9sZ6pM5uY73oM+m5GbH+QUZ8BQZJiDA/20Nttwu0ex+l0cN46hMlQQ6+pFqulE4utj84BEzVNJyg4vZmChjso1NyOpuRW9FWPEB3YRKNpA6N+ESEQ8DM41MvQQAfmoW6Ghvvp7zXS0aGnq72RYUsbg2Yjxp5TaHv3ccxwH8XtGyjuWk9h1waK2u7GaX0TjekuxqZFhGhkDtvIIH09rfR0nqOvW+TPwU56+42IYgViyy+Ixj+obvuQUvEeyoz38rPpLopMd/CTcR2VnRtp7n+Cn4134p428DedU+x4ijhmEgAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;retool results&quot;
        title=&quot;retool results&quot;
        src=&quot;/static/8b0943fa91e5d407af35560997bd4ab2/c1f10/retool-results.png&quot;
        srcset=&quot;/static/8b0943fa91e5d407af35560997bd4ab2/0eb09/retool-results.png 500w,
/static/8b0943fa91e5d407af35560997bd4ab2/c1f10/retool-results.png 591w&quot;
        sizes=&quot;(max-width: 591px) 100vw, 591px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Installing Prefect Server on Azure]]></title><description><![CDATA[Reading Firstly I came across the following docs on how to install prefect server Prefect Server 101 Deploying to GCP Deploying to a Single…]]></description><link>https://mortimer.xyz/prefect-on-azure/</link><guid isPermaLink="false">https://mortimer.xyz/prefect-on-azure/</guid><pubDate>Mon, 22 Nov 2021 12:00:00 GMT</pubDate><content:encoded>&lt;h2 id=&quot;Reading&quot; style=&quot;position:relative;&quot;&gt;Reading&lt;a href=&quot;#Reading&quot; aria-label=&quot;Reading permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Firstly I came across the following docs on how to install prefect server&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://medium.com/the-prefect-blog/prefect-server-101-deploying-to-google-cloud-platform-47354b16afe2&quot;&gt;Prefect Server 101 Deploying to GCP&lt;/a&gt;&lt;br&gt;
&lt;a href=&quot;https://docs.prefect.io/orchestration/server/deploy-local.html&quot;&gt;Deploying to a Single Node Prefect Server&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;None of these outlined fully in detail what I needed, which was how to install from scratch on Azure.&lt;/p&gt;
&lt;h2 id=&quot;The-Azure-VM&quot; style=&quot;position:relative;&quot;&gt;The Azure VM&lt;a href=&quot;#The-Azure-VM&quot; aria-label=&quot;The Azure VM permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;We are going to set ourselves up with a Linux VM with 4 GB of RAM and 60 GB of attached disk. By default, we will not be allowing any inbound ports, because we will be opening them with firewall rules later.&lt;/p&gt;
&lt;p&gt;The machine will be based on the basic Ubuntu 20.04 image from Canonical.
&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 400px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/d0a6aa24c2f88aa7f8db570cf1a85024/d9f49/prefect-01-ubuntu.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 68%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAOCAIAAACgpqunAAAACXBIWXMAAAsTAAALEwEAmpwYAAAB3klEQVQoz6WQXWsTQRSG82v9Bd4Ur0oRvBWEiiKCWBTUi1KQghf2WkSb1IZNUrOb/Zqd2U2y2dn53pkjk8bYGLzy5XCYGc5z3jOnB/+hHgAopaWUQkgh/0j4Fy8ppd6TUqrrOg+vKK8WyyzPSFUhjDEhhU9llmdxkiKEpJTGGK3NVlp7Pw93a5nbbMwm+xLzL2mtrbUeJgQXKEPIuxW4SNKE0na+0vNazWvdUF3MRVFyVAkuur//bExHWy6lUlrfjmCtdQDWOrM+OgDnwDrwt62c8/Dzj8XRSfz2c1lhFIVhniakKvM8q3ARTadxFMWzKE7iNEm6bs/58NX0zQV+fIYWVIZ5XVR1Ps1RQrKKMWWU7RjjbUMZY84PsQu/+8IfvU7PziPIUTcc28EP8/XSjW7s4BriGFY17BregRvKPrwkTw/Zp/dqNmtLIlZLITlnraiXqyiqL/vm23cIw/0WPQiCq4f3ghdHwbMHZV0HkzDN0DqKNEMZxgkmXAhAyA2He/DNz+sn94fHB8HxgTAwp5aKbslcq9ySO7stdA76fRBiF85yfn66OD3hoyvgDFq6CdYCa13TuDiGyQQGAyBk0+XOwhxYC0xCGLogcKPR7xi70RjGEygJNM3mw7vb/gWI8xbNdzFRIwAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Azure Ubuntu 20.04 VM&quot;
        title=&quot;Azure Ubuntu 20.04 VM&quot;
        src=&quot;/static/d0a6aa24c2f88aa7f8db570cf1a85024/d9f49/prefect-01-ubuntu.png&quot;
        srcset=&quot;/static/d0a6aa24c2f88aa7f8db570cf1a85024/d9f49/prefect-01-ubuntu.png 400w&quot;
        sizes=&quot;(max-width: 400px) 100vw, 400px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;The machine we will choose is the Standard_B2s machine which will have the resources that we need.
&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 742px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/07d73fac17728e058767b8051d4011c2/fb3ea/prefect-01-b2s.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 32.800000000000004%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAHCAIAAACHqfpvAAAACXBIWXMAAAsTAAALEwEAmpwYAAAA60lEQVQY022Q627DIAxG8/5vt2lSd1GUTkkaCBCVkHCxwZ7SNl2r7ej8QBYfNq4W5xCRH8AU5cebHJWUUopRa6OV8auPIWzueO8rQMwXiegaBsxnF+dYlphdJL/C7CBm/kt1tta52bkFAG6dQ0inntVI42bpuyKHImUeBpSS92tEVEFKT68R5cO7OHwOX7Vt2+n4PfWd6dqpaUxznOo6vr4kaxNiCKHCO3n/OUAZFQrFxrDRrC5uZ8Nac4y/YwshtNLGaOfmaykTx8KFGUq+L+JfqnVdvPchhJxvO0mQhdR931lrqRQmevIh/APDiZUOKXowiQAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Azure Machine&quot;
        title=&quot;Azure Machine&quot;
        src=&quot;/static/07d73fac17728e058767b8051d4011c2/fb3ea/prefect-01-b2s.png&quot;
        srcset=&quot;/static/07d73fac17728e058767b8051d4011c2/0eb09/prefect-01-b2s.png 500w,
/static/07d73fac17728e058767b8051d4011c2/fb3ea/prefect-01-b2s.png 742w&quot;
        sizes=&quot;(max-width: 742px) 100vw, 742px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;Set the public inbound ports to none.
&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 741px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/b4f4f4503d65054d12cb4997a1e91bb0/660ed/prefect-01-noports.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 14.399999999999999%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAADCAYAAACTWi8uAAAACXBIWXMAAA7DAAAOwwHHb6hkAAAAZklEQVQI143NywrDMAxEUf//v5ZYFpaC/LhFocmqhQ6c3YxUVJVkvTMiyGzgJc5RhYhgjMFai39SspzmnOy9Lxl3p7X2iPvZp/NLERGSuT2D66AZtR5oE6RWVBtuHfumd0431py8Aesx6zdfNFcdAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Azure No Ports&quot;
        title=&quot;Azure No Ports&quot;
        src=&quot;/static/b4f4f4503d65054d12cb4997a1e91bb0/660ed/prefect-01-noports.png&quot;
        srcset=&quot;/static/b4f4f4503d65054d12cb4997a1e91bb0/0eb09/prefect-01-noports.png 500w,
/static/b4f4f4503d65054d12cb4997a1e91bb0/660ed/prefect-01-noports.png 741w&quot;
        sizes=&quot;(max-width: 741px) 100vw, 741px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;Create an attached disk that is Standard SSD and 64 GB in size (E6 performance tier).
&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 444px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/9a666d749c39bb280bffb5218ba35e98/39d25/prefect-01-disk.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 57.20720720720721%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAALCAYAAAB/Ca1DAAAACXBIWXMAAA7DAAAOwwHHb6hkAAABO0lEQVQoz41Qi46DIBD0/3/x0tpTbFQEEUWU11xA26Sevesm46474zCY6XlGXdWo7y3kMMKuDt4FeOvhrH/pcR9nZxzsAXEfQkC2qBXfF4IiJyjzCiQn+L7G+Y7ydsftqwCJ/VqmrieDVVkY7ROs9lByQU8lYmXBA4IJSDGmFGa1T6TT1y1BfF+1SRrBBdqGglEO1vXgrIcze8LoqtQEKbcTAkJ6vgcwTiM45+CcQQgBKYfty4eh1jP6vof32/I/aK1xrAeXDOdZYRiGj8xiEqUUnHPbzvsXfjecU3R/IE8NQ8AcDb1P85Hfr6zBWAdr3fNf/GX4kvDMcFmWPWHAJxVv5MO5NnPOgtIGpCwwDALTJDGOEtMJ0n6SaJp6046/tZkxKypS4JZf0NEWPe/AGX2LyFekRNe14CfaH+bOWTRWYaiVAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Azure Disk&quot;
        title=&quot;Azure Disk&quot;
        src=&quot;/static/9a666d749c39bb280bffb5218ba35e98/39d25/prefect-01-disk.png&quot;
        srcset=&quot;/static/9a666d749c39bb280bffb5218ba35e98/39d25/prefect-01-disk.png 444w&quot;
        sizes=&quot;(max-width: 444px) 100vw, 444px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;For the rest of the image, set to defaults for Networking, Management, Advanced, Tags.
&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 655px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/891a419f09aa74f24c6a7443a76e145f/2728a/prefect-01-default.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 13.200000000000001%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAADCAIAAAAcOLh5AAAACXBIWXMAAAsTAAALEwEAmpwYAAAAiUlEQVQI13WLsQ6CMBQA+f9vcnEyDDiRsFAI2KJohJYH5b2+0pomDi5ebrjlsviLc3HfI1FETIGYJIp/yPjwbK2XkpR0Sh5KUddjXXPThPuD+hsPwzGOrJQHIGb2nsh9551wfT1tUXSns7hct/e0GDPN87oAL6CFgKrCslzzHNtWA1hrtTYhpPkD/fOq2UPd+dEAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Azure Disk&quot;
        title=&quot;Azure Disk&quot;
        src=&quot;/static/891a419f09aa74f24c6a7443a76e145f/2728a/prefect-01-default.png&quot;
        srcset=&quot;/static/891a419f09aa74f24c6a7443a76e145f/0eb09/prefect-01-default.png 500w,
/static/891a419f09aa74f24c6a7443a76e145f/2728a/prefect-01-default.png 655w&quot;
        sizes=&quot;(max-width: 655px) 100vw, 655px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 380px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/cec0d1b590c3a0bd591d7fa2ceef311c/2edd7/prefect-01-key.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 64.99999999999999%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAANCAYAAACpUE5eAAAACXBIWXMAAA7DAAAOwwHHb6hkAAACH0lEQVQ4y5WRXXLTQBCEfcEUd6C4AAQqxQNwAnhPIJyFA5A4kWXZlrQry5bsxIlj69eWZOmjduU4geIlW9U13TOa3hlth2enaRpeev7t6Sjdoi3Udc16tSJN0wM2mw1ZlhFFEXmeax3H8aFHxUefzlOiLZZliWn2uLj4zfXVFd3uJa7r0jMMDOMawzDoXl5imqa+fD/nHnrCp5Hr5qXrtj27uqGu2yn1ytNFhQgK1knF/brQeIgrVsmOZVSyiqtDVLnHmtJR1pBuIc6h2jXtyt3Rhl8XEZPghv7Qx5EzepZg4Ey4Nh3MgYc5kPSHYx2VVnmjL3C9OfNFoi/c1dBRrnkBcVZh2b42sWVIf+jRsyTWyNfcsMThEq37rtZXPVt//5fhKoW7dcXQDbHlnP7Ix/FuNB+JmY6WPcEdL3CCFBGmuGGGN8u5XW5ZxjvW2X7lRv9UqGooKyjKRkPx53pb1BRKq/zuKepHadp+/Sj5dsdwmmk4YY47y3HDXPP/6jDTcPawg+zQr7w6fT/l1ZceR58M3nwbcnIu+PjT4+SH5P2Z4ORc8u5UcHwmeHsq+PBdan581tZffx1w9NnQHsqrk21KZBgjZwnjeYoI1ljiltH4HttfMvTu9vyegVxoPpB3WGKBDCO8eaJ7lYfy6hTbDeU2oyo31FVBEj3g2gOC6Zj5bIo/FgQTj1ng40mHMPDxPRfpjkjiFXW11b3KQ3n9AfW8yaa7SZ8IAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Azure Disk&quot;
        title=&quot;Azure Disk&quot;
        src=&quot;/static/cec0d1b590c3a0bd591d7fa2ceef311c/2edd7/prefect-01-key.png&quot;
        srcset=&quot;/static/cec0d1b590c3a0bd591d7fa2ceef311c/2edd7/prefect-01-key.png 380w&quot;
        sizes=&quot;(max-width: 380px) 100vw, 380px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;h3 id=&quot;Key-file&quot; style=&quot;position:relative;&quot;&gt;Key file&lt;a href=&quot;#Key-file&quot; aria-label=&quot;Key file permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 248px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/cda0680a259ef6962aa662f1aa39955b/6fa18/prefect-01-keydownload.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 42.74193548387097%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAJCAYAAAAywQxIAAAACXBIWXMAAA7DAAAOwwHHb6hkAAAA9ElEQVQoz52PzU7DMBCE/aAckHrhwIFHQ70gLoiXgCSEpAmH0B810Di266ztQY4TiUJJUUf65NV6NB4zIgMigjEGZMIcGOZ+F/CeY9C3mWGQcw6n5D0/GWWsDYFaa+yaxtv7RkK0kFKBug5Kqt7kPfu9Pvro2MyOgUJIbLc1rCWkUYqXOEKcpCiLEslzhOw1R+zPRYGO6KCpF29bCCFgXSjEfLPpb1r8R97nrAUzAMgBjdTgSoPLQ1rV/dpNweb3j7i9e0C54ciqGtn7x3ksA+zicoar6xusOSGvaiyWn5MUf92tAix5W+Mpr1CsdifDJhkCvwBq9bBzC2LvgwAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Azure Disk&quot;
        title=&quot;Azure Disk&quot;
        src=&quot;/static/cda0680a259ef6962aa662f1aa39955b/6fa18/prefect-01-keydownload.png&quot;
        srcset=&quot;/static/cda0680a259ef6962aa662f1aa39955b/6fa18/prefect-01-keydownload.png 248w&quot;
        sizes=&quot;(max-width: 248px) 100vw, 248px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;Firstly we are going to move the keyfile from where ever your browser downloaded it to your &lt;code class=&quot;language-text&quot;&gt;.ssh&lt;/code&gt; directory in your home directory.
I am going to use Powershell on Windows for this example (but you could just as easily use any other shell or OS).&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token function&quot;&gt;ls&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; Where-Object &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$_&lt;/span&gt;.Name &lt;span class=&quot;token parameter variable&quot;&gt;-eq&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;prefect_key.pem&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;log&quot;&gt;&lt;pre class=&quot;language-log&quot;&gt;&lt;code class=&quot;language-log&quot;&gt;        &lt;span class=&quot;token property&quot;&gt;Directory:&lt;/span&gt; &lt;span class=&quot;token file-path string&quot;&gt;C:\Users\&amp;lt;username&gt;\.ssh&lt;/span&gt;


Mode                LastWriteTime         Length Name
&lt;span class=&quot;token separator comment&quot;&gt;----&lt;/span&gt;                &lt;span class=&quot;token separator comment&quot;&gt;-------------&lt;/span&gt;         &lt;span class=&quot;token separator comment&quot;&gt;------&lt;/span&gt; &lt;span class=&quot;token separator comment&quot;&gt;----&lt;/span&gt;
&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;a&lt;span class=&quot;token separator comment&quot;&gt;---&lt;/span&gt;        &lt;span class=&quot;token date number&quot;&gt;24/11/2021&lt;/span&gt;   &lt;span class=&quot;token number&quot;&gt;9&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;35&lt;/span&gt; AM           &lt;span class=&quot;token number&quot;&gt;2498&lt;/span&gt;   prefect_key&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;pem&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Now we can go to our resource and connect.
&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 829px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/3a06ffae864eb579542a04d5982dc944/c2a54/prefect-01-gotoresource.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 39.8%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAICAIAAAB2/0i6AAAACXBIWXMAAAsTAAALEwEAmpwYAAAA+ElEQVQY042QTWvDMAyG8/9/0+jYDmPQwy7toQRKYVuTNHZs2ZEUR7E1ylZooWN7DgKB3g9UtaetMUdrXde1pu+GwVpjmuYYQhRZRCTnvFz4XvVCBZtX+/l+bJvBQmsiBCRmIp6miYhSSoh46vvovTXGuUgYYFYCnqVUqoqI3nuA4GJCyjkXvUcpP3PJKlm1aJVzBgDnHTPNs4iUfL4q9w1uqbSUERkiJ9F5lpQ4paT/41xbpDAJYgJcTgM54BCnwROyEFEM8fpJt2Km+u2wftqtn+vD9mOMcXBgrWXm8lf3SgNsXurV4371UO/XO5nGOI5E9FvatfgLgvDP4i2DkuYAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Azure Disk&quot;
        title=&quot;Azure Disk&quot;
        src=&quot;/static/3a06ffae864eb579542a04d5982dc944/c2a54/prefect-01-gotoresource.png&quot;
        srcset=&quot;/static/3a06ffae864eb579542a04d5982dc944/0eb09/prefect-01-gotoresource.png 500w,
/static/3a06ffae864eb579542a04d5982dc944/c2a54/prefect-01-gotoresource.png 829w&quot;
        sizes=&quot;(max-width: 829px) 100vw, 829px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;h3 id=&quot;Networking&quot; style=&quot;position:relative;&quot;&gt;Networking&lt;a href=&quot;#Networking&quot; aria-label=&quot;Networking permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;To enable yourself to connect (using Secure Shell to connect to your remote computer) you must create a networking rule to allow your public IP address to access inbound port 22.&lt;/p&gt;
&lt;p&gt;The easiest way to find your public IP address is to ask Google.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 728px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/58f14800d995558aad96984829bf1c28/0b3a5/prefect-01-myip.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 33.199999999999996%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAHCAIAAACHqfpvAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAiUlEQVQY05XQQQ6DMAxEUe5/ybQkhSAEbUzANnZihNQDhK/Zvs10yMwirFrN7rWlqqWUbh7GJQTue9vAilpbf1zCm+K0fGLys7lghI2+1tp99+TGOP3ysCJtbCE8wNm5Y105pRw8vpyl9ABLAvJ+d+6IEQG2nAFARNpwKaxKIvft50lEeKCqtuAL+xabihEn/S4AAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Azure Disk&quot;
        title=&quot;Azure Disk&quot;
        src=&quot;/static/58f14800d995558aad96984829bf1c28/0b3a5/prefect-01-myip.png&quot;
        srcset=&quot;/static/58f14800d995558aad96984829bf1c28/0eb09/prefect-01-myip.png 500w,
/static/58f14800d995558aad96984829bf1c28/0b3a5/prefect-01-myip.png 728w&quot;
        sizes=&quot;(max-width: 728px) 100vw, 728px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;We are going to add three inbound port rule so that we can:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Connect to the server (Port 22)&lt;/li&gt;
&lt;li&gt;Enable access to the UI (Port 8080)&lt;/li&gt;
&lt;li&gt;Enable the UI to access the GraphQL API (Port 4200)&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 798px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/22f16d45510ded0fc0083c648e194381/50208/prefect-01-inboundport.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 76.8%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAPCAIAAABr+ngCAAAACXBIWXMAAAsTAAALEwEAmpwYAAACEklEQVQoz4VTS2/WMBDM//8liAsSEkjcERII1EKBqodWbb82+ZL4tfZ6ba8fgST0IYHEXOyDd2ZnZ93dCLwfjofDrZJqgwQwbfkPWmullI5CNMZYC8xcay2lLMuSc2ZOKaUYI2+3uCExU2B0Lue8LEunbTLGMOe28eWciUgYVJMvpdV/YH2zHq11AoJSEhFX5VK890IIAD+OChFLqe2B4e/mOzHbaZqMVkYr5mQBpJSIoKRQShL5EJkTpxRra0tr9dk8Oo8BnIsphRjDBqWUseKun++G+Tipw8CzLhiatMX4Grm1h/rOGY1alxA4xZzzXszM/TDe98fLGzOMdD+64+C0pUmqkvlJefz4qf/yWZ6e+B/fnQMhJjHPy7I4CxbAGCeEEkLUmmstKaVH8+vA7Kzk1VWa50pUayUiAABHw6hGYR1Fohh88J72eNozdBoDhfjYSa11TSs3oZzXOus5pThDMRYRnSfy3m8plC1nZz3iY3EpdVlK/+3q5YuTN2/P3767eP3qdDj9mZ9ZffKMEeWWs7UuxrimX4u6HU/en1+eHS7O7r5+uKBh3tauMP+hIKKUUofEYC15MlqHGPcly4VjcPtcUnCAjjztS7rbRsTM3GFIWmljDBjYPZeSU0ze0y7iPdl1hkAh7Mq11rjJdAZZCgkAO+v+N4io73uttVKq7/vr62sACCF47/d/Yq393fYvaq1eKBL+aJkAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Azure Disk&quot;
        title=&quot;Azure Disk&quot;
        src=&quot;/static/22f16d45510ded0fc0083c648e194381/50208/prefect-01-inboundport.png&quot;
        srcset=&quot;/static/22f16d45510ded0fc0083c648e194381/0eb09/prefect-01-inboundport.png 500w,
/static/22f16d45510ded0fc0083c648e194381/50208/prefect-01-inboundport.png 798w&quot;
        sizes=&quot;(max-width: 798px) 100vw, 798px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;For now we are going to only enable these ports to be open for us.&lt;/p&gt;
&lt;p&gt;This shows how to enable TCP through port 22 for only our public IP address.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 553px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/8212615d8ab246b56b5379b5ed36c7a1/d35da/prefect-01-inboundssh.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 159.60000000000002%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAgCAIAAACdAM/hAAAACXBIWXMAAAsTAAALEwEAmpwYAAACiklEQVQ4y51VyW7bMBDl/x967D/00FOPPTQtuiQOUqSwY3iVZFmxrH3hOkNSLOQYQYsmttyHd5wZkcP3nsg0Nd6umk0n2yiazWae5/m+F4ZhEASe50VR5Pt+13Xub3RdJ4QgyxTjksW7KM/zpmkopYwxRnu0bcsY45z/2+ycs9aSpzn9qJcqToNY556aOuesexoyFIRuo2KxEn5g4sTmhVFotD1LjcYaS8SPUfLtZv/1Rk7m3Xhq/dBYp6VBqeEVotKslZIDEWnWbrZ8Oq0m0zII831eFhWnDAE721ljX2G/IkJHo2w8Tm8nyc9F8vE6uJ7XDCSVIBD7779MxUEJIDLctut1Pl60yzX4gWXMGKfBIPTHe40g0RhLqBD9aypZ05ZJKZXCAQAA1EiklJwxKYTR+nCZQegrrSVFUWRp2lIqpdSDoQ4HJEIIADDG6EuglNJak6os93HMGTOohw9ARGMMYUJkZSE0gjUKUcEBeIZCCAWKNH6Q/3qoxw/Vw6yYL2lRK4kWrTlHazsivnyXywD8SK0CHN3hfK3QAUcQ+gQlU71I2ratNxubpF3TdNZ2R4OeZ2/JZhcX65XZBC5LXds6xAv8nHy6qaYzE+9cmrrJxG23x3gY0twmSbFc6iRxZemaxl2SJwQ0UtnL/DnaLmhmlAnOn5Pssgx7zNg+yRDBXY5e23VdwSVL/uPOAFIIjfg/0dtbMssOKXDQtTqq+zSOluRSCs4NgDkYZXAc9JWkur0rbu/xYYHztZ0vdVJo7bQyGuwJgtQaNLGLeRf4LtzgytezJUYxgAUOIPAEFVcggdxv+OdJczWuKwbQR7q2xgz51/QLe3eVvP3w+OZ9GO5pkSVlUdrBa/8NCRtDThg/TLgAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Azure Disk&quot;
        title=&quot;Azure Disk&quot;
        src=&quot;/static/8212615d8ab246b56b5379b5ed36c7a1/d35da/prefect-01-inboundssh.png&quot;
        srcset=&quot;/static/8212615d8ab246b56b5379b5ed36c7a1/0eb09/prefect-01-inboundssh.png 500w,
/static/8212615d8ab246b56b5379b5ed36c7a1/d35da/prefect-01-inboundssh.png 553w&quot;
        sizes=&quot;(max-width: 553px) 100vw, 553px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;We need to repeat the steps for ports 8080 and 4200.&lt;/p&gt;
&lt;h2 id=&quot;Connecting&quot; style=&quot;position:relative;&quot;&gt;Connecting&lt;a href=&quot;#Connecting&quot; aria-label=&quot;Connecting permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token function&quot;&gt;ssh&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-i&lt;/span&gt; C:&lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;Users&lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;username&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;.ssh&lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;prefect_key.pem azureuser@&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;azure-vm-public-ip&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;You’ll be prompted that the authenticity of host can’t be established and asked if you want to continue.&lt;br&gt;
Respond &lt;code class=&quot;language-text&quot;&gt;yes&lt;/code&gt; and the host will be saved to your &lt;code class=&quot;language-text&quot;&gt;know_hosts&lt;/code&gt; file so you will not have to be prompted again.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;log&quot;&gt;&lt;pre class=&quot;language-log&quot;&gt;&lt;code class=&quot;language-log&quot;&gt;The authenticity of host &lt;span class=&quot;token string&quot;&gt;&apos;&amp;lt;azure-vm-public-ip&gt; (&amp;lt;azure-vm-public-ip&gt;)&apos;&lt;/span&gt; can&apos;t be established&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;
ECDSA key fingerprint is SHA256&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;abcdefghij&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;12345678901234567&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt;g&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;abcdefghijkl&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;
Are you sure you want to continue connecting &lt;span class=&quot;token operator&quot;&gt;(&lt;/span&gt;yes&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;no&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;fingerprint&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt; yes
&lt;span class=&quot;token property&quot;&gt;Warning:&lt;/span&gt; Permanently added &lt;span class=&quot;token string&quot;&gt;&apos;&amp;lt;azure-vm-public-ip&gt;&apos;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;(&lt;/span&gt;ECDSA&lt;span class=&quot;token operator&quot;&gt;)&lt;/span&gt; to the list of known hosts&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;
azueruser&lt;span class=&quot;token operator&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;azure&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;vm&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;public&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;ip&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; Permission denied &lt;span class=&quot;token operator&quot;&gt;(&lt;/span&gt;publickey&lt;span class=&quot;token operator&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2 id=&quot;Installing&quot; style=&quot;position:relative;&quot;&gt;Installing&lt;a href=&quot;#Installing&quot; aria-label=&quot;Installing permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;h3 id=&quot;Following-the-steps&quot; style=&quot;position:relative;&quot;&gt;Following the steps&lt;a href=&quot;#Following-the-steps&quot; aria-label=&quot;Following the steps permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;This is where I am now going to basically follow the steps in &lt;a href=&quot;https://medium.com/the-prefect-blog/prefect-server-101-deploying-to-google-cloud-platform-47354b16afe2&quot;&gt;Prefect Server 101 Deploying to GCP&lt;/a&gt;. However, a few modifications have been made as some commands seemed to fail on my attempt.&lt;/p&gt;
&lt;h3 id=&quot;Installing-Python-and-Pip&quot; style=&quot;position:relative;&quot;&gt;Installing Python (and Pip)&lt;a href=&quot;#Installing-Python-and-Pip&quot; aria-label=&quot;Installing Python and Pip permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;h4 id=&quot;Update-your-package-information&quot; style=&quot;position:relative;&quot;&gt;Update your package information:&lt;a href=&quot;#Update-your-package-information&quot; aria-label=&quot;Update your package information permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h4&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token function&quot;&gt;sudo&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;apt&lt;/span&gt; update&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h4 id=&quot;Check-Python3-is-installed&quot; style=&quot;position:relative;&quot;&gt;Check Python3 is installed:&lt;a href=&quot;#Check-Python3-is-installed&quot; aria-label=&quot;Check Python3 is installed permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;It was already installed on my Azure Standard_B2s machine.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;python3 &lt;span class=&quot;token parameter variable&quot;&gt;--version&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h4 id=&quot;Install-Pip&quot; style=&quot;position:relative;&quot;&gt;Install Pip:&lt;a href=&quot;#Install-Pip&quot; aria-label=&quot;Install Pip permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h4&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token function&quot;&gt;sudo&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;apt&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;install&lt;/span&gt; python3-pip&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h4 id=&quot;Install-the-Python-3-virtual-environment-manager&quot; style=&quot;position:relative;&quot;&gt;Install the Python 3 virtual environment manager:&lt;a href=&quot;#Install-the-Python-3-virtual-environment-manager&quot; aria-label=&quot;Install the Python 3 virtual environment manager permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h4&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token function&quot;&gt;sudo&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;apt&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;install&lt;/span&gt; python3-venv&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h4 id=&quot;Installing-Docker&quot; style=&quot;position:relative;&quot;&gt;Installing Docker&lt;a href=&quot;#Installing-Docker&quot; aria-label=&quot;Installing Docker permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h4&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token function&quot;&gt;sudo&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;apt&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;install&lt;/span&gt; docker.io&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h4 id=&quot;Add-your-user-to-the-docker-group&quot; style=&quot;position:relative;&quot;&gt;Add your user to the docker group&lt;a href=&quot;#Add-your-user-to-the-docker-group&quot; aria-label=&quot;Add your user to the docker group permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h4&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token function&quot;&gt;sudo&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;usermod&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-aG&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;docker&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;${&lt;span class=&quot;token environment constant&quot;&gt;USER&lt;/span&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;After this you will need to disconnect your SSH session and reconnect to pickup the new group membership.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;# From remote&lt;/span&gt;
&lt;span class=&quot;token builtin class-name&quot;&gt;exit&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;# From local&lt;/span&gt;
&lt;span class=&quot;token function&quot;&gt;ssh&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-i&lt;/span&gt; C:&lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;Users&lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;username&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;.ssh&lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;prefect_key.pem azureuser@&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;azure-vm-public-ip&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h4 id=&quot;Get-the-Docker-Compose-repository&quot; style=&quot;position:relative;&quot;&gt;Get the Docker Compose repository:&lt;a href=&quot;#Get-the-Docker-Compose-repository&quot; aria-label=&quot;Get the Docker Compose repository permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h4&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token function&quot;&gt;sudo&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;curl&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-L&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;https://github.com/docker/compose/releases/download/1.25.4/docker-compose-&lt;span class=&quot;token variable&quot;&gt;&lt;span class=&quot;token variable&quot;&gt;$(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;uname&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-s&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;)&lt;/span&gt;&lt;/span&gt;-&lt;span class=&quot;token variable&quot;&gt;&lt;span class=&quot;token variable&quot;&gt;$(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;uname&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-m&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&quot;&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-o&lt;/span&gt; /usr/local/bin/docker-compose&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h4 id=&quot;Apply-executable-permissions&quot; style=&quot;position:relative;&quot;&gt;Apply executable permissions:&lt;a href=&quot;#Apply-executable-permissions&quot; aria-label=&quot;Apply executable permissions permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h4&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token function&quot;&gt;sudo&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;chmod&lt;/span&gt; +x /usr/local/bin/docker-compose&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;Installing-Prefect&quot; style=&quot;position:relative;&quot;&gt;Installing Prefect&lt;a href=&quot;#Installing-Prefect&quot; aria-label=&quot;Installing Prefect permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Now let’s install Prefect. We are going to do this from a virtual Python environment. Firstly, create and activate your virtual environment.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;python3 &lt;span class=&quot;token parameter variable&quot;&gt;-m&lt;/span&gt; venv prefect
&lt;span class=&quot;token builtin class-name&quot;&gt;source&lt;/span&gt; prefect/bin/activate&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;pip &lt;span class=&quot;token function&quot;&gt;install&lt;/span&gt; prefect&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h4 id=&quot;Starting-Prefect-Server&quot; style=&quot;position:relative;&quot;&gt;Starting Prefect Server&lt;a href=&quot;#Starting-Prefect-Server&quot; aria-label=&quot;Starting Prefect Server permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;Before we start the server infrastructure, we need to modify the UI endpoint so it knows how to access the GraphQL server correctly (note that this requires Prefect version 0.10.3+). We can do this by creating a user &lt;code class=&quot;language-text&quot;&gt;config.toml&lt;/code&gt; and adding the endpoint of our VM:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token function&quot;&gt;mkdir&lt;/span&gt; ~/.prefect
&lt;span class=&quot;token function&quot;&gt;touch&lt;/span&gt; ~/.prefect/config.toml&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Add the following to the file we just created (replacing the text in bold with your own IP). Note, I used &lt;strong&gt;vim&lt;/strong&gt; to edit the file.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;toml&quot;&gt;&lt;pre class=&quot;language-toml&quot;&gt;&lt;code class=&quot;language-toml&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token table class-name&quot;&gt;server&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token table class-name&quot;&gt;server.ui&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;token key property&quot;&gt;apollo_url&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;http://&amp;lt;azure-vm-public-ip&gt;:4200/graphql&quot;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Start the prefect server!&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;prefect server start &lt;span class=&quot;token parameter variable&quot;&gt;--expose&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Once the output looks like the following, go to this URL in your browser &lt;a href=&quot;http://azure-vm-public-ip:8080&quot;&gt;http://azure-vm-public-ip:8080&lt;/a&gt;.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;log&quot;&gt;&lt;pre class=&quot;language-log&quot;&gt;&lt;code class=&quot;language-log&quot;&gt; WELCOME TO

   _____  _____  ______ ______ ______ _____ _______    _____ ______ _______      ________ _____
  &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt;  __ \&lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt;  __ \&lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt;  ____&lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt;  ____&lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt;  ____&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt; ____&lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt;__   __&lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt;  &lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt; ____&lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt;  ____&lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt;  __ \ \    &lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;  ____&lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt;  __ \
  &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt;__&lt;span class=&quot;token operator&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt;__&lt;span class=&quot;token operator&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt;__  &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt;__  &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt;__ &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt;       &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt;    &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;(&lt;/span&gt;___ &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt;__  &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt;__&lt;span class=&quot;token operator&quot;&gt;)&lt;/span&gt; \ \  &lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt;__  &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt;__&lt;span class=&quot;token operator&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt;
  &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt;  ___&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt;  _  &lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt;  __&lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt;  __&lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt;  __&lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt;       &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt;     \___ \&lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt;  __&lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt;  _  &lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt; \ \&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt;  __&lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt;  _  &lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;
  &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt;    &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; \ \&lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt;____&lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt;    &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt;___&lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt;____   &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt;     ____&lt;span class=&quot;token operator&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt;____&lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; \ \  \  &lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;  &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt;____&lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; \ \
  &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt;_&lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt;    &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt;_&lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt;  \_\______&lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt;_&lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt;    &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt;______\_____&lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt;  &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt;_&lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt;    &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt;_____&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt;______&lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt;_&lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt;  \_\  \&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;   &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt;______&lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt;_&lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt;  \_\


   Visit &lt;span class=&quot;token url&quot;&gt;http://localhost:8080&lt;/span&gt; to get started&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 956px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/48af8752accae687ff8697eb66847778/78204/prefect-01-started.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 82%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAQCAIAAACZeshMAAAACXBIWXMAAAsTAAALEwEAmpwYAAACKUlEQVQoz5WSTWtTQRSG7w8SmgpCorRa09hWrMWtiDv/QLVJatMm2iS2AReupRhtkzaNxpgaLIJLJdJk18SNUBCUEJsv73zP3Hvk3ltDUq3iwzDMDOflvOec0XaKu/lC8WWhmNrKprdzj9fWk8/Sa8nUk/XNp6lMKpNLb+dSmdzG1ovNjWwy/Tybf5Pfeftq912u8Fobm7g6On5l1DftPj/pGbvs8lw8deaCy+0dcntdZ31D7nGXxzd8buL0yNTwyJTbO3Np5sbktZvT12/dXohos8Glu+HYbHBpPhzzh+7fiz8MhKKLy4n58IPQcuLOQnQuFJ0LxfyL8WBkJRhZDYRX/OHVQCQRTzzSuBCNRqPdaoGFCUfYB7N3teCcU0qlFIahAGBvr6xhjNvtFkI6F4JxC2d3YMxaDhhhXdcxIYRSACiVPmpSSnMwwz8xDOMos1LKNnhcr5SSNo5JJ8YJOy7+nV7oyZkrGudcCCGl7KvUehFCcM4Jw5RjwjFmCFEdUR1ThIiuQJZKJY0z1ul0DptNpZRhI6VECCmlOOeHev1L/eDg6+fv3foP1unSdlNv1NvfqIk+lN5rTlohRM+kaZqCcwCQVtlCCkkpU1IZhmkaphSKEmbZLlcGaqY2Az1TEmNEKLVHaA3OqoUSACiXK5rjs9vtAgChnDLeL3Z688eG7e9XLbHlUwgAwIQRynpf6+/iWu3TgO3/Fgsh1C+cCZ107X8HgGq19hMdw0ybfjJZqgAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;prefect 01 started&quot;
        title=&quot;prefect 01 started&quot;
        src=&quot;/static/48af8752accae687ff8697eb66847778/78204/prefect-01-started.png&quot;
        srcset=&quot;/static/48af8752accae687ff8697eb66847778/0eb09/prefect-01-started.png 500w,
/static/48af8752accae687ff8697eb66847778/78204/prefect-01-started.png 956w&quot;
        sizes=&quot;(max-width: 956px) 100vw, 956px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;For the remaining documentation please refer to &lt;a href=&quot;https://medium.com/the-prefect-blog/prefect-server-101-deploying-to-google-cloud-platform-47354b16afe2&quot;&gt;Prefect Server 101 Deploying to GCP&lt;/a&gt;.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Secret Santa with Node.js and Gmail]]></title><description><![CDATA[What is Secret Santa  an arrangement by which a group of friends or colleagues exchange Christmas presents anonymously, each member of the…]]></description><link>https://mortimer.xyz/secret-santa-node/</link><guid isPermaLink="false">https://mortimer.xyz/secret-santa-node/</guid><pubDate>Sat, 16 Oct 2021 12:00:00 GMT</pubDate><content:encoded>&lt;h2 id=&quot;What-is-Secret-Santa&quot; style=&quot;position:relative;&quot;&gt;What is Secret Santa&lt;a href=&quot;#What-is-Secret-Santa&quot; aria-label=&quot;What is Secret Santa permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 704px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/bea3d043b2c88eb72a14b89dedb5dcb3/3be76/secretsanta-googledefinition.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 10.4%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAACCAYAAABYBvyLAAAACXBIWXMAAA7DAAAOwwHHb6hkAAAASElEQVQI153MQQqAMAwAQf//RJtiUa/xoGlaBLOiLxAH9rrD4Re67eQsTKUgIqQxMS8rj4h4+2roZ1C9o6qYVVprmBnu/mt4A/P+nE41GqMAAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;secretsanta googledefinition&quot;
        title=&quot;secretsanta googledefinition&quot;
        src=&quot;/static/bea3d043b2c88eb72a14b89dedb5dcb3/3be76/secretsanta-googledefinition.png&quot;
        srcset=&quot;/static/bea3d043b2c88eb72a14b89dedb5dcb3/0eb09/secretsanta-googledefinition.png 500w,
/static/bea3d043b2c88eb72a14b89dedb5dcb3/3be76/secretsanta-googledefinition.png 704w&quot;
        sizes=&quot;(max-width: 704px) 100vw, 704px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;an arrangement by which a group of friends or colleagues exchange Christmas presents anonymously, each member of the group being assigned another member for whom to provide a small gift, typically costing no more than a set amount.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;We have an extra requirement in our family:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;No person should get their partner in the random anonymous allocation of Secret Santa.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&quot;Other-options&quot; style=&quot;position:relative;&quot;&gt;Other options&lt;a href=&quot;#Other-options&quot; aria-label=&quot;Other options permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;A quick google search shows that there are much easier ways to get a random Secret Santa emailing system going. I have listed the top two Google search results here. But what is the fun in that. Let’s learn something about sending email with &lt;code class=&quot;language-text&quot;&gt;Node.js&lt;/code&gt;.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://www.elfster.com/secret-santa-generator/&quot;&gt;Elfster&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.drawnames.com.au/&quot;&gt;Drawnames&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;Forking-a-Git-repository&quot; style=&quot;position:relative;&quot;&gt;Forking a Git repository&lt;a href=&quot;#Forking-a-Git-repository&quot; aria-label=&quot;Forking a Git repository permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;I had assumed I wasn’t the first person to want to, or have the need to give this a try. My first step was to see what Git repositories already existed. I found one that did 90% of what I wanted (credit to &lt;strong&gt;Dmitri Kunin&lt;/strong&gt;).&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/DKunin/secret-santa-module&quot;&gt;https://github.com/DKunin/secret-santa-module&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 939px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/b92cb4f540ebfda381d1fe75202f8255/c13bc/secretsanta-fork.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 13.999999999999998%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAADCAIAAAAcOLh5AAAACXBIWXMAAAsTAAALEwEAmpwYAAAAnElEQVQI1wXBwW6DMAwAUO5bq40lxrFNSExCgJKBekFIPfX/P6rvNdfrjRy9Fuq1BW6BLXrNtazPPO/1OPO8T8uBrMjaUQCKNJTBp/J/NhJXoBElAyfj1FIyGDqK4AK4gKzUj+O0OVFwg4RZXHj06VnqKmNjuPzRZGUFX8FvXdgtpV9D33dz+4Gvu2kta96QowGJUxX0B/prORbjPi1NFgnfLt2KAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;secretsanta fork&quot;
        title=&quot;secretsanta fork&quot;
        src=&quot;/static/b92cb4f540ebfda381d1fe75202f8255/c13bc/secretsanta-fork.png&quot;
        srcset=&quot;/static/b92cb4f540ebfda381d1fe75202f8255/0eb09/secretsanta-fork.png 500w,
/static/b92cb4f540ebfda381d1fe75202f8255/c13bc/secretsanta-fork.png 939w&quot;
        sizes=&quot;(max-width: 939px) 100vw, 939px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;So I forked the repository and started making changes.&lt;/p&gt;
&lt;h2 id=&quot;Stop-partners-from-getting-each-other&quot; style=&quot;position:relative;&quot;&gt;Stop partners from getting each other&lt;a href=&quot;#Stop-partners-from-getting-each-other&quot; aria-label=&quot;Stop partners from getting each other permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The repository I forked used another package called &lt;a href=&quot;https://github.com/DKunin/secret-santa-shuffler&quot;&gt;secret-santa-shuffler&lt;/a&gt; that was also created by &lt;strong&gt;Dmitri Kunin&lt;/strong&gt;. I took the &lt;code class=&quot;language-text&quot;&gt;index.js&lt;/code&gt; from this module and created it as a separate module in my forked repository so I could modify it.&lt;/p&gt;
&lt;p&gt;I added a function (&lt;code class=&quot;language-text&quot;&gt;partner()&lt;/code&gt;) to get the partner of a sender (&lt;code class=&quot;language-text&quot;&gt;s&lt;/code&gt;). Then added to the already existing condition so that I had both of:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;(sender (&lt;code class=&quot;language-text&quot;&gt;s&lt;/code&gt;) is not equal to receiver (&lt;code class=&quot;language-text&quot;&gt;receivers[j]&lt;/code&gt;))&lt;/li&gt;
&lt;li&gt;(receiver (&lt;code class=&quot;language-text&quot;&gt;receivers[j]&lt;/code&gt;) is not equal to sender’s partner (&lt;code class=&quot;language-text&quot;&gt;partner()&lt;/code&gt;))&lt;/li&gt;
&lt;/ul&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;js&quot;&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// If condition prior to change&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;s &lt;span class=&quot;token operator&quot;&gt;!==&lt;/span&gt; receivers&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;j&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token operator&quot;&gt;...&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;// if condition after change&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;s &lt;span class=&quot;token operator&quot;&gt;!==&lt;/span&gt; receivers&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;j&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; receivers&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;j&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;partner&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token operator&quot;&gt;...&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2 id=&quot;Use-Gmail-as-the-email-service&quot; style=&quot;position:relative;&quot;&gt;Use Gmail as the email service&lt;a href=&quot;#Use-Gmail-as-the-email-service&quot; aria-label=&quot;Use Gmail as the email service permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Converting to use Gmail was quite simple. I registered a new Gmail address for the purpose of sending these emails. The extra step is to allow &lt;a href=&quot;https://support.google.com/accounts/answer/6010255&quot;&gt;Less secure apps&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Since I am using this once a year, and only using for a fleeting moment, I can turn this feature on &lt;a href=&quot;https://myaccount.google.com/lesssecureapps&quot;&gt;https://myaccount.google.com/lesssecureapps&lt;/a&gt;, and then turn it off when I am finished.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 614px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/df0445565f563699829230037d066275/c42f5/secretsanta-gmaillesssecure.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 53.800000000000004%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAALCAYAAAB/Ca1DAAAACXBIWXMAAA7DAAAOwwHHb6hkAAABUklEQVQoz41SB27DMAz0///VF/QBTtFay5ZXtGJZuoLySNCmg8CZwpk6UiSrnnMI3kAxDiEV2rbFuq4gyzmf/sBfVq0xIsaI0+9iz+xZgq/JqtuywFiLpmEF7+8fYIzhcnlDXV/AOUdd15jn+VexU/A4rCmV6ggpJcR1qzjt/H+MtKpbCBiHsWAap4KhHzBPM+ZpwjRt3DgMW9w4otcave7Lmbih78+klfceUkgoAg1FtWANg9a6DKhVCkqp4gUX6NqueIIUfOMZg7VuF3QOUuwBcrtIl6gKKSUE4ydHcYXjoiRWakDXzeBCI4SwCVKfrLFw1hZvrgbeB1AiawwM/XOuXPDOwxB3NaUiFwDnM2J86CF9tiHcB/LoH1flu6USp6eI20I93AVpJegp1Leu23vXtuWZcU//fE0ywpLx8mox23gXPJD2ag+cfPoZh3DOW/wnwf5W0cGEcKMAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;secretsanta gmaillesssecure&quot;
        title=&quot;secretsanta gmaillesssecure&quot;
        src=&quot;/static/df0445565f563699829230037d066275/c42f5/secretsanta-gmaillesssecure.png&quot;
        srcset=&quot;/static/df0445565f563699829230037d066275/0eb09/secretsanta-gmaillesssecure.png 500w,
/static/df0445565f563699829230037d066275/c42f5/secretsanta-gmaillesssecure.png 614w&quot;
        sizes=&quot;(max-width: 614px) 100vw, 614px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;js&quot;&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// Using mailgun prior to change&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; mg &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;require&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;nodemailer-mailgun-transport&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; transporter &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; nodemailer&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;createTransport&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
  &lt;span class=&quot;token function&quot;&gt;mg&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token literal-property property&quot;&gt;from&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;secret@santa.com&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token literal-property property&quot;&gt;to&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token literal-property property&quot;&gt;subject&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;Secret Santa&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token literal-property property&quot;&gt;html&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;// Using gmail after change&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; transporter &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; nodemailer&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;createTransport&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token literal-property property&quot;&gt;service&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;gmail&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token literal-property property&quot;&gt;auth&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token literal-property property&quot;&gt;user&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; useremail&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token literal-property property&quot;&gt;pass&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; userpassword&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2 id=&quot;Sending-the-emails&quot; style=&quot;position:relative;&quot;&gt;Sending the emails&lt;a href=&quot;#Sending-the-emails&quot; aria-label=&quot;Sending the emails permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;My modified fork of &lt;strong&gt;Dmitri Kunin’s&lt;/strong&gt; repository is here &lt;a href=&quot;https://github.com/mortie23/secret-santa-module&quot;&gt;https://github.com/mortie23/secret-santa-module&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id=&quot;Installation&quot; style=&quot;position:relative;&quot;&gt;Installation&lt;a href=&quot;#Installation&quot; aria-label=&quot;Installation permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;git clone
npm install&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;Configure-your-environment-variables&quot; style=&quot;position:relative;&quot;&gt;Configure your environment variables&lt;a href=&quot;#Configure-your-environment-variables&quot; aria-label=&quot;Configure your environment variables permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;ini&quot;&gt;&lt;pre class=&quot;language-ini&quot;&gt;&lt;code class=&quot;language-ini&quot;&gt;&lt;span class=&quot;token key attr-name&quot;&gt;useremail&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token value attr-value&quot;&gt;hollywoodsecretsanta@gmail.com&lt;/span&gt;
&lt;span class=&quot;token key attr-name&quot;&gt;userpassword&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token value attr-value&quot;&gt;qwerty1234&lt;/span&gt;
&lt;span class=&quot;token key attr-name&quot;&gt;secretsantaname&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token value attr-value&quot;&gt;Hollywood&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;Debug-mode-by-default&quot; style=&quot;position:relative;&quot;&gt;Debug mode by default&lt;a href=&quot;#Debug-mode-by-default&quot; aria-label=&quot;Debug mode by default permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;By default both scripts &lt;code class=&quot;language-text&quot;&gt;confirmemail.js&lt;/code&gt; and &lt;code class=&quot;language-text&quot;&gt;secretsanta.js&lt;/code&gt; scripts have &lt;code class=&quot;language-text&quot;&gt;debug: true&lt;/code&gt;. Suggest you run it in this mode first. In this mode no emails are sent, only results logged to console.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;js&quot;&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// Example&lt;/span&gt;
&lt;span class=&quot;token function&quot;&gt;santaModule&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;list&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token literal-property property&quot;&gt;debug&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token literal-property property&quot;&gt;template&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&amp;lt;div&gt;&amp;lt;h2&gt;It&apos;s the &lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;secretsantaname&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt; Secret Santa!&amp;lt;/h2&gt;&amp;lt;div&gt;You should prepare for &amp;lt;h3&gt;{%=o.to%}&amp;lt;/h3&gt;&amp;lt;/div&gt;&amp;lt;/div&gt;&lt;/span&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;then&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;log&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;When you are happy, turn this flag to false.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;# Run this first if you want to confirm that the emails are reaching people&lt;/span&gt;
&lt;span class=&quot;token function&quot;&gt;npm&lt;/span&gt; run confirmemail
&lt;span class=&quot;token comment&quot;&gt;# Then run this to send them out&lt;/span&gt;
&lt;span class=&quot;token function&quot;&gt;npm&lt;/span&gt; run secretsanta&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2 id=&quot;List-Example&quot; style=&quot;position:relative;&quot;&gt;List Example&lt;a href=&quot;#List-Example&quot; aria-label=&quot;List Example permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;This program will not allow partners to have each other in the Secret Santa.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;csv&quot;&gt;&lt;pre class=&quot;language-csv&quot;&gt;&lt;code class=&quot;language-csv&quot;&gt;&lt;span class=&quot;token value&quot;&gt;Name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;token value&quot;&gt;email&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;token value&quot;&gt;partner&lt;/span&gt;
&lt;span class=&quot;token value&quot;&gt;Brad Pitt&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;token value&quot;&gt;brad.pitt@email.com&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;token value&quot;&gt;angelina.jolie@email.com&lt;/span&gt;
&lt;span class=&quot;token value&quot;&gt;Angelina Jolie&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;token value&quot;&gt;angelina.jolie@email.com&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;token value&quot;&gt;brad.pitt@email.com&lt;/span&gt;
&lt;span class=&quot;token value&quot;&gt;Justin Beiber&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;token value&quot;&gt;justin.beiber@email.com&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;token value&quot;&gt;hailey.bieber@email.com&lt;/span&gt;
&lt;span class=&quot;token value&quot;&gt;Hailey Bieber&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;token value&quot;&gt;hailey.bieber@email.com&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;token value&quot;&gt;justin.beiber@email.com&lt;/span&gt;
&lt;span class=&quot;token value&quot;&gt;Jake Gyllenhaal&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;token value&quot;&gt;jake.gyllenhaal@email.com&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2 id=&quot;confirmemail-Sent-message&quot; style=&quot;position:relative;&quot;&gt;confirmemail Sent message&lt;a href=&quot;#confirmemail-Sent-message&quot; aria-label=&quot;confirmemail Sent message permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;It’s the Hollywood Secret Santa!&lt;br&gt;
Please reply to confirm your email&lt;/p&gt;
&lt;h2 id=&quot;secretsanta-Sent-message&quot; style=&quot;position:relative;&quot;&gt;secretsanta Sent message&lt;a href=&quot;#secretsanta-Sent-message&quot; aria-label=&quot;secretsanta Sent message permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;It’s the Hollywood Secret Santa!&lt;br&gt;
You should prepare a present for: Brad Pitt (&lt;a href=&quot;mailto:brad.pitt@email.com&quot;&gt;brad.pitt@email.com&lt;/a&gt;)&lt;/p&gt;
&lt;h3 id=&quot;Debug-mode-on-output&quot; style=&quot;position:relative;&quot;&gt;Debug mode on output&lt;a href=&quot;#Debug-mode-on-output&quot; aria-label=&quot;Debug mode on output permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;json&quot;&gt;&lt;pre class=&quot;language-json&quot;&gt;&lt;code class=&quot;language-json&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&quot;giver&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;brad.pitt@email.com&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&quot;string&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;&amp;lt;div&gt;&amp;lt;h2&gt;It&apos;s the Mortimer Secret Santa!&amp;lt;/h2&gt;&amp;lt;div&gt;You should prepare for &amp;lt;h3&gt;undefined (hailey.bieber@email.com)&amp;lt;/h3&gt;&amp;lt;/div&gt;&amp;lt;/div&gt;&quot;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&quot;giver&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;angelina.jolie@email.com&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&quot;string&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;&amp;lt;div&gt;&amp;lt;h2&gt;It&apos;s the Mortimer Secret Santa!&amp;lt;/h2&gt;&amp;lt;div&gt;You should prepare for &amp;lt;h3&gt;undefined (jake.gyllenhaal@email.com)&amp;lt;/h3&gt;&amp;lt;/div&gt;&amp;lt;/div&gt;&quot;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&quot;giver&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;justin.beiber@email.com&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&quot;string&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;&amp;lt;div&gt;&amp;lt;h2&gt;It&apos;s the Mortimer Secret Santa!&amp;lt;/h2&gt;&amp;lt;div&gt;You should prepare for &amp;lt;h3&gt;undefined (brad.pitt@email.com)&amp;lt;/h3&gt;&amp;lt;/div&gt;&amp;lt;/div&gt;&quot;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&quot;giver&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;hailey.bieber@email.com&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&quot;string&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;&amp;lt;div&gt;&amp;lt;h2&gt;It&apos;s the Mortimer Secret Santa!&amp;lt;/h2&gt;&amp;lt;div&gt;You should prepare for &amp;lt;h3&gt;undefined (angelina.jolie@email.com)&amp;lt;/h3&gt;&amp;lt;/div&gt;&amp;lt;/div&gt;&quot;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&quot;giver&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;jake.gyllenhaal@email.com&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&quot;string&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;&amp;lt;div&gt;&amp;lt;h2&gt;It&apos;s the Mortimer Secret Santa!&amp;lt;/h2&gt;&amp;lt;div&gt;You should prepare for &amp;lt;h3&gt;undefined (justin.beiber@email.com)&amp;lt;/h3&gt;&amp;lt;/div&gt;&amp;lt;/div&gt;&quot;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;</content:encoded></item><item><title><![CDATA[How I develop Python in VSCode]]></title><description><![CDATA[Why Working on Javascript code in Visual Studio Code (VSCode) for the best part of last year got me use to all the good stuff…]]></description><link>https://mortimer.xyz/python-vscode/</link><guid isPermaLink="false">https://mortimer.xyz/python-vscode/</guid><pubDate>Sat, 28 Aug 2021 12:00:00 GMT</pubDate><content:encoded>&lt;h1 id=&quot;Why&quot; style=&quot;position:relative;&quot;&gt;Why&lt;a href=&quot;#Why&quot; aria-label=&quot;Why permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h1&gt;
&lt;p&gt;Working on Javascript code in Visual Studio Code (VSCode) for the best part of last year got me use to all the good stuff. Autoformatting, linting etc. Coming back to a role where Python and SQL are the primary languages and I had the need to find out the best way to develop in Python.&lt;/p&gt;
&lt;h2 id=&quot;Autoformatting&quot; style=&quot;position:relative;&quot;&gt;Autoformatting&lt;a href=&quot;#Autoformatting&quot; aria-label=&quot;Autoformatting permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;I’ll use this example valid Python code to demo my settings. This code isn’t nice. There is a lot of bad spacing, and some arguments being passed to a function of the wrong type.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;py&quot;&gt;&lt;pre class=&quot;language-py&quot;&gt;&lt;code class=&quot;language-py&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; pandas &lt;span class=&quot;token keyword&quot;&gt;as&lt;/span&gt; pd

&lt;span class=&quot;token keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;addcolumn&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;df&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;pd&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;DataFrame&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;pd&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;DataFrame&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token triple-quoted-string string&quot;&gt;&apos;&apos;&apos;
    Add a generic column to a dataframe
      :param df: the source dataframe
      :return: a dataframe with a new column populated with new value
    &apos;&apos;&apos;&lt;/span&gt;
    df&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;newcol&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;new value&apos;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; df
df &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; pd&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;DataFrame&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;numbers&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;colors&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;red&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;white&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;blue&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;Test add column&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;addcolumn&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;df&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;addition&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;x&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token builtin&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;y&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token builtin&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token builtin&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token triple-quoted-string string&quot;&gt;&apos;&apos;&apos;
    Add two numbers function using mypy types
      :param x: the first number
      :param y: the second number
      :return: the sum of the two arguments
    &apos;&apos;&apos;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; x&lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt;y
&lt;span class=&quot;token keyword&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;Test typing&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;addition&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;addition&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;1&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;2&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;As Donald Rumsfield said “But there are also unknown unknowns—the ones we don’t know we don’t know”. And if you didn’t know, we don’t have to manually enforce styles by hitting space bar a lot of times.&lt;/p&gt;
&lt;p&gt;To mirror the way I setup my VSCode, follow along.
Add this setting to you &lt;code class=&quot;language-text&quot;&gt;settings.json&lt;/code&gt;&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;json&quot;&gt;&lt;pre class=&quot;language-json&quot;&gt;&lt;code class=&quot;language-json&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;&quot;editor.formatOnSave&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 403px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/7cf8660fd6fb9801370ea22504db1351/6945a/python-vscode-01-settings.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 30.272952853598017%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAGCAIAAABM9SnKAAAACXBIWXMAAA7EAAAOxAGVKw4bAAABEUlEQVQY0z3LS26DMBSFYZbRdtAm2NcPsPELAr5gKCQ0hFG6/81USG2lb/APzsmMGoObg1tMmegpMFrbKoz9fp2fy+cztneMmzjX+YvOX3+Rt4oeTKaLwerRqGRUUkVfQrzouTNrHx7R3Vuztmat+KWgQXNU0CloJWngw8O7y7ydEz6snubpu/GLK8fU7l5NF3ur9RzU1NqbFk1wA8Yd4z7gw5tJQMtok9Fz4HAR0EmWSB6kHXiFvIxMdcL20idhsHAJeCfz4FhDT46cPOQ1kDpjpKlUUjyiWQXrXfxyeK/8YsLVDdsBNz9sSo1epsnMFR8k6wuGBeBxLgWWPHb2xmmUJIq8E+Qv/khAAXgMAP/9AC8QRVHgbA4IAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;python vscode 01 settings&quot;
        title=&quot;python vscode 01 settings&quot;
        src=&quot;/static/7cf8660fd6fb9801370ea22504db1351/6945a/python-vscode-01-settings.png&quot;
        srcset=&quot;/static/7cf8660fd6fb9801370ea22504db1351/6945a/python-vscode-01-settings.png 403w&quot;
        sizes=&quot;(max-width: 403px) 100vw, 403px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;When you save this setting you will get this toaster modal:&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 480px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/97c288afcf84c649a2ff6e6af5572842/9aebd/python-vscode-02-install-autopep8.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 29.791666666666668%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAGCAIAAABM9SnKAAAACXBIWXMAAA7EAAAOxAGVKw4bAAABJUlEQVQY0z3IO0/DMBiF4fyFVqihiZPY/nzPxY4dp4WEAakbYmCtygIbAxMSrIwMTPzhqlQgvTp6dBIBEegA2EPVnxZ7+gfAJzASGAkCxv8YGTgdBMQEcG9yKcFq6QWzkvfqF5xZLYMUPdCGM5uuSLoilylNV4TiGuW8RCaByqmMKWGbJmrpjAlNPUhhpbC1CUaHwd26diJlx4mnlS1z05nJiA0p2oSWVhlvtG+b2NSha8euHQsklot8uVinF/Tp4XO3fdzfvb89/xzuP3bbw+vh+2X/1bA5YdijtUSZQpk8l68FymSRqyJXJdKCRKgcqzzHESpPSitoPJ8JQ44XvShDJ66dmg2Nvr6xcjJ0dHquYZSVd3pyeu7UldOTVZPEXpOhhs0RarE9atuctGsAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;python vscode 02 install autopep8&quot;
        title=&quot;python vscode 02 install autopep8&quot;
        src=&quot;/static/97c288afcf84c649a2ff6e6af5572842/9aebd/python-vscode-02-install-autopep8.png&quot;
        srcset=&quot;/static/97c288afcf84c649a2ff6e6af5572842/9aebd/python-vscode-02-install-autopep8.png 480w&quot;
        sizes=&quot;(max-width: 480px) 100vw, 480px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;Click &lt;code class=&quot;language-text&quot;&gt;Yes&lt;/code&gt; and your terminal should load up a &lt;code class=&quot;language-text&quot;&gt;pip&lt;/code&gt; install command.
If you have issues &lt;code class=&quot;language-text&quot;&gt;pip install&lt;/code&gt;, that is a story for another time.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 1007px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/36ec256e975db05e370471647a682c8d/b82a7/python-vscode-03-install.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 19.8%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAECAIAAAABPYjBAAAACXBIWXMAAA7EAAAOxAGVKw4bAAAAvklEQVQI12NwcQr0D4oLSUh39o6w8Iqy8ok2sA/QsfUzs/ExMXAwMXQy1LM30ncwMnAwMnDU17Y11LPX1bIxNnDU17FjsLP26eueNm3ynAm90zpa+loaujxcgi1NXKzM3TXVLJQVjFSVTFQUjSEIxjaBsBmcHQIKcitzMsrSkwsyUwpSE3LdHQPtLT2tzT10tGw0VM3VlE1xIQY3pyBrOx9rdz9LBz9Tt3BdO39LzwhzjwhLB39TAydtTSs8mgErvTsJYpKk+gAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;python vscode 03 install&quot;
        title=&quot;python vscode 03 install&quot;
        src=&quot;/static/36ec256e975db05e370471647a682c8d/b82a7/python-vscode-03-install.png&quot;
        srcset=&quot;/static/36ec256e975db05e370471647a682c8d/0eb09/python-vscode-03-install.png 500w,
/static/36ec256e975db05e370471647a682c8d/1263b/python-vscode-03-install.png 1000w,
/static/36ec256e975db05e370471647a682c8d/b82a7/python-vscode-03-install.png 1007w&quot;
        sizes=&quot;(max-width: 1007px) 100vw, 1007px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;Now when we save the file, all that hideous formatting is gone. We don’t depend on ourselves to have to remember to hit &lt;code class=&quot;language-text&quot;&gt;Alt-Shift-F&lt;/code&gt; to format, or even worse, have to &lt;code class=&quot;language-text&quot;&gt;space bar&lt;/code&gt; our way through code to manually format it.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 1789px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/378707df5aafda7217cd6b02e1a53cb4/e4fd7/python-vscode-04-format.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 35.2%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAHCAIAAACHqfpvAAAACXBIWXMAAA7EAAAOxAGVKw4bAAABMElEQVQY0zXEB07FIAAA0N5ETdkUCh2UXSjf8d0xxnj/kxhX8vK6MJW8noM8DEkLiRpZjZxGboJO8cRtEWvlOpM5U5Wg/gNU1NttJ5BVfJ95mYc6kiCwE8hxaHhvBA1UZbHejHOTS+NjxjJhEeHge2KW/NRFHpuuRcbMQmA+Mh+ot8Q7ZFdVaD6Ne2P5gPHowwFDhaECXy9d1sdzN8l9m4+NOIU2ha3GVv1Cm1Y7bXeqXItQoC/4H/L1yualvXaKhUm2lZeJ5RH7AZgBbgJuAhjJPFsKM7diaUwVJCPgDnAHv7dyrB3GVtBAWCIsQWR7YMCPHhiILNNJmErXRtcDTwmoAH8AHeDgurycTvamMZ+IzcRl6n/txG06g493+vmGnu4vHu4uH89/Hs4XL/cgHV+tJ1V1AFZ2rwAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;python vscode 04 format&quot;
        title=&quot;python vscode 04 format&quot;
        src=&quot;/static/378707df5aafda7217cd6b02e1a53cb4/e4fd7/python-vscode-04-format.png&quot;
        srcset=&quot;/static/378707df5aafda7217cd6b02e1a53cb4/0eb09/python-vscode-04-format.png 500w,
/static/378707df5aafda7217cd6b02e1a53cb4/1263b/python-vscode-04-format.png 1000w,
/static/378707df5aafda7217cd6b02e1a53cb4/e4fd7/python-vscode-04-format.png 1789w&quot;
        sizes=&quot;(max-width: 1789px) 100vw, 1789px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;py&quot;&gt;&lt;pre class=&quot;language-py&quot;&gt;&lt;code class=&quot;language-py&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; pandas &lt;span class=&quot;token keyword&quot;&gt;as&lt;/span&gt; pd


&lt;span class=&quot;token keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;addcolumn&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;df&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; pd&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;DataFrame&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; pd&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;DataFrame&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token triple-quoted-string string&quot;&gt;&apos;&apos;&apos;
    Add a generic column to a dataframe
      :param df: the source dataframe
      :return: a dataframe with a new column populated with new value
    &apos;&apos;&apos;&lt;/span&gt;
    df&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;newcol&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;new value&apos;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; df


df &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; pd&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;DataFrame&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;numbers&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;colors&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;red&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;white&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;blue&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;Test add column&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;addcolumn&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;df&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;


&lt;span class=&quot;token keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;addition&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;x&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; y&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token triple-quoted-string string&quot;&gt;&apos;&apos;&apos;
    Add two numbers function using mypy types
      :param x: the first number
      :param y: the second number
      :return: the sum of the two arguments
    &apos;&apos;&apos;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; x&lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt;y


&lt;span class=&quot;token keyword&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;Test typing&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;addition&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;addition&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;1&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;2&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2 id=&quot;Typing-in-Python&quot; style=&quot;position:relative;&quot;&gt;Typing in Python&lt;a href=&quot;#Typing-in-Python&quot; aria-label=&quot;Typing in Python permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;These are &lt;strong&gt;type hints&lt;/strong&gt; and not static typing.&lt;/p&gt;
&lt;p&gt;Add these to your &lt;code class=&quot;language-text&quot;&gt;settings.json&lt;/code&gt;&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;json&quot;&gt;&lt;pre class=&quot;language-json&quot;&gt;&lt;code class=&quot;language-json&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;&quot;python.linting.mypyEnabled&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;&quot;python.linting.mypyPath&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;~/python-venv/myvenv/bin/mypy&quot;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;You will need to install &lt;code class=&quot;language-text&quot;&gt;mypy&lt;/code&gt; using &lt;code class=&quot;language-text&quot;&gt;pip install mypy&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Ok, now this will not break the program or stop it from running. The issue with the code, you may notice is with calling the &lt;code class=&quot;language-text&quot;&gt;addition&lt;/code&gt; function with &lt;code class=&quot;language-text&quot;&gt;str&lt;/code&gt; arguments.
It doesn’t cause an issue, but you can imagine another case where using the wrong type as arguments would cause an error, and we want our development environment to let us know this.&lt;/p&gt;
&lt;p&gt;We can run &lt;code class=&quot;language-text&quot;&gt;mypy&lt;/code&gt; from the command line against a python file to see what it outputs.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;powershell&quot;&gt;&lt;pre class=&quot;language-powershell&quot;&gt;&lt;code class=&quot;language-powershell&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;# Windows&lt;/span&gt;
mypy&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;exe &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;\addition&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;py&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The Windows one will return this:&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 1017px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/7ef466178caca83f04ddc90850c2529a/06fa9/python-vscode-05-mypy-cli.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 18.4%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAECAIAAAABPYjBAAAACXBIWXMAAAsSAAALEgHS3X78AAAA30lEQVQI1x3G226CMAAAUD5FUDQDpxZa7IVSLoVWUnSrU7dgYLolvuz/n5d4no5DhF6tBYiqDSjCJX8J0jDkz7AgSBdz5M/gfAZ9P16+4qkXeS6YepE7Ae4EOK1qq7KTxZvM97KyjO44NwSrXHQEawSrlGmCGwQrxjQhKo4KGJeUaIKVY7q+2/c4qbfrzNrx8vlrzNfpfO+vj8Phat+H2/ff5fxztOPp42bt0PcPa4ejHRndOZI0NdXpilMfqrQ1vGVAaN4qprNEIiTzzBDcJKiGcRlHBaN6m0iwEZ4L/gHQ3jt/1sYcxgAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;python vscode 05 mypy cli&quot;
        title=&quot;python vscode 05 mypy cli&quot;
        src=&quot;/static/7ef466178caca83f04ddc90850c2529a/06fa9/python-vscode-05-mypy-cli.png&quot;
        srcset=&quot;/static/7ef466178caca83f04ddc90850c2529a/0eb09/python-vscode-05-mypy-cli.png 500w,
/static/7ef466178caca83f04ddc90850c2529a/1263b/python-vscode-05-mypy-cli.png 1000w,
/static/7ef466178caca83f04ddc90850c2529a/06fa9/python-vscode-05-mypy-cli.png 1017w&quot;
        sizes=&quot;(max-width: 1017px) 100vw, 1017px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;This info will be surfaced right into VScode with the options we enabled in the &lt;code class=&quot;language-text&quot;&gt;settings.json&lt;/code&gt; file.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 1110px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/2488f5f7e436b45ec65f668c3e232dd7/ffc38/python-vscode-06-type-hints.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 10.999999999999998%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAACCAIAAADXZGvcAAAACXBIWXMAAAsSAAALEgHS3X78AAAAbElEQVQI1yXGUQ6DIAwAUM9itJQiQosUNsHNxL/t/udZFn9e3uCXFvTSeio3jXtaqwPOciTpHHfh5igvlA0I2c07RSMIMk9hGuOw+h710tdne36L78k9yCiHI8kp/Cas1hSyfxHKfbIVjcKcf5rOFxONmNYAAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;python vscode 06 type hints&quot;
        title=&quot;python vscode 06 type hints&quot;
        src=&quot;/static/2488f5f7e436b45ec65f668c3e232dd7/ffc38/python-vscode-06-type-hints.png&quot;
        srcset=&quot;/static/2488f5f7e436b45ec65f668c3e232dd7/0eb09/python-vscode-06-type-hints.png 500w,
/static/2488f5f7e436b45ec65f668c3e232dd7/1263b/python-vscode-06-type-hints.png 1000w,
/static/2488f5f7e436b45ec65f668c3e232dd7/ffc38/python-vscode-06-type-hints.png 1110w&quot;
        sizes=&quot;(max-width: 1110px) 100vw, 1110px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;h2 id=&quot;Docstrings&quot; style=&quot;position:relative;&quot;&gt;Docstrings&lt;a href=&quot;#Docstrings&quot; aria-label=&quot;Docstrings permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Last but not least, using standard docstrings for functions helps to surface other info as well.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 646px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/2c567f966dc643460819842af975a677/3ffe4/python-vscode-07-docstring.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 52%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAKCAIAAAA7N+mxAAAACXBIWXMAAAsSAAALEgHS3X78AAABrUlEQVQoz12RyW7bQBBE9RGJJVmKIokiOfu+cIbLkNRqO47jiw75/w8JYiGIEqBQQB9eV6N6IniHgK+0l/4kw4WbvSSR51aASIpgoEWl22X6XvnO3DQRvIOl3+0MJT2jA4I1YyOlI0YtJYmQhGBDac/5yOjA2EBJdwezDpQelBbCoNTZuW+cj0qepDgoeVbqVIV3zoYit0XuityVhf8/uSwcKBznR2OeGUmSH6Q8anNR6mzti1QnhBrOBozbjxX2DywSBB4UFpSeY8FEhc2AVEIggK8UrFm2lmTL841cr0W2EV8WYLUEf5NB6YvcSm6t0lJUCDuhaoodKSUFCgNNCkGhgUDOp9vZw+Zxlt3D7mPQecaDH2I1ahmtbp1NlR+saY+HV8kjKOVivps9bBbz/J+zeXXWVXr8tOzq/fPlu2C+DqPV7dA/tfXx7fWqRJx+3qxXcLUs59Pt7WcTo0aMYnz5mS5vyu3r/r1PPzyrrIzRJEY8p5WWjTVdHQ59egp+yDMOQYVRnGg1IBiYPihzaNI11NfKvQWRJIqW/a4NgkBxy2hLcYNgpLhhpL35L3QOctEErLdMAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;python vscode 07 docstring&quot;
        title=&quot;python vscode 07 docstring&quot;
        src=&quot;/static/2c567f966dc643460819842af975a677/3ffe4/python-vscode-07-docstring.png&quot;
        srcset=&quot;/static/2c567f966dc643460819842af975a677/0eb09/python-vscode-07-docstring.png 500w,
/static/2c567f966dc643460819842af975a677/3ffe4/python-vscode-07-docstring.png 646w&quot;
        sizes=&quot;(max-width: 646px) 100vw, 646px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Git Cleanup]]></title><description><![CDATA[What is the issue? Collaborating on a repo and someone accedientally commits a very large file that is not very compressable. It makes the…]]></description><link>https://mortimer.xyz/git-cleanup/</link><guid isPermaLink="false">https://mortimer.xyz/git-cleanup/</guid><pubDate>Thu, 29 Jul 2021 12:00:00 GMT</pubDate><content:encoded>&lt;h1 id=&quot;What-is-the-issue&quot; style=&quot;position:relative;&quot;&gt;What is the issue?&lt;a href=&quot;#What-is-the-issue&quot; aria-label=&quot;What is the issue permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h1&gt;
&lt;p&gt;Collaborating on a repo and someone accedientally commits a very large file that is not very compressable. It makes the repo size way too large and needs to be reomved.
We’ll use the repo &lt;a href=&quot;https://github.com/mortie23/git-cleaning&quot;&gt;here&lt;/a&gt; as an example. Firstly let’s look at the size of the directory, including the &lt;code class=&quot;language-text&quot;&gt;.git&lt;/code&gt; database. Before things go wrong.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 350px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/bae0c977192029283b6e85bd37ec10dd/f8bcd/dotgit.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 57.14285714285714%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAALCAIAAADwazoUAAAACXBIWXMAAAsSAAALEgHS3X78AAABJElEQVQoz6VR70/CMBTs////+F2jImEo+yGCsHYM7ECBdWzi1pm+MxssGQnoBy+XvLR9d/faMgAoCkgJHsDn4OICOYRAkoAIDRjSrDoLZ7QMES3pTVZGMkJUUx7Y7LyM4LgwphELAe+Z9in24e5r73FuWt4nIILWuH+AUoclq8rqnQZ96l193nfi6xttWcntne4/keOR41ZRtgvbgethYCNaHo2q5LqQ0cit7Uq+2rYYjQbdjj8cmrKsovL8yGba1p1rcVkU4VzGsVpv461S681G7dI0y/JCn7Qb07ZgVIvTLOs9epGUk4kvuPBcd75Y+D6PVVLPeP4VWOP4PZ12lVJxjUvd58Vlqcdja/2xCoJZEIhC60Pg7y4M/wA7/Uj6M60t/gGAmndhm/MnCQAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;dotgit directory&quot;
        title=&quot;dotgit directory&quot;
        src=&quot;/static/bae0c977192029283b6e85bd37ec10dd/f8bcd/dotgit.png&quot;
        srcset=&quot;/static/bae0c977192029283b6e85bd37ec10dd/f8bcd/dotgit.png 350w&quot;
        sizes=&quot;(max-width: 350px) 100vw, 350px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;# Check the complete size of the directory&lt;/span&gt;
&lt;span class=&quot;token function&quot;&gt;du&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-hcsb&lt;/span&gt; &lt;span class=&quot;token builtin class-name&quot;&gt;.&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;# 44,924 .&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Ok, so it is only about 45 K in size.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token function&quot;&gt;git&lt;/span&gt; checkout master
&lt;span class=&quot;token function&quot;&gt;mkdir&lt;/span&gt; lotsoffiles
&lt;span class=&quot;token function&quot;&gt;touch&lt;/span&gt; ./lotsoffiles/lotsoffiles-gen.sh&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;This is the &lt;code class=&quot;language-text&quot;&gt;lotsoffiles-gen.sh&lt;/code&gt; script:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token shebang important&quot;&gt;#! /bin/bash&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;token for-or-select variable&quot;&gt;n&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;..&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;20&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;do&lt;/span&gt;
  truncate &lt;span class=&quot;token parameter variable&quot;&gt;-s&lt;/span&gt; 5K &lt;span class=&quot;token function&quot;&gt;file&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$n&lt;/span&gt;.txt
&lt;span class=&quot;token keyword&quot;&gt;done&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;## this is a big file that is not very compressable&lt;/span&gt;
&lt;span class=&quot;token function&quot;&gt;wget&lt;/span&gt; https://chromedriver.storage.googleapis.com/93.0.4577.15/chromedriver_mac64.zip&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Run the script to generate the files:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token builtin class-name&quot;&gt;cd&lt;/span&gt; lotsoffiles
&lt;span class=&quot;token function&quot;&gt;bash&lt;/span&gt; ./lotsoffiles-gen.sh&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 394px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/e5a52f5af9a7ec9889bede619f2db995/b2f6b/bigfile.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 68.78172588832487%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAOCAIAAACgpqunAAAACXBIWXMAAAsSAAALEgHS3X78AAABWUlEQVQoz4WQ3VaDMBCEef8X8jHUG4ttpaUIJORnN79tIAkeW6reWOZu55xvZ84UxhjvvbHe+cs4TfP/CmF0/jJNMYRwcwoAsNYyoZuOobYPYOfPTUfDGH+cIuc8z3OKJue0eHmer+ZNKaf858wpxRjTVcXNd/2TpNv9x6E+1ZtNWW7e9h9V23ZlWW53u01ZEkK+H6VsjCZ0EJxxIYvbP40cJCN04Jwj4jRN41UhhCnGy/k8juOSnL+TnbPG2gUG1EKI+lQPAwWA+aFSSlopQLXAUkoh+LE+EkKVwlXYaIXqDiutEbHrWkKpUmoVdtZoY+61ARgbdrvtqWmE4I/hGCOCFPI+GCIqhX3fCymdtavJ3jvr3E8yAsjqUDVNg2uDxZi0Qon4C3POqkP12bawDkeFIAF+axujGWNSgjVmpXbOl7N33i8w45z03cvr8/t2Pwx0NVkwRgf2BWJYLPb12D5hAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;A big file in git repo&quot;
        title=&quot;A big file in git repo&quot;
        src=&quot;/static/e5a52f5af9a7ec9889bede619f2db995/b2f6b/bigfile.png&quot;
        srcset=&quot;/static/e5a52f5af9a7ec9889bede619f2db995/b2f6b/bigfile.png 394w&quot;
        sizes=&quot;(max-width: 394px) 100vw, 394px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;h2 id=&quot;Now-lets-create-the-mess-in-git-and-on-the-remote&quot; style=&quot;position:relative;&quot;&gt;Now lets create the mess in git and on the remote&lt;a href=&quot;#Now-lets-create-the-mess-in-git-and-on-the-remote&quot; aria-label=&quot;Now lets create the mess in git and on the remote permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token function&quot;&gt;git&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;add&lt;/span&gt; &lt;span class=&quot;token builtin class-name&quot;&gt;.&lt;/span&gt;
&lt;span class=&quot;token function&quot;&gt;git&lt;/span&gt; commit &lt;span class=&quot;token parameter variable&quot;&gt;-m&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;big commit lots of files one big bad file&quot;&lt;/span&gt;
&lt;span class=&quot;token function&quot;&gt;git&lt;/span&gt; push&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;What is our disk size now?&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token function&quot;&gt;du&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-hsb&lt;/span&gt; &lt;span class=&quot;token builtin class-name&quot;&gt;.&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;# 16,479,850        .&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Ok, committing a 7 MB file has blown out the entire size of the directory to 16.5 MB and that big commit with lots of files is hard to separate. The one big file we want gone and it is now in master branch on the remote. How do we not only get rid of it, but ensure that the &lt;code class=&quot;language-text&quot;&gt;.git&lt;/code&gt; database is clean?&lt;/p&gt;
&lt;p&gt;Oh no, wait, what has happened?&lt;/p&gt;
&lt;h2 id=&quot;Someone-on-another-branch-has-pulled-master-and-made-some-changes&quot; style=&quot;position:relative;&quot;&gt;Someone on another branch has pulled master and made some changes&lt;a href=&quot;#Someone-on-another-branch-has-pulled-master-and-made-some-changes&quot; aria-label=&quot;Someone on another branch has pulled master and made some changes permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token function&quot;&gt;git&lt;/span&gt; checkout working
&lt;span class=&quot;token function&quot;&gt;git&lt;/span&gt; pull origin master&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Some other files were added.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token function&quot;&gt;mkdir&lt;/span&gt; working
&lt;span class=&quot;token function&quot;&gt;touch&lt;/span&gt; ./working/test.js&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token function&quot;&gt;git&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;add&lt;/span&gt; &lt;span class=&quot;token builtin class-name&quot;&gt;.&lt;/span&gt;
&lt;span class=&quot;token function&quot;&gt;git&lt;/span&gt; commit &lt;span class=&quot;token parameter variable&quot;&gt;-m&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;a working commit with the big bad file&quot;&lt;/span&gt;
&lt;span class=&quot;token function&quot;&gt;git&lt;/span&gt; push origin working&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Someone pulled from master and kept working merrily on their branch. They made commits on top of the one we don’t want and pushed to the remote.&lt;/p&gt;
&lt;h2 id=&quot;The-last-thing-to-fix&quot; style=&quot;position:relative;&quot;&gt;The last thing to fix&lt;a href=&quot;#The-last-thing-to-fix&quot; aria-label=&quot;The last thing to fix permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;In an attempt to remove the big file, the user working on master deletes the file and makes another commit push. The big file is gone, but not from history.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token function&quot;&gt;git&lt;/span&gt; checkout master
&lt;span class=&quot;token function&quot;&gt;rm&lt;/span&gt; ./lotsoffiles/chromedriver_mac64.zip&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Now lets commit this.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token function&quot;&gt;git&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;add&lt;/span&gt; &lt;span class=&quot;token builtin class-name&quot;&gt;.&lt;/span&gt;
&lt;span class=&quot;token function&quot;&gt;git&lt;/span&gt; commit &lt;span class=&quot;token parameter variable&quot;&gt;-m&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;reversing big file&quot;&lt;/span&gt;
&lt;span class=&quot;token function&quot;&gt;git&lt;/span&gt; push&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;But what is the size now? It looks like the deleting of the file doesn’t remove the size from the &lt;code class=&quot;language-text&quot;&gt;.git&lt;/code&gt; database.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token function&quot;&gt;du&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-hsb&lt;/span&gt; &lt;span class=&quot;token builtin class-name&quot;&gt;.&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;# 8,321,042 .&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2 id=&quot;Now-the-cleanup&quot; style=&quot;position:relative;&quot;&gt;Now the cleanup&lt;a href=&quot;#Now-the-cleanup&quot; aria-label=&quot;Now the cleanup permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token function&quot;&gt;git&lt;/span&gt; checkout master
&lt;span class=&quot;token function&quot;&gt;git&lt;/span&gt; log&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Find the last commit on master that we want:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;log&quot;&gt;&lt;pre class=&quot;language-log&quot;&gt;&lt;code class=&quot;language-log&quot;&gt;commit &lt;span class=&quot;token number&quot;&gt;777e7a7ecaef0dfef8f2cf919cca2681296304b0&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;(&lt;/span&gt;HEAD &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; master
&lt;span class=&quot;token property&quot;&gt;Author:&lt;/span&gt; Christopher Mortimer &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;christopher&lt;span class=&quot;token operator&quot;&gt;@&lt;/span&gt;mortimer&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;xyz&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
&lt;span class=&quot;token property&quot;&gt;Date:&lt;/span&gt;   &lt;span class=&quot;token date number&quot;&gt;Thu Jul 29&lt;/span&gt; &lt;span class=&quot;token time number&quot;&gt;21:45:00&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;2021&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1000&lt;/span&gt;

    reversing big file

commit &lt;span class=&quot;token number&quot;&gt;431bd1123da15c597e496c413116919e0d9ae1bb&lt;/span&gt;
&lt;span class=&quot;token property&quot;&gt;Author:&lt;/span&gt; Christopher Mortimer &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;christopher&lt;span class=&quot;token operator&quot;&gt;@&lt;/span&gt;mortimer&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;xyz&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
&lt;span class=&quot;token property&quot;&gt;Date:&lt;/span&gt;   &lt;span class=&quot;token date number&quot;&gt;Thu Jul 29&lt;/span&gt; &lt;span class=&quot;token time number&quot;&gt;21:41:02&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;2021&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1000&lt;/span&gt;

    big commit lots of files one big bad file

commit &lt;span class=&quot;token number&quot;&gt;6b62247468340f8ec9ee61a2aee16bd5d43f038b&lt;/span&gt;
&lt;span class=&quot;token property&quot;&gt;Author:&lt;/span&gt; Christopher Mortimer &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;christopher&lt;span class=&quot;token operator&quot;&gt;@&lt;/span&gt;mortimer&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;xyz&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
&lt;span class=&quot;token property&quot;&gt;Date:&lt;/span&gt;   &lt;span class=&quot;token date number&quot;&gt;Thu Jul 29&lt;/span&gt; &lt;span class=&quot;token time number&quot;&gt;21:39:32&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;2021&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1000&lt;/span&gt;

    before things went wrong&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Now lets reset the head back to that commit. Noting that before we go this, retain the files you actually want somewhere else on your machine. You’ll need to recommit that work.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token function&quot;&gt;git&lt;/span&gt; reset &lt;span class=&quot;token parameter variable&quot;&gt;--hard&lt;/span&gt; 6b62247468340f8ec9ee61a2aee16bd5d43f038b
&lt;span class=&quot;token function&quot;&gt;git&lt;/span&gt; push &lt;span class=&quot;token parameter variable&quot;&gt;-f&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Great, now lets do the same for the working branch:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token function&quot;&gt;git&lt;/span&gt; checkout working
&lt;span class=&quot;token function&quot;&gt;git&lt;/span&gt; log&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Find the last commit on master that we want:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;log&quot;&gt;&lt;pre class=&quot;language-log&quot;&gt;&lt;code class=&quot;language-log&quot;&gt;commit a3e751174204b778e1f86228014d1bdb929d57c2 &lt;span class=&quot;token operator&quot;&gt;(&lt;/span&gt;HEAD &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; workin
&lt;span class=&quot;token property&quot;&gt;Author:&lt;/span&gt; Christopher Mortimer &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;christopher&lt;span class=&quot;token operator&quot;&gt;@&lt;/span&gt;mortimer&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;xyz&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
&lt;span class=&quot;token property&quot;&gt;Date:&lt;/span&gt;   &lt;span class=&quot;token date number&quot;&gt;Thu Jul 29&lt;/span&gt; &lt;span class=&quot;token time number&quot;&gt;21:42:19&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;2021&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1000&lt;/span&gt;

    a working commit with the big bad file

commit &lt;span class=&quot;token number&quot;&gt;431bd1123da15c597e496c413116919e0d9ae1bb&lt;/span&gt;
&lt;span class=&quot;token property&quot;&gt;Author:&lt;/span&gt; Christopher Mortimer &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;christopher&lt;span class=&quot;token operator&quot;&gt;@&lt;/span&gt;mortimer&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;xyz&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
&lt;span class=&quot;token property&quot;&gt;Date:&lt;/span&gt;   &lt;span class=&quot;token date number&quot;&gt;Thu Jul 29&lt;/span&gt; &lt;span class=&quot;token time number&quot;&gt;21:41:02&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;2021&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1000&lt;/span&gt;

    big commit lots of files one big bad file

commit &lt;span class=&quot;token number&quot;&gt;6b62247468340f8ec9ee61a2aee16bd5d43f038b&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;(&lt;/span&gt;origin&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;master&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;token property&quot;&gt;Author:&lt;/span&gt; Christopher Mortimer &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;christopher&lt;span class=&quot;token operator&quot;&gt;@&lt;/span&gt;mortimer&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;xyz&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
&lt;span class=&quot;token property&quot;&gt;Date:&lt;/span&gt;   &lt;span class=&quot;token date number&quot;&gt;Thu Jul 29&lt;/span&gt; &lt;span class=&quot;token time number&quot;&gt;21:39:32&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;2021&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1000&lt;/span&gt;

    before things went wrong&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Noting that before we go this, retain the files and changes from the working commit you actually want somewhere else on your machine. You’ll need to recommit that work.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token function&quot;&gt;git&lt;/span&gt; reset &lt;span class=&quot;token parameter variable&quot;&gt;--hard&lt;/span&gt; 6b62247468340f8ec9ee61a2aee16bd5d43f038b
&lt;span class=&quot;token function&quot;&gt;git&lt;/span&gt; push origin working &lt;span class=&quot;token parameter variable&quot;&gt;-f&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token function&quot;&gt;du&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-hsb&lt;/span&gt; &lt;span class=&quot;token builtin class-name&quot;&gt;.&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;# 8,218,212 .&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2 id=&quot;Is-the-git-database-clean&quot; style=&quot;position:relative;&quot;&gt;Is the git database clean?&lt;a href=&quot;#Is-the-git-database-clean&quot; aria-label=&quot;Is the git database clean permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Unfortunately we aren’t free yet. The &lt;code class=&quot;language-text&quot;&gt;.git/objects/pack&lt;/code&gt; pack file is still huge (tracking the size of the big file).&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token function&quot;&gt;git&lt;/span&gt; reflog expire &lt;span class=&quot;token parameter variable&quot;&gt;--expire&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;now &lt;span class=&quot;token parameter variable&quot;&gt;--all&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;git&lt;/span&gt; gc &lt;span class=&quot;token parameter variable&quot;&gt;--prune&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;now &lt;span class=&quot;token parameter variable&quot;&gt;--aggressive&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Now we are back to a clean repo.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token function&quot;&gt;du&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-hsb&lt;/span&gt; &lt;span class=&quot;token builtin class-name&quot;&gt;.&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;# 40,916   .&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;</content:encoded></item><item><title><![CDATA[RStudio with SSL proxy]]></title><description><![CDATA[What are we doing We have a server hosting RStudio, and we want encrypt the traffic too and from the RStudio service. We also do not want to…]]></description><link>https://mortimer.xyz/rstudio-https-proxy/</link><guid isPermaLink="false">https://mortimer.xyz/rstudio-https-proxy/</guid><pubDate>Wed, 26 May 2021 12:00:00 GMT</pubDate><content:encoded>&lt;h1 id=&quot;What-are-we-doing&quot; style=&quot;position:relative;&quot;&gt;What are we doing&lt;a href=&quot;#What-are-we-doing&quot; aria-label=&quot;What are we doing permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h1&gt;
&lt;p&gt;We have a server hosting RStudio, and we want encrypt the traffic too and from the RStudio service. We also do not want to have to open up port 8787 on the server’s network firewall.&lt;/p&gt;
&lt;h2 id=&quot;Why-would-we-want-this&quot; style=&quot;position:relative;&quot;&gt;Why would we want this&lt;a href=&quot;#Why-would-we-want-this&quot; aria-label=&quot;Why would we want this permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;RStudio server doesn’t come with encryption by default, you need an SSL certificate. This means all input in transit is not encrypted… this includes credentials.&lt;/p&gt;
&lt;h2 id=&quot;How-did-we-do-this&quot; style=&quot;position:relative;&quot;&gt;How did we do this&lt;a href=&quot;#How-did-we-do-this&quot; aria-label=&quot;How did we do this permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;This server already had an SSL certification configured on the Nginx HTTP server running a static site: (see &lt;a href=&quot;https://certbot.eff.org/&quot;&gt;Lets Encrypt&lt;/a&gt;).&lt;/p&gt;
&lt;p&gt;We want to use this certificate for the RStudio server running on the same machine. We also want it to be available by a &lt;code class=&quot;language-text&quot;&gt;&amp;lt;server&gt;/rstudio&lt;/code&gt; route.&lt;/p&gt;
&lt;p&gt;This documentation provided the solution on how to setup a reverse proxy at the route &lt;code class=&quot;language-text&quot;&gt;&amp;lt;server&gt;/rstudio&lt;/code&gt; (see &lt;a href=&quot;https://support.rstudio.com/hc/en-us/articles/200552326-Running-RStudio-Server-with-a-Proxy&quot;&gt;RStudio with a proxy&lt;/a&gt;).&lt;/p&gt;
&lt;h2 id=&quot;The-complete-configuration&quot; style=&quot;position:relative;&quot;&gt;The complete configuration&lt;a href=&quot;#The-complete-configuration&quot; aria-label=&quot;The complete configuration permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The configuration file to edit is &lt;code class=&quot;language-text&quot;&gt;/etc/nginx/sites-available/default.conf&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;The contents of this configuration file which includes the default Nginx configurations as well as both the Let’s Encrypt and the Rstudio reverse proxy.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;server {
  listen 80 default_server;
  listen [::]:80 default_server;

  root /var/www/html;

  index index.html index.htm index.nginx-debian.html;
  server_name _;

  location / {
    # First attempt to serve request as file, then
    # as directory, then fall back to displaying a 404.
    try_files $uri $uri/ =404;
  }
}

server {
  root /var/www/html;

  # Add index.php to the list if you are using PHP
  index index.html index.htm index.nginx-debian.html;
  server_name &amp;lt;domain.name&gt;; # managed by Certbot

  location / {
    # First attempt to serve request as file, then
    # as directory, then fall back to displaying a 404.
    try_files $uri $uri/ =404;
  }

  rewrite ^/rstudio$ $scheme://$http_host/rstudio/ permanent;

  location /rstudio/ {
    rewrite /rstudio/(.*) /$1 break;
    proxy_pass http://localhost:8787;
    proxy_redirect http://localhost:8787 $scheme://$host/rstudio/;
    proxy_http_version 1.1;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection $connection_upgrade;
    proxy_read_timeout 20d;
  }

  listen [::]:443 ssl ipv6only=on; # managed by Certbot
  listen 443 ssl; # managed by Certbot
  ssl_certificate /etc/letsencrypt/live/&amp;lt;domain.name&gt;/fullchain.pem; # managed by Certbot
  ssl_certificate_key /etc/letsencrypt/live/&amp;lt;domain.name&gt;/privkey.pem; # managed by Certbot
  include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot
  ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; # managed by Certbot
}

server {
  if ($host = &amp;lt;domain.name&gt;) {
    return 301 https://$host$request_uri;
  } # managed by Certbot

  listen 80 ;
  listen [::]:80 ;
  server_name &amp;lt;domain.name&gt;;
  return 404; # managed by Certbot
}&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;</content:encoded></item><item><title><![CDATA[Tableau Web Data Connector Node Generator]]></title><description><![CDATA[Using Tableau Web Data Connector (WDC) as the data source for dashboard. This post is in reference to the associated Github repo at https…]]></description><link>https://mortimer.xyz/tableau-wdc-gen/</link><guid isPermaLink="false">https://mortimer.xyz/tableau-wdc-gen/</guid><pubDate>Sat, 17 Apr 2021 12:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Using Tableau Web Data Connector (WDC) as the data source for dashboard.&lt;br&gt;
This post is in reference to the associated Github repo at &lt;a href=&quot;https://github.com/mortie23/tableau-wdc-genc&quot;&gt;https://github.com/mortie23/tableau-wdc-gen&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Reason&lt;/strong&gt;:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;The data we are sourcing is in SAS.&lt;/li&gt;
&lt;li&gt;If using Tableau Public the only server sources are Google Sheets and Tableau WDC.&lt;/li&gt;
&lt;li&gt;Tableau WDC is the easiest way to have a Tableau Public dashboard sourcing from a server that can be updated independently of the dashboard.&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id=&quot;Background&quot; style=&quot;position:relative;&quot;&gt;Background&lt;a href=&quot;#Background&quot; aria-label=&quot;Background permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;If you are interested in the details of how this package works it is built from knowledge gained through this tutorial.&lt;br&gt;
&lt;a href=&quot;https://tableau.github.io/webdataconnector/docs/wdc_tutorial.html&quot;&gt;https://tableau.github.io/webdataconnector/docs/wdc_tutorial.html&lt;/a&gt;&lt;/p&gt;
&lt;h2 id=&quot;Generate-the-data&quot; style=&quot;position:relative;&quot;&gt;Generate the data&lt;a href=&quot;#Generate-the-data&quot; aria-label=&quot;Generate the data permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;To generate the JSON that this package uses we will use the SASjs macro core library. Directly using &lt;code class=&quot;language-text&quot;&gt;proc json&lt;/code&gt; adds a high level array element to the JSON output that we don’t want: &lt;code class=&quot;language-text&quot;&gt;[&apos;SASTableData+&amp;lt;filename&gt;&apos;]&lt;/code&gt;.&lt;br&gt;
This means that we create a clean JSON output and we would be able to use different tools in the future and not have legacy SAS generated objects.&lt;/p&gt;
&lt;p&gt;Documentation on the SASjs &lt;code class=&quot;language-text&quot;&gt;mp_jsonout&lt;/code&gt; macro can be found at: &lt;a href=&quot;https://core.sasjs.io/mp__jsonout_8sas.html&quot;&gt;https://core.sasjs.io/mp__jsonout_8sas.html&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;The key steps are to create two file handles (one for the data, and one for the metadata) and use the SASjs macro to write the files out.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;sas&quot;&gt;&lt;pre class=&quot;language-sas&quot;&gt;&lt;code class=&quot;language-sas&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;* Create filename handle for files to write to;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;filename&lt;/span&gt; json &lt;span class=&quot;token string&quot;&gt;&quot;&amp;amp;outdir./datasourcename.json&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;filename&lt;/span&gt; jsonmeta &lt;span class=&quot;token string&quot;&gt;&quot;&amp;amp;outdir./datasourcename_meta.json&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;* Export to JSON;&lt;/span&gt;
&lt;span class=&quot;token function keyword&quot;&gt;%mp_jsonout&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;OBJ&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;datasourcename&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;jref&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;json&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token function keyword&quot;&gt;%mp_jsonout&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;OBJ&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;datasourcename_meta&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;jref&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;jsonmeta&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2 id=&quot;Create-the-WDC&quot; style=&quot;position:relative;&quot;&gt;Create the WDC&lt;a href=&quot;#Create-the-WDC&quot; aria-label=&quot;Create the WDC permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;A WDC needs the following 4 files:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;span&gt;&lt;/span&gt;&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;filename.html&lt;/td&gt;
&lt;td&gt;WDC page&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;filename.js&lt;/td&gt;
&lt;td&gt;WDC script&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;filename.json&lt;/td&gt;
&lt;td&gt;The dataset as JSON array&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;We will used a &lt;code class=&quot;language-text&quot;&gt;node&lt;/code&gt; script to generate these files from the metadata.&lt;/p&gt;
&lt;h3 id=&quot;Run-the-node-script&quot; style=&quot;position:relative;&quot;&gt;Run the node script&lt;a href=&quot;#Run-the-node-script&quot; aria-label=&quot;Run the node script permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;This has been tested on &lt;code class=&quot;language-text&quot;&gt;Node.js 12.19.0 on for Windows 10&lt;/code&gt; run using Powershell.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token function&quot;&gt;node&lt;/span&gt; .&lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;script&lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;tableauwdc.js &lt;span class=&quot;token parameter variable&quot;&gt;--filename&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;filename &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;no extension&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;--title&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;&amp;lt;a title for the dataset with quotes&gt;&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;--geojson&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;## example of flat tabular data from SAS&lt;/span&gt;
&lt;span class=&quot;token function&quot;&gt;node&lt;/span&gt; .&lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;script&lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;tableauwdc.js &lt;span class=&quot;token parameter variable&quot;&gt;--filename&lt;/span&gt; classdata &lt;span class=&quot;token parameter variable&quot;&gt;--title&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Class Data&quot;&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;## example GeoJSON data&lt;/span&gt;
&lt;span class=&quot;token function&quot;&gt;node&lt;/span&gt; .&lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;script&lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;tableauwdc.js &lt;span class=&quot;token parameter variable&quot;&gt;--filename&lt;/span&gt; geo_earthquake_4.5_week &lt;span class=&quot;token parameter variable&quot;&gt;--title&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Geo Earthquake 4.5 week&quot;&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;--geojson&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;You should see a quick summary of some validations and a prompt to continue.&lt;br&gt;
&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 779px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/d7a76d9242039cf47bd3c09e458ed895/a3e85/tableauwdc-run-validation.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 33.4%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAHCAYAAAAIy204AAAACXBIWXMAABJ0AAASdAHeZh94AAABV0lEQVQoz22QXW/aMBhG+RstJHYSO/FHPgjEBAiBbaJd28v9iUmTdru/f6YEhFSpF0d+bcnHz+NFOPc0xx9szy90337SXV4Jl1c24xUXBnw44XcnfF3SDe+8fRy4Xi2hr0lNSVI4UlshVU4kJIvjMUFphfMe6xyuLPFlhbEWmSQkaUqaKXRhKOua3DhimSGSDKnNLJ0Qmb4Jh1//CG9/2Vx/s335g1A1T0/PLFcxq1iyisTMMop5Xka380jMlyMhiOKJeN7HMmFRn0eay4jf99guYEND3ljMxqN8gSoLVGXus0HXdmYWxvIhmpgTms7i9h5d5xStwfUV+dqhG4vIUpJCkxp9W+/zhMymyhlSZQ/ZnDBvHNWpxYUKv2+ohjWm9TTjBr9rcH1N5osHc+L7PD2qa/NZ2G62dIeear1m2wcO54F21xEOPeP3C1Kp25/F8gvEp9pT5f+bV8+kZfhNigAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Valid&quot;
        title=&quot;Valid&quot;
        src=&quot;/static/d7a76d9242039cf47bd3c09e458ed895/a3e85/tableauwdc-run-validation.png&quot;
        srcset=&quot;/static/d7a76d9242039cf47bd3c09e458ed895/0eb09/tableauwdc-run-validation.png 500w,
/static/d7a76d9242039cf47bd3c09e458ed895/a3e85/tableauwdc-run-validation.png 779w&quot;
        sizes=&quot;(max-width: 779px) 100vw, 779px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;Do not continue if your validation fails.&lt;br&gt;
&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 927px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/7ea0f97051f981b30face9e8c5aa61ab/0342e/tableauwdc-run-validation-fail.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 27.200000000000003%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAFCAYAAABFA8wzAAAACXBIWXMAABJ0AAASdAHeZh94AAAA60lEQVQY013O206EMBSFYd5DhtIapkcoDDCnGickGr3xbm59/8f4DSBqvPiydppm7Z2NF0M/GkI3cDgeGS6J/pywTUflGrSvadqBc0rEVuODxsSIbnvk3rITgqKUP7LTywfn90/q650m3RFKk+f58nFTiJK8EDzs5izZidXfotn8lunhSpxeqW8T/nLDjC1VdChnkFaj3K/HYMgLuRT/ty3IYltw6AU2lLimJHQVLla4IDFWYpzC2JW2CusVziusW+dNqb4vjJ3n1AbSEEi9Zxobng6et+eeoa7QVi5Fs71Ry4K5YEm35mwr/ALT1pM81ZBYhAAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Fail&quot;
        title=&quot;Fail&quot;
        src=&quot;/static/7ea0f97051f981b30face9e8c5aa61ab/0342e/tableauwdc-run-validation-fail.png&quot;
        srcset=&quot;/static/7ea0f97051f981b30face9e8c5aa61ab/0eb09/tableauwdc-run-validation-fail.png 500w,
/static/7ea0f97051f981b30face9e8c5aa61ab/0342e/tableauwdc-run-validation-fail.png 927w&quot;
        sizes=&quot;(max-width: 927px) 100vw, 927px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;When you pass the validations, then continue, your WDC should be built.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Agent Based Modelling]]></title><description><![CDATA[I was experimenting with Agent based models and came across a really great talk Jackie Kazil | Agent based modelling in Python. This lead me…]]></description><link>https://mortimer.xyz/mesa-workforce/</link><guid isPermaLink="false">https://mortimer.xyz/mesa-workforce/</guid><pubDate>Fri, 16 Apr 2021 12:00:00 GMT</pubDate><content:encoded>&lt;p&gt;I was experimenting with Agent based models and came across a really great talk &lt;a href=&quot;https://www.youtube.com/watch?v=bjjoHji8KUQ&amp;#x26;ab_channel=PyData&quot;&gt;Jackie Kazil | Agent based modelling in Python&lt;/a&gt;. This lead me to look at the Python module that was being discussed &lt;a href=&quot;https://mesa.readthedocs.io/en/master/&quot;&gt;Mesa | Agent-based modelling in Python 3+&lt;/a&gt;.&lt;/p&gt;
&lt;h1 id=&quot;General-Practitioner-GP-Workforce-Model&quot; style=&quot;position:relative;&quot;&gt;General Practitioner (GP) Workforce Model&lt;a href=&quot;#General-Practitioner-GP-Workforce-Model&quot; aria-label=&quot;General Practitioner GP Workforce Model permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h1&gt;
&lt;p&gt;The Mesa project has many examples which I thought were a great place to get started. I decided to try to build a model that was similar to the Wolf Sheep model in this example: &lt;a href=&quot;https://github.com/projectmesa/mesa/tree/main/examples/wolf_sheep&quot;&gt;https://github.com/projectmesa/mesa/tree/main/examples/wolf_sheep&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;So I cloned the repo and the rest of this blog post is based on my associated &lt;a href=&quot;https://github.com/mortie23/mesa-workforce&quot;&gt;github repo&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;I decided to use the concepts and framework for a simple workforce model, consisting of three agent types:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;GP Fellows (fully qualified and practicing)&lt;/li&gt;
&lt;li&gt;GP trainees (working towards qualification)&lt;/li&gt;
&lt;li&gt;Patients&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;I made a few assumptions which were:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;The GP Fellows train trainees over 5 years&lt;/li&gt;
&lt;li&gt;The Trainees then become Fellows after 5 years.&lt;/li&gt;
&lt;li&gt;The GP Fellow may or may not take on another trainee.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;The model tests and demonstrates several Mesa concepts and features:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;MultiGrid&lt;/li&gt;
&lt;li&gt;Multiple agent types (GP Fellow, Trainee and Patient)&lt;/li&gt;
&lt;li&gt;Overlay arbitrary text (Age and Sex of the Provider) on agent’s shapes while drawing on CanvasGrid&lt;/li&gt;
&lt;li&gt;Agents inheriting a behaviour (random movement) from an abstract parent&lt;/li&gt;
&lt;li&gt;Writing a model composed of multiple files.&lt;/li&gt;
&lt;li&gt;Dynamically adding and removing agents from the schedule&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;Installation&quot; style=&quot;position:relative;&quot;&gt;Installation&lt;a href=&quot;#Installation&quot; aria-label=&quot;Installation permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;To install the dependencies use pip and the requirements.txt in this directory. e.g.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;pip &lt;span class=&quot;token function&quot;&gt;install&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-r&lt;/span&gt; requirements.txt&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2 id=&quot;How-to-Run&quot; style=&quot;position:relative;&quot;&gt;How to Run&lt;a href=&quot;#How-to-Run&quot; aria-label=&quot;How to Run permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;To run the model interactively, run &lt;code class=&quot;language-text&quot;&gt;mesa runserver&lt;/code&gt; in this directory. e.g.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;mesa runserver&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Then open your browser to &lt;a href=&quot;http://127.0.0.1:8521/&quot;&gt;http://127.0.0.1:8521/&lt;/a&gt; and press Reset, then Run.&lt;/p&gt;
&lt;h2 id=&quot;Files&quot; style=&quot;position:relative;&quot;&gt;Files&lt;a href=&quot;#Files&quot; aria-label=&quot;Files permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;This lists the key files that were modified from the base Wolf and Sheep model that this is inherited from.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;level&lt;/th&gt;
&lt;th&gt;file&lt;/th&gt;
&lt;th&gt;description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;span class=&quot;gatsby-resp-image-wrapper&quot; style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 50px; &quot;&gt;
      &lt;a class=&quot;gatsby-resp-image-link&quot; href=&quot;/static/872a116c2274b08175bbee49df42d4b1/77dd4/folder.png&quot; style=&quot;display: block&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;
    &lt;span class=&quot;gatsby-resp-image-background-image&quot; style=&quot;padding-bottom: 42%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAICAYAAAD5nd/tAAAACXBIWXMAAArwAAAK8AFCrDSYAAABWElEQVQoz5XRv0tCYRTG8QNBUxAtNQf9CREStDa3NDZVpqlRGdVoGBUVLYEUUVNbS0tbDRFlpJD3fU3UbhoNNWjn7d5SrtcfTygObdqBL4ezfJZDP5lTKr6edOUvh3qLMWfPWx6kCiAAnQD6AHQDjbtRywHQnxN76Q8RMnL6uW5YGGULDjMbujafNytmZv8ifUAkg0TlQrY1WCm8jajkLj71Q/DLMVg/Aj+HwMIL1qbr+0GsE0XmiWxDawMsscNIr4EfJ6pKm6oqbbLW2NJjK+mFii/eyi2iqL8OynbAvMNIBcGaq6bkXE1JXzNvhYUHKr5wJzf/A1q5YSMVAMcmbRbuMgtXM3eJNReU9N3IDaLIYh0UrcFq+XvQ1Hegnvz4SqziK7Hyp2UYyYCIbxNFl4js71RbX+4oGdq4xeFZi++dFt/PNHNaHPbYZmLs/WqAsmdEtUqxJfgL4PIOs7y7UGQAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;&gt;&lt;/span&gt;
  &lt;img class=&quot;gatsby-resp-image-image&quot; alt=&quot;folder&quot; title=&quot;folder&quot; src=&quot;/static/872a116c2274b08175bbee49df42d4b1/77dd4/folder.png&quot; srcset=&quot;/static/872a116c2274b08175bbee49df42d4b1/77dd4/folder.png 50w&quot; sizes=&quot;(max-width: 50px) 100vw, 50px&quot; style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot;&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/td&gt;
&lt;td&gt;workforce&lt;/td&gt;
&lt;td&gt;Workforce components&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;span class=&quot;gatsby-resp-image-wrapper&quot; style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 50px; &quot;&gt;
      &lt;a class=&quot;gatsby-resp-image-link&quot; href=&quot;/static/8294fa73a73eaea41736efb55b1a6fd7/77dd4/python-lvl1.png&quot; style=&quot;display: block&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;
    &lt;span class=&quot;gatsby-resp-image-background-image&quot; style=&quot;padding-bottom: 42%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAICAYAAAD5nd/tAAAACXBIWXMAAArwAAAK8AFCrDSYAAABcUlEQVQoz43RzyuDcRwH8Pf3ebbxkDjYRct1iYO7sourovwoV8lJyXJz4SD5tUnz7EczE1FIM6O1HyhsSMJ+hJa2xPZsMYS10kf+g+f9B7x6934DcqLTAdYEsJIFL8a5puE5CDOnDbwYHcRCTK+cPtWigzC63sfJ8oSJAzAxDm7+mmHMj6p9QqU1Pi0sJTNYTBGzPLjtwV6QC/JSOu4Fb7xgvOEciqnjWmaKGpVi5EhljtzA8UTlttvLTxcmE5ulWllgid4MLEs8nOn/prNwpAj2LMEm5WH9eq83b51RAJTzKEwyQQewWeCx8QPeEl+FPUMDzqHwjVsTPd6pu8t6BYkOQW97bE0eyDeCWe45WFNQma56IKaSaY9wRUegXx9yRT+kbx/7eNlm3bLAMm0n/sch6gKcBLRSRd6rDBWDoLxXiEm75ZagAWq6l3mKqm0ENYIAsR8IhVu4ja1m5Nzo/fbhsRAoOcl4qttffRrQs5r9AUXFoxNUOk2mAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;&gt;&lt;/span&gt;
  &lt;img class=&quot;gatsby-resp-image-image&quot; alt=&quot;python lvl1&quot; title=&quot;python lvl1&quot; src=&quot;/static/8294fa73a73eaea41736efb55b1a6fd7/77dd4/python-lvl1.png&quot; srcset=&quot;/static/8294fa73a73eaea41736efb55b1a6fd7/77dd4/python-lvl1.png 50w&quot; sizes=&quot;(max-width: 50px) 100vw, 50px&quot; style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot;&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/td&gt;
&lt;td&gt;random_walker.py&lt;/td&gt;
&lt;td&gt;This defines the &lt;em&gt;RandomWalker&lt;/em&gt; agent.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;span class=&quot;gatsby-resp-image-wrapper&quot; style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 50px; &quot;&gt;
      &lt;a class=&quot;gatsby-resp-image-link&quot; href=&quot;/static/8294fa73a73eaea41736efb55b1a6fd7/77dd4/python-lvl1.png&quot; style=&quot;display: block&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;
    &lt;span class=&quot;gatsby-resp-image-background-image&quot; style=&quot;padding-bottom: 42%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAICAYAAAD5nd/tAAAACXBIWXMAAArwAAAK8AFCrDSYAAABcUlEQVQoz43RzyuDcRwH8Pf3ebbxkDjYRct1iYO7sourovwoV8lJyXJz4SD5tUnz7EczE1FIM6O1HyhsSMJ+hJa2xPZsMYS10kf+g+f9B7x6934DcqLTAdYEsJIFL8a5puE5CDOnDbwYHcRCTK+cPtWigzC63sfJ8oSJAzAxDm7+mmHMj6p9QqU1Pi0sJTNYTBGzPLjtwV6QC/JSOu4Fb7xgvOEciqnjWmaKGpVi5EhljtzA8UTlttvLTxcmE5ulWllgid4MLEs8nOn/prNwpAj2LMEm5WH9eq83b51RAJTzKEwyQQewWeCx8QPeEl+FPUMDzqHwjVsTPd6pu8t6BYkOQW97bE0eyDeCWe45WFNQma56IKaSaY9wRUegXx9yRT+kbx/7eNlm3bLAMm0n/sch6gKcBLRSRd6rDBWDoLxXiEm75ZagAWq6l3mKqm0ENYIAsR8IhVu4ja1m5Nzo/fbhsRAoOcl4qttffRrQs5r9AUXFoxNUOk2mAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;&gt;&lt;/span&gt;
  &lt;img class=&quot;gatsby-resp-image-image&quot; alt=&quot;python lvl1&quot; title=&quot;python lvl1&quot; src=&quot;/static/8294fa73a73eaea41736efb55b1a6fd7/77dd4/python-lvl1.png&quot; srcset=&quot;/static/8294fa73a73eaea41736efb55b1a6fd7/77dd4/python-lvl1.png 50w&quot; sizes=&quot;(max-width: 50px) 100vw, 50px&quot; style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot;&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/td&gt;
&lt;td&gt;agents.py&lt;/td&gt;
&lt;td&gt;Defines the GP Fellow, Trainee, and Patient agent classes.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;span class=&quot;gatsby-resp-image-wrapper&quot; style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 50px; &quot;&gt;
      &lt;a class=&quot;gatsby-resp-image-link&quot; href=&quot;/static/8294fa73a73eaea41736efb55b1a6fd7/77dd4/python-lvl1.png&quot; style=&quot;display: block&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;
    &lt;span class=&quot;gatsby-resp-image-background-image&quot; style=&quot;padding-bottom: 42%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAICAYAAAD5nd/tAAAACXBIWXMAAArwAAAK8AFCrDSYAAABcUlEQVQoz43RzyuDcRwH8Pf3ebbxkDjYRct1iYO7sourovwoV8lJyXJz4SD5tUnz7EczE1FIM6O1HyhsSMJ+hJa2xPZsMYS10kf+g+f9B7x6934DcqLTAdYEsJIFL8a5puE5CDOnDbwYHcRCTK+cPtWigzC63sfJ8oSJAzAxDm7+mmHMj6p9QqU1Pi0sJTNYTBGzPLjtwV6QC/JSOu4Fb7xgvOEciqnjWmaKGpVi5EhljtzA8UTlttvLTxcmE5ulWllgid4MLEs8nOn/prNwpAj2LMEm5WH9eq83b51RAJTzKEwyQQewWeCx8QPeEl+FPUMDzqHwjVsTPd6pu8t6BYkOQW97bE0eyDeCWe45WFNQma56IKaSaY9wRUegXx9yRT+kbx/7eNlm3bLAMm0n/sch6gKcBLRSRd6rDBWDoLxXiEm75ZagAWq6l3mKqm0ENYIAsR8IhVu4ja1m5Nzo/fbhsRAoOcl4qttffRrQs5r9AUXFoxNUOk2mAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;&gt;&lt;/span&gt;
  &lt;img class=&quot;gatsby-resp-image-image&quot; alt=&quot;python lvl1&quot; title=&quot;python lvl1&quot; src=&quot;/static/8294fa73a73eaea41736efb55b1a6fd7/77dd4/python-lvl1.png&quot; srcset=&quot;/static/8294fa73a73eaea41736efb55b1a6fd7/77dd4/python-lvl1.png 50w&quot; sizes=&quot;(max-width: 50px) 100vw, 50px&quot; style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot;&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/td&gt;
&lt;td&gt;model.py&lt;/td&gt;
&lt;td&gt;Defines the Workforce model itself&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;span class=&quot;gatsby-resp-image-wrapper&quot; style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 50px; &quot;&gt;
      &lt;a class=&quot;gatsby-resp-image-link&quot; href=&quot;/static/0acb660aade2351b6dcd9ff433765eeb/77dd4/python.png&quot; style=&quot;display: block&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;
    &lt;span class=&quot;gatsby-resp-image-background-image&quot; style=&quot;padding-bottom: 42%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAICAYAAAD5nd/tAAAACXBIWXMAAArwAAAK8AFCrDSYAAABcUlEQVQoz43Qu0tCcRQH8O/vmjct0tqKhugxtLU0BC0NLkG01OYf0BRUQ0NDLUaTkL20urd3UBoFGiWmRA/sqfjoDTejpdSMXpJReeL2F9wDZzlwPpzzhcbkBoig7d/S8yNhI2xXPcwSaqIusGxAx6E+DqAbZ7M5UFS8/ZVTCxLyLCcG/UwsjKl7YsItoY/KRh0taLaLHBFwOqEQlK9jw5E2lfXcyVujAV68SUFMZWyLjeO0DiNFADoC8w+plXnMfFjDhBhh+pEw+fQBIfmOkefLqKs0nvXhzdxbUbA3qoa0xDFFYO7gQQPEB6oUPDf+1aqA31l9fblefEfboE8v0sEpVenFggoxh0KwzmQqhDUZ7JzvOKBd/Px6kf7ZQjzjRfplgznk/OSOzinNsID+F0JrJe3f26APT04itanzSUsqgzw/tkF+F0mXQs+82MrNirV4WUH5mxs7Xz4mvXqKBgKCJn9/TAtpmWexZQ4JpzLwD8VKqvGVt8BlAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;&gt;&lt;/span&gt;
  &lt;img class=&quot;gatsby-resp-image-image&quot; alt=&quot;python&quot; title=&quot;python&quot; src=&quot;/static/0acb660aade2351b6dcd9ff433765eeb/77dd4/python.png&quot; srcset=&quot;/static/0acb660aade2351b6dcd9ff433765eeb/77dd4/python.png 50w&quot; sizes=&quot;(max-width: 50px) 100vw, 50px&quot; style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot;&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/td&gt;
&lt;td&gt;run.py&lt;/td&gt;
&lt;td&gt;Launches a model visualization server.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;</content:encoded></item><item><title><![CDATA[Geospatial shapes for vizualisation]]></title><description><![CDATA[Putting analysis on a map People always seem to like seeing data visualised on a map. Something about looking at a gradient of colour on a…]]></description><link>https://mortimer.xyz/geospatial-for-visualisation/</link><guid isPermaLink="false">https://mortimer.xyz/geospatial-for-visualisation/</guid><pubDate>Fri, 12 Mar 2021 12:00:00 GMT</pubDate><content:encoded>&lt;h2 id=&quot;Putting-analysis-on-a-map&quot; style=&quot;position:relative;&quot;&gt;Putting analysis on a map&lt;a href=&quot;#Putting-analysis-on-a-map&quot; aria-label=&quot;Putting analysis on a map permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;People always seem to like seeing data visualised on a map. Something about looking at a gradient of colour on a map makes people feel like they are empowered to understand.&lt;/p&gt;
&lt;p&gt;But high-resolution shapefiles can be massive. While they are great for accurately allocating coordinates (latitude, longitude) to geospatial areas (polygons), they are terrible for rendering lower-resolution dynamic maps in dashboards.&lt;/p&gt;
&lt;p&gt;I was looking at the &lt;a href=&quot;https://www.abs.gov.au/AUSSTATS/abs@.nsf/DetailsPage/1270.0.55.005July%202016?OpenDocument&quot;&gt;Australian Remoteness Areas&lt;/a&gt; published by &lt;a href=&quot;https://www.abs.gov.au/&quot;&gt;Australia’s Bureau of statistics&lt;/a&gt;. The raw shapefile and subsequent raw GeoJSON are way too large (~110 MB) to download over HTTP for a simple data visualisation on a webpage.&lt;/p&gt;
&lt;p&gt;To solve this, I built a geo-processing pipeline to convert, simplify, and dissolve the boundaries at &lt;a href=&quot;https://github.com/mortie23/geoshape-reduce-size&quot;&gt;Github repository&lt;/a&gt;. Here is how it works.&lt;/p&gt;
&lt;h2 id=&quot;Setup&quot; style=&quot;position:relative;&quot;&gt;Setup&lt;a href=&quot;#Setup&quot; aria-label=&quot;Setup permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;First, let’s get our environment ready. We are using &lt;a href=&quot;https://docs.astral.sh/uv/&quot;&gt;uv&lt;/a&gt; for fast package management. Running the following command will sync all dependencies:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;sh&quot;&gt;&lt;pre class=&quot;language-sh&quot;&gt;&lt;code class=&quot;language-sh&quot;&gt;uv &lt;span class=&quot;token function&quot;&gt;sync&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2 id=&quot;Configuration&quot; style=&quot;position:relative;&quot;&gt;Configuration&lt;a href=&quot;#Configuration&quot; aria-label=&quot;Configuration permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Paths and parameters are managed in &lt;a href=&quot;config.yaml&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;config.yaml&lt;/code&gt;&lt;/a&gt;. Update these to point to your local data before running the scripts:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;yaml&quot;&gt;&lt;pre class=&quot;language-yaml&quot;&gt;&lt;code class=&quot;language-yaml&quot;&gt;&lt;span class=&quot;token key atrule&quot;&gt;paths&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;raw_shapefile&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;data/to/RA_2016_AUST.shp&quot;&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;geojson_output&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;data/to/RA_2016_AUST.geojson&quot;&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;simplified_geojson&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;data/RA_2016_AUST-simple.geojson&quot;&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;dissolved_geojson&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;data/RA_2016_AUST_all.geojson&quot;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;You can download the source shapefile directly from the &lt;a href=&quot;https://www.abs.gov.au/AUSSTATS/abs@.nsf/DetailsPage/1270.0.55.005July%202016?OpenDocument&quot;&gt;ABS&lt;/a&gt;. Or any shape file of your choice.&lt;/p&gt;
&lt;h2 id=&quot;The-Pipeline&quot; style=&quot;position:relative;&quot;&gt;The Pipeline&lt;a href=&quot;#The-Pipeline&quot; aria-label=&quot;The Pipeline permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The process consists of three main steps.&lt;/p&gt;
&lt;h3 id=&quot;1-Convert-Shapefile-to-GeoJSON&quot; style=&quot;position:relative;&quot;&gt;1. Convert Shapefile to GeoJSON&lt;a href=&quot;#1-Convert-Shapefile-to-GeoJSON&quot; aria-label=&quot;1 Convert Shapefile to GeoJSON permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;First, convert the ABS shapefile format to GeoJSON:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;sh&quot;&gt;&lt;pre class=&quot;language-sh&quot;&gt;&lt;code class=&quot;language-sh&quot;&gt;uv run convert&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;2-Simplify-the-Boundaries&quot; style=&quot;position:relative;&quot;&gt;2. Simplify the Boundaries&lt;a href=&quot;#2-Simplify-the-Boundaries&quot; aria-label=&quot;2 Simplify the Boundaries permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;To reduce the file size, we simplify the complex shapes of map borders. Dashboard users viewing a national map at low resolution don’t need to load every single nook and cranny of the coastline.&lt;/p&gt;
&lt;p&gt;Simplification uses the Douglas-Peucker algorithm. You can adjust the &lt;code class=&quot;language-text&quot;&gt;simplify_tolerance&lt;/code&gt; parameter in &lt;code class=&quot;language-text&quot;&gt;config.yaml&lt;/code&gt; to balance the file size vs. boundary details (default is &lt;code class=&quot;language-text&quot;&gt;0.001&lt;/code&gt; degrees, which is roughly 100 meters).&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;sh&quot;&gt;&lt;pre class=&quot;language-sh&quot;&gt;&lt;code class=&quot;language-sh&quot;&gt;uv run simplify&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The difference is clear. Take a look at the detail around Sydney:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Original (High Detail):&lt;/strong&gt;
&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 1102px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/d6b218ec83658446f75a53e70ad38660/39d99/geo-reduce-original-sydney.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 70%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAOCAIAAACgpqunAAAACXBIWXMAAAsSAAALEgHS3X78AAACZ0lEQVQoz12S6U8TYRDG9/+PiYm07tEWCLQSBYUQaEjFGAU1yhEFokB332N70JYe22N333vMtgaCyZP5MJnfPJM8Y7kYHPQoFz/KwbBST49bMRlzJ9A5BB6GZazsQOd8jSfScjB4gXKR+Y/05tX21X6YBGNRa/Fqg+URrCNxG4lffVah0iphVa7HNn5CZjDJagGpdSz9iJ330mCYlKl+dmsuh5IJuYGZVQrEDpq62DwYLrDF2TYy+430tJvkfXXUla2puI04V2bG5TKSlo1gCydvcbKEsulcMDck8xUECki/I6mLTB7BZiillDOhAUAocx1JyybwBrOPzdlBGJex2GsKDymHQJFmcgm8xFCg4FEoh2a3yc/vGYABYwDAyvlq1WfboThosuN2ej1IqiReDbg7h20MBWxKSHoUlhBsUamNSaSuj3gG15rpSSe56cdfu7zWSPyI4VH6uzerYOFivROyKp19ak4rRG5THqUSAFKpP7dTro112GZnvXSNyD+Rihm/GabXQzZOWHucXA74UZfPuJBK+pEIxwwA6ERc9NONUA6YthwCJWpsbGotdtVPi8R4Pj/rzC7u45P2JOaSKa20UdpwZcDoHz3xvK5LWCVSWx6FCpXhmP8c8ALReWy2Q3Z6NzlspS98WQ2Ti850yhUAjJhKhRoz5SCz11Jg5s4fWuxbj+fmwb4m4nt7uknYLomLDbADuUnZ7xG/GrAvXXYTZe/lELN/N4cLFBwCNjZZHgRWiHkVqjWqijTrLEJaJWqFqGVqPKRzvlnC8L6jAIy1mHiQS8Cev4f3tOngf/IQ5BHsZmebv65E1ec3KV01AAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;geo reduce original sydney&quot;
        title=&quot;geo reduce original sydney&quot;
        src=&quot;/static/d6b218ec83658446f75a53e70ad38660/39d99/geo-reduce-original-sydney.png&quot;
        srcset=&quot;/static/d6b218ec83658446f75a53e70ad38660/0eb09/geo-reduce-original-sydney.png 500w,
/static/d6b218ec83658446f75a53e70ad38660/1263b/geo-reduce-original-sydney.png 1000w,
/static/d6b218ec83658446f75a53e70ad38660/39d99/geo-reduce-original-sydney.png 1102w&quot;
        sizes=&quot;(max-width: 1102px) 100vw, 1102px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Reduced (Simplified):&lt;/strong&gt;
&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 1102px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/e64a969514908c5d09c664e96e26ead2/39d99/geo-reduce-reduce-sydney.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 70%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAOCAIAAACgpqunAAAACXBIWXMAAAsSAAALEgHS3X78AAACVElEQVQoz2WTCXOiMBTH+f6fZQ+FSrW1styiUqk6LVAXScIhKyDgDQq7wbbT3Z15k8l7k9878k8IKNxAnoI8iVeBwu4nAxy5fBJiawYECnAkDoo04MkF8y2FJlEfomr+X/IKe6NeAg1/zLmDLuApKNKRMY7nE3fYJaDYwvY/KbYwLNw4cjux9dDQIlNDcsd6+BKZ4+Kwcwf3GEZK5xPc+qsyT3mjXmg8Ao4Mpv0UGNGrtg+9Yxo6Sge37Sh3jnJ3HQlwTSjcIPE9BU/5IwaJNOBIp985bdNjGlVleUrj8EXFsDvoBhPJe2SRdOuNetfiSGyheiLAk1DEriO38eXNp1VVVmV5Ph0Im20gifZUxlOZYCqvrZmrMoCnMCzRb+PUiQDXdOR2vsvO+Snz7KoqiaXGR8Y4sfXV8zCYSImtJ7YemZojdwDX9FTGV5mlxiO57SidzJmfj4dzkQfTfr7LiOBJiuYTp99eW7ND/GuzhCk099Fy49sJMEJdzbfZKVsntr62ZufTYRugBBje8GEfBwTkSSTdQp4MpnICTSTRgG3GP2cbH8TWc77bXPJTeS6KXXY+7quqikxt0fsKOHIfBQSSaCTSmbdIbN2Rb2224Q660XyyehkBtuFr+IaK3aaqqmK/veTH43oFBMp/ZIv9Blf21R+hOQZcE/CUo9yHxthTGXf4gCQa51LucM8LPXp9SqGZuRbkyaUmXIqCeJOUa9abFpKxWq5yj2p56r5aUMTdOXg6CrDfAdsIJlJ5uRBXPT/s7Z+86/wRBO/x60NaatwfnX8DCTC6Ular1x4AAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;geo reduce reduce sydney&quot;
        title=&quot;geo reduce reduce sydney&quot;
        src=&quot;/static/e64a969514908c5d09c664e96e26ead2/39d99/geo-reduce-reduce-sydney.png&quot;
        srcset=&quot;/static/e64a969514908c5d09c664e96e26ead2/0eb09/geo-reduce-reduce-sydney.png 500w,
/static/e64a969514908c5d09c664e96e26ead2/1263b/geo-reduce-reduce-sydney.png 1000w,
/static/e64a969514908c5d09c664e96e26ead2/39d99/geo-reduce-reduce-sydney.png 1102w&quot;
        sizes=&quot;(max-width: 1102px) 100vw, 1102px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;This simple optimization easily drops the file size to a fraction of the original!&lt;/p&gt;
&lt;h3 id=&quot;3-Dissolve-State-Boundaries&quot; style=&quot;position:relative;&quot;&gt;3. Dissolve State Boundaries&lt;a href=&quot;#3-Dissolve-State-Boundaries&quot; aria-label=&quot;3 Dissolve State Boundaries permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;By default, the ABS remoteness boundaries are provided per state. So “Inner Regional Australia” has separate shapes for NSW, Victoria, Queensland, etc. For a national dashboard, we don’t care about the state borders. We just want a single national feature for each remoteness category.&lt;/p&gt;
&lt;p&gt;A sample of the input properties looks like this:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;json&quot;&gt;&lt;pre class=&quot;language-json&quot;&gt;&lt;code class=&quot;language-json&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;&quot;properties&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&quot;RA_CODE16&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;11&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&quot;RA_NAME16&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Inner Regional Australia&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&quot;STE_CODE16&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;1&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&quot;STE_NAME16&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;New South Wales&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&quot;AREASQKM16&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;87424.8418&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;To remove these state boundaries, we run:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;sh&quot;&gt;&lt;pre class=&quot;language-sh&quot;&gt;&lt;code class=&quot;language-sh&quot;&gt;uv run dissolve&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Here is what it looks like before and after dissolving the state borders:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;With state boundaries:&lt;/strong&gt;
&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 874px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/caf96469f83d6845fa1aae14cf381fe5/7bff9/geo-reduce-ra-with-state.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 55.00000000000001%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAALCAYAAAB/Ca1DAAAACXBIWXMAAA7DAAAOwwHHb6hkAAAB7UlEQVQoz32TS2tTQRSA8xf8Ae6UrvRnuHCpiHsNKFlUJVVrLdSNdSViQbIQMaDd2KJUQwsttUlJagKlAYmvGGhvNaYmbZKb3Pe9k0/uIze1tg4cznAe35yZOSeiqiqmaWJZVqhDMU16vR6mY9HVVAzdoNvtYu6PsSwcR9BpNWnuNogYhuElHVw9x7dpqkZxYZXF8STNvRaNdQltV/kn3mq3kBt1Irque8C/BB+2V2tQuDNH6VqKcmye7bkSXx+u8Gkm7fmF7YTFaLZBW+kMgF5VgbZ0k19rFbYf5ZGuv2frVpra2BqV4UWkeJqZ6D3KpaIPdQZQ27YPAIWvq5nvbF5Z4sdIBmk0Q+XmMtnYc4rDr9m5WyAffUHi4nlMuRXkiaOAvqOc3WA99oqd8TxSfIVvsQW2biwjjWaRRjKUbi8xdWqI8rvpoEr7/8BuvUbi7BkKl6epTub5mSyy+SBH6sIEs5cmSUUf83LoOPUP2fDahwMD7egqG0+fUMl9xJA1/4OabWYn4kydPsmzE8eYv3rO/zrRO/oNA+ohLSTC/Zdkgtz9GL9Lnwe3ClJCoBDCg/a1G+TYNsIRA58LDQ5bffOWZrUaAvvt5gHdxnbFnRjX0J8cV/bb3IlwD9cUBaUj05Flz6dpmmd3/Yqi8Ad1+ChBxiC3oQAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;geo reduce ra with state&quot;
        title=&quot;geo reduce ra with state&quot;
        src=&quot;/static/caf96469f83d6845fa1aae14cf381fe5/7bff9/geo-reduce-ra-with-state.png&quot;
        srcset=&quot;/static/caf96469f83d6845fa1aae14cf381fe5/0eb09/geo-reduce-ra-with-state.png 500w,
/static/caf96469f83d6845fa1aae14cf381fe5/7bff9/geo-reduce-ra-with-state.png 874w&quot;
        sizes=&quot;(max-width: 874px) 100vw, 874px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Without state boundaries:&lt;/strong&gt;
&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 877px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/605977b8acb059a962ef08ec76fce1e4/8d3a8/geo-reduce-ra-without-state.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 54.800000000000004%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAALCAYAAAB/Ca1DAAAACXBIWXMAAA7DAAAOwwHHb6hkAAAB8UlEQVQoz3WTwWsTQRTGc1QPxYrSSKF4rf+Aigfx6MEKFiqI4LUoIrTgWY+eRHuqeNIGVFTamj3EJOo2ahHTxJqsKGLRpqjYpq7r7nYzO5ufzHY3qbF98HjM973vY+bNTMJ1XRqNBkKIVo3TawiaQUDTFwjHwfM8bNtGbuhRGUjJqmXyY+UnCdXUbDbpDD/CXM8j/abEUOohiyt1MrXvfLHskNuo+uO6LK/WSaytrYWGnali+ZfJ4ckce7WXbJ+a4UrlE0OFEtf0VyEvpAxNQ41aC9E2JCJUNISPtrDIwGyVHdk5ks/n6dHf0ZUt0pUvc+zGOF+NatgbBLKl833/X8P4mI8WaiS0WbrzJXqflkk+e0v/XY196QI9LwyS9zKMjlzAMuvRRoK2obqU2FAG68TM+w/0PcjRW6iyK1di25MiO7NF9hQMuvUKffkyg2dOommpdZ2UmxvG1bNMhi8Oszulsb9Q4ezcRw7oZfrHbnHk5gSH7kxzfuAo5df6/4abzVD4gsf3bzM9b7BkOyFWt2zGr1/l3KnjDJ44yOil08joqrec4VbhB20+PTnB5bERPhtGdClBuy82VKAyjatKKf1wpjLCVI3FeibHt6VayzDWhIbqYasf4jhOCKiZdmKqqnX8UyzrN6ZptvrVptQbVNxfFJQaz4mTNRIAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;geo reduce ra without state&quot;
        title=&quot;geo reduce ra without state&quot;
        src=&quot;/static/605977b8acb059a962ef08ec76fce1e4/8d3a8/geo-reduce-ra-without-state.png&quot;
        srcset=&quot;/static/605977b8acb059a962ef08ec76fce1e4/0eb09/geo-reduce-ra-without-state.png 500w,
/static/605977b8acb059a962ef08ec76fce1e4/8d3a8/geo-reduce-ra-without-state.png 877w&quot;
        sizes=&quot;(max-width: 877px) 100vw, 877px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;h2 id=&quot;Visualising-Geospatial-Data&quot; style=&quot;position:relative;&quot;&gt;Visualising Geospatial Data&lt;a href=&quot;#Visualising-Geospatial-Data&quot; aria-label=&quot;Visualising Geospatial Data permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;If you are working with GeoJSON files in VS Code, I highly recommend checking out the &lt;a href=&quot;https://github.com/RandomFractals/geo-data-viewer&quot;&gt;VSCode Geo Data Viewer extension&lt;/a&gt;. It makes it super easy to view the maps, filter features, and colour them.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 1057px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/ca45d7465532c5422c26f7416d6a6e60/c3ac1/geo-reduce-vscode-plugin.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 55.8%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAALCAIAAADwazoUAAAACXBIWXMAAAsSAAALEgHS3X78AAACTUlEQVQoz13Lu27TUADGcQ9laEubSxOTxrFj55wTHzuxz833S3pBkLQlKUhcoiJKqRAqbDSRQKgvwEAROwMsbAhYeQQkClKRmHkTFLHx6bd8w19K87U8T9M0S7MsjOM4y5LZ0jCKfI97QgSeCAT3PeGJ2fUEp8T1hBCcSUEQWtGGQVNAEuD4yCaWK0CHrbZww4CyBooNWGiYKxqSNVhRgaxBBdpau2NgRxJR3k37JB/YIrdYimnS9XqrVlBo0RJgS4AzKl6NrX4oMkE1kywDWgSsDJnmJJIIc5cmHceHJv0HmUSGpADoQosdrrln++D3feXDCH4eGw+v4TmV10xeQVx3EynwAmz7uOtDzKA508a0DCi06NsR+ran/hgrZ/eUXwer5+P69+f19cvOYpNVEdOdWAqD2PXWnbDXcSNkcWSLDg2XdBr57vmh+vNOfbrlj4dX9nZ6g7j36bH+5aR5UfXKkNRMIXleSGjO/HWbxBaJLBrbJKwCWsH064vGn6NLozxF8bbtb7S8rfcT9PFEXdREGZISoBJl3Nq8TEYjLBLsBJhEuOvLkCwbfH8XDzLLFXnY67te1o2uvjvu3tpgcypdgXQWcz/Ub263HtwwmA8RhZghzGXES5DP6+JCk+sk64SbplgT+WA8FDXbLUIhm14RMCnhLlRaSGkhHbYNBHWIdCirsKSiqgarGiw3YEkBBQVWtdbdHaNm4ErTlJvmioqk9u1n2vWpujttjCb14UTbnaijaXU4WRgc/2e+/xQevHSOXncenbInb9jR6V9nyIq6REgckgAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;geo reduce vscode plugin&quot;
        title=&quot;geo reduce vscode plugin&quot;
        src=&quot;/static/ca45d7465532c5422c26f7416d6a6e60/c3ac1/geo-reduce-vscode-plugin.png&quot;
        srcset=&quot;/static/ca45d7465532c5422c26f7416d6a6e60/0eb09/geo-reduce-vscode-plugin.png 500w,
/static/ca45d7465532c5422c26f7416d6a6e60/1263b/geo-reduce-vscode-plugin.png 1000w,
/static/ca45d7465532c5422c26f7416d6a6e60/c3ac1/geo-reduce-vscode-plugin.png 1057w&quot;
        sizes=&quot;(max-width: 1057px) 100vw, 1057px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;h2 id=&quot;Working-Project&quot; style=&quot;position:relative;&quot;&gt;Working Project&lt;a href=&quot;#Working-Project&quot; aria-label=&quot;Working Project permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;As mentioned, the complete code for this geo-processing tool is available in the &lt;a href=&quot;https://github.com/mortie23/geoshape-reduce-size&quot;&gt;geoshape-reduce-size repository&lt;/a&gt;. Feel free to check it out!&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Setting up a cloud webserver for hosting a static site]]></title><description><![CDATA[Why would I want to do this? Since there are many easier (options) for hosting a static website (of which I recommend and also use Github…]]></description><link>https://mortimer.xyz/setting-up-webserver/</link><guid isPermaLink="false">https://mortimer.xyz/setting-up-webserver/</guid><pubDate>Fri, 05 Feb 2021 12:00:00 GMT</pubDate><content:encoded>&lt;h2 id=&quot;Why-would-I-want-to-do-this&quot; style=&quot;position:relative;&quot;&gt;Why would I want to do this?&lt;a href=&quot;#Why-would-I-want-to-do-this&quot; aria-label=&quot;Why would I want to do this permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Since there are many &lt;em&gt;easier&lt;/em&gt; (&lt;a href=&quot;https://geekflare.com/best-static-site-hosting-platform/&quot;&gt;options&lt;/a&gt;) for hosting a static website (of which I recommend and also use Github pages) then why would I want to go through this process. For me, I need to use the server for a few other things as well, but the main reason was just to test putting a few things together quickly to see what was involved. The TL;DR version is:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Getting a cloud linux server&lt;/li&gt;
&lt;li&gt;Installing NGINX for the webserver&lt;/li&gt;
&lt;li&gt;Getting a domain name and configuring the DNS&lt;/li&gt;
&lt;li&gt;Getting an SSL certificate&lt;/li&gt;
&lt;li&gt;Putting some static content up to test&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id=&quot;The-tools&quot; style=&quot;position:relative;&quot;&gt;The tools&lt;a href=&quot;#The-tools&quot; aria-label=&quot;The tools permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://www.linode.com/&quot;&gt;Linode&lt;/a&gt; for the Cloud server&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://ubuntu.com/&quot;&gt;Ubuntu&lt;/a&gt; for the operating system&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://ubuntu.com/tutorials/install-and-configure-nginx#1-overview&quot;&gt;Nginx&lt;/a&gt; for the webserver&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.fastcomet.com/domains/registration&quot;&gt;Fastcomet&lt;/a&gt; for the domain name&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://certbot.eff.org/&quot;&gt;Let’s Encrypt&lt;/a&gt; for the SSL certificate&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://reactjs.org&quot;&gt;React&lt;/a&gt; renders the UI&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://mdxjs.com&quot;&gt;MDX&lt;/a&gt; converts markdown into React components&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://frontarm.com/navi/&quot;&gt;Navi&lt;/a&gt; handles routing and creates static HTML files for each route&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://reactjs.org/docs/create-a-new-react-app.html&quot;&gt;create-react-app&lt;/a&gt; builds the app’s assets and provides the dev server&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;1-Getting-a-cloud-linux-server&quot; style=&quot;position:relative;&quot;&gt;1. Getting a cloud linux server&lt;a href=&quot;#1-Getting-a-cloud-linux-server&quot; aria-label=&quot;1 Getting a cloud linux server permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;There are lots of options out there these days. I went with Linode for this one. The smallest shared CPU option will be totally fine for hosting a static site and any of the LTS Ubuntu images will be fine too.
&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 1105px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/f5ba8b7023d6f10ef7541fd0401da8aa/0b602/setting-up-webserver-linode-machine.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 73.4%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAPCAYAAADkmO9VAAAACXBIWXMAABJ0AAASdAHeZh94AAABrElEQVQ4y41UXWvDMAzM//9rYzDY42CDPbVN6jixLcsfyQ3JaWi3rpvhkGxURTqd2n18HiCwM6E3AeMUMBiH09nBTl59Y5uV+2mYMU4ePtBddGaccDYWp75H3/dgZrSzYl1XtRfs97X599ANwwBrLWKM8D6glHoTsCyCBdg/8/h0RATnPMS2hEUT1FrVyj3n/GtFPyqkGMEpodSqyJqgbDZrQk36ACllpNzQHQ6HnbsQAoiitk+RQbEiUEGIRd9DIASiPUbiY2yc7xX2pwHGTMgla0XSaqtKKpaWF/Xl/T6WWw5fX97x/PSGUlt7l2neDmZ5OIgbDnMR3ipSblwIn8qJ+nn3HyJfcSiSOR6POuVv371b7Z9TTrlicozIFZEThvOoRHNaQCw8rsjlfy0rh8Jb5Kxte0o42wDnWaWTUpNO3oZyGdb3wVwn7lQiRJid22QgkhGxO3jv9U2siD5cxYmEZCEkvsoC1LZhnTiRGaO1CBQ3udT2QyINFl8WQKpx3rdFKAU+BBW22Gme1e+MKzATg5mUuwsfTULt6DZtfxopJU18LSehRaYtHH4BwxyXX/am694AAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;setting up webserver linode machine&quot;
        title=&quot;setting up webserver linode machine&quot;
        src=&quot;/static/f5ba8b7023d6f10ef7541fd0401da8aa/0b602/setting-up-webserver-linode-machine.png&quot;
        srcset=&quot;/static/f5ba8b7023d6f10ef7541fd0401da8aa/0eb09/setting-up-webserver-linode-machine.png 500w,
/static/f5ba8b7023d6f10ef7541fd0401da8aa/1263b/setting-up-webserver-linode-machine.png 1000w,
/static/f5ba8b7023d6f10ef7541fd0401da8aa/0b602/setting-up-webserver-linode-machine.png 1105w&quot;
        sizes=&quot;(max-width: 1105px) 100vw, 1105px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;Once you have created the machine you will need to get the machines IP address to access it.
&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 932px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/a22aee86782cbec3e017b3bb1bbc23f0/79586/setting-up-webserver-linode-details.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 51%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAKCAIAAAA7N+mxAAAACXBIWXMAAAsSAAALEgHS3X78AAABHklEQVQoz4WS627CMAyF8/7PCEVAA91yb2K7bZx0akuZYBL7dKTIPywf+0QobZQ2rbxdWtnKW59SAkyAgPRBnY7GJ6FRaWOMtWVl3im1ftY4sUgpxT4924gG5zwA1Frn/xDeB6V0Zt5qRLzfu677IqKcc2Ze9ApzTsQwVIGIPgQi2prHcVJKW+uMdc4751wIYXqF8+Qie6gCALzvn7ZTjN9K8UYp+/ug7mSuXGYBgDHBvC+IiNdWKm0BCYliSs55axcbMUZmXifnANxjFW83AIDLVcrb/XKV1gVr7fl8bprmcDgej82pOSmljFHKDx5nsR6Ap5xLWabXWkv5FSLGFUQcx4GIti0etp33WmttbIzpbxjGLF9oDQ+nKb9F9QNcgkM9uFqADgAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;setting up webserver linode details&quot;
        title=&quot;setting up webserver linode details&quot;
        src=&quot;/static/a22aee86782cbec3e017b3bb1bbc23f0/79586/setting-up-webserver-linode-details.png&quot;
        srcset=&quot;/static/a22aee86782cbec3e017b3bb1bbc23f0/0eb09/setting-up-webserver-linode-details.png 500w,
/static/a22aee86782cbec3e017b3bb1bbc23f0/79586/setting-up-webserver-linode-details.png 932w&quot;
        sizes=&quot;(max-width: 932px) 100vw, 932px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;h2 id=&quot;2-Installing-NGINX-for-the-webserver&quot; style=&quot;position:relative;&quot;&gt;2. Installing NGINX for the webserver&lt;a href=&quot;#2-Installing-NGINX-for-the-webserver&quot; aria-label=&quot;2 Installing NGINX for the webserver permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;There is no point in writing the steps to install Nginx as it has already been written many times. I’ll link to the tutorial from Ubuntu.
&lt;a href=&quot;https://ubuntu.com/tutorials/install-and-configure-nginx#1-overview&quot;&gt;Install and configure Nginx&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;But before you can run the shell commands you’ll need to access your machine via SSH. If you are using Windows, I’ll give a shout out to the Windows Terminal.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 1256px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/53ce799d1e8191e9dcf378b9c448e30d/30373/setting-up-webserver-windows-terminal.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 40.2%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAICAYAAAD5nd/tAAAACXBIWXMAABJ0AAASdAHeZh94AAAB/ElEQVQoz42R3UtTcRjHf7rtwGKslXIYcy4cHXJpSIs0g93knEYvSEUvF6VBwYIoiBIhCix6mUOdZvQvVHdBF0VXvWDdxW4GMl2+BBWCL6BnO9vZPvE7i+iyL3zhuXieD9/neYRhGEgXi0X+raULhQLFokG5XOZ/JeRgqVSyQJVKBdM0/0Kl3n/4yP0HCcZTU5aToykSI2OMjk0yPjFFauIpk0+eMZIc583bd1WgtASl02lyuZyVKJ/PW8Cbg7cRQiCEDZvDRYN/B/Wqnxq7CyFqEcKOEE6r5/S5gSpQwmS6TCbD0tJ3CyTXlbo7/BBhc+P0+Ai1huntPUJ37DDtHZ20tLbh9QVQ/UGE4uFS/DpCrqvrOisrK+h6no2NTVZX19jc1C3gnXuPEY5tKB4/rXsP0NV9lK6e4xzrO0MkEmX/nn0cDATxuuuIX72BkOmWl5eZm82yuDDP0uICi/PfWFtbrwKHHyEcW3G4vYTbI5w8dZa2cCe7WsK4t/toqmvkRH0jmmMLl69cQxiGPH6FTzO/ePk5x/PpObI/16mYJQs4NDT054aC5uYQ0WgPiuKkptaOza6g1ij0OV00CUH/wEWEIb9ZNnkxnWX0dZrEq698mflBxax+OZlMoqoqgUCAWCzGhfP9hEK70bSdaJpGR1AjHgpxqMHLrcFBfgOHh7NMEkYJEgAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;setting up webserver windows terminal&quot;
        title=&quot;setting up webserver windows terminal&quot;
        src=&quot;/static/53ce799d1e8191e9dcf378b9c448e30d/30373/setting-up-webserver-windows-terminal.png&quot;
        srcset=&quot;/static/53ce799d1e8191e9dcf378b9c448e30d/0eb09/setting-up-webserver-windows-terminal.png 500w,
/static/53ce799d1e8191e9dcf378b9c448e30d/1263b/setting-up-webserver-windows-terminal.png 1000w,
/static/53ce799d1e8191e9dcf378b9c448e30d/30373/setting-up-webserver-windows-terminal.png 1256w&quot;
        sizes=&quot;(max-width: 1256px) 100vw, 1256px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;Using a Powershell session in Windows Terminal we can connect to our server using:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token function&quot;&gt;ssh&lt;/span&gt; root@&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;ip-address&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2 id=&quot;3-Getting-a-domain-name-and-configuring-the-DNS&quot; style=&quot;position:relative;&quot;&gt;3. Getting a domain name and configuring the DNS&lt;a href=&quot;#3-Getting-a-domain-name-and-configuring-the-DNS&quot; aria-label=&quot;3 Getting a domain name and configuring the DNS permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Since I have other hosting and domains with Fastcomet it was easy enough for me to get another domain through them. I found a domain that cost around $10 USD a year.
&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 930px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/20b015006d64ffc9980d6bb5f6171d7c/77dcf/setting-up-webserver-domain.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 94.60000000000001%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAATCAYAAACQjC21AAAACXBIWXMAABJ0AAASdAHeZh94AAADNElEQVQ4y3WU22tcRRzHz98iVbEv1j5YSstK+6qCFCxC06akRhKpCkEJqA8VRLFekqVJSGsbpA+1bpN2S598sCoq+FAtCDXoXhKzl+wlu3uue25zzkdmTnaTNevAh+/8LvOb38xwjvbSy6c4cfI0J06O8MrIGM8eOc5jTx7g8f0H2ffUQfb1dDfKd4Annj7CsdMXeP61j3lh4iKHXnwdbeqd93h7+n3F9LsXGJ98i1Oj44yOTTJ6bpIzYxOM9jg32deRs+OcefU8b3xwmakPrzL10RIT05+i+b6P5yW4rkcYhsRRjGlZdHQDy7LRDRPDMJUtVdpRFBFFgsBz8F1bIQIPLSZm2AiFIAgChdzU9xOVG0qGDVlJsz2TaqeC67kqMdheIIRQXXiep7oyTRPP97c7i1R8N7qu43oemuzEcR1E+N+OEnq2vJIgCLc7DfCDHWSOjMtGNMM06XZdpJqmhW076i67rruXrjvc77pqjWU7aPLyHceh2WwqLMvac6TeMYcddXeOLKp1dB3dMFR3kuQ1dXr+cqVCvlCgWFyjVC6rl+7Fh6Hd//5HPrn4Ben0AjOzc4rZ9LxCzi/NLXL5ypJiYeEKMzOXkvjsDun0PJ99nuabzArabw9+4eaNeW4vX2M582WflVuJ3lm5xr27Xymyt5cGYju5V8ncXOT+d/fQnPoC0cYziNIxRCnVJyyliMoprNxRKg8PU/79MPpfR4krKcKN1ECuKD2HKB3Cq76J1mo8pFXJ0KpmaVfv0KllEzaz6LW71NYzFB5dJ//oOuX818onY31qcl2WVmWZVu0HtK4HfgheALYHugUdI8awwHSgbUS0OiFtXdAxIhU3bZK4neSrGgL8ADQROhDpRKKN5ZSpb+aob/5Ns16g01pnq1FUtLfWld2s55XdqOVpNdeUuk4dYgMiG813Gwg3h/CKxP4ahOsDdM1VHONPHGOVrrFK5Bd34kGi0qdq+FW0MIwHPu4ecrieoFDYIJdbU/rPRhXD7A7N7f8cgjBQ0ziO9mCaBlvtDo2ttsJVn59DJMT/rtF6v6I4jvvam0dRzI1vH7C48jNzt37i1z/y24Xigbzd+i8TpTYacC1mUQAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;setting up webserver domain&quot;
        title=&quot;setting up webserver domain&quot;
        src=&quot;/static/20b015006d64ffc9980d6bb5f6171d7c/77dcf/setting-up-webserver-domain.png&quot;
        srcset=&quot;/static/20b015006d64ffc9980d6bb5f6171d7c/0eb09/setting-up-webserver-domain.png 500w,
/static/20b015006d64ffc9980d6bb5f6171d7c/77dcf/setting-up-webserver-domain.png 930w&quot;
        sizes=&quot;(max-width: 930px) 100vw, 930px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;Once you have purchased a domain name, you’ll need to find out how to configure the DNS. Once you’ve found this you’ll need to configure an A Record and add your machines IP address.
&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 1772px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/59e34bb66e429787a59375696b7fd6d1/e670e/setting-up-webserver-dns.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 40.2%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAICAIAAAB2/0i6AAAACXBIWXMAAAsSAAALEgHS3X78AAABKElEQVQY03WQ22rDMAyG8/5PNEa3lba72nZVGC0rThYf0jh2fJTtNIeOtFA2yj4+hPiRLqRMKiVkK6S8Vi5EI+WxrjGlQkpjrbYXnLvpvHfeW+eyA0IoLzAmhLJyrpRQhjEpy7lnjLGqqjnnTcMbzhte8xoTUmLMqipLKYUQ+2E4X5mm841rP83csr7v3z+2q/VbXuAsdZ11zjjnAWKM4c6YYt+Nu+Pn89fjBi3X6GWFlov9A1Uk8x6U1tY5pTSEP1yuA6WVMByLgsqyqL9RlRN6YOboos8gRNEaY6FVVlvvId40825qtNzijx3dxpA0nPaooptXTRhok6Wud5B86CB2PqR7IZ5SmmIa5zFIsZ9kSYqnRZvn2TAMADCO4/l/pvl10+9kvPgDk9LCV1aEywwAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;setting up webserver dns&quot;
        title=&quot;setting up webserver dns&quot;
        src=&quot;/static/59e34bb66e429787a59375696b7fd6d1/e670e/setting-up-webserver-dns.png&quot;
        srcset=&quot;/static/59e34bb66e429787a59375696b7fd6d1/0eb09/setting-up-webserver-dns.png 500w,
/static/59e34bb66e429787a59375696b7fd6d1/1263b/setting-up-webserver-dns.png 1000w,
/static/59e34bb66e429787a59375696b7fd6d1/e670e/setting-up-webserver-dns.png 1772w&quot;
        sizes=&quot;(max-width: 1772px) 100vw, 1772px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;h2 id=&quot;4-Getting-an-SSL-certificate&quot; style=&quot;position:relative;&quot;&gt;4. Getting an SSL certificate&lt;a href=&quot;#4-Getting-an-SSL-certificate&quot; aria-label=&quot;4 Getting an SSL certificate permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The last thing I wanted to do was to get an SSL certificate. After listing to &lt;a href=&quot;https://syntax.fm/show/340/servers-with-matt-from-caddy&quot;&gt;this&lt;/a&gt; SyntaxFM podcast, and reading up the benefits for even static sites to have SSL certificated, I was convinced that it was easy enough to do using &lt;a href=&quot;https://certbot.eff.org/&quot;&gt;Let’s encrypt&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Just select NGINX and Ubuntu from the Let’s encrypt page and follow the instructions.
&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 641px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/7df8a10e813bed122b3f6a224d86d1d3/243e6/setting-up-webserver-certbot.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 55.00000000000001%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAALCAYAAAB/Ca1DAAAACXBIWXMAABJ0AAASdAHeZh94AAABkUlEQVQoz62SfY+aQBDG+f6f6pJeLrlUsQoW0Or5wgIqgugKFVl+zW7kelH7X59kdmbfnnl2Zq1N4BO8fqOUkro4oK5X2ralVQqlfdtyuVw4Ho9IKTmdTjRNg0Z729dQ+rxSWIftlreXF5zhkPMPG+SJe2ii6XTKcrnE8zyqqnog1AmLosDSQRTFLBYL4s0GEUUkSWLmYRgaE0KQ7naUZfmZpCP66rVZkRD0ej1838d1XebzufGOM2LkOHiejzMa0ev3ESIyl/WTuyd2hB0s/gM6dUahHrpsGuvVkvf37wSTAMd1GQyHjH969G2bXt8mmEyxBwPGYxffD2hu9zql1n1xq6okS1P2YUgWRexFyHa1Ig1DEx+ShH24Jt1sOEn5UEvrXrpBeYbflf4LcL0ycccsZzNdPGiutMUBzvKh0w8Kjek/VdfUeUad56hjcYuzW5xTZ5k50z7p9lOFzflMIyWqqtjHMcl6xVYI9kls1jR580XhP7v8l1BSH3KUlGQiRMx+EX98kEXCJKrTnfHPCP8A3ndOhRr82JoAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;setting up webserver certbot&quot;
        title=&quot;setting up webserver certbot&quot;
        src=&quot;/static/7df8a10e813bed122b3f6a224d86d1d3/243e6/setting-up-webserver-certbot.png&quot;
        srcset=&quot;/static/7df8a10e813bed122b3f6a224d86d1d3/0eb09/setting-up-webserver-certbot.png 500w,
/static/7df8a10e813bed122b3f6a224d86d1d3/243e6/setting-up-webserver-certbot.png 641w&quot;
        sizes=&quot;(max-width: 641px) 100vw, 641px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;Now you’ll have &lt;code class=&quot;language-text&quot;&gt;HTTPS&lt;/code&gt;. But you probably want a redirect for all HTTP traffic.
You can add a basic NGINX configuration change to achieve this:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;server {
    listen 80;
    server_name mystaticstie.net www.mystaticsite.net;
    return 301 https://mystaticstie.net$request_uri;
}&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2 id=&quot;5-Putting-some-static-content-up&quot; style=&quot;position:relative;&quot;&gt;5. Putting some static content up&lt;a href=&quot;#5-Putting-some-static-content-up&quot; aria-label=&quot;5 Putting some static content up permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;I previously have used Gatsby (this blog site is built using Gatsby), which is great, but I wanted to try a basic Create React App site this time. I chose to use &lt;a href=&quot;https://github.com/jamesknelson/create-react-blog&quot;&gt;create-react-blog&lt;/a&gt; a build a basic static blog.&lt;/p&gt;
&lt;h2 id=&quot;Results&quot; style=&quot;position:relative;&quot;&gt;Results&lt;a href=&quot;#Results&quot; aria-label=&quot;Results permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Check out the results at &lt;a href=&quot;https://thefog.xyz/&quot;&gt;https://thefog.xyz/&lt;/a&gt;&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Tableau Web Data Connector]]></title><description><![CDATA[This post is in reference to the associated Github repo at https://github.com/mortie23/tableau-wdc. Tableau WDC Quick example of a Tableau…]]></description><link>https://mortimer.xyz/tableau-wdc-sas/</link><guid isPermaLink="false">https://mortimer.xyz/tableau-wdc-sas/</guid><pubDate>Thu, 17 Dec 2020 12:00:00 GMT</pubDate><content:encoded>&lt;p&gt;This post is in reference to the associated Github repo at &lt;a href=&quot;https://github.com/mortie23/tableau-wdc&quot;&gt;https://github.com/mortie23/tableau-wdc&lt;/a&gt;.&lt;/p&gt;
&lt;h1 id=&quot;Tableau-WDC&quot; style=&quot;position:relative;&quot;&gt;Tableau WDC&lt;a href=&quot;#Tableau-WDC&quot; aria-label=&quot;Tableau WDC permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h1&gt;
&lt;p&gt;Quick example of a Tableau Web Data Connector (WDC) pointing to the output of a SAS proc json.&lt;br&gt;
Reason: For example, your source data is in SAS, and your BI tool is Tableau.
You want a way to publish the data to Tableau and you have access to a webserver.&lt;/p&gt;
&lt;h2 id=&quot;Automate-the-generation-of-the-WDC-package&quot; style=&quot;position:relative;&quot;&gt;Automate the generation of the WDC package&lt;a href=&quot;#Automate-the-generation-of-the-WDC-package&quot; aria-label=&quot;Automate the generation of the WDC package permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Before we start, if you have lots of datasets or need to make lots of changes to a dataset, then creating or modifying the javascript becomes tedious. I’ve built a simple Node.js package to automate this. See &lt;a href=&quot;../tableau-wdc-gen/&quot;&gt;Tableau Web Data Connector Node Generator&lt;/a&gt; post if you are interested.&lt;/p&gt;
&lt;h2 id=&quot;Tutorial&quot; style=&quot;position:relative;&quot;&gt;Tutorial&lt;a href=&quot;#Tutorial&quot; aria-label=&quot;Tutorial permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;This is based on this tutorial.&lt;br&gt;
&lt;a href=&quot;https://tableau.github.io/webdataconnector/docs/wdc_tutorial.html&quot;&gt;https://tableau.github.io/webdataconnector/docs/wdc_tutorial.html&lt;/a&gt;&lt;/p&gt;
&lt;h2 id=&quot;Generate-the-data&quot; style=&quot;position:relative;&quot;&gt;Generate the data&lt;a href=&quot;#Generate-the-data&quot; aria-label=&quot;Generate the data permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;To generate the JSON I installed WPS Community edition.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;sas&quot;&gt;&lt;pre class=&quot;language-sas&quot;&gt;&lt;code class=&quot;language-sas&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;libname&lt;/span&gt; this &lt;span class=&quot;token string&quot;&gt;&quot;./data&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token step keyword&quot;&gt;proc json&lt;/span&gt; &lt;span class=&quot;token proc-args&quot;&gt;&lt;span class=&quot;token arg keyword&quot;&gt;out&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;./json/class.json&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
  export this&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;class&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token step keyword&quot;&gt;run&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2 id=&quot;Deploy-the-HTMLJSJSON&quot; style=&quot;position:relative;&quot;&gt;Deploy the HTML/JS/JSON&lt;a href=&quot;#Deploy-the-HTMLJSJSON&quot; aria-label=&quot;Deploy the HTMLJSJSON permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;To get this demo going, copy the files in the Github repo to a location on your webserver. In this example I’ll be using my &lt;a href=&quot;https://mortie23.github.io/&quot;&gt;mortie23.github.io/&lt;/a&gt;&lt;/p&gt;
&lt;h2 id=&quot;Test-the-WDC&quot; style=&quot;position:relative;&quot;&gt;Test the WDC&lt;a href=&quot;#Test-the-WDC&quot; aria-label=&quot;Test the WDC permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Open up Tableau (in this case I’m just using Tableau Public Desktop).&lt;/p&gt;
&lt;h3 id=&quot;Add-a-data-connection&quot; style=&quot;position:relative;&quot;&gt;Add a data connection&lt;a href=&quot;#Add-a-data-connection&quot; aria-label=&quot;Add a data connection permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 542px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/476cd0f46aea624c55705662232c19a1/47f85/new-connection.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 91.19999999999999%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAASCAIAAADUsmlHAAAACXBIWXMAAAsSAAALEgHS3X78AAACJklEQVQ4y53SW2/SYBgHcL6OiRilpeWQ0pbD5JANdDMceqAcKh2liOMQZYv0bVkLCEvQcdiIN155NVz2DbzzA5jMOePkah/DFBaNmjhm8r/9vc/hfUwOrhnZ6nuF9v181y92A4VuqLjnzjbNcaU+Ojq7OP307cvn2fnZ7Osipxfns8vvxx8+OvP7JmeqGZR6XqHjEzq+zY7hpT08DSy0GpPfym9OdiYn9YPjyuCoNpzWhtPqYPp8/D738h0mDkxWWoYSDSstLwJTDYQBULSOchr55NC3NfGWDnBpjOZeQ5n+IpZ0H+FfuQpDE8IAB6faORWmGjA1f4IBUMzAnqeHgerEX574K5NAZUIUR78ijQyMsoB8rBO8jmc1W1Kx0jLCAEu0bktpLmmEifuYOMTEASYOXIXhHzGhDFjJGwOTfMs5r3+FOQ2Xxn+D37CVlvGsTmT1e/EXV8MvjxEGYOldV0ZzZXYdnHozjLIgUOh5hTbJ6wSv3biyd7PtybWgxH+17S/0QlKP5HWElm+IjYVpJK+7+BacVOGfX7UMhlkFZRWEVbDENhF9htAylGwujuR6TCS21+nqOlWORaVgrBx+mPc/KNzZqNlSS+C1R6XEBr8SyXnC2WCEXw1QmDt2KyjYUzo+v8F/YQsNyHiVCAv2UO7uqmheK96OlMzhos3ASywMooGFVWFWtbIKwgCUVeD4zlILsycVN68bFzLf+SJWqmFLt65t+wemfLpW/OYGBAAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;New Connection&quot;
        title=&quot;New Connection&quot;
        src=&quot;/static/476cd0f46aea624c55705662232c19a1/47f85/new-connection.png&quot;
        srcset=&quot;/static/476cd0f46aea624c55705662232c19a1/0eb09/new-connection.png 500w,
/static/476cd0f46aea624c55705662232c19a1/47f85/new-connection.png 542w&quot;
        sizes=&quot;(max-width: 542px) 100vw, 542px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;h3 id=&quot;Browse-the-the-published-WDC-HTML-page&quot; style=&quot;position:relative;&quot;&gt;Browse the the published WDC HTML page&lt;a href=&quot;#Browse-the-the-published-WDC-HTML-page&quot; aria-label=&quot;Browse the the published WDC HTML page permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 461px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/27ad152008f4614698cdbd3d4a2b8b3c/31fe3/browse-url.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 32.75488069414317%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAHCAIAAACHqfpvAAAACXBIWXMAAAsSAAALEgHS3X78AAABG0lEQVQY06WPvUrDYBiFvxtRcBCvRRHEwQvRKIpTkr0tKoUkiA0GWkkriSYm0Pw1/6RJq7azi4NaquLPZvPlk6hIOjn48PByOGd6wfP44eXpEU2R/WmWwfe3V1DmlAqnVnmjyhsHJ/qhYJ8aSVOPi7b0pKnlt5Xnr1LrHksemF3amFlcn1/ZWljdnlveXMMqbpiYbtLxEtONLTd2/N6F5rrRlW6H7U6o2aHpxTe392dmDHBaxGmBYESSEXFGKHFqQw1qksXK1pFksrJVk6264jfUoK74P6oB347YcwcUf4UZRAhFowjzMLJLELnkbrDj3Dm/axEAYZam8NuPyQQhNBwPS70yNaCpa4oa0PuXe/1RHyGUwjSbBqB/8AlEXVn2bGmsIAAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Browse URL&quot;
        title=&quot;Browse URL&quot;
        src=&quot;/static/27ad152008f4614698cdbd3d4a2b8b3c/31fe3/browse-url.png&quot;
        srcset=&quot;/static/27ad152008f4614698cdbd3d4a2b8b3c/31fe3/browse-url.png 461w&quot;
        sizes=&quot;(max-width: 461px) 100vw, 461px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;h3 id=&quot;Get-the-data&quot; style=&quot;position:relative;&quot;&gt;Get the data&lt;a href=&quot;#Get-the-data&quot; aria-label=&quot;Get the data permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Click &lt;code class=&quot;language-text&quot;&gt;Get Class Data!&lt;/code&gt; and you should be able to build a dashboard from the JSON data now.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 706px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/b9bb0793db04423ca892dd97da4fdf99/190d7/success.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 80%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAQCAIAAACZeshMAAAACXBIWXMAAAsSAAALEgHS3X78AAABeUlEQVQoz42TyZLiMAxA+f8vnD5R0EBC4lW2VkPoihn6MB265h1sH/Qkl2TvclwBgL5H7KiqmcUYiejxnl1OKYQAANRBolKxmd3vd0QUkd/k/eHw8fFnGMfQcd7PPtxut++I5cWGPE5uHEcfgvfeOTfPc0pRVc/ny36/PxyOqRNC2JA/j8d5mnOHmREx52xmRFRrRUR+sSE/qznnvPeIWDuttcd/sKMX3+ZTXn6wIeMPfqm8vNblnUxEpUDOWUWEWZiJiAmb6WNZ7n8HsWb4V65rh8iFeJ58qhRyDVB9Lj5DqlTFgKSqVW3abluVEStxYdV2SygohmaFBdViRdYWESurbctEOcXrODSzFAMRMmKKkQnPpxMiTtdxnufW7J2cfIhmVmtlZlXjPvYU0zAMzHy5XADgTcMAnHOq2s31kzDT8/0450RkmiZE3JD7tApkaK2JsJn1gzAz9KQicp2u2zIRpZTmeQ3KOT9vvg4PwDs/DEMp5fR5Aihf+QCbQswvO5QAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Success&quot;
        title=&quot;Success&quot;
        src=&quot;/static/b9bb0793db04423ca892dd97da4fdf99/190d7/success.png&quot;
        srcset=&quot;/static/b9bb0793db04423ca892dd97da4fdf99/0eb09/success.png 500w,
/static/b9bb0793db04423ca892dd97da4fdf99/190d7/success.png 706w&quot;
        sizes=&quot;(max-width: 706px) 100vw, 706px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Embedded BI]]></title><description><![CDATA[What is Embedded BI When people talk about Embedded Business Intelligence (BI) they are usually referring to putting things such as…]]></description><link>https://mortimer.xyz/embedded-bi/</link><guid isPermaLink="false">https://mortimer.xyz/embedded-bi/</guid><pubDate>Wed, 04 Nov 2020 12:00:00 GMT</pubDate><content:encoded>&lt;h1 id=&quot;What-is-Embedded-BI&quot; style=&quot;position:relative;&quot;&gt;What is Embedded BI&lt;a href=&quot;#What-is-Embedded-BI&quot; aria-label=&quot;What is Embedded BI permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h1&gt;
&lt;p&gt;When people talk about Embedded Business Intelligence (BI) they are usually referring to putting things such as interactive dashboards and/or analytics within another business application. If that other business application is web based, and is customizable, then you might be able to achieve some of this yourself.&lt;/p&gt;
&lt;p&gt;It seems that the popularity of data journalism and organisations want to publish data is increasing. Or maybe it’s just my perspective.&lt;/p&gt;
&lt;h2 id=&quot;A-sample-of-options&quot; style=&quot;position:relative;&quot;&gt;A sample of options&lt;a href=&quot;#A-sample-of-options&quot; aria-label=&quot;A sample of options permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;I’ve recently explored the use of multiple different BI tools and what capability they have to embed their content in a website. I’ll run through the few I’ve looked into, specifically the ones which you can use for &lt;em&gt;free&lt;/em&gt;.&lt;/p&gt;
&lt;h3 id=&quot;Tableau&quot; style=&quot;position:relative;&quot;&gt;Tableau&lt;a href=&quot;#Tableau&quot; aria-label=&quot;Tableau permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Tableau was my first choice as it is a product I had used before. It has a free tier and is nice enough to allow you to publish content and embed it on other sites.
You can sign up for an account here &lt;a href=&quot;https://public.tableau.com/s/&quot;&gt;https://public.tableau.com/s/&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 449px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/ef46960dc61824406d6367723175d3f6/46d70/embedded-bi-tableau-sign-up.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 55.90200445434299%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAALCAYAAAB/Ca1DAAAACXBIWXMAAA7DAAAOwwHHb6hkAAABDUlEQVQoz62SS6+CMBSE+///FRgWhIiyYEOAnRiQ90MFI3MzJylpDHelJJPS02E4/VoVxzHO5zPCMMTxeEQURQiCQGq+78N1XVwuF5RliaIocLvddkVPlmVQaZrC8zzYtg3LsiTgdDpJmOM4OBwOSJJEAvM83xV/xLDr9Qp1v9/xfD6xLIuI71rzPEttmiYMw4BxHP8VPRwVALzfb6zril88ih0w+fV6SfA3YlOy5bqu0TSNqOu6bd62rUivmR7Wq6oSJAzaAr/Z3h4mxeKv+Ekg2WmGZusml09Opu/Tq3g9yKXv+40RGXLOkZw0T71mMqaPo25IPR4PMegP9KHQSDHQPAQdpEO1n3eWW/4DkYJPzKzSDhwAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;embedded bi tableau sign up&quot;
        title=&quot;embedded bi tableau sign up&quot;
        src=&quot;/static/ef46960dc61824406d6367723175d3f6/46d70/embedded-bi-tableau-sign-up.png&quot;
        srcset=&quot;/static/ef46960dc61824406d6367723175d3f6/46d70/embedded-bi-tableau-sign-up.png 449w&quot;
        sizes=&quot;(max-width: 449px) 100vw, 449px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;You will need to download the free Tableau Public Desktop application to develop you first dashboard and publish it to your Tableau public profile. Once you do, the Tableau public dashboard will have a &lt;em&gt;share&lt;/em&gt; icon which will provide you with the embed code. It might look something like this:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;html&quot;&gt;&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;&amp;lt;!-- Start Tableau Embed --&gt;&lt;/span&gt;
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;section&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;brands-area&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;container&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;tableauPlaceholder&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;vizSOMEID&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token special-attr&quot;&gt;&lt;span class=&quot;token attr-name&quot;&gt;style&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;token value css language-css&quot;&gt;&lt;span class=&quot;token property&quot;&gt;position&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; relative&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
      &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;noscript&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
        &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;a&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;href&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;#&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
          &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;img&lt;/span&gt;
            &lt;span class=&quot;token attr-name&quot;&gt;alt&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
            &lt;span class=&quot;token attr-name&quot;&gt;src&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;https://public.tableau.com/static/images/nh/YOURDASHBOARDNAME/Dashboard1/1_rss.png&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
            &lt;span class=&quot;token special-attr&quot;&gt;&lt;span class=&quot;token attr-name&quot;&gt;style&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;token value css language-css&quot;&gt;&lt;span class=&quot;token property&quot;&gt;border&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; none&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
          &lt;span class=&quot;token punctuation&quot;&gt;/&gt;&lt;/span&gt;&lt;/span&gt;
        &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;a&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
      &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;noscript&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
      &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;object&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;tableauViz&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token special-attr&quot;&gt;&lt;span class=&quot;token attr-name&quot;&gt;style&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;token value css language-css&quot;&gt;&lt;span class=&quot;token property&quot;&gt;display&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;none&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
        &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;param&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;host_url&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;https%3A%2F%2Fpublic.tableau.com%2F&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;/&gt;&lt;/span&gt;&lt;/span&gt;
        &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;param&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;embed_code_version&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;3&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;/&gt;&lt;/span&gt;&lt;/span&gt;
        &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;param&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;site_root&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;/&gt;&lt;/span&gt;&lt;/span&gt;
        &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;param&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;name&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;YOURDASHBOARDNAME/Dashboard1&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;/&gt;&lt;/span&gt;&lt;/span&gt;
        &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;param&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;tabs&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;no&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;/&gt;&lt;/span&gt;&lt;/span&gt;
        &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;param&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;toolbar&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;yes&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;/&gt;&lt;/span&gt;&lt;/span&gt;
        &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;param&lt;/span&gt;
          &lt;span class=&quot;token attr-name&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;static_image&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
          &lt;span class=&quot;token attr-name&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;https://public.tableau.com/static/images/nh/YOURDASHBOARDNAME/Dashboard1/1.png&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;/&gt;&lt;/span&gt;&lt;/span&gt;
        &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;param&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;animate_transition&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;yes&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;/&gt;&lt;/span&gt;&lt;/span&gt;
        &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;param&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;display_static_image&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;yes&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;/&gt;&lt;/span&gt;&lt;/span&gt;
        &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;param&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;display_spinner&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;yes&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;/&gt;&lt;/span&gt;&lt;/span&gt;
        &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;param&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;display_overlay&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;yes&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;/&gt;&lt;/span&gt;&lt;/span&gt;
        &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;param&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;display_count&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;yes&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;/&gt;&lt;/span&gt;&lt;/span&gt;
        &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;param&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;language&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;en&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;/&gt;&lt;/span&gt;&lt;/span&gt;
      &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;object&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;section&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;&amp;lt;!-- End Tableau Embed --&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;You also get a script tag that you need to include in your site as well.&lt;/p&gt;
&lt;p&gt;Or you can directly embed with an iframe:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;html&quot;&gt;&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;container&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;iframe&lt;/span&gt;
    &lt;span class=&quot;token attr-name&quot;&gt;src&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;https://public.tableau.com/views/YOURDASHBOARDNAME/YOURDASHBOARDNAME?:showVizHome=no&amp;amp;:embed=true&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token attr-name&quot;&gt;width&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;1000&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token attr-name&quot;&gt;height&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;625&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;iframe&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;div class=&quot;gatsby-resp-iframe-wrapper&quot; style=&quot;padding-bottom: 62.5%; position: relative; height: 0; overflow: hidden; margin-bottom: 1rem&quot; &gt; &lt;div class=&quot;container&quot;&gt; &lt;iframe src=&quot;https://public.tableau.com/views/nfl-testing/nfl-testing?:showVizHome=no&amp;amp;:embed=true&quot; style=&quot; position: absolute; top: 0; left: 0; width: 100%; height: 100%; &quot;&gt;&lt;/iframe&gt; &lt;/div&gt; &lt;/div&gt;
&lt;h3 id=&quot;Power-BI&quot; style=&quot;position:relative;&quot;&gt;Power BI&lt;a href=&quot;#Power-BI&quot; aria-label=&quot;Power BI permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Power BI was the other option that I thought would be a good one to demo. It has a free tier, but it isn’t made as easily accessible.
You can sign up for an account here &lt;a href=&quot;https://powerbi.microsoft.com/en-us/get-started/&quot;&gt;https://powerbi.microsoft.com/en-us/get-started/&lt;/a&gt;, but you’ll need a corporate work email address (Gmail, Outlook etc won’t cut it).&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 615px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/b6ca83ff76b7a7eccd19c41f696f01eb/8ab79/embedded-bi-power-bi-sign-up.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 32.800000000000004%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAHCAYAAAAIy204AAAACXBIWXMAAA7DAAAOwwHHb6hkAAAAqklEQVQoz5WRuQ6DMBBE/f/fh4i4ChCWD8xpoJ1oFpFQRU7xNIWtN2uv2rYN+75fxCtjjJLHcXzPElFZlqHrOhRFgVeeo6xKVHUt0vM8RcpMgXdVCAHzPIM5jiOmiUxY1xXLsiRDB4dQ1loMwyB47+GcE4wxH3EKlGqtobxzIrphAWWEU1PKyX9x3+Egqm1bWGvQ9xpN00jTs/WfJ8tSaH228WOfpC7kXsobB44UFQ9jWA0AAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;embedded bi power bi sign up&quot;
        title=&quot;embedded bi power bi sign up&quot;
        src=&quot;/static/b6ca83ff76b7a7eccd19c41f696f01eb/8ab79/embedded-bi-power-bi-sign-up.png&quot;
        srcset=&quot;/static/b6ca83ff76b7a7eccd19c41f696f01eb/0eb09/embedded-bi-power-bi-sign-up.png 500w,
/static/b6ca83ff76b7a7eccd19c41f696f01eb/8ab79/embedded-bi-power-bi-sign-up.png 615w&quot;
        sizes=&quot;(max-width: 615px) 100vw, 615px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;You’ll also need to download the Power BI desktop application, I’d recommend getting this from the Microsoft Store (if you’re using a Windows computer).&lt;/p&gt;
&lt;p&gt;Use Power BI desktop application to develop a quick dashboard and publish it to the Power BI service (that is &lt;a href=&quot;https://powerbi.com&quot;&gt;https://powerbi.com&lt;/a&gt;).&lt;/p&gt;
&lt;p&gt;There are a few administration hoops to jump through, but once you do, you can get the embed code from your dashboard in the Power BI service by doing the following:&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 359px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/203296f57c35f37fa43ec3678f2709f7/16540/embedded-bi-power-bi-share.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 45.682451253481894%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAJCAIAAAC9o5sfAAAACXBIWXMAAAsSAAALEgHS3X78AAABIElEQVQoz42QjWqDMBCA8/6vto1uXbVOrMao+T1NYkxLrdphupXBWNnHwX3HcQd36OktftlGu81zHO/yLNUtDK53vf1PoNf3KN4n0XaTxFGepR0o19vemns8Gu6NMbqz1jrXj+M4T9McWJblfD5zzsUXv0UgxpgQApQCgGVZrj8Yx5FzrqRUoasCEOxWohakklxyClIITjlrlORGt1q3UrD8kBUYNxUhJWa0EZxVpGRNXRaFFAwRnBOcp0mcJnGWJh/7iNakA9kqsbZIWVNWV6TERVNXTbB1U5EzSpHW2hhz9N77wfvh6L21Zhic9/50OgKADqyfWdMqdxDGmFKqAET4Tdd1t8OM0fM8Xy6X698g55y1tv8muL1hjJmm6cHwJ01d9Y1dqgoBAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;embedded bi power bi share&quot;
        title=&quot;embedded bi power bi share&quot;
        src=&quot;/static/203296f57c35f37fa43ec3678f2709f7/16540/embedded-bi-power-bi-share.png&quot;
        srcset=&quot;/static/203296f57c35f37fa43ec3678f2709f7/16540/embedded-bi-power-bi-share.png 359w&quot;
        sizes=&quot;(max-width: 359px) 100vw, 359px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;It may look something like this:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;html&quot;&gt;&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;&amp;lt;!-- Start Power BI Embed --&gt;&lt;/span&gt;
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;section&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;brands-area&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;container&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;iframe&lt;/span&gt;
      &lt;span class=&quot;token attr-name&quot;&gt;width&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;1000&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
      &lt;span class=&quot;token attr-name&quot;&gt;height&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;625&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
      &lt;span class=&quot;token attr-name&quot;&gt;src&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;https://app.powerbi.com/view?r=YOURAPPID&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
      &lt;span class=&quot;token attr-name&quot;&gt;frameborder&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;0&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
      &lt;span class=&quot;token attr-name&quot;&gt;allowfullscreen&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;true&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;iframe&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;section&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;&amp;lt;!-- End Power BI Embed--&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;div class=&quot;gatsby-resp-iframe-wrapper&quot; style=&quot;padding-bottom: 62.5%; position: relative; height: 0; overflow: hidden; margin-bottom: 1rem&quot; &gt; &lt;div class=&quot;container&quot;&gt; &lt;iframe src=&quot;https://app.powerbi.com/view?r=eyJrIjoiYjY0ZThhN2QtMzlkMi00YWZkLTg0M2EtZjdlNmM0NzJiMzFjIiwidCI6ImQ2M2U3OGJkLTllZTMtNGE3ZC1hODY4LTczODgyNGMxM2JkMSJ9&quot; frameborder=&quot;0&quot; allowfullscreen=&quot;true&quot; style=&quot; position: absolute; top: 0; left: 0; width: 100%; height: 100%; &quot;&gt; &lt;/iframe&gt; &lt;/div&gt; &lt;/div&gt;
&lt;h2 id=&quot;Other-products-that-get-a-mention&quot; style=&quot;position:relative;&quot;&gt;Other products that get a mention&lt;a href=&quot;#Other-products-that-get-a-mention&quot; aria-label=&quot;Other products that get a mention permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Yellowfin&lt;/li&gt;
&lt;li&gt;Qlik Sense&lt;/li&gt;
&lt;li&gt;SAS Visual Analytics&lt;/li&gt;
&lt;li&gt;Looker&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I should also mention, that there are also a lot of open source and paid javascript charting and graphing frameworks that could be used to achieve the same thing.&lt;/p&gt;
&lt;p&gt;Wrapping it up, it’s never been a better time to embed charts, dashboards and/or analytics into your business application or site.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Speed Up My Site]]></title><description><![CDATA[Originally I built my personal site by using a free responsive template and then I just modified the source code. The page loads were slow…]]></description><link>https://mortimer.xyz/speed-up-my-site/</link><guid isPermaLink="false">https://mortimer.xyz/speed-up-my-site/</guid><pubDate>Sun, 20 Sep 2020 12:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Originally I built my personal site by using a free responsive template and then I just modified the source code. The page loads were slow and I didn’t really learn much in the process.&lt;/p&gt;
&lt;p&gt;I got interested in static site generators as they seemed to meet what I needed for most of my use cases. I’d heard about GatsbyJS from SyntaxFM and then watched a great conference talk from Kyle Mathews (&lt;a href=&quot;https://www.youtube.com/watch?v=MZLS4cq7fRY&amp;#x26;ab_channel=CodingTech&quot;&gt;Talk&lt;/a&gt;) about the framework. So I decided to give it a go.&lt;/p&gt;
&lt;h3 id=&quot;Template&quot; style=&quot;position:relative;&quot;&gt;Template&lt;a href=&quot;#Template&quot; aria-label=&quot;Template permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;The starter templates were a great place to start. I chose the &lt;a href=&quot;https://github.com/greglobinski/gatsby-starter-hero-blog&quot;&gt;Hero Blog&lt;/a&gt; by &lt;a href=&quot;https://github.com/greglobinski/&quot;&gt;Greg Lobinski&lt;/a&gt; and started modifying it.&lt;/p&gt;
&lt;h3 id=&quot;Quick-and-Fun&quot; style=&quot;position:relative;&quot;&gt;Quick and Fun&lt;a href=&quot;#Quick-and-Fun&quot; aria-label=&quot;Quick and Fun permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;I found the experience great and learnt a lot while having heaps of fun. The final site is much faster. I recommend it.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Linux Users Groups]]></title><description><![CDATA[This post is in reference to the associated Github repo at https://github.com/mortie23/linux-users-groups. Deploy a security model (users…]]></description><link>https://mortimer.xyz/linux-users-groups/</link><guid isPermaLink="false">https://mortimer.xyz/linux-users-groups/</guid><pubDate>Wed, 02 Sep 2020 12:00:00 GMT</pubDate><content:encoded>&lt;p&gt;This post is in reference to the associated Github repo at &lt;a href=&quot;https://github.com/mortie23/linux-users-groups&quot;&gt;https://github.com/mortie23/linux-users-groups&lt;/a&gt;.&lt;/p&gt;
&lt;h1 id=&quot;Deploy-a-security-model-users-groups-and-shared-directory-structure-for-Linix-system-for-enterprise-data&quot; style=&quot;position:relative;&quot;&gt;Deploy a security model (users, groups and shared directory structure) for Linix system for enterprise data&lt;a href=&quot;#Deploy-a-security-model-users-groups-and-shared-directory-structure-for-Linix-system-for-enterprise-data&quot; aria-label=&quot;Deploy a security model users groups and shared directory structure for Linix system for enterprise data permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h1&gt;
&lt;p&gt;This package was designed to run on Ubuntu 20.04.
This package has some dependencies including ACLs and JQ which will be outlined in the &lt;code class=&quot;language-text&quot;&gt;setup.sh&lt;/code&gt;.&lt;/p&gt;
&lt;h2 id=&quot;Description&quot; style=&quot;position:relative;&quot;&gt;Description&lt;a href=&quot;#Description&quot; aria-label=&quot;Description permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;This code repository will create everything required to implement the security model from start to finish.&lt;/p&gt;
&lt;p&gt;The structure of the security model is based on a two level organisation structure. The organisation has agencies which comprise of business units. This is represented by:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Agency NFL
&lt;ul&gt;
&lt;li&gt;Business unit 49ers&lt;/li&gt;
&lt;li&gt;Business unit patriots&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Agency NBA
&lt;ul&gt;
&lt;li&gt;Business unit Bulls&lt;/li&gt;
&lt;li&gt;Business unit Warriors&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 667px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/affcff86a253a40c45ee6d4d49b5814a/e31cc/organisation-structure.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 134%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAbCAYAAAB836/YAAAACXBIWXMAAAsTAAALEwEAmpwYAAAFkUlEQVRIx42TW4gbVRiAT7MoiCL0QcELKKK265NWwV0p3QptI31wC7aiT0Jtq+AFxBcR7dZiFUQE8VopItZGKV7xVty223Zz2clMJnPNTC4zySYzk+yue5trkp09v8wk2ehuHxz4+c8Mh+98//z/QR+qFXT4tnvQa5XZO7d89VlkouDeEC97w5OKNXQ+bw1PFJaGLkozw5cKS0PjeWv4UtEaGpfN9Xloomg9cpZe3IzQQ/vQk8+/8vRL4wn/iKQOS6r34AWhDsnSIrC6C6zeAqYOwBrt8J0Lv23MQUwq9ijKUKm7X3r19ZVnYz/Doen69koDHiDkGpz79tPVdK6GZaOBi6VLWND+xrzRxLzhYt7wMK/bnei8+wH4cskaRSdPf3fjgScOPL736LtPoOh91/IObE+QNPx48sQqVXFxscLg9uWbsS68j5kGYF63MK+7mK0DZuqAuRDuhsC4Yj+Orrtl8Pob7h2+detd225HCCG6vrqDqpiQLMyvckYLC/oScOIEiJUScEYbeN0Jc0WOgVr4PXgPoB3DojWKPv7ya/TC4UORzwn5mmxtBU0q9k5Gc4HX3VVOD8ppAlXzgdVXQhiv2cDWAeYzB8FODYOgzWHOaPcNBx/ehbY+vAulq14krpgoqdojIdDwVjk9LAcEwwkOAK7RAm7OB2GmDal8A+I5HVjN6f/DwHBwaBcaHNqN4oo1MKnYfcMQ6IZATnOAW8DAfz8B/MvHgSs7oHHHocacAF4zMWc0+4a9J1W2I0nVXmfo4mBjz45P5YF/7xSwig11/ijME/tB0OYxZ7T6hr0nodpXNzS8cNbERgsShTkY56qQm12BZH4OLssu8PV2cKjP6d5Gw4RqB+Ce4Qqnu0H4rOb40uyK/8sVzn/nk9N+uuL5i8whf4l60he0RZ8zWq3uHP4LqNoDCcVCCcXaxWpBI9aHBym5AT9N0JCtNaEifgXV7HEQtSUQjCYI4T+09q8Bz6vepnHFQ38p3mCy1jqb1ltnUtVmbKrWjKVqXpjP8Y3Y74wei6tm7GeuGTtLt2LxihsjtOY3yWrzh3Mlb9sacDlFb7JSNLJS9B1GnHxrbio7ZtLCmEnnxpYz/DGT5sbsbO6YzUpjZkY85rL8mMewx8ysOLacEcaWafHthYvJLX0gyQ7YJIccits9nSShQtBg54rgSkVw5GlwZQ2cYC2VwJVKYe6te2Gm2QNrQDPDR9oZEbUy4g5ligQieaVVIcV2Sxxve8kdbZc51XbyetuRim1HVjq5t5aVZgA3p7L9ppgkN+BlRORlxJ0zGRbk+JXVBb6IvRwBZuIpcLInwc0ba5aOrHdDwa5c8p2OYR9oZYSISwvIzYojJp+HKpFddaQSduUyNGgFlsUyuHK3TLkMLnsGHP4P6OxR/K5hf7AtiouYaRaZaWanIxahWVBDYLDRk0trMCdXAEfWoEnsh2bifnBzPHbksh+YrzPkIxbFIYvkRgKgIytdw34DvNI0eOUaePkyzFAEGMSVoHF9Q+I/hnzEJFlkkuxORyyA2wV2rIrgqVWYO/sbVE98BI6owhL5DiwQH4AjFbArq10gM/ofQ5PkAmBouAFYrkH9ixiUXnwDLE4FjzoCXvJRcKVeyf8T6MpKWG4zr8IszcN0koJ2qQyNjAgaKQflB4deBdgveWRdydiSShhKCv76zyI++GUBz4tV3CKfw276MHYlCTu9ktPMVZpCcSNBiQGwG9iWFIyLCv71fB6/9V0OL+aq2KHexGbqGexIYjBaKxuaEgy2STDIJJhHHaHQHZH+9Qqu4QxJwyyZgWVeAoMqQJUogCkUOlcxF4wNs69/lxNULzYvJzOPmWl2j5mio2aa2WNOZaNWmok6lBC1KDFqpdlok6KibZqKWkS2sydF7128kLjpH4nsUBhlGSIHAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;organisation structure&quot;
        title=&quot;organisation structure&quot;
        src=&quot;/static/affcff86a253a40c45ee6d4d49b5814a/e31cc/organisation-structure.png&quot;
        srcset=&quot;/static/affcff86a253a40c45ee6d4d49b5814a/0eb09/organisation-structure.png 500w,
/static/affcff86a253a40c45ee6d4d49b5814a/e31cc/organisation-structure.png 667w&quot;
        sizes=&quot;(max-width: 667px) 100vw, 667px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;Each agency has a shared directory that each business unit can read and write to for files that are shared between business units. Then each business unit has a directory that only they can read and write from.&lt;/p&gt;
&lt;h2 id=&quot;Getting-Setup&quot; style=&quot;position:relative;&quot;&gt;Getting Setup&lt;a href=&quot;#Getting-Setup&quot; aria-label=&quot;Getting Setup permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Firstly clone the repository to your Linux machine.&lt;/p&gt;
&lt;p&gt;Follow the manual steps in &lt;code class=&quot;language-text&quot;&gt;setup.sh&lt;/code&gt;.
The administrator account and group is &lt;code class=&quot;language-text&quot;&gt;nixadmin&lt;/code&gt;. If you choose to change this you will need to update the ACL files that use this user and group as well.&lt;/p&gt;
&lt;p&gt;The example has the root directory for setting up the shared directory structure in &lt;code class=&quot;language-text&quot;&gt;/u01/data&lt;/code&gt;. This is a parameter that can be changed before deployment by editing &lt;code class=&quot;language-text&quot;&gt;param-files/global.json&lt;/code&gt;.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;json&quot;&gt;&lt;pre class=&quot;language-json&quot;&gt;&lt;code class=&quot;language-json&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;&quot;dataRoot&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;/u01/data&quot;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Once you have run the manual steps in &lt;code class=&quot;language-text&quot;&gt;setup.sh&lt;/code&gt; you need to make the following scrips executable.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;sh&quot;&gt;&lt;pre class=&quot;language-sh&quot;&gt;&lt;code class=&quot;language-sh&quot;&gt;&lt;span class=&quot;token function&quot;&gt;sudo&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;chmod&lt;/span&gt; +x users-groups.sh
&lt;span class=&quot;token function&quot;&gt;sudo&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;chmod&lt;/span&gt; +x dir-agency.sh
&lt;span class=&quot;token function&quot;&gt;sudo&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;chmod&lt;/span&gt; +x dir-businessunit.sh&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Firstly run this script to create the users and groups. Modify this to suit your organisation, and also update the corresponding ACL files. If you have users and groups from an identity provider such as Active Directory then you will skip this step.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;## Create the users and groups&lt;/span&gt;
&lt;span class=&quot;token function&quot;&gt;sudo&lt;/span&gt; ./users-groups.sh&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2 id=&quot;Usage&quot; style=&quot;position:relative;&quot;&gt;Usage&lt;a href=&quot;#Usage&quot; aria-label=&quot;Usage permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Run order of the scripts there after is following:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;## Only run this command once for each agency (example agency NFL)&lt;/span&gt;
&lt;span class=&quot;token function&quot;&gt;sudo&lt;/span&gt; ./dir-agency.sh &lt;span class=&quot;token parameter variable&quot;&gt;-p&lt;/span&gt; ./param-files/global.json &lt;span class=&quot;token parameter variable&quot;&gt;-l&lt;/span&gt; a &lt;span class=&quot;token parameter variable&quot;&gt;-a&lt;/span&gt; nfl
&lt;span class=&quot;token comment&quot;&gt;## Only run this command once for each business unit (example business unit Patriots)&lt;/span&gt;
&lt;span class=&quot;token function&quot;&gt;sudo&lt;/span&gt; ./dir-businessunit.sh &lt;span class=&quot;token parameter variable&quot;&gt;-p&lt;/span&gt; ./param-files/global.json &lt;span class=&quot;token parameter variable&quot;&gt;-l&lt;/span&gt; b &lt;span class=&quot;token parameter variable&quot;&gt;-a&lt;/span&gt; nfl &lt;span class=&quot;token parameter variable&quot;&gt;-b&lt;/span&gt; patriots&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2 id=&quot;Success&quot; style=&quot;position:relative;&quot;&gt;Success&lt;a href=&quot;#Success&quot; aria-label=&quot;Success permalink&quot; class=&quot;custom-class after&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;20&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;20&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The directories have been deployed as per the security model:&lt;br&gt;
&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 509px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/5da1d079e4c9e16d4ff1cc6226053111/98235/ubuntu-deployed.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 61.199999999999996%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAMCAIAAADtbgqsAAAACXBIWXMAAAsSAAALEgHS3X78AAACYElEQVQoz22NS08aUQCF539UUxcUAvUxyFOMaZNWm/hf2qZbKWl9FGSMklYQBgq2rrtoumm7adJNQxQXRpiRKCowMDhPnMe9dyjOtIa4aOrJtzjnJCcH8026g27PnDP4ZHJmdiL0CA/NBx4+Hg/O+x7M4tNzrinf2ITXjQe83lAgEPT5/B7PlN/vcbu9OI5l0pnkZnJrY3M7lfuYzn/IFAqp3Nv15HYmv0PmC5lcliSzJJnLZt/nrn06lUpsrKe30lmSxBRV5wSp3bnoqhowesD43ZUl9uDXpSxoEOoQQoQgQgAhABGAUNN1XhB1AJHRw5pM++y8Xjs9bzRaHC9ygsw16+qnZeagWK3V221WECVeEG8F4wWJ7XBMi5W7iqLpiga7kqAUv8hso6sBVdNvADfoiqoNSuykdrZX2t8t7VPlI6pM0/RxZW+3tvWi9O3zXumgSlUrNE3RRxWKrlBUuUJVKPrwsEzRRxRNY8+ePh8avhOwjQdHxkN2Nz7smHZ5vffwB6OBGafHP+QaG7KPjNx1OhyjLtfY/VG7zeZ0OBx2u91mw/L5QjgcfrUQiYWXEssE8TqaWCGWFhbji/F3q+uJFYJ4E4tGo0Q8vkYQa2vEaiwWi0YjL8ORSARjWm1k9C4EQVYU07JM07IsS27UNK517Qb5PyEEDcPAjk9O2Q7XaDLNZktRVFXVtJ7V+7kDvqcZThR43jTNq381aEzTxE7P6pLcbTQZjhd0ADUdgF4fVYuoWjT6loHQrd+D/fVYlGSm1RYlGUCkaTq4stCPgvo1yUqX3EXn6rZ1v9//+/wHPD0WbzcYvDcAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;ubuntu deployed&quot;
        title=&quot;ubuntu deployed&quot;
        src=&quot;/static/5da1d079e4c9e16d4ff1cc6226053111/98235/ubuntu-deployed.png&quot;
        srcset=&quot;/static/5da1d079e4c9e16d4ff1cc6226053111/0eb09/ubuntu-deployed.png 500w,
/static/5da1d079e4c9e16d4ff1cc6226053111/98235/ubuntu-deployed.png 509w&quot;
        sizes=&quot;(max-width: 509px) 100vw, 509px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;The example user MichaelJordan in &lt;code class=&quot;language-text&quot;&gt;{agency: nba, businessunit: bulls}&lt;/code&gt; can change into directories allowed and write files where allowed.&lt;br&gt;
&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 610px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/b477a7774f9e00bb848945e73183f01a/5d5ad/linux-users-groups.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 71.8%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAOCAIAAACgpqunAAAACXBIWXMAAAsSAAALEgHS3X78AAACh0lEQVQoz1WRV2/bMBSF/ZTRJM1AXA9tyVrUoiiKkkiJ1LDleLVB2rRv/f8/o0jRpihwX7+D75w7GYKs82CleNlSx7JRSG4+RekUFUtCpzH+6KFrJ72y4ZWNbtz02oYfVu83OXj91mw6idBH2CvVoJb0MRxNKpZpeKYPerX3Ra+Rep5u7YbNkuzGSS6tP/BrcXyGo5Bxq5BnuP2G9xuL/mxfj0E/GMVXvP9enI5Bx2X8oziIVUcUiq7+wn2Qdn5SaR73wjaItjFpLd6DcROMB9Cdov4UDntX9FrBF2n+mKD7KH3Xbj1IDa91YevF3AupCmqNViorJdLr5c57w4SM12ZF7kB8psML81/nEWdtFHIQtknQoXCLcO9vuLt5QqdxxTqNoGsnOjOic+N31f8HY75DPauybRbaLLKY45Y6z42u1AW3xB50bxF6ubEoX6Lk0npb/h3u/JSZoHX+aDMtaDTG9JosSKWyrcvXRlkv0l4v6TSKz434wvy39gmLPWS1GpKlw3QgTFjOs0anVC6pVOT3QXJhshksH8P8DpCHgM0gvvX+wuHm4Pd8gTu5eLLF9/y4XdGvePeSjmKJyH2Ab/1XcvwcD71WrE36nGzKGcJ3AF5ak73f7VzBpkkzz0arfkmf1kb5Ld+domHnNING2DQ6Resnp2mVnEvZEXREppnC84dw0iCHZ3YFDJbpvDS71C/NnoJj6e0Y2DeWaIzmJT9+jvp6nnIp41KWfvThbYBu3Al2pdyVc1sloUziZW4qSK5jWSQST2QOlRap3cYVo1UdQP8lHtgMJucGvLTetHNHKzw1t7QCqEWkFCszkyhSBVI4UjiUeaGwVsHsU1Iv0noOyQN4f/gvHsaZKFkWiRUAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;linux users groups&quot;
        title=&quot;linux users groups&quot;
        src=&quot;/static/b477a7774f9e00bb848945e73183f01a/5d5ad/linux-users-groups.png&quot;
        srcset=&quot;/static/b477a7774f9e00bb848945e73183f01a/0eb09/linux-users-groups.png 500w,
/static/b477a7774f9e00bb848945e73183f01a/5d5ad/linux-users-groups.png 610w&quot;
        sizes=&quot;(max-width: 610px) 100vw, 610px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;</content:encoded></item></channel></rss>