<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Sofia Jonsson</title>
    <description>The latest articles on DEV Community by Sofia Jonsson (@sofiajonsson).</description>
    <link>https://dev.to/sofiajonsson</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F146006%2F6164fab8-6956-4cc2-9d23-e229969c7caa.jpg</url>
      <title>DEV Community: Sofia Jonsson</title>
      <link>https://dev.to/sofiajonsson</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/sofiajonsson"/>
    <language>en</language>
    <item>
      <title>Stop Refreshing Weather Sites: Automate Alerts with Python and Playwright</title>
      <dc:creator>Sofia Jonsson</dc:creator>
      <pubDate>Wed, 26 Nov 2025 20:02:54 +0000</pubDate>
      <link>https://dev.to/sofiajonsson/stop-refreshing-weather-sites-automate-alerts-with-python-and-playwright-52bm</link>
      <guid>https://dev.to/sofiajonsson/stop-refreshing-weather-sites-automate-alerts-with-python-and-playwright-52bm</guid>
      <description>&lt;h2&gt;
  
  
  Learn how to scrape live weather data, store it in SQLite, and receive automatic notifications - all in a practical end-to-end Python project.
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.verizon.com%2Fabout%2Fsites%2Fdefault%2Ffiles%2F2021-01%2Fsnow-forecast-%25232.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.verizon.com%2Fabout%2Fsites%2Fdefault%2Ffiles%2F2021-01%2Fsnow-forecast-%25232.gif" alt="Snow Forecast" width="700" height="393"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Living in a ski town means the weather basically runs my life. One minute it’s sunny and warm, the next it's below freezing, and we're getting over a foot of snow. More snow = more fun, but I have to be aware that I cannot simply get up and go. Someone has to shovel out the driveway and scrape off the car. Tired of constantly refreshing weather websites, I decided to automate the whole process. I built a Python pipeline that scrapes current conditions, stores them in SQLite, applies simple alert rules, and emails me whenever something important happens, so I never miss a snowstorm (or an exceptionally freezing morning) again.&lt;/p&gt;

&lt;p&gt;The goal was simple but practical: create a system that continuously monitors the weather, detects meaningful changes, and notifies me automatically. Here's what it does:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Scrapes weather data from dynamic, JavaScript-heavy sites using Playwright&lt;/li&gt;
&lt;li&gt;Extracts key details like temperature, description, and "feels like" values (10-day and snow forecast coming soon)&lt;/li&gt;
&lt;li&gt;Stores historical data in SQLite while avoiding duplicates&lt;/li&gt;
&lt;li&gt;Applies flexible &lt;strong&gt;alert rules&lt;/strong&gt; (temperature thresholds, snow warnings, etc.)&lt;/li&gt;
&lt;li&gt;Sends notifications via email&lt;/li&gt;
&lt;li&gt;Runs on a schedule every few minutes, fully automated&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This project combines &lt;strong&gt;web scraping&lt;/strong&gt;, &lt;strong&gt;scheduling&lt;/strong&gt;, and &lt;strong&gt;database pipelines&lt;/strong&gt; into one end-to-end system. It's designed to be extensible. You can swap in different data sources, add new alerts, or even integrate a dashboard in the future. &lt;/p&gt;

&lt;p&gt;Let's dive into the overall architecture so you can see how all the pieces fit together:&lt;/p&gt;




&lt;h2&gt;
  
  
  Stack:
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Python&lt;/li&gt;
&lt;li&gt;Playwright&lt;/li&gt;
&lt;li&gt;SQLite&lt;/li&gt;
&lt;li&gt;schedule&lt;/li&gt;
&lt;li&gt;SMTP email alerts&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is a practical, real-world automation project that I can keep building upon, not just a one-off script. Here's how I built it.&lt;/p&gt;




&lt;h2&gt;
  
  
  Architecture Overview
&lt;/h2&gt;

&lt;p&gt;Think of it as a mini ETL pipeline:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Extract via Playwright&lt;/li&gt;
&lt;li&gt;Transform via parsing + rules&lt;/li&gt;
&lt;li&gt;Load into SQLite&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Trigger alerts if conditions match &lt;br&gt;
&lt;br&gt;&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;+-----------------+
|  Scheduler      |
| (every 10 min)  |
+--------+--------+
         |
         v
+-----------------+
|  Playwright     |
|  Scraper        |
+--------+--------+
         | HTML
         v
+-----------------+
|  Data Extractor |
| (Selectors)     |
+--------+--------+
         | dict
         v
+-----------------+
|  SQLite DB      |
| (history + dedupe) |
+--------+--------+
         | latest update
         v
+-----------------+
| Alert Rules     |
+--------+--------+
         | yes/no
         v
+-----------------+
| Email Notifier  |
+-----------------+
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Each module is swappable—change data sources, database engines, or notification channels without touching the pipeline structure.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why I Chose Playwright Instead of Requests + BeautifulSoup
&lt;/h2&gt;

&lt;p&gt;Most modern weather sites are &lt;em&gt;not&lt;/em&gt; static HTML. They are heavily JavaScript-driven with lazy-loading and pop-ups that break simple scrapers. With &lt;code&gt;requests&lt;/code&gt;, you'd get a skeleton page. With Playwright, you get what a real browser renders. &lt;/p&gt;




&lt;h3&gt;
  
  
  Playwright gives us:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Headless browser rendering&lt;/li&gt;
&lt;li&gt;Reliable selectors&lt;/li&gt;
&lt;li&gt;Ability to interact with UI elements&lt;/li&gt;
&lt;li&gt;Proper async loading&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Example Scraper
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;playwright.sync_api&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;sync_playwright&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;scrape_weather&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="nf"&gt;sync_playwright&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;browser&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;chromium&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;launch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;headless&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;page&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;browser&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new_page&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="n"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;goto&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="n"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;wait_for_selector&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;.CurrentConditions--tempValue--3KcTQ&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="n"&gt;temp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;locator&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;.CurrentConditions--tempValue--3KcTQ&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;inner_text&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="n"&gt;desc&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;locator&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;.CurrentConditions--phraseValue--2xXSr&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;inner_text&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;temperature&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;temp&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;description&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;desc&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;url&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;url&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Storing Data in SQLITE
&lt;/h2&gt;

&lt;p&gt;We store only meaningful changes, not duplicates. This keeps the database useful without ballooning it with repeated identical values.&lt;/p&gt;

&lt;h3&gt;
  
  
  Example Schema:
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;TABLE&lt;/span&gt; &lt;span class="n"&gt;weather&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="nb"&gt;INTEGER&lt;/span&gt; &lt;span class="k"&gt;PRIMARY&lt;/span&gt; &lt;span class="k"&gt;KEY&lt;/span&gt; &lt;span class="n"&gt;AUTOINCREMENT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;temperature&lt;/span&gt; &lt;span class="nb"&gt;TEXT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;description&lt;/span&gt; &lt;span class="nb"&gt;TEXT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;feels_like&lt;/span&gt; &lt;span class="nb"&gt;TEXT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;url&lt;/span&gt; &lt;span class="nb"&gt;TEXT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nb"&gt;timestamp&lt;/span&gt; &lt;span class="nb"&gt;DATETIME&lt;/span&gt; &lt;span class="k"&gt;DEFAULT&lt;/span&gt; &lt;span class="k"&gt;CURRENT_TIMESTAMP&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Simple Duplicate Check
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;should_save&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cursor&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;record&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;cursor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;
        SELECT temperature, description FROM weather
        ORDER BY timestamp DESC LIMIT 1
    &lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;last&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;cursor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fetchone&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;last&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;record&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;temperature&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;record&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;description&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Alert Rules
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Current logic:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;should_alert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;record&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nf"&gt;float&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;record&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;temperature&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;32&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="bp"&gt;True&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;snow&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;record&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;description&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;lower&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="bp"&gt;True&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="bp"&gt;False&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Email Alerts
&lt;/h2&gt;

&lt;p&gt;Uses environment variables so credentials never end up in source control&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;smtplib&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;email.mime.text&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;MIMEText&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;send_email&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;subject&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;body&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;msg&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;MIMEText&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;body&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Subject&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;subject&lt;/span&gt;
    &lt;span class="n"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;From&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getenv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;EMAIL_FROM&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;To&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getenv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;EMAIL_TO&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="n"&gt;smtplib&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;SMTP_SSL&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;smtp.gmail.com&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;465&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;server&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;server&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;login&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getenv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;EMAIL_FROM&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getenv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;EMAIL_PASS&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
        &lt;span class="n"&gt;server&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;send_message&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Example alert:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Snow expected tonight. Feels like 18F.&lt;/em&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Running on a Schedule
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;schedule&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;time&lt;/span&gt;

&lt;span class="n"&gt;schedule&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;every&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="n"&gt;minutes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;do&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;run_scraper&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;schedule&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;run_pending&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sleep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Deployment options:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Systemd service&lt;/li&gt;
&lt;li&gt;Docker worker&lt;/li&gt;
&lt;li&gt;Cron + logs&lt;/li&gt;
&lt;li&gt;VPS&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Real-World Problems I Ran Into
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Problem&lt;/th&gt;
&lt;th&gt;Fix&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Blank data due to slow JS&lt;/td&gt;
&lt;td&gt;&lt;code&gt;wait_for_selector()&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Popups breaking scraping&lt;/td&gt;
&lt;td&gt;close modal buttons&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Class names changed&lt;/td&gt;
&lt;td&gt;centralized config&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Timeouts during storms&lt;/td&gt;
&lt;td&gt;retry logic&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;DB spam&lt;/td&gt;
&lt;td&gt;dedupe logic&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;This is where it became real engineering, not just scripting.&lt;/p&gt;




