<?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: Banjo Obayomi</title>
    <description>The latest articles on DEV Community by Banjo Obayomi (@banjtheman).</description>
    <link>https://dev.to/banjtheman</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%2F165639%2Fafa8e588-44c1-4082-901b-feb883af706f.jpg</url>
      <title>DEV Community: Banjo Obayomi</title>
      <link>https://dev.to/banjtheman</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/banjtheman"/>
    <language>en</language>
    <item>
      <title>Building Your First HuggingFace Transformers Tool</title>
      <dc:creator>Banjo Obayomi</dc:creator>
      <pubDate>Tue, 16 May 2023 17:33:53 +0000</pubDate>
      <link>https://dev.to/aws/building-your-first-huggingface-transformers-tool-3eba</link>
      <guid>https://dev.to/aws/building-your-first-huggingface-transformers-tool-3eba</guid>
      <description>&lt;p&gt;In this post, we'll walk you through the process of building a simple tool using the &lt;a href="https://huggingface.co/docs/transformers/main/transformers_agents" rel="noopener noreferrer"&gt;Transformers library&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;The tool we're building will fetch an image of a cat from the internet each time it is run. We'll be using the Cataas (Cat-As-A-Service) API to achieve this. Let's dive in!&lt;/p&gt;

&lt;h2&gt;
  
  
  Prerequisites
&lt;/h2&gt;

&lt;p&gt;Before we embark on this exciting journey of building the Cat Fetcher tool, we need to ensure that our development environment is properly set up. This tool relies on several Python packages - &lt;code&gt;requests&lt;/code&gt; for making HTTP requests, &lt;code&gt;Pillow&lt;/code&gt; for handling images, and &lt;code&gt;transformers&lt;/code&gt; for creating the AI tool.&lt;/p&gt;

&lt;p&gt;To install these packages, you can use the following command in your terminal:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pip &lt;span class="nb"&gt;install &lt;/span&gt;requests Pillow transformers
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Fetching a Random Cat Image
&lt;/h2&gt;

&lt;p&gt;To get started, here is the code that fetches a random cat image from the &lt;a href="https://cataas.com/" rel="noopener noreferrer"&gt;Cataas API&lt;/a&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="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;PIL&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Image&lt;/span&gt;

&lt;span class="n"&gt;image&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Image&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;requests&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;https://cataas.com/cat&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;stream&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;raw&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Creating a Tool
&lt;/h2&gt;

&lt;p&gt;To create a tool that can be used by our system, we first create a class that inherits from the superclass Tool:&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;from&lt;/span&gt; &lt;span class="n"&gt;transformers&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Tool&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;CatImageFetcher&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Tool&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;pass&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This class needs the following attributes:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;name&lt;/strong&gt;: This is the name of the tool. For consistency, we'll name it cat_fetcher.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;description&lt;/strong&gt;: This will be used to populate the prompt of the agent.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;inputs and outputs&lt;/strong&gt;: These help the interpreter make educated choices about types and allow for a demo to be spawned when we push our tool to the Hub. They are both a list of expected values, which can be text, image, or audio.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;A &lt;strong&gt;call&lt;/strong&gt; method&lt;/strong&gt;: This contains the inference code.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;So, our class now looks like this:&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;from&lt;/span&gt; &lt;span class="n"&gt;transformers&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Tool&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;huggingface_hub&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;list_models&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;CatImageFetcher&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Tool&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;cat_fetcher&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
    &lt;span class="n"&gt;description&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;This is a tool that fetches an actual image of a cat online. It takes no input, and returns the image of a cat.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;inputs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
    &lt;span class="n"&gt;outputs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;image&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;__call__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&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;Image&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;requests&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;https://cataas.com/cat&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;stream&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;raw&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;resize&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="mi"&gt;256&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;256&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, we can simply test the tool:&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="n"&gt;tool&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;CatImageFetcher&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="nf"&gt;tool&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Using the Tool with an Agent
&lt;/h2&gt;

&lt;p&gt;To use the tool with an agent, we recommend instantiating the agent with the tools directly:&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;from&lt;/span&gt; &lt;span class="n"&gt;transformers.tools&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;HfAgent&lt;/span&gt;

&lt;span class="n"&gt;agent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;HfAgent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;https://api-inference.huggingface.co/models/bigcode/starcoder&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;additional_tools&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;tool&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then, we can have the agent use it with other tools:&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="n"&gt;agent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Fetch an image of a cat online and caption it for me&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;p&gt;The tool successfully fetches a cat image, and the image captioning tool captions the image. That's it! You've just created your first tool with the Transformers library.&lt;/p&gt;

&lt;p&gt;As a final step, we recommend pushing the tool to the Hugging Face Model Hub so others can benefit from it.&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="n"&gt;tool&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push_to_hub&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;YOUR-TOOL&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;p&gt;Thank you for following along! We're excited to see the creative and useful tools you'll build.&lt;/p&gt;

&lt;p&gt;To learn more about building tools check out the &lt;a href="https://huggingface.co/docs/transformers/en/custom_tools#creating-a-new-tool" rel="noopener noreferrer"&gt;official documentation&lt;/a&gt;&lt;/p&gt;

</description>
      <category>huggingface</category>
      <category>ai</category>
      <category>python</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Building an AWS Well-Architected Chatbot with LangChain</title>
      <dc:creator>Banjo Obayomi</dc:creator>
      <pubDate>Thu, 11 May 2023 18:48:15 +0000</pubDate>
      <link>https://dev.to/aws/building-an-aws-well-architected-chatbot-with-langchain-13cd</link>
      <guid>https://dev.to/aws/building-an-aws-well-architected-chatbot-with-langchain-13cd</guid>
      <description>&lt;p&gt;If you're familiar with the &lt;a href="https://aws.amazon.com/architecture/well-architected/" rel="noopener noreferrer"&gt;AWS Well-Architected Framework&lt;/a&gt;, you'll know that it offers a set of best practices designed to help you achieve secure, high-performing, resilient, and efficient infrastructure for your applications. But with a vast amount of information available, navigating the framework can be a daunting task.&lt;/p&gt;

&lt;p&gt;This is why I decided to develop a chatbot to answer questions related to the framework, offering developers quick, accurate responses complete with supporting document links. If you're interested in how this project started, I encourage you to check out my &lt;a href="https://bit.ly/3VVkQSG" rel="noopener noreferrer"&gt;previous post&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Now, in this follow-up article, I'll guide you through the process of building an enhanced version of the chatbot using the open-source library, &lt;a href="https://github.com/hwchase17/langchain" rel="noopener noreferrer"&gt;LangChain&lt;/a&gt;.&lt;/p&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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fru8rjofed5brobi3xd23.png" 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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fru8rjofed5brobi3xd23.png" alt="AWS Well-Architected Framework Chatbot conversation" width="800" height="877"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;LangChain enhances the functionality of large language model (LLM) applications, providing features such as prompt management, chains for call sequences, and data augmented generation. This step-by-step guide will cover:&lt;/p&gt;

&lt;p&gt;• Data Collection&lt;br&gt;
• Text Embedding Creation&lt;br&gt;
• Prompt Engineering&lt;br&gt;
• Chat Interface Development&lt;/p&gt;

&lt;p&gt;You can try the chatbot &lt;a href="https://banjtheman-aws-well-arch-chatbot-well-arch-chatbot-st-la-aynu2g.streamlit.app/" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;And check out the GitHub repo with the code &lt;a href="https://github.com/banjtheman/aws_well_arch_chatbot/tree/langchain_version" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;
  
  
  Data Collection: Web Scrapping
&lt;/h2&gt;

&lt;p&gt;To obtain all the necessary links from the Well-Architected Framework, I extracted all the URLs from the sitemaps. The sitemaps provide a list of all links on the page, which allowed me to efficiently create a script to fetch all the text for each page. Here's the Python function I used:&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;extract_urls_from_sitemap&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sitemap_url&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sitemap_url&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;status_code&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Failed to fetch sitemap: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;status_code&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&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="n"&gt;sitemap_content&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;content&lt;/span&gt;
    &lt;span class="n"&gt;root&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ET&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fromstring&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sitemap_content&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c1"&gt;# Extract the URLs from the sitemap
&lt;/span&gt;    &lt;span class="n"&gt;urls&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="n"&gt;elem&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;
        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;elem&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;root&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;iter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;{http://www.sitemaps.org/schemas/sitemap/0.9}loc&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&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;urls&lt;/span&gt;


 &lt;span class="c1"&gt;# Site maps for the AWS Well-Architected Framework
&lt;/span&gt;    &lt;span class="n"&gt;sitemap_url_list&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;https://docs.aws.amazon.com/wellarchitected/latest/security-pillar/sitemap.xml&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;https://docs.aws.amazon.com/wellarchitected/latest/framework/sitemap.xml&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;https://docs.aws.amazon.com/wellarchitected/latest/operational-excellence-pillar/sitemap.xml&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;https://docs.aws.amazon.com/wellarchitected/latest/reliability-pillar/sitemap.xml&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;https://docs.aws.amazon.com/wellarchitected/latest/performance-efficiency-pillar/sitemap.xml&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;https://docs.aws.amazon.com/wellarchitected/latest/cost-optimization-pillar/sitemap.xml&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;https://docs.aws.amazon.com/wellarchitected/latest/sustainability-pillar/sitemap.xml&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;]&lt;/span&gt;

    &lt;span class="c1"&gt;# Get all links from the sitemaps
&lt;/span&gt;    &lt;span class="n"&gt;full_sitemap_list&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;sitemap&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;sitemap_url_list&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;full_sitemap_list&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;extend&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;extract_urls_from_sitemap&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sitemap&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once I compiled the list, I used the LangChain Selenium Document Loader to extract all the text from each page, dividing the text into chunks of 1000 characters. Breaking the text into 1000-character chunks simplifies handling large volumes of data and ensures that the text is in useful digestible segments for the model to process.&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;load_html_text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sitemap_urls&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;loader&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;SeleniumURLLoader&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;urls&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;sitemap_urls&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;loader&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;load&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="n"&gt;text_splitter&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;RecursiveCharacterTextSplitter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;chunk_size&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;chunk_overlap&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;texts&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;text_splitter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;split_documents&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data&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;texts&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Creating Text Embeddings
&lt;/h2&gt;

&lt;p&gt;Next, I generated text embeddings for each of the pages using the &lt;a href="https://platform.openai.com/docs/guides/embeddings/what-are-embeddings" rel="noopener noreferrer"&gt;OpenAI's embeddings API&lt;/a&gt;. Text embeddings are vectors (lists) of floating-point numbers used to measure the relatedness of text strings. They are commonly used for various tasks such as search, clustering, recommendations, anomaly detection, diversity measurement, and classification. Once the embeddings were generated, I used the vector search library &lt;a href="https://github.com/facebookresearch/faiss" rel="noopener noreferrer"&gt;Faiss&lt;/a&gt; to create an index, enabling rapid text searching for each user query.&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;embed_text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;texts&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;save_loc&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;embeddings&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;OpenAIEmbeddings&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;openai_api_key&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="n"&gt;environ&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;OPENAI_API_KEY&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
    &lt;span class="n"&gt;docsearch&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;FAISS&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;from_documents&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;texts&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;embeddings&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;docsearch&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;save_local&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;save_loc&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Check out the full ingestion pipeline &lt;a href="https://github.com/banjtheman/aws_well_arch_chatbot/blob/langchain_version/ingest.py" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Building the LLM Chain: Prompt Engineering
&lt;/h2&gt;

&lt;p&gt;With the data ready, it was time to build the LLM chain to respond to user queries. The first step involved creating a PromptTemplate, which is a schema representing a prompt that can be passed to an LLM. This template consisted of a system prompt and two variables: the user's question and the context from the Well-Architected Framework relevant to the question.&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="n"&gt;TEMPLATE&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;You are an AWS Certified Solutions Architect. Your role is to help customers understand best practices on building on AWS. Return your response in markdown, so you can bold and highlight important steps for customers. If the answer cannot be found within the context, write &lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;I could not find an answer&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt; 

Use the following context from the AWS Well-Architected Framework to answer the user&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;s query. Make sure to read all the context before providing an answer.&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;Context:&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;{context}&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;Question: {question}
&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;

&lt;span class="n"&gt;QA_PROMPT&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;PromptTemplate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;template&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;TEMPLATE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;input_variables&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;question&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;context&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;p&gt;Next, I set up the process to use the LLM to respond to user queries. I instanced the &lt;a href="https://platform.openai.com/docs/models" rel="noopener noreferrer"&gt;OpenAI model&lt;/a&gt; using the &lt;code&gt;ChatOpenAI&lt;/code&gt; class and set the parameters (&lt;strong&gt;Note:&lt;/strong&gt; you need your own &lt;a href="https://platform.openai.com/account/api-keys" rel="noopener noreferrer"&gt;OpenAI API key&lt;/a&gt;). Then, I established the embeddings endpoint for user queries and the local vector store from the scraped data. Finally, I set up the &lt;code&gt;ConversationalRetrievalChain&lt;/code&gt;, which facilitates the creation of a chatbot using retrieval augmented generation (RAG) by utilizing the documents for its response.&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;setup_chain&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="n"&gt;llm&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;ChatOpenAI&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;temperature&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mf"&gt;0.7&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;openai_api_key&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="n"&gt;environ&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;OPENAI_API_KEY&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
        &lt;span class="n"&gt;model_name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;gpt-3.5-turbo&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;embeddings&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;OpenAIEmbeddings&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;openai_api_key&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="n"&gt;environ&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;OPENAI_API_KEY&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
    &lt;span class="n"&gt;vectorstore&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;FAISS&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;load_local&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;local_index&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;embeddings&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;chain&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ConversationalRetrievalChain&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;from_llm&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;llm&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;vectorstore&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;as_retriever&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;return_source_documents&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="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;chain&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The full code is &lt;a href="https://github.com/banjtheman/aws_well_arch_chatbot/blob/langchain_version/ai_utils.py" rel="noopener noreferrer"&gt;here&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Creating the Chat Interface
&lt;/h2&gt;

&lt;p&gt;The chat interface was developed using &lt;a href="https://streamlit.io/" rel="noopener noreferrer"&gt;Streamlit&lt;/a&gt;, a versatile tool for building interactive Python web applications. This code creates a simple interface with a text input for user queries and a "Submit" button to submit the query. When the "Submit" button is clicked, the query, along with the chat history, is sent to the LLM chain, which returns a response along with the referenced documents.&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;app&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;
    Purpose:
        Controls the app flow
    Args:
        N/A
    Returns:
        N/A
    &lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;

    &lt;span class="c1"&gt;# Spin up the sidebar
&lt;/span&gt;    &lt;span class="nf"&gt;sidebar&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;st&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;container&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
        &lt;span class="c1"&gt;# Load chat history
&lt;/span&gt;        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;chat&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;enumerate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;st&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;session_state&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;chat_history&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]):&lt;/span&gt;
            &lt;span class="nf"&gt;message_func&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;chat&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="nf"&gt;message_func&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;chat&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;span class="bp"&gt;False&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

            &lt;span class="c1"&gt;# st.write(chat[0])
