<?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[Blog: Ben Hammond Music . Tech]]></title><description><![CDATA[Denver Developer &amp;&amp; Song Builder]]></description><link>https://blog.benhammondmusic.tech</link><generator>RSS for Node</generator><lastBuildDate>Wed, 15 Apr 2026 16:06:26 GMT</lastBuildDate><atom:link href="https://blog.benhammondmusic.tech/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[Fixing VSCode Ghost Text 👻]]></title><description><![CDATA[Uninstall (don’t just disable) Codeium from your VSCode extensions if Copilot inline suggestions aren’t working

As an employee of Morehouse School of Medicine in a research-related position, I qualify for GitHub Education, which in turn gave me free...]]></description><link>https://blog.benhammondmusic.tech/switching-back-from-codeium-to-github-copilot</link><guid isPermaLink="true">https://blog.benhammondmusic.tech/switching-back-from-codeium-to-github-copilot</guid><category><![CDATA[AI]]></category><category><![CDATA[vscode extensions]]></category><category><![CDATA[GitHub]]></category><category><![CDATA[codeium]]></category><dc:creator><![CDATA[Ben Hammond]]></dc:creator><pubDate>Tue, 24 Dec 2024 20:46:35 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1735101655906/31d933ba-2e1e-4455-9986-2aeb54d8f0db.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<blockquote>
<p>Uninstall (don’t just disable) Codeium from your VSCode extensions if Copilot inline suggestions aren’t working</p>
</blockquote>
<p>As an employee of Morehouse School of Medicine in a <a target="_blank" href="https://healthequitytracker.org">research-related position</a>, I qualify for <a target="_blank" href="https://github.com/education">GitHub Education</a>, which in turn gave me free access to GitHub Copilot. I happily used this for a month or so, but was intrigued by <a target="_blank" href="https://codeium.com">Codeium</a>, another VSCode extension that assisted with AI generated, context aware inline suggestions. Though I appreciated the free access, Codeium offered a free tier for all, and I like the idea of marketplace competition, so I started using that regularly instead of Copilot. Fast forward to this month, when GitHub announced its early Christmas present of a free tier for everyone on Copilot!</p>
<p>Deciding it had been enough time (and also on the heels of GitHub Universe where they announced a ton of new features trying to keep up with <a target="_blank" href="https://Cursor.com">Cursor</a>), I reinstalled Copilot and disabled Codeium, excited to see how it had improved. Disappointingly, the inline autocomplete was not working. I tweaked settings, refreshed, reinstalled, and searched Stack Overflow. I even ironically asked the in-editor chat window why it wasn't working, but never was able to isolate the issue or find the magic setting that was breaking it.</p>
<p>Finally, I decided to fully uninstall Codeium (whereas I had just disabled it previously) and voilà! Auto-suggestions are working! I'll report back after more use, but my initial impression is that Codeium's are more helpful. The next step is probably trying one of the AI-focused VSCode forks: Cursor or <a target="_blank" href="https://codeium.com/windsurf">Windsurf</a> which is Codeium's integrated editor.</p>
<p>Let me know in the comments what that magical setting was that was fixed when Codeium was uninstalled. And if you have opinions on Windsurf vs. Cursor let me know!​​​​​​​​​​​​​​​​</p>
]]></content:encoded></item><item><title><![CDATA[Making E2E Tests Harder, Better, Faster, and Stronger]]></title><description><![CDATA[💡
In short: use Playwright for end-to-end testing of your web app; don’t sleep on its built-in code generation and reporting features; aim to target stable, deployed URLs when possible.


What is Playwright?
Playwright is an exceptional package that...]]></description><link>https://blog.benhammondmusic.tech/making-e2e-tests-harder-better-faster-and-stronger</link><guid isPermaLink="true">https://blog.benhammondmusic.tech/making-e2e-tests-harder-better-faster-and-stronger</guid><category><![CDATA[playwright]]></category><category><![CDATA[E2E]]></category><category><![CDATA[Testing]]></category><category><![CDATA[github-actions]]></category><dc:creator><![CDATA[Ben Hammond]]></dc:creator><pubDate>Sat, 09 Dec 2023 06:04:13 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1702094754120/123b1617-c310-4f7e-ba6a-9720a23ba137.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<div data-node-type="callout">
<div data-node-type="callout-emoji">💡</div>
<div data-node-type="callout-text">In short: use Playwright for end-to-end testing of your web app; don’t sleep on its built-in code generation and reporting features; aim to target stable, deployed URLs when possible.</div>
</div>

<h2 id="heading-what-is-playwright">What is Playwright?</h2>
<p><a target="_blank" href="https://playwright.dev/">Playwright</a> is an exceptional package that facilitates automated end-to-end (E2E) testing for your web application. Its standout feature lies in its ability to utilize real browser engines, navigating and interacting with your web app programmatically to explore every corner of your user interface. This approach helps uncover bugs and regressions before they reach your users. If you've worked with Selenium or Puppeteer before, Playwright is similar but excels in allowing usage across multiple browser engines, such as Chromium, WebKit, and more.</p>
<p>Like any good testing tool, it can (and should) be available locally as you code, as well as on CI (continuous integration) to ensure code quality from the entire team, automatically.</p>
<h2 id="heading-what-was-wrong-with-the-current-setup">What was wrong with the current setup?</h2>
<p>When I started as a software engineer on the <a target="_blank" href="http://healthequitytracker.org">Health Equity Tracker</a>, there were no frontend-specific E2E tests in place. All QA was done manually by the developers (and helpful department members on big releases), and as you can imagine on a complex dashboard-style app, edge-case bugs often snuck through the review process. I took it as a challenge to a) identify the up-and-coming industry standard for frontend e2e testing and b) implement a testing suite basically on my own.</p>
<p>Overall I am happy to say I succeeded, and I was able to initially add coverage to the most basic parts of the application. However, with the improvement of both my development skills and Playwright as a tool, we have been able to make significant improvements to our E2E testing setup, making the tests harder (less fragile and flaky), better (improved developer experience with increased visibility and maintainability), faster (the tests were taking quite a long time on CI to run and we were able to reduce that time significantly), and stronger (more comprehensive and representative of the actual user experience).</p>
<h2 id="heading-harder"><code>HARDER</code></h2>
<blockquote>
<p>Making the tests less fragile and more resilient</p>
</blockquote>
<p>My initial setup used Playwright to spin up a Vite development server (<code>npm run dev</code>) regardless of the environment, and then ran the suite of tests against that dev server. This could happen locally or on CI using GitHub Actions. However, running tests against a quick development build (versus a real production build) can be unreliable for a testing situation.</p>
<p>To make our testing setup less fragile, we targeted our deployed, production builds instead of the temporary dev servers. To accomplish this, we needed to dynamically swap the <code>baseUrl</code> that Playwright relies upon, and ensure that all of our tests were using relative URLs. We set these <code>baseUrl</code> strings using either environmental variables or command line arguments, depending on the environment under test. This allows us to run things in the following ways:</p>
<div class="hn-table">
<table>
<thead>
<tr>
<td>INITIATING ENVIRONMENT</td><td>SITE UNDER TEST</td></tr>
</thead>
<tbody>
<tr>
<td>The developer manually runs the tests locally</td><td>Spin up <code>npm run dev</code> and then run tests against <code>http://localhost:3000</code></td></tr>
<tr>
<td>CI runs the tests every time a PR is made or updated</td><td>Netlify-generated URL specific to each GitHub pull request, like <a target="_blank" href="https://deploy-preview-2648--health-equity-tracker.netlify.app/">https://deploy-preview-2648--health-equity-tracker.netlify.app/</a> where 2648 is the number of the PR</td></tr>
<tr>
<td>CI runs the tests every time a PR is merged into <code>main</code> and a new version of the frontend is deployed to staging</td><td><code>https://dev.healthequitytracker.org</code> which is our "dev" site, a staging pre-production preview that should perfectly mirror what will be released to production</td></tr>
<tr>
<td>CI chron job runs nightly against the real production site</td><td><code>https://healthequitytracker.org</code></td></tr>
</tbody>
</table>
</div><p>The logic for this dynamic <code>baseUrl</code> was straightforward for prod, staging, and local, as each has a consistent URL to test against and its own <code>.env</code> file to store the URL string. However, our CI also uses a unique test URL for every PR opened against <code>main</code>. This enables fully deployed feature testing (e.g., on a real mobile device, or to run ideas by non-technical stakeholders).</p>
<p>Passing this unique deploy preview URL into our test via GitHub Actions required some research and manual trial and error, but we got it working fairly quickly. Some hiccups still need fine-tuning, but overall, I'm pleased to implement this idea that's been in the back of my head for over a year. To set the <code>baseUrl</code> per environment, I added the following to my config:</p>
<pre><code class="lang-typescript">use: {
    baseURL: process.env.PLAYWRIGHT_BASE_URL ?? <span class="hljs-string">'http://localhost:3000'</span>,
    <span class="hljs-comment">/* rest of the config stuff... */</span>
}
</code></pre>
<p>And set up my <code>.env</code> files like this:</p>
<pre><code class="lang-plaintext"># .env.production
PLAYWRIGHT_BASE_URL=https://healthequitytracker.org
</code></pre>
<pre><code class="lang-plaintext"># .env.staging
PLAYWRIGHT_BASE_URL=https://dev.healthequitytracker.org
</code></pre>
<p>This allows the specified URLs as needed, falling back to localhost when run locally or if <code>PLAYWRIGHT_BASE_URL</code> isn't set. For the deploy_preview, I used the following GitHub Actions file, with a few tricky bits borrowed from <a target="_blank" href="https://www.joranquinten.nl/tutorials/running-e2e-test-suite-on-a-netlify-preview-url/">a post by Joran Quinten</a>:</p>
<pre><code class="lang-yaml"><span class="hljs-comment"># frontend.yaml</span>
<span class="hljs-comment"># For more information see: https://help.github.com/actions/language-and-framework-guides/using-nodejs-with-github-actions</span>

<span class="hljs-attr">name:</span> <span class="hljs-string">Frontend</span> <span class="hljs-string">CI</span>

<span class="hljs-attr">env:</span>
  <span class="hljs-attr">NETLIFY_SITE_NAME:</span> <span class="hljs-string">'health-equity-tracker'</span>
  <span class="hljs-attr">GITHUB_PR_NUMBER:</span> <span class="hljs-string">${{github.event.pull_request.number}}</span>

<span class="hljs-comment"># run on any pushes to main (including merging PRs),</span>
<span class="hljs-comment"># and any PR creations or updates against main,</span>
<span class="hljs-comment"># as long as they include changes to files in /frontend</span>
<span class="hljs-attr">on:</span>
  <span class="hljs-attr">push:</span>
    <span class="hljs-attr">branches:</span> [<span class="hljs-string">main</span>]
  <span class="hljs-attr">pull_request:</span>
    <span class="hljs-attr">branches:</span> [<span class="hljs-string">main</span>]
    <span class="hljs-attr">paths:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">'frontend/**'</span> <span class="hljs-comment"># Run when changes occur in the frontend subfolder</span>
<span class="hljs-attr">defaults:</span>
  <span class="hljs-attr">run:</span>
    <span class="hljs-attr">working-directory:</span> <span class="hljs-string">frontend</span>

<span class="hljs-attr">jobs:</span>
  <span class="hljs-attr">tests_e2e_netlify_prepare:</span>
    <span class="hljs-comment"># third-party action that waits until Netlify deploy URL is ready</span>
    <span class="hljs-attr">name:</span> <span class="hljs-string">Wait</span> <span class="hljs-string">for</span> <span class="hljs-string">Netlify</span> <span class="hljs-string">deploy</span>
    <span class="hljs-attr">runs-on:</span> <span class="hljs-string">ubuntu-latest</span>
    <span class="hljs-attr">if:</span> <span class="hljs-string">github.event_name</span> <span class="hljs-string">==</span> <span class="hljs-string">'pull_request'</span>
    <span class="hljs-attr">steps:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Waiting</span> <span class="hljs-string">for</span> <span class="hljs-string">Netlify</span> <span class="hljs-string">Preview</span>
        <span class="hljs-attr">uses:</span> <span class="hljs-string">josephduffy/wait-for-netlify-action@v1</span>
        <span class="hljs-attr">id:</span> <span class="hljs-string">wait-for-netflify-preview</span>
        <span class="hljs-attr">with:</span>
          <span class="hljs-attr">site_name:</span> <span class="hljs-string">${{env.NETLIFY_SITE_NAME}}</span>
          <span class="hljs-attr">max_timeout:</span> <span class="hljs-number">60</span>

  <span class="hljs-attr">tests_e2e_netlify:</span>
    <span class="hljs-comment"># make sure the above job completes first</span>
    <span class="hljs-attr">needs:</span> <span class="hljs-string">tests_e2e_netlify_prepare</span>
    <span class="hljs-attr">name:</span> <span class="hljs-string">Run</span> <span class="hljs-string">E2E</span> <span class="hljs-string">tests</span> <span class="hljs-string">on</span> <span class="hljs-string">deploy</span> <span class="hljs-string">preview</span>
    <span class="hljs-attr">runs-on:</span> <span class="hljs-string">ubuntu-latest</span>
    <span class="hljs-attr">if:</span> <span class="hljs-string">github.event_name</span> <span class="hljs-string">==</span> <span class="hljs-string">'pull_request'</span>
    <span class="hljs-attr">timeout-minutes:</span> <span class="hljs-number">5</span>
    <span class="hljs-attr">steps:</span>
      <span class="hljs-comment"># install node, modules, and playwright</span>
      <span class="hljs-bullet">-</span> <span class="hljs-attr">uses:</span> <span class="hljs-string">actions/checkout@v3</span>
      <span class="hljs-bullet">-</span> <span class="hljs-attr">uses:</span> <span class="hljs-string">actions/setup-node@v3</span>
        <span class="hljs-attr">with:</span>
          <span class="hljs-attr">node-version:</span> <span class="hljs-number">19</span>
          <span class="hljs-attr">cache:</span> <span class="hljs-string">'npm'</span>
          <span class="hljs-attr">cache-dependency-path:</span> <span class="hljs-string">frontend/package-lock.json</span>
      <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Install</span> <span class="hljs-string">dependencies</span>
        <span class="hljs-attr">run:</span> <span class="hljs-string">npm</span> <span class="hljs-string">ci</span>
      <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Install</span> <span class="hljs-string">playwright</span> <span class="hljs-string">deps</span>
        <span class="hljs-attr">run:</span> <span class="hljs-string">npx</span> <span class="hljs-string">playwright</span> <span class="hljs-string">install</span> <span class="hljs-string">--with-deps</span> <span class="hljs-string">chromium</span>
      <span class="hljs-comment"># run the tests!</span>
      <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">run</span> <span class="hljs-string">E2E_DEPLOY_PREVIEW</span>
        <span class="hljs-attr">run:</span> <span class="hljs-string">npx</span> <span class="hljs-string">playwright</span> <span class="hljs-string">test</span> <span class="hljs-string">--project=E2E_DEPLOY_PREVIEW</span>
        <span class="hljs-comment"># base url based on the GITHUB_PR_NUMBER + NETLIFY_SITE_NAME</span>
        <span class="hljs-attr">env:</span>
          <span class="hljs-attr">PLAYWRIGHT_BASE_URL:</span> <span class="hljs-string">'https://deploy-preview-$<span class="hljs-template-variable">{{env.GITHUB_PR_NUMBER}}</span>--$<span class="hljs-template-variable">{{env.NETLIFY_SITE_NAME}}</span>.netlify.app'</span>
      <span class="hljs-comment"># store test run reports if they fail</span>
      <span class="hljs-bullet">-</span> <span class="hljs-attr">uses:</span> <span class="hljs-string">actions/upload-artifact@v3</span>
        <span class="hljs-attr">if:</span> <span class="hljs-string">${{</span> <span class="hljs-string">failure()</span> <span class="hljs-string">}}</span>
        <span class="hljs-attr">with:</span>
          <span class="hljs-attr">name:</span> <span class="hljs-string">playwright-report</span>
          <span class="hljs-attr">path:</span> <span class="hljs-string">frontend/playwright-report/</span>
          <span class="hljs-attr">retention-days:</span> <span class="hljs-number">30</span>