&lt;h3&gt;
  
  
  Example Terminal Output:
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="n"&gt;Running&lt;/span&gt; &lt;span class="n"&gt;scraper&lt;/span&gt;&lt;span class="p"&gt;...&lt;/span&gt;
&lt;span class="k"&gt;Found&lt;/span&gt; &lt;span class="n"&gt;weather&lt;/span&gt; &lt;span class="k"&gt;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;18&lt;/span&gt;&lt;span class="n"&gt;F&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Light&lt;/span&gt; &lt;span class="n"&gt;Snow&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Feels&lt;/span&gt; &lt;span class="k"&gt;like&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="n"&gt;F&lt;/span&gt;
&lt;span class="n"&gt;Saving&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="k"&gt;data&lt;/span&gt; &lt;span class="k"&gt;to&lt;/span&gt; &lt;span class="n"&gt;DB&lt;/span&gt;&lt;span class="p"&gt;...&lt;/span&gt;
&lt;span class="n"&gt;Alert&lt;/span&gt; &lt;span class="n"&gt;triggered&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;sending&lt;/span&gt; &lt;span class="n"&gt;email&lt;/span&gt;&lt;span class="p"&gt;...&lt;/span&gt;
&lt;span class="n"&gt;Done&lt;/span&gt; &lt;span class="n"&gt;scraping&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Future Enhancements:
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;SMS alerts via Twilio&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Multi-site verification&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Dockerized deployment&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Dashboard UI&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Logging + retry strategy&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Conclusion:
&lt;/h2&gt;

&lt;p&gt;This solved a meaningful personal problem while strengthening engineering skills. And it's something I can continue to build upon and customize to my personal needs. &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Automated scraping&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Data pipelines&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Scheduled services&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Secure credential handling&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Resilient retries and selectors&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmedia1.tenor.com%2Fm%2FvvnwTzB1Ve8AAAAd%2Ffinalthought-jerry.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmedia1.tenor.com%2Fm%2FvvnwTzB1Ve8AAAAd%2Ffinalthought-jerry.gif" alt="Final Thoughts" width="640" height="458"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you live somewhere where weather impacts your daily life, building a custom automated monitoring system isn’t just fun, it’s genuinely worthwhile.&lt;/p&gt;

&lt;p&gt;You can find the full code on &lt;a href="https://github.com/sofiajonsson11/scraper-alert-engine" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>python</category>
      <category>scraping</category>
      <category>playwright</category>
      <category>sqlite</category>
    </item>
    <item>
      <title>Progressive Web Applications: Under the Hood (PWA)</title>
      <dc:creator>Sofia Jonsson</dc:creator>
      <pubDate>Mon, 18 Nov 2019 07:51:29 +0000</pubDate>
      <link>https://dev.to/sofiajonsson/progressive-web-applications-under-the-hood-pwa-pfl</link>
      <guid>https://dev.to/sofiajonsson/progressive-web-applications-under-the-hood-pwa-pfl</guid>
      <description>&lt;p&gt;Welcome back to the continuation of this two-part PWA blog series! I'm stoked you guys also found PWA's interesting and I'm here to finish up the topic. For those of you who have not read the intro to PWAs, check it out here: &lt;/p&gt;
&lt;div class="ltag__link"&gt;
  &lt;a href="/sofiajonsson" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__pic"&gt;
      &lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F146006%2F6164fab8-6956-4cc2-9d23-e229969c7caa.jpg" alt="sofiajonsson"&gt;
    &lt;/div&gt;
  &lt;/a&gt;
  &lt;a href="/sofiajonsson/intro-to-progressive-web-apps-pwa-g46" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__content"&gt;
      &lt;h2&gt;Intro to Progressive Web Apps (PWA)&lt;/h2&gt;
      &lt;h3&gt;Sofia Jonsson ・ Nov 11 '19&lt;/h3&gt;
      &lt;div class="ltag__link__taglist"&gt;
        &lt;span class="ltag__link__tag"&gt;#pwa&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#mobile&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#webapplication&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#beginners&lt;/span&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/a&gt;
&lt;/div&gt;


&lt;p&gt;As promised, I'm going to be diving deeper into what really goes on under the hood of a PWA. So just to reiterate, a Progressive Application is a happy medium between a web and a native app and basically delivering a much more efficient and user-friendly experience.&lt;/p&gt;

&lt;p&gt;Let's dive into the main three components of a PWA!&lt;/p&gt;

&lt;h2&gt;
  
  
  Application Shell
&lt;/h2&gt;

&lt;p&gt;In a nutshell, this is where the content gets placed. You only need the bare essentials like navigation and header elements as this portion of the PWA aren't loaded each time it gets launched by the user. It is instead cached once and then stored to be displayed to the user no matter their internet connection. This is the core component that makes the PWA so fast and efficient. &lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fh1jicfhlvnkaj9nvolxc.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fh1jicfhlvnkaj9nvolxc.jpg" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Service Workers
&lt;/h2&gt;

&lt;p&gt;One of the reasons a PWA is so great is its ability to perform with limited to no internet connection. This is where the Service Worker comes into action. The Service Worker is an independent network script that focuses on the background and determines whether or not the content should be downloaded from the cache or server. This is the fundamental reason why PWA's work offline and are able to organize data. The central requirement for installing a Service Worker is the HTTPS server. The HTTPS server stops performing when idle but re-fires the next time it is needed. &lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fdf4mow85moq9uce3srli.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fdf4mow85moq9uce3srli.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Manifest Files
&lt;/h2&gt;

&lt;p&gt;The Manifest File allows you, the developer to create your custom splash screen. For those of you who don't know what a splash screen it is the default page shown until your app has loaded and provided your user a more native-like experience. For many applications, this can consist of a plain white screen but that's no bueno. You want to engage your user and make it feel like they're using an app, not an unfriendly mobile web page. &lt;/p&gt;

&lt;p&gt;Now, let's get into some of the other components:&lt;/p&gt;

&lt;h2&gt;
  
  
  Push Notifications
&lt;/h2&gt;

&lt;p&gt;PWA Push Notifications are pretty comparable to that of Native Apps, which is really really cool! This all works due to the Service Workers and Push API. Through the Service Worker's Push Manager, the user of the App gets a pop-up suggesting subscribing to push notifications. Once the user has agreed to the terms and conditions, their subscription ID is stored in the database. I mentioned earlier how SW's handle data, but following this order of events, the server takes over and pushes new messages and the SW subsequently fires off the push notification to the specific user device. &lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fxy97m75tryhkgtzmdpl3.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fxy97m75tryhkgtzmdpl3.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Installation Banners
&lt;/h2&gt;

&lt;p&gt;Also referred to as the web app install prompt, it is one of the methods used by PWA's to captivate and engage its users. As modern-day web users, our attention span is quite limited as we bounce back from different applications, activities, etc. so in order to create an engaging experience and reel in the attention of its users PWA's offer for users to save the page to their home screen after about 30 seconds of use. This install prompt is supported by several browsers including Opera v. 32+, Chrome v.31+, Firefox v. 589+, etc. This gives users the experience of having downloaded an app without the hassle of having to actually do it. &lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fygxz6g6bzbi4ab5d09hh.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fygxz6g6bzbi4ab5d09hh.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Different Caching Strategies
&lt;/h2&gt;

&lt;p&gt;This is super important for creating a seamless experience with a limited internet connection. There are a few different options to consider:&lt;/p&gt;

&lt;h4&gt;
  
  
  Cache
&lt;/h4&gt;

&lt;p&gt;A cache only PWA is optimal for a completely static site. Here, the data is cached during the installation process and there are no requests sent by the Service Worker to the server.&lt;/p&gt;

&lt;h4&gt;
  
  
  Network
&lt;/h4&gt;

&lt;p&gt;It's almost self-explanatory. This approach does not use caching, instead, the Service Worker exclusively sends requests to the network and if there's no network response the request simply fails. &lt;/p&gt;

&lt;h4&gt;
  
  
  Cache First:
&lt;/h4&gt;

&lt;p&gt;Here the Service Worker automatically loads the cached data, without even trying to reach the network. The requests are only sent to the network when there is no cached data available. This approach is actually a great blueprint for stashing static information and App Shell resources. Information will then only be refreshed when the actual app version gets updated. &lt;/p&gt;

&lt;h4&gt;
  
  
  Network First:
&lt;/h4&gt;

&lt;p&gt;Here, the PWA's strategy is to essentially send requests to the network. If there is a response, the app will render the received data. If not, the Service Worker only renders cached data. This strategy is ideal for apps with dynamic content.&lt;/p&gt;

&lt;h4&gt;
  
  
  Cache Vs. Network
&lt;/h4&gt;