&lt;/span&gt;            &lt;span class="c1"&gt;# st.write(chat[1])
&lt;/span&gt;            &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="n"&gt;st&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;expander&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Resources&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
                &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;doc&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;st&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;session_state&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;docs&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;
                    &lt;span class="n"&gt;st&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;write&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;doc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;metadata&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;source&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
                    &lt;span class="n"&gt;st&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;write&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;doc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;page_content&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;st&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;form&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;my_form&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;clear_on_submit&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;query&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;st&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;text_input&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Query: &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;input&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;""&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="n"&gt;placeholder&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Type your query here...&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="n"&gt;label_visibility&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;hidden&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="n"&gt;submit_button&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;st&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;form_submit_button&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;label&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Submit&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;col1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;col2&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;st&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;columns&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;span class="mf"&gt;3.2&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
        &lt;span class="n"&gt;reset_button&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;col1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;button&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Reset Chat History&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;submit_button&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;st&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;spinner&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Generating...&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
            &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;chain&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;question&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;query&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;chat_history&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;st&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;session_state&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;chat_history&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]}&lt;/span&gt;
            &lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="n"&gt;st&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;session_state&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;chat_history&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;question&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;answer&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
            &lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="n"&gt;st&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;session_state&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;docs&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;source_documents&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
            &lt;span class="n"&gt;st&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;experimental_rerun&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;  &lt;span class="c1"&gt;# Add Chat to UI
&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;reset_button&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;st&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;session_state&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;chat_history&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="p"&gt;[]&lt;/span&gt;
        &lt;span class="n"&gt;st&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;session_state&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;docs&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="p"&gt;[]&lt;/span&gt;
        &lt;span class="n"&gt;st&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;experimental_rerun&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can find the full code &lt;a href="https://github.com/banjtheman/aws_well_arch_chatbot/blob/langchain_version/well_arch_chatbot_st.py" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;

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

&lt;p&gt;In this guide, I've taken you through the process of building an AWS Well-Architected chatbot leveraging LangChain, the OpenAI GPT model, and Streamlit. We began by gathering data from the AWS Well-Architected Framework, proceeded to create text embeddings, and finally used LangChain to invoke the OpenAI LLM to generate responses to user queries.&lt;/p&gt;

&lt;p&gt;You can interact with the chatbot &lt;a href="https://banjtheman-aws-well-arch-chatbot-well-arch-chatbot-st-la-aynu2g.streamlit.app/" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;If you're interested in creating your own applications powered by LangChain, I hope this guide has offered useful insights and guidelines. Enjoy coding and exploring the possibilities of language models!&lt;/p&gt;

&lt;p&gt;Remember, chatbots are powerful tools for simplifying complex processes and making information more accessible. They can be tailored to meet various needs, and with the right tools like LangChain and OpenAI, creating an intelligent, context-aware chatbot is easier than you might think. Happy bot-building!&lt;/p&gt;

&lt;h2&gt;
  
  
  About the Author
&lt;/h2&gt;

&lt;p&gt;Banjo is a Senior Developer Advocate at AWS, where he helps builders get excited about using AWS. Banjo is passionate about operationalizing data and has started a podcast, a meetup, and open-source projects around utilizing data. When not building the next big thing, Banjo likes to relax by playing video games, especially JRPGs, and exploring events happening around him.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>langchain</category>
      <category>aws</category>
      <category>python</category>
    </item>
    <item>
      <title>How I used GPT-3 to Build 1,000 AWS Quiz Questions</title>
      <dc:creator>Banjo Obayomi</dc:creator>
      <pubDate>Wed, 22 Feb 2023 16:04:51 +0000</pubDate>
      <link>https://dev.to/banjtheman/how-i-used-gpt-3-to-build-1000-aws-quiz-questions-fdb</link>
      <guid>https://dev.to/banjtheman/how-i-used-gpt-3-to-build-1000-aws-quiz-questions-fdb</guid>
      <description>&lt;p&gt;As demand for AWS-certified professionals continues to grow, having a solid understanding of the services and architecture is becoming increasingly essential. However, preparing for certification exams can be challenging, especially when trying to find relevant and engaging study materials. &lt;/p&gt;

&lt;p&gt;In this blog post, I'll show you how I leveraged GPT-3 and the &lt;a href="https://docs.aws.amazon.com/wellarchitected/latest/framework/welcome.html" rel="noopener noreferrer"&gt;AWS Well-Architected Framework&lt;/a&gt;  to create a significantly improved quiz experience from the one I made in &lt;a href="https://twosixtech.com/automated-aws-knowledge-graph-creation-with-grakn-ai/" rel="noopener noreferrer"&gt;2020&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;This time around, I was able to generate 1,000 scenario-based multiple choice questions, each accompanied by an explanation for the correct answer and additional resources for further study.&lt;/p&gt;

&lt;p&gt;Compared to my previous quiz, which was based on a knowledge graph and offered limited question variety, this new approach allowed me to create sophisticated questions that test your understanding of real-world scenarios within each domain of the exam guide.&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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fd0qkkhwdp3sbq8i4fkwx.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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fd0qkkhwdp3sbq8i4fkwx.png" alt="Knowledge Graph Powered Quiz"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The release of &lt;a href="https://chat.openai.com/chat/" rel="noopener noreferrer"&gt;ChatGPT&lt;/a&gt; showed that generative AI is ready, and my vision can finally be implemented.&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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4vkvlwugmsxuvsw4mj8o.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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4vkvlwugmsxuvsw4mj8o.png" alt="GPT-3 Powered Quiz"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;To accomplish this, I'll walk you through the following&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Data Collection&lt;/li&gt;
&lt;li&gt;Creating Text Embeddings&lt;/li&gt;
&lt;li&gt;Prompt Engineering&lt;/li&gt;
&lt;li&gt;Question Generation&lt;/li&gt;
&lt;li&gt;Creating the Quiz&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You can try out the quiz &lt;a href="https://banjtheman-aws-saa-ai-quiz-aws-quiz-st-qvxt6f.streamlit.app/" rel="noopener noreferrer"&gt;here&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And the GitHub repo with the code &lt;a href="https://github.com/banjtheman/aws_saa_ai_quiz" rel="noopener noreferrer"&gt;here&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Data Collection
&lt;/h3&gt;

&lt;p&gt;I started by using &lt;code&gt;Selenium&lt;/code&gt; and &lt;code&gt;BeautifulSoup&lt;/code&gt; to scrape the HTML page of the Well-Architected Framework, and stored all the text on each page into a CSV. I also extracted the title and URL for each page so they can be referenced later.&lt;/p&gt;

&lt;p&gt;Here is some of the code used to gather data for each page.&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;get_page_info&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;browser&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&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;html&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="n"&gt;page_source&lt;/span&gt;
    &lt;span class="c1"&gt;# Have soup parse the website
&lt;/span&gt;    &lt;span class="n"&gt;soup&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;BeautifulSoup&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;html&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;html.parser&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c1"&gt;# Get title
&lt;/span&gt;    &lt;span class="n"&gt;title&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;soup&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;find&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;title&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="n"&gt;string&lt;/span&gt;

    &lt;span class="n"&gt;main_article&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;soup&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;find&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;main-col-body&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="c1"&gt;# main text of article
&lt;/span&gt;    &lt;span class="c1"&gt;# Get text sections
&lt;/span&gt;    &lt;span class="n"&gt;text_sections&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;main_article&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;findAll&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;p&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;text_list&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;

    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;list_item&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;text_sections&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;text_list&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;list_item&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c1"&gt;# Get info in tables
&lt;/span&gt;    &lt;span class="n"&gt;tables&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;main_article&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;findAll&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;table&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;table&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;tables&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="c1"&gt;# Add all ths and tds
&lt;/span&gt;        &lt;span class="n"&gt;tds&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;table&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;findAll&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;td&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;ths&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;table&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;findAll&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;th&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;th&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;ths&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;text_list&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;th&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;td&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;tds&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;text_list&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;td&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;json_obj&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
    &lt;span class="n"&gt;json_obj&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="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;url&lt;/span&gt;
    &lt;span class="n"&gt;json_obj&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;title&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;title&lt;/span&gt;
    &lt;span class="n"&gt;json_obj&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;sections&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;text_list&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;json_obj&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Data Cleaning
&lt;/h4&gt;

&lt;p&gt;After the raw data is collected, I did some light data cleanup to normalize text and to split up any sections over 5,000 &lt;a href="https://platform.openai.com/docs/introduction/tokens" rel="noopener noreferrer"&gt;tokens&lt;/a&gt; as their model has an upper limit of tokens it can process. I also removed text that had less than 13 tokens, as there was bunch of text that just had service names such as "Amazon S3" and no context around what it does.&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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fyzzscn0odw3ugre0a601.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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fyzzscn0odw3ugre0a601.png" alt="Data of sections"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Creating Text Embeddings
&lt;/h3&gt;

&lt;p&gt;Next, I created text embeddings for each of the pages using &lt;br&gt;
OpenAI's &lt;a href="https://platform.openai.com/docs/guides/embeddings/what-are-embeddings" rel="noopener noreferrer"&gt;embeddings API&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Text embeddings measure the relatedness of text strings. Embeddings are commonly used for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Search (where results are ranked by relevance to a query string)&lt;/li&gt;
&lt;li&gt;Clustering (where text strings are grouped by similarity)&lt;/li&gt;
&lt;li&gt;Recommendations (where items with related text strings are recommended)&lt;/li&gt;
&lt;li&gt;Anomaly detection (where outliers with little relatedness are identified)&lt;/li&gt;
&lt;li&gt;Diversity measurement (where similarity distributions are analyzed)&lt;/li&gt;
&lt;li&gt;Classification (where text strings are classified by their most similar label)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;An embedding is a vector (list) of floating point numbers. The distance between two vectors measures their relatedness. Small distances suggest high relatedness and large distances suggest low relatedness.&lt;/p&gt;

&lt;p&gt;This &lt;a href="https://github.com/openai/openai-cookbook/blob/main/examples/Question_answering_using_embeddings.ipynb" rel="noopener noreferrer"&gt;OpenAI Notebook&lt;/a&gt; provides a full end to end example of creating text embeddings&lt;/p&gt;

&lt;p&gt;Here is an example of how the text looks like as an embedding, an list of 1536 numbers that represent the text.&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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fe70p00x0rh9sl40ao3fs.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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fe70p00x0rh9sl40ao3fs.png" alt="Example Text Embeddings"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;With text embeddings we can now do a Search of all the text based on an input query. By asking &lt;code&gt;Designing VPC architectures with security components&lt;/code&gt; we get a list of the documents that has text which is relevant to the query.&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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkoklmdbig4zs5g8niqjn.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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkoklmdbig4zs5g8niqjn.png" alt="Document Serach"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  Prompt Engineering
&lt;/h3&gt;

&lt;p&gt;Prompt engineering is about designing prompts that elicit the most relevant and desired response from a Large Language Model (LLM) such as GPT-3.&lt;/p&gt;

&lt;p&gt;Crafting these &lt;a href="https://platform.openai.com/docs/guides/completion/prompt-design" rel="noopener noreferrer"&gt;prompts is an art&lt;/a&gt;, that many are still figuring out but a rule of thumb is the more detailed the prompt, the better the desired outcome.&lt;/p&gt;

&lt;p&gt;I first started out with a simple prompt&lt;/p&gt;

&lt;p&gt;&lt;code&gt;Generate a multiple choice question that can appear on a AWS Certified Solutions Architect - Associate Exam&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;The responses varied in quality without any context, here's an example of one.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;'Q1. Which of the following is a feature of Amazon EC2?
A. Automated scaling
B. Automated backups
C. Automated patching
D. Automated deployment

Answer: A. Automated scaling. Explanation: Amazon EC2 provides automated scaling, which allows users to scale their compute resources up or down based on their application needs.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here is where prompt engineering can shine. I need to "overfit" my prompt so I can get the desired outcome of a question that is derived from the study material.&lt;/p&gt;

&lt;p&gt;For this quiz, I want the questions to be about the specific testing areas for the test, and then inject the relevant content, a random scenario and have the data be returned in a JSON format so it can be used in different applications.&lt;/p&gt;

