<?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: William Raffaelle</title>
    <description>The latest articles on DEV Community by William Raffaelle (@wraffaelle98).</description>
    <link>https://dev.to/wraffaelle98</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%2F893461%2F7ac8f380-937c-4534-a6b7-262b2e1d9617.png</url>
      <title>DEV Community: William Raffaelle</title>
      <link>https://dev.to/wraffaelle98</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/wraffaelle98"/>
    <language>en</language>
    <item>
      <title>Event-Driven Python on AWS</title>
      <dc:creator>William Raffaelle</dc:creator>
      <pubDate>Tue, 02 Aug 2022 02:58:00 +0000</pubDate>
      <link>https://dev.to/wraffaelle98/event-driven-python-on-aws-4jc1</link>
      <guid>https://dev.to/wraffaelle98/event-driven-python-on-aws-4jc1</guid>
      <description>&lt;p&gt;Tonight I finished my first data engineering project on AWS. This project was based off of a challenge on the &lt;a href="https://acloudguru.com/blog/engineering/cloudguruchallenge-python-aws-etl" rel="noopener noreferrer"&gt;A Cloud Guru website&lt;/a&gt;. The challenge involves automating an ETL processing pipeline for COVID-19 data using Python and cloud services. This was my introduction to data engineering and ETL processing on AWS. I got to use my Python skills and learn about different cloud services in completing this project.&lt;/p&gt;

&lt;h2&gt;
  
  
  ETL
&lt;/h2&gt;

&lt;p&gt;To begin, I created a Python compute job. The job runs once a day thanks to a CloudWatch rule. This means that the function will process COVID-19 data that is updated daily. &lt;/p&gt;

&lt;h2&gt;
  
  
  DATA
&lt;/h2&gt;

&lt;p&gt;Two datasets were used in this project:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;a href="https://github.com/nytimes/covid-19-data/blob/master/us.csv" rel="noopener noreferrer"&gt;New York Times repository&lt;/a&gt; updated daily&lt;/li&gt;
&lt;li&gt;&lt;a href="https://raw.githubusercontent.com/datasets/covid-19/master/data/time-series-19-covid-combined.csv" rel="noopener noreferrer"&gt;Johns Hopkins dataset&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The Python function downloads each dataset using the Pandas library. Additionally, a merge is done on both datasets to ensure that days that do not exist in both datasets are removed.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;url_data = (r'https://raw.githubusercontent.com/nytimes/covid-19-data/master/us.csv')&lt;br&gt;
    data_csv = pd.read_csv(url_data)&lt;/code&gt; &lt;code&gt;url_data2 = (r'https://raw.githubusercontent.com/datasets/covid-19/master/data/time-series-19-covid-combined.csv')&lt;br&gt;
    data_csv2 = pd.read_csv(url_data2)&lt;/code&gt; &lt;code&gt;data_csv = data_csv.merge(data_csv2, how='inner', on='date')&lt;/code&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  DATA CLEANING
&lt;/h2&gt;

&lt;p&gt;Next, the data was transformed. To do so, any non-US data was removed from the dataset. &lt;/p&gt;

&lt;h2&gt;
  
  
  LOAD
&lt;/h2&gt;

&lt;p&gt;The data was then loaded into a DynamoDB table using the Boto3 put_item method.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8m8c0dqy5bhf7z8l6bfw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8m8c0dqy5bhf7z8l6bfw.png" alt="DynamoDB" width="800" height="452"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  NOTIFY
&lt;/h2&gt;

&lt;p&gt;An SNS topic was created to notify individuals that the database had been loaded. This message includes the number of rows updated in the database.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjhgl5lzrmzkn1bgxg5pw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjhgl5lzrmzkn1bgxg5pw.png" alt="ETL NOTIFICATION" width="800" height="221"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  ERROR HANDLING
&lt;/h2&gt;