&lt;p&gt;An instance where you would be interested in this approach is for a device with slow disk access. The PWA will send a simultaneous request to the network and the cache. Normally, the cached data will render first. During this time, the network shoots over the renewed data and saves it to the cache. This data is refreshed in the background in order to not interrupt the rendering data in a cache. &lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Frryue2mui1velq14kqtj.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Frryue2mui1velq14kqtj.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Well folks, there you have it. You now have the basic information you need to determine why you would want to dive deeper into Progressive Web applications and what type of Caching strategy you need based on your own specific app needs. I for one am looking forward to refactoring some of my Flatiron School projects to make them more efficient, but also mobile-friendly! There is so much more to this subject so depending on how my to refactor goes I might have to just keep y'all posted.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://i.giphy.com/media/12xSrwKxHxB3BS/giphy.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://i.giphy.com/media/12xSrwKxHxB3BS/giphy.gif" alt="the end"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>pwa</category>
      <category>webapplications</category>
      <category>techtrends</category>
      <category>beginners</category>
    </item>
    <item>
      <title>Intro to Progressive Web Apps (PWA)</title>
      <dc:creator>Sofia Jonsson</dc:creator>
      <pubDate>Mon, 11 Nov 2019 01:49:06 +0000</pubDate>
      <link>https://dev.to/sofiajonsson/intro-to-progressive-web-apps-pwa-g46</link>
      <guid>https://dev.to/sofiajonsson/intro-to-progressive-web-apps-pwa-g46</guid>
      <description>&lt;p&gt;This last week I encountered a job application that asked me for my favorite trend in technology. Naturally, I went and googled top tech trends in 2019 and came across a pretty interesting list. What really caught my attention was Progressive Web Applications or PWA's. This was super interesting to me since I've recently been working on expanding my React Native knowledge for a mobile app project and this seems like a happy modern medium instead of building a pure native app. Sure, there are situations when you need a mobile application, but more often than not I'm clicking the dismiss popup in my browser. Ain't nobody got time to download an app just to delete it later. &lt;/p&gt;

&lt;p&gt;So, this week I want to go over PWA's and why I think they're so cool and why you might want to use one!&lt;/p&gt;

&lt;h2&gt;
  
  
  What is PWA?
&lt;/h2&gt;

&lt;p&gt;This application type was unveiled by Google back in 2015. It's got web and native app traits and lands somewhere in the middle taking the best from both approaches. It was initially only for Android users, but due to the iOS 11.3 update in March of 2018, iPhone users get to join in on the fun. &lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Vtw02RV9--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/e6j7x6xbzx8778zzrw71.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Vtw02RV9--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/e6j7x6xbzx8778zzrw71.jpeg" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Features
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Responsiveness&lt;/strong&gt;: Building responsive applications is another trend for 2019 as there is a high need to provide an excellent user experience for all types of screens and platforms&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Progressive updates&lt;/strong&gt;: Sure, it's in the name. But more importantly, it needs to be progressive so that all types of users can interact with their application.  This strategy is the core of the PWA and as a result, the most critical content is pushed to the forefront&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Offline Use&lt;/strong&gt;: This is HUGE. This means that users can use this app despite their browser. This type of application can work with a bad internet connection or even offline&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Keeps it Fresh&lt;/strong&gt;: Due to service workers, new updates are downloaded while the app is open and in use&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Secure&lt;/strong&gt;: Are only served through HTTPS to protect the privacy and security of its app's users&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Discoverable&lt;/strong&gt;: Is indexed in search engines and receives better ranks &lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Installable and linkable&lt;/strong&gt;: No need for users to install PWA's through app stores. This is great for the developer's as well as they do not need to wait for approval from Play Market or App Store. The application can be sent via a link on the web and can be saved on a home screen instead.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Push Notifications&lt;/strong&gt;: As opposed to web applications, PWA has access to sending push notifications and pop up and alert you about friend requests, recent news, new messages, etc.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--g15MZJ_---/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/v91vvp90prgjjth1r732.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--g15MZJ_---/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/v91vvp90prgjjth1r732.jpg" alt="Alt Text"&gt;&lt;/a&gt;&lt;br&gt;
Basically: it takes far less time to load and is fast to use. It's easier to share, doesn't require a download which is great for users. It's much faster to build and therefore cheaper and you don't have to wait for approval to launch on the market or store. Compared to a website it offers superior UX, works more securely offline and is impressively lightweight. &lt;/p&gt;

&lt;h2&gt;
  
  
  Good Examples
&lt;/h2&gt;

&lt;p&gt;Here are some examples of large companies that have implemented PWA's. &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Twitter&lt;/strong&gt;: Launched PWA in 2017. Since then Twitter has cut average load time down by a third, decreased bounce rate by a fifth and increased their pages per session metric over 65%!&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Forbes&lt;/strong&gt;: Also launched PWA in 2017 with the goal of increasing user engagement and provide a fast personalized experience. It's PWA loads in 0.8 seconds and increased session per user metric by 43% and engagement by 100%&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Tinder&lt;/strong&gt;: Built using React and Redux. PWA is 90% smaller than the previous native app and loads in less than half the time of the native app.
etc..&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Lot's of stats here but as a general consensus, their PWA's operate on a fraction of their previous load time and have increased user engagement exponentially! &lt;/p&gt;

&lt;h2&gt;
  
  
  Frameworks
&lt;/h2&gt;

&lt;p&gt;There are a few different options for frameworks when building out a PWA. Angular JS and Polymer which are both JavaScript based. There are also other Progressive web app tools such as React and Lighthouse, Dev Tools, and Workbox which are all valid options for building a robust progressive web app. &lt;/p&gt;

&lt;h2&gt;
  
  
  What's going on Under the Hood?
&lt;/h2&gt;

&lt;p&gt;I will be diving into this topic in my next blog post to really get into the different elements that are used in web dev to make up the PWA.&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--tHJGWBEd--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/vaz99n8pxm8rw5to16pn.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--tHJGWBEd--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/vaz99n8pxm8rw5to16pn.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;br&gt;
Next week I will be covering: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;App Shell&lt;/li&gt;
&lt;li&gt;Service Workers&lt;/li&gt;
&lt;li&gt;Manifest Files&lt;/li&gt;
&lt;li&gt;Push notifications&lt;/li&gt;
&lt;li&gt;Installation banners&lt;/li&gt;
&lt;li&gt;Different Caching strategies&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://i.giphy.com/media/3ohs7XmlgV1Vg6urPa/giphy.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://i.giphy.com/media/3ohs7XmlgV1Vg6urPa/giphy.gif" alt="Stay Tuned"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>pwa</category>
      <category>mobile</category>
      <category>webapplication</category>
      <category>beginners</category>
    </item>
    <item>
      <title>Beginner Tips: Solving Programming Problems</title>
      <dc:creator>Sofia Jonsson</dc:creator>
      <pubDate>Mon, 04 Nov 2019 03:59:11 +0000</pubDate>
      <link>https://dev.to/sofiajonsson/beginner-tips-solving-code-problems-55hg</link>
      <guid>https://dev.to/sofiajonsson/beginner-tips-solving-code-problems-55hg</guid>
      <description>&lt;p&gt;This week, I want to focus on one of the most important parts of the interview process; the code challenge. Whether you are working on a whiteboard or are able to code over a shared screen environment, being presented with a question you don't know how to approach can be a daunting task. Perhaps you understand the logic and syntax but don't feel confident getting the ball rolling or you are able to follow someone else's code without a problem and rely on the crutch of too much guidance. What you don't want to do is freeze up and stare at the blank screen.&lt;br&gt;
There can be quite a bit of uncertainty of where or how to start so I decided to write a basic guideline to keep you moving forward.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://i.giphy.com/media/gmJjYtLrEwDBe/giphy.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://i.giphy.com/media/gmJjYtLrEwDBe/giphy.gif" alt="test"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  1. Recieve the question
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://i.giphy.com/media/VbnUQpnihPSIgIXuZv/giphy.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://i.giphy.com/media/VbnUQpnihPSIgIXuZv/giphy.gif" alt="test"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  2. Read the problem. Now read it again.
&lt;/h2&gt;

&lt;p&gt;Give yourself a minute to understand the prompt. If you don't understand the problem properly you can't answer it either. Ask questions, you don't want to misunderstand the directions and waste time working in the wrong direction. &lt;br&gt;
Depending on the prompt, your questions might be along the lines of:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt; &lt;strong&gt;What am I passing into this function?&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;What is the purpose of this function? What will I return at the end?&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;What datatypes does this array contain?&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  3. Plan of attack
&lt;/h2&gt;

&lt;p&gt;This is a good time to write down notes on the board. It will allow you to structure your problem and look for patterns and steps you can eventually optimize and refactor. You will have just established what the purpose of the function is and its parameters in the step above, and you can talk through your strategy by listing it off as pseudo-code on the side of the whiteboard, or just making quick comments in your coding environment. The purpose of this pseudo-code is to help you translate it into code. You can do this line by line in the text editor and provide a clear outline of what you are working to achieve.&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--JD4zjFvj--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/cyoars2qzxjaqnwolcsh.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--JD4zjFvj--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/cyoars2qzxjaqnwolcsh.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  4. Answer the Question
&lt;/h2&gt;