&lt;h4&gt;
  
  
  Question Generation
&lt;/h4&gt;

&lt;p&gt;To construct my prompt, I made up around 20 different scenarios like &lt;code&gt;A healthcare company wants to store and process sensitive patient information in the cloud.&lt;/code&gt; and then did a text embedding search on a topic that will show up on the exam such as &lt;code&gt;Determining when to federate a directory service with IAM roles&lt;/code&gt; and then injected the text from the retrieved documents to make a robust prompt.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Generate a scenario-based multiple-choice question for the AWS Certified Solutions Architect Associate Exam using the provided scenario, context, and knowledge area. 

The response must be returned in the specified JSON format with nothing else. 

There should be one correct answer and 3 incorrect answers. The incorrect answers should be response options that a candidate with incomplete knowledge or skill might choose. 

Provide an explanation for the answer to each question as well. The question must be about a scenario, and not a simple definition question such as What type of storage is Amazon S3. 

The answers must also be action-oriented and not just the name of a service.

Scenario: A healthcare company wants to store and process sensitive patient information in the cloud."

Context:
Require identities to dynamically acquire temporary credentials. For workforce identities, use AWS IAM Identity Center (successor to AWS Single Sign-On)....

Knowledge Area: Determining when to federate a directory service with IAM roles

JSON Format: {"question": "","answer_choices": [{"answer": "","is_correct": "","explanation": ""},{"answer": "","is_correct": "","explanation": ""},{"answer": "","is_correct": "","explanation": ""},{"answer": "","is_correct": "","explanation": ""}]}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Using OpenAI's &lt;a href="https://platform.openai.com/docs/api-reference/completions" rel="noopener noreferrer"&gt;Completions API&lt;/a&gt;, with the prompt, I was able to use a Python script to iterate through all 189 domains on the exam guide with the example scenarios to generate 1,000 questions. &lt;/p&gt;

&lt;p&gt;Here is an example question&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Which of the following is the best practice for granting consumers access to a healthcare company's AWS resources, while storing and processing sensitive patient information in the cloud?

A. Federate with AWS IAM roles to access AWS accounts
B. Use Amazon Cognito identity pools and assign them a set of temporary, limited privilege credentials to access AWS resources
C. Grant consumers access to AWS resources using IAM users with long-term access keys
D. Grant consumers access to AWS resources using AWS Single Sign-On
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And each question provides explanations, and link to the reference material.&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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F721860fovbhty9slt4qo.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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F721860fovbhty9slt4qo.png" alt="Explanations and Reference Material"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  Pitfalls
&lt;/h4&gt;

&lt;p&gt;Not every question generated by GPT-3 was perfect. Sometimes the JSON did not have a question, or there were two answers or none at all despite the prompt saying "There should be one correct answer and 3 incorrect answers." This required me to manually review and correct the data. There could also be a chance that some of the questions may even be wrong, but by having the explanation paired with the documentation allows us a way to verify the information.&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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9qu7mv29tnob7dv8cy2x.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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9qu7mv29tnob7dv8cy2x.png" alt="Two correct answers"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Creating the Quiz
&lt;/h3&gt;

&lt;p&gt;I developed this quiz using my go-to tool for building web applications with Python, &lt;a href="https://streamlit.io/" rel="noopener noreferrer"&gt;Streamlit&lt;/a&gt;. Streamlit allows builders to easily create interactive web apps that provide instant feedback on user responses. The quiz data is loaded from a JSON file and rendered using interactive widgets, making it simple for users to interact with the quiz. Additionally, I included links to documentation for each question, allowing developers to further explore the topics covered in the quiz.&lt;/p&gt;

&lt;h3&gt;
  
  
  Conclusion
&lt;/h3&gt;

&lt;p&gt;In this blog post, I shared how I used GPT-3 and the AWS Well-Architected Framework to generate 1,000 scenario-based multiple choice questions for AWS certification exams. &lt;/p&gt;

&lt;p&gt;I covered the entire process, from data collection to quiz creation, and provided insights into the challenges I faced along the way. &lt;/p&gt;

&lt;p&gt;You can check out the quiz &lt;a href="https://banjtheman-aws-saa-ai-quiz-aws-quiz-st-qvxt6f.streamlit.app/" rel="noopener noreferrer"&gt;here&lt;/a&gt;, and use it as a study aid for your own AWS certification journey. &lt;/p&gt;

&lt;p&gt;If you're interested in building your GPT-3 powered applications, I hope this post has provided you with some helpful tips and guidance.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>chatgpt</category>
      <category>aws</category>
    </item>
    <item>
      <title>My Developer Advocacy Flywheel</title>
      <dc:creator>Banjo Obayomi</dc:creator>
      <pubDate>Tue, 20 Sep 2022 02:41:32 +0000</pubDate>
      <link>https://dev.to/banjtheman/my-developer-advocacy-flywheel-4l9i</link>
      <guid>https://dev.to/banjtheman/my-developer-advocacy-flywheel-4l9i</guid>
      <description>&lt;p&gt;Wow it's been one year since I've started full time as A Developer Advocate at AWS!!!&lt;/p&gt;


&lt;blockquote class="ltag__twitter-tweet"&gt;
      &lt;div class="ltag__twitter-tweet__media"&gt;
        &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--x5JLK1bS--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://pbs.twimg.com/media/E_vCz7sXsAk8g8H.jpg" alt="unknown tweet media content"&gt;
      &lt;/div&gt;

  &lt;div class="ltag__twitter-tweet__main"&gt;
    &lt;div class="ltag__twitter-tweet__header"&gt;
      &lt;img class="ltag__twitter-tweet__profile-image" src="https://res.cloudinary.com/practicaldev/image/fetch/s--qkWFPhb5--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://pbs.twimg.com/profile_images/1508665586364174337/7G2BUoJw_normal.png" alt="Banjo Obayomi profile image"&gt;
      &lt;div class="ltag__twitter-tweet__full-name"&gt;
        Banjo Obayomi
      &lt;/div&gt;
      &lt;div class="ltag__twitter-tweet__username"&gt;
        &lt;a class="mentioned-user" href="https://dev.to/banjtheman"&gt;@banjtheman&lt;/a&gt;
      &lt;/div&gt;
      &lt;div class="ltag__twitter-tweet__twitter-logo"&gt;
        &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--ir1kO05j--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev.to/assets/twitter-f95605061196010f91e64806688390eb1a4dbc9e913682e043eb8b1e06ca484f.svg" alt="twitter logo"&gt;
      &lt;/div&gt;
    &lt;/div&gt;
    &lt;div class="ltag__twitter-tweet__body"&gt;
      Hey friends, I'm excited to announce that today is my Day 1 as a Senior Developer Advocate at &lt;a href="https://twitter.com/awscloud"&gt;@awscloud&lt;/a&gt; &lt;a href="https://twitter.com/hashtag/LetsGoBuild"&gt;#LetsGoBuild&lt;/a&gt; 
    &lt;/div&gt;
    &lt;div class="ltag__twitter-tweet__date"&gt;
      14:24 PM - 20 Sep 2021
    &lt;/div&gt;


    &lt;div class="ltag__twitter-tweet__actions"&gt;
      &lt;a href="https://twitter.com/intent/tweet?in_reply_to=1439958707388010496" class="ltag__twitter-tweet__actions__button"&gt;
        &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--fFnoeFxk--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev.to/assets/twitter-reply-action-238fe0a37991706a6880ed13941c3efd6b371e4aefe288fe8e0db85250708bc4.svg" alt="Twitter reply action"&gt;
      &lt;/a&gt;
      &lt;a href="https://twitter.com/intent/retweet?tweet_id=1439958707388010496" class="ltag__twitter-tweet__actions__button"&gt;
        &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--k6dcrOn8--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev.to/assets/twitter-retweet-action-632c83532a4e7de573c5c08dbb090ee18b348b13e2793175fea914827bc42046.svg" alt="Twitter retweet action"&gt;
      &lt;/a&gt;
      &lt;a href="https://twitter.com/intent/like?tweet_id=1439958707388010496" class="ltag__twitter-tweet__actions__button"&gt;
        &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--SRQc9lOp--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev.to/assets/twitter-like-action-1ea89f4b87c7d37465b0eb78d51fcb7fe6c03a089805d7ea014ba71365be5171.svg" alt="Twitter like action"&gt;
      &lt;/a&gt;
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/blockquote&gt;


&lt;p&gt;I've always thought of &lt;a href="https://dev.to/banjtheman/how-i-became-a-developer-advocate-at-aws-5a01"&gt;myself as a "Developer Advocate"&lt;/a&gt; due to my innate excitement of building content for developers to learn. &lt;/p&gt;

&lt;p&gt;Now that I've been officially working in this role for a year, I wanted to share my experience and provide my framework for sourcing ideas to &lt;strong&gt;create fun experiences to help developers learn.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What is Developer Advocate
&lt;/h2&gt;

&lt;p&gt;Developer Advocates are many things depending on the company's goals. They can be storytellers, community managers, documentation writers, and much more. At the heart of it, the focus is on providing support for a developer community to thrive. &lt;/p&gt;

&lt;p&gt;At AWS my role as a developer advocate has allowed me engage the community through various mediums such as Twitter Spaces, blog posts and conferences. With many different AWS products, I often get asked, "how do you come up with content ideas to help developers?&lt;/p&gt;

&lt;h2&gt;
  
  
  Content Generation Flywheel
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://fourweekmba.com/amazon-flywheel/"&gt;The Amazon Flywheel&lt;/a&gt; is a scared document that highlights how by creating a virtuous cycle focused on customers, Amazon was able to grow and offer new products. &lt;/p&gt;

&lt;p&gt;Similarly when it comes to creating content for developers to consume we can take the same approach by focusing on the community and working backwards to identify a cycle that can constantly produce content geared for the community. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--EvpoDP1_--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ayum1s1u4xge205eo4sk.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--EvpoDP1_--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ayum1s1u4xge205eo4sk.png" alt="Content Generation Flywheel" width="880" height="517"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The three pillars of my Content Generation Flywheel are...&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Collaboration&lt;/li&gt;
&lt;li&gt;Content&lt;/li&gt;
&lt;li&gt;Community&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This post will dive deep into how this pillars enable me to build experiences to help developers learn.&lt;/p&gt;

&lt;h2&gt;
  
  
  Collaboration
&lt;/h2&gt;

&lt;p&gt;At AWS, there are many &lt;a href="https://docs.aws.amazon.com/whitepapers/latest/introduction-devops-aws/two-pizza-teams.html"&gt;"two pizza"&lt;/a&gt; teams working on a variety of projects. Often times teams want to be able to reach a developer audience but do not have the right mechanisms in place to create content for technical audiences.&lt;/p&gt;

&lt;p&gt;This is where I come in, to craft an experience that resonates with developers. For this I create a document outlining our strategy with the relevant data points. When we are in alignment, the content creation can begin.&lt;/p&gt;

&lt;h2&gt;
  
  
  Content
&lt;/h2&gt;

&lt;p&gt;This is where I would build the actual content such as a blog post, a lineup for a Twitter Space, or a workshop. The collaboration session helps provide the blueprint for the content, which enables me to enter a &lt;a href="https://en.wikipedia.org/wiki/Flow_(psychology)"&gt;flow state&lt;/a&gt; to build out the necessary components needed to create a fun learning experience for the audience. &lt;/p&gt;

&lt;h2&gt;
  
  
  Community
&lt;/h2&gt;

&lt;p&gt;When the content is ready to be delivered, I will provide ways to market the content based on the medium and then collect feedback from the community through mechanisms such surveys or live questions. &lt;/p&gt;

&lt;p&gt;Community feedback enables me to collect data to measure how effective the content resonates with the audience. This data is then used to help inform the collaboration phrase, which kicks off another cycle...&lt;/p&gt;

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

&lt;p&gt;The Developer Advocate job is always changing. This post introduced my Content Generation Framework which gives me mechanism to align my advocacy work with my north star of &lt;strong&gt;"Help Builders Win."&lt;/strong&gt; &lt;/p&gt;

&lt;p&gt;As I start off my next year at AWS, my goals will be to scale my efforts to create more opportunities for builders to learn and have fun!!&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Follow Banjo on Twitter at &lt;a class="mentioned-user" href="https://dev.to/banjtheman"&gt;@banjtheman&lt;/a&gt; for more useful tips and tricks about building on AWS. And of course there are many &lt;a href="https://www.linkedin.com/pulse/open-roles-join-aws-devrel-felipe-lemaitre/"&gt;roles open in Developer Relations at AWS&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

</description>
      <category>devrel</category>
      <category>community</category>
      <category>career</category>
      <category>aws</category>
    </item>
    <item>
      <title>How I became a Developer Advocate at AWS</title>
      <dc:creator>Banjo Obayomi</dc:creator>
      <pubDate>Tue, 07 Jun 2022 01:37:19 +0000</pubDate>
      <link>https://dev.to/banjtheman/how-i-became-a-developer-advocate-at-aws-5a01</link>
      <guid>https://dev.to/banjtheman/how-i-became-a-developer-advocate-at-aws-5a01</guid>
      <description>&lt;p&gt;Being a Developer Advocate is a fuzzy role that is different at every company. From the outside the emphasis of the role centered around speaking at conferences and providing content to their audience. As a passionate Meetup speaker and blog writer the role was highly aligned with what I did for fun on my own time. Becoming a Developer Advocate was really about "Following my fun". &lt;/p&gt;

&lt;p&gt;In the post I will walk through my journey of how I grew as a builder that led me to become a Developer Advocate at AWS.&lt;/p&gt;