&lt;p&gt;Some error handling was implemented to speed up data processing and ensure that the compute job responds to malformed data properly. &lt;br&gt;
To speed up ETL processing, the function determines whether it is to perform an initial load or an update. To do so, the function checks whether the row is already in the database or not. If the row is already in the database, the processing stops. This way only the current day's numbers are updated and not the entire database.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;r = table.get_item(Key={'date' : row.date})&lt;br&gt;
            if r.get('Item') == None:&lt;br&gt;
                batch.put_item(json.loads(row.to_json(), parse_float=Decimal))&lt;br&gt;
                new += 1&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Second, the function checks for malformed data. If the date format is incorrect, or the case or death numbers are not inputted as numbers, the processing stops and a notification is sent stating that the data is malformed. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fedr90c1enbctkq4euy6h.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fedr90c1enbctkq4euy6h.png" alt="SNS Notification" width="800" height="260"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Using Systems Manager Parameter Store the function is able to detect if processing was cancelled last time it ran. If this is the case and the data is still malformed, the function will skip the malformed rows and continue with processing.&lt;/p&gt;

&lt;h2&gt;
  
  
  INFRASTRUCTURE
&lt;/h2&gt;

&lt;p&gt;A CloudFormation template was then written to define the project's resources. This way the project can be deployed in any AWS environment. The resources include the following: Lambda function, CloudWatch rule, SNS trigger, DynamoDB table. &lt;/p&gt;

&lt;h2&gt;
  
  
  DASHBOARD
&lt;/h2&gt;

&lt;p&gt;To build a dashboard AWS Quicksight was used. The dashboard includes the following: sum of cases by date, sum of deaths by date, and sum of recovered by date.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F69e72wf2fknnlrtvuh1y.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F69e72wf2fknnlrtvuh1y.jpg" alt="Cases" width="800" height="395"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fe9h15l56h2b50p4iq4kn.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fe9h15l56h2b50p4iq4kn.jpg" alt="Deaths" width="800" height="416"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fx9hknjya6cb3i4ardang.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fx9hknjya6cb3i4ardang.jpg" alt="Recovered" width="800" height="416"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  ADDITIONAL
&lt;/h2&gt;

&lt;p&gt;A second Lambda function was written to download the updated DynamoDB table to a .csv file that is uploaded to S3. This way, QuickSight can use the latest data to build a dashboard. This function is triggered by a CloudWatch rule. It runs daily immediately after the first function runs and after the database has been updated. &lt;/p&gt;