&lt;p&gt;Now that you have asked your clarifying questions, organized your plan of attack, and begun to write pseudo-code, its time to translate it into your language of choice and debug. In a text editor environment, I find that I put a &lt;em&gt;console.log()&lt;/em&gt; if I'm coding in JavaScript, or &lt;em&gt;puts&lt;/em&gt; in Ruby after every line as I'm checking the values of my code and seeing if it is operating the way I intended. What's important is that you come up with a solution. Don't be too good for the brute force solution because the sooner you get that up and running you can move on to your refactor.&lt;/p&gt;
&lt;h2&gt;
  
  
  5. Simplify
&lt;/h2&gt;

&lt;p&gt;If you went with the brute force solution it's a good idea to mention that you are aware that you provided the brute force or naive solution but that you would improve your code through x and y because of z. If you have time, this is your opportunity to simplify and optimize your code with a refactor. Take the time to consider the space and time complexity of your solution and how it would work on a larger scale. If you're feeling a little fuzzy on Big O notation check out my blog post on it:&lt;/p&gt;
&lt;div class="ltag__link"&gt;
  &lt;a href="/sofiajonsson" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__pic"&gt;
      &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--WjNnOTip--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://res.cloudinary.com/practicaldev/image/fetch/s--rSiD4fKQ--/c_fill%2Cf_auto%2Cfl_progressive%2Ch_150%2Cq_auto%2Cw_150/https://dev-to-uploads.s3.amazonaws.com/uploads/user/profile_image/146006/6164fab8-6956-4cc2-9d23-e229969c7caa.jpg" alt="sofiajonsson image"&gt;
    &lt;/div&gt;
  &lt;/a&gt;
  &lt;a href="/sofiajonsson/basics-big-o-notation-a6m" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__content"&gt;
      &lt;h2&gt;Basics: Big O Notation&lt;/h2&gt;
      &lt;h3&gt;Sofia Jonsson ・ Oct 18 '19 ・ 3 min read&lt;/h3&gt;
      &lt;div class="ltag__link__taglist"&gt;
        &lt;span class="ltag__link__tag"&gt;#bigo&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#complexities&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#algorithms&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#datastructure&lt;/span&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/a&gt;
&lt;/div&gt;


&lt;h2&gt;
  
  
  6. Practice, practice, practice
&lt;/h2&gt;

&lt;p&gt;Learning to code is a journey, the only way we are going to get better at it, interviewing, and trusting our instincts (not relying on others) is through practice. Welcome constructive criticism, feedback, and make sure to redo a problem especially if you had trouble with it. If you have someone to practice with I highly recommend alternating the interviewer/interviewee position and to put yourself in the hot seat. When you encounter a particularly difficult problem and have to resort to looking up the answer I find that it's super helpful to take the time to write it out with a test dataset to see what your algorithm does at each step of the way. That way you get to dive into the problem and understand how it works so that you can remember it and solve it the next time around. &lt;/p&gt;

&lt;p&gt;Remember to not be so hard on yourself and remember how far you've come! &lt;br&gt;
&lt;a href="https://i.giphy.com/media/12vJgj7zMN3jPy/giphy.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://i.giphy.com/media/12vJgj7zMN3jPy/giphy.gif" alt="test"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>beginners</category>
      <category>interview</category>
      <category>codingproblems</category>
    </item>
    <item>
      <title>Interview Prep: System Design</title>
      <dc:creator>Sofia Jonsson</dc:creator>
      <pubDate>Sun, 27 Oct 2019 17:06:07 +0000</pubDate>
      <link>https://dev.to/sofiajonsson/interview-prep-system-design-4p50</link>
      <guid>https://dev.to/sofiajonsson/interview-prep-system-design-4p50</guid>
      <description>

&lt;p&gt;One topic I've been thinking about for the last little bit is System Design. Throughout my informational interviews, I've gotten little tidbits of information to help me prepare for the interview process' at said company. One of these bits of information pertained to system design often phrased in the manner of "be prepared to architect Twitter or FaceBook."&lt;/p&gt;

&lt;h2&gt;
  
  
  What is Systems Design?
&lt;/h2&gt;

&lt;p&gt;According to the University of Waterloo: Systems design is identified as the philosophy, methods, and approaches to solving problems that are intrinsically multi-disciplinary. Through the consideration of the objective and subjective performance requirements, a design solution is constructed to meet the needs of the customer, the user, and society. Some of the main tools include strategies, procedures, and techniques that help in architecting a specific product or project. &lt;/p&gt;

&lt;h2&gt;
  
  
  Main Steps
&lt;/h2&gt;

&lt;h5&gt;
  
  
  Step 1: Understand the goals of the design
&lt;/h5&gt;

&lt;p&gt;Ask clarifying questions to eliminate ambiguity with the interviewer. Who are the users? What is its primary purpose? How will it be used? What are the likely inputs and outputs?&lt;/p&gt;

&lt;h5&gt;
  
  
  Step 2:Establish the scope of the project
&lt;/h5&gt;

&lt;p&gt;Now that basic understanding has been established, this is a good opportunity to ask questions about the features of the application. It's imperative that what you are suggesting is along the lines with what the interviewer wants before you move forward. What type of clients does this application want to support(mobile, web, etc)? Are we requiring authentication? Do we want to incorporate analytics? Are we integrating with existing systems?&lt;/p&gt;

&lt;h5&gt;
  
  
  Step 3: Design for the appropriate scale
&lt;/h5&gt;

&lt;p&gt;We need to determine the scale to gauge if our data is compatible and can fit on one machine. What is the read-to-write ratio? How many simultaneous requests should we expect? Average response time? Limit on data we allow users to provide?&lt;/p&gt;

&lt;h5&gt;
  
  
  Step 4: Start from a high-level perspective, then get into the nitty-gritty
&lt;/h5&gt;

&lt;p&gt;Begin with the end-to-end processes determined from the goals set in &lt;strong&gt;step 1&lt;/strong&gt;. This might cover APIs, backend services, network architecture, data stores, outlining different clients, etc. and how they all join together to meet the requirements of those stated goals. This is where the conversation will end up covering potential performance bottlenecks and decisions about the delegation of responsibilities. Whatever way you approach this step, remember to start simple and build it out from there.&lt;/p&gt;

&lt;h5&gt;
  
  
  Step 5: DS&amp;amp;A - Data Structures and Algorithms
&lt;/h5&gt;

&lt;p&gt;Runtime and memory complexity are super important in designing software systems. The efficiency and usability of the application (depending on the scale of the project) are completely dependent on a deep understanding and implementation of DS&amp;amp;A.&lt;/p&gt;

&lt;h5&gt;
  
  
  Step 6: Tradeoffs
&lt;/h5&gt;

&lt;p&gt;Just about every decision you make will have trade-offs. It's important to be able to talk about them in real-time, offer solutions for them and demonstrate that you are aware of the complexities of systems and how they often require compromise and showcase your understanding through the pros and cons of each approach. Remember, there is no correct answer, just be prepared to explain yourself to show the interviewer that you're logical and will use the appropriate tools to get the job done.  &lt;/p&gt;

&lt;h2&gt;
  
  
  The Process
&lt;/h2&gt;

&lt;p&gt;This can be broken down into four main compartments and are best illustrated by this image:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;task definition &lt;/li&gt;
&lt;li&gt;conceptual stage &lt;/li&gt;
&lt;li&gt;design stage &lt;/li&gt;
&lt;li&gt;implementation stage 
&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fops92y0hdbcxld5ksg52.png" alt="Alt Text"&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Basic Examples
&lt;/h2&gt;

&lt;p&gt;For the example portion, I am going to keep it simple and stick to Twitter and showcase some smaller examples. As I'm sure you know by now, architecting an entire social media page would be quite extensive and this goes back to &lt;strong&gt;step 2&lt;/strong&gt; of the Main Steps portion of this blog and establishing the scope. Remember to ask lots of questions so you aren't diving deeper than you need to and wasting interview time.&lt;/p&gt;

&lt;p&gt;In this first example, I am using a diagram showcasing how to design Twitters REST API:&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2F610ivj706gfmf4s0v3jf.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2F610ivj706gfmf4s0v3jf.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Next, the Full Object Model:&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fsgnpq4tvvhf6284plfna.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fsgnpq4tvvhf6284plfna.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h6&gt;
  
  
  &lt;a href="https://www.visual-paradigm.com/tutorials/rest-api-design-twitter-example.jsp" rel="noopener noreferrer"&gt;https://www.visual-paradigm.com/tutorials/rest-api-design-twitter-example.jsp&lt;/a&gt;
&lt;/h6&gt;

&lt;p&gt;I hope this blog post gave you some clarity on the subject and a better understanding of how to tackle this prompt next time you encounter it.&lt;/p&gt;

&lt;p&gt;Good luck!&lt;/p&gt;

</description>
      <category>systemdesign</category>
      <category>technicalinterview</category>
      <category>beginners</category>
      <category>interviewprep</category>
    </item>
    <item>
      <title>Basics: Big O Notation</title>
      <dc:creator>Sofia Jonsson</dc:creator>
      <pubDate>Fri, 18 Oct 2019 07:08:57 +0000</pubDate>
      <link>https://dev.to/sofiajonsson/basics-big-o-notation-a6m</link>
      <guid>https://dev.to/sofiajonsson/basics-big-o-notation-a6m</guid>
      <description>&lt;p&gt;This week I decided to dive into Big O notation, or algorithmic efficiency. I know it can be a bit of an intimidating topic, but the only way to get more comfortable with it is to dive in and practice! This blog post will cover the basics and serve as a refresher/guide for your journey to better understanding them. &lt;/p&gt;