&lt;h2&gt;
  
  
  My First App
&lt;/h2&gt;

&lt;p&gt;My interest in video games led me to take on a traditional software engineering path by studying computer science in college.  &lt;/p&gt;

&lt;p&gt;Initially classes were tough and I didn't really get software engineering. That changed when I took a 3 week 1 credit course over my Junior year winter break about Ruby on Rails. In this class I built a "Real Life Achievement" (RLA) Tracker, that allowed users to record accomplishments and share them on social media. While the app was simple the process of setting up the database, the Application Programming Interface (API), integrating social features and deploying to &lt;a href="https://www.heroku.com/" rel="noopener noreferrer"&gt;Heroku&lt;/a&gt; ignited my passion for building applications.&lt;/p&gt;

&lt;p&gt;While I graduated and started my first job as a software engineer, I would still tinker with RLA to learn new skills. This led me to learning about &lt;a href="https://github.com/phonegap/phonegap-cli" rel="noopener noreferrer"&gt;Phonegap&lt;/a&gt; which was a way to build iOS and Android Apps with just JavaScript,CSS, and HTML. Launching my first app was exhilarating, being able to think of an idea and use code to bring it into the world was just an awesome process. &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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0fsm8vi60fex7skprniu.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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0fsm8vi60fex7skprniu.png" alt="RLA AppStore Image"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;While I really enjoyed how I built my app, I wanted to share with others so that can be inspired to build as well. This led me to seek opportunities to find a venue to share my story.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Meetup Circuit
&lt;/h2&gt;

&lt;p&gt;I was able to find the DC Tech Meetup Group through &lt;a href="https://www.meetup.com/" rel="noopener noreferrer"&gt;Meetup&lt;/a&gt; which held gatherings for builders which focused on networking, startups, and tech. I attended one of the events and was instantly hooked. &lt;/p&gt;

&lt;p&gt;The raw excitement of the speakers and the energy from the attendees made me feel like I found my long lost tribe. I was currently building another app called Mealscope which was social platform for finding friends nearby to go out and eat, and knew the meetup would be the perfect venue to highlight what I was building. &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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1dkyloecg7rgclgqlrj8.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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1dkyloecg7rgclgqlrj8.png" alt="Mealscope Meetup Talk"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Going on stage was such an amazing moment. From walking through how I got the idea, to explaining the tech I used to build, and answering audience questions. This experience solidified my desire to help builders win. &lt;/p&gt;

&lt;p&gt;From there I would seek out other Meetups to attend to learn from the community, answer questions on &lt;a href="https://stackoverflow.com/users/4955321/banjo-obayomi" rel="noopener noreferrer"&gt;Stack Overflow&lt;/a&gt;, and attend conferences. I was going to at least 2 Meetups a week and applied to speak at different conferences every month. I even started my own &lt;a href="https://www.meetup.com/DataOpsDC/" rel="noopener noreferrer"&gt;Meetup group&lt;/a&gt; and &lt;a href="https://anchor.fm/dataops" rel="noopener noreferrer"&gt;podcast&lt;/a&gt; to provide others the opportunity to share content. However, this lifestyle came to abrupt end in March 2020... &lt;/p&gt;

&lt;h2&gt;
  
  
  Covid
&lt;/h2&gt;

&lt;p&gt;During the pandemic all in person events stopped, and Meetups and conferences were all going virtual. While the community adapted for while, zoom fatigue eventually set in and the events became less engaging. With no outlet to the community, and just writing code isolated at home led me to reevaluate what I would want to do with my career. &lt;/p&gt;

&lt;p&gt;This is when I started to research "Developer Advocates" and found out that their work aligns with what I enjoy doing, helping builders win with their product. From there I started to &lt;a href="https://www.aboutamazon.com/news/workplace/recruiters-offer-their-best-tips-for-interviewing-at-amazon" rel="noopener noreferrer"&gt;prepare for interviews&lt;/a&gt; and was able to land a role at AWS.&lt;/p&gt;

&lt;p&gt;&lt;iframe class="tweet-embed" id="tweet-1439958707388010496-992" src="https://platform.twitter.com/embed/Tweet.html?id=1439958707388010496"&gt;
&lt;/iframe&gt;

  // Detect dark theme
  var iframe = document.getElementById('tweet-1439958707388010496-992');
  if (document.body.className.includes('dark-theme')) {
    iframe.src = "https://platform.twitter.com/embed/Tweet.html?id=1439958707388010496&amp;amp;theme=dark"
  }



&lt;/p&gt;

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

&lt;p&gt;The &lt;a href="https://www.amazon.jobs/en/principles" rel="noopener noreferrer"&gt;Amazon Leadership Principle&lt;/a&gt; of "Learn and be Curious" embodies the path I took to becoming a Developer Advocate. I've learned many different languages, frameworks, and tools over the years and have been able to create content to enable builders to develop solutions. While &lt;a href="https://en.wikipedia.org/wiki/Survivorship_bias" rel="noopener noreferrer"&gt;every path is different&lt;/a&gt;, I believe the guiding philosophy to becoming a developer advocate is to demonstrate how you &lt;strong&gt;"Help Builders Win"&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Follow Banjo on Twitter at &lt;a class="mentioned-user" href="https://dev.to/banjtheman"&gt;@banjtheman&lt;/a&gt; for more useful tips and tricks about building on AWS. And of course there are many &lt;a href="https://www.linkedin.com/pulse/open-roles-join-aws-devrel-felipe-lemaitre/" rel="noopener noreferrer"&gt;roles open in Developer Relations at AWS&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

</description>
      <category>aws</category>
      <category>devrel</category>
      <category>community</category>
    </item>
    <item>
      <title>Building DevDaoStory: A Dynamic Visual Novel</title>
      <dc:creator>Banjo Obayomi</dc:creator>
      <pubDate>Tue, 01 Mar 2022 22:26:40 +0000</pubDate>
      <link>https://dev.to/banjtheman/building-devdaostory-a-dynamic-visual-novel-738</link>
      <guid>https://dev.to/banjtheman/building-devdaostory-a-dynamic-visual-novel-738</guid>
      <description>&lt;p&gt;Composability is the general ability to reuse the components of a system to develop applications. For example Legos are the building blocks for many type of different structures that can be created.&lt;/p&gt;

&lt;p&gt;In web3 world text based NFTs which are just list of words which are akin to Lego blocks that allow the community to build applications around these composable parts.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--C0xZli6F--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://storage.opensea.io/files/55635e01e96f55677e85f081f06e5ee6.svg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--C0xZli6F--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://storage.opensea.io/files/55635e01e96f55677e85f081f06e5ee6.svg" alt="Dev Dao NFT" width="350" height="350"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This post will walk through how I built &lt;a href="https://www.devdaostory.com/"&gt;DevDaoStory&lt;/a&gt; a dynamic visual novel based on the &lt;a href="https://opensea.io/collection/devs-for-revolution"&gt;DevDao NFT&lt;/a&gt; your wallet holds. I will highlight how I built the following sections&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Front End using server side rendering&lt;/li&gt;
&lt;li&gt;Utilizing OpenSea API for Identity&lt;/li&gt;
&lt;li&gt;Generating a unique game with the Ren'py Engine&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--VcvgIXBz--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/m10c95403jzdja2xzlmi.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--VcvgIXBz--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/m10c95403jzdja2xzlmi.png" alt="DevDaoStory Architecture" width="880" height="397"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Front end
&lt;/h2&gt;

&lt;p&gt;The user interface is a website built using HTML, &lt;a href="https://getbootstrap.com/"&gt;Bootstrap&lt;/a&gt;, JavaScript, &lt;a href="https://palletsprojects.com/p/jinja/"&gt;jinja2&lt;/a&gt; and &lt;a href="https://jquery.com/"&gt;JQuery&lt;/a&gt;. I find it simplest to build using primitives instead of being locked into a framework like React for simple interfaces.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--L41JFqtM--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/w2xequ8we9fnj2c6059o.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--L41JFqtM--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/w2xequ8we9fnj2c6059o.png" alt="DevDaoStory Home Page" width="880" height="189"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Since I'm leveraging jinja2 for server side rending, the application is hosted on &lt;a href="https://aws.amazon.com/lambda/"&gt;AWS Lambda&lt;/a&gt; and &lt;a href="https://aws.amazon.com/api-gateway/"&gt;API Gateway&lt;/a&gt; which provides one million events for free to use each month which provides a cost-effective way to host the application.&lt;/p&gt;

&lt;p&gt;There are only two pages for this app, the home page and the select your Dev page. Here is a snippet of code for rendering Devs in the user's wallet.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;{% for dev in devs %}
&lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"col-md-5 offset-md-1"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;

    &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"card mb-3"&lt;/span&gt; &lt;span class="na"&gt;style=&lt;/span&gt;&lt;span class="s"&gt;"max-width: 540px;"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;h3&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"card-title mt-3 mx-auto dark-text"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Dev #{{dev['id']}}&lt;span class="nt"&gt;&amp;lt;/h3&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"row g-0"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"col-md-5"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"card-body"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;

                    &lt;span class="nt"&gt;&amp;lt;ul&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"list-group list-group-flush"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
                        &lt;span class="nt"&gt;&amp;lt;li&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"list-group-item"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Os: {{dev['os']}}&lt;span class="nt"&gt;&amp;lt;/li&amp;gt;&lt;/span&gt;
                        &lt;span class="nt"&gt;&amp;lt;li&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"list-group-item"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Text Editor: {{dev['textEditor']}}&lt;span class="nt"&gt;&amp;lt;/li&amp;gt;&lt;/span&gt;
                        &lt;span class="nt"&gt;&amp;lt;li&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"list-group-item"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Language: {{dev['language']}}&lt;span class="nt"&gt;&amp;lt;/li&amp;gt;&lt;/span&gt;
                        &lt;span class="nt"&gt;&amp;lt;li&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"list-group-item"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Vibe: {{dev['vibe']}}&lt;span class="nt"&gt;&amp;lt;/li&amp;gt;&lt;/span&gt;
                        &lt;span class="nt"&gt;&amp;lt;li&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"list-group-item"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Location: {{dev['location']}}&lt;span class="nt"&gt;&amp;lt;/li&amp;gt;&lt;/span&gt;
                        &lt;span class="nt"&gt;&amp;lt;li&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"list-group-item"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Mind: {{dev['mind']}}&lt;span class="nt"&gt;&amp;lt;/li&amp;gt;&lt;/span&gt;
                        &lt;span class="nt"&gt;&amp;lt;li&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"list-group-item"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Industry: {{dev['industry']}}&lt;span class="nt"&gt;&amp;lt;/li&amp;gt;&lt;/span&gt;
                        &lt;span class="nt"&gt;&amp;lt;li&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"list-group-item"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Clothing: {{dev['clothing']}}&lt;span class="nt"&gt;&amp;lt;/li&amp;gt;&lt;/span&gt;
                        &lt;span class="nt"&gt;&amp;lt;li&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"list-group-item"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Background: {{dev['background']}}&lt;span class="nt"&gt;&amp;lt;/li&amp;gt;&lt;/span&gt;

                    &lt;span class="nt"&gt;&amp;lt;/ul&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;  
...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The next section will focus on how to build mechanisms to get identity details from a user.&lt;/p&gt;

&lt;h2&gt;
  
  
  Identity
&lt;/h2&gt;

&lt;p&gt;In order to know what Devs, a user has connect to their wallet I utilized the &lt;a href="https://docs.ethers.io/v5/"&gt;ethers.js&lt;/a&gt; library to provide a way for users to sign a "Login" message with their MetaMask Wallet.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;connectWallet&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ethereum&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;accounts&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ethereum&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;method&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;eth_requestAccounts&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
            &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;accounts&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;accounts&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

            &lt;span class="nx"&gt;walletProvider&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;ethers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;providers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Web3Provider&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ethereum&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="nx"&gt;walletSigner&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;walletProvider&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getSigner&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; 
            &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;base_message&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Login to DevDaoStory&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
            &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;signature&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;walletSigner&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;signMessage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;base_message&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;signature&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

            &lt;span class="nx"&gt;login_info&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
            &lt;span class="nx"&gt;login_info&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;address&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;accounts&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
            &lt;span class="nx"&gt;login_info&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;signature&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;signature&lt;/span&gt;
            &lt;span class="nx"&gt;login_info&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;message&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;base_message&lt;/span&gt;

            &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;login_info_string&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;login_info&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This code provides users a way to allow the application to read their public key, so their Devs can be verified. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s---46CD1mc--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/hecgwk4w6p5b8n3mej8e.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s---46CD1mc--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/hecgwk4w6p5b8n3mej8e.png" alt="Login to DevDaoStory" width="450" height="738"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Once the User accepts a POST request is sent to the server to gather the details of the user&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;
            &lt;span class="nx"&gt;$&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ajax&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
                &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;POST&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="na"&gt;url&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/web3_login&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="na"&gt;dataType&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;json&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="na"&gt;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;login_info_string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="na"&gt;contentType&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;application/json&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="na"&gt;success&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;login finished&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                    &lt;span class="p"&gt;...&lt;/span&gt;

                &lt;span class="p"&gt;},&lt;/span&gt;
                &lt;span class="na"&gt;error&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;xhr&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;textStatus&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;thrownError&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                    &lt;span class="nx"&gt;alert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Error: &lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;thrownError&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
                    &lt;span class="nx"&gt;$&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;body&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;css&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;opacity&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;1&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
                    &lt;span class="nx"&gt;$&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;#load_spinner&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;toggle&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

                &lt;span class="p"&gt;}&lt;/span&gt;
            &lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The message can be verified leveraging the &lt;a href="https://web3py.readthedocs.io/en/stable/"&gt;web3&lt;/a&gt; python module.&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;from&lt;/span&gt; &lt;span class="nn"&gt;web3&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Web3&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;eth_account.messages&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;encode_defunct&lt;/span&gt;