</code></pre>
<h2 id="heading-better"><code>BETTER</code></h2>
<blockquote>
<p>Increasing reporting visibility to improve developer experience and velocity</p>
</blockquote>
<p>Initial configuration attempts inadvertently failed to pass the required environmental variables. As a result, the React site would load, but the fetches from our data API would fail. This caused endless issues; running the tests locally would pass fine, but running on CI would fail. Lack of visibility into the Playwright reporting on CI made it impossible to debug these failures.</p>
<p>Finally, I properly implemented the reporting feature on CI, and then, importantly, set it to upload the generated reports as artifacts (final step of the GitHub Actions file above). Playwright includes great video replay, screenshot, and stack trace visualizations that allow you to rewatch what was happening when your tests fail. This insight into the CI failures made it immediately where I had been calling the wrong npm script. Going forward, this increased visibility and improved testing structure should allow our team to write tests more easily, have more confidence in their results, and quickly update them when things go wrong.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1702099118926/becf4765-32f7-41ac-842e-d8b2aae1e4df.gif" alt="actual video of playwright's video capture showing site working but failing to load data" class="image--center mx-auto" /></p>
<h2 id="heading-faster"><code>FASTER</code></h2>
<blockquote>
<p>Faster checks result in a happier, more productive team</p>
</blockquote>
<p>Several aspects of this effort result in significantly faster test runs:</p>
<ol>
<li><p>Running against an already deployed site eliminates the need to start the dev server on CI</p>
</li>
<li><p>With the CI server no longer serving the dev build and running tests, we can increase the number of workers Playwright uses, enabling parallelization for simultaneous execution of unrelated tests.</p>
</li>
<li><p>Switched the report artifact upload step to run only on test failure, reducing unnecessary executions.</p>
</li>
</ol>
<p>As an example, comparing the before and after run results, we see an improvement of over 30%! Not bad considering the new, faster test suite is both more comprehensive AND more reliable.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1702085968481/49ca2552-ea33-4b14-a9cc-f08c64616239.png" alt="screenshots of before and after github action reports, showing over 30% speed improvement of E2E runs on deploy preview" class="image--center mx-auto" /></p>
<h2 id="heading-stronger"><code>STRONGER</code></h2>
<blockquote>
<p>Increasing coverage of our tests, more realistically replicating user journeys</p>
</blockquote>
<p>A major goal was to increase coverage across all public health topics and demographic/geographic disaggregations provided by the tracker. This wasn't possible before due to the flaky, slow configuration. By running tests against real, deployed production builds, we can now navigate our tests with the same confidence we have using the actual app. We have even had our first open-source community contributions from friends and fellow Denver developers <a target="_blank" href="https://github.com/alinix1">Ali</a> and <a target="_blank" href="https://jamesdemlow.com/">James</a>, whom I would like to personally thank for their efforts!</p>
<p>To easily generate new coverage, we added a new shortcut to package.json, <code>npm run e2e-new</code>, which quickly runs Playwright's fantastic codegen feature. Regrettably, I had overlooked this feature earlier, likely because of my negative experiences decades ago with code generation in early site builders like Frontpage and Dreamweaver. However, Playwright's codegen is not only easy; it writes more reliable steps than I could write myself! It launches a special browser instance and smaller console and then records your steps as you navigate throughout the app, using the most robust locators available. Initially, we don't even need to include <code>expect</code> statements, since a <code>click()</code>action will fail if its target is not available.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1702097874680/a7b36654-f8a1-4c15-974b-098d6bcdc9b1.gif" alt class="image--center mx-auto" /></p>
<h1 id="heading-whats-next">What's next?</h1>
<p>Going forward, we hope to:</p>
<ul>
<li><p>Ensure coverage for every health topic available on the tracker.</p>
</li>
<li><p>Include tests with extensive filtering/UI settings across those topics.</p>
</li>
<li><p>Divide the tests into 'essential' (run every time) and 'non-essential' (only run on production releases), minimizing wait times.</p>
</li>
<li><p>Implement a matrix of test settings on nightly production runs, allowing a combination of browsers (Edge, Firefox, Safari) and device settings (mobile, tablet, and desktop widths).</p>
</li>
<li><p>Ensure the development team culture emphasizes adding test coverage whenever a bug is fixed; minimizing regressions.</p>
</li>
</ul>
<p>Thanks for reading! Feel free to follow along or contribute to the open-source <a target="_blank" href="https://github.com/SatcherInstitute/health-equity-tracker">HET GitHub repo</a>, and let me know if you have any questions on optimizing your Playwright configuration!</p>
<hr />
<p><em>Photo by</em> <a target="_blank" href="https://unsplash.com/@rocknrollmonkey?utm_content=creditCopyText&amp;utm_medium=referral&amp;utm_source=unsplash"><em>Rock'n Roll Monkey</em></a> <em>on</em> <a target="_blank" href="https://unsplash.com/photos/blue-plastic-robot-toy-R4WCbazrD1g?utm_content=creditCopyText&amp;utm_medium=referral&amp;utm_source=unsplash"><em>Unsplash</em></a></p>
]]></content:encoded></item><item><title><![CDATA[Eliminating over 3,000 DOM elements and improving accessibility along the way]]></title><description><![CDATA[With the Health Equity Tracker, we display a lot of data visualizations including choropleth maps (where data is represented visually with color and shade). Unfortunately, these types of graphics are inherently inaccessible for visually impaired user...]]></description><link>https://blog.benhammondmusic.tech/eliminating-over-3000-dom-elements-and-improving-accessibility-along-the-way</link><guid isPermaLink="true">https://blog.benhammondmusic.tech/eliminating-over-3000-dom-elements-and-improving-accessibility-along-the-way</guid><category><![CDATA[Accessibility]]></category><category><![CDATA[DataVisualization]]></category><dc:creator><![CDATA[Ben Hammond]]></dc:creator><pubDate>Thu, 05 Oct 2023 18:14:13 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1696529204144/4219df9f-c967-4f35-b802-82923b859779.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>With the <a target="_blank" href="https://healthequitytracker.org">Health Equity Tracker</a>, we display a lot of data visualizations including choropleth maps (where data is represented visually with color and shade). Unfortunately, these types of graphics are inherently inaccessible for visually impaired users, and the driving goal of our site is ensuring data is available to everyone!</p>
<p><a target="_blank" href="https://healthequitytracker.org/exploredata?mls=1.women_in_gov-3.00&amp;group1=All&amp;dt1=women_in_us_congress"><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1696529404786/d1163cb4-ffe9-455d-aa18-cf3fec5b13ab.png" alt="Map of the United States and territories from the Health Equity Tracker. Rates of women in US Congress by state are represented using color, which is inherently inaccessible to unsighted users." class="image--center mx-auto" /></a></p>
<p>Last year, I spent a lot of time improving accessibility, including some open-source contributions upstream to fix the 3rd-party package we are using to render Vega.js using React. My best (somewhat hacky) solution for this data issue though, was to render duplicate invisible text elements with the geography name and data point alongside the visible map shape. This allowed a screen-reader user to "walk" through the listed items on the map and hear each item's information.  </p>
<p>This inflated the DOM size, but I didn't realize by how much until recently! I figured there would be roughly twice as many elements... half visible, half not... turns out I was accidentally reproducing the ENTIRE accessible dataset several times: for the primary map as expected, and also for every small territory circle we produce under the main map. This means in some cases I was generating THOUSANDS more invisible elements than expected (56 states and territories <em>7 Vega.js components</em> up to 10 maps with one per race group in our multimap mode)!!! Even worse than the additional work for the browser caused by that many elements, the screen reader user would have to skip over those additional 6 lists, hearing duplicate information each time.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1696529520666/2d440346-8af5-4765-bc2d-119377ac63fd.png" alt="Before and After lighthouse reports showing huge reduction in number of DOM elements and some improvement in loading times" class="image--center mx-auto" /></p>
<p>As you can see from the lighthouse report, there are still TOO MANY ELEMENTS, but cutting back from over 6k to over 3k is a big step in the right direction! Beyond the code improvements, I'm also excited to start incorporating ML in generating human-readable insights from datasets for situations like this (and importantly, to present the "insight" text for ALL users since it could help everyone in understanding the map data). Anyone else working on similar accessibility projects or have innovative data visualization solutions to share?</p>
]]></content:encoded></item><item><title><![CDATA[Shaving hours off a Pandas script]]></title><description><![CDATA[Crunching a few hundred million lines of data
At the Health Equity Tracker, the largest dataset we work with is a case-level set provided by the CDC with every COVID-19 infection in the United States, along with additional information including race,...]]></description><link>https://blog.benhammondmusic.tech/shaving-hours-off-a-pandas-script</link><guid isPermaLink="true">https://blog.benhammondmusic.tech/shaving-hours-off-a-pandas-script</guid><category><![CDATA[DataVisualization]]></category><category><![CDATA[pandas]]></category><category><![CDATA[Rust]]></category><category><![CDATA[Polars]]></category><category><![CDATA[Python]]></category><dc:creator><![CDATA[Ben Hammond]]></dc:creator><pubDate>Thu, 14 Sep 2023 05:28:02 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1694667364082/8389465b-263f-475f-ab94-a7e8850caac1.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h2 id="heading-crunching-a-few-hundred-million-lines-of-data">Crunching a few hundred million lines of data</h2>
<p>At the <a target="_blank" href="https://healthequitytracker.org/exploredata">Health Equity Tracker</a>, the largest dataset we work with is a case-level set provided by the CDC with every COVID-19 infection in the United States, along with additional information including race, ethnicity, county, and other symptoms. The data is provided in zipped .csv files in a private GitHub repo (due to patient privacy) and contains several hundred million rows across over a dozen files. Initially, our tracker as coded by <a target="_blank" href="http://Google.org">Google.org</a> provided only the cumulative "snapshot" of the current rates of disease outcomes by race, age, or sex to the county level.</p>
<p><a target="_blank" href="https://healthequitytracker.org/exploredata?mls=1.covid-3.00&amp;group1=All&amp;dt1=covid_hospitalizations#rate-map"><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1694668435759/03be141e-0b64-4cde-8671-ca840b25b819.png" alt="Choropleth map from the Health Equity Tracker showing the United States, with states and territories colored from dark green to yellow representing cumulative COVID rates in each state" class="image--center mx-auto" /></a></p>
<p>Last year however, in a big push by our team at Morehouse School of Medicine, we were able to implement time-tracking, whereby we additionally aggregate and plot these disease rates across every month since January 2020.</p>
<p><a target="_blank" href="https://healthequitytracker.org/exploredata?mls=1.covid-3.00&amp;group1=All&amp;dt1=covid_hospitalizations#rates-over-time"><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1694668563775/827ccfaf-99e1-4d7b-8d5e-eefb0029e80d.png" alt="Time-series line chart comparing monthly rates of White and Native American COVID hospitalizations since early 2020. The line for American Indian and Alaska Native is significantly higher than White at essentially every measured point in time  " class="image--center mx-auto" /></a></p>
<p>As the requirements grew, so did the time it took to run the aggregation script, which needed to be done locally by an authorized team member before uploading for further processing and calculations on Google Cloud Run. The CDC releases new datasets regularly, so this entire process easily burned an entire day each month for one of our team members. After starting the script locally on my 2021 MacBook Pro, it would take <strong>nearly 3 hours to complete the aggregations</strong>.</p>
<blockquote>
<p>With a few tweaks, I was able to reduce that 3-hour run-time to ~25 minutes!</p>
</blockquote>
<h2 id="heading-improvements">Improvements</h2>
<p>I love that our codebase is open-source, so you can <a target="_blank" href="https://github.com/SatcherInstitute/health-equity-tracker/pull/2342">check out the exact PR here</a>, but below I'll outline the specific steps that sped things up so significantly. To benchmark, I ran the Python script against just a single raw .csv file (instead of all ~20).</p>
<blockquote>
<p>Time taken by the original code to process a single file: 3<em>13s</em></p>
</blockquote>
<p>Here are the incremental changes I made, and the improved speed measured from each step:</p>
<ul>
<li><p>Two major items: Using a vectorized <code>combine_race_eth()</code> function, which used Pandas' built-in optimized functions rather than mapping/applying a regular Python lambda function against each row, and only reading in the needed columns with the arg <code>usecols</code> in the <code>read_csv</code>: <strong>191s</strong></p>
</li>
<li><p>The above changes, plus removing some unused string manipulations meant to detect and remove empty quotes <code>""</code>: <strong>180s</strong></p>
</li>
<li><p>The above changes, plus using <code>chunk_size</code> = 1 million (using chunk size splits the df into chunks so that your machine doesn't have to hold the entire thing in memory; however my computer is fast enough to hold the entire thing so it's faster to have bigger chunks and fewer iterations. : <strong>75s</strong></p>
</li>
<li><p>Increasing <code>chunk_size</code> to 2 million: <strong>64s</strong></p>
</li>
<li><p>Increasing <code>chunk_size</code> to 5 million (I left the chunking in place even though the <code>chunk_size</code> effectively doesn't use it, in case the CDC does ever ship us a file over 5 million rows, then this code will use the chunking as needed): <strong>57s</strong></p>
</li>
</ul>
<blockquote>
<p>When running against the full set of all raw files, the run time went <a target="_blank">from 2 hours</a> and 40 minutes down to 34 minutes and produced identical output files: Nearly 5x faster!</p>
</blockquote>
<h2 id="heading-sanity-checks">Sanity Checks</h2>
<p>Of the utmost importance for us as a research institution, particularly one presenting information on underserved populations, is to ensure data integrity every step of the way. To ensure my refactor didn't cause unexpected results, I implemented a "sanity check" and renamed all of the existing .csv results (produced by the old code) with the suffix <code>_old,</code> then wrote a quick bash script that compared every line in the <code>_old</code> and newly refactored results:</p>
<ul>
<li><pre><code class="lang-bash">            <span class="hljs-keyword">for</span> new_file <span class="hljs-keyword">in</span> cdc_restricted_by_*.csv; <span class="hljs-keyword">do</span>
                old_file=<span class="hljs-string">"old_<span class="hljs-variable">$new_file</span>"</span>
                <span class="hljs-keyword">if</span> diff -q <span class="hljs-string">"<span class="hljs-variable">$old_file</span>"</span> <span class="hljs-string">"<span class="hljs-variable">$new_file</span>"</span>; <span class="hljs-keyword">then</span>
                    <span class="hljs-built_in">echo</span> <span class="hljs-string">"Files <span class="hljs-variable">$old_file</span> and <span class="hljs-variable">$new_file</span> are identical."</span>
                <span class="hljs-keyword">fi</span>
            <span class="hljs-keyword">done</span>
</code></pre>
</li>
</ul>
<p>To ensure the check itself was working, I also ran this <code>diff</code> command to compare the county_race file and the state_race file and observed hundreds of differences (as expected).</p>
<h2 id="heading-abandoned-optimizations">Abandoned Optimizations</h2>
<p>The most common suggestion I found when researching Panda's optimization was to utilize the vectorized methods built-in to Pandas, rather than using <code>.apply()</code> to apply a lambda or function against each row of the dataframe iteratively. In some cases, like refactoring the <code>race</code> and <code>ethnicity</code> -&gt; <code>race/ethnicity</code> function, this did speed up the algorithm significantly. However, there were several further vectorization optimizations I tried out that surprisingly <em>slowed down</em> the process and made it worse. These were all <code>.str</code> dataframe methods, which are <a target="_blank" href="https://github.com/pandas-dev/pandas/issues/35864">a known subset</a> of vectorized methods that can be less efficient than the Python loop-based approach.</p>
<p>One example of an abandoned optimization was dealing with our county FIPS codes that need to be treated as strings, complete with leading zeros (for example Denver County FIPS is <code>08031</code>, which when treated as a number turns into <code>8,031</code>.</p>
<p>The type conversion and string formatting are done in the existing code using <code>.map()</code>:</p>
<pre><code class="lang-python">df[COUNTY_FIPS_COL] = df[COUNTY_FIPS_COL].map(<span class="hljs-keyword">lambda</span> x: x.zfill(<span class="hljs-number">5</span>) <span class="hljs-keyword">if</span> len(x) &gt; <span class="hljs-number">0</span> <span class="hljs-keyword">else</span> x)
</code></pre>
<p>and I tried using the built-in method to accomplish the same thing:</p>
<pre><code class="lang-python">df[COUNTY_FIPS_COL] = df[COUNTY_FIPS_COL].str.zfill(<span class="hljs-number">5</span>) <span class="hljs-comment"># actually slower 😿</span>
</code></pre>
<p>Overall, any attempts at optimizations with the data frame <code>.str</code> methods were abandoned as they did not improve performance, and in some cases slowed it down significantly.</p>
<h2 id="heading-next-steps">Next steps</h2>
<p>Although 25 minutes is a lot more manageable than a few hours, and the script is now only run monthly, there are several more steps I am looking into to further optimize this aggregation. If you are reading this and have insight into any of these techniques, let me know in the comments!</p>
<ul>
<li><p>🧑‍💻<strong>Parquet</strong> <strong>instead of</strong> 🧑‍💻<strong>CSV</strong>: Using the CDC's provided <code>.parquet</code> files instead of the <code>.csv</code> files. From what I understand Parquet files are binary/machine-readable, so slightly more difficult to work with (you can't just peek at the contents in VSCode) but more efficient from a memory standpoint. It's unclear if this would be super helpful though, since each file is only used briefly and not continuously opened and manipulated.</p>
</li>
<li><p><strong>🐻‍❄️Polars instead of 🐼Pandas:</strong> A more significant change, which would further complicate the codebase, is to introduce a new library for this aggregation called Polars. Its usage is quite similar to Pandas, but it can stream data and manipulate datasets that are larger than the machine's memory. You can load data into a "lazyframe", and perform various aggregations, filters, and calculations, and then the library is clever enough to only deal with the bits of data that are needed for the specified processes. Interestingly, Polars is written in Rust, but the library is available both within Rust AND within Python.</p>
</li>
<li><p>🦀<strong>Rust</strong> <strong>instead of</strong> 🐍<strong>Python</strong>: Maybe out of scope for the project, but of course the only option I've started pursuing (mainly because it was exciting to finally have a tech problem that Rust could help me solve and I wanted to check out what all the buzz was about). Since this aggregation is performed on the local development machine, and is quite distinct from the rest of our Airflow data pipelines, I could refactor this entire script into Rust (using Polars as mentioned above).</p>
</li>
</ul>
<p>Here's a little snippet of what I've gotten working... Be nice Rustaceans! I'm sure this is super inefficient!</p>
<pre><code class="lang-rust"><span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">process_lazyframe_into_by_sex_df</span></span>(lf: LazyFrame) -&gt; <span class="hljs-built_in">Result</span>&lt;DataFrame, PolarsError&gt; {
    <span class="hljs-keyword">let</span> known_sex_groups = <span class="hljs-built_in">vec!</span>[<span class="hljs-string">"Male"</span>, <span class="hljs-string">"Female"</span>, <span class="hljs-string">"Other"</span>];
    <span class="hljs-keyword">let</span> know_sex_series = Series::new(<span class="hljs-string">"KNOWN_SEX_GROUP"</span>, known_sex_groups);
    <span class="hljs-keyword">let</span> is_known_sex_group = col(<span class="hljs-string">"sex"</span>).is_in(lit(know_sex_series));

    <span class="hljs-keyword">let</span> groupby_cols = <span class="hljs-built_in">vec!</span>[col(<span class="hljs-string">"state_postal"</span>), col(<span class="hljs-string">"sex"</span>), col(<span class="hljs-string">"time_period"</span>)];

    <span class="hljs-keyword">let</span> df = lf
        <span class="hljs-comment">// "time_period" as cdc col with only YYYY-MM</span>
        .with_column((col(<span class="hljs-string">"cdc_case_earliest_dt"</span>).<span class="hljs-built_in">str</span>().str_slice(<span class="hljs-number">0</span>, <span class="hljs-literal">Some</span>(<span class="hljs-number">7</span>))).alias(<span class="hljs-string">"time_period"</span>))
        <span class="hljs-comment">// count every row as 1 case</span>
        .with_column(col(<span class="hljs-string">"time_period"</span>).is_not_null().alias(<span class="hljs-string">"cases"</span>))
        .with_column(col(<span class="hljs-string">"hosp_yn"</span>).eq(lit(<span class="hljs-string">"Yes"</span>)).alias(<span class="hljs-string">"hosp_y"</span>))
        .with_column(col(<span class="hljs-string">"hosp_yn"</span>).eq(lit(<span class="hljs-string">"No"</span>)).alias(<span class="hljs-string">"hosp_n"</span>))
        .with_column(
            col(<span class="hljs-string">"hosp_yn"</span>)
                .neq(lit(<span class="hljs-string">"Yes"</span>))
                .and(col(<span class="hljs-string">"hosp_yn"</span>).neq(lit(<span class="hljs-string">"No"</span>)))
                .alias(<span class="hljs-string">"hosp_unknown"</span>),
        )
        .with_column(col(<span class="hljs-string">"death_yn"</span>).eq(lit(<span class="hljs-string">"Yes"</span>)).alias(<span class="hljs-string">"death_y"</span>))
        .with_column(col(<span class="hljs-string">"death_yn"</span>).eq(lit(<span class="hljs-string">"No"</span>)).alias(<span class="hljs-string">"death_n"</span>))
        .with_column(
            col(<span class="hljs-string">"death_yn"</span>)
                .neq(lit(<span class="hljs-string">"Yes"</span>))
                .and(col(<span class="hljs-string">"death_yn"</span>).neq(lit(<span class="hljs-string">"No"</span>)))
                .alias(<span class="hljs-string">"death_unknown"</span>),
        )
        <span class="hljs-comment">// only keep Male/Female/Other/Unknown options for sex</span>
        .with_column(
            when(is_known_sex_group)
                .then(col(<span class="hljs-string">"sex"</span>))
                .otherwise(lit(<span class="hljs-string">"Unknown"</span>)),
        )
        <span class="hljs-comment">// only keep known postal codes; combine rest as "Unknown" for national numbers</span>
        .with_column(
            when(
                col(<span class="hljs-string">"res_state"</span>)
                    .is_null()
                    .or(col(<span class="hljs-string">"res_state"</span>).eq(lit(<span class="hljs-string">"Missing"</span>)))
                    .or(col(<span class="hljs-string">"res_state"</span>).eq(lit(<span class="hljs-string">"Unknown"</span>)))
                    .or(col(<span class="hljs-string">"res_state"</span>).eq(lit(<span class="hljs-string">"NA"</span>))),
            )
            .then(lit(<span class="hljs-string">"Unknown"</span>))
            .otherwise(col(<span class="hljs-string">"res_state"</span>))
            .alias(<span class="hljs-string">"state_postal"</span>),
        )
        .groupby(groupby_cols)
        .agg(<span class="hljs-built_in">vec!</span>[
            col(<span class="hljs-string">"cases"</span>).sum(),
            col(<span class="hljs-string">"hosp_y"</span>).sum(),
            col(<span class="hljs-string">"hosp_n"</span>).sum(),
            col(<span class="hljs-string">"hosp_unknown"</span>).sum(),
            col(<span class="hljs-string">"death_y"</span>).sum(),
            col(<span class="hljs-string">"death_n"</span>).sum(),
            col(<span class="hljs-string">"death_unknown"</span>).sum(),
        ])
        .collect();

    df
}
</code></pre>
<p>So far I've gotten it to the point that it:</p>
<ul>
<li><p>Loads all the .csv files in a folder</p>
</li>
<li><p>Combines them into a LazyFrame</p>
</li>
<li><p>Performs the column calculations, manipulations, and aggregations needed for the <code>SEX</code> table generations and the produced tables are identical to the full tables produced by our Python code</p>
</li>
</ul>
<p>Run time is well under 5 minutes! Of course, that's only for one of the breakdowns, so it's still not clear if my super newbie Rust code is actually going to save any time or not. I'll follow up once I've had a chance to finish, for now the <a target="_blank" href="https://github.com/benhammondmusic/rust-big-data">repo is available on my GitHub</a>. Overall, I've enjoyed messing around in Rust (it feels like when I was learning TypeScript, except I can't just cheat and use <code>any</code> to force things to work when the compiler yells!). And importantly, I've freed up a lot of free time for our small team, allowing us to work on new features for the tracker and <a target="_blank" href="https://xkcd.com/303/">waste less time on the waiting for code to run</a>.</p>
]]></content:encoded></item><item><title><![CDATA[Ignore GitHub Whitespace Diffs]]></title><description><![CDATA[In Chrome:1. ⌘+D to make a new bookmarklet in your bookmarks toolbar2. Name it whatever, like "Hide PR Whitespace"3. Paste javascript:document.location+="?w=1" as the URL4. Click it whenever GitHub is showing you whitespace diff

Ideally, everyone in...]]></description><link>https://blog.benhammondmusic.tech/ignore-github-whitespace-diffs</link><guid isPermaLink="true">https://blog.benhammondmusic.tech/ignore-github-whitespace-diffs</guid><category><![CDATA[GitHub]]></category><category><![CDATA[bookmarklet]]></category><dc:creator><![CDATA[Ben Hammond]]></dc:creator><pubDate>Wed, 26 Apr 2023 20:14:47 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1682539990227/ebf039fb-61aa-4e02-8982-e0e52960fcc5.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<blockquote>
<p>In Chrome:<br />1. <strong>⌘+D</strong> to make a new bookmarklet in your bookmarks toolbar<br />2. Name it whatever, like "Hide PR Whitespace"<br />3. Paste <code>javascript:document.location+="?w=1"</code> as the URL<br />4. Click it whenever GitHub is showing you whitespace diff</p>
</blockquote>
<p>Ideally, everyone in your repo will end up pushing pre-linted code, which should align styling and white-space issues. In reality this won't always happen, and seeing white space in the diff can be annoying and make the PR looks more massive than it is.</p>
<p>GitHub recently introduced a Pull Request level setting that gets remembered for the life of the PR that allows whitespace to be hidden. This is a pretty useless feature for most of us since it requires 3 clicks to enable and must be done on every new PR.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1682539042831/ed73886c-0c64-428a-8bd3-9b7542a2b1e2.gif" alt class="image--center mx-auto" /></p>
<p>The other option is to manually append <code>?w=1</code> to the pull request URL, which triggers the same white space suppression. Luckily, this can be done automatically via JavaScript plopped into a bookmarklet, allowing us a 1-click option for easier PR reviewing. Follow the steps at the top of this post to make your very own bookmarklet. Enjoy!</p>
<p>Caveat: this won’t work if there are existing params on the URL, since subsequent params require the <code>&amp;</code> rather than the initial param’s <code>?</code></p>
]]></content:encoded></item><item><title><![CDATA[Expose Vite's local address]]></title><description><![CDATA[Add -- --host to the end of your package manager's start command. Note the extra hyphens!

As a follow-up to my blog on viewing localhost on mobile, I thought I would share how to do the same with Vite specifically. A recent task I completed at work ...]]></description><link>https://blog.benhammondmusic.tech/expose-vites-local-address</link><guid isPermaLink="true">https://blog.benhammondmusic.tech/expose-vites-local-address</guid><category><![CDATA[vite]]></category><category><![CDATA[React]]></category><category><![CDATA[npm]]></category><category><![CDATA[localhost]]></category><category><![CDATA[Responsive Web Design]]></category><dc:creator><![CDATA[Ben Hammond]]></dc:creator><pubDate>Fri, 07 Apr 2023 17:53:36 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1680889637729/03cd9244-9ded-45d2-a194-4c0ba80d3263.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<blockquote>
<p>Add <code>-- --host</code> to the end of your package manager's start command. Note the extra hyphens!</p>
</blockquote>
<p>As a follow-up to my blog on <a target="_blank" href="https://blog.benhammond.tech/how-to-view-localhost-on-mobile">viewing localhost on mobile</a>, I thought I would share how to do the same with Vite specifically. A recent task I completed at work was migrating <a target="_blank" href="https://healthequitytracker.org">healthequitytracker.org</a> from create-react-app to Vite. In another article, I'll share the details of how this cut our CI build time by 75%, saving the team over 25 hours per month 🚀, but for now, I'll discuss the not-so-obvious way you can have Vite expose the local server's actual IP address (allowing you to view on another device as explained in <a target="_blank" href="https://blog.benhammond.tech/how-to-view-localhost-on-mobile">my other blog post</a>).</p>
<h2 id="heading-updating-your-packagejson-scripts">Updating your package.json scripts</h2>
<pre><code class="lang-json"><span class="hljs-string">"scripts"</span>: {
...
 <span class="hljs-attr">"start"</span>: <span class="hljs-string">"vite -- --host"</span>
...
}
</code></pre>
<p>Vite politely gives you the message <code>Network: use --host to expose</code> when you run the dev server, however it doesn't explain HOW to use that flag. Most likely you have a <code>package.json</code> folder inside your project, and in that JSON is a property called <code>"scripts"</code>. These are the scripts that run when you start your local server with <code>npm run start</code> or <code>npm run dev</code>, or whatever your team has set up. If you were running the Vite command directly (and not via npm scripts), you would use the flag as they write: <code>--host</code>, however, to pass the flag along via npm to Vite, you must include the additional double-hyphen, making the needed flag <code>-- --host</code>. By adding that flagged-flag to your script command, you'll tell the script to pass along the flag to Vite, which will then automatically expose the local host address when the server starts up.</p>
<h2 id="heading-directly-from-the-command-line">Directly from the command line</h2>
<p>You can also use this flagged-flag directly on the command line if you only want to expose the IP a single time and not by default, so instead of using <code>npm run dev</code> you would use <code>npm run dev -- --host</code></p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1680888592800/350734b2-f968-4c2a-ba1d-0013152eb155.png" alt class="image--center mx-auto" /></p>
<p>Now that you have the address, you can easily <a target="_blank" href="https://blog.benhammond.tech/how-to-view-localhost-on-mobile">view the development server directly on your mobile device</a>.</p>
<p>And yes, if you look closely, I am pleased to finally be working on a feature that will kill a UI carousel that has been in place for far too long. If you're wondering why this is exciting to me, check out <a target="_blank" href="https://shouldiuseacarousel.com/">Should I Use A Carousel?</a></p>
]]></content:encoded></item><item><title><![CDATA[How to view localhost on mobile]]></title><description><![CDATA[Just swap localhost for your machine's local IP address
Update: for vite bundled apps, read my follow up: Expose Vite's local address
Update: for create-react-app you can easily copy it from the terminal where you are running the server


While build...]]></description><link>https://blog.benhammondmusic.tech/how-to-view-localhost-on-mobile</link><guid isPermaLink="true">https://blog.benhammondmusic.tech/how-to-view-localhost-on-mobile</guid><category><![CDATA[Developer Tools]]></category><category><![CDATA[mobile]]></category><category><![CDATA[Responsive Web Design]]></category><category><![CDATA[React]]></category><dc:creator><![CDATA[Ben Hammond]]></dc:creator><pubDate>Thu, 15 Dec 2022 18:11:59 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1665315585921/8t8VadjZI.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<blockquote>
<p>Just swap <code>localhost</code> for your machine's local IP address</p>
<p>Update: for <code>vite</code> bundled apps, read my follow up: <a target="_blank" href="https://blog.benhammond.tech/expose-vites-local-address">Expose Vite's local address</a></p>
<p>Update: for <code>create-react-app</code> you can easily copy it from the terminal where you are running the server</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1671127403878/7NJRbtqd5.png" alt="output from the terminal prompt showing the local ip address" class="image--center mx-auto" /></p>
</blockquote>
<p>While building sites, your content should always be easily accessible regardless of the user's device size, speed or type. It is much easier to build in the responsive design from the beginning, rather than the trap we often fall into where we develop on blazingly fast desktop machines hooked up to ethernet, and then wonder why the site barely functions on an older mobile phone via crappy 3G connection.</p>
<p>Thankfully, it's pretty painless to view your local development from one machine (like your laptop) on another machine (like your iPhone). Follow the steps below (for Mac but they'd be similar on any OS) to start seeing your dev site on a true mobile device!</p>
<ol>
<li><p>Find your computer's local IP address:</p>
</li>
<li><p>Open “Network settings”</p>
<p> <img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1665315772597/yygKT9eL9.png" alt="wifi.png" /></p>
</li>
<li><p>Highlight and copy the IP address (using right click -&gt; copy, NOT cmd-C)</p>
<p> <img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1665315787587/fZ6aXZiZi.png" alt="ip.png" /></p>
</li>
<li><p>Start your local dev server, launch browser and view the site in progress. For <code>create-react-app</code> it would be <code>localhost:3000</code></p>
</li>
<li><p>Replace <code>localhost</code> with the computer's IP address, but keep the port (the <code>:3000</code>), and confirm the same site loads. My app’s new url would be <code>192.168.1.192:3000</code></p>
</li>
<li><p>Done! Copy-paste that new address and send it to any iPhone, Android, tablet, smartTV, whatever! As long as the device is on the same network as your computer running the dev server, it should work.</p>
</li>
</ol>
]]></content:encoded></item><item><title><![CDATA[Right-click to "Shrink Video" on Mac]]></title><description><![CDATA[I frequently upload screen recordings on my work in progress to our GitHub issues / PRs, but often hit the max file size of 10mb when demonstrating complicated features. I tried a few solutions including the built in "Encode Selected Video Files" whi...]]></description><link>https://blog.benhammondmusic.tech/right-click-to-shrink-video-on-mac</link><guid isPermaLink="true">https://blog.benhammondmusic.tech/right-click-to-shrink-video-on-mac</guid><category><![CDATA[macOS]]></category><category><![CDATA[Bash]]></category><category><![CDATA[video]]></category><category><![CDATA[GitHub]]></category><dc:creator><![CDATA[Ben Hammond]]></dc:creator><pubDate>Wed, 29 Dec 2021 21:56:08 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1640841173320/4tBD3nExD.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>I frequently upload screen recordings on my work in progress to our GitHub issues / PRs, but often hit the max file size of 10mb when demonstrating complicated features. I tried a few solutions including the built in "Encode Selected Video Files" which uses Quicktime, but that reduced the resolution of the video and I knew there was a better solution. </p>
<p>There was! <code>ffmpeg</code> is a super powerful CLI that allows extremely flexible video processing. I was able to run this command in the terminal <code>ffmpeg -i ./path/to/my/bigvideo.mp4 ./path/to/my/shrunkenVideo.mp4</code> and it would shrink it nicely. </p>
<p>However, I personally hate switching contexts between GUI stuff (screen recordings, browser based tools, etc) and CLI stuff (like ffmpeg). I wanted to be able to simply right-click on the video file in question on my desktop and click "Shrink Video". </p>
<p>Enter Automator Quick Actions! Here's how I configured my right-click "Shrink Video" option:</p>
<ol>
<li>Open Automator from your Applications</li>
<li>Select the gear icon "Quick Action" and click "Choose"</li>
<li>Scroll down the list and drag "Run Shell Script" (with a little terminal icon) onto the right-hand "workflow" box</li>
<li>Make the phrase say: "Workflow receives current <code>movie files</code> in <code>Finder.app</code>"</li>
<li>Inside the "Run Shell Script" box, select "Pass Input: <code>as arguments</code>"</li>
<li>Paste this script in the text box:<pre><code class="lang-bash"><span class="hljs-built_in">export</span> PATH=/usr/<span class="hljs-built_in">local</span>/bin:<span class="hljs-variable">$PATH</span>
<span class="hljs-keyword">for</span> f <span class="hljs-keyword">in</span> <span class="hljs-string">"<span class="hljs-variable">$@</span>"</span>
<span class="hljs-keyword">do</span>
 <span class="hljs-built_in">cd</span> ~/Desktop
 ffmpeg -i <span class="hljs-string">"<span class="hljs-variable">$f</span>"</span> <span class="hljs-string">"<span class="hljs-variable">$f</span>"</span>-shrunk.mp4