&lt;h2&gt;
  
  
  What is Big O?
&lt;/h2&gt;

&lt;p&gt;Big-O complexities pertain to the limiting behaviors of a function when the argument leans towards a particular value or infinity. Basically, it's used in Computer Science to illustrate the performance or complexity of an algorithm. This is always done in the worst-case scenario of that algorithm and can be applied to both the time and space required. &lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--L9ofhoxD--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/gh1kib4sfcj83347mx7f.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--L9ofhoxD--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/gh1kib4sfcj83347mx7f.jpeg" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As a developer its important to be aware of the worst possible outcome of your code when it's put to the test. Being aware of this helps us developers improve our efficiency and create applications with better performance.&lt;br&gt;
For instance, if you build an application and only expect a few users to visit your site then there's no problem. However, if you were, say Mark Zuckerberg and designing Facebook, then you would definitely be concerned about how your application would run as millions of users worldwide interact with your site. It is important to understand the worst-case scenario of any code that you build. As someone who is self-taught in this area I admit, it can be difficult to know what each of the complexities looks like out in the "real world" so I have prepared examples below.&lt;/p&gt;

&lt;p&gt;Let's look at the time complexities shown in the chart above, explaining the algorithms:&lt;/p&gt;
&lt;h2&gt;
  
  
  O(log n)
&lt;/h2&gt;

&lt;p&gt;"O of log N"&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Ob9VXdrE--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/60btp3m1y27rplq9i76m.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Ob9VXdrE--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/60btp3m1y27rplq9i76m.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;br&gt;
O(log n) is the best-case scenario. A good example of this is a binary search which divides the growth rate in two by every step of the code's execution. Therefore, on a graph, O(log n) will flatten out as the function progresses and outperform linear algorithms as the inputs increase.&lt;/p&gt;
&lt;h2&gt;
  
  
  O(1)
&lt;/h2&gt;

&lt;p&gt;&lt;em&gt;"O of 1"&lt;/em&gt;&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--aPpQPlUU--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/vwai9bp5evkkr8fuz7qq.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--aPpQPlUU--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/vwai9bp5evkkr8fuz7qq.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;br&gt;
O(1) is referred to as &lt;strong&gt;constant time&lt;/strong&gt; due to the fact that despite the size of our input, it will always take the same amount of time to compute things. Constant time is regarded as the best-case scenario for a function. Basically, it's a function &lt;strong&gt;without&lt;/strong&gt; a loop. &lt;/p&gt;
&lt;h2&gt;
  
  
  O(n)
&lt;/h2&gt;

&lt;p&gt;&lt;em&gt;"O of N"&lt;/em&gt;&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--FVuVysWx--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/54zokqqlof1tuokpw01y.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--FVuVysWx--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/54zokqqlof1tuokpw01y.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;br&gt;
O(n), also known as &lt;strong&gt;linear time&lt;/strong&gt; references an algorithm whose performance grows linearly, in direct correlation to the size of the input. You can think of this as one loop over a variable.&lt;/p&gt;
&lt;h2&gt;
  
  
  O(n log n)
&lt;/h2&gt;

&lt;p&gt;&lt;em&gt;"O of N log N"&lt;/em&gt;&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--uU2GeQ2T--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/6kmn62yq0lzoifooe4np.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--uU2GeQ2T--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/6kmn62yq0lzoifooe4np.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;br&gt;
O(n log n) is referred to as &lt;strong&gt;logarithmic time&lt;/strong&gt;. A common example of this algorithm is the implementation of a sorting function and in this case, we'll look at &lt;strong&gt;Merge Sort&lt;/strong&gt;. Just like our O(log n)'s binary search we will divide and conquer, and break the problem into smaller sub-problems, in order to recursively solve the subproblems and then combine the solutions to the smaller problems to the initial problem. &lt;/p&gt;
&lt;h2&gt;
  
  
  O(n&lt;sup&gt;2)&lt;/sup&gt;
&lt;/h2&gt;

&lt;p&gt;&lt;em&gt;"O of N squared"&lt;/em&gt;&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Whyc4xhz--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/1o0ice3lg74dd1fiyfmb.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Whyc4xhz--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/1o0ice3lg74dd1fiyfmb.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;br&gt;
O(n&lt;sup&gt;2),&lt;/sup&gt; also known as &lt;strong&gt;quadratic time&lt;/strong&gt;, pertains to an algorithm whose performance increases exponentially to the input data size. This is commonly seen in algorithms with nested iterations or loops and the deeper you go, the higher your exponent O(n&lt;sup&gt;3),&lt;/sup&gt; O(n&lt;sup&gt;4)..etc.&lt;/sup&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  O(2&lt;sup&gt;n)&lt;/sup&gt;
&lt;/h2&gt;

&lt;p&gt;&lt;em&gt;"Big O of two to the N"&lt;/em&gt;&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--OhFdBvE---/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/bnl0isp1k4zds3c6x8ig.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--OhFdBvE---/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/bnl0isp1k4zds3c6x8ig.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;br&gt;
O(2&lt;sup&gt;n)&lt;/sup&gt; often referenced as &lt;strong&gt;exponential time&lt;/strong&gt;, is an algorithm that doubles with each addition to the inputted data. The growth curve of this complexity is exponential, slow at first, and then skyrocketing. An example of this is the recursive solution for the Fibonacci sequence. &lt;/p&gt;
&lt;h2&gt;
  
  
  O(n!)
&lt;/h2&gt;

&lt;p&gt;&lt;em&gt;"O of n factorial"&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;O(N!)is bad. Really, really bad. Therefore it's hardly ever shown along with the other complexities, but it's always mentioned in the chart just to show you that it still exists. &lt;/p&gt;

&lt;p&gt;I'll end the blog post with these useful tables from &lt;a href="https://www.bigocheatsheet.com"&gt;https://www.bigocheatsheet.com&lt;/a&gt; &lt;/p&gt;
&lt;h2&gt;
  
  
  Common Data Structure Operations
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--c2AW8Ymp--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/j7t6colddwihfy86qxsb.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--c2AW8Ymp--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/j7t6colddwihfy86qxsb.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Array Sorting Algorithms
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--CHm4i8Mt--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/pqhhinuwq1q0y9rwwbac.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--CHm4i8Mt--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/pqhhinuwq1q0y9rwwbac.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you need a video explanation, Gayle Laakman McDowell, from &lt;em&gt;Cracking the Coding Interview&lt;/em&gt; does a great job in this YouTube video:&lt;/p&gt;

&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/v4cd1O4zkGw"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

</description>
      <category>bigo</category>
      <category>complexities</category>
      <category>algorithms</category>
      <category>datastructure</category>
    </item>
    <item>
      <title>Roman To Integer</title>
      <dc:creator>Sofia Jonsson</dc:creator>
      <pubDate>Mon, 14 Oct 2019 03:11:11 +0000</pubDate>
      <link>https://dev.to/sofiajonsson/roman-to-integer-3a4k</link>
      <guid>https://dev.to/sofiajonsson/roman-to-integer-3a4k</guid>
      <description>&lt;h2&gt;
  
  
  Intro
&lt;/h2&gt;

&lt;p&gt;This week I decided to do some whiteboard practice and came across a pretty neat solution in Ruby that I wanted to share. I had previously completed this question in JavaScript but decided to go back and attempt the LeetCode questions in a different language.&lt;/p&gt;

&lt;p&gt;For this specific blog post I am focusing on the classic whiteboard question of converting Roman letters to an integer: &lt;/p&gt;

&lt;h2&gt;
  
  
  Problem
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--RpyiIRwh--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/k6ekihsw09833idh7687.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--RpyiIRwh--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/k6ekihsw09833idh7687.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As stated above, there are 7 Roman numerals to keep track of and for most cases, they are usually written largest to smallest from left to right. However, for any instance where the numbers 4 and 9 are shown we need to adjust our output to reflect that subtraction. &lt;/p&gt;

&lt;p&gt;For this example, our input will be "XLIV", and our Output will be "44"&lt;/p&gt;

&lt;h2&gt;
  
  
  JavaScript Solution
&lt;/h2&gt;

&lt;p&gt;First, I'll show you how I solved for JavaScript: &lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--SMjrbn6Z--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/8mccldg5z0g9newdnvgd.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--SMjrbn6Z--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/8mccldg5z0g9newdnvgd.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;br&gt;
To break it down further, I put some Console.logs in my Repl.it to see the outputs from the algorithm and how it produced the specific solution of 44.&lt;/p&gt;

&lt;p&gt;I set Roman numerals and decimals equal to specific values in separate arrays so that I would not have to subtract once I came across a number with a 4 or 9 and could then loop through it.&lt;/p&gt;

&lt;p&gt;As you can see, the algorithm went through the problem 2 times before arriving at the solution:&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--HajQx3sq--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/5kf9yrtnfmsdkf2fit7y.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--HajQx3sq--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/5kf9yrtnfmsdkf2fit7y.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Ruby Solution
&lt;/h2&gt;