&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;verify_signature&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;address&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;signature&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;w3&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="s"&gt;"""
    Purpose:
        Verify user signed the message
    Args:
        address - address of user
        signature - the signature from user
        message - message to check
    Returns:
        boolean - True if verify, false if not
    """&lt;/span&gt;
    &lt;span class="n"&gt;encoded_message&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;encode_defunct&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;pub_key&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;w3&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;eth&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;account&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;recover_message&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;encoded_message&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;signature&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;signature&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;logging&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;info&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pub_key&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c1"&gt;# make sure same case
&lt;/span&gt;    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;w3&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;toChecksumAddress&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pub_key&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;lower&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;w3&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;toChecksumAddress&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;address&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&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;else&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;False&lt;/span&gt;


&lt;span class="p"&gt;...&lt;/span&gt;

&lt;span class="o"&gt;@&lt;/span&gt;&lt;span class="n"&gt;application&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;route&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/web3_login"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;methods&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"POST"&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;web3_login&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="s"&gt;"""
    Purpose:
        login
    Args:
        N/A
    Returns:
        login object
    """&lt;/span&gt;

    &lt;span class="n"&gt;jsonResp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
    &lt;span class="n"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;

    &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;login_info&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;loads&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;decode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"utf-8"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;

        &lt;span class="n"&gt;logging&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;info&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;login_info&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="n"&gt;address&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;w3&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;toChecksumAddress&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;login_info&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"address"&lt;/span&gt;&lt;span class="p"&gt;]).&lt;/span&gt;&lt;span class="n"&gt;lower&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
        &lt;span class="n"&gt;message&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;login_info&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"message"&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
        &lt;span class="n"&gt;signature&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;login_info&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"signature"&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
        &lt;span class="n"&gt;valid&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;web3_utils&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;verify_signature&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;address&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;signature&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;w3&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;...&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For more information on setting up your python web3 environment check out the &lt;a href="https://web3py.readthedocs.io/en/stable/providers.html"&gt;docs&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  OpenSea API
&lt;/h3&gt;

&lt;p&gt;Once the message has been verified we can now use the &lt;a href="https://docs.opensea.io/reference/api-overview"&gt;OpenSea API&lt;/a&gt; to get the Devs for the user.&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;get_opensea_assets&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;userAddress&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;contract&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="s"&gt;"""
    Purpose:
        Get assets from opensea
    Args:s
        userAddress: user to get
        contract: contract to get
    Returns:
        json_obj - Opensea assets
    """&lt;/span&gt;

    &lt;span class="n"&gt;API_KEY&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="n"&gt;environ&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"OPENSEA_API"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

    &lt;span class="n"&gt;HEADERS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s"&gt;"x-api-key"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;API_KEY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Accept"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"application/json"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="n"&gt;url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"https://api.opensea.io/api/v1/assets"&lt;/span&gt;

    &lt;span class="n"&gt;querystring&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="s"&gt;"owner"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;userAddress&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s"&gt;"order_direction"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"desc"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s"&gt;"offset"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"0"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s"&gt;"limit"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"20"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s"&gt;"asset_contract_address"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;contract&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="n"&gt;json_obj&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get&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;params&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;querystring&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;headers&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;HEADERS&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="n"&gt;json&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;json_obj&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"assets"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

&lt;span class="n"&gt;assets&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;web3_utils&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get_opensea_assets&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;address&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;DEVS_CONTRACT&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The API call will get all the information about the item, including the &lt;code&gt;tokenid&lt;/code&gt;. By building upon the &lt;a href="https://github.com/Developer-DAO/pixel-avatars"&gt;pixel-avatars project&lt;/a&gt; we can now get an image, and the data that defines your Dev, so it can be rendered on the front end.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--GPR03jN_--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/kzliyo87w1bf6lmoxhdz.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--GPR03jN_--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/kzliyo87w1bf6lmoxhdz.png" alt="Dev 2096" width="677" height="660"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The next section will focus on how the actual game was built, now that all the setup is complete. &lt;/p&gt;

&lt;h2&gt;
  
  
  Ren'py Game Generation
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://www.renpy.org/"&gt;Ren'Py&lt;/a&gt; is an open-source visual novel engine used by thousands of creators from around the world. It provides a scripting language that allow users to build interactive stories with words, images, and sounds. &lt;/p&gt;

&lt;p&gt;For Ren'py to work for DevDaoStory I created a custom docker image with a modified version of Ren'py to compile only the web version of the game. I also leverage AWS Lambda to provide an endpoint to run the docker image with the necessary data to generate the story.&lt;/p&gt;

&lt;p&gt;Once the game is compiled, it is uploaded to an &lt;a href="https://aws.amazon.com/s3/"&gt;S3 bucket&lt;/a&gt; for users to play. Here is sample code highlight the process&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;main&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="s"&gt;"""
    Purpose:
        Main driver
    Args:
        N/A
    Returns:
        N/A
    """&lt;/span&gt;

    &lt;span class="n"&gt;test_dev&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="s"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"2"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s"&gt;"os"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"Windows 95"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s"&gt;"textEditor"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"Emacs"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s"&gt;"clothing"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"Pink Hoodie"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s"&gt;"language"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"C"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s"&gt;"industry"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"Traveling Consultant"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s"&gt;"location"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"London"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s"&gt;"mind"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"Concrete"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s"&gt;"vibe"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"Optimist"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s"&gt;"background"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"Blue"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="n"&gt;wallet&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"0x00000000000"&lt;/span&gt;

    &lt;span class="n"&gt;logging&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;info&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Building Dynamic Game"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;gen_game_script&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;test_dev&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;wallet&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;build_renpy_game&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/tmp/stories/devdaostory/"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;upload_to_s3_bucket&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"dev2"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;logging&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;info&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Done and done"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once the game is uploaded to s3, the UI renders an iframe with the game so users can begin the story with their Dev.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--VfKvP7Tk--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/nt1r1rp5hoy07uzhxv6g.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--VfKvP7Tk--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/nt1r1rp5hoy07uzhxv6g.png" alt="gm fren" width="880" height="562"&gt;&lt;/a&gt;&lt;/p&gt;

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

&lt;p&gt;DevDaoStory was fun to build, and provides a use case for building an application for a community leveraging NFTs. The game is also playable without a Dev in your wallet to ensure other builders can be inspired by the story. You can play here: &lt;a href="https://www.devdaostory.com/"&gt;https://www.devdaostory.com/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you want to make your own story, or update the existing one, you can checkout the &lt;a href="https://github.com/banjtheman/devdaostory_script"&gt;github repo&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Follow Banjo on Twitter at &lt;a href="https://twitter.com/banjtheman"&gt;@banjtheman&lt;/a&gt; and &lt;a href="https://twitter.com/developer_dao"&gt;@developer_dao&lt;/a&gt; for more useful tips and tricks about building web3 applications.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>web3</category>
      <category>aws</category>
      <category>renpy</category>
    </item>
    <item>
      <title>Addressing 3 criticisms of web3</title>
      <dc:creator>Banjo Obayomi</dc:creator>
      <pubDate>Tue, 08 Feb 2022 21:46:58 +0000</pubDate>
      <link>https://dev.to/banjtheman/addressing-3-criticisms-of-web3-255c</link>
      <guid>https://dev.to/banjtheman/addressing-3-criticisms-of-web3-255c</guid>
      <description>&lt;p&gt;Web3 is an architecture and set of patterns for building distributed systems. Some of those patterns are implemented by different infrastructure components at different layers of the stack. Web3 will not solve society's problem similar to how AI did not solve our problems, but offers a new way to build applications. This post will touch on 3 common criticisms I hear when developing Web3 applications. &lt;/p&gt;

&lt;h2&gt;
  
  
  Anti Network Effects
&lt;/h2&gt;

&lt;p&gt;The early adopters that get in early need to sell their tokens to new comers at higher prices to make a profit. Not everyone wants to do that so they will make their own coin which sometimes will be a scam.  &lt;/p&gt;


&lt;blockquote class="ltag__twitter-tweet"&gt;

  &lt;div class="ltag__twitter-tweet__main"&gt;
    &lt;div class="ltag__twitter-tweet__header"&gt;
      &lt;img class="ltag__twitter-tweet__profile-image" src="https://res.cloudinary.com/practicaldev/image/fetch/s--Ye_R57No--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://pbs.twimg.com/profile_images/72647502/tyler_normal.jpg" alt="zerohedge profile image"&gt;
      &lt;div class="ltag__twitter-tweet__full-name"&gt;
        zerohedge
      &lt;/div&gt;
      &lt;div class="ltag__twitter-tweet__username"&gt;
        @zerohedge
      &lt;/div&gt;
      &lt;div class="ltag__twitter-tweet__twitter-logo"&gt;
        &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--ir1kO05j--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev.to/assets/twitter-f95605061196010f91e64806688390eb1a4dbc9e913682e043eb8b1e06ca484f.svg" alt="twitter logo"&gt;
      &lt;/div&gt;
    &lt;/div&gt;
    &lt;div class="ltag__twitter-tweet__body"&gt;
      'Squid Game' Crypto-Scam Exposed, COIN Crashes 100% After Explosive Gains &lt;a href="https://t.co/RfXT8B9JIW"&gt;zerohedge.com/crypto/squid-g…&lt;/a&gt;
    &lt;/div&gt;
    &lt;div class="ltag__twitter-tweet__date"&gt;
      17:34 PM - 01 Nov 2021
    &lt;/div&gt;


    &lt;div class="ltag__twitter-tweet__actions"&gt;
      &lt;a href="https://twitter.com/intent/tweet?in_reply_to=1455226722308268037" class="ltag__twitter-tweet__actions__button"&gt;
        &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--fFnoeFxk--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev.to/assets/twitter-reply-action-238fe0a37991706a6880ed13941c3efd6b371e4aefe288fe8e0db85250708bc4.svg" alt="Twitter reply action"&gt;
      &lt;/a&gt;
      &lt;a href="https://twitter.com/intent/retweet?tweet_id=1455226722308268037" class="ltag__twitter-tweet__actions__button"&gt;
        &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--k6dcrOn8--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev.to/assets/twitter-retweet-action-632c83532a4e7de573c5c08dbb090ee18b348b13e2793175fea914827bc42046.svg" alt="Twitter retweet action"&gt;
      &lt;/a&gt;
      &lt;a href="https://twitter.com/intent/like?tweet_id=1455226722308268037" class="ltag__twitter-tweet__actions__button"&gt;
        &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--SRQc9lOp--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev.to/assets/twitter-like-action-1ea89f4b87c7d37465b0eb78d51fcb7fe6c03a089805d7ea014ba71365be5171.svg" alt="Twitter like action"&gt;
      &lt;/a&gt;
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/blockquote&gt;


&lt;p&gt;These anti patterns of jumping to coin to coin are not a sustainable roadmap for building applications. The desire to get in early and for coins to moon is for speculation and not builders. Builders are focused on protocols and services that provide value to their customers and will focus their energy in building systems. Just how their are scams and fraud in web2, &lt;strong&gt;moving to a new paradigm does not remove societal problems.&lt;/strong&gt; The builders will continue to build and scammers will continue to scam, what we can do as a community is educate how to detect scams and protect your assets. I reckon a Gmail style "filter" will emerge that can automatically block out illegitimate projects in the near future. &lt;/p&gt;

&lt;h2&gt;
  
  
  "Decentralization"
&lt;/h2&gt;

&lt;p&gt;A key concept for web3 is having decentralized protocols to build with. While not having to solely rely on a 3rd party, you give up the aspect of responsibility. If your AWS account gets hacked and you get a massive bill, they can waive the fee for you and you can upgrade your account security. But if an NFT gets stolen or wallet comprised your are out of luck... in theory. Centralized services are already here in the in web3 space such as OpenSea and Coinbase. These entities already take the onus responsibility in certain use cases such as a "stolen" NFT.&lt;/p&gt;


&lt;blockquote class="ltag__twitter-tweet"&gt;
    &lt;div class="ltag__twitter-tweet__media ltag__twitter-tweet__media__two-pics"&gt;
      &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--PdCha2y_--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://pbs.twimg.com/media/FIPRTHMUcAECpqZ.jpg" alt="unknown tweet media content"&gt;
    &lt;/div&gt;

  &lt;div class="ltag__twitter-tweet__main"&gt;
    &lt;div class="ltag__twitter-tweet__header"&gt;
      &lt;img class="ltag__twitter-tweet__profile-image" src="https://res.cloudinary.com/practicaldev/image/fetch/s--WwwYTuyU--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://pbs.twimg.com/profile_images/1459474459748827143/y3uUVj3t_normal.jpg" alt="Nikita profile image"&gt;
      &lt;div class="ltag__twitter-tweet__full-name"&gt;
        Nikita
      &lt;/div&gt;
      &lt;div class="ltag__twitter-tweet__username"&gt;
        @nikitabier
      &lt;/div&gt;
      &lt;div class="ltag__twitter-tweet__twitter-logo"&gt;
        &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--ir1kO05j--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev.to/assets/twitter-f95605061196010f91e64806688390eb1a4dbc9e913682e043eb8b1e06ca484f.svg" alt="twitter logo"&gt;
      &lt;/div&gt;
    &lt;/div&gt;
    &lt;div class="ltag__twitter-tweet__body"&gt;
      If centralized marketplaces can flag &amp;amp; block “stolen” NFTs—effectively delegitimizing the ledger—then what’s the point of the blockchain?&lt;br&gt;&lt;br&gt;The end state of decentralized protocols will likely be the same system we have today: banks reversing transactions they deem illegitimate. 
    &lt;/div&gt;
    &lt;div class="ltag__twitter-tweet__date"&gt;
      06:41 AM - 04 Jan 2022
    &lt;/div&gt;


    &lt;div class="ltag__twitter-tweet__actions"&gt;
      &lt;a href="https://twitter.com/intent/tweet?in_reply_to=1478255235218821120" class="ltag__twitter-tweet__actions__button"&gt;
        &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--fFnoeFxk--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev.to/assets/twitter-reply-action-238fe0a37991706a6880ed13941c3efd6b371e4aefe288fe8e0db85250708bc4.svg" alt="Twitter reply action"&gt;
      &lt;/a&gt;
      &lt;a href="https://twitter.com/intent/retweet?tweet_id=1478255235218821120" class="ltag__twitter-tweet__actions__button"&gt;
        &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--k6dcrOn8--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev.to/assets/twitter-retweet-action-632c83532a4e7de573c5c08dbb090ee18b348b13e2793175fea914827bc42046.svg" alt="Twitter retweet action"&gt;
      &lt;/a&gt;
      &lt;a href="https://twitter.com/intent/like?tweet_id=1478255235218821120" class="ltag__twitter-tweet__actions__button"&gt;
        &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--SRQc9lOp--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev.to/assets/twitter-like-action-1ea89f4b87c7d37465b0eb78d51fcb7fe6c03a089805d7ea014ba71365be5171.svg" alt="Twitter like action"&gt;
      &lt;/a&gt;
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/blockquote&gt;


&lt;p&gt;Guard rails must be in place for developers to want to build. The centralized services will be offering guard rails which will also improve developer experience. Think of services such as Steam and Netflix that provide a centralized experience for games and movies. As much as we may hawk about decentralized, when something goes wrong we want to blame someone and having central players gives us the confidence to build. &lt;/p&gt;

&lt;h2&gt;
  
  
  What data goes on chain?
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;"If you ask 10 developers to architect a system that leverages blockchain, you will get 10 different responses on how to build the system" - Aaron Levie CEO Box&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Storing data on chain is expansive and is not better than using stand SQL databases of "web2". So this begs the question why should we even us blockchain then? &lt;/p&gt;

&lt;p&gt;Blockchains provide a new way to build networks that combines the publicly-owned network effects of web1 with the advanced functionality of web2.&lt;/p&gt;


&lt;blockquote class="ltag__twitter-tweet"&gt;

  &lt;div class="ltag__twitter-tweet__main"&gt;
    &lt;div class="ltag__twitter-tweet__header"&gt;
      &lt;img class="ltag__twitter-tweet__profile-image" src="https://res.cloudinary.com/practicaldev/image/fetch/s--94sxYisB--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://pbs.twimg.com/profile_images/1487630901592969218/PiE5iwsO_normal.jpg" alt="nader dabit (🧱, 🚀) | sha.eth | nader.sol profile image"&gt;
      &lt;div class="ltag__twitter-tweet__full-name"&gt;
        nader dabit (🧱, 🚀) | sha.eth | nader.sol
      &lt;/div&gt;
      &lt;div class="ltag__twitter-tweet__username"&gt;
        &lt;a class="mentioned-user" href="https://dev.to/dabit3"&gt;@dabit3&lt;/a&gt;
      &lt;/div&gt;
      &lt;div class="ltag__twitter-tweet__twitter-logo"&gt;
        &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--ir1kO05j--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev.to/assets/twitter-f95605061196010f91e64806688390eb1a4dbc9e913682e043eb8b1e06ca484f.svg" alt="twitter logo"&gt;
      &lt;/div&gt;
    &lt;/div&gt;
    &lt;div class="ltag__twitter-tweet__body"&gt;
      People will point to blockchain technology &amp;amp; argue its inefficiencies when compared to traditional high throughput databases.&lt;br&gt;&lt;br&gt;The real comparison should be between highly inefficient &amp;amp; expensive intermediaries like banks, lawyers, art brokers, governments, &amp;amp; stock exchanges.
    &lt;/div&gt;
    &lt;div class="ltag__twitter-tweet__date"&gt;
      04:14 AM - 05 Feb 2022
    &lt;/div&gt;


    &lt;div class="ltag__twitter-tweet__actions"&gt;
      &lt;a href="https://twitter.com/intent/tweet?in_reply_to=1489814648992018433" class="ltag__twitter-tweet__actions__button"&gt;
        &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--fFnoeFxk--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev.to/assets/twitter-reply-action-238fe0a37991706a6880ed13941c3efd6b371e4aefe288fe8e0db85250708bc4.svg" alt="Twitter reply action"&gt;
      &lt;/a&gt;
      &lt;a href="https://twitter.com/intent/retweet?tweet_id=1489814648992018433" class="ltag__twitter-tweet__actions__button"&gt;
        &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--k6dcrOn8--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev.to/assets/twitter-retweet-action-632c83532a4e7de573c5c08dbb090ee18b348b13e2793175fea914827bc42046.svg" alt="Twitter retweet action"&gt;
      &lt;/a&gt;
      &lt;a href="https://twitter.com/intent/like?tweet_id=1489814648992018433" class="ltag__twitter-tweet__actions__button"&gt;
        &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--SRQc9lOp--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev.to/assets/twitter-like-action-1ea89f4b87c7d37465b0eb78d51fcb7fe6c03a089805d7ea014ba71365be5171.svg" alt="Twitter like action"&gt;
      &lt;/a&gt;
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/blockquote&gt;


&lt;p&gt;This allows blockchains to act as public goods rather than private entities. In terms of what should be stored...&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The least amount of information possible, in the most efficient way possible, should be stored on the blockchain &lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Blockchains at is core is about developing a system of trust. In the end &lt;strong&gt;a human must verify the data on chain&lt;/strong&gt;. Sentiment such as "code is law" fall apart as soon as bugs or errors  are introduced. Having a way for a human to verify the hash of data provides a mechanism for humans to understand data stored on chain for applications.  &lt;/p&gt;

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

&lt;p&gt;We are still early... and we are "flying the airplane while building it". It is important to remember that web3 does not solve societal problems and centralization is not inherently bad. Its up to use the builders to architect systems that humans can trust and develop applications we enjoy using. &lt;/p&gt;

</description>
      <category>web3</category>
      <category>blockchain</category>
    </item>
    <item>
      <title>DC Fire and EMS Data Visualizer</title>
      <dc:creator>Banjo Obayomi</dc:creator>
      <pubDate>Tue, 11 Jan 2022 15:17:23 +0000</pubDate>
      <link>https://dev.to/banjtheman/dc-fire-and-ems-data-visualizer-4a6l</link>
      <guid>https://dev.to/banjtheman/dc-fire-and-ems-data-visualizer-4a6l</guid>
      <description>&lt;h3&gt;
  
  
  Overview of My Submission
&lt;/h3&gt;

&lt;p&gt;This application uses MongoDB time series collections to highlight the DC Fire and EMS call volume between August 2014 through August 2015. The data is visualized using Streamlit and provides an interface for users to select different days to view. The app is hosted &lt;a href="https://share.streamlit.io/banjtheman/dcfireems_mongo/main/dcfireems_mongo_st.py"&gt;here&lt;/a&gt; to try out.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--kZb1wKDo--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://raw.githubusercontent.com/banjtheman/dcfireems_mongo/main/images/dc_fire_ems_small.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--kZb1wKDo--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://raw.githubusercontent.com/banjtheman/dcfireems_mongo/main/images/dc_fire_ems_small.png" alt="Overview of dashboard" width="400" height="357"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Submission Category:
&lt;/h3&gt;

&lt;p&gt;Prime Time&lt;/p&gt;

&lt;h3&gt;
  
  
  Link to Code
&lt;/h3&gt;


&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--566lAguM--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev.to/assets/github-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/banjtheman"&gt;
        banjtheman
      &lt;/a&gt; / &lt;a href="https://github.com/banjtheman/dcfireems_mongo"&gt;
        dcfireems_mongo
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      Visualzing DCFireEMS call data with MongoDB
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;
&lt;h1&gt;
DC Fire and EMS Data Visualizer&lt;/h1&gt;
&lt;p&gt;This project uses &lt;a href="https://streamlit.io/" rel="nofollow"&gt;Streamlit&lt;/a&gt; to display the DC Fire and EMS call volume between August 2014 through August 2015. The data is being stored in a a MongoDB time series collection. This project was built for the &lt;a href="https://dev.to/devteam/announcing-the-mongodb-atlas-hackathon-on-dev-4b6m" rel="nofollow"&gt;MongoDB Atlas Hackathon&lt;/a&gt;]&lt;/p&gt;
&lt;p&gt;Try out the &lt;a href="https://share.streamlit.io/banjtheman/dcfireems_mongo/main/dcfireems_mongo_st.py" rel="nofollow"&gt;demo&lt;/a&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;a rel="noopener noreferrer" href="https://github.com/banjtheman/dcfireems_mongoimages/dc_fire_ems_small.png"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--CdtrWUN5--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://github.com/banjtheman/dcfireems_mongoimages/dc_fire_ems_small.png" alt="App Screenshot"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;/div&gt;



&lt;/div&gt;
&lt;br&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/banjtheman/dcfireems_mongo"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;br&gt;
&lt;/div&gt;
&lt;br&gt;


&lt;h3&gt;
  
  
  Additional Resources / Info
&lt;/h3&gt;

&lt;p&gt;The data was collected during the &lt;br&gt;
&lt;a href="https://www.codeforamerica.org/national-day-of-civic-hacking%20/ndoch-2021/"&gt;2021 National Day of Civic Hacking from Code for America&lt;/a&gt; by the &lt;a href="https://github.com/banjtheman/dc_ndoch_2021"&gt;Code for DC Group&lt;/a&gt;. &lt;a href="https://streamlit.io/"&gt;Streamlit&lt;/a&gt; was used to build the visualization. &lt;/p&gt;

</description>
      <category>atlashackathon</category>
      <category>mongodb</category>
      <category>streamlit</category>
      <category>python</category>
    </item>
    <item>
      <title>How I built a Full Stack Web3 App on the Cloud</title>
      <dc:creator>Banjo Obayomi</dc:creator>
      <pubDate>Mon, 03 Jan 2022 23:04:38 +0000</pubDate>
      <link>https://dev.to/banjtheman/how-i-built-a-full-stack-web3-app-on-the-cloud-574j</link>
      <guid>https://dev.to/banjtheman/how-i-built-a-full-stack-web3-app-on-the-cloud-574j</guid>
      <description>&lt;p&gt;As a builder, I am always curious about how to develop applications with emerging technology.  A technology that has captured my attention recently has been non fungible tokens (NFTs). An NFT is a unique digital item that is stored on a blockchain. A common use case is to store digital art as an NFT that can be sold to collectors. I’ve always fostered the idea that code is art. With the resurgent demand for NFTs it provided the perfect opportunity to build a product that can embed code as a art. Thus &lt;a href="https://www.gitgallery.com/" rel="noopener noreferrer"&gt;GitGallery&lt;/a&gt; was born, a website that allows you to mint your GitHub commits as an NFT.  This post will dive deep on how I built GitGallery highlighting&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Smart Contract Development&lt;/li&gt;
&lt;li&gt;Backend API&lt;/li&gt;
&lt;li&gt;User Interface&lt;/li&gt;
&lt;li&gt;Application Deployment&lt;/li&gt;
&lt;/ul&gt;

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

&lt;p&gt;To provide a scalable, cost effective process for minting NFTs I leveraged &lt;a href="https://polygon.technology/" rel="noopener noreferrer"&gt;Polygon&lt;/a&gt;  which is a protocol and framework that provides reduced fees needed to run Ethereum based applications.&lt;/p&gt;

&lt;p&gt;In addition, the entire web application is deployed on Amazon Web Services (AWS). By leveraging AWS, I was able to create a serverless application that responds to events rather than wasting compute sitting idly if deployed onto a server. With &lt;a href="https://aws.amazon.com/lambda/" rel="noopener noreferrer"&gt;AWS Lambda&lt;/a&gt; and &lt;a href="https://aws.amazon.com/api-gateway/" rel="noopener noreferrer"&gt;API Gateway&lt;/a&gt; one million events are free to use each month which provides a cost-effective way to host the application. The following sections will detail the tools and processes I used to develop the smart contract, the backend API (Application Programming Interface) and the user interface. &lt;/p&gt;

&lt;h2&gt;
  
  
  Smart Contract
&lt;/h2&gt;

&lt;p&gt;The smart contract I developed provides the ability to create a unique token named GitNFT which is linked to a GitHub commit. The code was developed using the Solidity programming language. I was able to import code developed by OpenZeppelin a firm that provides secure code for blockchain applications. The code implements the NFT standard known as ERC (Ethereum Request for Comment) 721 which provides the functionality to deploy the smart contract to the blockchain and mint NFTs.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;pragma solidity ^0.8.0;

import "./ERC721Tradable.sol";

/**
 * @title GitNFT
 * GitNFT - a contract for code GitNFTs.
 */
contract GitNFT is ERC721Tradable {
    uint256 public nextTokenId;
    address public admin;

    constructor(address _proxyRegistryAddress)
        public
        ERC721Tradable("GitNFT", "GitNFT", _proxyRegistryAddress)
    {
        admin = msg.sender;
    }

    // only our wallet should be able to mint
    function mint(address to) external onlyOwner {
        _safeMint(to, nextTokenId);
        nextTokenId++;
    }

    function baseTokenURI() public pure override returns (string memory) {
        return "https://www.gitgallery.com/tokenid/";
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once the code is written the next step is to deploy the contract to the Polygon blockchain. &lt;/p&gt;

&lt;p&gt;I used the deployment software &lt;a href="https://trufflesuite.com/" rel="noopener noreferrer"&gt;Truffle&lt;/a&gt; with the Ethereum API &lt;a href="https://infura.io/" rel="noopener noreferrer"&gt;Infura&lt;/a&gt; to compile my contract code and deploy the artifacts to the Polygon blockchain. Here is a code used to deploy the contract to the Polygon Blockchain&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;NFT&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;artifacts&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;GitNFT&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;proxyRegistryAddress&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;OWNER_ADDRESS&lt;/span&gt;


&lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exports&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nf"&gt;function &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;deployer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;_network&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;accounts&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;deployer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;deploy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;NFT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nx"&gt;proxyRegistryAddress&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="na"&gt;gas&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;5000000&lt;/span&gt;&lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;nft&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;NFT&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;deployed&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This transaction required the Polygon token MATIC to pay for the contract fees, which in total cost about 1 cent. Compiling the code also produces an Application Binary Interface (ABI) which is a JavaScript Object Notation (JSON) file that allows for other applications to interact with the contract. &lt;/p&gt;

&lt;h2&gt;
  
  
  Backend API
&lt;/h2&gt;

&lt;p&gt;I used Python to develop the business logic needed for GitGallery and used the &lt;a href="https://flask.palletsprojects.com/en/2.0.x/" rel="noopener noreferrer"&gt;Flask&lt;/a&gt; framework to encode the API endpoints. The 4 major endpoints needed for the application are login, verify, mint, and get items.&lt;/p&gt;

&lt;h3&gt;
  
  
  Login
&lt;/h3&gt;

&lt;p&gt;By leveraging &lt;a href="https://auth0.com/" rel="noopener noreferrer"&gt;Auth0&lt;/a&gt; a platform designed to make it easy to login via 3rd party authentication, I created an endpoint that allows users to login via their GitHub account. The code stores the logged in user details via cookies in the current web browser session, to track state from each request. When a user logs out, the browser session clears all cookies.&lt;/p&gt;

&lt;h3&gt;
  
  
  Verify
&lt;/h3&gt;

&lt;p&gt;To verify a GitHub commit, users enter in the URL of their GitHub commit. By using the GitHub GraphQL API, I created a query that could verify if the user created the commit ID for the given project. Here is the GraphQL query used to verify the author of the commit.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="err"&gt;repository(owner:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"OWNER"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;name:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"NAME"&lt;/span&gt;&lt;span class="err"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="err"&gt;object(expression:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"COMMIT_ID"&lt;/span&gt;&lt;span class="err"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="err"&gt;...&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;on&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;Commit&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="err"&gt;author&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="err"&gt;name&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="err"&gt;user&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="err"&gt;login&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Mint
&lt;/h3&gt;

&lt;p&gt;Once a commit is verified, the user can mint the commit as an NFT. By using the ABI and the python Ethereum library &lt;a href="https://web3py.readthedocs.io/en/stable/" rel="noopener noreferrer"&gt;web3.py&lt;/a&gt; the code executes the mint function of the NFT contract. Here is the code used to mint the GitNFT&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;web3_mint&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;userAddress&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;
    Purpose:
        mint a token for user on blockchain
    Args:
        userAddress - the user to mint for
    Returns:
        hash - txn of mint
        tokenid - token minted
    &lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;

    &lt;span class="n"&gt;nonce&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;w3&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;eth&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get_transaction_count&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;PUBLIC_KEY&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c1"&gt;# Create the contract function
&lt;/span&gt;    &lt;span class="n"&gt;mint_txn&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;CODE_NFT&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;functions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;mint&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;userAddress&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;buildTransaction&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;chainId&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;CHAIN_ID&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;gas&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;10000000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;gasPrice&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;w3&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toWei&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;1&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;gwei&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;nonce&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;nonce&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;signed_txn&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;w3&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;eth&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;account&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sign_transaction&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mint_txn&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;private_key&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;PRIVATE_KEY&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;w3&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;eth&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;send_raw_transaction&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;signed_txn&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;rawTransaction&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nb"&gt;hash&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;w3&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toHex&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;w3&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;keccak&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;signed_txn&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;rawTransaction&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="n"&gt;receipt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;w3&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;eth&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;wait_for_transaction_receipt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;hash&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  
    &lt;span class="n"&gt;hex_tokenid&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;receipt&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;logs&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;topics&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;hex&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;  &lt;span class="c1"&gt;# this is token id in hex
&lt;/span&gt;
    &lt;span class="c1"&gt;# convert from hex to decmial
&lt;/span&gt;    &lt;span class="n"&gt;tokenid&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;int&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;hex_tokenid&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;16&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;logging&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;info&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Got tokenid: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;tokenid&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;hash&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;tokenid&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The fee for calling the contract is paid from MATIC I allocated for the project. Since the minting cost is less then a fraction of a penny the process is virtually free and more importantly has no costs to the user. Each minted NFT produces a JSON file with the commit metadata. The metadata is stored in &lt;a href="https://aws.amazon.com/s3/" rel="noopener noreferrer"&gt;Amazon S3&lt;/a&gt; and &lt;a href="https://aws.amazon.com/dynamodb/" rel="noopener noreferrer"&gt;DynamoDB&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Get Items
&lt;/h3&gt;

&lt;p&gt;To display Items for a particular user the code creates a DynamoDB query using the selected user as the partition key. The JSON in S3 is used for external platforms such as OpenSea which is a marketplace for NFTs. There is a particular format needed for the data to show correctly in external applications which is handled by S3, while data on GitGallery is rendered from DynamoDB.&lt;/p&gt;

&lt;h3&gt;
  
  
  Deployment
&lt;/h3&gt;

&lt;p&gt;To deploy the code to the web, I leveraged the &lt;a href="https://www.serverless.com/" rel="noopener noreferrer"&gt;Serverless&lt;/a&gt; framework. Serverless allows me to create a YAML (YAML Ain't Markup Language) file that specifies all infrastructure and security needed to deploy the Flask application as a docker image. Serverless will deploy the docker image on AWS Lambda with the endpoints mapping to API Gateway, with the necessary Identity and Access Management (IAM) roles to access services such as S3 and DynamoDB.&lt;/p&gt;

&lt;h2&gt;
  
  
  User Interface
&lt;/h2&gt;

&lt;p&gt;The user interface is a website built using HTML, Bootstrap, JavaScript, jinja2 and JQuery. These tools allowed me to create a user interface that leverages server-side rendering to allow for dynamic routes such as &lt;code&gt;www.gitgallery.com/gallery/{USER_NAME}/minted&lt;/code&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="nd"&gt;@application.route&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/gallery/&amp;lt;user&amp;gt;/minted&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;minted&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;
    Purpose:
        load gallery page
    Args:
        N/A
    Returns:
        html - gallery html
    &lt;/span&gt;&lt;span class="sh"&gt;"""&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;profile&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;session&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;curr_user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;profile&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;curr_user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;

    &lt;span class="c1"&gt;# check if user exists...