<span class="hljs-keyword">done</span>
</code></pre>
</li>
<li><code>CMD-S</code> to save; I used the quick action name "Shrink Video"</li>
<li>Find a video file, right-click on it, and the new Option should appear under "Quick Actions" and/or "Services". </li>
</ol>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1640840046221/4djOapC8-.png" alt="Screen Shot 2021-12-29 at 9.52.40 PM.png" /></p>
<p>Next step: figure out the flags in <code>ffmpeg</code> to limit the output size to 10mb, compressing the original file as much as necessary to make it fit.</p>
]]></content:encoded></item><item><title><![CDATA[Getting a Sophos "Protected" MacBook onto Starbuck's WiFi]]></title><description><![CDATA[Quick tip for anyone working from a Starbucks on a work laptop that's "protected" by Sophos or any other software that intercepts web modals. After connecting to the official public WiFi network, normally the computer will open up the modal displayin...]]></description><link>https://blog.benhammondmusic.tech/getting-a-sophos-protected-macbook-onto-starbucks-wifi</link><guid isPermaLink="true">https://blog.benhammondmusic.tech/getting-a-sophos-protected-macbook-onto-starbucks-wifi</guid><dc:creator><![CDATA[Ben Hammond]]></dc:creator><pubDate>Tue, 26 Oct 2021 17:59:58 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1635271107628/DFGoN9L5g.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Quick tip for anyone working from a Starbucks on a work laptop that's "protected" by Sophos or any other software that intercepts web modals. After connecting to the official public WiFi network, normally the computer will open up the modal displaying a "You're Connected" message and some advertising; in my case Sophos was blocking this modal (and annoyingly giving no indication any window had been blocked). A great option for situations like this is to visit <code>1.1.1.1</code> or <code>0.0.0.0</code> in your browser, which will often manually trigger this modal process. However, at least at my local green mermaid mansion, this still doesn't work. After several extended forum searches (on my phone) I finally found the magic address, which I am publishing here both for you and for myself next time I'm inevitably locked out for my own good:</p>
<blockquote>
<p>Try to connect to the Starbucks WiFi and then visit
<a target="_blank" href="http://captive.apple.com/hotspot-detect.html">http://captive.apple.com/hotspot-detect.html</a></p>
</blockquote>
<p>Image by <a href="https://unsplash.com/@jontyson?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText">Jon Tyson</a> on <a href="https://unsplash.com/s/photos/coffee-wifi?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText">Unsplash</a></p>
]]></content:encoded></item><item><title><![CDATA[Honoring Reduced Motion Preferences in React]]></title><description><![CDATA[Thanks again to Josh W. Comeau and his awesome blog, I have drafted some changes to the  Health Equity Tracker  website which will honor a user's accessibility settings for "user prefers reduced motion". 

On the site are some existing animations whi...]]></description><link>https://blog.benhammondmusic.tech/honoring-reduced-motion-preferences-in-react</link><guid isPermaLink="true">https://blog.benhammondmusic.tech/honoring-reduced-motion-preferences-in-react</guid><category><![CDATA[a11y]]></category><category><![CDATA[animation]]></category><category><![CDATA[React]]></category><category><![CDATA[ReactHooks]]></category><dc:creator><![CDATA[Ben Hammond]]></dc:creator><pubDate>Mon, 16 Aug 2021 16:20:18 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1629172867402/DNaI5_p-T.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Thanks again to Josh W. Comeau and <a target="_blank" href="https://www.joshwcomeau.com/react/prefers-reduced-motion/">his awesome blog</a>, I have drafted some changes to the  <a target="_blank" href="https://healthequitytracker.org/">Health Equity Tracker</a>  website which will honor a user's accessibility settings for "user prefers reduced motion". </p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1629130657408/_4HJtglDS.gif" alt="Animated GIFs stopping as user enables preferred reduced motion" /></p>
<p>On the site are some existing animations which are pretty, but purely decorative.  Due to their motion effects, they could potentially trigger vestibular disorders in some users, resulting in nausea, dizziness, etc. Thankfully, we have an <a target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/CSS/@media/prefers-reduced-motion">easy way to account for these animation preferences in pure CSS</a>, however in React and in particular while rendering various images, it requires a different approach.</p>
<p>The step-by-step is outlined clearly on <a target="_blank" href="https://www.joshwcomeau.com/react/prefers-reduced-motion/">Josh's post</a>, but in short I created a new hook called <code>usePrefersReducedMotion</code> in our /utils folder, and then instantiated that hook on the two components where animated gifs are rendered. Then, I use a ternary to render either the fully animated gif or a single frame still image, based on the Boolean result of the function call:</p>
<pre><code class="lang-jsx">&lt;AimToGoItem
  src={ prefersReducedMotion 
    ? <span class="hljs-string">"img/HET-fields-no-motion.gif"</span> 
    : <span class="hljs-string">"img/HET_Fields_1_v2_1000px.gif"</span>
  alt=<span class="hljs-string">""</span>
  title=<span class="hljs-string">"Empower policy makers"</span>
  text=<span class="hljs-string">"We plan to develop policy templates for local, state, and
federal policy makers, and help create actionable policies
with diverse communities."</span>
/&gt;
</code></pre>
<p>Also, note I have included blank alt text to the image using <code>alt=""</code>. This is best practice for situations like this where the images are decorative, and a screen reader user would have no use for knowing what the images contains. However, you do need to specify the blank string as the attribute, otherwise leaving off the <code>alt</code> entirely will result in the screen reader trying to parse the element by reading the file name or similar. </p>
<p>I am still quite new to the world of accessibility (commonly referred to as A11y), so if anything you see here is not in fact best practice I would love to know; leave a comment or direct message me here or on  <a target="_blank" href="http://www.twitter.com/benhammondmusic">Twitter</a>.  </p>
<p>Photo by <a href="https://unsplash.com/@madhatter_granneman?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText">Jacob Granneman</a> on <a href="https://unsplash.com/s/photos/reduced-motion?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText">Unsplash</a></p>
]]></content:encoded></item><item><title><![CDATA[Connecting Google Cal API and Django]]></title><description><![CDATA[This post was written back in the days where Heroku provided free hosting; do not expect any example projects or links to Heroku to work.

Some tricky configuration is required, and the existing Python-specific documentation is sparse and buggy (or s...]]></description><link>https://blog.benhammondmusic.tech/connecting-google-cal-api-and-django</link><guid isPermaLink="true">https://blog.benhammondmusic.tech/connecting-google-cal-api-and-django</guid><category><![CDATA[Django]]></category><category><![CDATA[google cloud]]></category><category><![CDATA[APIs]]></category><category><![CDATA[Heroku]]></category><category><![CDATA[Python]]></category><dc:creator><![CDATA[Ben Hammond]]></dc:creator><pubDate>Wed, 23 Jun 2021 22:12:28 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1624486185513/V-yDfTTa0.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p><em>This post was written back in the days where Heroku provided free hosting; do not expect any example projects or links to Heroku to work.</em></p>
<blockquote>
<p>Some tricky configuration is required, and the existing Python-specific documentation is sparse and buggy (or simply not written for a production setting). My eventual solution involves using a Google Cloud Platform service account and a custom Heroku buildpack</p>
</blockquote>
<h1 id="heading-the-problem">The Problem</h1>
<p>Little Cabin (my final project for my recently completed General Assembly bootcamp), provides tools for extended families to securely share their vacation property’s logistics and memories. One key feature is allowing the in-app scheduling to sync with a Google Calendar, and though I assumed it would be a simple matter of hooking up to the Google Calendar API, it was by far the most difficult problem to solve. </p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1624483254791/89bl7Mbi7.png" alt="Screenshot from the author's Little Cabin App" /></p>
<h1 id="heading-the-project-setup">The Project Setup</h1>
<p>To get myself started, I've followed along with my two previous blog posts:</p>
<ul>
<li><a target="_blank" href="https://blog.benhammond.tech/django-getting-started">Setup a new Django app</a></li>
<li><a target="_blank" href="https://blog.benhammond.tech/deploying-django-to-heroku">Deploy it to Heroku</a></li>
</ul>
<blockquote>
<p>I have also created a <a target="_blank" href="https://github.com/benhammondmusic/example-django-gcal-heroku">blog example repo on my GitHub</a> that includes all of the steps in this entire tutorial, so feel free to clone that repo and have a look at the code. You'll still need to do a lot of person configuration to get it working, including creating your own service account, google calendar, Heroku project, .env files and Heroku config vars, etc. </p>
</blockquote>
<h2 id="heading-calendar-access-module">Calendar Access Module</h2>
<p>We will start by creating a new module (just a separate  <code>.py</code> file) to contain all of the Calendar API access methods. Python automatically <em>exports</em>, but you must explicitly <em>import</em> them into any code that needs access.</p>
<p>In your <code>main_app/</code> folder, create a file called <code>calendar_API.py</code>.  In that blank file:</p>
<ul>
<li><code>touch main_app/calendar_API.py</code></li>
<li>then in VSCode, add the following to the blank file:</li>
</ul>
<pre><code class="lang-python"><span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">test_calendar</span>():</span>
    print(<span class="hljs-string">"RUNNING TEST_CALENDAR()"</span>)
    test_event1 = {<span class="hljs-string">"start"</span>: {<span class="hljs-string">"date"</span>: <span class="hljs-string">"2022-01-01"</span>}, <span class="hljs-string">"end"</span>: {<span class="hljs-string">"date"</span>: <span class="hljs-string">"2022-01-07"</span>}, <span class="hljs-string">"summary"</span>:<span class="hljs-string">"test event 1"</span>}
    test_event2 = {<span class="hljs-string">"start"</span>: {<span class="hljs-string">"date"</span>: <span class="hljs-string">"2022-02-01"</span>}, <span class="hljs-string">"end"</span>: {<span class="hljs-string">"date"</span>: <span class="hljs-string">"2022-02-07"</span>}, <span class="hljs-string">"summary"</span>:<span class="hljs-string">"test event 2"</span>}
    events = [test_event1, test_event2]

    <span class="hljs-keyword">return</span> events
</code></pre>
<h2 id="heading-add-route-to-urls">Add Route to URLs</h2>
<p>In VSCode, open <code>main_app/urls.py</code> and add the following the following item to the <code>urlpatterns[]</code> array below the existing "home" route. This will give us two pages in total, our "Home" and our "Demo" which will demonstrate the connection to Google Calendar API</p>
<ul>
<li><code>path('demo/', views.demo, name='demo'),</code></li>
</ul>
<h2 id="heading-add-to-views">Add To Views</h2>
<p>In your <code>views.py</code>, import the module method at the top of the file using:</p>
<ul>
<li><code>from .calendar_API import test_calendar</code></li>
</ul>
<p>Now add a second method which will display the demo template (later on we'll fill in the code that actually does some work)</p>
<pre><code class="lang-python"><span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">demo</span>(<span class="hljs-params">request</span>):</span>
    results = test_calendar()
    context = {<span class="hljs-string">"results"</span>: results}
    <span class="hljs-keyword">return</span> render(request, <span class="hljs-string">'demo.html'</span>, context)
</code></pre>
<h2 id="heading-add-to-templates">Add To Templates</h2>
<p>Create the html template that will serve as the HTML for eventually displaying the fetched results from the API:</p>
<ul>
<li><code>touch main_app/templates/demo.html</code></li>
</ul>
<p>Add the following to to the blank <code>demo.html</code> file in VSCode:</p>
<pre><code class="lang-HTML"><span class="hljs-meta">&lt;!DOCTYPE <span class="hljs-meta-keyword">html</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">html</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">head</span>&gt;</span> <span class="hljs-tag">&lt;/<span class="hljs-name">head</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">body</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">h1</span>&gt;</span>Demo<span class="hljs-tag">&lt;/<span class="hljs-name">h1</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">ul</span>&gt;</span>
      {% for result in results %}
      <span class="hljs-tag">&lt;<span class="hljs-name">li</span>&gt;</span>{{result.start.date}}{% if result.end.date %}-{% endif%}{{result.end.date}}: {{result.summary}}<span class="hljs-tag">&lt;/<span class="hljs-name">li</span>&gt;</span>
      {% endfor %}
    <span class="hljs-tag">&lt;/<span class="hljs-name">ul</span>&gt;</span>
  <span class="hljs-tag">&lt;/<span class="hljs-name">body</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">html</span>&gt;</span>
</code></pre>
<p>Now, edit your <code>home.html</code> to include a link to this new <code>demo.html</code>. Add the following line after the <code>&lt;h1&gt;Home&lt;/h1&gt;</code> line:</p>
<ul>
<li><code>&lt;a href="{% url 'demo' %}"&gt;Connect to Google Calendar&lt;/a&gt;</code></li>
</ul>
<h2 id="heading-install-dependencies">Install Dependencies</h2>
<p>In your terminal, run the following commands to install needed packages and save those as project dependencies:</p>
<ul>
<li><code>pip install --upgrade google-api-python-client google-auth-httplib2 google-auth-oauthlib</code></li>
<li><code>pip3 freeze &gt; requirements.txt</code></li>
</ul>
<h1 id="heading-test-it">TEST IT!</h1>
<p>Let's make sure the plumbing is working before we add more logical complexity! </p>
<p>In your terminal, start the local server (make sure you're in your activated <code>env</code>):</p>
<ul>
<li><code>python3 manage.py runserver</code></li>
</ul>
<p>In your browser</p>
<ul>
<li>Visit <a href="http://localhost:8000">localhost:8000</a></li>
<li>Click on the "Connect to Google Calendar" link</li>
<li>Confirm the headline "Demo" is displayed</li>
</ul>
<h1 id="heading-setting-up-calendar-access">Setting up Calendar Access</h1>
<blockquote>
<p>These instructions draw heavily on <a target="_blank" href="https://stackoverflow.com/questions/49480930/django-server-rw-access-to-self-owned-google-calendar">this amazing Stack Overflow answer</a></p>
</blockquote>
<h2 id="heading-register-for-google-service-account">Register for Google Service Account</h2>
<ul>
<li>Follow Google's directions to setup the new credentials here: <a target="_blank" href="https://developers.google.com/identity/protocols/oauth2/service-account">developers.google.com</a></li>
<li>You can skip the section entitled: "Delegating domain-wide authority to the service account"</li>
<li>Save the credentials file somewhere memorable; you'll be using these locally and later when setting up Heroku config vars</li>
</ul>
<h2 id="heading-configure-a-test-calendar">Configure a Test Calendar</h2>
<ul>
<li>View your <a target="_blank" href="https://calendar.google.com/calendar/r/month">Google Calendar</a></li>
<li>In the sidebar will be a list of your calendars; click the "+" button and select "Create New Calendar" from the dropdown</li>
<li>Fill in a name "Example", and click the "Create Calendar" button</li>
<li>Hover over this newly created calendar back in the sidebar, and click the three vertical dots, and select "Settings and Sharing" from the dropdown</li>
<li>Scroll down to "share with specific people"</li>
<li>Add your newly created service account email address (it will be "client_email" in your saved credentials file)</li>
<li>Scroll down to "Integrate Calendar" and copy the Calendar ID string, then paste it somewhere safe as you'll need it again soon</li>
</ul>
<h2 id="heading-manipulate-calendar-from-local-django-app">Manipulate Calendar from Local Django App</h2>
<h3 id="heading-setup-local-config-vars">Setup Local Config Vars</h3>
<ul>
<li>Open your local environmental variables file; in this setup it's <code>example/.env</code>. </li>
<li>Add the following:<pre><code># Google API
CAL_ID={{{THE CAL ID YOU COPY-PASTED FROM YOUR GOOGLE CALENDAR SETTINGS PAGE}}}
</code></pre></li>
</ul>
<h3 id="heading-create-your-local-credentials-file">Create Your Local Credentials File</h3>
<ul>
<li><code>touch google-credentials.json</code></li>
<li>Copy paste the entire JSON object from your downloaded credentials file into this newly created .json file. As an example, mine looks like this:<pre><code>{
<span class="hljs-string">"type"</span>: <span class="hljs-string">"service_account"</span>,
<span class="hljs-string">"project_id"</span>: <span class="hljs-string">"example"</span>,
<span class="hljs-string">"private_key_id"</span>: <span class="hljs-string">"BLAHBALH"</span>,
<span class="hljs-string">"private_key"</span>: <span class="hljs-string">"-----BEGIN PRIVATE KEY-----\nSUUUUUUPER------LONG-------STRING\n-----END PRIVATE KEY-----\n"</span>,
<span class="hljs-string">"client_email"</span>: <span class="hljs-string">"USERNAME@EXAMPLE.iam.gserviceaccount.com"</span>,
<span class="hljs-string">"client_id"</span>: <span class="hljs-string">"1234567890987654321"</span>,
<span class="hljs-string">"auth_uri"</span>: <span class="hljs-string">"https://accounts.google.com/o/oauth2/auth"</span>,
<span class="hljs-string">"token_uri"</span>: <span class="hljs-string">"https://oauth2.googleapis.com/token"</span>,
<span class="hljs-string">"auth_provider_x509_cert_url"</span>: <span class="hljs-string">"https://www.googleapis.com/oauth2/v1/certs"</span>,
<span class="hljs-string">"client_x509_cert_url"</span>: <span class="hljs-string">"https://www.googleapis.com/robot/v1/metadata/x509/username%40example.iam.gserviceaccount.com"</span>
}
</code></pre></li>
</ul>
<h3 id="heading-update-your-gitignore">Update Your <code>.gitignore</code></h3>
<ul>
<li>in VSCode open your <code>.gitignore</code> file</li>
<li>Ensure all of the following lines are present, which will prevent git from indexing sensitive information (and later distributing it publicly on your GitHub)</li>
</ul>
<pre><code># <span class="hljs-built_in">this</span> hidden folder contains your local, virtual environment
.env

# <span class="hljs-built_in">this</span> hidden file contains sensitive keys and environmental config vars
# <span class="hljs-keyword">if</span> you<span class="hljs-string">'ve named your primary project folder something other than
# '</span>example<span class="hljs-string">' please adjust the following line as needed 
example/.env

# this token contains your sensitive google service account info
google-credentials.json</span>
</code></pre><h2 id="heading-important">Important!</h2>
<p>Please ensure both <code>example/.env</code> and the newly created <code>google-credentials.json</code> appear greyed out in VSCode</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1624485007810/u__Hg8BBu.png" alt="Screenshot showing greyed out gitignored files" /></p>
<h2 id="heading-add-the-api-calls">Add The API Calls</h2>
<ul>
<li>Open <code>calendar_API.py</code>, and replace the existing code with everything in the following block (basically replacing our test logging function with the real API calls)</li>
</ul>
<pre><code><span class="hljs-keyword">from</span> decouple <span class="hljs-keyword">import</span> config
<span class="hljs-keyword">from</span> google.oauth2 <span class="hljs-keyword">import</span> service_account
<span class="hljs-keyword">import</span> googleapiclient.discovery
<span class="hljs-keyword">import</span> datetime

CAL_ID = config(<span class="hljs-string">'CAL_ID'</span>)
SCOPES = [<span class="hljs-string">'https://www.googleapis.com/auth/calendar'</span>]
SERVICE_ACCOUNT_FILE = <span class="hljs-string">'./google-credentials.json'</span>


def test_calendar():
    print(<span class="hljs-string">"RUNNING TEST_CALENDAR()"</span>)

    credentials = service_account.Credentials.from_service_account_file(SERVICE_ACCOUNT_FILE, scopes=SCOPES)
    service = googleapiclient.discovery.build(<span class="hljs-string">'calendar'</span>, <span class="hljs-string">'v3'</span>, credentials=credentials)

    # CREATE A NEW EVENT
    new_event = {
    <span class="hljs-string">'summary'</span>: <span class="hljs-string">"Ben Hammond Tech's Super Awesome Event"</span>,
    <span class="hljs-string">'location'</span>: <span class="hljs-string">'Denver, CO USA'</span>,
    <span class="hljs-string">'description'</span>: <span class="hljs-string">'https://benhammond.tech'</span>,
    <span class="hljs-string">'start'</span>: {
        <span class="hljs-string">'date'</span>: f<span class="hljs-string">"{datetime.date.today()}"</span>,
        <span class="hljs-string">'timeZone'</span>: <span class="hljs-string">'America/New_York'</span>,
    },
    <span class="hljs-string">'end'</span>: {
        <span class="hljs-string">'date'</span>: f<span class="hljs-string">"{datetime.date.today() + datetime.timedelta(days=3)}"</span>,
        <span class="hljs-string">'timeZone'</span>: <span class="hljs-string">'America/New_York'</span>,
    },
    }
    service.events().insert(calendarId=CAL_ID, body=new_event).execute()
    print(<span class="hljs-string">'Event created'</span>)

 # GET ALL EXISTING EVENTS
    events_result = service.events().list(calendarId=CAL_ID, maxResults=<span class="hljs-number">2500</span>).execute()
    events = events_result.get(<span class="hljs-string">'items'</span>, [])

    # LOG THEM ALL OUT IN DEV TOOLS CONSOLE
    <span class="hljs-keyword">for</span> e <span class="hljs-keyword">in</span> events:

        print(e)

    #uncomment the following lines to <span class="hljs-keyword">delete</span> each existing item <span class="hljs-keyword">in</span> the calendar
    #event_id = e[<span class="hljs-string">'id'</span>]
        # service.events().delete(calendarId=CAL_ID, eventId=event_id).execute()


    <span class="hljs-keyword">return</span> events
</code></pre><h1 id="heading-test-it-again">Test It, Again!</h1>
<p>At this point, running the local server (make sure you're in your activated <code>env</code>):</p>
<ul>
<li><code>python3 manage.py run server</code> 
and viewing localhost:8000 in browser should display the same button as before. However, now clicking the button should trigger the series of API calls we added above:</li>
<li>Create a new test event on your test calendar</li>
<li>Download ALL events from that calendar (there's probably only the one unless you manually added any on the Google Calendar site)</li>
<li>Display those downloaded events both as console logs and render them onto the HTML page under the "DEMO" heading</li>
</ul>
<p>If that's all working locally, the next big step is adjusting everything so that it works in production on Heroku.</p>
<h1 id="heading-connecting-the-deployed-app">Connecting the Deployed App</h1>
<blockquote>
<p>These steps come mainly from <a target="_blank" href="https://devdojo.com/bryanborge/adding-google-cloud-credentials-to-heroku">this excellent DevDojo.com blog post</a></p>
</blockquote>
<h2 id="heading-add-custom-buildpack">Add Custom Buildpack</h2>
<ul>
<li>In your browser, head to <a target="_blank" href="https://dashboard.heroku.com/apps">Heroku</a> and log in</li>
<li>Open up the existing App that you've deployed (perhaps you followed my previous blog post: <a target="_blank" href="https://blog.benhammond.tech/deploying-django-to-heroku">Deploying Django to Heroku</a>?</li>
<li>Choose "Settings" from the nav bar</li>
<li>Scroll down and click the "Add Buildpack" button (there should already be "heroku/python" listed as installed</li>
<li>Paste <code>https://github.com/buyersight/heroku-google-application-credentials-buildpack</code> into the text input box labeled "Enter Buildpack URL"</li>
<li>Click "Save changes"</li>
</ul>
<h2 id="heading-add-heroku-config-vars">Add Heroku Config Vars</h2>
<ul>
<li>Scroll up just a bit and click the "Reveal Config Vars" buttons</li>
<li>You will now be adding all of the keys and corresponding values from your local <code>.env</code> file, along with some addition Heroku only entries.</li>
<li>In the KEY box, paste <code>CAL_ID</code>, and in the corresponding VALUE box, paste the string that occurs after the <code>=</code> in your local .env. In my case it looks like this <code>fF43grgewefwFWeww1231233r23rwq@group.calendar.google.com</code>. Make sure there are no quotation marks or spaces before and after the string</li>
<li>Click "Add"</li>
<li>In the new blank KEY box, paste <code>GOOGLE_APPLICATION_CREDENTIALS</code>, and in the corresponding VALUE box paste <code>google-credentials.json</code></li>
<li>Click the "Add" button</li>
<li>In the new KEY box, paste <code>GOOGLE_CREDENTIALS</code>, and in the corresponding VALUE box, paste the entire JSON object. You can literally copy all of the code that is now in your local <code>google-credentials.json</code>, starting with a <code>{</code> and ending with a <code>}</code></li>
<li>Click the "Add" button</li>
</ul>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1624484649512/UXwecl10w.png" alt="Screenshot showing what Heroku settings pages should look like with custom buildpack and config vars" /></p>
<h2 id="heading-deploy-to-heroku">Deploy to Heroku</h2>
<ul>
<li>Head back into your terminal, and save your progress in your local git:</li>
<li><code>git add .</code></li>
<li><code>git commit -m "getting ready for deploy"</code></li>
<li>Optionally push to your GitHub if you have added a remote repo</li>
<li>Deploy using <code>git push heroku main</code> (or <code>master</code> if you haven't yet switched your local git to default to the <code>main</code> branch)</li>
</ul>
<h2 id="heading-confirm-credentials-were-created">Confirm Credentials Were Created</h2>
<p>Once you're notified the deploy was successful, you can use the terminal to manually explore your project's Heroku server and confirm that the credentials file was created automagically by the build pack and those config vars.</p>
<ul>
<li><code>heroku run bash</code> (wait a few seconds for it to log in)</li>
<li><code>ls</code></li>
<li>You should see <code>google-credentials.json</code> listed on the floor of your project. Yay!</li>
<li><code>exit</code> will bring you back onto your local machine</li>
</ul>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1624486726987/AZixt1Yb7.gif" alt="heroku run bash screen recording" /></p>
<h1 id="heading-run-it">Run It!</h1>
<p>Head to your deployed Heroku site, and see if the button still works to populate / print out the Google Calendar events. If so, great job! If not, scope the last paragraph here for some potential debugging tricks. </p>
<iframe src="https://giphy.com/embed/l41YmQjOz9qg2Ecow" width="480" height="270" class="giphy-embed"></iframe>

<blockquote>
<p>As noted in the previous article <a target="_blank" href="https://blog.benhammond.tech/deploying-django-to-heroku">Deploying Django to Heroku</a>, it can be helpful to configure your Django project and Heroku to give more detailed error logging. Check out the instructions on <a target="_blank" href="https://stackoverflow.com/questions/52311724/500-error-when-debug-false-with-heroku-and-django">this stackoverflow</a> where they explain adding <code>DEBUG_PROPAGATE_EXCEPTIONS = True</code> and a <code>LOGGING = { ... }</code> library to their <code>settings.py</code></p>
<p>Sundial Photo by <a href="https://unsplash.com/@mkunsplash84?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText">Marian Kroell</a> on <a href="https://unsplash.com/s/photos/sundial?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText">Unsplash</a></p>
</blockquote>
]]></content:encoded></item><item><title><![CDATA[Django - Getting Started]]></title><description><![CDATA[This post was co-authored with fellow General Assembly Software Engineering Immersive member JC Coles, following some custom suggestions from our instructor Adonis . Also, check out the entire "Getting Started with Django" Series
If you've written fu...]]></description><link>https://blog.benhammondmusic.tech/django-getting-started</link><guid isPermaLink="true">https://blog.benhammondmusic.tech/django-getting-started</guid><category><![CDATA[Django]]></category><category><![CDATA[Python]]></category><category><![CDATA[PostgreSQL]]></category><category><![CDATA[routing]]></category><category><![CDATA[#codenewbies]]></category><dc:creator><![CDATA[Ben Hammond]]></dc:creator><pubDate>Wed, 09 Jun 2021 06:00:00 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1618595523357/7vFdzZM1H.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p><em><strong>This post was co-authored with fellow General Assembly Software Engineering Immersive member <a target="_blank" href="https://jctech.blog/">JC Coles</a>, following some custom suggestions from our instructor Adonis </strong></em>. Also, check out the entire <a target="_blank" href="https://blog.benhammond.tech/series/django">"Getting Started with Django" Series</a></p>
<p>If you've written full stack applications in JavaScript using Node, Express, perhaps Mongoose for accessing a database, maybe some packages like Passport to help manage user authentication and authorization, etc, you quickly find yourself repeating the same setup patterns over and over. The only differences come in when you accidentally misconfigure a setting or a route and spend hours debugging. Django aims to, if not <em>replace</em> all of those steps, at least condense those disparate processes into one. </p>
<p>The following step-by-step guide will assist a beginning Python developer in launching a Django project on their local development server (and then <a target="_blank" href="https://blog.benhammond.tech/deploying-django-to-heroku">deploying it to Heroku in the next blog post</a>).</p>
<blockquote>
<p>If you’re starting a brand new project, skip this section. Otherwise if you are downloading someone else’s project, run the following commands instead to properly configure it on your machine. This is the equivalent of running <code>npm i</code> when freshly cloning someone else's repo. When finished, continue at step "Creating a Home Page" below</p>
<ol>
<li><code>python3 -m venv .env</code></li>
<li><code>source .env/bin/activate</code></li>
<li><code>pip3 install -r requirements.txt</code></li>
<li><code>python3 manage.py migrate</code></li>
<li><code>python3 manage.py runserver</code></li>
<li>Visit <a target="_blank" href="http://localhost:8000">localhost:8000</a> in your browser</li>
</ol>
<p>Having issues?</p>
<ul>
<li>if your project contains environmental variables, add them to an <code>.env</code> file inside your inner project folder (mine is <code>pedalcollector_project/.env</code>)</li>
<li>try reseting your database (you'll lose anything you've added so be careful!) <code>DROPDB {{{your_database}}}</code> and <code>CREATEDB {{{your_database}}}</code></li>
<li>in settings.py, to run locally you'll want <code>DEBUG=True</code>; to deploy securely you'll have to change to <code>DEBUG=False</code>. I manually set an env variable in each environment and then <code>DEBUG</code> will be set automatically.</li>
</ul>
</blockquote>
<h1 id="django-environment-setup-new-project">Django Environment Setup (New Project)</h1>
<p>Python can and should be run in a virtual environment; this relates to the fact that your actual operating system utilizes Python code and by safely keeping your projects in a protected environment, you minimize the risk of accidentally misconfiguring your entire system. In short, you'll be simulating a mini-computer inside your computer, and running your Django project from there. You can quickly tell when your terminal is inside of the virtual environment by looking for the <em>(.env)</em> at the beginning of your terminal prompt:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1618546676648/ES9RjRW8D.png" alt="command line prompt showing parentheses env" /></p>
<h2 id="new-folder">New Folder</h2>
<p>Create and navigate into a new folder that will contain your entire project</p>
<ul>
<li><code>mkdir your_cool_thing</code></li>
<li><code>cd your_cool_thing</code></li>
</ul>
<h2 id="setup-local-git-repo">Setup Local Git Repo</h2>
<ul>
<li><code>git init</code></li>
<li><code>git add .</code></li>
<li>`git commit -m "initialize a cool thing"</li>
<li>optionally connect to a remote Github repo at this point</li>
</ul>
<h2 id="virtual-environment">Virtual Environment</h2>
<p>Create a virtual environment</p>
<ul>
<li><code>python3 -m venv .env</code></li>
</ul>
<p>Activate it</p>
<ul>
<li><code>source .env/bin/activate</code></li>
</ul>
<h2 id="database">Database</h2>
<p>Create a postgres database. <em>You'll need this exact name again soon, so write it in your project notes somewhere you'll remember.</em></p>
<ul>
<li><code>createdb example-db</code> </li>
</ul>
<blockquote>
<p><code>example-db</code> is the example I'll be using; you can use whatever you want throughout this post, just be consistent!</p>
<p>If you have weird errors while testing things and somehow perhaps have corrupt data in your database, you can easily remove and re-add the database with the following commands in your terminal:</p>
<ol>
<li><code>dropdb example-db</code></li>
<li><code>createdb example-db</code></li>
<li><code>python manage.py createsuperuser</code></li>
</ol>
</blockquote>
<h2 id="dependencies">Dependencies</h2>
<p>Install Django and dependencies. Psycopg2 helps Python interact with your postgres DB (similar to how Mongoose allowed Node to easily interact with MongoDB)</p>
<ul>
<li><code>pip3 install django-extensions</code></li>
<li><code>pip3 install Django</code></li>
<li><code>pip3 install psycopg2</code><blockquote>
<p>if that didn’t work run: <code>psycopg2-binary</code>. </p>
</blockquote>
</li>
<li><code>pip3 install python-decouple</code><blockquote>
<p>please note there is a difference between _python<em>decouple</em> and <em>decouple</em>, you need the former.</p>
</blockquote>
</li>
</ul>
<p>Record those settings: <em>requirements.text</em> is the Django equivalent of your Node app's <em>package.json</em>, informing everyone what settings and packages must be configured to function.</p>
<ul>
<li><code>pip3 freeze &gt; requirements.txt</code></li>
</ul>
<h1 id="project-setup">Project Setup</h1>
<p>Django <em>projects</em> can contain multiple Django <em>apps</em>. </p>
<h2 id="new-project">New Project</h2>
<p>Create a new project</p>
<ul>
<li><code>django-admin startproject example .</code> </li>
</ul>
<p>Make a file to secretly store your local environmental variables (a.k.a. config vars)</p>
<ul>
<li><code>touch example/.env</code></li>
</ul>
<p>Make a git ignore file</p>
<ul>
<li><code>touch .gitignore</code></li>
</ul>
<p>Specify the files and folders to ignore</p>
<ul>
<li>Open the blank <code>.gitignore</code> file in VSCode</li>
</ul>
<blockquote>
<p>For a comprehensive <code>.gitignore</code> file like you might get from a create-react-app, you can visit <a target="_blank" href="https://gitignore.io">gitignore.io</a> and select <em>Django</em>. This will generate a complete file for you: <a target="_blank" href="https://www.toptal.com/developers/gitignore/api/django">click to view Django .gitignore</a></p>
</blockquote>
<ul>
<li>Whether you build your gitignore or copy and paste from the site above, be sure it includes the following items:</li>
</ul>
<pre><code># <span class="hljs-keyword">this</span> hidden folder contains your local, virtual environment
.env

# <span class="hljs-keyword">this</span> hidden file contains sensitive keys and environmental config vars 
example_project/.env
</code></pre><h2 id="setting-up-environmental-variables">Setting Up Environmental Variables</h2>
<ul>
<li>in VSCode, open <code>settings.py</code> (inside your <code>example/</code> folder)</li>
<li>scroll down to the line that declares the <code>SECRET_KEY=</code>, and <strong>cut</strong> the entire line (CMD+x)</li>
<li>open the newly created <code>example_project/.env</code> file (it should be currently empty)</li>
<li>paste in the secret key from above, but be sure to remove the empty spaces around the <code>=</code> and remove all quotes from the value after the <code>=</code>. You could also use any random secret string you like</li>
<li>add in <code>ENVIRONMENT=development</code> on a new line</li>
<li>add in <code>DATABASE_NAME=example-db</code> (or whatever you named your db that I told you to write down for later!)</li>
</ul>
<p>Your .env file should end up looking like this:</p>
<pre><code><span class="hljs-attr">ENVIRONMENT</span>=development
<span class="hljs-attr">SECRET_KEY</span>={{{your_secret_key}}}
<span class="hljs-attr">DATABASE_NAME</span>=example-db
</code></pre><h2 id="reading-in-env-variables">Reading In Env Variables</h2>
<p>Now, we want to make our app read these sensitive or configuration dependent variables, whether it is in production (e.g. deployed to Heroku) or development (working locally on your own computer). To read variables from the <code>.env</code> file you created and filled in the last step:</p>
<ul>
<li>open <strong>settings.py</strong></li>
<li>add <code>from decouple import config</code> at the top (right below the existing import)</li>
<li>where you removed the <code>SECRET_KEY</code> line in <strong>settings.py</strong>, add back this line which will now read it dynamically (from your .env locally or your deployed config vars): <code>SECRET_KEY = config('SECRET_KEY')</code></li>
<li>scroll to the line that says <code>DEBUG=True</code> and replace with the following block: <pre><code><span class="hljs-keyword">if</span> config(<span class="hljs-string">'ENVIRONMENT'</span>) == "production":
  <span class="hljs-keyword">DEBUG</span>=<span class="hljs-keyword">False</span>
<span class="hljs-keyword">if</span> config(<span class="hljs-string">'ENVIRONMENT'</span>) == "development":
  <span class="hljs-keyword">DEBUG</span>=<span class="hljs-keyword">True</span>
</code></pre></li>
<li>below, replace the line with <code>ALLOWED_HOSTS</code> with <code>ALLOWED_HOSTS = ['herokuapp.com','.localhost', '127.0.0.1', '[::1]']</code><blockquote>
<p>If you will be deploying this somewhere else, replace Heroku with your actual app URL</p>
</blockquote>
</li>
</ul>
<h2 id="main-app">Main App</h2>
<p>Back in your terminal (which should still have the environment activated), create a second app within this project called <strong>main_app</strong></p>
<ul>
<li><code>python3 manage.py startapp main_app</code></li>
</ul>
<blockquote>
<p>If you're having issues, make sure:</p>
<ul>
<li>you included the <code>.</code> at the end of the start project command</li>
<li>you might need to use the command <code>python</code> instead of <code>python3</code>; this seemed to occur on newer Macs or machines where <code>python --version</code> and <code>python3 --version</code> both referenced the same release of Python. The error popping up was a <code>raise ImportError</code> with the message <code>couldn't import django no module found</code></li>
</ul>
</blockquote>
<p>Let Django know about your 2nd app:</p>
<ul>
<li>Open the project in VSCode: <code>code .</code></li>
<li>Open your project settings: <code>example_project/settings.py</code></li>
<li>Scroll down to <code>Installed_Apps</code> and add <code>main_app,</code> as the new first element in the array. Remember the comma!
<img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1618548443970/aAONZQhrq.png" alt="Adding main_app screenshot" /></li>
</ul>
<h2 id="development-server">Development Server</h2>
<p>Start up the development server on your local machine</p>
<ul>
<li><code>python3 manage.py runserver</code></li>
<li>Open a browser and visit <a target="_blank" href="http://localhost:8000] to see your site (well, just the fun rocket for now... 🚀">localhost:8000</a></li>
</ul>
<h2 id="development-database">Development Database</h2>
<p>Configure the project for postgres by changing the database entry in <strong>settings.py</strong>, and reading the database name as an environmental variable.</p>
<pre><code># <span class="hljs-built_in">new</span> <span class="hljs-keyword">database</span> <span class="hljs-keyword">info</span>
DATABASES = {
    <span class="hljs-string">'default'</span>: {
        <span class="hljs-string">'ENGINE'</span>: <span class="hljs-string">'django.db.backends.postgresql'</span>,
        <span class="hljs-string">'NAME'</span>: config(<span class="hljs-string">'DATABASE_NAME'</span>),
    }
}
</code></pre><h2 id="migrations">Migrations</h2>
<p>Stage the db migrations (kind of like git adding and committing, but for changes to the database schema as the app evolves over time</p>
<ul>
<li>stop the development server by going back to your terminal and pressing <code>CTRL+C</code></li>
<li><code>python3 manage.py makemigrations</code> 
(this probably won't do anything)</li>
<li><code>python3 manage.py migrate</code>
(this should run through the models in the app like the built in users, and add this info to your database)</li>
</ul>
<p>Restart your server (you can do this regularly while debugging)</p>
<ul>
<li>start your server again: <code>python3 manage.py runserver</code></li>
</ul>
<h1 id="creating-a-home-page">Creating a Home Page</h1>
<h2 id="add-a-home-route">Add a <code>home</code> route</h2>
<p>Routes are defined in the <code>urls.py</code> file(s); you <em>could</em> add all of your routes to <code>example_project/urls.py</code>, but it is best practice for each Django app to define its own and include a link to the new URLs file in the project's URLconf:</p>
<ul>
<li>stop the server: <code>CTRL+C</code></li>
<li><code>touch main_app/urls.py</code></li>
<li>Open the newly created file in VSCode</li>
<li>Set up the imports and basic route using the code below:</li>
</ul>
<pre><code class="lang-python"><span class="hljs-keyword">from</span> django.urls <span class="hljs-keyword">import</span> path
<span class="hljs-keyword">from</span> . <span class="hljs-keyword">import</span> views

urlpatterns = [
    path(<span class="hljs-string">''</span>, views.home, name=<span class="hljs-string">'home'</span>),
    <span class="hljs-comment"># more routes will go here</span>
]
</code></pre>
<blockquote>
<p>Using an empty string <code>""</code> as the first parameter in <code>path()</code> is telling Django to route the base URL with no added paths to the home view (e.g. <code>your-project.com/</code> Later we will add more routes, for example using <code>"about/"</code> as the first argument in the new route, and linking that to an <code>about</code> page, which the user will access by going to <code>your-project.com/about</code></p>
</blockquote>
<p>Now, include this new file in the existing project’s urls.py</p>
<ul>
<li>Open <code>example_project/urls.py</code></li>
<li>Add a new item to the <code>urlpatterns</code> array, making sure the items are separated by commas</li>
<li>Also you will need to import the <code>include</code> function before calling it in the array</li>
<li>It will end up looking like this:</li>
</ul>
<pre><code class="lang-python"><span class="hljs-keyword">from</span> django.contrib <span class="hljs-keyword">import</span> admin
<span class="hljs-comment"># from django.urls import path</span>
<span class="hljs-comment"># add include to your imports from django.urls</span>
<span class="hljs-keyword">from</span> django.urls <span class="hljs-keyword">import</span> path, include

urlpatterns = [
    path(<span class="hljs-string">'admin/'</span>, admin.site.urls),
    <span class="hljs-comment"># add this line below</span>
    path(<span class="hljs-string">''</span>, include(<span class="hljs-string">'main_app.urls'</span>)),
]
</code></pre>
<h2 id="add-a-home-view">Add a home view</h2>
<p>Now that the home <em>route</em> knows where to direct the client, you need to make a home <em>view</em> as a connector between the current route and the correct template to render. Open up your <strong>views.py</strong> and add the following method:</p>
<pre><code><span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">home</span>(<span class="hljs-params">request</span>):</span>
    <span class="hljs-keyword">return</span> render(request, <span class="hljs-string">'home.html'</span>)
</code></pre><h2 id="add-a-home-template">Add a home template</h2>
<p>Starting very simply, you'll create the <code>home.html</code> file</p>
<ul>
<li><code>mkdir main_app/templates</code></li>
<li><code>touch main_app/templates/home.html</code> </li>
<li>Open <code>home.html</code> in VSCode and paste in some valid HTML as a starting point:</li>
</ul>
<blockquote>
<p>Tip: to save a step, in VSCode you could right click on <strong>main_app</strong> in the navigation bar, and select "New File", and then add the line <code>templates/home.html</code>. This will create the templates folder and also the html file in one command</p>
</blockquote>
<pre><code><span class="hljs-meta">&lt;!DOCTYPE <span class="hljs-meta-keyword">html</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">html</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">head</span>&gt;</span>

    <span class="hljs-tag">&lt;/<span class="hljs-name">head</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">body</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">h1</span>&gt;</span>Home<span class="hljs-tag">&lt;/<span class="hljs-name">h1</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">body</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">html</span>&gt;</span>
</code></pre><p>Back in your browser, visit <a target="_blank" href="http://localhost:8000">localhost:8000</a> and refresh to view your sweet new home page. If it doesn't work, remember to restart your Django server:</p>
<ul>
<li><code>python3 manage.py runserver</code></li>
</ul>
<h1 id="done">Done!</h1>
<p>You should now have a functioning (very simple) Django app within your project, viewable on your local machine. To continue building, follow the next steps:</p>
<ul>
<li>Adding more routes, templates, and rendered data <em>(blog post currently being written)</em></li>
<li>Reading from your database <em>(blog post currently being written)</em></li>
<li><a target="_blank" href="https://blog.benhammond.tech/deploying-django-to-heroku">Deploying your Django app on Heroku</a></li>
</ul>
<p><strong>Cover Photo by <a href="https://unsplash.com/@tateisimikito?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText">Jukan Tateisi</a> on <a href="https://unsplash.com/collections/1342728/climbing?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText">Unsplash</a></strong>
<strong>Pedalboard Photo by <a href="https://unsplash.com/@jondubon?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText">Jonathan Dubon</a> on <a href="https://unsplash.com/s/photos/pedalboard?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText">Unsplash</a></strong></p>
]]></content:encoded></item><item><title><![CDATA[Deploying Django to Heroku]]></title><description><![CDATA[This post was written back in the days where Heroku provided free hosting; do not expect any example projects or links to Heroku to work.
In the previous Django - Getting Started post, we set up a new Django app, basically creating a fullstack Python...]]></description><link>https://blog.benhammondmusic.tech/deploying-django-to-heroku</link><guid isPermaLink="true">https://blog.benhammondmusic.tech/deploying-django-to-heroku</guid><category><![CDATA[Django]]></category><category><![CDATA[Python]]></category><category><![CDATA[Heroku]]></category><category><![CDATA[deployment]]></category><category><![CDATA[python projects]]></category><dc:creator><![CDATA[Ben Hammond]]></dc:creator><pubDate>Wed, 09 Jun 2021 05:01:08 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1619500573814/YH8ClxkpH.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p><em>This post was written back in the days where Heroku provided free hosting; do not expect any example projects or links to Heroku to work.</em></p>
<p>In the previous <a target="_blank" href="https://blog.benhammond.tech/django-getting-started">Django - Getting Started</a> post, we set up a new Django app, basically creating a fullstack Python <code>Hello World</code>.</p>
<p>In this post, we will deploy this very basic app to Heroku (a free hosting platform which can run a Django server along with other types of web services). There are <em>many</em> ways to deploy Django, but not a single tutorial I've found online actually worked for me as written. Therefor, I've collected the process that finally did the trick and have outlined it below. A lot of the process comes from our bootcamp instructors, with some customization found several layers deep on Stack Overflow and Reddit (and eventually some personal manipulation). Please let me know if this works for you, and comment below if you have any questions!</p>
<h1 id="heading-deploy-process">Deploy Process</h1>
<h2 id="heading-get-ready">Get Ready</h2>
<ul>
<li>Load up your terminal and <code>cd</code> into the floor of your Django project. Running the command <code>ls</code> should your <code>requirements.txt</code>, <code>manage.py</code>, <code>main_app</code> folder, and <code>example_project</code>, amongst several others. My example is below (some of these files will be created in the following steps)</li>
</ul>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1619496779439/qetCJyLwD.png" alt="Screen Shot of terminal showing example files at floor level" /></p>
<h2 id="heading-log-in-with-heroku-cli">Log In With Heroku CLI</h2>
<p>You will need to have a Heroku account for this process. It used to be free to <a target="_blank" href="https://signup.heroku.com/">sign up</a>, and you should also <a target="_blank" href="https://devcenter.heroku.com/articles/heroku-cli#download-and-install">install the CLI</a> (command line interface) from Heroku to complete the following steps: </p>
<ul>
<li><code>heroku login</code> and press any key to launch a browser window which will allow you to sign in to Heroku</li>
</ul>
<h2 id="heading-procfile">Procfile</h2>
<p>This file will tell Heroku what actions to take when it receives a web request:</p>
<ul>
<li>create a blank file with <em>no file extension</em>: <code>touch Procfile</code><blockquote>
<p>ensure the capitalization is exact on Procfile; if you accidentally mixed the casing up you can fix it using <a target="_blank" href="https://blog.benhammond.tech/renaming-your-github-projects">this method I wrote about</a></p>
</blockquote>
</li>
<li>open the new <code>Procfile</code> in VSCode, and add this line. If you followed along with part one the project is called <code>example</code>. There should be 3 folders inside your local directory, a <code>.env</code>, a <code>main_app/</code> that was created most recently that contains your home route/view and a <code>example/</code> that was created initially and contains a route to your admin, and a <code>settings.py</code>. You want the last one of these.<pre><code>web: gunicorn example.wsgi
</code></pre></li>
</ul>
<h2 id="heading-runtime">Runtime</h2>
<p>This file simply tells Heroku which exact version of Python to use:</p>
<ul>
<li><code>python3 --version</code> displays your current version of Python locally</li>
<li>copy that exact series of numbers; mine is <em><strong>3.9.2</strong></em></li>
<li><code>touch runtime.txt</code> and open it in VSCode</li>
<li>type <code>python-</code> and then paste (no space in between) the exact version. Mine ends up as <em><strong>python-3.9.2</strong></em></li>
</ul>
<h2 id="heading-more-packages">More Packages</h2>
<p>Why re-invent the wheel? Someone already got all this cool stuff working; use it!</p>
<ul>
<li><code>pip3 install django-on-heroku</code></li>
<li><code>pip3 install gunicorn</code></li>
<li><code>pip3 install whitenoise</code></li>
<li>open <strong>settings.py</strong></li>
<li>add <code>import django_on_heroku</code> near the top of your file under your other import statements</li>
<li>scroll the very bottom of <strong>settings.py</strong></li>
<li>add <code>django_on_heroku.settings(locals())</code></li>
</ul>
<h2 id="heading-update-dependencies">Update Dependencies</h2>
<ul>
<li><code>pip3 freeze &gt; requirements.txt</code></li>
</ul>
<h2 id="heading-deploy">Deploy</h2>
<ul>
<li><code>heroku create example --buildpack heroku/python</code><blockquote>
<p>This will try and create a Heroku project with the URL <strong>example.herokuapp.com</strong>. If someone has already taken your preferred name you can change to anything else that you want</p>
</blockquote>
</li>
<li><code>heroku addons:create heroku-postgresql:hobby-dev</code> - adds a free postgreSQL database to your free Heroku account</li>
</ul>
<h2 id="heading-set-heroku-config-vars">Set Heroku Config Vars</h2>
<blockquote>
<p>In <a target="_blank" href="https://blog.benhammond.tech/django-getting-started">the previous post</a>, we set our app's local config vars in an .env file and read them out dynamically. Please ensure your app is set up properly to use the following commands and allow Heroku to store its own config vars</p>
</blockquote>
<ul>
<li><code>heroku config:set DISABLE_COLLECTSTATIC=1</code> - prevents one problem, seems to cause some others 🤦‍♂️ <em>(see note at the end for a potential fix)</em></li>
<li><code>heroku config:set DATABASE_NAME=example-db</code></li>
<li><code>heroku config:set ENVIRONMENT=production</code></li>
<li><code>heroku config:set SECRET_KEY=whatever_your_secret_key_is</code> </li>
</ul>
<h2 id="heading-push-to-production">Push to Production</h2>
<ul>
<li><code>git add .</code></li>
<li><code>git commit -m "fingers crossed"</code></li>
<li><code>git push heroku main</code> - this will push your app up to the newly created Heroku project, rather than pushing to GitHub or some other remote as you normally would with <em>git push origin main</em><blockquote>
<p>If you are still using <code>master</code> branch instead of <code>main</code>, you'll want to swap that word, and it <strong>must</strong> be from one of those two named branches that you initiate the push</p>
</blockquote>
</li>
</ul>
<h2 id="heading-fix-deployed-database">Fix Deployed Database</h2>
<p>Now, you'll tunnel in to the Heroku server with your command line, so you can see what's going on in there and make needed updates to the free database they gave you.</p>
<ul>
<li><code>heroku run bash</code></li>
<li>once inside, run <code>python3 manage.py makemigrations</code>. As before, there might not be any changes after this step</li>
<li><code>python3 manage.py migrate</code> - this brings your database up to date with the built-in user models that Django provides</li>
<li><code>exit</code> gets you out of Heroku and back on to your local machine</li>
</ul>
<h2 id="heading-see-if-it-works">See If It Works!</h2>
<ul>
<li><code>heroku open</code> will launch the site in a browser.<blockquote>
<p>If you have problems, typing <code>heroku logs --tail</code> will let your terminal log all sorts of difficult to decipher messages, including some that will help you figure out what's wrong. To exit this logging feature, use <code>CTRL+C</code>. </p>
</blockquote>
</li>
</ul>
<blockquote>
<p>To view even more helpful errors, follow the instructions on this <a target="_blank" href="https://stackoverflow.com/questions/52311724/500-error-when-debug-false-with-heroku-and-django">stackoverflow</a> where they explain adding <code>DEBUG_PROPAGATE_EXCEPTIONS = True</code> and a <code>LOGGING = { ... }</code> library to their <code>settings.py</code>. And as <a target="_blank" href="https://hashnode.com/@codeperfectplus">Deepak Raj</a> mentioned in the comments, you may also need to run <code>python manage.py collectstatic</code> (or <code>python3 manage.py collectstatic</code>) in your terminal with your project's virtual environment activated before git committing and redeploying. This fixes some of Django's issues finding static files.</p>
</blockquote>
<iframe src="https://giphy.com/embed/dIxkmtCuuBQuM9Ux1E" width="480" height="240" class="giphy-embed"></iframe>


<p>Photo by <a href="https://unsplash.com/@frostroomhead?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText">Rodion Kutsaev</a> on <a href="https://unsplash.com/s/photos/stairs?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText">Unsplash</a></p>
]]></content:encoded></item><item><title><![CDATA[Twitter Time 🤠]]></title><description><![CDATA[Hey all, I'd love if you would follow me on Twitter! That way you won't miss these 🔥hottt🔥 new tech memes I'm pumping out!
https://twitter.com/benhammondmusic/status/1385321601504202753]]></description><link>https://blog.benhammondmusic.tech/twitter-time</link><guid isPermaLink="true">https://blog.benhammondmusic.tech/twitter-time</guid><category><![CDATA[Twitter]]></category><category><![CDATA[vim]]></category><category><![CDATA[command line]]></category><category><![CDATA[terminal]]></category><dc:creator><![CDATA[Ben Hammond]]></dc:creator><pubDate>Thu, 22 Apr 2021 21:10:13 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1619125656995/mlyaVb3JB.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Hey all, I'd love if you would <a target="_blank" href="https://twitter.com/benhammondmusic">follow me on Twitter</a>! That way you won't miss these 🔥hottt🔥 new tech memes I'm pumping out!</p>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://twitter.com/benhammondmusic/status/1385321601504202753">https://twitter.com/benhammondmusic/status/1385321601504202753</a></div>
]]></content:encoded></item><item><title><![CDATA[Building a Hash Map in Python]]></title><description><![CDATA[Until last night I really didn't understand hash tables. I've watched videos and read about them computer science textbooks, but some part of my brain always said "this is confusing and not something I really need to understand.... too many words ......]]></description><link>https://blog.benhammondmusic.tech/build-a-hash-map-in-python</link><guid isPermaLink="true">https://blog.benhammondmusic.tech/build-a-hash-map-in-python</guid><category><![CDATA[Python]]></category><category><![CDATA[data structures]]></category><category><![CDATA[Hashing]]></category><category><![CDATA[stack]]></category><category><![CDATA[Computer Science]]></category><dc:creator><![CDATA[Ben Hammond]]></dc:creator><pubDate>Tue, 20 Apr 2021 21:51:44 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1618955275677/p77bwQ3tw.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Until last night I really didn't understand hash tables. I've watched videos and read about them computer science textbooks, but some part of my brain always said "this is confusing and not something I really need to understand.... too many words ... too many weird terms.... ignore for now". I’m pretty sure I vaguely (and wrongly) thought the items themselves were split apart into chunks and somehow distributed throughout a structure. So I decided it was finally time to dig in and figure it out, and as in my previous posts in this series <a target="_blank" href="https://blog.benhammond.tech/series/comp-sci-coding">studying computer science concepts by coding</a>, the best way to learn it to <strong>actually write some code</strong>. This blog is <strong>not</strong> intended to help you have your own hashmap epiphany; instead it will serve as a way for me to document my thought process and code implementation in hopes it might assist both of us in learning. </p>
<h1 id="some-terminology">Some Terminology</h1>
<p>Understanding a new concept is often frustrating as the resources you reference might include unfamiliar terminology. Even worse are the circular definitions where they “explain” one term you don’t know by using 3 other terms you also have never heard of. Here is a brief run down of the key elements of a hash map and how they relate to one another:</p>
<ul>
<li><strong>Hash (Hash Function)</strong>: a process of converting an item (or <em>value</em>) into a <em>key</em> (or location index) in a predictable way; in this case determining a table location based on some property of the item that needs to be stored there</li>
<li><strong>Hash Map</strong>: a data structure that stores items efficiently by using a <strong>hash</strong> function to determine items locations</li>
<li><strong>Hash Table</strong>: an implementation of a <strong>Hash Map</strong> that specifically uses a table-like structure (as opposed to another type of data structure)</li>
<li><strong>Collision</strong>: when your <strong>Hash</strong> function assigns the same location to multiple items</li>
<li><strong>Probing</strong>: one way of resolving a collision: iterate through the rest of your table (looping around to the beginning if needed) until you find a new, open location for the item that caused the collision</li>
<li><strong>Chaining</strong>: another way of resolving a collision; insert the new colliding item into the location along with the existing item(s), and chain them together using a linked list or similar sub-structure</li>
</ul>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1619110286045/1PcMLAYeP.png" alt="hash table: a diagram showing an array as a table, and collision of items at the same address chained together with a linked list" /></p>
<h1 id="getting-started">Getting Started</h1>
<p>I personally start these computer science coding exercises the same way I start my web application projects: by writing my <strong>user stories</strong>. What exactly will I, as a user of this data structure, want it to do? Once I have these stories in place, I start to determine what <strong>classes</strong> I might use to best represent the entities involved. Then, it's a matter of creating <strong>methods</strong> for the relevant <em>classes</em> that will allow me, as a user, to interact with this object. The final step is creating a test program that can instantiate this class and test its functionality by walking through the execution of each user story. I look forward to learning more about Jest, PyTest, and other similar automated testing tools to improve this part of my process.</p>
<h2 id="user-stories">User Stories</h2>
<p>These are the basic things someone would want a hash map to do:</p>
<ul>
<li>accept a value and store it using a hash function</li>
<li>inform you whether a value is present in the map or not</li>
<li>remove a particular value from the map (if it is present)</li>
</ul>
<h2 id="classes">Classes</h2>
<p>The classes I used in my implementation were pretty limited:</p>
<ul>
<li><code>Hash_Map()</code></li>
<li><code>Stack()</code> - in a <a target="_blank" href="https://blog.benhammond.tech/linked-list-stack-in-python">previous blog post</a>, I had created a <strong>stack</strong> structure utilizing a linked list. It needed some updates, but worked well for resolving collisions using <strong>chaining</strong></li>
<li><code>Node</code> - I had already created this class when creating the <a target="_blank" href="https://blog.benhammond.tech/linked-list-stack-in-python">stack with linked list</a> implementation, and didn't even need to touch it for this exercise. Modularity!</li>
</ul>
<h2 id="functionality">Functionality</h2>
<h3 id="brainstorming">Brainstorming</h3>
<iframe src="https://giphy.com/embed/JUSzU4js22LDdmbX6c" width="480" height="270" class="giphy-embed"></iframe>

<p>I find it helpful to sketch out all of the functionality I can think of that might be needed for each user story. Sometimes it's 1-to-1, but often a story might contain several steps that can be broken down. In my case, all of these <em>functions</em> will technically be <em>methods</em> since they will exist as properties of the classes I defined above. Some of these <strong>class methods</strong> will be <em>public</em> and accessible directly by the user, while others will be <em>private</em> and simply accessed internally by other <strong>class methods</strong>. </p>
<p>To start, I often just create empty placeholder functions which log their own name:</p>
<pre><code class="lang-python"><span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">hash</span>(<span class="hljs-params">self, value</span>):</span>
     <span class="hljs-comment"># placeholder method - but this will eventually do something cool!</span>
     print(<span class="hljs-string">f'Hash'</span>)
     <span class="hljs-keyword">return</span>
</code></pre>
<p>When I start to flesh out those functions, I'll write out the steps in <code>#comments</code> to visualize my logic flow. Another helpful step is to log any passed arguments with some descriptive text: <code>print(f'Hash(): hashing {value}")</code>. Once these are logging properly, it will be much easier to isolate future errors as being in the business logic rather than in the routing or setup.</p>
<h1 id="class-methods-of-hashmap">Class Methods of Hash_Map()</h1>
<h2 id="public-methods">Public Methods</h2>
<h3 id="called-by-the-user-in-our-testpy-or-any-implementation-program">(called by the user in our <code>test.py</code> or any implementation program)</h3>
<ul>
<li><code>insert()</code> - <strong>User Story #1</strong>. Accepts an item, hashes to calculate the key, and places the item in the structure (in my code, a list within <code>Hash_Map</code> called referenced with <code>self.table</code>)</li>
<li><code>search()</code> - <strong>User Story #2</strong>. Accepts an item, and returns a Boolean (<code>True</code> or <code>False</code>) indicating whether the item was found in the hash map.</li>
<li><code>remove()</code> - <strong>User Story #3</strong>. Accepts an item which it removes and returns if found, otherwise returns <code>False</code></li>
</ul>
<h2 id="internal-methods">Internal Methods</h2>
<h3 id="called-from-other-methods-within-the-same-instantiated-class">(called from other methods within the same instantiated class)</h3>
<ul>
<li><code>get_hash()</code> - take an item and calculate a <em>key</em> or table index based on the item's <em>value(s)</em>.<blockquote>
<p>Python Note: <code>hash</code> is already a <strong>keyword</strong> in Python so don't try and name this method <code>hash()</code> as I initially did; it was causing some very strange errors until I realized my mistake</p>
</blockquote>
</li>
<li><code>__str__()</code> - this is a special method in Python, referred to as a "dunder" method (<code>d</code>ouble-<code>under</code>scores), and will allow you to add a customized response when your object is converted to a string (using <code>print(object)</code> or <code>str(object)</code> in the test code for example). Normal Python behavior for objects of instantiated classes would be to return a memory address, but we would like to return a formatted string so we can actually view our beautiful new hash map! </li>
</ul>
<h1 id="class-methods-of-stack">Class Methods of Stack()</h1>
<p>Since our <code>Hash_Map</code> class will rely on inner linked lists (to resolve collisions), we will load up a previously written <a target="_blank" href="https://blog.benhammond.tech/linked-list-stack-in-python">stack with a linked list</a> module using <code>from linked_list_stack import Stack</code>. We can eliminate some of the methods from <code>Stack</code> as they won't be needed, e.g. <code>reverse()</code>, <code>to_list()</code>, and <code>peek()</code>. </p>
<p>We will also need to add some additional methods to complete the user stories' <code>search()</code> and <code>remove()</code> methods, specifically those in table locations that are storing more than one item. So <code>Hash_Map.search()</code> will find the correct location on the <em>table</em> where the item should be, but <code>Stack.contains()</code> will then check every item stored at that particular location's <em>Stack</em> for a match.</p>
<blockquote>
<p>Note: I decided to implement a Stack() on <em>every</em> table location, regardless of the number items at that location. This may be inefficient; I'll continue my studying and see if it's better practice to store an item "naked" at first, and then place the naked item and any new items into a linked list only as needed to resolve collisions.</p>
</blockquote>
<h2 id="public-methods">Public Methods</h2>
<h3 id="called-from-within-hashmap">(called from within <code>Hash_Map</code>)</h3>
<ul>
<li><code>push()</code> - classic stack behavior which plops the value on top of the stack. This continues the work of <code>Hash_Map.insert()</code> in my code</li>
<li><code>contains()</code> - walks node by node through the particular stack looking for a match.</li>
<li><code>remove()</code> - similar to <code>contains()</code> in that it walks through the stack checking for a match (and then removing it if found). However, the implementation gets a bit trickier; first, you must check your match against the <em>next</em> node in line, not the current, since to remove the current you'll need to connect the <em>previous</em> node to the <em>next</em>. This is very similar to the idea of "don't drop your rope" I discussed in <a target="_blank" href="https://blog.benhammond.tech/reverse-a-stack-with-python">Reverse a Stack with Python</a>; you need to take care not to lose track of your <em>previous</em> and <em>next</em> nodes as those are the connections that hold the chain together</li>
<li><code>__str__()</code>: similar to the special method within our hash map class, we need a custom method when an instance of our stack is converted to a string</li>
</ul>
<blockquote>
<p>Note: as in our <a target="_blank" href="https://blog.benhammond.tech/snaketactoe-in-two-hours">Snake Tac Toe</a> game; it's very easy as a beginner to get tripped up by referring to an instantiated <em>object</em>, when you really meant to refer to a particular <em>property</em> of that object. Here, in both <code>contains()</code> and <code>remove()</code>, we need to compare the search item (a string) to the <em>string value</em> of each particular node, not to the node itself. In my code there are two ways I do this, the first is by calling <code>.value</code> on the node, other places I use <code>str(node)</code>, which internally calls the special <code>__str__()</code> method we added. Either way, you must be sure you are comparing values of the same <strong>type</strong>. You can confirm a variable's type by logging: <code>print(type(a_variable))</code> and seeing what you are actually dealing with. </p>
</blockquote>
<pre><code class="lang-python"><span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">contains</span>(<span class="hljs-params">self, item</span>):</span>
        current_head = self.head
        <span class="hljs-keyword">while</span> current_head != <span class="hljs-literal">None</span>:

            <span class="hljs-comment"># 👎This won't work 👎</span>
            <span class="hljs-comment"># if current_head == item: return True</span>

            <span class="hljs-keyword">if</span> current_head.value == item: <span class="hljs-keyword">return</span> <span class="hljs-literal">True</span>
            <span class="hljs-comment"># 👍 But this will! 👍  </span>

            current_head = current_head.next

        <span class="hljs-keyword">return</span> <span class="hljs-literal">False</span>
</code></pre>
<h1 id="testing-our-hash-map">Testing our Hash Map</h1>
<p>I try to keep my class definitions in their own <code>.py</code> files when possible. They can easily be imported into other files and scripts as needed. For this project, I am using 3 files:</p>
<ol>
<li><code>hash_table.py</code> - contains our new hash map class, utilizing a <em>table</em> full of <em>stacks</em> internally</li>
<li><code>linked_list_stack.py</code> - contains our <code>Stack</code> class and the very simple <code>Node</code> class, which is used to create the links in our linked list stack</li>
<li><code>test.py</code> - starts with some arbitrary data; instantiates an object of class <code>Hash_Map()</code>; then adds, finds and removes various data from that structure</li>
</ol>
<p>To import one file into another, you can just use the <code>import</code> statement (as opposed to JavaScript where you must also explicitly <code>export</code> within the file to be imported).</p>
<h1 id="performance-and-big-o">Performance and Big O()</h1>
<p>A rule of thumb recommendation is to make your table about 30% larger than your data set when hash mapping; this helps to minimize wasted resources by having the table too large and with lots of empty locations or too small and resulting in many collisions. We therefor send in the <code>TABLE_SIZE</code> as a calculated argument using this line: <code>TABLE_SIZE = int(len(data) * 1.3)</code></p>
<p>A properly hashed table should be <em>nearly</em> as fast as a basic array, since the values are mostly a single lookup. This means the time complexity would be BigO(1) for arrays, and also for absolute best-case hash maps. However, if the table you choose is too small, there will be more collisions (multiple items overlapping and stored in the same locations). Our <code>insert()</code> method still will be BigO(1) since it's just the single operation to get to the correct location and the second operation to push our item on to the top of that stack. Retrieving and deleting that item though could require walking through every item within a particular stack. It's possible that every item we need to store could somehow end up in the same table location, meaning our hash map is really just one giant linked list. In this worst-case scenario, our hash map would have BigO(n), since it would walk through every single item we added to the structure. </p>
<h1 id="more-to-come">More To Come...</h1>
<p>Overall, this implementation is very simple, as my goal was really to <em>just to get it working</em>. The hash algorithm I chose is not very sophisticated; it tallies the ascii value of a string's characters and then chops the sum down until that number fits inside the table. There are many other hash methods, which can optimize performance and minimize required space in memory. I'm excited to have this basic version up and going, and a more complete mental model of what is happening. I plan to continue implementing these data structures and algorithms in the languages I'm learning, and would love to hear from you if you have any comments, corrections or feedback. Thanks for reading! </p>
<p>To view the code or suggest changes, <a target="_blank" href="https://github.com/benhammondmusic/hash_table">visit the GitHub repo</a>. </p>
<p>Photo in cover image by <a href="https://unsplash.com/@janbaborak?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText">Jan Baborák</a> on <a href="https://unsplash.com/s/photos/hashtag?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText">Unsplash</a></p>
]]></content:encoded></item><item><title><![CDATA[Renaming Your GitHub Projects]]></title><description><![CDATA[If your projects start generically and later grow into something cool (or the focus changes completely) it's easy to change their names and stay organized. It requires renaming locally: mv {{{ OLD_FOLDER_NAME/ }}} {{{ NEW_FOLDER _NAME/ }}}, renaming ...]]></description><link>https://blog.benhammondmusic.tech/renaming-your-github-projects</link><guid isPermaLink="true">https://blog.benhammondmusic.tech/renaming-your-github-projects</guid><category><![CDATA[GitHub]]></category><category><![CDATA[Git]]></category><category><![CDATA[terminal]]></category><category><![CDATA[command line]]></category><category><![CDATA[remote]]></category><dc:creator><![CDATA[Ben Hammond]]></dc:creator><pubDate>Wed, 14 Apr 2021 16:55:12 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1618371560004/F6B0JOcI1.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<blockquote>
<p>If your projects start generically and later grow into something cool (or the focus changes completely) it's easy to change their names and stay organized. It requires renaming locally: mv {{{ OLD_FOLDER_NAME/ }}} {{{ NEW_FOLDER _NAME/ }}}, renaming remotely on GitHub (via their web interface), and updating the connections between the two: git remote set-url origin {{{NEW<em>GITHUB</em>.GIT_URL}}}</p>
</blockquote>
<h1 id="two-repos">Two Repos</h1>
<p>The biggest thing to understand when you're first starting off with git and GitHub is that <strong>they are two different things!</strong> Like Java and JavaScript, just because one name contains the other doesn't mean they are the same. <strong>Git</strong> is the <em>program</em> that runs on your machine and keeps track of incremental changes to selected projects. <strong>GitHub</strong> is an online company (owned by Microsoft now) that allows you to store and share your <strong>git</strong> repositories online. It also allows for all sorts of wonderful collaboration via the web interface.</p>
<p>Specifically, if you have a project on your computer that's also on GitHub, you are managing two distinct git repos. You are likely keeping the two syncing by using <code>git pull</code> and <code>git push</code> from your local machine. If you decide to change the name of a project, there are a few simple but crucial steps you need to take to keep everything consistent for your own organization and for this connection to be maintained.</p>
<h1 id="rename-the-remote-repo">Rename the remote repo</h1>
<p>You could do these steps in any order, but I like to start with the visual interface provided by the GitHub website. Head to the GitHub repo page for the project you'd like to change the name of.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1618416364166/1RxxsYcHL.png" alt="Screen Shot of GitHub project" /></p>
<h2 id="rename-on-github-using-the-web-interface">Rename on GitHub using the web interface</h2>
<ul>
<li>Click on "settings" (next to "insights" on the horizontal navbar)</li>
<li>Type in a new name in the "Repository Name" box. In general, avoid using spaces or weird symbols in any file or project names</li>
<li>If that name is available (i.e. you haven't used that exact name before) you'll see a green check mark, and you can then click "Rename"</li>
</ul>
<h2 id="copy-the-new-url">Copy the new URL</h2>
<ul>
<li>You'll be redirected back to your repo page</li>
<li>Click on the green "Code" button/dropdown</li>
</ul>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1618416713246/h-iiV4h-9.png" alt="Screen Shot showing github green button" /></p>
<ul>
<li>Click the clipboard icon button, which will copy the project's .git url to your computer's clipboard. This will be the new URL for your <em>remote</em> repo.</li>
</ul>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1618416827836/OxC_Zuxe1.png" alt="Screen Shot showing clipboard copy button for new URL" /></p>
<h1 id="rename-the-local-repo-on-your-computer-using-terminal">Rename the local repo on your computer (using terminal)</h1>
<p>Back on your computer, load up your terminal and <code>cd</code> your way into your local project folder. Note: by default .git files are hidden, meaning using <code>ls</code> in terminal will not show your .git folder. You can type <code>ls -a</code> to confirm you're in the right folder when you see your .git folder displayed.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1618417184900/Ls02aWvj7.png" alt="Screen Shot showing terminal displaying hidden files using ls -a" /></p>
<h2 id="set-your-new-git-remote">Set your new git remote</h2>
<ul>
<li>Once you confirm you're in the correct folder in your terminal, type <code>git remote -v</code>. This will display all of your remotely connected .git repos. Below mine only shows the old GitHub repo, but if you have deployed to Heroku or Netlify using their CLIs, you will see other remote locations beside <code>origin</code></li>
</ul>
<pre><code class="lang-text">origin    https://github.com/benhammondmusic/generic-project-1.git (fetch)
origin    https://github.com/benhammondmusic/generic-project-1.git (push)
</code></pre>
<ul>
<li>Since these old remote links are now out of date from our GitHub project's new name, we need to <em>set</em> the new <em>url</em> .
<code>git remote set-url origin {{{PASTE_THE-COPIED_GITHUB_URL_HERE}}}</code>. In my example, the full command is <code>git remote set-url origin https://github.com/benhammondmusic/cool-new-name.git</code>. Make sure your URL ends with ".git". </li>
<li>If you have a different setup, or are trying to change a different repo (for instance on Heroku, etc), replace the word <code>origin</code> with the name of of your remote location (e.g. <code>heroku</code>).</li>
<li>Test that the git remotes have been updated properly by again running <code>git remote -v</code>. It should show the updated remote URLs:</li>
</ul>
<pre><code class="lang-text">origin    https://github.com/benhammondmusic/cool-new-name.git (fetch)
origin    https://github.com/benhammondmusic/cool-new-name.git (push)
</code></pre>
<blockquote>
<p>Important! If you have you collaborators on this project, they will each need to update their local git remotes using this same command!</p>
</blockquote>
<h2 id="rename-your-local-repo">Rename your local repo</h2>
<p>You could leave everything as is now, and be able to <code>git push</code> and <code>git pull</code> your changes between the local and remote repos with no problem. However, personally I like to update my project's local folder to match the new name of the remote repo.</p>
<ul>
<li>Confirming you are still in the same folder as the above step (at the level where your .git file is), type <code>cd ..</code> to go up one level.</li>
<li>Confirm you're in the right place by typing <code>ls</code> and viewing your old project folder name in the displayed list of folders and files</li>
<li>Change the folder's name by using <code>mv {{{ OLD_FOLDER_NAME/ }}} {{{ NEW_FOLDER _NAME/ }}}</code>. In my example, my command is <code>mv generic-project-1/ cool-new-name/</code></li>
<li>Again, confirm by using <code>ls</code> and your new project name should be displayed instead of the old name</li>
</ul>
<p><strong>Things to note:</strong></p>
<ul>
<li><code>mv</code> command can be used to <em>move</em> files and folders, or to <em>rename</em> them. It's weird, but it's just the way it is</li>
<li>Confirm that both of your {{{ FOLDER_NAME/ }}} names above end in a trailing slash: <code>mv generic-project-1/ cool-new-name/</code> not <em>mv generic-project-1 cool-new-name</em>. The trailing slash tells the command it's a folder, not a file</li>
</ul>
<h1 id="bonus-points-update-the-github-projects-about">Bonus Points: Update the GitHub project's "About"</h1>
<p>This is one aspect of GitHub that gets neglected but can make a big difference in your presentation. You should, of course, be including a <code>README.md</code> file to explain the whats, whys and hows of your projects, but don't forget to keep the little sidebar "about" section up to date as well. </p>
<ul>
<li>Click the gear icon (not the gear on the "settings" tab though) and update the description field. </li>
<li>Make sure you include any URL where the app is deployed as well! </li>
</ul>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1618419209001/ANy1pCsRd.gif" alt="update about github.gif" /></p>
<p>If you need help deploying, I have several blog posts detailing how to complete that process:</p>
<ul>
<li><a target="_blank" href="https://blog.benhammond.tech/deploying-a-create-react-app-to-netlify">Deploying a Create-React-App to Netlify</a></li>
<li><a target="_blank" href="https://blog.benhammond.tech/deploying-a-node-express-backend-to-heroku">Deploying an Express Backend to Heroku</a></li>
<li><a target="_blank" href="https://blog.benhammond.tech/deploying-a-react-app-to-heroku">Deploying a React App Frontend to Heroku</a></li>
<li><a target="_blank" href="https://blog.benhammond.tech/connecting-your-deployed-frontend-backend-and-mongodb-atlas-database">Connecting MERN Fullstack Heroku Deployments</a></li>
</ul>
<p>Hopefully this has helped you in keeping your projects updated and organized! Let me know if you have any issues or suggestions on better ways to do this. Thanks!</p>
<p>Photo by <a href="https://unsplash.com/@imperiumnordique?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText">Helena Hertz</a> on <a href="https://unsplash.com/@imperiumnordique?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText">Unsplash</a></p>
]]></content:encoded></item><item><title><![CDATA[Reverse() a Stack with Python.]]></title><description><![CDATA[Having created a stack using a linked list in Python (more details in  my previous blog post ), I was excited to expand on the functionality and add a reverse() method to this data structure class. Again, as with many of these basic computer science ...]]></description><link>https://blog.benhammondmusic.tech/reverse-a-stack-with-python</link><guid isPermaLink="true">https://blog.benhammondmusic.tech/reverse-a-stack-with-python</guid><category><![CDATA[Python]]></category><category><![CDATA[data structures]]></category><category><![CDATA[Computer Science]]></category><category><![CDATA[stack]]></category><category><![CDATA[#codenewbies]]></category><dc:creator><![CDATA[Ben Hammond]]></dc:creator><pubDate>Tue, 13 Apr 2021 20:15:28 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1618344843292/mewDyCqDq.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Having created a <strong>stack</strong> using a linked list in Python (more details in  <a target="_blank" href="https://blog.benhammond.tech/linked-list-stack-in-python">my previous blog post</a> ), I was excited to expand on the functionality and add a <code>reverse()</code> method to this data structure class. Again, as with many of these basic computer science concepts, the idea is easy to understand, but the actual code can be challenging to write, and presents severals "gotchas" along the way that might trip you up. </p>
<h2 id="big-picture">Big Picture</h2>
<p>A stack is a collection of data where items are added and removed according to the "LIFO" acronym: <em>Last In, First Out</em>. Meaning, the last item that was pushed to the stack will be the first one to be popped off. I think of it like a Pez dispenser.</p>
<iframe src="https://giphy.com/embed/NmovCS9E8pVcY" width="480" height="360" class="giphy-embed"></iframe>

<p>So what would we do if we needed to turn the whole thing around? A simple singly-linked list contains a series of nodes, and those nodes only contain 2 piece of information: the node's <code>value</code>, and the location of the <code>next</code> node in the list. To reverse, we would have to make all of our <code>next</code> pointers point to the previous node. This is where things get tricky however, because we can simply swap them or we would lose track of the <code>next</code> node's <code>next</code>, and the remainder of the list would just be floating around our computer somewhere, untethered and unreachable. </p>
<h2 id="dont-drop-the-rope">Don't Drop the Rope!</h2>
<p>I've used this rock climbing metaphor previously but it's so fitting: when a climber leads and reaches the top of a pitch, they need to be able to get back down to the ground. Their belayer can lower them, but now they've left a carabiner at the top of the pitch (the one that's functioned as their "pulley" and allowed them to be softly lowered to the ground). A common solution to this problem is <em>rappelling</em> down (a.k.a. <em>abseiling</em>), instead of belaying; rather than the climber leaving gear at the top, they actually thread their rope through the fixed-bolt into the mountainside, and then are able to use specialized gear to carefully lower themselves down the fixed rope, finally pulling the rope free from it's threaded anchor once they're safely on the ground.</p>
<iframe src="https://giphy.com/embed/3o7TKoLSUWybe97uWA" width="480" height="330" class="giphy-embed"></iframe>

<p>Climbing specifics aside, this switch over from <strong>belay</strong> to <strong>rappel</strong> is extremely dangerous if done incorrectly, and requires careful attention to each simply step. At some point between untying their knot from their harness (on belay) and tying it back on (on rappel), the climber will be unprotected by their safety line, and could easily fall and be killed. For this reason, climbers carry small loops of nylon and extra carabiners, and are able to go "in direct" and have a 2nd, temporary connection to the mountainside that will protect them if they fall. To stretch this metaphor further, a climber will literally tie the end of their safety rope to their harness somewhere, to prevent the untied rope from slipping through the hands or gear and falling to the ground, leaving them stranded on the pitch.</p>
<p>Though far less dramatic, the same applies when reversing a linked-list; you must have a temporary variable to hold the "rope" of your remaining list, while you switch the current node's <code>next</code> pointer to reference the <code>previous</code> node, and the <code>next</code> node's <code>next</code> to reference the current node. </p>
<pre><code>            <span class="hljs-attr">temporary</span> = current.next
            <span class="hljs-attr">current.next</span> = previous
            <span class="hljs-attr">previous</span> = current
            <span class="hljs-attr">current</span> = temporary
</code></pre><p>Notice how at each step, the 2nd variable becomes the first variable of the next line. This also reminds me of how you swap the primitive values of two variables:</p>
<pre><code><span class="hljs-comment"># task: swap the values of variables a and b</span>

<span class="hljs-attr">a</span>=<span class="hljs-number">1</span>
<span class="hljs-attr">b</span>=<span class="hljs-number">2</span>

<span class="hljs-comment"># you can't just use a = b or b = a to start because then you'll lose the value stored in the overwritten variable</span>

<span class="hljs-comment"># instead, use a temporary variable</span>

<span class="hljs-attr">x</span> = a
<span class="hljs-attr">a</span> = b
<span class="hljs-attr">b</span> = x
</code></pre><p>We are doing basically the same thing in our linked-list loop, but each variable is instead a node with a <code>value</code> and a pointer to <code>next</code>; this loop continues until you reach the end of the linked list; </p>
<h2 id="linked-list-reversal-function-vs-class-method">Linked List Reversal: Function vs Class Method</h2>
<p>There is one final step left: assign the list's <code>head</code> value to point to the last item you found in the list, which would be our <code>previous</code>. I followed along with  <a target="_blank" href="https://techsloth.io/crushing-tech-interviews-with-the-linked-list-reversal-pattern">a great blog post</a>  in working through this concept, and this last step was what tripped me up the most; I couldn't quite figure out why they were sending in the <code>head</code> node and returning just the last <code>previous</code> node. Some experimenting allowed me to discover the missing step (for my implementation) of explicitly assigning the new <code>head</code>. As you can read in the comment's of their post; their particular case was not adding a <code>reverse()</code> method to a listed link stack class like mine, instead they were making a function which would reverse a linked list given that list's first node. In that case you'd presumably want the result of the function to be the new "first" node (the <code>head</code>) of this newly reversed linked list.</p>
<h2 id="the-complete-reverse-method">The complete <code>reverse()</code> method</h2>
<pre><code class="lang-python">    <span class="hljs-comment"># reverse: reverse the linked list</span>
    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">reverse</span>(<span class="hljs-params">self</span>):</span>

        <span class="hljs-comment"># start at the top of the stack</span>
        current = self.head

        <span class="hljs-comment"># initialize</span>
        temporary = <span class="hljs-literal">None</span>
        previous = <span class="hljs-literal">None</span>

        <span class="hljs-comment"># walk through the linked list</span>
        <span class="hljs-keyword">while</span> (current != <span class="hljs-literal">None</span>):

            <span class="hljs-comment"># don't let go of your rope (the rest of your linked list)!</span>
            temporary = current.next

            <span class="hljs-comment"># swap the link direction</span>
            current.next = previous

            <span class="hljs-comment"># attach current to the next nodes "previous"</span>
            previous = current

            <span class="hljs-comment"># step to what used to be the "next" node; which is stored in temp</span>
            current = temporary


        <span class="hljs-comment"># once reached the end/bottom of the stack</span>
        <span class="hljs-comment"># it's important to make that bottom item the new head</span>
        self.head = previous
</code></pre>
<p>Thanks for following along! Hopefully you didn't get too lost on my rock climbing analogies, and hopefully you will do some more research before implementing my rock climbing advice in real life! No warranties or guarantees implied of the performance of your climbing or data structure performance. </p>
<p>Photo in cover image by <a href="https://unsplash.com/@brookanderson?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText">Brook Anderson</a> on <a href="https://unsplash.com/s/photos/caribiner?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText">Unsplash</a></p>
]]></content:encoded></item><item><title><![CDATA[MacBook Emojis! 😀]]></title><description><![CDATA[<^ CTRL> + <⌘ CMD> + <SPACE BAR> loads the MacOS emoji menu

Emojis are exciting; honestly one of the most reliable to ways to convey emotional content in the written word. They might not be viewed as professional, but I honestly feel that moderate u...]]></description><link>https://blog.benhammondmusic.tech/macbook-emojis</link><guid isPermaLink="true">https://blog.benhammondmusic.tech/macbook-emojis</guid><category><![CDATA[emoji]]></category><category><![CDATA[macOS]]></category><category><![CDATA[osx]]></category><category><![CDATA[macbook]]></category><category><![CDATA[mac]]></category><dc:creator><![CDATA[Ben Hammond]]></dc:creator><pubDate>Sun, 11 Apr 2021 15:37:30 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1618155362369/gy4dzW3Mt.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<blockquote>
<p><code>&lt;^ CTRL&gt;</code> + <code>&lt;⌘ CMD&gt;</code> + <code>&lt;SPACE BAR&gt;</code> loads the MacOS emoji menu</p>
</blockquote>
<p>Emojis are exciting; honestly one of the most reliable to ways to convey <em>emotional</em> content in the written word. They might not be viewed as professional, but I honestly feel that moderate use can increase productivity and streamline communications. </p>
<p>Until recently, I'd only been incorporating emojis into my writing while typing on my iPhone; iOS makes it clear and easy how to access the menu. Now that I am blogging, writing <code>//comments</code> and texting regularly from my laptop, I desired a way to implement emojis directly from MacOS. The first solution I found was  <a target="_blank" href="https://matthewpalmer.net/rocket/">Rocket</a> , a clever app that listens to your keyboard input and offers a popup menu when you type <code>:</code> followed by a search term. This works well, despite popping-up unwanted during some JS ternary operator implementations. The other issue was the lack of search; I believe he offers a paid upgrade to add searching, but as it stands the emoji pop-up only displays a few options based off of the actual name for each emoji. It's difficult to get the exact emoji you want at times, since the real names for each emoji can be somewhat obscure.</p>
<p>Then I happened upon a blog post that showed what I'd been seeking all along: built-in MacOS support for emojis! Simply press <code>&lt;CONTROL&gt;</code> + <code>&lt;COMMAND&gt;</code> + <code>&lt;SPACE BAR&gt;</code>, and a floating menu pops up with categorized tabs, search functionality, and your frequently used (my top 5 right now: 💩🤦‍♂️😀🐍😂). I've since disabled Rocket from loading on startup, but I will leave it installed for now, just in case. Otherwise, this "hidden" secret functionality seems perfect. What other "secret" keyboard shortcuts have you been using? 👩‍💻🎸</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1618154568801/y-01LBe0X.gif" alt="macbook emoji.gif" /></p>
<p>Photo in cover image by <a href="https://unsplash.com/@vbcreative?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText">Vanessa Bucceri</a> on <a href="https://unsplash.com/s/photos/emoji?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText">Unsplash</a></p>
]]></content:encoded></item><item><title><![CDATA[Snake_Tac_Toe in Two Hours]]></title><description><![CDATA[Snake_Tac_Toe

Python Tic-Tac-Toe on the Command Line, coded in 2 hours as a group project

Today in class we dug into object-oriented programming in Python using classes. I had recently investigated this a bit on my own while implementing a  stack u...]]></description><link>https://blog.benhammondmusic.tech/snaketactoe-in-two-hours</link><guid isPermaLink="true">https://blog.benhammondmusic.tech/snaketactoe-in-two-hours</guid><category><![CDATA[Games]]></category><category><![CDATA[python beginner]]></category><category><![CDATA[Python]]></category><category><![CDATA[command line]]></category><category><![CDATA[Object Oriented Programming]]></category><dc:creator><![CDATA[Ben Hammond]]></dc:creator><pubDate>Sun, 11 Apr 2021 04:46:39 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1618115567460/zCFKFfUhP.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h1 id="snaketactoe">Snake_Tac_Toe</h1>
<blockquote>
<p>Python Tic-Tac-Toe on the Command Line, coded in 2 hours as a group project</p>
</blockquote>
<p>Today in class we dug into object-oriented programming in Python using classes. I had recently investigated this a bit on my own while implementing a  <a target="_blank" href="https://blog.benhammond.tech/linked-list-stack-in-python">stack using linked lists in Python</a>. Though I would have preferred a different game since I somewhat recently coded <a target="_blank" href="https://blog.benhammond.tech/react-tac-toe">React-Tac-Toe</a>, it was another fun experience to work as a group; thinking out loud through our issues and bouncing ideas off of one another. Also, since the basics of the game were pretty clear, we could focus more on how to implement classes, functions and magic "dunder" methods like <code>__str__()</code> and how they can be helpful in a Python application. </p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1618115689143/o94AyT9W_.gif" alt="Screen grab of command line game play" /></p>
<h2 id="tech">Tech</h2>
<ul>
<li>Python</li>
</ul>
<h2 id="classes">Classes</h2>
<p>We were required to implement classes in this Python coding exercise, so our group started by brainstorming the various entities present in a game of Tic Tac Toe, and which of those would be best represented by a class, vs. properties of a class instance. The two classes we decided on were:</p>
<ul>
<li><code>Game</code>: would represent rounds of the game, and would keep track of whose turn it was, which squares were filled, etc.</li>
<li><code>GamePiece</code>: would represent the individual objects filling each square on the <code>Game</code>'s <code>gameboard[]</code> list.</li>
</ul>
<h2 id="tricky-bits">Tricky Bits</h2>
<h3 id="making-game-playable-in-command-line">Making Game "Playable" in Command Line</h3>
<p>There's a neat little snippet of code that's been helpful while we are mainly coding on the terminal with no GUI or view engine. It's a one-liner (plus a package import) that clears the terminal (similar to <strong>CMD+K</strong> or <code>clear</code>). If you'd like your code to display a clear screen (it actually just autoscrolls it down), simply:</p>
<ol>
<li><code>import os</code></li>
<li>use <code>os.system('clear')</code> (on Mac)</li>
</ol>
<p>We utilized this to clear the screen before each redraw of the screen in our <code>Game</code>'s <code>while()</code> loop. </p>
<h3 id="checking-for-win">Checking for Win</h3>
<p>The logic behind win a "win" relies on iterating over an array of all possible win conditions, and checking the current state of the board against each. We struggled getting these checks to work, until we finally isolated the bug: we were comparing the string <code>'O'</code> or <code>'X'</code> to the <em><strong>object</strong></em> <code>GamePiece</code>, rather than the string <em><strong>property</strong></em><code>GamePiece.mark</code> as we intended.</p>
<h3 id="checking-for-tie">Checking for Tie</h3>
<p>Another issue with the logic involved the tie condition; there were two ways we discussed that could check for a tie:</p>
<ul>
<li>Track total played pieces, and if it reaches 9 and there's no winner, it's a tie</li>
<li>Scan for empty spaces; once 0 (and no winner), it's a tie</li>
</ul>
<p>I had implemented the first technique in my React-Tac-Toe, but for this code the 2nd seemed quicker to integrate. </p>
<h3 id="cats-eye">Cat's Eye</h3>
<p>I truly enjoy working in groups, and in particular pair-programming exercises. Being able to have one person "think" through their typing, while the other can theorize more abstractly, and even go off to google for deeper research without interrupting the coding flow. Obviously another key benefit is simply having more eyeballs on your code; as long as communication is good and you aren't second-guessing each other too often, you can exterminate bugs before they can take hold, saving hours if not days of frustration. I look forward to continuing collaboration with fellow member of my General Assembly cohort, during the remainder of this course and long after. </p>
<h3 id="code">Code</h3>
<ul>
<li><a target="_blank" href="https://github.com/benhammondmusic/snake_tac_toe">GitHub</a></li>
</ul>
<p>Photo in cover image by <a href="https://unsplash.com/@sidverma?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText">Sid Verma</a> on <a href="https://unsplash.com/?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText">Unsplash</a></p>
]]></content:encoded></item><item><title><![CDATA[Linked List Stack in Python]]></title><description><![CDATA[Part of my "Learn Computer Science by Coding" series; also check out some of my posts on bubble sort, binary search and recursive merge sort algorithms using JavaScript.

Coding in Python instead of JavaScript
We have been completing modules on compu...]]></description><link>https://blog.benhammondmusic.tech/linked-list-stack-in-python</link><guid isPermaLink="true">https://blog.benhammondmusic.tech/linked-list-stack-in-python</guid><category><![CDATA[data structures]]></category><category><![CDATA[Computer Science]]></category><category><![CDATA[Python]]></category><category><![CDATA[classes]]></category><category><![CDATA[Object Oriented Programming]]></category><dc:creator><![CDATA[Ben Hammond]]></dc:creator><pubDate>Fri, 09 Apr 2021 21:20:03 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1618002962287/NuOT_UpzM.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<blockquote>
<p>Part of my "Learn Computer Science by Coding" series; also check out some of my posts on <a target="_blank" href="https://blog.benhammond.tech/studying-compsci-by-actually-writing-code">bubble sort, binary search</a> and <a target="_blank" href="https://blog.benhammond.tech/recursive-merge-sort-in-vanilla-javascript">recursive merge sort</a> algorithms using JavaScript.</p>
</blockquote>
<h2 id="coding-in-python-instead-of-javascript">Coding in Python instead of JavaScript</h2>
<p>We have been completing modules on computer science topics in the course of my General Assembly bootcamp, and I have been making a point to implement some of the most famous data structures and algorithms from scratch, using actual code. This process continues to be super helpful in understanding the concepts at a deeper level, and I'd recommend it to anyone learning (or relearning) these ideas. </p>
<p>Since we just started a new course unit on Python (which is famous for its ability to work with data), I thought it'd be an interesting challenge to step out of my new JavaScript comfort zone and explore Python while also learning these data structure concepts. I had used Python a few years ago to cobble together my  <a target="_blank" href="https://blog.benhammond.tech/giguploader">GigUploader</a>  project, but my coding skills in general are so much stronger now that I was excited to see my progress in this language.</p>
<h3 id="getting-started">Getting Started</h3>
<p>I already had a pretty decent mental model for arrays, linked lists, stacks and queues, from various college courses and several videos. Making the connection between how they work <em>in theory</em> to how they work <em>in code</em> can be quite a challenge however, and I did rely on several resources to finally get this implemented. The first was this tutorial implementing a <a target="_blank" href="https://learnersbucket.com/tutorials/data-structures/implement-stack-using-linked-list/">linked list stack in Javascript</a> which was super useful as a "cheat sheet" that I needed to translate into Python. They also clearly outlined the methods that a stack offers, which served as my user stories to get the ball rolling:</p>
<ul>
<li>Push: Adds the item in the stack at the top.</li>
<li>Pop: Removes the top item from the stack and returns it.</li>
<li>Peek: Shows the top item from the stack.</li>
<li>toArray: Convert the stack to the array.</li>
<li>size: Returns the size of the stack.</li>
<li>isEmpty: Returns true if stack is empty, false other wise.</li>
<li>clear: Clears the stack.</li>
</ul>
<p><em>from <a target="_blank" href="https://learnersbucket.com/tutorials/data-structures/implement-stack-using-linked-list/">learnersbucket.com</a></em></p>
<h2 id="using-classes">Using Classes</h2>
<p>Despite covering the idea of classes early on, the concept certainly isn't front and center in my programming brain. Even once we started working in React with the idea of small, self-container reusable object, we touched on the "old way" of using class-based components and then quickly shifted focus to using hooks and functional components. I have even heard that JavaScript classes aren't technically classes, and simply exist to appease programmers migrating in from other class-based object-oriented languages like Java and C. This is somewhat unclear to me since JavaScript is considered "object-oriented", however the objects it's oriented towards are at the <em>prototypes</em> level, rather than the class level.</p>
<h3 id="two-classes-stack-and-node">Two classes: <strong>Stack</strong> and <strong>Node</strong></h3>
<p>In any case, this low-level concept of a linked list seemed a good opportunity for using classes, and I assumed Python must use them as a true object-oriented language. In object-oriented programming (OOP), you can define a class for each conceptual entity in your program (similar to how we define our models/schemas when working with MongoDB). In a stack implemented using a linked list, there's the <strong>stack</strong> at the top level, and then the <strong>links</strong> of the linked list (usually referred to as 'nodes'). </p>
<h3 id="python-class-idiosyncrasies-init-and-self">Python Class Idiosyncrasies:  <code>__init()__</code> and <code>self</code></h3>
<p>This one took some careful copy-pasting 😂, but eventually it became clear what was actually going on: the constructor method (the one that automatically runs once any time a member of a class is instantiated) is called <code>__init()__</code> in Python (short for initialization?). In JavaScript we instead call it the <code>constructor</code>. </p>
<p>Also interesting was how we need to explicitly include <code>self</code> as an argument within the definition of every class method. <code>self</code> seems to function very similarly to JavaScript's <code>this</code>, but must be present in those incoming arguments.</p>
<h2 id="class-methods">Class Methods</h2>
<p>Beyond the actual coding differences, it's been a small challenge to try and maintain a pythonic style: for instance using the word <code>list</code> instead of <code>array</code>, and naming variables and functions with snake_case and not camelCase (e.g. <code>to_list()</code> in Python vs. <code>toArray()</code> in JavaScript). Now that I had my "nouns" defined with classes <code>Node</code> and <code>Stack</code>, it's time to flesh out the "verbs" using class methods.</p>
<h3 id="isempty"><code>is_empty()</code></h3>
<p>This was the first method I implemented, as the logic from several other methods would depend on the state of the stack.  It was also pretty simple: check if the current <code>head</code> is equal to <code>None</code>, and if so it's empty! Note: <code>None</code> in Python is similar (but not identical) to the ideas of <code>undefined</code> and <code>null</code> in JavaScript. It's a <em><strong>thing</strong></em> that represents <em><strong>nothing</strong></em>.</p>
<h3 id="push-and-pop"><code>push()</code> and <code>pop()</code></h3>
<p>I'll admit I needed to frequently reference the tutorial to get these working; I just had a bit of trouble wrapping my head around the syntax and how <code>self</code>, <code>head</code>, <code>next</code> and <code>value</code> were all connected. Here's what I ended up with:</p>
<ul>
<li><code>self</code>: the current stack<ul>
<li><code>head</code>: the "top" of the stack<ul>
<li><code>next</code>: the pointer that connects the current node to the next one<ul>
<li><code>value</code>: the data or item that is being stored in each node</li>
</ul>
</li>
</ul>
</li>
</ul>
</li>
</ul>
<p>The processes for <code>push()</code> and <code>pop()</code> are very similar, and require care to not break the links of the chain. It actuals reminds me of rock-climbing, when you are switching from secure anchor #1 to secure anchor #2, you must assure #2 is connected before you disconnect from #1. Similarly, when you create a new node, you set its <code>next</code> to point to the current head first, and then swap the value of <code>head</code> to reference this new node after. </p>
<h3 id="peek-and-display"><code>peek()</code> and <code>display()</code></h3>
<p>Also related, these methods allow you to view the first and the entire stack respectively. <code>peek()</code> simply returns the <code>head.value</code> if the stack isn't empty, while <code>display()</code> continues to walk through each node-&gt;next-&gt;node-&gt;next-&gt;node and display each node's value as it gets there.</p>
<h3 id="size-and-tolist"><code>size()</code> and <code>to_list()</code></h3>
<p>These two methods both involve keeping track of a variable outside of your node-traverse, and updating that variable as you walk through: the integer <code>stack_size</code> for <code>size()</code>, and the list <code>stack_as_list[]</code> for the <code>to_list()</code> method (described as <code>toArray()</code> in the JavaScript implementation.</p>
<p>I also needed to reverse the final list before returning from the method; since the items are extracted one-by-one from the last item added and added to the end of the tally list using <code>append()</code>, I needed to do a final <code>stack_as_list.reverse()</code> to put items in the correct order. Another approach would be to directly place the items into beginning of the tally list using <code>stack_as_list.insert(0, item)</code> where <code>0</code> specifies the first position (<code>stack_as_list[0]</code> since Python lists are  <a target="_blank" href="https://en.wikipedia.org/wiki/Zero-based_numbering">zero-indexed</a> ). </p>
<h3 id="clear"><code>clear()</code></h3>
<p>To finish the set of methods listed at the beginning, we'll finish as simply as we began by simply clearing the array. This mimics the behavior of the stack's <code>__init__()</code> method, by simply setting the <code>head</code> to <code>None</code>. I suspect this might require some sort of garbage collection since all those nodes are still in memory, just simply no longer connected to our stack. If anyone has answers please let me know, and I'll update as I learn more!</p>
<h2 id="conclusion">Conclusion</h2>
<p>Thanks again for reading; I will be attempting some more data structure implementations using Python in future blog posts so stayed tuned. Here are some more of the pages I referenced while learning this material:</p>
<ul>
<li><a target="_blank" href="https://openbookproject.net/thinkcs/python/english3e/classes_and_objects_I.html">Python Classes and Objects</a> </li>
<li><a target="_blank" href="https://gist.github.com/dineshrajpurohit/0bb67d29a039f85f2f10">Gist from Dinesh</a> </li>
<li><a target="_blank" href="https://www.w3schools.com/python/python_classes.asp">W3Schools Python Classes</a> </li>
<li><a target="_blank" href="https://www.geeksforgeeks.org/implement-a-stack-using-singly-linked-list/">Geeks for Geeks</a> </li>
</ul>
<p>Photo in cover image by <a href="https://unsplash.com/@conti_photos?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText">Fabrizio Conti</a> on <a href="https://unsplash.com/s/photos/link-rock-climbing?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText">Unsplash</a></p>
]]></content:encoded></item></channel></rss>