&lt;p&gt;Next, I solved for Ruby: &lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--MXZJUTYt--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/rzz567u9ukvxprz5fjyy.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--MXZJUTYt--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/rzz567u9ukvxprz5fjyy.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Once again, I put some "puts" statements in my Repl.it to see how the algorithm arrived at 44. This solution was initially much longer as I thought I needed to incorporate more Roman Numerals into my hash_&lt;em&gt;map&lt;/em&gt;_. Ruby has a super useful function for enumerables called &lt;strong&gt;each_cons&lt;/strong&gt;. This then "Iterates the given block for each array of consecutive elements." I put the "puts" statements in there to show how each time it loops through, it looks to the if statement and produces an output for the appreciating and depreciating sums as displayed in the Repl.it as &lt;strong&gt;:sum1 and :sum2&lt;/strong&gt;. &lt;/p&gt;

&lt;p&gt;This time around, the algorithm went through the Roman letters 3 times before arriving at the solution: &lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--hF1UpXHw--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/cpbnw8f9nqb0jxj758nl.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--hF1UpXHw--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/cpbnw8f9nqb0jxj758nl.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Time and Space Complexity
&lt;/h2&gt;

&lt;p&gt;The time and space complexity between these two answers were displayed in my submission: &lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--XdKvQVVD--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/6b36lft15jx00yys5j2y.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--XdKvQVVD--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/6b36lft15jx00yys5j2y.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;br&gt;
As we can see, the JavaScript solution was &lt;strong&gt;significantly&lt;/strong&gt; more costly than the Ruby solution. &lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;There are so many different ways to solve these LeetCode questions and this is simply an example of two different ways to do it. It's easy to overthink your answer as you're going through the process of trying to solve the problem, but it's good to struggle for a bit and see what you can extract from memory. As you're practicing it's also important to be mindful of how much time you spend trying to solve a problem so that you don't burn yourself out on something so small. &lt;/p&gt;

&lt;p&gt;Hope this was useful, good luck practicing these questions! &lt;/p&gt;

</description>
      <category>ruby</category>
      <category>javascript</category>
      <category>whiteboard</category>
      <category>leetcode</category>
    </item>
    <item>
      <title>Adding PDF to React Site</title>
      <dc:creator>Sofia Jonsson</dc:creator>
      <pubDate>Mon, 07 Oct 2019 04:37:32 +0000</pubDate>
      <link>https://dev.to/sofiajonsson/adding-pdf-to-react-site-f36</link>
      <guid>https://dev.to/sofiajonsson/adding-pdf-to-react-site-f36</guid>
      <description>&lt;h2&gt;
  
  
  Intro
&lt;/h2&gt;

&lt;p&gt;I'm currently working on my portfolio site and want to incorporate a PDF version of my resume. To do so I made a copy of my resume, convert it into a PDF and then dragged it into the React components folder of my project. I tried &lt;em&gt;several&lt;/em&gt; tutorials to try to simply get a PDF to render on-screen and finally came across a solution which I am here to share with you today.&lt;/p&gt;

&lt;p&gt;If you're looking to generate a PDF document in React, look no further than React-PDF.js and Logan Bittner's detailed blog post on &lt;em&gt;How to Build a PDF Viewer with React and PDF.js&lt;/em&gt;. I was then able to take what I learned from this tutorial and incorporate it into my personal project.&lt;br&gt;
&lt;a href="https://www.pdftron.com/blog/react/how-to-build-a-react-pdf-viewer/" rel="noopener noreferrer"&gt;https://www.pdftron.com/blog/react/how-to-build-a-react-pdf-viewer/&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What is PDF.js?
&lt;/h2&gt;

&lt;p&gt;It is a JavaScript library that displays Portable Document Format(PDF) files through HTML5 Canvas. It is a general-purpose, web standards-based platform for parsing and rendering PDFs and can work as a part of a website or a browser. &lt;/p&gt;

&lt;h2&gt;
  
  
  Outline
&lt;/h2&gt;

&lt;p&gt;These guidelines are as stated by Logan's blog&lt;br&gt;
*If starting a new project&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Install Dependencies* &lt;/li&gt;
&lt;li&gt;Create Project*&lt;/li&gt;
&lt;li&gt;Setup Project&lt;/li&gt;
&lt;li&gt;Define the Backend &lt;/li&gt;
&lt;li&gt;Invoke pdf.js&lt;/li&gt;
&lt;li&gt;Implementation of Webviewer&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Diving In
&lt;/h2&gt;

&lt;p&gt;If you are starting a new project, make sure to install your dependencies before moving on and starting your local server. Next, we move on to the project setup. The blog post guides you through the setup of your root file and the creation of your first component. As I progressed, I incorporated this new component in my Linked routes, alongside my other specified pages so that I could direct my user to this PDF viewer. Functionality gets added as we define our backend and we are able to pass a class to implement these functions to the component. By creating separate backend classes to assist the component to render the PDF we make our code more reusable and adaptable for future changes. This is the alternative I was looking for to hard-coding my resume. To implement pdf.js, we need to download the library from &lt;a href="https://mozilla.github.io/pdf.js/getting_started/#download" rel="noopener noreferrer"&gt;https://mozilla.github.io/pdf.js/getting_started/#download&lt;/a&gt; and derive contents inside our &lt;code&gt;public&lt;/code&gt; folder. If you are confused about how your files should be structured at this point, you can refer to Logan's:&lt;br&gt;
 &lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fxwi3k3ur2npvi47bqvri.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fxwi3k3ur2npvi47bqvri.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;br&gt;
and mine: &lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fnyhx53uexigmtw0x2xv8.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fnyhx53uexigmtw0x2xv8.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In order to add features such as page manipulation or real-time collaboration, we implement the Webviewer library. Once again, download the contents from &lt;a href="https://www.pdftron.com/documentation/web/get-started/manually/" rel="noopener noreferrer"&gt;https://www.pdftron.com/documentation/web/get-started/manually/&lt;/a&gt; and extract them to your public file (as seen in my file structure above). This isn't necessary and your PDF will render without it, but it'll make all the difference aesthetically and user-wise. This is where I decided to stop as I did not want a PDF seal on my resume and I did not need the extended functionality of the extended backends that Logan goes over at the end of his article. &lt;/p&gt;

&lt;h2&gt;
  
  
  Reflection
&lt;/h2&gt;

&lt;p&gt;Incorporating the code from the tutorial into my pre-existing project was super easy. Finding a tutorial that actually did what it set out to do was by far the hardest part. &lt;br&gt;
By creating a simple route: &lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fbtoj9h3ehl6ufclktwnj.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fbtoj9h3ehl6ufclktwnj.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;br&gt;
I was able to create a separate route for the PDF that I could &lt;strong&gt;Link&lt;/strong&gt; to and that could be reached through the click of an icon. &lt;/p&gt;

&lt;p&gt;Good luck, and have fun!&lt;/p&gt;

</description>
      <category>react</category>
      <category>pdf</category>
      <category>pdfjs</category>
      <category>beginners</category>
    </item>
    <item>
      <title>Rails Refresher</title>
      <dc:creator>Sofia Jonsson</dc:creator>
      <pubDate>Sun, 29 Sep 2019 18:53:42 +0000</pubDate>
      <link>https://dev.to/sofiajonsson/rails-refresher-3i9h</link>
      <guid>https://dev.to/sofiajonsson/rails-refresher-3i9h</guid>
      <description>&lt;p&gt;This blog post is intended to be used as a refresher for basic Ruby on Rails Concepts. This might be helpful for someone who learned Rails a while ago and finds themselves asking "what was that one command again...?"&lt;br&gt;
So, first off..&lt;/p&gt;

&lt;h2&gt;
  
  
  What is Ruby on Rails?
&lt;/h2&gt;

&lt;p&gt;Ruby on Rails is a web-application framework that includes everything needed to create database-backed web applications according to the Model-View-Controller(MVC) and implementing a RESTful design. Rails has two main guiding philosophies: &lt;strong&gt;Don't Repeat Yourself&lt;/strong&gt; (keep it DRY) and &lt;strong&gt;Convention over Configuration&lt;/strong&gt;. What this means is that you want to keep your code as concise as possible. Writing lots of unnecessary code results in WET code (We Like Typing) which honestly nobody likes. Rails also has its own opinion over the best ways to do things in a web application and default to this set standard of conventions, instead of requiring you, the developer, to specify minutiae through countless configuration files. &lt;/p&gt;

&lt;h2&gt;
  
  
  Object Oriented Principles
&lt;/h2&gt;