&lt;/span&gt;
    &lt;span class="n"&gt;userdata&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;users&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get_item&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Key&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;username&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Item&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;userdata&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;logging&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;No such user &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;redirect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;user_metadata&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;userdata&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Item&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

    &lt;span class="c1"&gt;# get minted items
&lt;/span&gt;    &lt;span class="n"&gt;minted_items&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;get_minted_items&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;render_template&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;gallery.html&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;userinfo&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;curr_user&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;minted_items&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;minted_items&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;user_metadata&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;user_metadata&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In addition, I utilized the ethers.js library to provide a way for users to sign their GitHub commits using their MetaMask Wallet. The MetaMask wallet provides users an interface to interact with the Ethereum blockchain. The following is the front end code used to mint tokens&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;safe_mint&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                    &lt;span class="c1"&gt;// console.log("safe mint called")&lt;/span&gt;

                    &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;userAddress&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;""&lt;/span&gt;
                    &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                        &lt;span class="nx"&gt;userAddress&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;walletSigner&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getAddress&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

                    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                        &lt;span class="nf"&gt;alert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Metamask not detected&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                        &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&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="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;username&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;username&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
                    &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;ver_url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;verify_url&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
                    &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;ver_date&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;status_json&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;date&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

                    &lt;span class="c1"&gt;// Commint&lt;/span&gt;
                    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;base_message&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Signed on GitGallery&lt;/span&gt;&lt;span class="se"&gt;\n\n&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;username&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt; &lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;ver_date&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt; &lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;ver_url&lt;/span&gt;
                    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;signature&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;walletSigner&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;signMessage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;base_message&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

                    &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;mintObj&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
                    &lt;span class="nx"&gt;mintObj&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;verify_json&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;
                    &lt;span class="nx"&gt;mintObj&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;signature&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;signature&lt;/span&gt;
                    &lt;span class="nx"&gt;mintObj&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;userAddress&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;userAddress&lt;/span&gt;

                    &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;mintString&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;mintObj&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

                    &lt;span class="nf"&gt;$&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;body&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;css&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;opacity&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;.3&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
                    &lt;span class="nf"&gt;$&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;#load_spinner&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toggle&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

                    &lt;span class="nx"&gt;$&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ajax&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
                        &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;POST&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                        &lt;span class="na"&gt;url&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/mint_token&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                        &lt;span class="na"&gt;dataType&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;json&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                        &lt;span class="na"&gt;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;mintString&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                        &lt;span class="na"&gt;contentType&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;application/json&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                        &lt;span class="na"&gt;success&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;function &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                            &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;mint returned&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                            &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

                            &lt;span class="nf"&gt;$&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;#load_spinner&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toggle&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
                            &lt;span class="nf"&gt;$&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;body&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;css&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;opacity&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;1&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;


                            &lt;span class="c1"&gt;//check for error&lt;/span&gt;
                            &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;error&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                                &lt;span class="nf"&gt;alert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;error&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
                                &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;
                            &lt;span class="p"&gt;}&lt;/span&gt;

                            &lt;span class="nf"&gt;alert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Mint Successful&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

                            &lt;span class="c1"&gt;//passed? reload page?&lt;/span&gt;
                            &lt;span class="nx"&gt;location&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;reload&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
                        &lt;span class="p"&gt;},&lt;/span&gt;
                        &lt;span class="na"&gt;error&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;function &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;xhr&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;textStatus&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;thrownError&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                            &lt;span class="nf"&gt;alert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Error: &lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;thrownError&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
                            &lt;span class="nf"&gt;$&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;body&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;css&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;opacity&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;1&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
                            &lt;span class="nf"&gt;$&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;#load_spinner&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toggle&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

                        &lt;span class="p"&gt;}&lt;/span&gt;
                    &lt;span class="p"&gt;})&lt;/span&gt;


                &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Conclusion