&lt;p&gt;The code for this project can be found on [GitHub].(&lt;a href="https://github.com/wraffaelle98/Event-Driven-Python-on-AWS" rel="noopener noreferrer"&gt;https://github.com/wraffaelle98/Event-Driven-Python-on-AWS&lt;/a&gt;)&lt;/p&gt;

</description>
      <category>etl</category>
      <category>python</category>
      <category>cloud</category>
      <category>database</category>
    </item>
    <item>
      <title>Cloud Resume Challenge</title>
      <dc:creator>William Raffaelle</dc:creator>
      <pubDate>Mon, 25 Jul 2022 02:36:00 +0000</pubDate>
      <link>https://dev.to/wraffaelle98/cloud-resume-challenge-3chf</link>
      <guid>https://dev.to/wraffaelle98/cloud-resume-challenge-3chf</guid>
      <description>&lt;p&gt;I recently completed the Cloud Resume Challenge by Forrest Brazeal. I thought the challenge was well-thought-out and had the right amount of difficulty. Although I already had some cloud experience prior to this project, the project put my skills to the test and I learned some stuff along the way. This was my first time deploying a full-stack web application using an infrastructure as code tool (CloudFormation). I was also exposed to GitHub Actions which I used to create a deployment pipeline. All in all this challenge was rewarding and I am happy with the end result.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Phase 1. Front End&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The resume website was written in HTML. I used the following example to help me: &lt;a href="https://codepen.io/emzarts/pen/OXzmym" rel="noopener noreferrer"&gt;A simple HTML Resume&lt;/a&gt;. I also used Adobe Dreamweaver to fix some issues I had with spacing. Next, I styled the website with CSS. The code was then uploaded to an S3 bucket which had static web hosting enabled. I experienced a strange error with Route 53, so I decided to use Cloudflare as a DNS provider.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Femz1sjjv1luyblnaxvs3.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Femz1sjjv1luyblnaxvs3.png" alt="HTML Resume" width="800" height="847"&gt;&lt;/a&gt; &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Phase 2. API&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;To start this phase, I first created a DynamoDB table to store the visitor counter. I then created a REST API using AWS API Gateway. The API had one simple GET method. I then integrated the API method with a Lambda function. I wrote the Lambda function using Python and Boto3. The function updates the database's primary key by incrementing the visitor counter by 1 and then retrieves the visitor count and returns the number. The back end is represented by the following diagram:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Figz7i7dqen0hu2yfji8i.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Figz7i7dqen0hu2yfji8i.png" alt="Infrastructure Diagram" width="800" height="403"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Phase 3. Integration&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;It was finally time to integrate my front end to my back end. To make this happen, I wrote some JavaScript. The script I wrote uses the fetch() method to make an API call; in this case to update and return the visitor counter. &lt;/p&gt;

&lt;p&gt;&lt;code&gt;fetch('')&lt;br&gt;
        .then(response =&amp;gt; response.json()) &lt;br&gt;
        .then((response) =&amp;gt; {&lt;br&gt;
          console.log(response)&lt;br&gt;
            document.getElementById('replaceme').innerText = response&lt;br&gt;
        })&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;I used Open Up The Cloud's video series Cloud Resume Challenge to help me with this part.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Phase 4. Automation&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This was by far the most challenging part of the Cloud Resume Challenge. I now needed to represent my cloud resources as configuration. To complete the Infrastructure as Code step, I used AWS CloudFormation. The template defines all the back end resources: DynamoDB table, API Gateway (method, stage, deployment), and the Lambda function. This step took a lot of troubleshooting. I used different examples I could find on the internet to help me construct the template. Here are some things I noted:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;When defining a method, even if the HttpMethod is GET, the IntegrationHttpMethod is POST&lt;/li&gt;
&lt;li&gt;A GatewayResponse can be defined to create ResponseParameters to avoid CORS issues. I defined the following ResponseParameters: &lt;code&gt;gatewayresponse.header.Access-Control-Allow-Headers: "'*'"
    gatewayresponse.header.Access-Control-Allow-Methods: "'*'"
    gatewayresponse.header.Access-Control-Allow-Origin: "'*'"&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I then used GitHub Actions to deploy my application. If I were to complete this challenge again, I would set this up earlier since it saves a lot of time. I set up an action to deploy the CloudFormation template if there is a change to the template. I set up a different workflow to push any code change to the Lambda function. Lastly, I set up a workflow to push any HTML/CSS/Javascript changes to the S3 bucket where the website code is stored. I can now make many changes to either my front end or back end code and the changes will be pushed to the cloud. CI/CD is awesome.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Phase 5. Blog&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;As soon as I hit the Publish button, I will have completed the Cloud Resume Challenge. I enjoyed doing this challenge because it helped me to understand how full-stack web applications are deployed to AWS. Although I had written Lambda functions and CloudFormation templates prior to partaking this challenge, I had never used API Gateway and CloudFormation together. The challenge helped me to realize just how powerful and important infrastructure as code is. Additionally, prior to this challenge, I had no experience with GitHub Actions. I now see how useful it is and how it can save you a lot of time when you are working on a full-stack web application in the cloud. Overall, I thought this challenge was great and I recommend it to anyone who is new or has some cloud experience. &lt;/p&gt;

&lt;p&gt;Link to resume website: &lt;a href="https://raffaelle-resume.com/" rel="noopener noreferrer"&gt;https://raffaelle-resume.com/&lt;/a&gt;&lt;/p&gt;

</description>
      <category>aws</category>
      <category>html</category>
      <category>javascript</category>
      <category>api</category>
    </item>
  </channel>
</rss>