&lt;p&gt;In Ruby there are four principles of OOP: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Abstraction&lt;/strong&gt;: Keeping the viewed interface as simple as possible&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Polymorphism&lt;/strong&gt;: Using the same method for multiple different objects. Modules and ActiveRecord(AR) are examples of this. They help your code be more dynamic and help you keep it DRY&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Inheritance&lt;/strong&gt;: Models in Rails inherit from AR and provide us with helpful methods like find, sum, where, and average. Also helps keep code DRY!&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Encapsulation&lt;/strong&gt;: Maintaining a barrier wherever possible. In Ruby we utilize Encapsulation when we only use attr_reader. This keeps the user of the application from being able to alter any of the code. When we incorporate attr_writer and attr_accessor we allow outside access. In Rails we use strong parameters to encapsulate and shield our attributes. Without strong params we are subject to outside hacking. By setting a private method like set_user and user_params we can limit @user to only be accessible within the scope of the show, edit, update, and destroy actions. The User will also only have access to changing their :username, :password, :bio, :photo_url.
For a UsersController with a follow to follower relationship we would set up it up as so: 
&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--5ddVRGni--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/916my8lr3l6bn8z6q0rp.png" alt="Alt Text"&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Creating a New Rails Project
&lt;/h2&gt;

&lt;p&gt;First, make sure you have Rails installed on your machine, if you are reading this as a refresher, then it should already be there. You can check which version you have by running &lt;code&gt;rails --v&lt;/code&gt; and if its something like "Rails 6.0.0" then you're good to continue. To create a new application run the line &lt;code&gt;rails new name_of_new_project&lt;/code&gt;. Go ahead and open up your text editor, I personally use atom so I type in &lt;code&gt;atom .&lt;/code&gt; and then proceed to run the server in my terminal to see that my initial set up is working &lt;code&gt;rails s&lt;/code&gt;. You should be able to see this default image on &lt;a href="http://localhost:3000"&gt;http://localhost:3000&lt;/a&gt;&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--tptHPU0A--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/vn3sa5li4lxoug2b8c4e.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--tptHPU0A--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/vn3sa5li4lxoug2b8c4e.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Generate MVC
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--QTINFNR8--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/c8njqxex2flveolwvg6v.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--QTINFNR8--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/c8njqxex2flveolwvg6v.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;br&gt;
The Model maintains the relationship between the objects and the databases through AR, and handles validation, association, traction, etc. &lt;br&gt;
The View, ActionView, is what displays our code onto the webpage. The Controller, ActionController handles the commands for your Views. For instance, if you want to "show" your page, then you need to create a command for it in the controller. &lt;/p&gt;

&lt;p&gt;This is where CRUD comes in. On an interactive web-page, you will often find yourself creating features for users to &lt;strong&gt;Create&lt;/strong&gt;, &lt;strong&gt;Read&lt;/strong&gt;, &lt;strong&gt;Update&lt;/strong&gt;, and &lt;strong&gt;Destroy&lt;/strong&gt; content. As an example, you can think about Instagram. If you were to create an Instagram copy site with Rails you would want users to be able to see their posts (Read), upload new content (Create), edit their captions (Update), and delete a comment or post (Destroy). &lt;/p&gt;

&lt;p&gt;If you're in a hurry and you know which unnecessary files you want to remove for space complexity, then you can run the incredible &lt;code&gt;rails g scaffold _name_ _attribute_:_type_&lt;/code&gt; and Scaffold your models. &lt;br&gt;
*g is short for generate (you can write it out if fully if you love typing)&lt;/p&gt;

&lt;p&gt;However, if you want to do it manually, and were making an application about Movies then you would run the commands: &lt;/p&gt;

&lt;p&gt;&lt;code&gt;rails g model Movie name: string rating: integer&lt;/code&gt;&lt;br&gt;
&lt;code&gt;rails g controller Movies index show&lt;/code&gt; &lt;br&gt;
&lt;code&gt;rails g migration AddBiographyTo Movies biography:text&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Note&lt;/em&gt; that the controller and migration are written plurally as the Model is singular. &lt;/p&gt;

&lt;p&gt;At this point in your setup, you can run &lt;code&gt;rails db:migrate&lt;/code&gt; to run the migration and set up your relationships. Make sure your relationships between your models are set up the way you intended before you run the migration. &lt;/p&gt;

&lt;p&gt;If you make a mistake and need to "go-back" then you can run &lt;code&gt;rails db:rollback&lt;/code&gt; and keep running it until you reach the point you want to continue from. This command is like the ctrl+z or cmd+z of your migrations but use it sparingly and go slow if you do end up needing it!&lt;/p&gt;

&lt;p&gt;Associations are connections between models and make common operations much easier in your code. So for our Movie example, we could create another Model of "Directors" where both will have a relationship to AR and we can establish their relationship to each other through association.&lt;br&gt;&lt;br&gt;
The Associations between the models through AR use one of the six: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;belongs_to&lt;/li&gt;
&lt;li&gt;has_one &lt;/li&gt;
&lt;li&gt;has_many &lt;/li&gt;
&lt;li&gt;has_many :through&lt;/li&gt;
&lt;li&gt;has_one :through &lt;/li&gt;
&lt;li&gt;has_and_belongs_to_many&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The &lt;code&gt;belongs_to&lt;/code&gt; association will be written in singular due to it belonging to &lt;strong&gt;one&lt;/strong&gt; element. The plural &lt;code&gt;has_many&lt;/code&gt; pertains to the plural elements in the model. Ex: &lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--MRqiQQPL--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/k11vbzd1mpuy88acrokp.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--MRqiQQPL--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/k11vbzd1mpuy88acrokp.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Routes
&lt;/h2&gt;

&lt;p&gt;Rails router organizes the URLs and expedites them to a controllers action (Rack application) and generates &lt;strong&gt;paths&lt;/strong&gt; and URLs removing the need to hardcode strings within your Views. &lt;br&gt;
When naming a route make sure to use the plural when defining a route for a resource. Ex: &lt;code&gt;resurces :movies&lt;/code&gt;&lt;br&gt;
If you are ever confused about what routes you have, you can always rake/run your routes in the terminal using to show the parameters of your routes.&lt;br&gt;
A resourceful route in Rails maps between the HTTP verbs and the URLs to the controller actions. By convention, each of these actions map to a distinct CRUD operation in a database.&lt;br&gt;
 Ex: &lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--BHoeW_1p--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/ud9v59unifahehikcvxt.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--BHoeW_1p--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/ud9v59unifahehikcvxt.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I think that covers the basics. There are way more concepts and topics I could dive into, but I might just have to save them for a deep dive blog post! &lt;/p&gt;

</description>
      <category>rails</category>
      <category>ruby</category>
      <category>beginners</category>
      <category>refresher</category>
    </item>
    <item>
      <title>Staying Productive While Traveling</title>
      <dc:creator>Sofia Jonsson</dc:creator>
      <pubDate>Sun, 22 Sep 2019 19:59:16 +0000</pubDate>
      <link>https://dev.to/sofiajonsson/staying-productive-on-the-road-lg0</link>
      <guid>https://dev.to/sofiajonsson/staying-productive-on-the-road-lg0</guid>
      <description>&lt;p&gt;If you're like me you have probably found it difficult to balance learning, coding, and everything else that goes on in your daily life when you're on the road and traveling around. I personally struggled with how I was going to keep up with my studies while traveling to Colorado to attend a friends wedding. My main priorities at this time are to network, study algorithms, and code to keep my skills sharp, but I've found this a bit difficult as I'm out of my normal productive work environment.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://i.giphy.com/media/B2NKPKFTHtB7rTYNhN/giphy.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://i.giphy.com/media/B2NKPKFTHtB7rTYNhN/giphy.gif" alt="Productivity"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Staying on track can be difficult, but I found that it boils down to three main points: &lt;/p&gt;

&lt;h2&gt;
  
  
  1. Schedule time for work
&lt;/h2&gt;

&lt;p&gt;I know this might sound redundant, but when you're out of your normal routine and visiting old friends and attending wedding festivities it can be difficult to find the time for yourself and your work- so plan ahead! &lt;br&gt;
Set time aside each morning to go visit a coffee shop and make sure you designate this for your work. Make the most of your time, think of it as High-Intensity Interval Training for your brain. I'm sure you've heard that you need to look away from your screen every 20 minutes for better eye health, but have you heard of interval focus for productivity? This quote from J.D. Meier, a project manager, and productivity blogger states: &lt;/p&gt;

&lt;p&gt;&lt;em&gt;"A 20-minute chunk of time is a very useful slice of time and the productive possibilities are endless if you can sustain your focus. The key is to know that sustained thinking takes energy, and it burns out. To address this, take breaks to recharge and renew. Five-minute breaks are a great way to stay focused"*&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Leverage your network
&lt;/h2&gt;

&lt;p&gt;You never know who the people you know, know. Yeah, that's a tongue twister, but seriously, I had no clue I knew this many people with connections to people in tech. Depending on where you are and who you know this might be easier or more difficult but reach out to the people you know because if you don't ask you'll never know. If you can try to meet up with those people while you're in your set destination. &lt;/p&gt;

&lt;h2&gt;
  
  
  3. Make a plan for when you return.
&lt;/h2&gt;

&lt;p&gt;It's difficult to achieve everything you plan for during your time away from your normal routine. The most important thing is to keep busy, get at least the bare minimum done, and plan to make up for what you have not been able to achieve when you get back. If you can be proactive and plan to make up for what you lost while traveling or on vacation you'll save yourself the anxiety of what you have not been able to achieve since you've already set a plan to make up for it. &lt;/p&gt;

&lt;h2&gt;
  
  
  4. Have some fun while you're at it
&lt;/h2&gt;