&lt;/h3&gt;

&lt;p&gt;Connecting all the dots results in a full stack NFT application with a simple workflow. A user signs in using their GitHub credentials. The user enters in a URL for a GitHub Commit. The system verifies the commit for the user. The user then signs the commit with MetaMask. Finally, the GitHub commit is minted as an NFT and stored in the user’s MetaMask wallet. This workflow allows for the creation of an NFT in less than 30 seconds for free. &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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9uukxcji7qgsemfdyym7.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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9uukxcji7qgsemfdyym7.png" alt="GitGallery Mint Diagram"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you itching to build your own NFT project, I've created a hands on &lt;a href="https://github.com/banjtheman/polygon_nft_workshop" rel="noopener noreferrer"&gt;workshop&lt;/a&gt; that focusing on leveraging these tools to launch your own smart contract.&lt;/p&gt;

</description>
      <category>web3</category>
      <category>aws</category>
      <category>python</category>
      <category>javascript</category>
    </item>
    <item>
      <title>Using GitHub Actions to answer questions about Pull Requests</title>
      <dc:creator>Banjo Obayomi</dc:creator>
      <pubDate>Wed, 10 Nov 2021 17:27:34 +0000</pubDate>
      <link>https://dev.to/banjtheman/using-github-actions-to-answer-questions-about-pull-requests-e64</link>
      <guid>https://dev.to/banjtheman/using-github-actions-to-answer-questions-about-pull-requests-e64</guid>
      <description>&lt;div class="ltag_github-liquid-tag"&gt;
  &lt;h1&gt;
    &lt;a href="https://github.com/banjtheman/prfaq_action/pull/34#issuecomment-964757256" rel="noopener noreferrer"&gt;
      &lt;img class="github-logo" alt="GitHub logo" 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"&gt;
      &lt;span class="issue-title"&gt;
        Comment for
      &lt;/span&gt;
      &lt;span class="issue-number"&gt;#34&lt;/span&gt;
    &lt;/a&gt;
  &lt;/h1&gt;
  &lt;div class="github-thread"&gt;
    &lt;div class="timeline-comment-header"&gt;
      &lt;a href="https://github.com/apps/github-actions" rel="noopener noreferrer"&gt;
        &lt;img class="github-liquid-tag-img" src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Favatars.githubusercontent.com%2Fin%2F15368%3Fv%3D4" alt="github-actions[bot] avatar"&gt;
      &lt;/a&gt;
      &lt;div class="timeline-comment-header-text"&gt;
        &lt;strong&gt;
          &lt;a href="https://github.com/apps/github-actions" rel="noopener noreferrer"&gt;github-actions[bot]&lt;/a&gt;
        &lt;/strong&gt; commented on &lt;a href="https://github.com/banjtheman/prfaq_action/pull/34#issuecomment-964757256" rel="noopener noreferrer"&gt;&lt;time&gt;Nov 10, 2021&lt;/time&gt;&lt;/a&gt;
      &lt;/div&gt;
    &lt;/div&gt;
    &lt;div class="ltag-github-body"&gt;
      &lt;h2&gt;