&lt;p&gt;Seriously, don't be so hard on yourself. Coding and learning are important, but make sure to make time for yourself and the people you love while you're out wherever you are as you'll feel more prepared to return to your work if you actually enjoyed yourself as you were gone. &lt;/p&gt;

&lt;p&gt;Good luck out there!&lt;br&gt;
&lt;a href="https://i.giphy.com/media/xTiTnJ1DMox0mM5N6M/giphy.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://i.giphy.com/media/xTiTnJ1DMox0mM5N6M/giphy.gif" alt="Good Luck"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Source: &lt;a href="https://www.theglobeandmail.com/report-on-business/careers/management/how-to-focus-in-20-minute-bursts/article13327566/"&gt;https://www.theglobeandmail.com/report-on-business/careers/management/how-to-focus-in-20-minute-bursts/article13327566/&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>productivity</category>
      <category>beginners</category>
      <category>planning</category>
    </item>
    <item>
      <title>Deploying to AWS - help</title>
      <dc:creator>Sofia Jonsson</dc:creator>
      <pubDate>Mon, 16 Sep 2019 01:16:03 +0000</pubDate>
      <link>https://dev.to/sofiajonsson/deploying-to-aws-help-2556</link>
      <guid>https://dev.to/sofiajonsson/deploying-to-aws-help-2556</guid>
      <description>&lt;p&gt;A few months ago, during my Flatiron days, I decided to create a guide to deploying your app to Heroku: &lt;a href="https://dev.to/sofiajonsson/deploying-your-app-to-heroku-ob6"&gt;https://dev.to/sofiajonsson/deploying-your-app-to-heroku-ob6&lt;/a&gt; Heroku is a great free platform, but what you're not paying for is a quick and responsive website. &lt;/p&gt;

&lt;p&gt;I'm in the process of uploading my portfolio site through Amazon Web Services (AWS) using S3 and route 53 and have run into an issue getting my static endpoint to display the uploaded application. I have spent hours browsing blog posts and StackOverflow and there's something I'm not understanding. &lt;br&gt;
Instead of seeing my site I get a 404 NoSuchKey error:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--QsOLV0GW--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/mcvyqtijxixl3i0rjta3.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--QsOLV0GW--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/mcvyqtijxixl3i0rjta3.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The YouTube tutorial I've been following suggests setting my endpoint to index.html, but that's not working and I'm assuming it's because the file is nested within one of my other React.js files? I also tried with index.js as that would make more sense, but ran into the same issue. When I tried to provide a direct location such as "src/index.js"or "public/index.html" I would get the following error:&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--kjhdSm-E--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/4s8bj22jxtdsk8f4bqm0.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--kjhdSm-E--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/4s8bj22jxtdsk8f4bqm0.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;From what I've gathered, AWS is great for hosting static sites and suggests using vanilla JS, so could that be the problem?&lt;/p&gt;

&lt;p&gt;I'm curious to hear about your experiences uploading to AWS using S3 and route 53  and any tips or tricks you might have. I'm going to keep working on this and hopefully follow up with an outlined post in case anyone else runs into this issue.&lt;/p&gt;

</description>
      <category>discuss</category>
      <category>aws</category>
      <category>s3</category>
      <category>route53</category>
    </item>
    <item>
      <title>Web Scraping for custom API</title>
      <dc:creator>Sofia Jonsson</dc:creator>
      <pubDate>Sat, 07 Sep 2019 17:38:03 +0000</pubDate>
      <link>https://dev.to/sofiajonsson/web-scraping-for-custom-api-6dp</link>
      <guid>https://dev.to/sofiajonsson/web-scraping-for-custom-api-6dp</guid>
      <description>&lt;h3&gt;
  
  
  Intro
&lt;/h3&gt;

&lt;p&gt;For my final project at the Flatiron School, I decided that I wanted to build a custom &lt;strong&gt;Ski&lt;/strong&gt; &lt;em&gt;resort&lt;/em&gt;, &lt;em&gt;forecast&lt;/em&gt;, and &lt;em&gt;snow report&lt;/em&gt; tracking app. I recently moved back to the Seattle area after spending the last four years in a remote Colorado ski town and decided to build something I would actually want to use.&lt;/p&gt;

&lt;p&gt;The idea stemmed from my obsessive stalking of weather in different cities near Seattle as I try to plan for my weekend activities. Where am I going to find the warmest weather? Where will I have most sun? Most importantly, where can I avoid the rain? I keep track of this information within my weather app on my iPhone and decide what my plans are based on that incoming information. &lt;/p&gt;

&lt;p&gt;During the winter I like to do the exact same thing, but I'm browsing various websites checking snow reports, ski resort ticket prices, and weather forecasts. I decided to create an application that incorporated all of those information points that I value as a user where I could then "favorite" them and keep track of them on my own personal page. &lt;/p&gt;

&lt;p&gt;After planning out the details I went looking for online API's both free and paid ones. I quickly realized that no online resource was going to provide me with the exact data that I wanted, let alone get half of it and for a decent price so I decided to teach myself how to create a web scraper. &lt;/p&gt;

&lt;h3&gt;
  
  
  Scraping
&lt;/h3&gt;

&lt;p&gt;There is a great online resource I came across that guides the developer(you and I) through the process of creating a clean and efficient scraping tool by utilizing Ruby, Nokogiri, and HTTParty. The back-end of my project is coded in Ruby on Rails and I highly recommend watching this 30-minute video to create a basic, yet efficient scraper. &lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Frfphixpyx5xqsxb7lm1b.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Frfphixpyx5xqsxb7lm1b.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;br&gt;
&lt;a href="https://www.youtube.com/watch?v=b3CLEUBdWwQ" rel="noopener noreferrer"&gt;Link to YouTube Video&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  Source of Data
&lt;/h3&gt;

&lt;p&gt;I sourced my information from a public website, and since my projects are just for fun and for my portfolio I'm not going to run into any copyright issues. I decided to scrape 3 different pages off of &lt;em&gt;skiresort.info&lt;/em&gt; and limit my data to North American resorts. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fjrcmto3zaa955a39l4la.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fjrcmto3zaa955a39l4la.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I have linked my project at the bottom if anyone is interested in checking out my scraping file. It's located in &lt;em&gt;back_end_final/scraper.rb&lt;/em&gt; I believe I scraped almost 90 snow reports, 500ish forecast reports, and almost 1300 resorts for my project. By inspecting the website and targeting the specific id of the element I wanted to be scraped I was able to play around in the terminal until I had every bit of data, down the proper weather icon for the day in my database. &lt;/p&gt;
&lt;h3&gt;
  
  
  Diving Deep
&lt;/h3&gt;

&lt;p&gt;If you look at the code, you'll notice that my final function &lt;strong&gt;resort scraper&lt;/strong&gt; is filled with ternary statements. &lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2F32ycsseakxp38ua78o5g.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2F32ycsseakxp38ua78o5g.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;br&gt;
One of the most difficult things that I came across in this process was dealing with incomplete data. &lt;em&gt;skiresort.info&lt;/em&gt; hosts so much data on their site that they are unable to keep the amounts available for each resort uniform. Some small ski resort in Canada simply cannot have the same amount of information about it as Whistler or Vail. I dealt with this problem by utilizing ternary statements and diving into the nth-child elements to target the exact data point I wanted for my application. &lt;/p&gt;

&lt;p&gt;It's pretty hacky looking and I should probably refactor it, but hey, it works!&lt;br&gt;
Extracting my own data for a project was a lot of fun and definitely see myself using a Web Scraper for my upcoming projects. I realized that &lt;strong&gt;Pow Tracker&lt;/strong&gt; * only * scrapes data from the date that you run the scraping function. Since that makes for a pretty inefficient tracker of data, I would like to set a stretch goal for myself to automate the scraper so that I can have real-time data to work with. &lt;/p&gt;


&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev.to%2Fassets%2Fgithub-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/sofiajonsson" rel="noopener noreferrer"&gt;
        sofiajonsson
      &lt;/a&gt; / &lt;a href="https://github.com/sofiajonsson/back_end_final" rel="noopener noreferrer"&gt;
        back_end_final
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      Pow Tracker Final Project For Flatiron School Back End
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;
&lt;div class="markdown-heading"&gt;
&lt;h1 class="heading-element"&gt;Welcome to Pow Tracker!&lt;/h1&gt;
&lt;/div&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;What is Pow Tracker?&lt;/h2&gt;
&lt;/div&gt;
&lt;p&gt;This app was created for the avid skiier, or ski vacationer to check out North American stats for different ski resorts, their forecasts, and their snow reports. Imagine you are visiting Salt Lake City and want to make the most of your ski trip.. where do you go for the best conditions? You could use a couple of apps, or google your way through a couple of pages, OR you could use Pow Tracker! Pow Tracker pulls live data from the internet to provide the user with current stats on weather, snowfall, what kind of terrain a resort has, and how much each resort costs. As a visitor to the site you can access all these features, but as a user, you will be able to "favorite" a resort, forecast, and snow report and have those stats render on your personalized site…&lt;/p&gt;
&lt;/div&gt;
  &lt;/div&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/sofiajonsson/back_end_final" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;/div&gt;


</description>
      <category>webscraping</category>
      <category>httparty</category>
      <category>beginners</category>
      <category>nokogiri</category>
    </item>
  </channel>
</rss>