&lt;span class="octicon octicon-link"&gt;&lt;/span&gt;PR FAQ&lt;/h2&gt;
&lt;h3&gt;
&lt;span class="octicon octicon-link"&gt;&lt;/span&gt;How long was PR open?&lt;/h3&gt;
&lt;p&gt;The PR was open for 0 days, 0 hours, 0 minutes, 4 seconds&lt;/p&gt;
&lt;h3&gt;
&lt;span class="octicon octicon-link"&gt;&lt;/span&gt;Who contributed to the PR?&lt;/h3&gt;
&lt;p&gt;The following users contributed to the PR&lt;br&gt;
banjtheman&lt;/p&gt;
&lt;h3&gt;
&lt;span class="octicon octicon-link"&gt;&lt;/span&gt;What files changed in the PR?&lt;/h3&gt;
&lt;p&gt;The following files changed in the PR&lt;br&gt;
templates/prfaq-template.md&lt;/p&gt;
&lt;h3&gt;
&lt;span class="octicon octicon-link"&gt;&lt;/span&gt;How many lines of code changed in the PR?&lt;/h3&gt;
&lt;p&gt;+5 -5 lines of code changed&lt;/p&gt;

    &lt;/div&gt;
    &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/banjtheman/prfaq_action/pull/34#issuecomment-964757256" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;


&lt;p&gt;The Pull Request FAQs (PRFAQs) is a mechanism to automatically capture relevant data points on a finished Pull Request. The PRFAQs currently answers the following questions.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;How long was PR open?&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Who contributed to the PR?&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;What files changed in the PR?&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;How many lines of code changed in the PR? &lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;By leveraging existing GitHub Actions I was able to build a workflow to answer the questions.&lt;/p&gt;

&lt;h3&gt;
  
  
  My Workflow
&lt;/h3&gt;

&lt;p&gt;The following highlights how this workflow works.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;When a pull request is closed, the workflow is started.&lt;/li&gt;
&lt;li&gt;The workflow downloads the repo using the GitHub Action &lt;a href="https://github.com/actions/checkout" rel="noopener noreferrer"&gt;actions/checkout@v2&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;The workflow uses the GitHub GraphQL API using the template query to get data on the pull request using the GitHub Action &lt;a href="https://github.com/helaili/github-graphql-action" rel="noopener noreferrer"&gt;helaili/github-graphql-action@2.0.1&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;The workflow saves the output as a JSON file&lt;/li&gt;
&lt;li&gt;The workflow uses the &lt;a href="https://github.com/jannekem/run-python-script-action" rel="noopener noreferrer"&gt;jannekem/run-python-script-action@v1&lt;/a&gt; to parse the JSON file to set answers to the questions as environment variables&lt;/li&gt;
&lt;li&gt;The workflow uses the &lt;a href="https://github.com/chuhlomin/render-template" rel="noopener noreferrer"&gt;chuhlomin/render-template@v1.2&lt;/a&gt; to set the answers into the PRFAQs template &lt;/li&gt;
&lt;li&gt;The workflow uses the &lt;a href="https://github.com/peter-evans/create-or-update-comment" rel="noopener noreferrer"&gt;peter-evans/create-or-update-comment@v1&lt;/a&gt; to post the answers as a comment on the Pull Request &lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Submission Category: Maintainer Must-Haves
&lt;/h3&gt;

&lt;p&gt;While Maintainers can find this information by scrolling through each Pull Request the PRFAQ workflow consolidates the effort into a single comment and provides vital information not found directly such as &lt;a href="https://stackoverflow.com/questions/55861526/how-can-i-track-how-long-pull-requests-have-been-open-on-github" rel="noopener noreferrer"&gt;how long the PR was open for&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Yaml File and Link to Code
&lt;/h3&gt;

&lt;p&gt;Here is the GitHub Repo&lt;br&gt;
&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/banjtheman" rel="noopener noreferrer"&gt;
        banjtheman
      &lt;/a&gt; / &lt;a href="https://github.com/banjtheman/prfaq_action" rel="noopener noreferrer"&gt;
        prfaq_action
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      Create a PRFAQ for merged pull requests
    &lt;/h3&gt;
  &lt;/div&gt;
&lt;/div&gt;


&lt;p&gt;Here is the Workflow Yaml&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;PRFAQ&lt;/span&gt;

&lt;span class="c1"&gt;# only trigger on pull request closed events&lt;/span&gt;
&lt;span class="na"&gt;on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;pull_request&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;types&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;closed&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
&lt;span class="na"&gt;env&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;PR_NUMBER&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ github.event.pull_request.number }}&lt;/span&gt;

&lt;span class="na"&gt;jobs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;get_pr_stats&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;runs-on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ubuntu-latest&lt;/span&gt;
    &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Checkout repository&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/checkout@v2&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;GitHub GraphQL Action&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;helaili/github-graphql-action@2.0.1&lt;/span&gt;
        &lt;span class="na"&gt;env&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;GITHUB_TOKEN&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.GITHUB_TOKEN }}&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;query&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;queries/pr_stats.yaml&lt;/span&gt;
          &lt;span class="na"&gt;logLevel&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;debug&lt;/span&gt;
          &lt;span class="na"&gt;outputFile&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;pr_stats.json&lt;/span&gt;
          &lt;span class="na"&gt;pr_num&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ env.PR_NUMBER }}&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Set env variables&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;jannekem/run-python-script-action@v1&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;script&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
            &lt;span class="s"&gt;import json&lt;/span&gt;
            &lt;span class="s"&gt;import datetime&lt;/span&gt;

            &lt;span class="s"&gt;with open("pr_stats.json", "r") as pr_file:&lt;/span&gt;
                &lt;span class="s"&gt;pr_stats = json.load(pr_file)&lt;/span&gt;

                &lt;span class="s"&gt;# Get Time Open&lt;/span&gt;
                &lt;span class="s"&gt;created_at = pr_stats["data"]["repository"]["pullRequest"]["createdAt"]&lt;/span&gt;
                &lt;span class="s"&gt;merged_at = pr_stats["data"]["repository"]["pullRequest"]["closedAt"]&lt;/span&gt;

                &lt;span class="s"&gt;created_at_timesamp = datetime.datetime.strptime(created_at, "%Y-%m-%dT%H:%M:%SZ")&lt;/span&gt;
                &lt;span class="s"&gt;merged_at_timestamp = datetime.datetime.strptime(merged_at, "%Y-%m-%dT%H:%M:%SZ")&lt;/span&gt;
                &lt;span class="s"&gt;timestamp_diff = merged_at_timestamp - created_at_timesamp&lt;/span&gt;

                &lt;span class="s"&gt;days, seconds = timestamp_diff.days, timestamp_diff.seconds&lt;/span&gt;
                &lt;span class="s"&gt;hours = days * 24 + seconds // 3600&lt;/span&gt;
                &lt;span class="s"&gt;minutes = (seconds % 3600) // 60&lt;/span&gt;
                &lt;span class="s"&gt;seconds = seconds % 60&lt;/span&gt;

                &lt;span class="s"&gt;timestamp_open = f" {days} days, {hours} hours, {minutes} minutes, {seconds} seconds"&lt;/span&gt;
                &lt;span class="s"&gt;print(timestamp_open)&lt;/span&gt;
                &lt;span class="s"&gt;set_env("PR_OPEN", timestamp_open)&lt;/span&gt;

                &lt;span class="s"&gt;# Get participants&lt;/span&gt;
                &lt;span class="s"&gt;participants = pr_stats["data"]["repository"]["pullRequest"]["participants"][&lt;/span&gt;
                    &lt;span class="s"&gt;"edges"&lt;/span&gt;
                &lt;span class="s"&gt;]&lt;/span&gt;
                &lt;span class="s"&gt;participants_string = ""&lt;/span&gt;
                &lt;span class="s"&gt;for participant in participants:&lt;/span&gt;
                    &lt;span class="s"&gt;participants_string += participant["node"]["login"] + "  "&lt;/span&gt;

                &lt;span class="s"&gt;print(participants_string)&lt;/span&gt;
                &lt;span class="s"&gt;set_env("PR_USERS", participants_string)&lt;/span&gt;

                &lt;span class="s"&gt;# Lines of code changed&lt;/span&gt;
                &lt;span class="s"&gt;additions = pr_stats["data"]["repository"]["pullRequest"]["additions"]&lt;/span&gt;
                &lt;span class="s"&gt;deletions = pr_stats["data"]["repository"]["pullRequest"]["deletions"]&lt;/span&gt;
                &lt;span class="s"&gt;content_changed = f"+{additions} -{deletions} lines of code"&lt;/span&gt;
                &lt;span class="s"&gt;print(content_changed)&lt;/span&gt;
                &lt;span class="s"&gt;set_env("PR_LOC", content_changed)&lt;/span&gt;

                &lt;span class="s"&gt;# Files that changed&lt;/span&gt;
                &lt;span class="s"&gt;files = pr_stats["data"]["repository"]["pullRequest"]["files"]["edges"]&lt;/span&gt;
                &lt;span class="s"&gt;files_string = ""&lt;/span&gt;
                &lt;span class="s"&gt;for file_path in files:&lt;/span&gt;
                    &lt;span class="s"&gt;files_string += file_path["node"]["path"] + "  "&lt;/span&gt;
                &lt;span class="s"&gt;print(files_string)&lt;/span&gt;
                &lt;span class="s"&gt;set_env("PR_FILES", files_string)&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Render template&lt;/span&gt;
        &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;template&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;chuhlomin/render-template@v1.2&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;template&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;templates/prfaq-template.md&lt;/span&gt;
          &lt;span class="na"&gt;vars&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
            &lt;span class="s"&gt;pr_open: ${{ env.PR_OPEN }}&lt;/span&gt;
            &lt;span class="s"&gt;pr_users: ${{ env.PR_USERS }}&lt;/span&gt;
            &lt;span class="s"&gt;pr_loc: ${{ env.PR_LOC }}&lt;/span&gt;
            &lt;span class="s"&gt;pr_files: ${{ env.PR_FILES }}&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Create comment&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;peter-evans/create-or-update-comment@v1&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;issue-number&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ env.PR_NUMBER }}&lt;/span&gt;
          &lt;span class="na"&gt;body&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ steps.template.outputs.result }}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Additional Resources / Info
&lt;/h3&gt;

&lt;p&gt;These are all the Actions I used to build the workflow&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;a href="https://github.com/actions/checkout" rel="noopener noreferrer"&gt;actions/checkout@v2&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/helaili/github-graphql-action" rel="noopener noreferrer"&gt;helaili/github-graphql-action@2.0.1&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/jannekem/run-python-script-action" rel="noopener noreferrer"&gt;jannekem/run-python-script-action@v1&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/chuhlomin/render-template" rel="noopener noreferrer"&gt;chuhlomin/render-template@v1.2&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/peter-evans/create-or-update-comment" rel="noopener noreferrer"&gt;peter-evans/create-or-update-comment@v1&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;What other questions should this workflow answer? Leave your suggestions in the comments below.&lt;/p&gt;

</description>
      <category>actionshackathon21</category>
      <category>github</category>
      <category>opensource</category>
    </item>
    <item>
      <title>Ideal GitHub Pull Request Labels?</title>
      <dc:creator>Banjo Obayomi</dc:creator>
      <pubDate>Thu, 04 Jun 2020 18:34:07 +0000</pubDate>
      <link>https://dev.to/banjtheman/ideal-github-pull-request-labels-2dd5</link>
      <guid>https://dev.to/banjtheman/ideal-github-pull-request-labels-2dd5</guid>
      <description>&lt;p&gt;Im currently working on a GitHub bot that uses a machine learning model that can read info from a pull request (via graphql) and automatically tag the PRs with labels like author_confidence (high/low), pr_complexity (high/low), stackoverflow_tag (python , aws, etc...)&lt;/p&gt;

&lt;p&gt;What other types of label information would add value to a PR?&lt;/p&gt;

</description>
      <category>discuss</category>
      <category>github</category>
    </item>
    <item>
      <title>DataOps Podcast: Structuring Unstructured Data </title>
      <dc:creator>Banjo Obayomi</dc:creator>
      <pubDate>Wed, 03 Jun 2020 22:03:12 +0000</pubDate>
      <link>https://dev.to/banjtheman/dataops-podcast-structuring-unstructured-data-13i2</link>
      <guid>https://dev.to/banjtheman/dataops-podcast-structuring-unstructured-data-13i2</guid>
      <description>&lt;p&gt;Join &lt;a class="mentioned-user" href="https://dev.to/banjtheman"&gt;@banjtheman&lt;/a&gt;
 and @victori_ousg, as they have a conversation about Structuring Unstructured Data with @NNData_Corp&lt;br&gt;
&lt;a href="https://www.nndata.com/"&gt;https://www.nndata.com/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://anchor.fm/dataops/episodes/DataOps-Podcast-Structuring-Unstructured-Data-eenv18/a-a2b3qb3"&gt;https://anchor.fm/dataops/episodes/DataOps-Podcast-Structuring-Unstructured-Data-eenv18/a-a2b3qb3&lt;/a&gt;&lt;/p&gt;

</description>
      <category>podcast</category>
      <category>dataops</category>
      <category>devops</category>
      <category>machinelearning</category>
    </item>
  </channel>
</rss>
