<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[Wamaitha Nyamu  Software Engineer | AI Engineer]]></title><description><![CDATA[A consummate computer scientist of towering genius and uncommon brilliance and an inspiration to many.]]></description><link>https://wamaithanyamu.com</link><generator>RSS for Node</generator><lastBuildDate>Wed, 15 Apr 2026 01:10:32 GMT</lastBuildDate><atom:link href="https://wamaithanyamu.com/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[How to transfer files from Linux to Windows: The SMB network file-sharing protocol]]></title><description><![CDATA[The SMB (Server Message Block) protocol is a widely used network file-sharing protocol that enables users to access and manage shared resources, such as files and printers. Although initially designed for Windows environments, SMB is also supported i...]]></description><link>https://wamaithanyamu.com/how-to-transfer-files-from-linux-to-windows-the-smb-network-file-sharing-protocol</link><guid isPermaLink="true">https://wamaithanyamu.com/how-to-transfer-files-from-linux-to-windows-the-smb-network-file-sharing-protocol</guid><category><![CDATA[Linux]]></category><category><![CDATA[linux for beginners]]></category><category><![CDATA[linux-commands]]></category><category><![CDATA[Windows]]></category><category><![CDATA[files]]></category><dc:creator><![CDATA[Wamaitha ]]></dc:creator><pubDate>Sun, 05 Feb 2023 20:07:48 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1675617059029/d25c4394-f118-489a-b672-4ba481190d6e.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>The SMB (Server Message Block) protocol is a widely used network file-sharing protocol that enables users to access and manage shared resources, such as files and printers. Although initially designed for Windows environments, SMB is also supported in Linux operating systems and can transfer files between Windows and Linux computers. This article will explore how to transfer files from a Linux machine to a Windows machine using the SMB protocol.</p>
<h2 id="heading-install-samba-on-linux">Install Samba on Linux</h2>
<p>Samba is an open-source software suite that provides file and print services to SMB/CIFS clients. It allows Linux computers to participate in a Windows-based network and supports accessing and sharing files and printers with Windows computers. Samba implements the SMB/CIFS protocol and can create file and printer shares on a Linux machine that can be accessed from Windows computers and access Windows files and printer shares from a Linux machine. This way, Samba enables seamless file and printer sharing between Windows and Linux computers in a mixed-operating system environment.</p>
<p>Install samba:</p>
<pre><code class="lang-bash">sudo apt install samba -y
</code></pre>
<p>On successful installation, check the smbd status</p>
<pre><code class="lang-bash">systemctl status smbd
</code></pre>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1675617411019/95176638-fcda-499c-b303-00d9135a3851.png" alt class="image--center mx-auto" /></p>
<blockquote>
<p>Replace <code>wamaitha</code> with your computer username and change <code>paths</code> to suite your needs.</p>
</blockquote>
<p>We want to share movies from Linux to Windows. Create a folder that will be the shared folder.</p>
<pre><code class="lang-bash">mkdir /media/wamaitha/data/movies
</code></pre>
<p>Give full permission to the folder.</p>
<pre><code class="lang-bash">chmod 777 /media/wamaitha/data/MOVIES
</code></pre>
<p>Create a user that will be associated with the file sharing:</p>
<pre><code class="lang-bash"> useradd sambauser
</code></pre>
<p>Create an smb password for this user</p>
<pre><code class="lang-bash">smbpasswd -a sambauser
</code></pre>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1675617838240/00d16b48-73e7-4696-a497-3748b43159f7.png" alt class="image--center mx-auto" /></p>
<p>Edit the samba config file using nano or any editor of your choice</p>
<pre><code class="lang-bash">nano /etc/samba/smb.conf
</code></pre>
<p>Scroll to the bottom of the config file and add the following:</p>
<pre><code class="lang-bash">[Movies]
path = /media/wamaitha/data/MOVIES
valid users = sambauser
<span class="hljs-built_in">read</span> only = no
browsable = yes
public = yes
writable = yes
browsable = yes
</code></pre>
<p>Save the file and check if everything was correctly configured using the following command:</p>
<pre><code class="lang-bash">testparm
</code></pre>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1675618276936/83f2d787-1320-4a58-a2a0-1aac3eed925c.png" alt class="image--center mx-auto" /></p>
<p>Check your IP address on the Linux machine using ifconfig:</p>
<pre><code class="lang-bash">ifconfig wlo1
</code></pre>
<p>On the output, the IP address is shown below:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1675626622043/3f024c3a-9520-4768-b6bf-9f8b206f0e40.png" alt class="image--center mx-auto" /></p>
<h2 id="heading-connecting-on-windows">Connecting on windows</h2>
<p>On windows launch <code>run</code> by pressing the <code>windows logo</code> + <code>R</code> .</p>
<p>Type the IP address of the Linux machine from the ipconfig output with two slashes.</p>
<pre><code class="lang-bash">\\192.168.0.101
</code></pre>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1675626246023/1649642e-7b94-4793-a06c-11801f272d6a.png" alt class="image--center mx-auto" /></p>
<p>This will open the Linux-shared folders.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1675626123532/7a8db8a4-f176-46af-8f70-021cfca57db0.png" alt class="image--center mx-auto" /></p>
<p>You can now access the files in the Movies folder on Linux from windows.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1675627065974/94b6deaf-b137-4d08-b624-57c805b745fe.png" alt class="image--center mx-auto" /></p>
<p>Resources:</p>
<p><a target="_blank" href="https://www.samba.org/samba/what_is_samba.html">https://www.samba.org/samba/what_is_samba.html</a></p>
]]></content:encoded></item><item><title><![CDATA[How to download large files on Linux from the terminal]]></title><description><![CDATA[wget is a free command line tool that downloads files from the terminal non-interactively; it can work in the background without needing the user to log on. Downloading from the browser may require the user's constant presence for downloads to finish...]]></description><link>https://wamaithanyamu.com/how-to-download-large-files-on-linux-from-the-terminal</link><guid isPermaLink="true">https://wamaithanyamu.com/how-to-download-large-files-on-linux-from-the-terminal</guid><category><![CDATA[Linux]]></category><category><![CDATA[Productivity]]></category><category><![CDATA[cli]]></category><dc:creator><![CDATA[Wamaitha ]]></dc:creator><pubDate>Thu, 05 Jan 2023 13:39:10 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1672915777739/4e529abb-d40f-4dc7-9034-a86c7af20a1d.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p><code>wget</code> is a free command line tool that downloads files from the terminal non-interactively; it can work in the background without needing the user to log on. Downloading from the browser may require the user's constant presence for downloads to finish, which is not always ideal when dealing with large file downloads.</p>
<h2 id="heading-advantages-of-using-wget">Advantages of using <code>wget</code></h2>
<ul>
<li><p><code>wget</code> is built to cater to slow and/or unstable networks. The <code>wget</code> utility retries when a download fails and supports downloading from where the last download failed (if the server the user is downloading from supports Range headers).</p>
</li>
<li><p>Furthermore, the utility allows the use of proxy servers which can aid in faster retrievals and lighter network loads.</p>
</li>
</ul>
<ul>
<li><code>wget</code> supports recursive downloads. A recursive download refers to the process of downloading a file and then using that file to download additional files, repeating this process until all necessary files have been downloaded. This is often used in downloading a website or other collection of files from the internet, where the initial file (e.g., a HTML page) contains links to other files (e.g., images, stylesheets, etc.) that are also needed to render the content fully. Therefore, <code>wget</code> can be tweaked to work as a website crawler.</li>
</ul>
<ul>
<li>Last but not least, <code>wget</code> supports downloads using the <mark>FTP</mark>, <mark>HTTP</mark>, and <mark>HTTPS</mark> protocols, all internet protocols used to transfer data between computers. <mark>FTP</mark> (File Transfer Protocol) is a standard network protocol that transfers files from one host to another over a TCP-based network. FTP allows clients to upload, download, and manage files on a remote server. <mark>HTTP</mark> (Hypertext Transfer Protocol) is a standard protocol for sending and receiving information on the World Wide Web. It is the foundation of data communication on the web and is used to transmit data from a server to a client, such as a web browser. <mark>HTTPS</mark> (HTTP Secure) is a variant of HTTP that uses a secure SSL/TLS connection to encrypt data sent between a server and a client. This makes it more difficult for someone to intercept and read the data as it is transmitted. HTTPS is commonly used to protect sensitive information, such as login credentials and financial transactions.</li>
</ul>
<p>The <code>wget</code> comes installed on most Linux distributions. To install it on Debian-based distributions use:</p>
<pre><code class="lang-bash">sudo apt install wget
</code></pre>
<p>The <code>wget</code> syntax is:</p>
<pre><code class="lang-bash">wget [options] [URL]
</code></pre>
<ul>
<li><p><code>wget</code> invokes the wget utility</p>
</li>
<li><p><code>[options]</code> instruct wget on what to do with the URL provided. Some options have a long-form and short-form version.</p>
</li>
<li><p><code>[URL]</code> file or folder to be downloaded. This can also be a website link to download</p>
</li>
</ul>
<h2 id="heading-download-options">Download Options</h2>
<p>We will cover the following download options</p>
<ol>
<li><p>Retrying when a connection fails.</p>
</li>
<li><p>Saving downloaded files with a different name.</p>
</li>
<li><p>Continuation of partially downloaded files.</p>
</li>
<li><p>Download to a specific folder.</p>
</li>
<li><p>Using proxies.</p>
</li>
<li><p>Limit download speeds.</p>
</li>
<li><p>Extracting from multiple URLs.</p>
</li>
<li><p>Mirror a webpage.</p>
</li>
<li><p>Extract entire websites.</p>
</li>
<li><p>Extract a website as if you were Googlebot.</p>
</li>
</ol>
<h3 id="heading-retrying-when-a-connection-fails">Retrying when a connection fails.</h3>
<ul>
<li><p>By default <code>wget</code> retries 20 times except for fatal errors. Examples of fatal errors are connection refused or 404 (not found) errors.</p>
<pre><code class="lang-bash">  <span class="hljs-comment"># Long form </span>
  wget --tries=number http://example.com/myfile.zip

  <span class="hljs-comment"># Short form</span>
  wget -t number http://example.com/myfile.zip
</code></pre>
<p>  The <code>number</code> can be specified as <code>0</code> or <code>inf</code> for infinite retries</p>
<pre><code class="lang-bash">  <span class="hljs-comment"># Long form </span>
  wget --tries=0 http://example.com/myfile.zip

  <span class="hljs-comment"># Short form</span>
  wget -t 0 http://example.com/myfile.zip
</code></pre>
</li>
</ul>
<h3 id="heading-saving-downloaded-files-with-a-different-name">Saving downloaded files with a different name.</h3>
<ul>
<li><p>The default behavior is to save the file with the name derived from the URL. Using the output file option, you can specify a new name for the file.</p>
<pre><code class="lang-bash">  <span class="hljs-comment"># Long form</span>
  wget -O newfilename.zip http://example.com/myfile.zip
  <span class="hljs-comment"># short form</span>
  wget --output-file=newfilename.zip http://example.com/myfile.zip
</code></pre>
<p>  In the code above, the file downloaded <code>myfile.zip</code> will be saved as <code>newfilename.zip</code> .</p>
<blockquote>
<p>However, it is important to note that the use of <code>-O</code> is <em>not</em> intended to mean simply “use the name <code>newfilename</code> instead of <code>myfilezip</code> as provided in the URL;”. Rather, it is analogous to shell redirection that works like :</p>
</blockquote>
<pre><code class="lang-bash">  wget -O - http://example.com/myfile.zip &gt; newfilename.zip
</code></pre>
<p>  <code>newfilename.zip</code> will be truncated, and all downloaded content will be saved there.</p>
</li>
</ul>
<h3 id="heading-continuation-of-partially-downloaded-files">Continuation of partially downloaded files.</h3>
<ul>
<li><p>The continue option allows the continuation of a partially downloaded file. This can be a file that was downloaded by another <code>wget</code> instance or another program.</p>
<pre><code class="lang-bash">  <span class="hljs-comment"># Long form</span>
  wget --<span class="hljs-built_in">continue</span> http://example.com/myfile.zip

  <span class="hljs-comment"># Short form</span>
  wget -c http://example.com/myfile.zip
</code></pre>
<p>  Assuming there was a partial download with the name <code>myfile.zip</code>, <code>wget</code> assumes the local copy is the first portion of the file and attempts to retrieve the rest of the file from the server using an offset equal to the length of the local file. In the event, the file found locally, and that on the server are of equal size, <code>wget</code> prints an explanatory message</p>
<p>  <img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1672921814572/e07e4f4a-1486-456d-bb23-0b54b25b2eba.png" alt class="image--center mx-auto" /></p>
<p>  Consequently, should the file on the server be smaller than the one locally, the assumption made is the file on the server is an updated version. However, if the file on the server is bigger than locally due to a new version, the download will continue and the result becomes a garbled file.</p>
<blockquote>
<p>The continue option only works with FTP servers and HTTP servers that support the Range header.</p>
</blockquote>
</li>
</ul>
<h3 id="heading-download-to-a-specific-folder">Download to a specific folder</h3>
<p>To download a file with <code>wget</code> and save it to a specific folder, the <code>-P</code> or <code>--directory-prefix</code> option followed by the path to the directory where files are to be saved are used.</p>
<p>For example:</p>
<pre><code class="lang-bash"><span class="hljs-comment"># Long Form</span>
wget -directory-prefix=/path/to/directory http://example.com/myfile.zip

<span class="hljs-comment"># Short form</span>
wget -P /path/to/directory http://example.com/myfile.zip
</code></pre>
<h3 id="heading-using-proxies">Using proxies</h3>
<p>To use a proxy with <code>wget</code>, the <code>--proxy</code> option followed by the proxy URL are used.</p>
<p>For example:</p>
<pre><code class="lang-bash">wget --proxy=http://myproxy.example.com:8080 http://example.com/myfile.zip
</code></pre>
<p>This downloads the file <a target="_blank" href="http://myfile.zip"><code>myfile.zip</code></a> from <a target="_blank" href="http://example.com"><code>http://example.com</code></a> using the HTTP proxy at <a target="_blank" href="http://myproxy.example.com:8080"><code>http://myproxy.example.com:8080</code></a>.</p>
<p>If the proxy requires authentication, use the <code>--proxy-user</code> and <code>--proxy-password</code> options to specify the username and password.</p>
<p>For example:</p>
<pre><code class="lang-bash">wget --proxy=http://myproxy.example.com:8080 --proxy-user=myusername --proxy-password=mypassword http://example.com/myfile.zip
</code></pre>
<p>The <code>--proxy-type</code> option can also be used to specify the type of proxy being used. Valid values include <code>http</code>, <code>socks4</code>, and <code>socks5</code>.</p>
<p>For example:</p>
<pre><code class="lang-bash">wget --proxy=http://myproxy.example.com:8080 --proxy-type=socks5 http://example.com/myfile.zip
</code></pre>
<p>If all downloads require going through a proxy every time, modify the <code>~/.wgetrc</code> file. You can do this with <code>nano</code> or your favorite text editor</p>
<pre><code class="lang-bash">nano ~/.wgetrc
</code></pre>
<p>Add the following lines to the file</p>
<pre><code class="lang-bash">use_proxy = on
http_proxy =  http://username:password@proxy.server.address:port/
https_proxy =  http://username:password@proxy.server.address:port/
</code></pre>
<h3 id="heading-limit-download-speeds">Limit download speeds</h3>
<p>The <code>--limit-rate</code> option is used to limit the download speed of <code>wget</code>. For example, to limit the download speed to 100KB/s:</p>
<pre><code class="lang-bash">wget --limit-rate=100k http://example.com/file.zip
</code></pre>
<p>The maximum download speed in bytes per second can be specified by using the <code>B</code> suffix. For example, to limit the download speed to 100KB/s:</p>
<pre><code class="lang-bash">wget --limit-rate=100000B http://example.com/file.zip
</code></pre>
<h3 id="heading-extracting-multiple-urls">Extracting multiple URLs</h3>
<p>To download multiple files with <code>wget</code>, specify multiple URLs on the command line, separated by spaces.</p>
<p>For example:</p>
<pre><code class="lang-bash">wget http://example.com/file1.zip http://example.com/file2.zip http://example.com/file3.zip
</code></pre>
<p>This will download the files <a target="_blank" href="http://file1.zip"><code>file1.zip</code></a>, <a target="_blank" href="http://file2.zip"><code>file2.zip</code></a>, and <a target="_blank" href="http://file3.zip"><code>file3.zip</code></a> . If you have a list of URLs in a file, you can use the <code>-i</code> or <code>--input-file</code> option to tell Wget to read the URLs from the file.</p>
<p>For example:</p>
<pre><code class="lang-bash"><span class="hljs-comment"># Long form</span>
wget --input-file=url-list.txt
<span class="hljs-comment"># Short form</span>
wget -i url-list.txt
</code></pre>
<h3 id="heading-mirror-a-webpage">Mirror a webpage</h3>
<p>To create a mirror of a webpage, you can use the <code>wget</code> command-line utility with the <code>-m</code> or <code>--mirror</code> option. This option tells <code>wget</code> to download the webpage and all the files it references (such as images, stylesheets, etc.), recursively and save them to your local machine.</p>
<p>For example:</p>
<pre><code class="lang-bash"><span class="hljs-comment"># Long form</span>
wget --mirror http://example.com

<span class="hljs-comment"># Short form</span>
wget -m http://example.com
</code></pre>
<p>This downloads the webpage at <a target="_blank" href="http://example.com"><code>http://example.com</code></a>and all the files it references, and save them to the current working directory. The files will be saved in a directory structure that mirrors the structure of the website, with the home page saved as <code>index.html</code> and other pages saved in subdirectories as necessary.</p>
<p>You can also use the <code>-k</code> or <code>--convert-links</code> option to tell <code>wget</code> to convert the links in the downloaded HTML pages to point to the local copies of the files so that the downloaded copy of the website can be browsed offline.</p>
<p>For example:</p>
<pre><code class="lang-bash"><span class="hljs-comment"># Long form</span>
wget --mirror --convert-links http://example.com

<span class="hljs-comment"># Short form</span>
wget -m -k http://example.com
</code></pre>
<h3 id="heading-extract-entire-websites">Extract entire websites</h3>
<p>To download an entire website with <code>wget</code>, you can use the <code>--recursive</code> option to follow links and download all the files it finds, recursively.</p>
<p>For example:</p>
<pre><code class="lang-bash">wget --recursive http://example.com
</code></pre>
<h3 id="heading-extract-the-website-as-googlebot">Extract the website as Googlebot</h3>
<p>To extract a website as if you were Googlebot using Wget, you can use the <code>--user-agent</code> option to set the user agent string to <code>"Googlebot"</code>. For example:</p>
<pre><code class="lang-bash">wget --user-agent=<span class="hljs-string">"Googlebot"</span> http://example.com
</code></pre>
<p>The website will be downloaded using the user agent string <code>"Googlebot"</code>, which tells the server that the request is coming from Google's web crawler. This can be useful if you want to see how Googlebot would render a website or if you want to download a website while bypassing any restrictions that may be in place based on the user agent.</p>
<p>You can also combine options demonstrated, for example:</p>
<pre><code class="lang-bash">wget --user-agent=<span class="hljs-string">"Googlebot"</span> -O example.html http://example.com
</code></pre>
<p>Resources:</p>
<p><a target="_blank" href="https://www.gnu.org/software/wget/manual/wget.html#Examples">https://www.gnu.org/software/wget/manual/wget.html#Examples</a></p>
]]></content:encoded></item><item><title><![CDATA[alias  commands in Linux]]></title><description><![CDATA[In Linux, an alias is a command that executes another command or series of commands using a different name. Aliases are typically used to make it easier to run frequently used or complex commands by typing a shorter, easier-to-remember version. The a...]]></description><link>https://wamaithanyamu.com/alias-commands-in-linux</link><guid isPermaLink="true">https://wamaithanyamu.com/alias-commands-in-linux</guid><category><![CDATA[Linux]]></category><category><![CDATA[command line]]></category><category><![CDATA[terminal]]></category><category><![CDATA[Productivity]]></category><dc:creator><![CDATA[Wamaitha ]]></dc:creator><pubDate>Sun, 01 Jan 2023 17:31:24 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1672584598168/a7cd21d9-9bdc-4f3d-8885-207f08909bdd.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>In Linux, an <code>alias</code> is a command that executes another command or series of commands using a different name. Aliases are typically used to make it easier to run frequently used or complex commands by typing a shorter, easier-to-remember version. The alias command takes the following structure:</p>
<pre><code class="lang-bash"><span class="hljs-built_in">alias</span> [alias-name]=<span class="hljs-string">'[command]'</span>
</code></pre>
<ul>
<li><p><code>alias</code>: Invokes the <code>alias</code> command.</p>
</li>
<li><p><code>alias-name</code>: An alias is a user-defined string that acts as a reference to a command, with the exception of certain special characters and the words 'alias' and 'unalias'.</p>
</li>
<li><p><code>command</code>: Specifies the command the alias references.</p>
</li>
</ul>
<p>For example:</p>
<ol>
<li><p><code>alias ll='ls -al'</code>: This alias creates a new command called <code>ll</code> that is equivalent to running the <code>ls -al</code> command. You could use this alias to list the contents of a directory in long format by typing <code>ll</code> instead of <code>ls -al</code>.</p>
</li>
<li><p><code>alias df='df -h'</code>: This alias creates a new command called <code>df</code> that is equivalent to running the <code>df -h</code> command. The <code>-h</code> flag is used to display the sizes of files in "human-readable" format (e.g., in MB or GB instead of bytes).</p>
</li>
<li><p><code>alias grep='grep --color=auto'</code>: This alias creates a new command called <code>grep</code> that is equivalent to running the <code>grep --color=auto</code> command. The <code>--color=auto</code> flag tells <code>grep</code> to highlight the matching text in the output, making it easier to spot.</p>
</li>
<li><p><code>alias shutdown='sudo shutdown -h now'</code>: This alias creates a new command called <code>shutdown</code> that is equivalent to running the <code>sudo shutdown -h now</code> command. The <code>-h</code> flag tells the <code>shutdown</code> command to halt the system, and the <code>now</code> argument tells it to do so immediately.</p>
</li>
<li><p><code>alias update='sudo apt update &amp;&amp; sudo apt upgrade -y'</code>: This alias creates a new command called <code>update</code> that is equivalent to running the <code>sudo apt update</code> and <code>sudo apt upgrade -y</code> commands. The <code>update</code> command updates the package manager's list of available packages, and the <code>upgrade</code> command installs any available updates for installed packages. The <code>-y</code> flag tells <code>apt</code> to assume "yes" as the answer to all prompts.</p>
</li>
</ol>
<p>A few ways I use the alias command in my day to day development:</p>
<h3 id="heading-copying-trading-bots-from-wine-to-my-documents">Copying trading bots from <code>wine</code> to my documents.</h3>
<p>I currently work on Linux but need to do MQL5 programming from time to time. With MQL5, I can write trading bots for clients. However, to compile the MQL5 code to an <code>.ex5</code> executable, I need to have MT5 installed. MT5 is a Windows and Mac program; therefore, I have to install <code>wine</code> on Linux. <code>Wine</code> is a compatibility layer that allows you to run Windows applications on Linux. It does this by translating the Windows application's calls into calls that the Linux operating system can understand and processing them on behalf of the application. This allows you to run Windows applications on Linux without installing a copy of the Windows operating system. Once the code is compiled into an executable, the files are stored in the <code>wine</code> drive. To copy the <code>.ex5</code> to a specific folder, I would normally run the following command</p>
<pre><code class="lang-bash"><span class="hljs-built_in">cd</span> /home/wamaitha/.wine/drive_c/Program\ Files/Deriv/MQL5
</code></pre>
<p>The <code>MQL5</code> directory has a folder that holds all the trading bots I program. Therefore, I can copy all the <code>.ex5</code> files to the <code>EAS</code> folder with the command below.</p>
<pre><code class="lang-bash">cp -r Experts/**/*.ex5 ~/Documents/FOREX/EAS
</code></pre>
<p>To make my work easier, I've written an alias command that does all this</p>
<pre><code class="lang-bash"><span class="hljs-built_in">alias</span> fxeas=<span class="hljs-string">"cd /home/wamaitha/.wine/drive_c/Program\ Files/Deriv/MQL5 &amp;&amp; cp -r Experts/**/*.ex5 ~/Documents/FOREX/EAS"</span>
</code></pre>
<h3 id="heading-activating-virtual-environments">Activating virtual environments</h3>
<p>In Python when using <code>virtualenv</code> , to activate the environment on Linux we need to type the following command:</p>
<pre><code class="lang-bash"><span class="hljs-built_in">source</span> venv/bin/activate
</code></pre>
<p>Then navigate back to the project folder</p>
<pre><code class="lang-bash"><span class="hljs-built_in">cd</span> .. &amp;&amp; <span class="hljs-built_in">cd</span> ..
</code></pre>
<p>We can write an <code>alias</code> for this</p>
<pre><code class="lang-bash"><span class="hljs-built_in">alias</span> activate=<span class="hljs-string">"source venv/bin/activate &amp;&amp; cd .. &amp;&amp; cd .."</span>
</code></pre>
<h3 id="heading-permanent-aliases">Permanent aliases</h3>
<p>Using the <code>alias</code> command only applies to the current terminal session. To make the commands useful on other terminal sessions, you would need to edit the <code>.bashrc</code> file if running <code>bash</code> or the <code>.zshrc</code> if on <code>zsh</code>. I'm currently on <code>zsh</code> , therefore I add these commands at the end of the <code>.zshrc</code> file.</p>
<p>Launch the <code>.zshrc</code> using the command below</p>
<pre><code class="lang-bash">nano ~/.zshrc
</code></pre>
<p>Add the <code>alias</code> commands at the end of the file</p>
<pre><code class="lang-bash"><span class="hljs-built_in">alias</span> activate=<span class="hljs-string">"source venv/bin/activate &amp;&amp; cd .. &amp;&amp; cd .."</span>
<span class="hljs-built_in">alias</span> fxeas=<span class="hljs-string">"cd /home/wamaitha/.wine/drive_c/Program\ Files/Deriv/MQL5 &amp;&amp; cp -r Experts/**/*.ex5 ~/Documents/FOREX/EAS"</span>
</code></pre>
<p>Run the command below to activate the changes for all terminal sessions.</p>
<pre><code class="lang-bash"><span class="hljs-built_in">source</span> ~/.zshrc
</code></pre>
]]></content:encoded></item><item><title><![CDATA[Building microservices using Terraform, Ansible, Docker, Docker Compose, and Github Actions.]]></title><description><![CDATA[Infrastructure as code(IAC) is a methodology for deploying, configuring, and managing servers. Typically when you need to host your application on the internet, you create VMs on hosting providers like Linode, Vultr, DigitalOcean, AWS, or GCP. Once t...]]></description><link>https://wamaithanyamu.com/building-microservices-using-terraform-ansible-docker-docker-compose-and-github-actions</link><guid isPermaLink="true">https://wamaithanyamu.com/building-microservices-using-terraform-ansible-docker-docker-compose-and-github-actions</guid><category><![CDATA[Terraform]]></category><category><![CDATA[ansible]]></category><category><![CDATA[Docker]]></category><category><![CDATA[Devops]]></category><category><![CDATA[Microservices]]></category><dc:creator><![CDATA[Wamaitha ]]></dc:creator><pubDate>Mon, 19 Dec 2022 20:34:45 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1674028433265/808fe0af-82bd-4716-8b7d-820976d3e1c1.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Infrastructure as code(IAC) is a methodology for deploying, configuring, and managing servers. Typically when you need to host your application on the internet, you create VMs on hosting providers like Linode, Vultr, DigitalOcean, AWS, or GCP. Once the VMs are up, you either SSH directly into the instance or use a CICD workflow to get your application running on the VM. Using IAC tools, we can automate this process. This guide will cover Terraform, Ansible, Docker, and GitHub Actions to build microservices.</p>
<p>A demo of what we will be building is shown below.</p>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://www.youtube.com/watch?v=k7f0KiQPdL0">https://www.youtube.com/watch?v=k7f0KiQPdL0</a></div>
<p> </p>
<h2 id="heading-tools-used">Tools used</h2>
<h3 id="heading-1-terraform">1. Terraform</h3>
<p>Terraform is an IAC tool widely adopted to create, modify and destroy servers. Normally you would log on to Linode and deploy a Linux Server manually while specifying how many vCPUs, RAM, and storage your project will need. Terraform allows users to write a file that, when executed, will deploy the server on Linode. Terraform also allows the user to modify the servers by scaling up or down different resources. For example, increasing the amount of RAM an existing server has. Terraform can consequently destroy all or some of the servers as needed.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1670713881038/Ij5MQrUU7.png" alt class="image--center mx-auto" /></p>
<h3 id="heading-2-ansible">2. Ansible</h3>
<p>Ansible is a suite of software IAC tools for configuration management, deployments, and software provisioning. Ansible works the same way a puppet master and his dolls do. The puppet master pulls strings that control the doll(s) to make them move or do something. The puppet master, in this case, is called the control machine, while the dolls are the remote machines. The strings that connect the doll and the puppet's hand are SSH connections. Ansible is only installed on the control machine. Therefore, the remote machines do not need special configurations making Ansible agentless.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1670714116558/R020zs3Od.png" alt class="image--center mx-auto" /></p>
<h3 id="heading-3-github-actions">3. Github Actions</h3>
<p>Github Actions is a CICD platform that allows users to automate the build, test, and deployment pipelines when an event happens. Github Actions works through workflows. A workflow is a configurable automated process running one or more jobs. A repository can have one or more workflows.</p>
<p>For example, we can have a workflow that runs tests using Jest on every push request. Yet another workflow can deploy the application on every merge to the master branch. Events trigger the workflows. An event on Github can be a pull request, push a commit, or open or closed issue. The workflow runs a series of steps known as a job. The workflow runs on servers provisioned by Github called runners. Github provisions runners for Linux, Windows, and macOS. Therefore, if you're building a program that's meant to be run on windows specifically, you can create a workflow that runs on the Windows environment.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1670755473065/i4RNdwvcW.png" alt class="image--center mx-auto" /></p>
<h3 id="heading-4-docker">4 . Docker</h3>
<p>Docker solves the classical problem of <strong><em>working on my machine but not on my friend's machine.</em></strong> Docker does this by providing a virtualization layer on the operating system it's installed on. The software is delivered in packages called <mark>containers.</mark> The container is realized from a docker image. A docker image is an immutable(Cannot be changed) snapshot of the environment in the container that will run the software. Immutability means the image can be consistently deployed in any environment.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1670755703706/TRpcmFOG9.png" alt class="image--center mx-auto" /></p>
<h3 id="heading-5-docker-compose">5. Docker Compose</h3>
<p>Compose is a tool for defining and running multi-container Docker applications. Our example will have five microservices, each on its own docker container. Using docker-compose, we will configure and manage these services. Docker compose is the puppet master, while the containers are the dolls.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1670756256706/Xtvs3Esj7.png" alt class="image--center mx-auto" /></p>
<h2 id="heading-project-overview">Project Overview</h2>
<p>This guide will use Linode as the hosting provider to deploy a full-stack application using React for the front end and five microservices on the back end. The microservices are:</p>
<ol>
<li><p>Kanye as a service - Kanye jokes being served as a Python application.</p>
</li>
<li><p>Dad Jokes as a service - dadjokes being served as a Python application.</p>
</li>
<li><p>My jokes as a service - custom jokes being served using NodeJs from a MongoDB microservice.</p>
</li>
<li><p>React is served with nginx as a web server.</p>
</li>
<li><p>MongoDB stores the jokes added to microservice 3.</p>
</li>
</ol>
<p>Since we will not be provisioning infrastructure every time, we will run Terraform on the laptop and let Github Actions handle CICD with Ansible. The initial setup steps will be:</p>
<ul>
<li><p>Generate an API key on Linode.</p>
</li>
<li><p>Install Terraform locally</p>
</li>
<li><p>Generate ssh keys.</p>
</li>
<li><p>Spin up a nanode using Terraform.</p>
</li>
<li><p>Terraform logs on to the VM and copies the public key to the nanode.</p>
</li>
</ul>
<p>Development steps once the infrastructure is up will be:</p>
<ul>
<li><p>Develop microservices in Docker containers.</p>
</li>
<li><p>Manage containers using docker-compose.</p>
</li>
<li><p>Push code to Github.</p>
</li>
<li><p>The Github runner updates the code in the VM using Ansible. Ansible will:</p>
<ul>
<li><p>Login to VM using the private key.</p>
</li>
<li><p>Copy the new code into the VM</p>
</li>
<li><p>Rebuild the docker images and spin the containers using docker-compose.</p>
</li>
</ul>
</li>
</ul>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1671448723203/cDJipNGPx.png" alt class="image--center mx-auto" /></p>
<h2 id="heading-prerequisites">Prerequisites</h2>
<ul>
<li><p><a target="_blank" href="https://github.com">Github</a> account and git installed on your laptop.</p>
</li>
<li><p><a target="_blank" href="https://python.org">Python</a>, <a target="_blank" href="https://nodejs.org">NodeJs</a>, and Npm are installed on your laptop.</p>
</li>
<li><p><a target="_blank" href="https://docs.docker.com/get-docker/">Docker</a> installed on your laptop.</p>
</li>
<li><p>Javascript and Python knowledge. The focus will be on the DevOps aspects of the project.</p>
</li>
<li><p><a target="_blank" href="https://linode.com">Linode</a> account.</p>
</li>
<li><p>Bash for executing bash scripts</p>
</li>
</ul>
<blockquote>
<p>The code on this blog was written on Ubuntu 22.04.1 LTS. You may need to do more tweaking to support your current OS.</p>
</blockquote>
<h2 id="heading-installations">Installations</h2>
<p>Install terraform on Linux.</p>
<pre><code class="lang-bash">wget -O- https://apt.releases.hashicorp.com/gpg | gpg --dearmor | sudo tee /usr/share/keyrings/hashicorp-archive-keyring.gpg &amp;&amp; 
<span class="hljs-built_in">echo</span> <span class="hljs-string">"deb [signed-by=/usr/share/keyrings/hashicorp-archive-keyring.gpg] https://apt.releases.hashicorp.com <span class="hljs-subst">$(lsb_release -cs)</span> main"</span> | sudo tee /etc/apt/sources.list.d/hashicorp.list &amp;&amp; sudo apt update &amp;&amp; sudo apt install terraform
</code></pre>
<p>Install Ansible</p>
<pre><code class="lang-bash">python3 -m pip install  ansible
</code></pre>
<p>Install ssh-pass. Used to pass password to ssh-copy-id when Terraform logs in to the VM to copy the public key.</p>
<pre><code class="lang-bash">sudo apt-get install sshpass
</code></pre>
<h2 id="heading-microservices">Microservices</h2>
<p>We will start with the backend microservices REST APIs.All services will be in the microservices folder.</p>
<h3 id="heading-dad-jokes-as-a-service">Dad Jokes as a service.</h3>
<p>The service runs a flask app using gunicorn. The folder structure is as shown below:</p>
<pre><code class="lang-bash">Project_Folder
└─── microservices
    └─── Dadjokes
         | .dockerignore
         | dadjokes.py
         | Dockerfile
         | gunicorn.sh
         | requirements.txt
         | wsgi.py
    | docker-compose.yml
</code></pre>
<p>Code in <code>.dockerignore</code> excludes the Dockerfile from being included during the copying of files to docker.</p>
<pre><code class="lang-bash"><span class="hljs-comment"># .dockerignore</span>
Dockerfile
</code></pre>
<p>Code in <code>dadjokes.py</code></p>
<pre><code class="lang-python"><span class="hljs-comment"># dadjokes.py</span>
<span class="hljs-keyword">from</span> flask <span class="hljs-keyword">import</span> Flask
<span class="hljs-keyword">import</span> requests
<span class="hljs-keyword">import</span> json
<span class="hljs-keyword">from</span> flask_cors <span class="hljs-keyword">import</span> CORS

app = Flask(__name__)
CORS(app)

<span class="hljs-meta">@app.route("/dadjoke", methods=['GET'])</span>
<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">random_dad_joke</span>():</span>
    random_dad_joke = requests.get(<span class="hljs-string">"https://icanhazdadjoke.com/"</span>,
                                   headers={<span class="hljs-string">"Accept"</span>: <span class="hljs-string">"application/json"</span>})
    random_dad_joke = json.loads(random_dad_joke.text)[<span class="hljs-string">"joke"</span>]

    <span class="hljs-keyword">return</span> json.dumps(random_dad_joke)
</code></pre>
<p>Code in <code>gunicorn.sh</code></p>
<pre><code class="lang-bash"><span class="hljs-meta">#!/bin/sh</span>
<span class="hljs-comment"># gunicorn.sh</span>
gunicorn -b :7000 --access-logfile - --error-logfile - wsgi:app
</code></pre>
<p>WSGI (Web Server Gateway Interface) is a specification for a common interface between web servers and web applications. It defines a standard way for a web server to communicate with a web application and for the web application to communicate with the web server.</p>
<p>The above command will start the gunicorn web server and run the WSGI application specified by <code>wsgi:app</code>. The <code>wsgi</code> part specifies the module that contains the application, and the <code>app</code> part specifies the name of the application within the module. In this case, the module is specified in the <code>wsgi.py</code> The server will listen for connections on the port <code>7000</code> and write access and error logs to the console. The <code>-b</code> flag specifies the address and port on which the server should listen for connections. Therefore the server will listen on port <code>7000</code> on all available network interfaces. The <code>--access-logfile</code> and <code>--error-logfile</code> flags specify the locations of the access and error log files, respectively. The <code>-</code> character indicates that the log output should be written to standard output (the console) rather than a file.</p>
<p>Code in <code>wsgi.py</code></p>
<pre><code class="lang-python"><span class="hljs-comment"># wsgi.py</span>
<span class="hljs-keyword">from</span> dadjokes <span class="hljs-keyword">import</span> app

<span class="hljs-keyword">if</span> __name__ == <span class="hljs-string">"__main__"</span>:
    app.run(debug=<span class="hljs-literal">True</span>)
</code></pre>
<p>Code in <code>requirements.txt</code></p>
<pre><code class="lang-python"><span class="hljs-comment"># requirements.txt</span>
flask
requests
gunicorn
flask-cors
</code></pre>
<p>Code in the <code>Dockerfile</code></p>
<pre><code class="lang-bash"><span class="hljs-comment"># Dockerfile</span>
FROM python:3.8-slim-buster

WORKDIR /app

COPY requirements.txt requirements.txt
RUN pip3 install -r requirements.txt

COPY . .

EXPOSE 7000
CMD [<span class="hljs-string">"./gunicorn.sh"</span>]
</code></pre>
<p>The Dockerfile creates a Docker image that will be run as a container. When the Docker image is built using this <code>Dockerfile</code>, it will create a container that has the Python 3.8 environment and installs the packages listed in <code>requirements.txt</code>, and runs the gunicorn web server on port <code>7000</code>.</p>
<p>The first line of the <code>Dockerfile</code> specifies the base image on which the container should be based. Here the base image is <code>python:3.8-slim-buster</code>, which is a minimal Python 3.8 installation on top of the Debian "Buster" operating system.</p>
<p>The <code>WORKDIR</code> instruction sets the current working directory for subsequent instructions in the <code>Dockerfile</code>. The working directory is set to <code>/app</code>.</p>
<p>The <code>COPY</code> instruction copies a file or directory from the host file system into the container. The <code>requirements.txt</code> file is copied from the host into the <code>/app</code> directory in the container.</p>
<p>The <code>RUN</code> instruction runs a command in the container. The command <code>pip3 install -r requirements.txt</code> is run, which installs the Python packages listed in the <code>requirements.txt</code> file.</p>
<p>The second <code>COPY</code> instruction copies the entire current directory (<code>.</code>) from the host into the <code>/app</code> directory in the container. This will include all the files and directories in the current directory, including the <code>gunicorn.sh</code> script.</p>
<p>The <code>EXPOSE</code> instruction specifies the port that the container should expose to the host. The container will expose port <code>7000</code>.</p>
<p>The <code>CMD</code> instruction specifies the command that should be run when the container is started. The command is <code>./gunicorn.sh</code>, which is a shell script that starts the gunicorn web server.</p>
<p>Add the service to <code>docker-compose.yml</code>.</p>
<pre><code class="lang-bash">version: <span class="hljs-string">'3.8'</span>

services:
  dadjokes:
    image: dadjokes
    container_name: dadjokes
    build:
      context: ./DadJokes
    networks:
      - dadjokes
    ports:
      - <span class="hljs-string">"7000:7000"</span>

networks:
  dadjokes:
</code></pre>
<p>The first line <code>version: '3.8'</code>specifies the version of the Docker Compose file format being used.</p>
<p>The <code>services</code> block defines the services that make up the application. The first service is<code>dadjokes</code>.</p>
<p>The <code>image</code> property specifies the name of the Docker image that should be used to create the container. The image name is <code>dadjokes</code>.</p>
<p>The <code>container_name</code> property specifies the name that should be given to the container when it is created. The specified container name is <code>dadjokes</code>.</p>
<p>The <code>build</code> block specifies the build options for the service. The <code>context</code> property specifies the directory that contains the Dockerfile used to build the image. For <code>dadjokes</code> the Dockerfile is located in the <code>./DadJokes</code> directory.</p>
<p>The <code>networks</code> block specifies the networks that the container should be connected to. The container will be connected to a network called <code>dadjokes</code>.</p>
<p>The <code>ports</code> block specifies the port mapping between the container and the host. Port <code>7000</code> on the container will be mapped to port <code>7000</code> on the host. This means that connections to port <code>7000</code> on the host will be forwarded to the container, and the container will be able to receive connections on port <code>7000.</code></p>
<p>The <code>networks</code> section defines a network called <code>dadjokes</code>. When you run <code>docker-compose up</code>, Docker will create this network if it doesn't already exist. The <code>dadjokes</code> service is then connected to this network, allowing the containers to communicate with each other over the network.</p>
<h3 id="heading-kanye-as-a-service">Kanye as a service</h3>
<p>The service runs a flask app using gunicorn. The structure of this service looks similar to what we have in the <code>dadjokes</code> service. However, the port in use is is <code>8080</code> , and the code <code>kanye.py</code> returns a Kanye Quote.</p>
<pre><code class="lang-bash">Project_Folder
    └─── microservices
        └─── DadJokes
        └─── Kanye <span class="hljs-comment"># &lt;----------------- we are here</span>
            | .dockerignore
            | Dockerfile
            | gunicorn.sh
            | kanye.py
            | requirements.txt
            | wsgi.py
       | docker-compose.yml
</code></pre>
<p>Code in <code>.dockerignore</code></p>
<pre><code class="lang-bash">Dockerfile
</code></pre>
<p>Code in <code>Dockerfile</code></p>
<pre><code class="lang-bash">FROM python:3.8-slim-buster

WORKDIR /app

COPY requirements.txt requirements.txt
RUN pip3 install -r requirements.txt

COPY . .

EXPOSE 8080 <span class="hljs-comment"># &lt;---- different port</span>
CMD [<span class="hljs-string">"./gunicorn.sh"</span>]
</code></pre>
<p>Code in <code>gunicorn.sh</code></p>
<pre><code class="lang-bash"><span class="hljs-meta">#!/bin/sh</span>
gunicorn -b :8080 --access-logfile - --error-logfile - wsgi:app
</code></pre>
<p>Code in <code>kanye.py</code></p>
<pre><code class="lang-python"><span class="hljs-keyword">from</span> flask <span class="hljs-keyword">import</span> Flask
<span class="hljs-keyword">import</span> requests
<span class="hljs-keyword">import</span> json
<span class="hljs-keyword">from</span> flask_cors <span class="hljs-keyword">import</span> CORS

app = Flask(__name__)
CORS(app)

<span class="hljs-meta">@app.route("/kanye", methods=['GET'])</span>
<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">random_kanye_joke</span>():</span>
    random_kanye_joke = requests.get(<span class="hljs-string">"https://api.kanye.rest"</span>,
                                   headers={<span class="hljs-string">"Accept"</span>: <span class="hljs-string">"application/json"</span>})
    random_kanye_joke = json.loads(random_kanye_joke.text)[<span class="hljs-string">"quote"</span>]

    <span class="hljs-keyword">return</span> json.dumps(random_kanye_joke)
</code></pre>
<p>Code in <code>requirements.txt</code></p>
<pre><code class="lang-bash">flask
requests
gunicorn
flask-cors
</code></pre>
<p>Code in <code>wsgi.py</code></p>
<pre><code class="lang-python"><span class="hljs-keyword">from</span> kanye <span class="hljs-keyword">import</span> app

<span class="hljs-keyword">if</span> __name__ == <span class="hljs-string">"__main__"</span>:
    app.run(debug=<span class="hljs-literal">True</span>)
</code></pre>
<p>Add the service to the <code>docker-compose.yml</code> file</p>
<pre><code class="lang-bash">version: <span class="hljs-string">'3.8'</span>

services:

  dadjokes:
    image: dadjokes
    container_name: dadjokes
    build:
      context: ./DadJokes
    networks:
      - dadjokes
    ports:
      - <span class="hljs-string">"7000:7000"</span>

  kanye: <span class="hljs-comment"># &lt;-------------------- kanye service added</span>
    image: kanye
    container_name: kanye
    build:
      context: ./Kanye
    networks:
      - kanye
    ports:
      - <span class="hljs-string">"8080:8080"</span>

networks:
  dadjokes:
  kanye:
</code></pre>
<h3 id="heading-mongodb-as-a-service">MongoDB as a service</h3>
<p>The service is defined in the <code>docker-compose.yml</code> file.</p>
<pre><code class="lang-bash">version: <span class="hljs-string">'3.8'</span>

services:

  dadjokes:
    image: dadjokes
    container_name: dadjokes
    build:
      context: ./DadJokes
    networks:
      - dadjokes
    ports:
      - <span class="hljs-string">"7000:7000"</span>

  kanye:
    image: kanye
    container_name: kanye
    build:
      context: ./Kanye
    networks:
      - kanye
    ports:
      - <span class="hljs-string">"8080:8080"</span>

  mongo: <span class="hljs-comment">#&lt;------------------------- add mongo</span>
    container_name: mongo
    image: mongo:5.0
    ports:
      - <span class="hljs-string">"27017:27017"</span>
    env_file:
      - .env    
    environment:
      - MONGO_INITDB_DATABASE=<span class="hljs-variable">${MONGO_INITDB_DATABASE}</span>
      - MONGO_INITDB_ROOT_USERNAME=<span class="hljs-variable">${MONGO_INITDB_ROOT_USERNAME}</span>
      - MONGO_INITDB_ROOT_PASSWORD=<span class="hljs-variable">${MONGO_INITDB_ROOT_PASSWORD}</span>
    volumes:
      - mongo-database:/data/db
    networks:
      - mongonetwork

networks:
  dadjokes:
  kanye:
  mongonetwork:

volumes:
  mongo-database:
</code></pre>
<p>The service is based on the <code>mongo:5.0</code> image and is run in a container named <code>mongo</code>. The container exposes port <code>27017</code> on the host machine, which is mapped to port <code>27017</code> in the container. Port <code>27017</code> is also the default port MongoDB runs on. This allows you to access the MongoDB database running in the container from the host machine.</p>
<p>The <code>env_file</code> and <code>environment</code> sections specify environment variables that will be passed to the container when it is started. The <code>env_file</code> section specifies a file called <code>.env</code> that contains environment variables, and the <code>environment</code> section specifies additional environment variables that will be passed to the container. These environment variables are used to configure the MongoDB database, such as the name of the database and the username and password of the root user.</p>
<p>The <code>volumes</code> section specifies a named volume called <code>mongo-database</code>, which is mapped to the <code>/data/db</code> directory in the container. This allows you to persist the data stored in the MongoDB database even if the container is stopped or removed.</p>
<p>Finally, the <code>networks</code> section specifies that the <code>mongo</code> container should be connected to a network called <code>mongonetwork</code>. This network allows the <code>mongo</code> container to communicate with other containers connected to the same network.</p>
<p>Create the .env file</p>
<pre><code class="lang-bash">Project_Folder
    └─── microservices
        └─── DadJokes
        └─── Kanye
       | docker-compose.yml 
       | .env <span class="hljs-comment"># &lt;----- add the .env</span>
</code></pre>
<p>In the .env, add the environmental variables.</p>
<pre><code class="lang-bash">MONGO_INITDB_DATABASE=admin
MONGO_INITDB_ROOT_USERNAME=root
MONGO_INITDB_ROOT_PASSWORD=example
MONGODB_CONNSTRING=mongodb://root:example@mongo:27017/<span class="hljs-built_in">test</span>?directConnection=<span class="hljs-literal">true</span>&amp;authSource=admin&amp;replicaSet=replicaset&amp;retryWrites=<span class="hljs-literal">true</span>
</code></pre>
<h3 id="heading-my-jokes-microservice">My Jokes Microservice</h3>
<p>This microservice implements two endpoints in NodeJS. One endpoint will fetch data from the MongoDB service, while the other will feed data to the database.</p>
<p>The file structure for the microservice is as follows:</p>
<pre><code class="lang-bash">
   microservices    
        └─── MyJokes   
            └─── Config
                | db.js
            └─── Controller
                | quoteController.js
            └─── Models
                | quoteModel.js
            └─── Routes
                | quoteRoute.js
            | .dockerignore
            | .gitignore
            | Dockerfile 
            | server.js
</code></pre>
<p>Initialize npm and install dependencies using npm. This will create <code>package.json</code> and <code>package-lock.json</code> files.</p>
<pre><code class="lang-bash">npm init -y &amp;&amp; npm install colors &amp;&amp; cors &amp;&amp; express &amp;&amp; mongoose
</code></pre>
<p>The <code>db.js</code> contains the connection to the mongo service. The connection string will be passed as an environment variable in <code>docker-compose.yml</code>.</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> mongoose = <span class="hljs-built_in">require</span>(<span class="hljs-string">'mongoose'</span>)
<span class="hljs-keyword">const</span> colors = <span class="hljs-built_in">require</span>(<span class="hljs-string">'colors'</span>)

<span class="hljs-keyword">const</span> connectDB = <span class="hljs-keyword">async</span> () =&gt; {
  <span class="hljs-keyword">try</span> {

    <span class="hljs-keyword">const</span> conn = <span class="hljs-keyword">await</span> mongoose.connect(process.env.MONGODB_CONNSTRING)

    <span class="hljs-built_in">console</span>.log(<span class="hljs-string">`MongoDB Connected: <span class="hljs-subst">${conn.connection.host}</span>`</span>.cyan.underline)
  } <span class="hljs-keyword">catch</span> (error) {
    <span class="hljs-built_in">console</span>.error(<span class="hljs-string">`Error: <span class="hljs-subst">${error.message}</span>`</span>.red.underline.bold)
    process.exit(<span class="hljs-number">1</span>)
  }
}

<span class="hljs-built_in">module</span>.exports = {connectDB}
</code></pre>
<p>The <code>quoteModel.js</code> contains the Mongoose schema of the data being saved to Mongo. The data will be saved to the <code>Quote</code> collection.</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> mongoose = <span class="hljs-built_in">require</span>(<span class="hljs-string">'mongoose'</span>)

<span class="hljs-keyword">const</span> quotesSchema = <span class="hljs-keyword">new</span> mongoose.Schema({
    <span class="hljs-attr">quote</span>: {
        <span class="hljs-attr">type</span>: <span class="hljs-built_in">String</span>,
        <span class="hljs-attr">required</span>: <span class="hljs-literal">true</span>
    },
},
    {
        <span class="hljs-attr">timestamps</span>: <span class="hljs-literal">true</span>,
    }
)

<span class="hljs-built_in">module</span>.exports = mongoose.model(<span class="hljs-string">'Quote'</span>, quotesSchema)
</code></pre>
<p>The <code>quoteController.js</code> contains business logic for interfacing with the database and performing CRUD operations.</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> Quote = <span class="hljs-built_in">require</span>(<span class="hljs-string">'../Models/quoteModel'</span>)


<span class="hljs-comment">// @desc    Fetch random quote</span>
<span class="hljs-comment">// @route   GET /api/quote</span>
<span class="hljs-comment">// @access  Public</span>
<span class="hljs-keyword">const</span> randomQuote = <span class="hljs-keyword">async</span> (req, res) =&gt; {
    <span class="hljs-keyword">try</span> {

        Quote.count().exec(<span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params">err, count</span>) </span>{
            <span class="hljs-comment">// Get a random entry</span>
            <span class="hljs-keyword">const</span> random = <span class="hljs-built_in">Math</span>.floor(<span class="hljs-built_in">Math</span>.random() * count)
            <span class="hljs-comment">// Again query all users but only fetch one offset by our random #</span>
            Quote.findOne().skip(random).exec(
                <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params">err, result</span>) </span>{
                    <span class="hljs-comment">// Tada! random user</span>
                    <span class="hljs-keyword">return</span> res.json(result)
                })
        })

    } <span class="hljs-keyword">catch</span> (error) {
        <span class="hljs-built_in">console</span>.error(error)
        res.status(<span class="hljs-number">500</span>).json({ <span class="hljs-attr">message</span>: <span class="hljs-string">'Server Error'</span> })
    }
}

<span class="hljs-keyword">const</span> postQuote = <span class="hljs-keyword">async</span> (req, res) =&gt; {
    <span class="hljs-keyword">try</span> {

        <span class="hljs-built_in">console</span>.log(req.body)

        <span class="hljs-keyword">const</span> { quote } = req.body

        <span class="hljs-keyword">const</span> newQuote = <span class="hljs-keyword">new</span> Quote({
            quote
        })
        <span class="hljs-keyword">const</span> createdQuote = <span class="hljs-keyword">await</span> newQuote.save()
        res.json(createdQuote)
    } <span class="hljs-keyword">catch</span> (error) {
        <span class="hljs-built_in">console</span>.error(error)
        res.status(<span class="hljs-number">500</span>).json({ <span class="hljs-attr">message</span>: <span class="hljs-string">'Server Error'</span> })
    }
}

<span class="hljs-built_in">module</span>.exports = { randomQuote, postQuote }
</code></pre>
<p>The <code>quoteRoute.js</code> defines the API routes for the two controllers. Fetching a new quote is done using <code>get</code> while saving to the DB uses <code>post.</code></p>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> express = <span class="hljs-built_in">require</span>(<span class="hljs-string">'express'</span>)

<span class="hljs-keyword">const</span> router = express.Router()

<span class="hljs-keyword">const</span> { randomQuote,postQuote } = <span class="hljs-built_in">require</span>(<span class="hljs-string">'../Controller/quoteController.js'</span>)

router.route(<span class="hljs-string">'/'</span>).get(randomQuote)

router.route(<span class="hljs-string">'/new'</span>).post(postQuote)

<span class="hljs-built_in">module</span>.exports = router
</code></pre>
<p>The <code>server.js</code> will start the express server on port <code>3030</code> after connecting to the DB and serving the API on <code>localhost:3030</code></p>
<pre><code class="lang-javascript">
<span class="hljs-keyword">const</span>  express = <span class="hljs-built_in">require</span>(<span class="hljs-string">'express'</span>)
<span class="hljs-keyword">const</span> cor = <span class="hljs-built_in">require</span>(<span class="hljs-string">'cors'</span>)
<span class="hljs-keyword">const</span>  {connectDB} = <span class="hljs-built_in">require</span>(<span class="hljs-string">'./Config/db.js'</span>)
<span class="hljs-keyword">const</span> quoteRoute = <span class="hljs-built_in">require</span>(<span class="hljs-string">'./Routes/quoteRoute.js'</span>)
<span class="hljs-comment">// Constants</span>
<span class="hljs-keyword">const</span> PORT = <span class="hljs-number">3030</span>;
<span class="hljs-comment">// App</span>
<span class="hljs-keyword">const</span> app = express();
app.use(cor())
app.use(express.json());
app.use(<span class="hljs-string">'/api/quote'</span>, quoteRoute)
<span class="hljs-keyword">const</span> start = <span class="hljs-keyword">async</span> () =&gt; {
  <span class="hljs-keyword">try</span> {
    <span class="hljs-keyword">await</span> connectDB()
    app.listen(PORT,  <span class="hljs-function">() =&gt;</span> {
      <span class="hljs-built_in">console</span>.log(<span class="hljs-string">`Running on <span class="hljs-subst">${PORT}</span>`</span>);
    });
  } <span class="hljs-keyword">catch</span> (error) {
    <span class="hljs-built_in">console</span>.log(error);
  }
}

start()
</code></pre>
<p>The <code>.dockerignore</code> has:</p>
<pre><code class="lang-bash">node_modules
Dockerfile
</code></pre>
<p>The <code>Dockerfile</code> Has:</p>
<pre><code class="lang-yaml"><span class="hljs-string">FROM</span> <span class="hljs-string">node:16</span>

<span class="hljs-comment"># Create app directory</span>
<span class="hljs-string">WORKDIR</span> <span class="hljs-string">/app</span>

<span class="hljs-comment"># Install app dependencies</span>
<span class="hljs-string">COPY</span> <span class="hljs-string">package.json</span> <span class="hljs-string">.</span>
<span class="hljs-string">RUN</span> <span class="hljs-string">npm</span> <span class="hljs-string">install</span>
<span class="hljs-comment"># If you are building your code for production</span>
<span class="hljs-string">RUN</span> <span class="hljs-string">npm</span> <span class="hljs-string">ci</span> <span class="hljs-string">--only=production</span>

<span class="hljs-comment"># Bundle app source</span>
<span class="hljs-string">COPY</span> <span class="hljs-string">.</span> <span class="hljs-string">.</span>
<span class="hljs-string">EXPOSE</span> <span class="hljs-number">3030</span>

<span class="hljs-string">CMD</span> [ <span class="hljs-string">"node"</span>, <span class="hljs-string">"server.js"</span> ]
</code></pre>
<p>The Dockerfile begins by specifying the base image to use as the starting point for the image being built. The base image is <code>node:16</code>, which is a version of the Node.js runtime.</p>
<p>Next, the <code>WORKDIR</code> instruction sets the working directory for subsequent instructions. Any files or directories added to the image will be placed in the <code>/app</code> directory.</p>
<p>The <code>COPY</code> instruction copies the <code>package.json</code> file from the source directory (the directory containing the Dockerfile) to the <code>/app</code> directory in the image.</p>
<p>The <code>RUN</code> instruction then runs the <code>npm install</code> command, which installs the dependencies listed in the <code>package.json</code> file. The <code>npm ci</code> command is similar to <code>npm install</code>, but is intended to be used in automated environments like continuous integration and deployment, where the package-lock.json file is also checked into version control.</p>
<p>The <code>COPY</code> instruction then copies all files and directories in the source directory (including the <code>package.json</code> file and the dependencies that were just installed) to the <code>/app</code> directory in the image.</p>
<p>The <code>EXPOSE</code> instruction indicates that the container listens on the specified port at runtime. The container will listen on port <code>3030</code>.</p>
<p>Finally, the <code>CMD</code> instruction specifies the command to run when the container is started. The command is <code>node server.js</code>, which runs the Node.js server.</p>
<p>Add the service to <code>docker-compose.yml</code></p>
<pre><code class="lang-javascript">version: <span class="hljs-string">'3.8'</span>

<span class="hljs-attr">services</span>:

  dadjokes:
    image: dadjokes
    <span class="hljs-attr">container_name</span>: dadjokes
    <span class="hljs-attr">build</span>:
      context: ./DadJokes
    <span class="hljs-attr">networks</span>:
      - dadjokes
    <span class="hljs-attr">ports</span>:
      - <span class="hljs-string">"7000:7000"</span>

  <span class="hljs-attr">kanye</span>:
    image: kanye
    <span class="hljs-attr">container_name</span>: kanye
    <span class="hljs-attr">build</span>:
      context: ./Kanye
    <span class="hljs-attr">networks</span>:
      - kanye
    <span class="hljs-attr">ports</span>:
      - <span class="hljs-string">"8080:8080"</span>

  <span class="hljs-attr">mongo</span>:
    container_name: mongo
    <span class="hljs-attr">image</span>: mongo:<span class="hljs-number">5.0</span>
    <span class="hljs-attr">ports</span>:
      - <span class="hljs-string">"27017:27017"</span>
    <span class="hljs-attr">env_file</span>:
      - .env    
    <span class="hljs-attr">environment</span>:
      - MONGO_INITDB_DATABASE=${MONGO_INITDB_DATABASE}
      - MONGO_INITDB_ROOT_USERNAME=${MONGO_INITDB_ROOT_USERNAME}
      - MONGO_INITDB_ROOT_PASSWORD=${MONGO_INITDB_ROOT_PASSWORD}
    <span class="hljs-attr">volumes</span>:
      - mongo-database:<span class="hljs-regexp">/data/</span>db
    <span class="hljs-attr">networks</span>:
      - mongonetwork

  <span class="hljs-attr">myjokes</span>:
    container_name: myjokes
    <span class="hljs-attr">image</span>: myjokes
    <span class="hljs-attr">build</span>:
      context: ./MyJokes
    <span class="hljs-attr">ports</span>:
      - <span class="hljs-string">"3030:3030"</span>
    <span class="hljs-attr">networks</span>:
      - myjokes
      - mongonetwork
    <span class="hljs-attr">links</span>:
      - mongo
    <span class="hljs-attr">depends_on</span>:
      - mongo
    <span class="hljs-attr">env_file</span>:
      - .env
    <span class="hljs-attr">environment</span>:
      - MONGODB_CONNSTRING=${MONGODB_CONNSTRING}

<span class="hljs-attr">networks</span>:
  dadjokes:
  kanye:
  myjokes:
  mongonetwork:

volumes:
  mongo-database:
</code></pre>
<p>The <code>myjokes</code> container uses an image named <code>myjokes</code>, which is built from the context in the <code>./MyJokes</code> directory. The <code>ports</code> configuration maps the container's port <code>3030</code> to the host machine's port <code>3030</code>.</p>
<p>The <code>networks</code> configuration specifies the networks that the container should be connected to. The container is connected to two networks: <code>myjokes</code> and <code>mongonetwork</code>. We connect to the <code>mongonetwork</code> to access the database.</p>
<p>The <code>links</code> configuration specifies any containers that the container depends on. The container depends on the <code>mongo</code> container.</p>
<p>The <code>depends_on</code> configuration specifies any containers that must be started before the container can be started. The container is only spin up after the mongo container is up.</p>
<p>The <code>env_file</code> configuration specifies a file containing environment variables that should be passed to the container. <code>.env</code> in this case.</p>
<p>The <code>environment</code> configuration specifies additional environment variables that should be passed to the container. The <code>MONGODB_CONNSTRING</code> environment variable is being set to the value of the <code>MONGODB_CONNSTRING</code> variable in the <code>.env</code> file.</p>
<h3 id="heading-react-microservice">React Microservice</h3>
<p>The front end will be built using ReactJS. The front end will contain components that interact with all our backend microservices. The dashboard will have four components:</p>
<ul>
<li><p>Kanye-as-a-service component will display data from the Kanye quotes backend microservice.</p>
</li>
<li><p>Dadjokes-as-a-service will display data from the dadjokes quotes backend microservice.</p>
</li>
<li><p>Custom quotes will be what we create and store on the MongoDB microservice.</p>
</li>
<li><p>An input component for adding custom quotes to the MongoDB microservice.</p>
</li>
</ul>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1671465613843/3iElMNZHy.png" alt class="image--center mx-auto" /></p>
<p>On the project folder, create a react app with npm</p>
<pre><code class="lang-bash">npx create-react-app frontend
</code></pre>
<p>The above command will create a <code>frontend</code> directory. Your current directory structure should now be as follows:</p>
<pre><code class="lang-bash">microservices
    └───Dadjokes
    └───frontend <span class="hljs-comment"># &lt;--- new react folder </span>
    └─── Kanye
    └───MyJokes
    | docker-compose.yml
</code></pre>
<p>Switch to the <code>frontend</code> directory</p>
<pre><code class="lang-bash"><span class="hljs-built_in">cd</span> frontend
</code></pre>
<p>and install Tailwind CSS</p>
<pre><code class="lang-bash">npm install -D tailwindcss
</code></pre>
<p>Initialize Tailwind CSS</p>
<pre><code class="lang-bash">npx tailwindcss init
</code></pre>
<p>In the <code>tailwind.config.js</code> file created by running the above command, point the content to all subfolders within <code>src</code> folder.</p>
<pre><code class="lang-javascript"><span class="hljs-comment">// frontend/tailwind.config.js</span>
<span class="hljs-comment">/** <span class="hljs-doctag">@type <span class="hljs-type">{import('tailwindcss').Config}</span> </span>*/</span>
<span class="hljs-built_in">module</span>.exports = {
  <span class="hljs-attr">content</span>: [<span class="hljs-string">"./src/**/*.js"</span>],
  <span class="hljs-attr">theme</span>: {
    <span class="hljs-attr">extend</span>: {},
  },
  <span class="hljs-attr">plugins</span>: [],
}
</code></pre>
<p>Add tailwind to <code>src/App.css</code> .</p>
<pre><code class="lang-css"><span class="hljs-comment">/* frontend/src/App.css */</span>
<span class="hljs-keyword">@tailwind</span> base;
<span class="hljs-keyword">@tailwind</span> components;
<span class="hljs-keyword">@tailwind</span> utilities;
</code></pre>
<p>Add a script in <code>package.json</code> that compiles Tailwind to <code>public/style.css</code></p>
<pre><code class="lang-json"><span class="hljs-comment">// frontend/package.json</span>
...
  <span class="hljs-string">"scripts"</span>: {
    <span class="hljs-attr">"start"</span>: <span class="hljs-string">"react-scripts start"</span>,
    <span class="hljs-attr">"css"</span>: <span class="hljs-string">"npx tailwindcss -i ./src/App.css -o ./public/syle.css --watch"</span>,
    <span class="hljs-attr">"dev"</span>: <span class="hljs-string">"npm run start &amp;&amp; npm run css"</span>,
    <span class="hljs-attr">"build"</span>: <span class="hljs-string">"react-scripts build"</span>,
    <span class="hljs-attr">"test"</span>: <span class="hljs-string">"react-scripts test"</span>,
    <span class="hljs-attr">"eject"</span>: <span class="hljs-string">"react-scripts eject"</span>
  },
....
</code></pre>
<p>Add the compiled CSS to the <code>public/index.html</code> file in the public directory.</p>
<pre><code class="lang-xml"><span class="hljs-comment">&lt;!--frontend/public/index.html  --&gt;</span>
<span class="hljs-meta">&lt;!DOCTYPE <span class="hljs-meta-keyword">html</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">html</span> <span class="hljs-attr">lang</span>=<span class="hljs-string">"en"</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">head</span>&gt;</span>
...

<span class="hljs-tag">&lt;<span class="hljs-name">link</span> <span class="hljs-attr">rel</span>=<span class="hljs-string">"stylesheet"</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"style.css"</span>/&gt;</span>

...
<span class="hljs-tag">&lt;/<span class="hljs-name">head</span>&gt;</span>
</code></pre>
<p>Create the following files and folders in the <code>src</code> folder</p>
<pre><code class="lang-bash">frontend
   └─── src
        └─── Components <span class="hljs-comment"># &lt;-----folder</span>
             | Dadjokes.js
             | Kanye.js
             | MyJokes.js
             | NewJoke.js
        └─── config <span class="hljs-comment"># &lt;-----folder</span>
             | config.js
</code></pre>
<p>The <code>config/config.js</code> file contains all the URLs to REST APIs from the kanye, dadjokes and myjokes microservices. Its contents are:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> {
    <span class="hljs-attr">services</span>: {
      <span class="hljs-attr">kanye</span>: process.env.REACT_APP_KANYE_SERVICE_URL ,
      <span class="hljs-attr">dadjokes</span>: process.env.REACT_APP_DADJOKES_SERVICE_URL,
      <span class="hljs-attr">myjokes</span>: process.env.REACT_APP_MYJOKES_SERVICE_URL

    }
  }
</code></pre>
<p>The <code>Components/Dadjokes.js</code> will use the dadjoke endpoint from the config file to fetch a dadjoke from the dadjoke container. Its contents are:</p>
<pre><code class="lang-javascript"><span class="hljs-comment">// frontend/Components/Dadjokes.js</span>
<span class="hljs-keyword">import</span> {useEffect,useState} <span class="hljs-keyword">from</span> <span class="hljs-string">'react'</span>
<span class="hljs-keyword">import</span> config <span class="hljs-keyword">from</span> <span class="hljs-string">'../config/config'</span>
<span class="hljs-keyword">const</span> Dadjokes = <span class="hljs-function">() =&gt;</span> {

    <span class="hljs-keyword">const</span> [jokes, setJokes] = useState([])
    <span class="hljs-keyword">const</span> [loading, setLoading] = useState(<span class="hljs-literal">false</span>)

    <span class="hljs-keyword">const</span> getJokes = <span class="hljs-keyword">async</span> (e = <span class="hljs-literal">undefined</span>) =&gt; {
        <span class="hljs-keyword">if</span>(e) e.preventDefault()
        setLoading(<span class="hljs-literal">true</span>)
        <span class="hljs-keyword">const</span> res = <span class="hljs-keyword">await</span> fetch(config.services.dadjokes)
        <span class="hljs-keyword">const</span> data = <span class="hljs-keyword">await</span> res.json()
        setJokes(data)
        setLoading(<span class="hljs-literal">false</span>)
    }

    useEffect( <span class="hljs-function">() =&gt;</span> {
        getJokes()
    }, [])



  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"flex font-mono"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"flex-none w-56 relative"</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">img</span> <span class="hljs-attr">src</span>=<span class="hljs-string">"images/leo.jpg"</span> <span class="hljs-attr">alt</span>=<span class="hljs-string">""</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"absolute inset-0 w-full h-full object-cover rounded-lg"</span> <span class="hljs-attr">loading</span>=<span class="hljs-string">"lazy"</span> /&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">form</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"flex-auto p-6 m-10  "</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"flex flex-wrap border-green-600"</span>&gt;</span>

            <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"w-full flex-none mt-2 order-1 text-2xl font-bold text-slate-600"</span>&gt;</span>
                {loading ? 'Loading...' : jokes}
            <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"text-sm font-medium text-slate-400"</span>&gt;</span>
                Dadjokes as a service
            <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>

        <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"flex space-x-4 mb-5 text-sm font-medium py-4"</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"flex-auto flex space-x-4"</span> <span class="hljs-attr">onClick</span>=<span class="hljs-string">{(e)</span>=&gt;</span>  getJokes(e)}&gt;
                <span class="hljs-tag">&lt;<span class="hljs-name">button</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"h-10 px-6 font-semibold rounded-full bg-violet-600 text-white"</span>&gt;</span>
                    New Dadjoke
                <span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>

            <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">button</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"flex-none flex items-center justify-center w-9 h-9 rounded-full text-green-600 bg-violet-50"</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"button"</span> <span class="hljs-attr">aria-label</span>=<span class="hljs-string">"Like"</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">svg</span> <span class="hljs-attr">width</span>=<span class="hljs-string">"20"</span> <span class="hljs-attr">height</span>=<span class="hljs-string">"20"</span> <span class="hljs-attr">fill</span>=<span class="hljs-string">"currentColor"</span> <span class="hljs-attr">aria-hidden</span>=<span class="hljs-string">"true"</span>&gt;</span>
                    <span class="hljs-tag">&lt;<span class="hljs-name">path</span> <span class="hljs-attr">fill-rule</span>=<span class="hljs-string">"evenodd"</span> <span class="hljs-attr">clip-rule</span>=<span class="hljs-string">"evenodd"</span> <span class="hljs-attr">d</span>=<span class="hljs-string">"M3.172 5.172a4 4 0 015.656 0L10 6.343l1.172-1.171a4 4 0 115.656 5.656L10 17.657l-6.828-6.829a4 4 0 010-5.656z"</span> /&gt;</span>
                <span class="hljs-tag">&lt;/<span class="hljs-name">svg</span>&gt;</span>
            <span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">p</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"text-sm text-slate-500"</span>&gt;</span>
            Coming from the Dadjokes microservice
        <span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">form</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span></span>

        )
}

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> Dadjokes
</code></pre>
<p>The <code>Components\Kanye.js</code> will use the Kanye service endpoint from the config file to fetch a Kanye quote from the Kanye container. Its contents are:</p>
<pre><code class="lang-javascript"><span class="hljs-comment">// frontend/Components/Kanye.js</span>
<span class="hljs-keyword">import</span> {useEffect,useState} <span class="hljs-keyword">from</span> <span class="hljs-string">'react'</span>
<span class="hljs-keyword">import</span> config <span class="hljs-keyword">from</span> <span class="hljs-string">'../config/config'</span>

<span class="hljs-keyword">const</span> Kanye = <span class="hljs-function">() =&gt;</span> {
    <span class="hljs-keyword">const</span> [jokes, setJokes] = useState([])
    <span class="hljs-keyword">const</span> [loading, setLoading] = useState(<span class="hljs-literal">false</span>)

    <span class="hljs-keyword">const</span> getJokes = <span class="hljs-keyword">async</span> (e = <span class="hljs-literal">undefined</span>) =&gt; {
        <span class="hljs-keyword">if</span>(e) e.preventDefault()
        setLoading(<span class="hljs-literal">true</span>)
        <span class="hljs-keyword">const</span> res = <span class="hljs-keyword">await</span> fetch(config.services.kanye)
        <span class="hljs-keyword">const</span> data = <span class="hljs-keyword">await</span> res.json()
        setJokes(data)
        setLoading(<span class="hljs-literal">false</span>)
    }


    useEffect( <span class="hljs-function">() =&gt;</span> {
        getJokes()
    }, [])


    <span class="hljs-keyword">return</span> (

            <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"flex font-mono"</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"flex-none w-56 relative"</span>&gt;</span>
                    <span class="hljs-tag">&lt;<span class="hljs-name">img</span> <span class="hljs-attr">src</span>=<span class="hljs-string">"images/kanye.jpg"</span> <span class="hljs-attr">alt</span>=<span class="hljs-string">""</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"absolute inset-0 w-full h-full object-cover rounded-lg"</span> <span class="hljs-attr">loading</span>=<span class="hljs-string">"lazy"</span> /&gt;</span>
                <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">form</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"flex-auto p-6 m-10  "</span>&gt;</span>
                    <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"flex flex-wrap border-green-600"</span>&gt;</span>

                        <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"w-full flex-none mt-2 order-1 text-2xl font-bold text-slate-600"</span>&gt;</span>
                            {loading ? 'Loading...' : jokes}
                        <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
                        <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"text-sm font-medium text-slate-400"</span>&gt;</span>
                            Kanye as a service
                        <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
                    <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>

                    <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"flex space-x-4 mb-5 text-sm font-medium py-4"</span>&gt;</span>
                    <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"flex-auto flex space-x-4"</span> <span class="hljs-attr">onClick</span>=<span class="hljs-string">{(e)</span>=&gt;</span>  getJokes(e)}&gt;
                            <span class="hljs-tag">&lt;<span class="hljs-name">button</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"h-10 px-6 font-semibold rounded-full bg-violet-600 text-white"</span> &gt;</span>
                                New Kanye
                            <span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>

                        <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
                        <span class="hljs-tag">&lt;<span class="hljs-name">button</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"flex-none flex items-center justify-center w-9 h-9 rounded-full text-green-600 bg-violet-50"</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"button"</span> <span class="hljs-attr">aria-label</span>=<span class="hljs-string">"Like"</span>&gt;</span>
                            <span class="hljs-tag">&lt;<span class="hljs-name">svg</span> <span class="hljs-attr">width</span>=<span class="hljs-string">"20"</span> <span class="hljs-attr">height</span>=<span class="hljs-string">"20"</span> <span class="hljs-attr">fill</span>=<span class="hljs-string">"currentColor"</span> <span class="hljs-attr">aria-hidden</span>=<span class="hljs-string">"true"</span>&gt;</span>
                                <span class="hljs-tag">&lt;<span class="hljs-name">path</span> <span class="hljs-attr">fill-rule</span>=<span class="hljs-string">"evenodd"</span> <span class="hljs-attr">clip-rule</span>=<span class="hljs-string">"evenodd"</span> <span class="hljs-attr">d</span>=<span class="hljs-string">"M3.172 5.172a4 4 0 015.656 0L10 6.343l1.172-1.171a4 4 0 115.656 5.656L10 17.657l-6.828-6.829a4 4 0 010-5.656z"</span> /&gt;</span>
                            <span class="hljs-tag">&lt;/<span class="hljs-name">svg</span>&gt;</span>
                        <span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>
                    <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
                    <span class="hljs-tag">&lt;<span class="hljs-name">p</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"text-sm text-slate-500"</span>&gt;</span>
                        Coming from the Kanye microservice
                    <span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
                <span class="hljs-tag">&lt;/<span class="hljs-name">form</span>&gt;</span>
            <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span></span>


    )
}

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> Kanye
</code></pre>
<p>The <code>Components\MyJokes.js</code> will use the myjokes service endpoint from the config file to fetch a joke from the MongoDB container. Its contents are:</p>
<pre><code class="lang-javascript"><span class="hljs-comment">// frontend/Components/MyJokes.js</span>
<span class="hljs-keyword">import</span> { useState ,useEffect} <span class="hljs-keyword">from</span> <span class="hljs-string">'react'</span>
<span class="hljs-keyword">import</span> config <span class="hljs-keyword">from</span> <span class="hljs-string">'../config/config'</span>
<span class="hljs-keyword">const</span> MyJokes = <span class="hljs-function">() =&gt;</span> {

    <span class="hljs-keyword">const</span> [jokes, setJokes] = useState([])
    <span class="hljs-keyword">const</span> [loading, setLoading] = useState(<span class="hljs-literal">false</span>)

    <span class="hljs-keyword">const</span> getJokes = <span class="hljs-keyword">async</span> (e = <span class="hljs-literal">undefined</span>) =&gt; {
        <span class="hljs-keyword">if</span>(e) e.preventDefault()

        setLoading(<span class="hljs-literal">true</span>)
        <span class="hljs-keyword">const</span> res = <span class="hljs-keyword">await</span> fetch(config.services.myjokes)
        <span class="hljs-keyword">const</span> data = <span class="hljs-keyword">await</span> res.json()
        setJokes(data[<span class="hljs-string">'quote'</span>])
        setLoading(<span class="hljs-literal">false</span>)
    }

    useEffect( <span class="hljs-function">() =&gt;</span> {
        getJokes()
    }, [])



    <span class="hljs-keyword">return</span> (
        <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"flex font-mono"</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"flex-none w-56 relative"</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">img</span> <span class="hljs-attr">src</span>=<span class="hljs-string">"images/joker.jpg"</span> <span class="hljs-attr">alt</span>=<span class="hljs-string">""</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"absolute inset-0 w-full h-full object-cover rounded-lg"</span> <span class="hljs-attr">loading</span>=<span class="hljs-string">"lazy"</span> /&gt;</span>
            <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">form</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"flex-auto p-6 m-10  "</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"flex flex-wrap border-green-600"</span>&gt;</span>

                    <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"w-full flex-none mt-2 order-1 text-2xl font-bold text-slate-600"</span>&gt;</span>
                       {loading ? 'Loading...' : jokes}

                    <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
                    <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"text-sm font-medium text-slate-400"</span>&gt;</span>
                        Jokes as a service
                    <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
                <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>

                <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"flex space-x-4 mb-5 text-sm font-medium py-4"</span>&gt;</span>
                    <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"flex-auto flex space-x-4"</span> <span class="hljs-attr">onClick</span>=<span class="hljs-string">{(e)</span>=&gt;</span>  getJokes(e)}&gt;
                        <span class="hljs-tag">&lt;<span class="hljs-name">button</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"h-10 px-6 font-semibold rounded-full bg-violet-600 text-white"</span> &gt;</span>
                           Load New Joke 
                        <span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>

                    <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
                    <span class="hljs-tag">&lt;<span class="hljs-name">button</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"flex-none flex items-center justify-center w-9 h-9 rounded-full text-green-600 bg-violet-50"</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"button"</span> <span class="hljs-attr">aria-label</span>=<span class="hljs-string">"Like"</span>&gt;</span>
                        <span class="hljs-tag">&lt;<span class="hljs-name">svg</span> <span class="hljs-attr">width</span>=<span class="hljs-string">"20"</span> <span class="hljs-attr">height</span>=<span class="hljs-string">"20"</span> <span class="hljs-attr">fill</span>=<span class="hljs-string">"currentColor"</span> <span class="hljs-attr">aria-hidden</span>=<span class="hljs-string">"true"</span>&gt;</span>
                            <span class="hljs-tag">&lt;<span class="hljs-name">path</span> <span class="hljs-attr">fill-rule</span>=<span class="hljs-string">"evenodd"</span> <span class="hljs-attr">clip-rule</span>=<span class="hljs-string">"evenodd"</span> <span class="hljs-attr">d</span>=<span class="hljs-string">"M3.172 5.172a4 4 0 015.656 0L10 6.343l1.172-1.171a4 4 0 115.656 5.656L10 17.657l-6.828-6.829a4 4 0 010-5.656z"</span> /&gt;</span>
                        <span class="hljs-tag">&lt;/<span class="hljs-name">svg</span>&gt;</span>
                    <span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>
                <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">p</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"text-sm text-slate-500"</span>&gt;</span>
                    Coming from the MyJoke microservice
                <span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
            <span class="hljs-tag">&lt;/<span class="hljs-name">form</span>&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span></span>

    )
}

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> MyJokes.js
</code></pre>
<p>The <code>Components/NewJoke.js</code> component has a form that submits jokes to the MongoDB service. Its contents are:</p>
<pre><code class="lang-javascript"><span class="hljs-comment">// frontend/Components/NewJokes.js</span>
<span class="hljs-keyword">import</span> { useState } <span class="hljs-keyword">from</span> <span class="hljs-string">'react'</span>
<span class="hljs-keyword">import</span> config <span class="hljs-keyword">from</span> <span class="hljs-string">'../config/config'</span>

<span class="hljs-keyword">const</span> NewJoke = <span class="hljs-function">() =&gt;</span> {
    <span class="hljs-keyword">const</span> [joke, setJoke] = useState(<span class="hljs-string">''</span>)
    <span class="hljs-keyword">const</span> [showModal, setShowModal] = useState(<span class="hljs-literal">false</span>)


    <span class="hljs-keyword">const</span> handleSubmit = <span class="hljs-keyword">async</span> (e) =&gt; {
        e.preventDefault()
        <span class="hljs-keyword">let</span> formData = <span class="hljs-keyword">new</span> FormData();
        formData.append(<span class="hljs-string">'quote'</span>, joke);
        <span class="hljs-keyword">await</span> fetch(<span class="hljs-string">`<span class="hljs-subst">${config.services.myjokes}</span>/new`</span>,
            {
                <span class="hljs-attr">method</span>: <span class="hljs-string">"post"</span>,
                <span class="hljs-attr">headers</span>: {
                    <span class="hljs-string">'Accept'</span>: <span class="hljs-string">'application/json'</span>,
                    <span class="hljs-string">'Content-Type'</span>: <span class="hljs-string">'application/json'</span>
                  },
                <span class="hljs-attr">body</span>: <span class="hljs-built_in">JSON</span>.stringify({
                    <span class="hljs-attr">quote</span>: joke
                }),
            });
        setJoke(<span class="hljs-string">''</span>)
        setShowModal(<span class="hljs-literal">false</span>)

    }
    <span class="hljs-keyword">return</span> (
        <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span>&gt;</span>



            <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"flex"</span> <span class="hljs-attr">onClick</span>=<span class="hljs-string">{(e)</span> =&gt;</span> {
                e.preventDefault()
                setShowModal(!showModal)
            }}&gt;
                <span class="hljs-tag">&lt;<span class="hljs-name">a</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"/new"</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"hover:border-blue-500 hover:border-solid hover:bg-white hover:text-blue-500 group w-full flex flex-col items-center justify-center rounded-md border-2 border-dashed border-slate-300 text-sm leading-6 text-slate-900 font-medium py-3"</span>&gt;</span>
                    <span class="hljs-tag">&lt;<span class="hljs-name">svg</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"group-hover:text-blue-500 mb-1 text-slate-400"</span> <span class="hljs-attr">width</span>=<span class="hljs-string">"20"</span> <span class="hljs-attr">height</span>=<span class="hljs-string">"20"</span> <span class="hljs-attr">fill</span>=<span class="hljs-string">"currentColor"</span> <span class="hljs-attr">aria-hidden</span>=<span class="hljs-string">"true"</span>&gt;</span>
                        <span class="hljs-tag">&lt;<span class="hljs-name">path</span> <span class="hljs-attr">d</span>=<span class="hljs-string">"M10 5a1 1 0 0 1 1 1v3h3a1 1 0 1 1 0 2h-3v3a1 1 0 1 1-2 0v-3H6a1 1 0 1 1 0-2h3V6a1 1 0 0 1 1-1Z"</span> /&gt;</span>
                    <span class="hljs-tag">&lt;/<span class="hljs-name">svg</span>&gt;</span>
                    <span class="hljs-tag">&lt;<span class="hljs-name">h1</span> <span class="hljs-attr">className</span>=<span class="hljs-string">'text-2xl'</span>&gt;</span>New Joke<span class="hljs-tag">&lt;/<span class="hljs-name">h1</span>&gt;</span>
                <span class="hljs-tag">&lt;/<span class="hljs-name">a</span>&gt;</span>
            <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
            {showModal &amp;&amp;
                <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">'w-100 flex justify-center py-10  w-full'</span> &gt;</span>
                    <span class="hljs-tag">&lt;<span class="hljs-name">form</span> <span class="hljs-attr">onSubmit</span>=<span class="hljs-string">{handleSubmit}</span>&gt;</span>
                        <span class="hljs-tag">&lt;<span class="hljs-name">h1</span> <span class="hljs-attr">className</span>=<span class="hljs-string">'text-2xl'</span>&gt;</span>Create New Joke<span class="hljs-tag">&lt;/<span class="hljs-name">h1</span>&gt;</span>
                        <span class="hljs-tag">&lt;<span class="hljs-name">div</span>&gt;</span>

                            <span class="hljs-tag">&lt;<span class="hljs-name">label</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"block"</span>&gt;</span>

                                <span class="hljs-tag">&lt;<span class="hljs-name">textarea</span> <span class="hljs-attr">rows</span>=<span class="hljs-string">"5"</span> <span class="hljs-attr">cols</span>=<span class="hljs-string">"50"</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"block text-3xl text-slate-500
                                 border border-slate-500 rounded-md shadow-sm focus:border-blue-500 focus:ring-blue-500
                                 "</span>
                                    <span class="hljs-attr">value</span>=<span class="hljs-string">{joke}</span>
                                    <span class="hljs-attr">onChange</span>=<span class="hljs-string">{(e)</span> =&gt;</span> setJoke(e.target.value)}
                                &gt;

                                <span class="hljs-tag">&lt;/<span class="hljs-name">textarea</span>&gt;</span>
                            <span class="hljs-tag">&lt;/<span class="hljs-name">label</span>&gt;</span>
                        <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
                        <span class="hljs-tag">&lt;<span class="hljs-name">div</span>&gt;</span>

                            <span class="hljs-tag">&lt;<span class="hljs-name">button</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"h-10 px-6 font-semibold rounded-full bg-violet-600 text-white"</span>
                                <span class="hljs-attr">type</span>=<span class="hljs-string">'submit'</span>
                            &gt;</span>
                                Save
                            <span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>
                        <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>

                    <span class="hljs-tag">&lt;/<span class="hljs-name">form</span>&gt;</span>
                <span class="hljs-tag">&lt;/<span class="hljs-name">div</span> &gt;</span>

            }

        <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
    )
}
export default NewJoke</span>
</code></pre>
<p>All the components are rendered in <code>frontend/App.js</code></p>
<pre><code class="lang-javascript"><span class="hljs-comment">// frontend/App.js</span>
<span class="hljs-keyword">import</span> Kanye <span class="hljs-keyword">from</span> <span class="hljs-string">"./Components/Kanye"</span>;
<span class="hljs-keyword">import</span> Dadjokes <span class="hljs-keyword">from</span> <span class="hljs-string">"./Components/Dadjokes"</span>;
<span class="hljs-keyword">import</span> MyJokes <span class="hljs-keyword">from</span> <span class="hljs-string">"./Components/MyJokes"</span>;
<span class="hljs-keyword">import</span> NewJoke <span class="hljs-keyword">from</span> <span class="hljs-string">"./Components/NewJoke"</span>;
<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">App</span>(<span class="hljs-params"></span>) </span>{

  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"flex justify-center"</span>&gt;</span>

        <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"flex justify-center flex-col space-y-4 px-10 w-1/2 py-10"</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">NewJoke</span> /&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">MyJokes</span> /&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">Dadjokes</span> /&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">Kanye</span> /&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span></span>
  );
}

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> App;
</code></pre>
<p>Create the following files in the frontend folder</p>
<pre><code class="lang-bash">  └─── frontend
            └─── nginx <span class="hljs-comment"># &lt;-- create this</span>
                | nginx.conf <span class="hljs-comment"># &lt;-- create this</span>
            └─── public
            └─── src
            | .dockerignore <span class="hljs-comment"># &lt;-- create this</span>
            | .gitignore 
            | Dockerfile <span class="hljs-comment"># &lt;-- create this</span>
            | package-lock.json
            | package.json
            | tailwind.config.js
</code></pre>
<p>Since we are creating a <code>docker-compose</code> for use in production, we will need to serve React with a production-grade server. We do this using <code>nginx</code>. We will create a multi-stage <code>Dockerfile</code> that will allow us to build the react app and then serve it using an nginx image. In the nginx, we will also set up a reverse proxy. A reverse proxy is a server that sits between client computers and backend servers and forwards incoming requests to the appropriate backend server.</p>
<p>The <code>.dockerignore</code> file has the following:</p>
<pre><code class="lang-bash">Dockerfile
node_modules
</code></pre>
<p>The Dockerfile has the following:</p>
<pre><code class="lang-yaml"><span class="hljs-string">FROM</span> <span class="hljs-string">node:16</span> <span class="hljs-string">as</span> <span class="hljs-string">build</span>

<span class="hljs-string">ARG</span> <span class="hljs-string">KANYE_SERVICE_URL</span>
<span class="hljs-string">ARG</span> <span class="hljs-string">DADJOKES_SERVICE_URL</span>
<span class="hljs-string">ARG</span> <span class="hljs-string">MYJOKES_SERVICE_URL</span>
<span class="hljs-string">ARG</span> <span class="hljs-string">REACT_APP_HOST_IP_ADDRESS</span>

<span class="hljs-string">ENV</span> <span class="hljs-string">REACT_APP_HOST_IP_ADDRESS</span> <span class="hljs-string">$REACT_APP_HOST_IP_ADDRESS</span>
<span class="hljs-string">ENV</span> <span class="hljs-string">REACT_APP_KANYE_SERVICE_URL</span> <span class="hljs-string">$KANYE_SERVICE_URL</span>
<span class="hljs-string">ENV</span> <span class="hljs-string">REACT_APP_DADJOKES_SERVICE_URL</span> <span class="hljs-string">$DADJOKES_SERVICE_URL</span>
<span class="hljs-string">ENV</span> <span class="hljs-string">REACT_APP_MYJOKES_SERVICE_URL</span> <span class="hljs-string">$MYJOKES_SERVICE_URL</span>

<span class="hljs-string">WORKDIR</span> <span class="hljs-string">/app</span>

<span class="hljs-string">COPY</span> <span class="hljs-string">./package.json</span> <span class="hljs-string">/app/package.json</span>
<span class="hljs-string">COPY</span> <span class="hljs-string">./package-lock.json</span> <span class="hljs-string">/app/package-lock.json</span>

<span class="hljs-string">RUN</span> <span class="hljs-string">npm</span> <span class="hljs-string">install</span>

<span class="hljs-string">COPY</span> <span class="hljs-string">.</span> <span class="hljs-string">.</span>

<span class="hljs-string">RUN</span> <span class="hljs-string">npm</span> <span class="hljs-string">run</span> <span class="hljs-string">css</span>
<span class="hljs-string">RUN</span> <span class="hljs-string">npm</span> <span class="hljs-string">run</span> <span class="hljs-string">build</span>

<span class="hljs-string">FROM</span> <span class="hljs-string">nginx</span>

<span class="hljs-string">COPY</span> <span class="hljs-string">./nginx/nginx.conf</span> <span class="hljs-string">/etc/nginx/conf.d/default.conf</span>

<span class="hljs-string">COPY</span> <span class="hljs-string">--from=build</span> <span class="hljs-string">/app/build</span> <span class="hljs-string">/usr/share/nginx/html</span>
</code></pre>
<p>This multi-stage Dockerfile builds the React application in the first stage. The first stage uses the <code>node:16</code> image as the base image and installs the application's dependencies. It then copies the application code into the image and runs the <code>css</code> and <code>build</code> scripts to build the application. The <code>css</code> compiles Tailwind.</p>
<p>The second stage uses the <code>nginx</code> image as the base image and copies a custom Nginx configuration file into the image. It also copies the build artifacts from the first stage into the image.</p>
<p>The <code>nginx.conf</code> has the following:</p>
<pre><code class="lang-nginx"><span class="hljs-section">server</span> { 
 <span class="hljs-attribute">listen</span> <span class="hljs-number">80</span>;
 <span class="hljs-attribute">server_name</span> frontend;
 <span class="hljs-attribute">location</span> / {
   <span class="hljs-comment"># This would be the directory where your React app's static files are stored at</span>
   <span class="hljs-attribute">root</span> /usr/share/nginx/html;
   <span class="hljs-attribute">try_files</span> <span class="hljs-variable">$uri</span> /index.html;
 }

 <span class="hljs-attribute">location</span> /dadjoke {
   <span class="hljs-attribute">proxy_set_header</span> X-Real-IP <span class="hljs-variable">$remote_addr</span>;
   <span class="hljs-attribute">proxy_set_header</span> X-Forwarded-For <span class="hljs-variable">$proxy_add_x_forwarded_for</span>;
   <span class="hljs-attribute">proxy_set_header</span> X-NginX-Proxy <span class="hljs-literal">true</span>;
   <span class="hljs-attribute">proxy_pass</span> http://dadjokes:7000/dadjoke;
   <span class="hljs-attribute">proxy_ssl_session_reuse</span> <span class="hljs-literal">off</span>;
   <span class="hljs-attribute">proxy_set_header</span> Host <span class="hljs-variable">$http_host</span>;
   <span class="hljs-attribute">proxy_cache_bypass</span> <span class="hljs-variable">$http_upgrade</span>;
   <span class="hljs-attribute">proxy_redirect</span> <span class="hljs-literal">off</span>;
 }


 <span class="hljs-attribute">location</span> /kanye {
   <span class="hljs-attribute">proxy_set_header</span> X-Real-IP <span class="hljs-variable">$remote_addr</span>;
   <span class="hljs-attribute">proxy_set_header</span> X-Forwarded-For <span class="hljs-variable">$proxy_add_x_forwarded_for</span>;
   <span class="hljs-attribute">proxy_set_header</span> X-NginX-Proxy <span class="hljs-literal">true</span>;
   <span class="hljs-attribute">proxy_pass</span> http://kanye:8080/kanye;
   <span class="hljs-attribute">proxy_ssl_session_reuse</span> <span class="hljs-literal">off</span>;
   <span class="hljs-attribute">proxy_set_header</span> Host <span class="hljs-variable">$http_host</span>;
   <span class="hljs-attribute">proxy_cache_bypass</span> <span class="hljs-variable">$http_upgrade</span>;
   <span class="hljs-attribute">proxy_redirect</span> <span class="hljs-literal">off</span>;
 }


 <span class="hljs-attribute">location</span> /myjoke {
   <span class="hljs-attribute">proxy_set_header</span> X-Real-IP <span class="hljs-variable">$remote_addr</span>;
   <span class="hljs-attribute">proxy_set_header</span> X-Forwarded-For <span class="hljs-variable">$proxy_add_x_forwarded_for</span>;
   <span class="hljs-attribute">proxy_set_header</span> X-NginX-Proxy <span class="hljs-literal">true</span>;
   <span class="hljs-attribute">proxy_pass</span> http://myjokes:3030/api/quote;
   <span class="hljs-attribute">proxy_ssl_session_reuse</span> <span class="hljs-literal">off</span>;
   <span class="hljs-attribute">proxy_set_header</span> Host <span class="hljs-variable">$http_host</span>;
   <span class="hljs-attribute">proxy_cache_bypass</span> <span class="hljs-variable">$http_upgrade</span>;
   <span class="hljs-attribute">proxy_redirect</span> <span class="hljs-literal">off</span>;
 }



}
</code></pre>
<p>All the configuration blocks for the Nginx web server that sets up a reverse proxy for the <code>/dadjoke</code> , <code>/myjoke</code> , <code>/kanye</code> <code>/</code> locations.</p>
<p>The <code>proxy_set_header</code> directives set various request headers that are forwarded to the backend server. The <code>X-Real-IP</code> and <code>X-Forwarded-For</code> headers contain the client's IP address and the <code>X-NginX-Proxy</code> header is set to <code>true</code> to indicate that the request is being proxied.</p>
<p>The <code>proxy_pass</code> directive specifies the URL of the backend server to which the request should be forwarded.</p>
<p>The <code>proxy_ssl_session_reuse</code> directive is set to <code>off</code> to disable SSL session reuse, which can improve security but may also reduce performance.</p>
<p>The <code>proxy_set_header</code> directive sets the <code>Host</code> header to the value of the <code>$http_host</code> variable, which contains the hostname of the original request.</p>
<p>The <code>proxy_cache_bypass</code> directive is set to the value of the <code>$http_upgrade</code> variable, which allows the reverse proxy to pass through WebSocket and HTTP/2 requests.</p>
<p>The <code>proxy_redirect</code> directive is set to <code>off</code> to disable automatic redirection of the client to the URL specified in the <code>Location</code> header of the backend server's response.</p>
<h3 id="heading-run-all-the-microservices">Run all the microservices</h3>
<p>To run all the microservices using docker-compose run the following command on the microservices folder.</p>
<pre><code class="lang-bash">docker-compose up --force-recreate --build -d
</code></pre>
<p><code>docker-compose up</code> is a command used to start and run a Docker Compose application. The <code>--force-recreate</code> flag forces the recreation of any containers that have been previously created, which can be useful if you have made changes to the application and want to ensure that the containers are recreated with the updated configuration.</p>
<p>The <code>--build</code> flag forces a rebuild of the images, even if they are already up to date. This can be useful if you have made changes to the application code or the Dockerfile and want to ensure that the images are rebuilt with the updated code.</p>
<p>The <code>-d</code> flag runs the containers in the background, allowing you to continue using the command prompt while the containers are running.</p>
<p>This command will start and run the Docker Compose application in the background, rebuilding the images if necessary and recreating any containers that have been previously created.</p>
<p>Navigate to <code>localhost</code>, and you should see the live react app.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1671478081081/7LFNcCkkp.png" alt class="image--center mx-auto" /></p>
<h2 id="heading-provisioning-vm-with-terraform">Provisioning VM with Terraform</h2>
<p>Create a terraform folder on the project root and the subsequent files, as shown below.</p>
<pre><code class="lang-bash">Project_Folder
    └─── microservices
    └─── terraform <span class="hljs-comment"># &lt;----- new folder</span>
         | linode.tf
         | terraform.tfvars
         | variable.tf
</code></pre>
<p>Navigate to Linode dashboard and generate an API key.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1671476324024/zXc3mEZkH.png" alt class="image--center mx-auto" /></p>
<p>Paste the key in the <code>terraform.tfvars</code> file. Also, pass a strong password that terraform will use as the default password on the VMs.</p>
<pre><code class="lang-bash"><span class="hljs-comment"># variables.tf</span>
linode-token=<span class="hljs-string">"paste-token-here"</span>
root_pass=<span class="hljs-string">"a-very-strong-password"</span>
</code></pre>
<p>Define variables in the <code>variables.tf</code> file. The variables are read from the <code>variables.tfvars</code> file.</p>
<pre><code class="lang-bash"><span class="hljs-comment"># variables.tfvars</span>
variable <span class="hljs-string">"linode-token"</span> {}
variable <span class="hljs-string">"root_pass"</span> { 
}
variable <span class="hljs-string">"region"</span> {
  default = <span class="hljs-string">"us-southeast"</span>
}
</code></pre>
<p>The <code>linode.tf</code> file is where all the magic happens.</p>
<pre><code class="lang-bash"><span class="hljs-comment"># Linode Provider definition</span>
terraform {
  required_providers {
    linode = {
      <span class="hljs-built_in">source</span>  = <span class="hljs-string">"linode/linode"</span>
      version = <span class="hljs-string">"1.27.1"</span>
    }
  }
}

<span class="hljs-comment"># Configure the Linode provider</span>
provider <span class="hljs-string">"linode"</span> {
  token = var.linode-token
}
<span class="hljs-comment"># Create a Linode instance</span>
resource <span class="hljs-string">"linode_instance"</span> <span class="hljs-string">"example"</span> {
  image      = <span class="hljs-string">"linode/ubuntu20.04"</span>
  <span class="hljs-built_in">type</span>       = <span class="hljs-string">"g6-nanode-1"</span>
  region     = <span class="hljs-string">"us-east"</span>
  label      = <span class="hljs-string">"Microservice-blog"</span>
  private_ip = <span class="hljs-literal">true</span>
  root_pass  = var.root_pass
}
output <span class="hljs-string">"ip_address"</span> {
  value = linode_instance.example.ip_address
}
</code></pre>
<p>The code defines a Terraform configuration that uses the Linode provider to create a Linode instance running Ubuntu 20.04. The Linode provider is required to be version 1.27.1 and is configured with a <code>token</code> that is stored in a variable called <code>linode-token</code>.</p>
<p>The configuration then creates a Linode instance resource, specifying the image to use (<code>linode/ubuntu20.04</code>), the type of instance to create (<code>g6-nanode-1</code>), the region where the instance will be located (<code>us-east</code>), and a label for the instance (<code>Microservice-blog</code>). The instance is also configured to have a private IP address and a root password that is stored in the <code>root_pass</code> variable.</p>
<p>Finally, the configuration defines an output called <code>ip_address</code> that returns the public IP address of the created Linode instance.</p>
<p>To avoid retyping or forgetting the commands, we will build a bash script that will call terraform and perform other operations.</p>
<p>Create a Scripts folder in the project root folder.</p>
<pre><code class="lang-bash">Project_Folder
    └─── microservices
    └─── terraform 
    └─── Scripts <span class="hljs-comment"># &lt;----- new folder</span>
        | terraform_setup.sh
</code></pre>
<p>In <code>terraform_setup.sh</code> paste the code below.</p>
<pre><code class="lang-bash"><span class="hljs-meta">#!/bin/bash</span>
<span class="hljs-comment"># create the directory name of the ssh keys</span>
<span class="hljs-comment"># $HOME is a bash variable that points to the home directory of the current user</span>
<span class="hljs-built_in">echo</span> <span class="hljs-string">"-----------------------Creating ssh keys-----------------------"</span>
mkdir -p <span class="hljs-variable">$HOME</span>/.ssh/microservicekeys

<span class="hljs-comment"># create the ssh keys</span>
ssh-keygen -t rsa -b 4096 -C <span class="hljs-string">"global microservices"</span> -f <span class="hljs-variable">$HOME</span>/.ssh/microservicekeys/id_rsa -N <span class="hljs-string">""</span> &lt;&lt;&lt; y
<span class="hljs-built_in">echo</span> <span class="hljs-string">"------Adjust permissions of generated key-files locally---"</span>
chmod 0600 <span class="hljs-string">"<span class="hljs-variable">$HOME</span>/.ssh/microservicekeys/id_rsa"</span> <span class="hljs-string">"<span class="hljs-variable">$HOME</span>/.ssh/microservicekeys/id_rsa.pub"</span>

<span class="hljs-comment"># get to root directory</span>
<span class="hljs-built_in">cd</span> .. 

<span class="hljs-comment"># get into the terraform directory</span>
<span class="hljs-built_in">echo</span> <span class="hljs-string">"-----------------------Starting Terraform Execution-----------------------"</span>
<span class="hljs-built_in">cd</span> terraform

<span class="hljs-comment"># Terraform destroy</span>
<span class="hljs-built_in">echo</span> <span class="hljs-string">"----------------------- Terraform Destroy -----------------------"</span>
terraform destroy -auto-approve -lock=<span class="hljs-literal">false</span>

<span class="hljs-comment"># initialize terraform</span>
terraform init

<span class="hljs-comment"># plan terraform</span>
<span class="hljs-built_in">echo</span> <span class="hljs-string">"----------------------- Terraform Plan -----------------------"</span>
terraform plan -lock=<span class="hljs-literal">false</span>

<span class="hljs-comment"># apply terraform</span>
<span class="hljs-built_in">echo</span> <span class="hljs-string">"----------------------- Terraform Apply -----------------------"</span>
terraform apply -auto-approve -lock=<span class="hljs-literal">false</span>

<span class="hljs-comment"># save the ip address of the instance in variable ip</span>
<span class="hljs-built_in">echo</span> <span class="hljs-string">"----------------------- Terraform Output -----------------------"</span>
ip=$(terraform output -json | jq -r <span class="hljs-string">'.ip_address.value'</span>)


<span class="hljs-comment"># get back to root directory</span>
<span class="hljs-built_in">cd</span> ..

<span class="hljs-comment"># get into the terraform directory</span>
<span class="hljs-built_in">cd</span> terraform

<span class="hljs-built_in">echo</span> <span class="hljs-string">"----------------------- Copying public Key to Instance -----------------------"</span>

<span class="hljs-comment"># Read the terraform.tfvars file</span>
<span class="hljs-keyword">while</span> <span class="hljs-built_in">read</span> -r line
<span class="hljs-keyword">do</span>
  <span class="hljs-comment"># Split the line into a key and a value</span>
  key=$(<span class="hljs-built_in">echo</span> <span class="hljs-string">"<span class="hljs-variable">$line</span>"</span> | cut -d<span class="hljs-string">'='</span> -f1)
  value=$(<span class="hljs-built_in">echo</span> <span class="hljs-string">"<span class="hljs-variable">$line</span>"</span> | cut -d<span class="hljs-string">'='</span> -f2)
  <span class="hljs-comment"># If the key is "password", store the value in a bash variable</span>
  <span class="hljs-keyword">if</span> [ <span class="hljs-string">"<span class="hljs-variable">$key</span>"</span> == <span class="hljs-string">"root_pass"</span> ]; <span class="hljs-keyword">then</span>
    <span class="hljs-comment"># Delete the quotes from the value</span>
    password=$(<span class="hljs-built_in">echo</span> <span class="hljs-string">"<span class="hljs-variable">$value</span>"</span> | tr -d <span class="hljs-string">'"'</span>)
  <span class="hljs-keyword">fi</span>
<span class="hljs-keyword">done</span> &lt; terraform.tfvars

<span class="hljs-comment"># get back to root directory</span>
<span class="hljs-built_in">cd</span> ..

<span class="hljs-comment"># save the ip to a file</span>
<span class="hljs-built_in">echo</span> <span class="hljs-string">"----------------------- Saving IP Addresses -----------------------"</span>

<span class="hljs-comment"># create a file to store the ip address</span>
touch inventory.ini
<span class="hljs-built_in">echo</span> <span class="hljs-variable">$ip</span> &gt; inventory.ini


<span class="hljs-keyword">while</span> <span class="hljs-built_in">read</span> line; 
<span class="hljs-keyword">do</span> 
    <span class="hljs-built_in">echo</span> <span class="hljs-string">"----------------Copying ssh keys to server id -&gt; <span class="hljs-variable">$line</span>----------------"</span>; 
    sshpass -p <span class="hljs-variable">$password</span> ssh-copy-id -i <span class="hljs-variable">$HOME</span>/.ssh/microservicekeys/id_rsa.pub -o PubkeyAuthentication=yes -o PasswordAuthentication=yes -o StrictHostKeyChecking=no root@<span class="hljs-variable">$line</span>
<span class="hljs-keyword">done</span> &lt;  inventory.ini

<span class="hljs-built_in">echo</span> <span class="hljs-string">"----------------------- All done now! -----------------------"</span>
<span class="hljs-built_in">echo</span> <span class="hljs-string">"Copy the following private key to the github repository secrets"</span>
cat <span class="hljs-variable">$HOME</span>/.ssh/microservicekeys/id_rsa

<span class="hljs-built_in">exit</span>

<span class="hljs-built_in">exec</span> bash
</code></pre>
<p>The script first creates a directory for the SSH keys and generates a new set of RSA keys using the <code>ssh-keygen</code> command. It then adjusts the permissions of the generated key files and moves to the root directory.</p>
<p>The script then enters the <code>terraform</code> directory and runs <code>terraform init</code> to initialize Terraform, and runs <code>terraform plan</code> to create a plan for creating the new Linode instance. The script then applies this plan using <code>terraform apply</code>.</p>
<p>After the Linode instance is created, the script retrieves its public IP address using <code>terraform output</code> and stores it in a file called <code>inventory.ini</code>. It then reads the <code>terraform.tfvars</code> file to retrieve the root password for the Linode instance and uses this password to copy the public SSH key to the instance using <code>sshpass</code>.</p>
<p>Finally, the script prints the private key to the terminal and exits.</p>
<p>Run the script while in the Scripts folder using:</p>
<pre><code class="lang-bash">./terraform_setup.sh
</code></pre>
<p>On Linode, you should see the instance created.</p>
<h2 id="heading-automating-deploys-with-ansible">Automating deploys with Ansible</h2>
<p>At this point, we can use the local machine (your laptop) as an ansible controller to ssh into the VM, install dependencies, and run docker-compose.</p>
<p>In the project folder create a <code>playbook.yml</code> , <code>ansible.cfg</code> and a <code>run_ansible.sh</code> file in the Scripts folder.</p>
<pre><code class="lang-bash">Project_Folder
    └─── microservices
    └─── terraform 
    └─── Scripts 
        | terraform_setup.sh
        | run_ansible.sh <span class="hljs-comment"># &lt;---create this</span>
    | inventory.ini
    | ansible.cfg <span class="hljs-comment"># &lt;---create this</span>
    | playbook.yml <span class="hljs-comment"># &lt;---create this</span>
</code></pre>
<p>The <code>ansible.cfg</code> has the following:</p>
<pre><code class="lang-bash">[ssh_connection]
pipelining=True
</code></pre>
<p>This is a configuration block for the <code>ssh_connection</code> plugin in Ansible. The <code>pipelining</code> option is set to <code>True</code>, which means that Ansible will use an optimized command execution flow that allows multiple commands to be sent to the remote host without waiting for the previous command to finish. This can improve the performance of Ansible by reducing the number of network round-trips required to execute a playbook.</p>
<p>The <code>playbook.yml</code> file has the following</p>
<pre><code class="lang-yaml"><span class="hljs-meta">---</span>
<span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Connect</span> <span class="hljs-string">to</span> <span class="hljs-string">linode</span> <span class="hljs-string">and</span> <span class="hljs-string">install</span> <span class="hljs-string">dependencies</span> <span class="hljs-string">and</span> <span class="hljs-string">run</span> <span class="hljs-string">docker-compose</span>
  <span class="hljs-attr">hosts:</span> <span class="hljs-string">all</span>
  <span class="hljs-attr">become:</span> <span class="hljs-literal">true</span>
  <span class="hljs-attr">vars:</span>
    <span class="hljs-attr">source:</span> <span class="hljs-string">"<span class="hljs-template-variable">{{ source }}</span>"</span>

  <span class="hljs-attr">tasks:</span>
    <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Install</span> <span class="hljs-string">required</span> <span class="hljs-string">system</span> <span class="hljs-string">packages</span>
      <span class="hljs-attr">apt:</span>
        <span class="hljs-attr">pkg:</span>
          <span class="hljs-bullet">-</span> <span class="hljs-string">apt-transport-https</span>
          <span class="hljs-bullet">-</span> <span class="hljs-string">ca-certificates</span>
          <span class="hljs-bullet">-</span> <span class="hljs-string">curl</span>
          <span class="hljs-bullet">-</span> <span class="hljs-string">software-properties-common</span>
          <span class="hljs-bullet">-</span> <span class="hljs-string">python3-pip</span>
          <span class="hljs-bullet">-</span> <span class="hljs-string">virtualenv</span>
          <span class="hljs-bullet">-</span> <span class="hljs-string">python3-setuptools</span>

        <span class="hljs-attr">state:</span> <span class="hljs-string">latest</span>
        <span class="hljs-attr">update_cache:</span> <span class="hljs-literal">true</span>

    <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Install</span> <span class="hljs-string">Docker</span>
      <span class="hljs-attr">become:</span> <span class="hljs-literal">true</span>
      <span class="hljs-attr">apt:</span>
        <span class="hljs-attr">name:</span> <span class="hljs-string">docker.io</span>
        <span class="hljs-attr">state:</span> <span class="hljs-string">present</span>
    <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Install</span> <span class="hljs-string">Docker</span> <span class="hljs-string">Compose</span>
      <span class="hljs-attr">pip:</span>
        <span class="hljs-attr">name:</span> <span class="hljs-string">docker-compose</span>
      <span class="hljs-attr">tags:</span>
        <span class="hljs-bullet">-</span> <span class="hljs-string">docker</span>

    <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Install</span> <span class="hljs-string">Python</span>
      <span class="hljs-attr">apt:</span>
        <span class="hljs-attr">name:</span> <span class="hljs-string">python3</span>
        <span class="hljs-attr">state:</span> <span class="hljs-string">present</span>
      <span class="hljs-attr">tags:</span>
        <span class="hljs-bullet">-</span> <span class="hljs-string">python</span>

    <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Synchronize</span> <span class="hljs-string">src</span> <span class="hljs-string">and</span> <span class="hljs-string">dest,</span> <span class="hljs-string">excluding</span> <span class="hljs-string">node_modules</span> <span class="hljs-string">subfolders</span>
      <span class="hljs-attr">ansible.builtin.synchronize:</span>
        <span class="hljs-attr">src:</span> <span class="hljs-string">"<span class="hljs-template-variable">{{ source}}</span>"</span>
        <span class="hljs-attr">dest:</span> <span class="hljs-string">/home</span>
        <span class="hljs-attr">rsync_opts:</span>
          <span class="hljs-bullet">-</span> <span class="hljs-string">"--exclude=**/node_modules"</span>
          <span class="hljs-bullet">-</span> <span class="hljs-string">"--exclude=**/data"</span>

    <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Copy</span> <span class="hljs-string">env</span>
      <span class="hljs-attr">copy:</span>
            <span class="hljs-attr">src:</span>   <span class="hljs-string">"<span class="hljs-template-variable">{{source}}</span>/.env"</span>
            <span class="hljs-attr">dest:</span> <span class="hljs-string">/home/microservices/.env</span>

    <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Run</span> <span class="hljs-string">Docker</span> <span class="hljs-string">Compose</span> <span class="hljs-string">Build</span> <span class="hljs-string">and</span> <span class="hljs-string">run</span> <span class="hljs-string">image</span>
      <span class="hljs-attr">command:</span> <span class="hljs-string">docker-compose</span> <span class="hljs-string">-f</span> <span class="hljs-string">/home/microservices/docker-compose.yml</span> <span class="hljs-string">up</span> <span class="hljs-string">--force-recreate</span> <span class="hljs-string">--build</span> <span class="hljs-string">-d</span>
      <span class="hljs-attr">tags:</span>
        <span class="hljs-bullet">-</span> <span class="hljs-string">docker</span>
</code></pre>
<p>This Ansible playbook connects to the Linode instance and installs some required system packages, Docker, Docker Compose, and Python. It then synchronizes the contents of the microservices directory with a destination on the Linode instance set to <code>/home</code>, excluding the <code>node_modules</code> subfolders. The playbook copies the<code>.env</code> from the <code>microservices/.env</code> to the <code>/home/microservices/.env</code> on the Linode instance. Finally, the playbook runs <code>docker-compose</code> to build and run our Docker images using the <code>docker-compose.yml</code> file.</p>
<p>The playbook is intended to be run on all hosts in the <code>inventory.ini</code> file and uses <code>become</code> to run tasks with root privileges. The <code>source</code> variable is a playbook variable used to specify the local directory that should be synchronized with the destination on the Linode instance.</p>
<p>The <code>run_ansible.sh</code> bash script has the following</p>
<pre><code class="lang-yaml"><span class="hljs-comment">#!/bin/bash</span>
<span class="hljs-comment"># this runs ansible on your laptop</span>
<span class="hljs-comment"># cd to root</span>
<span class="hljs-string">cd</span> <span class="hljs-string">..</span>
<span class="hljs-comment">#  create environment variables from .env in the microservices folder file</span>
<span class="hljs-string">cd</span> <span class="hljs-string">microservices</span>

<span class="hljs-comment"># Read the .env file and export each key-value pair as an environment variable</span>
<span class="hljs-string">while</span> <span class="hljs-string">read</span> <span class="hljs-string">line;</span> <span class="hljs-string">do</span>
  <span class="hljs-string">export</span> <span class="hljs-string">$line</span>
<span class="hljs-string">done</span> <span class="hljs-string">&lt;</span> <span class="hljs-string">.env</span>

<span class="hljs-comment"># cd to root and run ansible</span>
<span class="hljs-string">cd</span> <span class="hljs-string">..</span>

<span class="hljs-string">ansible-playbook</span> <span class="hljs-string">playbook.yml</span> <span class="hljs-string">-u</span> <span class="hljs-string">root</span> <span class="hljs-string">--private-key=$HOME/.ssh/microservicekeys/id_rsa</span> <span class="hljs-string">-i</span> <span class="hljs-string">inventory.ini</span> <span class="hljs-string">-vvv</span> <span class="hljs-string">--extra-vars</span> <span class="hljs-string">"source=microservices"</span>

<span class="hljs-string">echo</span> <span class="hljs-string">"----------------------- All done now! -----------------------"</span>

<span class="hljs-string">exit</span>
<span class="hljs-string">exec</span> <span class="hljs-string">bash</span>
</code></pre>
<p>The script first changes the current working directory to the root directory and then enters the <code>microservices</code> directory. It reads the <code>.env</code> file and exports each key-value pair as an environment variable.</p>
<p>The script then changes the current working directory to the root directory and runs the <code>ansible-playbook</code> command to execute the playbook. The playbook is run with verbose output enabled, and the <code>source</code> variable is passed as an extra variable. The <code>source</code> variable is specified as <code>microservices</code> The <code>ansible-playbook</code> command is run with the <code>-u</code> flag to specify the user to connect as (<code>root</code> in this case) and the <code>--private-key</code> flag to specify the private key for authentication. The <code>-i</code> flag is used to specify the inventory file to use (<code>inventory.ini</code>), and the <code>-vvv</code> flag enables verbose output.</p>
<p>Finally, the script prints a message to the terminal and exits.</p>
<p>Run the script while in the Scripts folder using</p>
<pre><code class="lang-yaml"><span class="hljs-string">./run_ansible.sh</span>
</code></pre>
<p>You should see the live react app if you navigate to your IP address.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1671478120382/04_OaoPTM.png" alt class="image--center mx-auto" /></p>
<h2 id="heading-using-github-actions-as-the-controller-machine">Using Github Actions as the Controller Machine</h2>
<p>Normally, you would push code to a VCS, and we want to leverage Github actions to automatically update code on the server on push. For this, we need to create a Github actions workflow.</p>
<pre><code class="lang-yaml"><span class="hljs-string">Project_Folder</span>
    <span class="hljs-string">└───</span> <span class="hljs-string">microservices</span>
    <span class="hljs-string">└───</span> <span class="hljs-string">terraform</span> 
    <span class="hljs-string">└───</span> <span class="hljs-string">Scripts</span> 
    <span class="hljs-string">└───</span> <span class="hljs-string">.github</span> <span class="hljs-comment"># &lt;---create this</span>
         <span class="hljs-string">└───</span> <span class="hljs-string">workflows</span> <span class="hljs-comment"># &lt;---create this</span>
             <span class="hljs-string">|</span> <span class="hljs-string">actions.yml</span> <span class="hljs-comment"># &lt;---create this</span>
    <span class="hljs-string">|</span> <span class="hljs-string">inventory.ini</span>
    <span class="hljs-string">|</span> <span class="hljs-string">ansible.cfg</span> 
    <span class="hljs-string">|</span> <span class="hljs-string">playbook.yml</span>
</code></pre>
<p>The <code>actions.yml</code> has the following:</p>
<pre><code class="lang-yaml"><span class="hljs-attr">name:</span> <span class="hljs-string">Building</span> <span class="hljs-string">microservices</span> <span class="hljs-string">with</span> <span class="hljs-string">Terraform,</span> <span class="hljs-string">Ansible,</span> <span class="hljs-string">docker,</span> <span class="hljs-string">docker-compose,</span> <span class="hljs-string">and</span> <span class="hljs-string">Github</span> <span class="hljs-string">Actions</span>
<span class="hljs-attr">on:</span>
  <span class="hljs-attr">push:</span>
      <span class="hljs-attr">branches:</span>
        <span class="hljs-bullet">-</span> <span class="hljs-string">"main"</span>
<span class="hljs-attr">jobs:</span>
  <span class="hljs-attr">provision:</span>
    <span class="hljs-attr">name:</span> <span class="hljs-string">Run</span> <span class="hljs-string">Ansible</span>
    <span class="hljs-attr">runs-on:</span> <span class="hljs-string">ubuntu-latest</span>
    <span class="hljs-attr">steps:</span>      
    <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Checkout</span> <span class="hljs-string">repository</span>
      <span class="hljs-attr">uses:</span> <span class="hljs-string">actions/checkout@v3</span>
    <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Set</span> <span class="hljs-string">up</span> <span class="hljs-string">Python</span>
      <span class="hljs-attr">uses:</span> <span class="hljs-string">actions/setup-python@v2</span>
    <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Install</span> <span class="hljs-string">dependencies</span>
      <span class="hljs-attr">run:</span> <span class="hljs-string">|
        python -m pip install ansible
</span>    <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Add</span> <span class="hljs-string">hosts</span> <span class="hljs-string">to</span> <span class="hljs-string">known_hosts</span>
      <span class="hljs-attr">env:</span>
        <span class="hljs-attr">SSH_AUTH_SOCK:</span> <span class="hljs-string">/tmp/ssh_agent.sock</span>

      <span class="hljs-attr">run:</span> <span class="hljs-string">|
        mkdir -p ~/.ssh
        touch ~/.ssh/known_hosts
        ssh-keyscan -f inventory.ini &gt; known_hosts
        cat known_hosts &gt;&gt; ~/.ssh/known_hosts
        ssh-agent -a $SSH_AUTH_SOCK &gt; /dev/null
        ssh-add - &lt;&lt;&lt; "${{ secrets.SSH_PRIVATE_KEY }}"
</span>

    <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Run</span> <span class="hljs-string">ansible</span> <span class="hljs-string">playbook</span>
      <span class="hljs-attr">env:</span>
          <span class="hljs-attr">SSH_AUTH_SOCK:</span> <span class="hljs-string">/tmp/ssh_agent.sock</span>              
      <span class="hljs-attr">run:</span> <span class="hljs-string">|
           cd microservices 
           echo MONGODB_CONNSTRING="${{secrets.MONGODB_CONNSTRING}}" &gt;&gt; .env
           echo MONGO_INITDB_DATABASE="${{secrets.MONGO_INITDB_DATABASE}}" &gt;&gt; .env
           echo MONGO_INITDB_ROOT_PASSWORD="${{secrets.MONGO_INITDB_ROOT_PASSWORD}}" &gt;&gt; .env
           echo MONGO_INITDB_ROOT_USERNAME="${{secrets.MONGO_INITDB_ROOT_USERNAME}}" &gt;&gt; .env
           cd .. 
           ansible-playbook playbook.yml -u root --private-key="${{ secrets.SSH_PRIVATE_KEY }}" -i inventory.ini -vvv  --extra-vars "source=microservices"</span>
</code></pre>
<p>The workflow is triggered by a push to the <code>main</code> branch and runs on an Ubuntu machine.</p>
<p>The workflow consists of several steps that perform various tasks. The first step checks out the repository, and the second step sets up Python. The third step installs the dependencies required to run ansible, including the <code>ansible</code> package. The fourth step adds the hosts specified in the <code>inventory.ini</code> file to the <code>known_hosts</code> file, which is used to store the public keys of known hosts. This step also sets up an SSH agent to manage SSH keys and adds the private key stored in the <code>SSH_PRIVATE_KEY</code> secret to the agent.</p>
<p>The final step runs the ansible playbook, passing the private key stored in the <code>SSH_PRIVATE_KEY</code> secret as an argument to the <code>--private-key</code> flag. This step also reads the secrets stored in the repository and appends them to the <code>.env</code> file in the <code>microservices</code> directory. Finally, the <code>ansible-playbook</code> command is run to execute the playbook.</p>
<p>Add secrets in your .env to github secrets. Go to the setting tab on the Github repo</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1671478473721/m0pJTxhVW.png" alt class="image--center mx-auto" /></p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1671478519957/f0EqYjekE.png" alt class="image--center mx-auto" /></p>
<p>The <code>SSH_PRIVATE_KEY</code> is the private key generated by the <code>terraform_setup.sh</code> script. You can view it by running the following command:</p>
<pre><code class="lang-yaml">
<span class="hljs-string">cat</span> <span class="hljs-string">$HOME/.ssh/microservicekeys/id_rsa</span>
</code></pre>
<p>Once you push changes to github, you should have the code auto-deploy on the server</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1671478698630/f2mgGBtaK.png" alt class="image--center mx-auto" /></p>
<p>Resources:</p>
<p><a target="_blank" href="https://github.com/wamaithaNyamu/building-microservices-with-terraform-ansible-docker-dockercompose-and-github-actions/actions">Code used in this blog</a></p>
<p><a target="_blank" href="https://www.terraform.io/use-cases/infrastructure-as-code?product_intent=terraform">https://www.terraform.io/use-cases/infrastructure-as-code?product_intent=terraform</a></p>
<p><a target="_blank" href="https://docs.github.com/en/actions">https://docs.github.com/en/actions</a></p>
]]></content:encoded></item><item><title><![CDATA[How to publish an npm package.]]></title><description><![CDATA[This guide shows how to publish a node js package on npm. The package takes in a sheng word and returns a 'shembetenged' version. Sheng is a slang derived from Swahili. Shembeteng is derived from sheng.
For example:




EnglishShengShembeteng



Mine...]]></description><link>https://wamaithanyamu.com/how-to-publish-an-npm-package</link><guid isPermaLink="true">https://wamaithanyamu.com/how-to-publish-an-npm-package</guid><category><![CDATA[Node.js]]></category><category><![CDATA[npm publish]]></category><category><![CDATA[JavaScript]]></category><category><![CDATA[Productivity]]></category><category><![CDATA[npm]]></category><dc:creator><![CDATA[Wamaitha ]]></dc:creator><pubDate>Mon, 05 Dec 2022 23:26:24 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1672517968068/c5a334a8-794d-4957-8666-c71efdfa60d3.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>This guide shows how to publish a node js package on npm. The package takes in a sheng word and returns a 'shembetenged' version. Sheng is a slang derived from Swahili. Shembeteng is derived from sheng.</p>
<p>For example:</p>
<div class="hn-table">
<table>
<thead>
<tr>
<td>English</td><td>Sheng</td><td>Shembeteng</td></tr>
</thead>
<tbody>
<tr>
<td>Mine</td><td>Wangu</td><td>Wambatangu</td></tr>
<tr>
<td>Yours</td><td>Yako</td><td>Yambatako</td></tr>
<tr>
<td>A little</td><td>Kiasi</td><td>Kiambatasi</td></tr>
<tr>
<td>English</td><td>Kizungu</td><td>Kizumbutungu</td></tr>
<tr>
<td>All of it</td><td>Zote</td><td>Zombotote</td></tr>
<tr>
<td>Drip</td><td>Luku</td><td>Lumbutuku</td></tr>
<tr>
<td>I love you</td><td>Nakulove</td><td>Nakulombotove</td></tr>
<tr>
<td>I hate you</td><td>Nakuhate</td><td>Nakuhambatate</td></tr>
</tbody>
</table>
</div><h1 id="heading-introduction">Introduction</h1>
<p>Npm registry is the largest Javascript Software library. Developers publish packages on the registry as tools. For example, if you are a react developer and need to code an image upload component, instead of coding the component from scratch, you can use a published component in the registry.</p>
<p>There are several advantages of open source:</p>
<ol>
<li><p>A package that many people have used has been battle tested on most environments. For example, the component's contributors have ensured the image component works on most browsers.</p>
</li>
<li><p>The lead time to production reduces significantly. The time that would have been spent implementing and testing the component is allocated to other aspects of the software development process.</p>
</li>
<li><p>There are multiple solutions for the same problem. If one package doesnt meet the requirements, you can always try out others.</p>
</li>
<li><p>The code is open source; hence, you can always use the package and build on it to meet your requirements.</p>
</li>
<li><p>If you publish your package, as more people use it, you can improve the project from feedback from the community.</p>
</li>
<li><p>The community can contribute to the codebase by reporting bugs, solving bugs, and adding new features. Through this, critical vulnerabilities are easy to identify as more use the project.</p>
</li>
<li><p>More recently, companies have been supporting open-source contributors. Sponsorships make it possible to become an open-source contributor and earn from it.</p>
</li>
</ol>
<h1 id="heading-prerequisites">Prerequisites</h1>
<ul>
<li><p>Npm account. Create one from the <a target="_blank" href="https://www.npmjs.com/signup">npm website</a></p>
</li>
<li><p>NodeJS and npm installed on your computer. Follow NodeJS installation instructions from the <a target="_blank" href="https://nodejs.org/en/download/">official documentation</a> for your operating system. This guide uses the following versions</p>
</li>
</ul>
<pre><code class="lang-sh">node v18.12.1
npm 8.19.2
</code></pre>
<h1 id="heading-sheng-to-shembeteng">Sheng to shembeteng</h1>
<p>The current project folder looks as follows:</p>
<pre><code class="lang-plaintext">Project_Folder
  |__ index.js
</code></pre>
<p>The sheng to shembeteng logic relies on a regular expression. Going over the pseudo-code:</p>
<ul>
<li>Given the sentence :</li>
</ul>
<pre><code class="lang-sh"> Luku yako. <span class="hljs-comment"># Your drip, your style</span>
</code></pre>
<ul>
<li>Split the sentence into an array of words</li>
</ul>
<pre><code class="lang-sh">[<span class="hljs-string">'Luku'</span>,<span class="hljs-string">'yako'</span>]
</code></pre>
<ul>
<li>Loop over the words. On each word, run a regular expression that splits the word into an array with two parts. The first part holds the constants before the first vowel, while the second part holds the rest of the word from the first vowel.</li>
</ul>
<pre><code class="lang-sh">Luku ---&gt; [<span class="hljs-string">'L'</span>,<span class="hljs-string">'uku'</span>]

Yako ---&gt; [<span class="hljs-string">'Y'</span>,<span class="hljs-string">'ako'</span>]
</code></pre>
<ul>
<li>On the second part of the word, extract the vowel and match the corresponding sheng injection from the following dictionary.</li>
</ul>
<pre><code class="lang-sh">{
    <span class="hljs-string">'a'</span>:<span class="hljs-string">'mbata'</span>,
    <span class="hljs-string">'e'</span>:<span class="hljs-string">'mbete'</span>,
    <span class="hljs-string">'i'</span>:<span class="hljs-string">'mbiti'</span>,
    <span class="hljs-string">'o'</span>:<span class="hljs-string">'mboto'</span>,
    <span class="hljs-string">'u'</span>:<span class="hljs-string">'mbutu'</span>,
}
</code></pre>
<p>The full code for this implementation:</p>
<pre><code class="lang-js"><span class="hljs-keyword">const</span> shembetengInjections = {
    <span class="hljs-string">'a'</span>:<span class="hljs-string">'mbata'</span>,
    <span class="hljs-string">'e'</span>:<span class="hljs-string">'mbete'</span>,
    <span class="hljs-string">'i'</span>:<span class="hljs-string">'mbiti'</span>,
    <span class="hljs-string">'o'</span>:<span class="hljs-string">'mboto'</span>,
    <span class="hljs-string">'u'</span>:<span class="hljs-string">'mbutu'</span>,
}


<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> shembeteng = <span class="hljs-function">(<span class="hljs-params">sentence</span>) =&gt;</span> {
    <span class="hljs-comment">// split sentence into array of words</span>
    <span class="hljs-keyword">const</span> words = sentence.split(<span class="hljs-string">' '</span>);

    <span class="hljs-comment">// loop through each word</span>
    <span class="hljs-keyword">const</span> shembetengedSentence = words.map(<span class="hljs-function"><span class="hljs-params">word</span> =&gt;</span> {
        <span class="hljs-keyword">let</span> shembetengPart = <span class="hljs-string">''</span>;
        <span class="hljs-comment">// split each word into array of letters</span>
        <span class="hljs-keyword">const</span> wordSplitOnFirstVowel = word.split(<span class="hljs-regexp">/([aeiou].*)/</span>)

        <span class="hljs-keyword">if</span>(wordSplitOnFirstVowel.length &gt; <span class="hljs-number">1</span>){
            <span class="hljs-comment">// the word has a vowel</span>

            <span class="hljs-comment">// get the specific vowel</span>
            <span class="hljs-keyword">const</span> vowel = wordSplitOnFirstVowel[<span class="hljs-number">1</span>][<span class="hljs-number">0</span>];

            <span class="hljs-comment">// get the shembeteng injection</span>
            <span class="hljs-keyword">const</span> injection = shembetengInjections[vowel];
            <span class="hljs-comment">// get the rest of the word</span>
            <span class="hljs-keyword">const</span> restOfWord = wordSplitOnFirstVowel[<span class="hljs-number">1</span>].slice(<span class="hljs-number">1</span>);
            <span class="hljs-comment">// return the word with the injection</span>
            shembetengPart = vowel + injection + restOfWord;
            <span class="hljs-keyword">return</span>  wordSplitOnFirstVowel[<span class="hljs-number">0</span>] + shembetengPart;
        }

        <span class="hljs-comment">// the word has no vowel</span>
        <span class="hljs-keyword">return</span> word;

    })

    <span class="hljs-keyword">return</span> shembetengedSentence.join(<span class="hljs-string">' '</span>)
}
</code></pre>
<p>The injections do not always happen on the first vowel occurrence. This would make a good case for neural machine translation if we had a dataset to train a model. Since translation is not the goal of this article, we will leave the simple implementation as is.</p>
<h1 id="heading-publishing-to-npm">Publishing to npm</h1>
<p>On your terminal, initialize npm using npm init and follow the prompts. Make sure the entry point is the same name as the file containing the logic. In this case index.js</p>
<pre><code class="lang-sh">npm init
</code></pre>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1670280542888/jciV5xELi.png" alt="image.png" /></p>
<p>This will create a package.json file. Add the "type": "module" to the package.json above the scripts. Adding the "type": "module" means our package user will need to use the import syntax.</p>
<p>The final package.json is as follows:</p>
<pre><code class="lang-sh">{
  <span class="hljs-string">"name"</span>: <span class="hljs-string">"shembeteng"</span>,
  <span class="hljs-string">"version"</span>: <span class="hljs-string">"1.0.0"</span>,
  <span class="hljs-string">"description"</span>: <span class="hljs-string">"Sheng to shembeteng"</span>,
  <span class="hljs-string">"main"</span>: <span class="hljs-string">"index.js"</span>,
  <span class="hljs-string">"type"</span>: <span class="hljs-string">"module"</span>,
  <span class="hljs-string">"scripts"</span>: {
    <span class="hljs-string">"test"</span>: <span class="hljs-string">"echo \"Error: no test specified\" &amp;&amp; exit 1"</span>
  },
  <span class="hljs-string">"repository"</span>: {
    <span class="hljs-string">"type"</span>: <span class="hljs-string">"git"</span>,
    <span class="hljs-string">"url"</span>: <span class="hljs-string">"git+https://github.com/wamaithaNyamu/shembeteng.git"</span>
  },
  <span class="hljs-string">"keywords"</span>: [
    <span class="hljs-string">"Sheng"</span>,
    <span class="hljs-string">"shembeteng"</span>
  ],
  <span class="hljs-string">"author"</span>: <span class="hljs-string">"Wamaitha Nyamu"</span>,
  <span class="hljs-string">"license"</span>: <span class="hljs-string">"MIT"</span>,
  <span class="hljs-string">"bugs"</span>: {
    <span class="hljs-string">"url"</span>: <span class="hljs-string">"https://github.com/wamaithaNyamu/shembeteng/issues"</span>
  },
  <span class="hljs-string">"homepage"</span>: <span class="hljs-string">"https://github.com/wamaithaNyamu/shembeteng#readme"</span>
}
</code></pre>
<p>On your terminal, login to npm using npm adduser and follow the prompts.</p>
<pre><code class="lang-sh">npm adduser
</code></pre>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1670280367584/tPNTzlI11.png" alt="image.png" /></p>
<p>Publish the package using npm publish</p>
<pre><code class="lang-sh">npm publish
</code></pre>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1670281094802/jch2IjWDc.png" alt="image.png" /></p>
<p>The package is now live and ready to be used by other developers.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1670281170895/XpUwj80Tb.png" alt="image.png" /></p>
<h1 id="heading-testing">Testing</h1>
<p>To test the package. Create a new folder outside the project folder. In the new folder, run npm install shembeteng</p>
<pre><code class="lang-sh"> npm install shembeteng
</code></pre>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1670281306409/RWx2PqXqE.png" alt="image.png" /></p>
<p>This installs shemebteng from the registry and creates a package.json. Be sure to add the "type":"module" in the test folder.</p>
<pre><code class="lang-sh">{
  <span class="hljs-string">"type"</span>: <span class="hljs-string">"module"</span>,
  <span class="hljs-string">"dependencies"</span>: {
    <span class="hljs-string">"shembeteng"</span>: <span class="hljs-string">"^1.0.0"</span>
  }
}
</code></pre>
<p>Create a file to test out the project. In the file, import shemebteng and test it out.</p>
<pre><code class="lang-js"><span class="hljs-keyword">import</span> { shembeteng } <span class="hljs-keyword">from</span> <span class="hljs-string">"shembeteng"</span>;

<span class="hljs-keyword">const</span> sentence = <span class="hljs-string">"Luku Yako"</span>;

<span class="hljs-keyword">const</span> shemebetenged = shembeteng(sentence);

<span class="hljs-built_in">console</span>.log(shemebetenged);
</code></pre>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1670281657678/tFpLtqg3d.png" alt="image.png" /></p>
<h1 id="heading-updating-the-npm-package">Updating the npm package</h1>
<p>Our package is live, but we need to add a README on how to use the package and push the changes to npm. This would be the same process you would follow if you needed to update the code or release a new version. After changes have been made, change the version number on the package.json to 1.0.1. Then run npm publish to push the new changes to npm.</p>
<pre><code class="lang-sh">npm publish
</code></pre>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1670282484930/8WoMZ-K9f.png" alt="image.png" />
<img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1670282638327/6dNKSjri5.png" alt="image.png" /></p>
<p>Resources:</p>
<p><a target="_blank" href="https://github.com/wamaithaNyamu/shembeteng">Code used for the article</a></p>
<p><a target="_blank" href="https://www.npmjs.com/package/shembeteng">Published npm package</a></p>
<p><a target="_blank" href="https://docs.npmjs.com/about-npm">https://docs.npmjs.com/about-npm</a></p>
]]></content:encoded></item><item><title><![CDATA[How to Set Permission-based access on Linux]]></title><description><![CDATA[In the previous article, we created a new user and enabled ssh key access to the user. This guide covers how to restrict access using permissions. 
Login as root
To log in as root, retrieve the root password from Vultr on the specific instance. 

On ...]]></description><link>https://wamaithanyamu.com/how-to-set-permission-based-access-on-linux</link><guid isPermaLink="true">https://wamaithanyamu.com/how-to-set-permission-based-access-on-linux</guid><category><![CDATA[Linux]]></category><category><![CDATA[permissions]]></category><category><![CDATA[access control]]></category><category><![CDATA[Security]]></category><category><![CDATA[chmod]]></category><dc:creator><![CDATA[Wamaitha ]]></dc:creator><pubDate>Thu, 01 Dec 2022 14:46:32 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1672513212682/d764cee9-858a-45e5-9c08-5ebf0272bac4.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>In the <a target="_blank" href="https://wamaithanyamu.com/ssh-key-acces-as-a-non-root-user">previous article</a>, we created a new user and enabled ssh key access to the user. This guide covers how to restrict access using permissions. </p>
<h2 id="heading-login-as-root">Login as root</h2>
<p>To log in as root, retrieve the root password from Vultr on the specific instance. </p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1669903327949/-HbDg8SVJ.png" alt="image.png" /></p>
<p>On your terminal, log in as root using. </p>
<pre><code class="lang-sh">ssh root@ip_address
</code></pre>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1669903471309/52MgAWijP.png" alt="image.png" /></p>
<p>To demonstrate how permissions work on Linux, we create a testfolder in the new user's directory while logged in as root, then change permissions for the user in that folder.</p>
<h2 id="heading-test-folder">Test folder</h2>
<p>Navigate to the new user home directory</p>
<pre><code class="lang-sh"><span class="hljs-built_in">cd</span> /home/wamaitha
</code></pre>
<p>Create the testfolder</p>
<pre><code class="lang-sh">mkdir testfolder
</code></pre>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1669903797300/fjZ056K3o.png" alt="image.png" /></p>
<p>Navigate to the testfolder, create a text file hello.txt, and populate the txt file with "Hello from root".</p>
<pre><code class="lang-sh"><span class="hljs-comment"># cd into folder</span>
<span class="hljs-built_in">cd</span> testfolder

<span class="hljs-comment"># create file and populate with echo</span>
<span class="hljs-built_in">echo</span> <span class="hljs-string">"Hello from root!"</span> &gt; hello.txt

<span class="hljs-comment"># view file content of the hello.txt using cat</span>
cat hello.txt
</code></pre>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1669904079192/JXZMwUzue.png" alt="image.png" /></p>
<p>Check folder permissions as shown below</p>
<pre><code class="lang-sh">ls -l
</code></pre>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1669904249001/LhKIfXc3i.png" alt="image.png" /></p>
<p>The permissions are sectioned into 4; file type, user permissions, group permissions, and other user permissions. The permissions are a combination of read, write and execute. No permissions are denoted by a dash(-),</p>
<pre><code class="lang-sh">[file <span class="hljs-built_in">type</span>][user][group][other users]
</code></pre>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1669904686353/7MlVUJYfH.png" alt="image.png" /></p>
<p>Root's permissions on the testfolder are</p>
<pre><code class="lang-sh">drwxr-xr-x
</code></pre>
<ul>
<li>d - directory</li>
<li>rwx - root has read , write and execute permissions</li>
<li>r-x - the group associated with root has read and execute permissions only</li>
<li>r-x - other users on the directory have read and execute permission only.</li>
</ul>
<p>We want to add the user wamaitha to this directory and give them read and write permissions. By default, a new user is created and added to a group of the same name. Check this using the group's command</p>
<pre><code class="lang-sh">groups wamaitha
</code></pre>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1669904778482/f18EoHD7z.png" alt="image.png" /></p>
<h2 id="heading-permissions">Permissions</h2>
<p>Permissions on Linux are given using the chmod command followed by the permission number. </p>
<p>Permission numbers are:</p>
<ul>
<li><p>0 = --- (no permissions at all)</p>
</li>
<li><p>1 = --x (execute permission only)</p>
</li>
<li><p>2 = -w- (write permission only)</p>
</li>
<li><p>3 = -wx (write and execute permission only)</p>
</li>
<li><p>4 = r- (read permission only)</p>
</li>
<li><p>5 = r-x (read and execute only)</p>
</li>
<li><p>6 = rw- (read and write permission only)</p>
</li>
<li><p>7 = rwx (read,write and execute permissions)</p>
</li>
</ul>
<p>With this, we can combine these numbers to change permissions. Chmod accepts three numbers. The first number represents the user's permission. The second represents the group permissions, and the third represents permission for all other users. </p>
<pre><code class="lang-sh">chmod 744  testfolder
</code></pre>
<p>For succinctness, the  command above will yield:</p>
<ul>
<li>read, write and execute permissions to the current user logged in (root) on the testfolder.</li>
<li>read permissions to the group (all groups that are added to the folder).</li>
<li>read permissions to all other users accessing the testfolder (other users like wamaitha).</li>
</ul>
<p>Execute the command and check the permissions again.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1669905288766/ptNSG_n7p.png" alt="image.png" /></p>
<p>Switch to the new user. If prompted for a password, use the password you used when creating the new user. The new user only has read permissions on the test folder. Thus any execution commands will be denied due to insufficient permissions.</p>
<pre><code class="lang-sh">su wamaitha
</code></pre>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1669905606478/wxXEzi9XW.png" alt="image.png" /></p>
<p>Resources</p>
<p><a target="_blank" href="https://www.linuxfoundation.org/blog/blog/classic-sysadmin-understanding-linux-file-permissions">Classic SysAdmin: Understanding Linux File Permissions</a></p>
]]></content:encoded></item><item><title><![CDATA[SSH-Key access as a non-root user.]]></title><description><![CDATA[The previous article covered how to log in to a Vultr instance using ssh keys as root. It is recommended to disable root access in the event of an attack. Root privileges mean the user has access to all aspects of the system. Normally, ssh keys are u...]]></description><link>https://wamaithanyamu.com/ssh-key-access-as-a-non-root-user</link><guid isPermaLink="true">https://wamaithanyamu.com/ssh-key-access-as-a-non-root-user</guid><category><![CDATA[ssh]]></category><category><![CDATA[Linux]]></category><category><![CDATA[linux-commands]]></category><category><![CDATA[Security]]></category><category><![CDATA[Key-based authentication]]></category><dc:creator><![CDATA[Wamaitha ]]></dc:creator><pubDate>Thu, 01 Dec 2022 12:04:09 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1672513560404/6536ac03-3b19-438b-964b-af2332a6c47d.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p><a target="_blank" href="https://wamaithanyamu.com/secure-server-login-using-ssh-keys">The previous article</a> covered how to log in to a Vultr instance using ssh keys as root. It is recommended to disable root access in the event of an attack. Root privileges mean the user has access to all aspects of the system. Normally, ssh keys are used using automation tools such as Ansible to perform automated tasks such as pulling code from github on the server and restarting the server. For such a task, Ansible having root access would be a point of vulnerability should attackers get hold of the Ansible private ssh keys. Therefore, creating a non-root user with limited permissions is advised. </p>
<p>Login to the Vultr instance or whichever ssh server you are using. This guide assumes the ssh server is an Ubuntu 22.04 LTS x64 instance.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1669885190195/pCPlyItxH.png" alt="image.png" /></p>
<h2 id="heading-install-tmux">Install tmux</h2>
<p>tmux is an open-source terminal multiplexer for Unix-based systems. tmux is used for simultaneously running multiple terminal sessions. For example, you may need one terminal running the frontend server and another terminal running a backend server on a full-stack application. </p>
<p>Install tmux using:</p>
<pre><code class="lang-sh">sudo apt-get install tmux
</code></pre>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1669884904162/n-aHpW6Kt.png" alt="image.png" /></p>
<p>Start a tmux session using the tmux command.</p>
<pre><code class="lang-sh">tmux
</code></pre>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1669887416952/ylJ2cNvTz.png" alt="image.png" />
You can exit the tmux session using the exit command.</p>
<pre><code class="lang-sh"><span class="hljs-built_in">exit</span>
</code></pre>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1669887446268/DAu4esztG.png" alt="image.png" /></p>
<p>To get back to the tmux session, list all available tmux sessions using tmux ls.</p>
<pre><code class="lang-sh">tmux ls
</code></pre>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1669887293274/4XtzvVz4R.png" alt="image.png" /></p>
<p>Re-launch the session using</p>
<pre><code class="lang-sh">tmux attach-session -t 0
</code></pre>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1669887513479/8jRVUxGrk.png" alt="image.png" /></p>
<h2 id="heading-create-a-new-user">Create a new user</h2>
<p>Create a new user using the adduser command followed by the name you want to assign to the new user.</p>
<pre><code class="lang-sh">adduser wamaitha
</code></pre>
<p>Create a password for the user and fill in the rest of the details on the prompt.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1669885769874/gJeWUlYYQ.png" alt="image.png" /></p>
<p>Verify the user was created using awk. Awk is a programming language used on bash as a scripting tool for text processing. Awk can be used for pattern matching. Users are stored on the /etc/passwd file. The command below returns the names of the users on the system as the /etc/passwd file is quite verbose.</p>
<pre><code class="lang-sh">awk -F<span class="hljs-string">':'</span> <span class="hljs-string">'{ print $1}'</span> /etc/passwd
</code></pre>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1669888040270/vvPAMa6OL.png" alt="image.png" /></p>
<h2 id="heading-move-ssh-keys">Move SSH keys</h2>
<p>Create a .ssh directory on the new user's home directory</p>
<pre><code class="lang-sh">mkdir /home/wamaitha/.ssh
</code></pre>
<p>Move the ssh keys to the new folder</p>
<pre><code class="lang-sh">mv /root/.ssh/authorized_keys /home/wamaitha/.ssh
</code></pre>
<p>Change ownership of the .ssh directory from root to the new user.</p>
<pre><code class="lang-sh">chown -R wamaitha:wamaitha /home/wamaitha/.ssh
</code></pre>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1669895687704/8l95fkem2.png" alt="image.png" /></p>
<p>By default, new users are added to a group of the same name. Log out from tmux using exit, then log out from Vultr using exit.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1669896012610/28RlpqNhy.png" alt="image.png" /></p>
<p>Login as the new user using ssh with the username as follows</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1669896096091/ldysBZ7jy.png" alt="image.png" /></p>
<p>Use the same passphrase used while generating the ssh keys. </p>
]]></content:encoded></item><item><title><![CDATA[How to login securely to a Linux server using SSH keys]]></title><description><![CDATA[The Secure Shell(SSH) is a networking protocol that enables computers to communicate and exchange data. SSH keys enable secure remote login without a password through public and private keys. The private key is kept on the client machine, while the p...]]></description><link>https://wamaithanyamu.com/how-to-login-securely-to-a-linux-server-using-ssh-keys</link><guid isPermaLink="true">https://wamaithanyamu.com/how-to-login-securely-to-a-linux-server-using-ssh-keys</guid><category><![CDATA[Developer]]></category><category><![CDATA[Devops articles]]></category><category><![CDATA[DevOps Journey]]></category><category><![CDATA[ssh]]></category><category><![CDATA[ssh-keys]]></category><dc:creator><![CDATA[Wamaitha ]]></dc:creator><pubDate>Thu, 01 Dec 2022 07:58:24 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1672513608272/8c229b5b-d28b-4854-ac25-52b6c483dba1.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>The Secure Shell(SSH) is a networking protocol that enables computers to communicate and exchange data. SSH keys enable secure remote login without a password through public and private keys. The private key is kept on the client machine, while the public key is stored on the server machine. Once the ssh client verifies the identity of the ssh server, a secure connection is established. The SSH protocol ensures the privacy and integrity of data exchanged between the two machines using symmetric encryption and hashing algorithms. </p>
<p> This guide uses Ubuntu 22.04.1 LTS(my laptop as the client machine)  and a Vultr Ubuntu 22.04 LTS (server machine) to demonstrate the use of the SSH protocol. A summary of what to expect is as shown on the diagram below.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1669837332619/VThLmjfEh.png" alt="ssh.drawio (4).png" /></p>
<h2 id="heading-generating-ssh-keys-with-openssh">Generating SSH Keys with OpenSSH</h2>
<p>OpenSSH ships on  Ubuntu 22.04.1 LTS by default. Launch a terminal using Ctrl+Alt+t  and follow the following steps:</p>
<p>Create a folder that will store the public and private keys. Create the folder <em>vultrKeyFolder</em> on the root folder using the command</p>
<pre><code class="lang-sh">mkdir ~/.ssh/vultrkeysfolder
</code></pre>
<p> Check if the folder was created successfully.</p>
<pre><code class="lang-sh">ls ~/.ssh/
</code></pre>
<p>  <img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1669817802504/cG3U6SedJ.png" alt="one.png" /></p>
<p>Generate the keys using ssh-keygen. The -t flag specifies the type of key to generate. Options for ssh protocol 2 are dsa, ecdsa, ed25519, rsa, and rsa1. The -C flag stands for comment. Using the ed25519 generate the key using the command below</p>
<pre><code class="lang-sh">ssh-keygen -t ed25519 -C <span class="hljs-string">"Key for the vultr ssh demo on my hashnode blog"</span>
</code></pre>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1669835360914/3Fcp5NLp-.png" alt="two.png" /></p>
<p>The output prompts a location to save the key. Save the key on the folder we created on step 1 and give the keys a name; vultrkey for my case.</p>
<pre><code class="lang-sh">/home/user/.ssh/vultrkeysfolder/vultrkey
</code></pre>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1669835754197/ecgiTYisE.png" alt="three.png" /></p>
<p>The next step asks for a passphrase. A passphrase protects the private key from someone who doesn't know the passphrase. If an attacker were to get hold of the key, they would not be able to use it without the passphrase. Use an easy-to-remember passphrase. If you do not wish to have a passphrase, press enter. A random art based on the key will be generated on success.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1669836033430/UK-4Hz46o.png" alt="image.png" /></p>
<p>Two files will be generated. The file with the .pub extension is the public key, while the other is the private key. Your private key should always be kept a secret. The .pub key will be used on the server. You can check for the files generated using</p>
<pre><code class="lang-sh">ls ~/.ssh/vultrkeysfolder
</code></pre>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1669836161689/zYPLboSN7.png" alt="image.png" /></p>
<p>Head over to <a target="_blank" href="https://vultr.com">Vultr</a> and create an account. Get <a target="_blank" href="https://www.vultr.com/?ref=9301631-8H">$100 Free to follow along</a>.  Click on  <em>Products</em>  on the left panel. Click on <em>Instances</em>, then click on <em>Deploy Server</em>.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1669838074510/ATYUb_bDv.png" alt="image.png" /></p>
<p>Choose the <em>Cloud Compute shared vCPU</em>  and the <em>AMD High Performance </em>option for the <em>CPU &amp; Storage Technology</em>. </p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1669879697033/R0PTuKRab.png" alt="image.png" /></p>
<p>Choose a location that is closest to you. For the <em>Server Image</em>, choose  <em>Ubuntu 22.04 LTS x64</em></p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1669838799298/n7D-Krges.png" alt="image.png" /></p>
<p>Select the least server size possible; however, you are free to choose whichever configurations that work for you. </p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1669880283296/nV7Li-0Yp.png" alt="image.png" /></p>
<p>Scrolling down, there is an <em>SSH Keys</em> section. Click on the <em>Add New</em> button.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1669838967554/fAE_mIh4T.png" alt="image.png" /></p>
<p>This opens a new window as shown below</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1669839066875/48nARYjoC.png" alt="image.png" /></p>
<p>Going back to the terminal and open the file containing the public key using cat .</p>
<pre><code class="lang-sh">cat ~/.ssh/vultrkeysfolder/vultrkey.pub
</code></pre>
<p>Copy the public key.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1669839589566/qci_1EfEl.png" alt="image.png" />
Paste the key on Vultr and click on <em>Add SSH Key</em>.
<img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1669839630112/3oL5oeAmL.png" alt="image.png" /></p>
<p>The SSH Keys section will show an added key on the dashboard.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1669839741239/45yxkb4SQ.png" alt="image.png" /></p>
<p>Click on the <em>Deploy Now </em>button to deploy the instance.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1669839815174/lAEJTo1E6.png" alt="image.png" /></p>
<p>Once the instance deploys, the status will change to Running. You may need to give the instance around 10 minutes to boot fully. </p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1669880351795/tUcQbsDEb.png" alt="image.png" /></p>
<p>Click on the instance and copy the IP Address.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1669880425590/HakZ-EyZj.png" alt="image.png" /></p>
<p>On your terminal connect to the server using the command</p>
<pre><code class="lang-sh">ssh -i ~/.ssh/vultrkeysfolder/vultrkey root@202.182.111.47
</code></pre>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1669880647165/rnGjJdZB7.png" alt="image.png" /></p>
<p>Answer Yes to the prompt asking if you are sure you want to connect</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1669880784658/SVXFlq9hP.png" alt="image.png" />
Enter your passphrase, and you should be logged in to the Vultr instance.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1669880937066/9h0BdX5jR.png" alt="image.png" /></p>
<p>Resources</p>
<p><a target="_blank" href="https://www.ssh.com/academy/ssh/protocol">https://www.ssh.com/academy/ssh/protocol</a></p>
<p><a target="_blank" href="https://www.commandlinux.com/man-page/man1/ssh-keygen.1.html">https://www.commandlinux.com/man-page/man1/ssh-keygen.1.html</a></p>
]]></content:encoded></item><item><title><![CDATA[Machine Learning For All]]></title><description><![CDATA[How does a baby learn? How are you able to read this blog right now? After all, we were born with a "blank" brain, and somehow through development, we learnt how to walk and read and everything else we can do. In fact, the impact of parenthood on chi...]]></description><link>https://wamaithanyamu.com/machine-learning-for-all</link><guid isPermaLink="true">https://wamaithanyamu.com/machine-learning-for-all</guid><category><![CDATA[Machine Learning]]></category><category><![CDATA[Data Science]]></category><category><![CDATA[Artificial Intelligence]]></category><category><![CDATA[data analysis]]></category><dc:creator><![CDATA[Wamaitha ]]></dc:creator><pubDate>Thu, 10 Nov 2022 13:59:57 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1672514672552/37b48eb0-b86a-4eea-9218-594f76d38447.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>How does a baby learn? How are you able to read this blog right now? After all, we were born with a "blank" brain, and somehow through development, we learnt how to walk and read and everything else we can do. In fact, the impact of parenthood on children is so well documented. A <a target="_blank" href="https://marripedia.org/effects_of_fatherless_families_on_crime_rates">research</a> on the rate of crime rates in 25 million  fatherless families showed the following:</p>
<ul>
<li>Of all adolescent incarcerations, the majority are from fatherless families. These adolescents are more susceptible to delinquency.</li>
<li>Adults that grew up without a father are three times more likely to end up in jail by the time they clock  30 years.</li>
<li>Poverty rates in female-led families are at 45.8%, while those with two parents are at 9.5%.</li>
</ul>
<p>But wait, what does this have to do with Machine learning? Just like humans, machines can learn how to "do" things, good and bad. More importantly, who we learn from directly impacts the kind of humans we turn out to be. Garbage in, garbage out also apply to training machines to "think". You might have encountered a few use cases of machine learning, let's explore a few. </p>
<p>If you have a google photos account, you may notice that similar images are often grouped together. How does Google do it? With enough samples of your face, Google can correctly group other photos your in. Google AI has "learnt" how you look and can confidently identify you. This specific technique of using samples to learn is called supervised machine learning. </p>
<p>Go is an ancient board game from China from over 2,500 years ago. The game has more moves than there are atoms in the universe! Wining in Go requires <a target="_blank" href="https://www.deepmind.com/research/highlighted-research/alphago">multiple layers of strategic thinking</a>.  Therefore, computers cannot brute force and win this game. Yet, the team at Deepmind trained an AI (Alpha Go) that beat the world champion in Go. Alpha Go researchers implemented deep neural networks to achieve this great fit. </p>
<p>An example close to home is social media algorithms. Oh, the mighty algorithms! Ever search for a YouTube video on "how to do X", and then suddenly YouTube starts recommending videos on that search? Instagram works the same, too, search a page on shoes, and suddenly your feed is full of vendors selling shoes. These algorithms learn from what we search for and recommend products to us. Meta heavily relies on personalized recommendations to generate revenue for the company. Arguably all big tech is making billions of our search data. </p>
<p>It gets more interesting! Researchers from Harrisburg University developed a facial recognition system that <a target="_blank" href="https://archive.md/N1HVe">predicts criminality.</a> The AI was developed to assist law enforcement and military operations. It works by extracting facial features that predict criminality. The research also emphasized on the AI not being racially biased. Racial bias is perpetuated by having sample data that does not have a balanced representation of all races. However, the AI has an 80% accuracy. A very low accuracy for the stakes at hand. Imagine going to prison becomes your eyebrow matches that of a criminal!</p>
<p>In 2016, an AI chatbot was released by Microsoft on Twitter. Tay was the chatbot's name. Tay was designed to learn through interacting with Twitter users. Through interactions, Tay's personality would be likened to that of a racist within 24 hours of launch. </p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1668087937490/HO9Zrw5MD.png" alt="Screenshot from 2022-11-10 16-45-14.png" class="image--center mx-auto" /></p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1668087983160/By3lABddC.png" alt="Screenshot from 2022-11-10 16-46-12.png" class="image--center mx-auto" /></p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1668088017953/E_LNDnzFi.png" alt="Screenshot from 2022-11-10 16-46-45.png" class="image--center mx-auto" /></p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1668088029139/r0uCRLQxg.png" alt="tay.png" class="image--center mx-auto" /></p>
<p>Tay was shut down, with Microsoft citing a coordinated attack.</p>
<p>These are just but a few of the many applications of machine learning. Sounds exciting? Good! Jenga school and Zindi will conduct a 5-day hackathon on Machine learning. Come one come all. Tell a friend to tell a friend. Come ready to learn and, most importantly, have fun with all the endless possibilities that Machine Learning offers! Find registration information on the poster below. </p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1668088323477/iSs1Tws5y.jpeg" alt="mlforall.jpeg" class="image--center mx-auto" /></p>
<p>Happy hacking! You've got this!</p>
]]></content:encoded></item><item><title><![CDATA[Finding prime numbers in a list using Python]]></title><description><![CDATA[Prime numbers are positive integers with only 2 factors: 1 and themselves. In other words, these are numbers that can only be perfectly divided by 1 and themselves. One is not a prime number because it only has one factor. A factor is a number or alg...]]></description><link>https://wamaithanyamu.com/finding-prime-numbers-in-a-list-using-python</link><guid isPermaLink="true">https://wamaithanyamu.com/finding-prime-numbers-in-a-list-using-python</guid><category><![CDATA[Python]]></category><category><![CDATA[Programming Blogs]]></category><category><![CDATA[Programming Tips]]></category><category><![CDATA[algorithms]]></category><category><![CDATA[data structures]]></category><dc:creator><![CDATA[Wamaitha ]]></dc:creator><pubDate>Mon, 26 Sep 2022 18:18:53 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1672514968639/771889b3-08de-43d7-a181-5ba8ec6d765b.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Prime numbers are positive integers with only 2 factors: 1 and themselves. In other words, these are numbers that can only be perfectly divided by 1 and themselves. One is not a prime number because it only has one factor. A factor is a number or algebraic expression that divides another number or expression evenly; with no remainder.</p>
<p> For example, 2 and 4 are factors of 16 because 16÷ 2 = 8 exactly and 16 ÷ 4 = 4 exactly. The other factors of 16 are 1, 8, and 16.</p>
<p>Take 13 as an example; we would need to divide 13 by every number from 2 to 13 to check if we get a perfect division. We can do this using the modulo. The modulus operator (%) in python gives the remainder after division. </p>
<p>For example:</p>
<pre><code class="lang-py"><span class="hljs-number">13</span> % <span class="hljs-number">2</span> -&gt; <span class="hljs-number">1</span> <span class="hljs-comment"># 13/2 = 6 remainder 1</span>

<span class="hljs-number">13</span> % <span class="hljs-number">3</span> -&gt; <span class="hljs-number">1</span> <span class="hljs-comment"># 13/3 = 4 remainder 1</span>

<span class="hljs-number">13</span> % <span class="hljs-number">4</span> -&gt; <span class="hljs-number">1</span> <span class="hljs-comment"># 13/4 = 3 remainder 1</span>

<span class="hljs-number">13</span> % <span class="hljs-number">5</span> -&gt; <span class="hljs-number">3</span> <span class="hljs-comment"># 13/5 = 2 remainder 3</span>

<span class="hljs-number">13</span> % <span class="hljs-number">6</span> -&gt; <span class="hljs-number">1</span> <span class="hljs-comment"># 13/6 = 2 remainder 1</span>

<span class="hljs-number">13</span> % <span class="hljs-number">7</span> -&gt; <span class="hljs-number">6</span> <span class="hljs-comment"># 13/7 = 1 remainder 6</span>

<span class="hljs-number">13</span> % <span class="hljs-number">8</span> -&gt; <span class="hljs-number">5</span> <span class="hljs-comment"># 13/8 = 1 remainder 5</span>

<span class="hljs-number">13</span> % <span class="hljs-number">9</span> -&gt; <span class="hljs-number">4</span> <span class="hljs-comment"># 13/9 = 1 remainder 4</span>

<span class="hljs-number">13</span> % <span class="hljs-number">10</span> -&gt; <span class="hljs-number">3</span> <span class="hljs-comment"># 13/10 = 1 remainder 3</span>

<span class="hljs-number">13</span> % <span class="hljs-number">11</span> -&gt; <span class="hljs-number">1</span> <span class="hljs-comment"># 13/11 = 1 remainder 2</span>

<span class="hljs-number">13</span> % <span class="hljs-number">12</span> -&gt; <span class="hljs-number">1</span> <span class="hljs-comment"># 13/12 = 1 remainder 1</span>

<span class="hljs-number">13</span> % <span class="hljs-number">13</span> -&gt; <span class="hljs-number">0</span> <span class="hljs-comment"># 13/13 = 1 remainder 0</span>
</code></pre>
<p>We can conclude that a  number qualifies as a prime number when we only get 0 as the result of the modulus when we divide the number by itself. In code, we can implement this as follows:</p>
<pre><code class="lang-py">
<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">check_if_prime</span>(<span class="hljs-params">x</span>):</span>
      <span class="hljs-keyword">for</span> i <span class="hljs-keyword">in</span> range(<span class="hljs-number">2</span>, x):
          <span class="hljs-keyword">if</span> x % i == <span class="hljs-number">0</span>:
             <span class="hljs-keyword">return</span> print(<span class="hljs-string">"not prime"</span>)              
          <span class="hljs-keyword">return</span> print(<span class="hljs-string">"Is prime"</span>)
</code></pre>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1664191089002/nc3rsrALF.JPG" alt="1.JPG" />
The code works as expected for checking if a number is prime. We need to modify the code to return a list of prime numbers within a given range. For example, prime numbers between 1 and 100 are: </p>
<pre><code class="lang-sh">
2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97
</code></pre>
<h1 id="heading-find-prime-numbers-within-a-range">Find prime numbers within a range</h1>
<p>Working with lists requires the use of nested for loops. </p>
<p><iframe src="https://giphy.com/embed/31ee5G7Adb4DJLA87B" width="480" height="266" class="giphy-embed"></iframe></p><p><a href="https://giphy.com/gifs/rickandmorty-31ee5G7Adb4DJLA87B">via GIPHY</a></p><p></p>
<p>The complete code is as shown below:</p>
<pre><code class="lang-py"><span class="hljs-keyword">import</span> time    

<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">check_if_prime</span>(<span class="hljs-params">upper_range</span>):</span>

  <span class="hljs-keyword">if</span> upper_range  &lt; <span class="hljs-number">2</span> :
     <span class="hljs-keyword">return</span> []  
  <span class="hljs-comment"># append all prime numbers</span>
  list_of_primes = [<span class="hljs-number">2</span>] 
  <span class="hljs-comment"># outter loop from 2 to x</span>
  <span class="hljs-keyword">for</span> number <span class="hljs-keyword">in</span> range(<span class="hljs-number">3</span>, upper_range + <span class="hljs-number">1</span>):
      is_prime = <span class="hljs-literal">True</span>
      <span class="hljs-comment"># check if the current value of j is prime</span>
      <span class="hljs-keyword">for</span> num <span class="hljs-keyword">in</span> range(<span class="hljs-number">3</span>, number):
          <span class="hljs-comment"># check if j is prime by getting the modulus from 2 to j</span>
          <span class="hljs-keyword">if</span> number % num == <span class="hljs-number">0</span>:
             <span class="hljs-comment"># if num is not prime break loop and moves to the next value in the range and change is_prime to false</span>
             is_prime = <span class="hljs-literal">False</span>
             <span class="hljs-keyword">break</span>
      <span class="hljs-comment"># if  the number is prime, append it to the list</span>
      <span class="hljs-keyword">if</span> is_prime :
        list_of_primes.append(number)             
  <span class="hljs-keyword">return</span>  list_of_primes         

tic = time.perf_counter() <span class="hljs-comment"># start time</span>
primes = check_if_prime(<span class="hljs-number">100000</span>)
toc = time.perf_counter() <span class="hljs-comment"># End Time</span>

print(primes)
<span class="hljs-comment"># TIME TAKEN</span>
print(<span class="hljs-string">f"Build finished in <span class="hljs-subst">{toc - tic:<span class="hljs-number">0.4</span>f}</span> seconds"</span>)
</code></pre>
<p>Going over the code above:</p>
<ul>
<li><p>The function takes in a number as a parameter. The parameter represents the maximum value in a range. For example, if we need to find the prime numbers between 0 and 1000, 1000 would be the <strong>upper_range</strong></p>
</li>
<li><p>Initialize a list that will hold all the prime numbers of the given range. The list initializes at 2 since 2 is the first prime number. Any number below two returns an empty list.</p>
</li>
<li><p>Create an outer for loop that loops through 0 to the upper_range. We add one since the range function excludes the upper range. </p>
</li>
</ul>
<p>For example, when finding the range from 0-5, the range function only works from 0-4. Therefore, if we need the 5 included, we need to add 1 -i.e., from 0-6.</p>
<ul>
<li>Initialize a boolean that tracks if the current digit is a prime. It initializes as True and changes to False when the modulus yields a zero.</li>
<li>The inner for loop checks the specific if the digit is a prime number. This is the same implementation we had earlier on for a single number.</li>
<li>In the event, the number is prime, the is_prime variable changes to False and is appended to the list_of_primes list.</li>
<li>lastly, we measure how long this algorithm takes when the upper_range supplied is 100000. It takes 47.34 seconds.</li>
</ul>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1664205474623/cabVdbcYa.JPG" alt="2.JPG" /></p>
<h1 id="heading-optimization">Optimization</h1>
<p>We can tweak the algorithm to be more efficient. </p>
<ul>
<li><p>All even numbers are not prime numbers since they are divisible by 2. Thus if a number is not divisible by 2, what's the point of dividing by 4,6, or 8? Essentially, we can eliminate even numbers.</p>
</li>
<li><p>Consider the number 80. Below are all the factors of 80</p>
</li>
</ul>
<pre><code class="lang-py">
    <span class="hljs-number">2</span> *  <span class="hljs-number">40</span> = <span class="hljs-number">80</span>
    <span class="hljs-number">4</span> *  <span class="hljs-number">20</span> = <span class="hljs-number">80</span>
    <span class="hljs-number">5</span> *  <span class="hljs-number">16</span> = <span class="hljs-number">80</span>
    <span class="hljs-number">8</span> * <span class="hljs-number">10</span> = <span class="hljs-number">80</span>
  <span class="hljs-number">10</span> * <span class="hljs-number">8</span> = <span class="hljs-number">80</span> <span class="hljs-comment"># from here, we are repeating the factors in a different order</span>
  <span class="hljs-number">16</span> *  <span class="hljs-number">5</span> = <span class="hljs-number">80</span>
  <span class="hljs-number">20</span> *  <span class="hljs-number">4</span>= <span class="hljs-number">80</span>
  <span class="hljs-number">40</span> *  <span class="hljs-number">2</span>= <span class="hljs-number">80</span>
</code></pre>
<ul>
<li><p>Once we get to 8 *10, the factors don't change—the order changes with the bigger number starting. Coincidentally, the square root of 80 is 8.94427191. We can then reduce iteration up to the square root of the number when looking for primes.</p>
</li>
<li><p>Lastly, we use the existing prime values to get the modulus in the inner loop.</p>
</li>
</ul>
<pre><code class="lang-py"><span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">check_if_prime</span>(<span class="hljs-params">upper_range</span>):</span>

    <span class="hljs-keyword">if</span> upper_range  &lt; <span class="hljs-number">2</span> :
       <span class="hljs-keyword">return</span> []

    list_of_primes=[<span class="hljs-number">2</span>] 

    <span class="hljs-keyword">for</span> number <span class="hljs-keyword">in</span> range(<span class="hljs-number">3</span>, upper_range+<span class="hljs-number">1</span>, <span class="hljs-number">2</span>):
          is_prime = <span class="hljs-literal">True</span>
          square_root_stop = number**<span class="hljs-number">0.5</span>
          <span class="hljs-keyword">for</span> num <span class="hljs-keyword">in</span> list_of_primes: <span class="hljs-comment"># &lt;---only use primes</span>
                <span class="hljs-keyword">if</span> num &gt; square_root_stop: <span class="hljs-comment"># &lt;--- square root</span>
                      <span class="hljs-keyword">break</span>
                <span class="hljs-keyword">if</span> number % num == <span class="hljs-number">0</span>:
                      is_prime = <span class="hljs-literal">False</span>
                      <span class="hljs-keyword">break</span>
          <span class="hljs-keyword">if</span> is_prime:
                list_of_primes.append(number)
    print(list_of_primes)
    <span class="hljs-keyword">return</span> list_of_primes     

tic = time.perf_counter() <span class="hljs-comment"># </span>
primes = check_if_prime(<span class="hljs-number">100000</span>)
toc = time.perf_counter() <span class="hljs-comment"># End Time</span>
<span class="hljs-comment"># Print the Difference Minutes and Seconds</span>
print(<span class="hljs-string">f"Build finished in <span class="hljs-subst">{(toc - tic)/<span class="hljs-number">60</span>:<span class="hljs-number">0.0</span>f}</span> minutes <span class="hljs-subst">{(toc - tic)%<span class="hljs-number">60</span>:<span class="hljs-number">0.0</span>f}</span> seconds"</span>)
<span class="hljs-comment"># For additional Precision</span>
print(<span class="hljs-string">f"Build finished in <span class="hljs-subst">{toc - tic:<span class="hljs-number">0.4</span>f}</span> seconds"</span>)
</code></pre>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1664216052944/UGbiUy8mr.JPG" alt="4.JPG" /></p>
<p>This reduces the runtime of our algorithm by 46 seconds! Amazing! </p>
<p><iframe src="https://giphy.com/embed/Jp4JNduLIpCPA1sPUd" width="480" height="270" class="giphy-embed"></iframe></p><p><a href="https://giphy.com/gifs/rickandmorty-season-1-adult-swim-rick-and-morty-Jp4JNduLIpCPA1sPUd">via GIPHY</a></p><p></p>
]]></content:encoded></item><item><title><![CDATA[How to determine if a number is a Palindrome]]></title><description><![CDATA[A palindrome is a word or number that reads the same forward and backwards. 
A few examples of palindromes are :
- Madam
- Hannah
- Bob
- Never odd or even
- A man, a plan, a canal – Panama
- 121
- 99
- 22

When dealing with strings, it's rather stra...]]></description><link>https://wamaithanyamu.com/how-to-determine-if-a-number-is-a-palindrome</link><guid isPermaLink="true">https://wamaithanyamu.com/how-to-determine-if-a-number-is-a-palindrome</guid><category><![CDATA[Python]]></category><category><![CDATA[algorithms]]></category><category><![CDATA[data structures]]></category><category><![CDATA[Programming Blogs]]></category><dc:creator><![CDATA[Wamaitha ]]></dc:creator><pubDate>Sun, 25 Sep 2022 22:47:26 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1672515413100/331e39ae-6ca3-42d9-864c-00a5ffcaa9c7.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>A palindrome is a word or number that reads the same forward and backwards. </p>
<p>A few examples of palindromes are :</p>
<pre><code class="lang-sh">- Madam
- Hannah
- Bob
- Never odd or even
- A man, a plan, a canal – Panama
- 121
- 99
- 22
</code></pre>
<p>When dealing with strings, it's rather straightforward. The reverse of the string should be equal to the original string. </p>
<p>An implementation of this in python, is as shown below</p>
<pre><code class="lang-py">original_string = <span class="hljs-string">"Madam"</span>
original_string = original_string.lower() <span class="hljs-comment"># convert to lowercase</span>
string_reversed = <span class="hljs-string">""</span>.join(reversed(original_string))
</code></pre>
<p>Comparing both variables yields true.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1664127820019/YsPJn0On3.JPG" alt="1.JPG" />
The implementation works with one-word strings but needs some modifications to work with the sentences. The transformations needed would be :</p>
<ol>
<li>Remove punctuations and special characters</li>
<li>Remove all spaces</li>
</ol>
<p>The transformations are done using regex. Code implementation for these transformations is as shown below</p>
<pre><code class="lang-py"><span class="hljs-keyword">import</span> re <span class="hljs-comment"># import regex</span>

original_sentence = <span class="hljs-string">"A man, a plan, a canal – Panama "</span>
original_sentence = original_sentence.lower() <span class="hljs-comment"># convert to lowercase</span>
original_sentence = re.sub(<span class="hljs-string">'[^A-Za-z0-9]+'</span>, <span class="hljs-string">''</span>, original_sentence) <span class="hljs-comment"># remove all special characters and spaces</span>
sentence_reversed = <span class="hljs-string">""</span>.join(reversed(original_sentence))
</code></pre>
<p>Wait, but you said this is about numbers!</p>
<p> Buckle up, boys! A palindrome adventure awaits!</p>
<p><iframe src="https://giphy.com/embed/ZdBnIqBeVWBYTobH8U" width="480" height="264" class="giphy-embed"></iframe></p><p><a href="https://giphy.com/gifs/rickandmorty-season-4-episode-7-rick-and-morty-ZdBnIqBeVWBYTobH8U">via GIPHY</a></p><p></p>
<p>The goal is to avoid converting the integer into a string to check if it qualifies as a palindrome. A few base cases we need to cover:</p>
<p>1 . Negative numbers cannot be palindromes</p>
<pre><code class="lang-sh">-121 -&gt; 121-
</code></pre>
<p>2 . 10 is not a palindrome. In fact, any number ending with a zero is not a palindrome. When the zero moves to the ones position, a new number is formed. </p>
<p>3 . All numbers between 0 and 9 are palindromes</p>
<pre><code class="lang-sh">1010 -&gt; 0101 <span class="hljs-comment"># not read as the same number</span>
</code></pre>
<p>Implementation for the base cases :</p>
<pre><code class="lang-py"><span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">check_if_palindrome</span>(<span class="hljs-params">x</span>):</span>
<span class="hljs-comment"># check if negative</span>
    <span class="hljs-keyword">if</span> x &lt; <span class="hljs-number">0</span> :
      <span class="hljs-keyword">return</span> <span class="hljs-string">"Not a palindrome"</span>
<span class="hljs-comment"># check if ends with zero</span>
    <span class="hljs-keyword">if</span> x % <span class="hljs-number">10</span> == <span class="hljs-number">0</span>:
       <span class="hljs-keyword">return</span> <span class="hljs-string">"Not a palindrome"</span>
 <span class="hljs-comment"># check if x is between 0 and 9</span>
    <span class="hljs-keyword">if</span>(x &gt; <span class="hljs-number">0</span> &amp;&amp; x &lt;<span class="hljs-number">10</span>):
       <span class="hljs-keyword">return</span> <span class="hljs-string">"Is palindrome"</span>
</code></pre>
<p>The <strong>%</strong> in python is used to get the <strong>modulus</strong>. In layman's terms, the modulus returns the remainder when we perform division.</p>
<p> For example, the division of 12 by 10 yields 1.2. 2 is the remainder of the division. Thus if we perform 12 % 10, we get 2.</p>
<p> This concept will be the backbone of our implementation for finding palindromes. </p>
<p>A few more examples for clarity :</p>
<pre><code class="lang-py"><span class="hljs-number">100</span> % <span class="hljs-number">10</span> -&gt; <span class="hljs-number">0</span>
<span class="hljs-number">11</span> % <span class="hljs-number">10</span> -&gt; <span class="hljs-number">1</span>
<span class="hljs-number">13</span> % <span class="hljs-number">6</span> -&gt; <span class="hljs-number">1</span>
<span class="hljs-number">22</span> % <span class="hljs-number">4</span> -&gt; <span class="hljs-number">2</span>
</code></pre>
<p><iframe src="https://giphy.com/embed/liBsVeLILcyaY" width="480" height="270" class="giphy-embed"></iframe></p><p><a href="https://giphy.com/gifs/adultswim-liBsVeLILcyaY">via GIPHY</a></p><p></p>
<p>Look at the following examples. Do you notice a pattern?</p>
<pre><code class="lang-py"><span class="hljs-keyword">import</span> math <span class="hljs-comment"># math module in python</span>

<span class="hljs-number">121</span>  % <span class="hljs-number">10</span> -&gt; <span class="hljs-number">1</span>
math.floor(<span class="hljs-number">121</span> / <span class="hljs-number">10</span> )-&gt; <span class="hljs-number">12</span> 

<span class="hljs-number">1771</span> % <span class="hljs-number">10</span> -&gt; <span class="hljs-number">1</span>
math.floor(<span class="hljs-number">1771</span> / <span class="hljs-number">10</span> -&gt; <span class="hljs-number">177</span>

<span class="hljs-number">2442</span> % <span class="hljs-number">10</span> -&gt; <span class="hljs-number">2</span>
math.floor(<span class="hljs-number">2442</span> / <span class="hljs-number">10</span>) -&gt; <span class="hljs-number">244</span>

<span class="hljs-number">396693</span> % <span class="hljs-number">10</span> -&gt; <span class="hljs-number">3</span>
math.floor(<span class="hljs-number">396693</span> / <span class="hljs-number">10</span>) -&gt; <span class="hljs-number">39669</span>

<span class="hljs-number">120</span> % <span class="hljs-number">10</span> -&gt; <span class="hljs-number">0</span>
math.floor(<span class="hljs-number">120</span> / <span class="hljs-number">10</span>) -&gt; <span class="hljs-number">12</span>
</code></pre>
<p>Using 10 as the modulo yields the last digit, while getting the floor of the division with 10 yields all digits with the last digit eliminated. </p>
<p>We can then use two variables to keep track of both halves of the digit. The idea is to have both halves and compare them. If they are the same when reversed, we can identify a 
palindrome. </p>
<p>Let's first look at the even number <strong>396693</strong>. We need an algorithm that splits the number into two, such that :</p>
<pre><code class="lang-py">half_one = <span class="hljs-number">396</span>
half_two = <span class="hljs-number">396</span> <span class="hljs-comment"># 693 reversed</span>
</code></pre>
<p>Hence in the first iteration, we get :</p>
<pre><code class="lang-py"><span class="hljs-keyword">import</span> math <span class="hljs-comment"># math module in python</span>

half_one  = math.floor(<span class="hljs-number">396693</span> / <span class="hljs-number">10</span>)
half_two  = <span class="hljs-number">396693</span> % <span class="hljs-number">10</span>
</code></pre>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1664144897029/RMw-HlVR5.JPG" alt="2.JPG" /></p>
<p>On the next iteration, we need to use the value in half_one (39669) and eliminate the 9 at the end. The 9 needs to move to half_two(3) and have the value in half_two bump up to 39. </p>
<p>We start with half_two since we need the current value in half_one. Performing the % on 39669 yields 9. To get half_one's value to 39, we first need to multiply it by 10 to get 30. Then add 9 to 30.</p>
<p>Lastly, we can floor half_one by 10 to get  3966 . Our code thus becomes:</p>
<pre><code class="lang-py"><span class="hljs-keyword">import</span> math <span class="hljs-comment"># math module in python</span>

half_one  = math.floor(<span class="hljs-number">396693</span> / <span class="hljs-number">10</span>)
half_two  = <span class="hljs-number">396693</span> % <span class="hljs-number">10</span>

half_two  = half_two * <span class="hljs-number">10</span> + half_one % <span class="hljs-number">10</span> <span class="hljs-comment"># need to use the current value of half_one before it is changed</span>
half_one  = math.floor(half_one / <span class="hljs-number">10</span>)
</code></pre>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1664144938982/ax9wEOeVV.JPG" alt="3.JPG" /></p>
<p>Performing this operation one more time :</p>
<pre><code class="lang-py">
<span class="hljs-keyword">import</span> math <span class="hljs-comment"># math module in python</span>

half_one  = math.floor(<span class="hljs-number">396693</span> / <span class="hljs-number">10</span>)
half_two  = <span class="hljs-number">396693</span> % <span class="hljs-number">10</span>

half_two  = half_two * <span class="hljs-number">10</span> + half_one % <span class="hljs-number">10</span> <span class="hljs-comment"># need to use the current value of half_one before it is changed</span>
half_one  = math.floor(half_one / <span class="hljs-number">10</span>)


half_two  = half_two * <span class="hljs-number">10</span> + half_one % <span class="hljs-number">10</span> <span class="hljs-comment"># need to use the current value of half_one before it is changed</span>
half_one  = math.floor(half_one / <span class="hljs-number">10</span>)
</code></pre>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1664144998854/UECsszfpR.JPG" alt="4.JPG" /></p>
<p>At this point, we can check if half_one is equivalent to half_two. We can generalise this to work for any even number as :</p>
<pre><code class="lang-py">
half_one = <span class="hljs-number">396693</span>
half_two = <span class="hljs-number">0</span> <span class="hljs-comment"># initialise as 0</span>

<span class="hljs-keyword">while</span> half_one &gt; half_two :
      half_two  = half_two * <span class="hljs-number">10</span> + half_one % <span class="hljs-number">10</span> <span class="hljs-comment"># need to use the current value of half_one before it is changed</span>
      half_one  = math.floor(half_one / <span class="hljs-number">10</span>)

print(half_one, half_two)
</code></pre>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1664145121851/r4b49L3i9.JPG" alt="5.JPG" />
When half_one and half_two are compared, we get a true value and conclude the number is a palindrome and vice versa. </p>
<p>Updating our palindrome function to accomodate even numbers :</p>
<pre><code class="lang-py">
<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">check_if_palindrome</span>(<span class="hljs-params">x</span>):</span>
    <span class="hljs-comment"># check if negative</span>
    <span class="hljs-keyword">if</span> x &lt; <span class="hljs-number">0</span> :
      <span class="hljs-keyword">return</span> <span class="hljs-string">"Not a palindrome"</span>

    <span class="hljs-comment"># check if ends with zero</span>
    <span class="hljs-keyword">if</span> x % <span class="hljs-number">10</span> == <span class="hljs-number">0</span>:
       <span class="hljs-keyword">return</span> <span class="hljs-string">"Not a palindrome"</span>

    <span class="hljs-comment"># check if x is between 0 and 9</span>
    <span class="hljs-keyword">if</span> x &gt; <span class="hljs-number">0</span> <span class="hljs-keyword">and</span> x &lt;<span class="hljs-number">10</span>:
       <span class="hljs-keyword">return</span> <span class="hljs-string">"Is palindrome"</span>

    <span class="hljs-comment"># palindrome for even numbers</span>
    half_one = x
    half_two = <span class="hljs-number">0</span> <span class="hljs-comment"># initialise as 0    </span>
    <span class="hljs-keyword">while</span> half_one &gt; half_two :              
          half_two  = half_two * <span class="hljs-number">10</span> + half_one % <span class="hljs-number">10</span> <span class="hljs-comment"># need to use the current value of half_one before it is changed</span>
          half_one  = math.floor(half_one / <span class="hljs-number">10</span>)

    <span class="hljs-keyword">if</span> half_one == half_two:
        <span class="hljs-keyword">return</span> <span class="hljs-string">"Is palindrome"</span>
    <span class="hljs-keyword">else</span>:
        <span class="hljs-keyword">return</span> <span class="hljs-string">"Not a palindrome"</span>
</code></pre>
<p>We now need to cover base for odd numbers. Odd numbers are such that when the middle number is removed , we are left with an even number, and we repeat the process above for even numbers.</p>
<pre><code class="lang-py"><span class="hljs-keyword">import</span> math <span class="hljs-comment"># math module in python</span>

half_one  = math.floor(<span class="hljs-number">121</span>/ <span class="hljs-number">10</span>)
half_two  = <span class="hljs-number">121</span>% <span class="hljs-number">10</span>
</code></pre>
<p>This yields :</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1664145107228/TetSzT64Q.JPG" alt="6.JPG" /></p>
<p>Performing this operation again moves 2 to the second half. We want to always have the odd value on the second half. This ensures the code runs until all values omitting the middle value have been separated.</p>
<pre><code class="lang-py"><span class="hljs-keyword">import</span> math <span class="hljs-comment"># math module in python</span>

half_one  = math.floor(<span class="hljs-number">121</span>/ <span class="hljs-number">10</span>)
half_two  = <span class="hljs-number">121</span>% <span class="hljs-number">10</span>

half_two  = half_two * <span class="hljs-number">10</span> + half_one % <span class="hljs-number">10</span> <span class="hljs-comment"># need to use the current value of half_one before it is changed</span>
half_one  = math.floor(half_one / <span class="hljs-number">10</span>)
</code></pre>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1664145135989/L7Mxz9K71.JPG" alt="7.JPG" /></p>
<p>At this point we need to drop the 2 on half_two and compare the values in half_one and half_two again to check if the number is a palindrome</p>
<pre><code class="lang-py">
<span class="hljs-keyword">import</span> math <span class="hljs-comment"># math module in python</span>

half_one  = math.floor(<span class="hljs-number">121</span>/ <span class="hljs-number">10</span>)
half_two  = <span class="hljs-number">121</span>% <span class="hljs-number">10</span>

half_two  = half_two * <span class="hljs-number">10</span> + half_one % <span class="hljs-number">10</span> <span class="hljs-comment"># need to use the current value of half_one before it is changed</span>
half_one  = math.floor(half_one / <span class="hljs-number">10</span>)

half_two = math.floor(half_two / <span class="hljs-number">10</span>)
</code></pre>
<p>At this point half_one and half_two only contain the value 1. Comparing them yields true which  means 121  was a palindrome. This can be generalised as :</p>
<pre><code class="lang-py">
half_one = <span class="hljs-number">121</span>
half_two = <span class="hljs-number">0</span> <span class="hljs-comment"># initialise as 0</span>

<span class="hljs-keyword">while</span> half_one &gt; half_two :


      half_two  = half_two * <span class="hljs-number">10</span> + half_one % <span class="hljs-number">10</span> <span class="hljs-comment"># need to use the current value of half_one before it is changed</span>
      half_one  = math.floor(half_one / <span class="hljs-number">10</span>)     

print(half_one, math.floor(half_two / <span class="hljs-number">10</span>) )
</code></pre>
<p>Hold up! We've seen this before! Therefore we can adjust our palindrome function to accommodate odd numbers as follows:</p>
<pre><code class="lang-py">
<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">check_if_palindrome</span>(<span class="hljs-params">x</span>):</span>
    <span class="hljs-comment"># check if negative</span>
    <span class="hljs-keyword">if</span> x &lt; <span class="hljs-number">0</span> :
      <span class="hljs-keyword">return</span> <span class="hljs-string">"Not a palindrome"</span>

    <span class="hljs-comment"># check if ends with zero</span>
    <span class="hljs-keyword">if</span> x % <span class="hljs-number">10</span> == <span class="hljs-number">0</span>:
       <span class="hljs-keyword">return</span> <span class="hljs-string">"Not a palindrome"</span>

    <span class="hljs-comment"># check if x is between 0 and 9</span>
    <span class="hljs-keyword">if</span> x &gt; <span class="hljs-number">0</span> <span class="hljs-keyword">and</span> x &lt;<span class="hljs-number">10</span>:
       <span class="hljs-keyword">return</span> <span class="hljs-string">"Is palindrome"</span>

    <span class="hljs-comment"># palindrome for odd and  even numbers</span>
    half_one = x
    half_two = <span class="hljs-number">0</span> <span class="hljs-comment"># initialise as 0    </span>
    <span class="hljs-keyword">while</span> half_one &gt; half_two :              
          half_two  = half_two * <span class="hljs-number">10</span> + half_one % <span class="hljs-number">10</span> <span class="hljs-comment"># need to use the current value of half_one before it is changed</span>
          half_one  = math.floor(half_one / <span class="hljs-number">10</span>)

    <span class="hljs-keyword">if</span> half_one == half_two <span class="hljs-keyword">or</span> half_one == math.floor(half_two / <span class="hljs-number">10</span>) :
        <span class="hljs-keyword">return</span> <span class="hljs-string">"Is palindrome"</span>
    <span class="hljs-keyword">else</span>:
        <span class="hljs-keyword">return</span> <span class="hljs-string">"Not a palindrome"</span>
</code></pre>
<p>Testing the handy dandy function</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1664145151800/SpGImeJ4v.JPG" alt="8.JPG" /></p>
<p>Yeap, that's all! You did it! I'm so proud of you! Thank you for being on this adventure with me. </p>
<p><iframe src="https://giphy.com/embed/i2dE5VvBNxBw4" width="480" height="264" class="giphy-embed"></iframe></p><p><a href="https://giphy.com/gifs/adult-swim-rick-and-morty-thats-season-one-i2dE5VvBNxBw4">via GIPHY</a></p><p></p>
<p>Perhaps now you're wondering, is this solution optimal? Space-time complexity? Worry not! I will be covering these complex topics in a future blog. So please subscribe to my newsletter or follow me here on hashnode to ensure you do not miss that! </p>
]]></content:encoded></item><item><title><![CDATA[How to integrate the Mpesa STK push API in Nodejs.]]></title><description><![CDATA[Overview
By the end of this guide, you should be able to integrate the Mpesa express API in Nodejs and express. You will achieve this in several steps:

Authorize your Mpesa API by generating an access token for every API call

Invoke the STK push en...]]></description><link>https://wamaithanyamu.com/how-to-integrate-the-mpesa-stk-push-api-in-nodejs</link><guid isPermaLink="true">https://wamaithanyamu.com/how-to-integrate-the-mpesa-stk-push-api-in-nodejs</guid><category><![CDATA[MPesa]]></category><category><![CDATA[APIs]]></category><category><![CDATA[Node.js]]></category><category><![CDATA[Mobile money]]></category><category><![CDATA[transactions]]></category><dc:creator><![CDATA[Wamaitha ]]></dc:creator><pubDate>Sat, 20 Aug 2022 08:09:28 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1672515793437/8c01c5af-da72-4c4a-980d-ee060352fd07.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h1 id="heading-overview">Overview</h1>
<p>By the end of this guide, you should be able to integrate the Mpesa express API in Nodejs and express. You will achieve this in several steps:</p>
<ol>
<li><p>Authorize your Mpesa API by generating an access token for every API call</p>
</li>
<li><p>Invoke the STK push endpoint that prompts the user to impute their mpesa pin to authorize transactions</p>
</li>
<li><p>Have a callback that saves the transaction details to a database of your choice.</p>
</li>
<li><p>Confirm payments</p>
</li>
</ol>
<h1 id="heading-prerequisites">Prerequisites:</h1>
<ul>
<li><p>Safaricom developer account. Create one <a target="_blank" href="https://developer.safaricom.co.ke/">here</a></p>
</li>
<li><p>Node js installed</p>
</li>
<li><p>Postman</p>
</li>
</ul>
<p>The project folder structure for this guide is as shown below.</p>
<pre><code class="lang-bash">Project_Folder
└───controllers
      | controllers.lipanampesa.js
└───middlewares
      | middlewares.generateAccessToken.js
└───routes
      | routes.lipanampesa.js
└───utils
      | utils.timestamp.js

| server.js
| package.json
| .env
</code></pre>
<h1 id="heading-setup">Setup</h1>
<p>Log on to the developer portal and create an app.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1659988883291/hlyDUSsnS.png" alt="mpesa1.png" /></p>
<p>Click on show key/secret to reveal the test application's consumer key and secret key.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1660910929214/tWoCVvZce.png" alt="mpesa2.png" /></p>
<p>Save the consumer key and the secret key in the .env file</p>
<pre><code class="lang-sh">SAFARICOM_CONSUMER_SECRET=paste_secret_here
SAFARICOM_CONSUMER_KEY=paste_key_here
</code></pre>
<p>On the navbar, switch to the APIS tab, then click on the simulate button on the Mpesa express card</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1660910201989/iG24LvJGS.png" alt="mpesa5.png" /></p>
<p>On the simulator on the right, choose the TestApp.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1660910600510/dkycV4z6S.png" alt="mpesa7.png" /></p>
<p>Save the business short code and the passkey in the .env</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1660910313999/Io1o326rh.png" alt="mpesa6.png" /></p>
<p>The complete .env now looks as follows</p>
<pre><code class="lang-sh">SAFARICOM_CONSUMER_SECRET=paste_secret_here
SAFARICOM_CONSUMER_KEY=paste_key_here
PASS_KEY=paste_passkey_here
BUSINESS_SHORT_CODE=paste_shortcode_here
PORT=3000 <span class="hljs-comment"># port your server will run on</span>
</code></pre>
<p>The project dependencies can be installed using npm on the root folder at the terminal using</p>
<pre><code class="lang-sh"> npm install cors dotenv express request ngrok
</code></pre>
<ul>
<li><p>Cors - package for providing a Connect/Express middleware that can be used to enable CORS .</p>
</li>
<li><p>dotenv - loads environment variables from .env file</p>
</li>
<li><p>express - minimalist web framework</p>
</li>
<li><p>request - making HTTP calls</p>
</li>
<li><p>ngrok - node wrapper for ngrok</p>
</li>
</ul>
<h1 id="heading-use-case">Use Case</h1>
<p>Let's dive into the code</p>
<iframe src="https://giphy.com/embed/CjmvTCZf2U3p09Cn0h" width="480" height="452" class="giphy-embed"></iframe>

<p><a target="_blank" href="https://giphy.com/gifs/leroypatterson-cat-glasses-CjmvTCZf2U3p09Cn0h">via GIPHY</a></p>
<p>The code you will cover assumes the user will pay for an order identified by an Order_ID. You will initiate a payment request to the end user for that specific Order_ID. You will also implement a route that confirms the payment was made.</p>
<h1 id="heading-server">Server</h1>
<p>Add the following code in the server.js</p>
<pre><code class="lang-js"><span class="hljs-keyword">import</span> express <span class="hljs-keyword">from</span> <span class="hljs-string">"express"</span>;
<span class="hljs-keyword">import</span> cors <span class="hljs-keyword">from</span> <span class="hljs-string">"cors"</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">'dotenv/config'</span>

<span class="hljs-comment">// initialize express</span>
<span class="hljs-keyword">const</span> app = express()

<span class="hljs-comment">// middlewares</span>
app.use(express.json())
app.use(cors())

<span class="hljs-comment">// import routes</span>
<span class="hljs-keyword">import</span> lipaNaMpesaRoutes <span class="hljs-keyword">from</span> <span class="hljs-string">"./routes/routes.lipanampesa.js"</span>
app.use(<span class="hljs-string">'/api'</span>,lipaNaMpesaRoutes)

<span class="hljs-keyword">const</span> port = process.env.PORT

app.listen(port, <span class="hljs-function">() =&gt;</span> {
    <span class="hljs-built_in">console</span>.log(<span class="hljs-string">`App listening on port <span class="hljs-subst">${port}</span>`</span>)
})
</code></pre>
<p>In the above code, you import relevant libraries, initialize express, and pass the express.json and the cors middleware. In this case, you then import the routes and start the server on the port number in the .env; Port 3000.</p>
<h1 id="heading-routes">Routes</h1>
<p>Moving to the routes.lipanampesa.js, the code in this file is as follows</p>
<pre><code class="lang-js"><span class="hljs-keyword">import</span> express <span class="hljs-keyword">from</span> <span class="hljs-string">'express'</span>
<span class="hljs-keyword">const</span> router = express.Router()
<span class="hljs-keyword">import</span> {
    initiateSTKPush,
    stkPushCallback,
    confirmPayment
} <span class="hljs-keyword">from</span> <span class="hljs-string">"../controllers/controllers.lipanampesa.js"</span>;


<span class="hljs-keyword">import</span> {accessToken} <span class="hljs-keyword">from</span> <span class="hljs-string">"../middlewares/middlewares.generateAccessToken.js"</span>;

router.route(<span class="hljs-string">'/stkPush'</span>).post(accessToken,initiateSTKPush)
router.route(<span class="hljs-string">'/stkPushCallback/:Order_ID'</span>).post(stkPushCallback)
router.route(<span class="hljs-string">'/confirmPayment/:CheckoutRequestID'</span>).post(accessToken,confirmPayment)

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> router
</code></pre>
<p>In the routes file, you initialize the express router and import functions from the controllers.lipanampesa.js file (To be covered in the next section). You also import the accessToken middleware function. This middleware will be used for the authorization of all our Safaricom requests. Lastly, we create three post-request routes that will be used.</p>
<ul>
<li><p>The /stkPush route will initiate stk push popup on the users phone.</p>
</li>
<li><p>The /stkPushCallback/:Order_ID route will be the route Safaricom sends the results of the stk push.</p>
</li>
<li><p>The /confirmPayment/:CheckoutRequestID route will use the CheckoutRequestID to confirm payment details. The CheckoutRequestID comes from successfully executing the stk push.</p>
</li>
</ul>
<h1 id="heading-generating-the-access-token">Generating the Access Token</h1>
<p>The code in the middlewares.generateAccessToken.js is as follows</p>
<pre><code class="lang-js"><span class="hljs-keyword">import</span> request <span class="hljs-keyword">from</span> <span class="hljs-string">"request"</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">'dotenv/config'</span>

<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> accessToken = <span class="hljs-function">(<span class="hljs-params">req, res, next</span>)=&gt;</span> {
    <span class="hljs-keyword">try</span>{

        <span class="hljs-keyword">const</span> url = <span class="hljs-string">"https://sandbox.safaricom.co.ke/oauth/v1/generate?grant_type=client_credentials"</span>
        <span class="hljs-keyword">const</span> auth = <span class="hljs-keyword">new</span> Buffer.from(<span class="hljs-string">`<span class="hljs-subst">${process.env.SAFARICOM_CONSUMER_KEY}</span>:<span class="hljs-subst">${process.env.SAFARICOM_CONSUMER_SECRET}</span>`</span>).toString(<span class="hljs-string">'base64'</span>);

        request(
            {
                <span class="hljs-attr">url</span>: url,
                <span class="hljs-attr">headers</span>: {
                    <span class="hljs-string">"Authorization"</span>: <span class="hljs-string">"Basic "</span> + auth
                }
            },
            <span class="hljs-function">(<span class="hljs-params">error, response, body</span>) =&gt;</span> {
                <span class="hljs-keyword">if</span> (error) {
                    res.status(<span class="hljs-number">401</span>).send({
                        <span class="hljs-string">"message"</span>: <span class="hljs-string">'Something went wrong when trying to process your payment'</span>,
                        <span class="hljs-string">"error"</span>:error.message
                    })
                }
                <span class="hljs-keyword">else</span> {
                    req.safaricom_access_token = <span class="hljs-built_in">JSON</span>.parse(body).access_token
                    next()
                }
            }
        )
    }<span class="hljs-keyword">catch</span> (error) {

        <span class="hljs-built_in">console</span>.error(<span class="hljs-string">"Access token error "</span>, error)
        res.status(<span class="hljs-number">401</span>).send({
            <span class="hljs-string">"message"</span>: <span class="hljs-string">'Something went wrong when trying to process your payment'</span>,
            <span class="hljs-string">"error"</span>:error.message
        })
    }

}
</code></pre>
<p>The middleware requests https://sandbox.safaricom.co.ke/oauth/v1/generate?grant_type=client_credentials using the consumer key and consumer secret parsed to base64 as the authorization header. The request returns an access token that we append to the original request as req.safaricom_access_token to be used in the controller that the request will be passed along.</p>
<h1 id="heading-utils">Utils</h1>
<p>Before moving to the controllers, you will need a utility function that converts DateTime to the format YearMonthDayHourMinuteSecond</p>
<pre><code class="lang-sh">timestamp -&gt; YearMonthDayHourMinuteSecond
</code></pre>
<p>For example, the date conversion is as shown below</p>
<pre><code class="lang-sh">Date -&gt; 8/20/2022, 9:17:44 AM
Timestamp -&gt; 20220820091744
</code></pre>
<p>The code to do this in the utils.timestamp.js is as follows</p>
<pre><code class="lang-js"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">parseDate</span>(<span class="hljs-params">val</span>) </span>{
    <span class="hljs-keyword">return</span> (val &lt; <span class="hljs-number">10</span>) ? <span class="hljs-string">"0"</span> + val : val;
}

<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span>  getTimestamp = <span class="hljs-function">() =&gt;</span> {

    <span class="hljs-keyword">const</span> dateString  = <span class="hljs-keyword">new</span> <span class="hljs-built_in">Date</span>().toLocaleString(<span class="hljs-string">"en-us"</span>, {<span class="hljs-attr">timeZone</span>: <span class="hljs-string">"Africa/Nairobi"</span>})
    <span class="hljs-keyword">const</span> dateObject = <span class="hljs-keyword">new</span> <span class="hljs-built_in">Date</span>(dateString);
    <span class="hljs-keyword">const</span> month  = parseDate(dateObject.getMonth() + <span class="hljs-number">1</span>);
    <span class="hljs-keyword">const</span> day  = parseDate(dateObject.getDate());
    <span class="hljs-keyword">const</span> hour = parseDate(dateObject.getHours());
    <span class="hljs-keyword">const</span> minute = parseDate(dateObject.getMinutes());
    <span class="hljs-keyword">const</span> second = parseDate(dateObject.getSeconds());
    <span class="hljs-keyword">return</span> dateObject.getFullYear() + <span class="hljs-string">""</span> + month + <span class="hljs-string">""</span> + day + <span class="hljs-string">""</span> +
        hour + <span class="hljs-string">""</span> + minute + <span class="hljs-string">""</span> + second;
}
</code></pre>
<p>The parseDate is a helper function that appends a zero on values less than 10. For example, August the 8th month is converted to 08, 2A.M to 02, etc. Values greater than 10 remain unchanged.</p>
<p>The getTimestamp function gets the current local date time as a string and then converts it to a date object. We get the month, day, hour, minute, and second from the date object. Lastly, the function returns a timestamp in the form YearMonthDayHourMinuteSecond.</p>
<h1 id="heading-controllers">Controllers</h1>
<p>The first function on the controllers.lipanampesa.js :</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">import</span> request <span class="hljs-keyword">from</span> <span class="hljs-string">"request"</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">'dotenv/config'</span>
<span class="hljs-keyword">import</span> {getTimestamp} <span class="hljs-keyword">from</span> <span class="hljs-string">"../Utils/utils.timestamp.js"</span>;
<span class="hljs-keyword">import</span> ngrok <span class="hljs-keyword">from</span> <span class="hljs-string">'ngrok'</span>

<span class="hljs-comment">// @desc initiate stk push</span>
<span class="hljs-comment">// @method POST</span>
<span class="hljs-comment">// @route /stkPush</span>
<span class="hljs-comment">// @access public</span>
<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> initiateSTKPush = <span class="hljs-keyword">async</span>(req, res) =&gt; {
    <span class="hljs-keyword">try</span>{

        <span class="hljs-keyword">const</span> {amount, phone,Order_ID} = req.body
        <span class="hljs-keyword">const</span> url = <span class="hljs-string">"https://sandbox.safaricom.co.ke/mpesa/stkpush/v1/processrequest"</span>
        <span class="hljs-keyword">const</span> auth = <span class="hljs-string">"Bearer "</span> + req.safaricom_access_token

        <span class="hljs-keyword">const</span> timestamp = getTimestamp()
        <span class="hljs-comment">//shortcode + passkey + timestamp</span>
        <span class="hljs-keyword">const</span> password = <span class="hljs-keyword">new</span> Buffer.from(process.env.BUSINESS_SHORT_CODE + process.env.PASS_KEY + timestamp).toString(<span class="hljs-string">'base64'</span>)
        <span class="hljs-comment">// create callback url</span>
        <span class="hljs-keyword">const</span> callback_url = <span class="hljs-keyword">await</span> ngrok.connect(process.env.PORT);
        <span class="hljs-keyword">const</span> api = ngrok.getApi();
        <span class="hljs-keyword">await</span> api.listTunnels();


        <span class="hljs-built_in">console</span>.log(<span class="hljs-string">"callback "</span>,callback_url)
        request(
            {
                <span class="hljs-attr">url</span>: url,
                <span class="hljs-attr">method</span>: <span class="hljs-string">"POST"</span>,
                <span class="hljs-attr">headers</span>: {
                    <span class="hljs-string">"Authorization"</span>: auth
                },
                <span class="hljs-attr">json</span>: {
                    <span class="hljs-string">"BusinessShortCode"</span>: process.env.BUSINESS_SHORT_CODE,
                    <span class="hljs-string">"Password"</span>: password,
                    <span class="hljs-string">"Timestamp"</span>: timestamp,
                    <span class="hljs-string">"TransactionType"</span>: <span class="hljs-string">"CustomerPayBillOnline"</span>,
                    <span class="hljs-string">"Amount"</span>: amount,
                    <span class="hljs-string">"PartyA"</span>: phone,
                    <span class="hljs-string">"PartyB"</span>: process.env.BUSINESS_SHORT_CODE,
                    <span class="hljs-string">"PhoneNumber"</span>: phone,
                    <span class="hljs-string">"CallBackURL"</span>: <span class="hljs-string">`<span class="hljs-subst">${callback_url}</span>/api/stkPushCallback/<span class="hljs-subst">${Order_ID}</span>`</span>,
                    <span class="hljs-string">"AccountReference"</span>: <span class="hljs-string">"Wamaitha Online Shop"</span>,
                    <span class="hljs-string">"TransactionDesc"</span>: <span class="hljs-string">"Paid online"</span>
                }
            },
            <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params">e, response, body</span>) </span>{
                <span class="hljs-keyword">if</span> (e) {
                    <span class="hljs-built_in">console</span>.error(e)
                    res.status(<span class="hljs-number">503</span>).send({
                        <span class="hljs-attr">message</span>:<span class="hljs-string">"Error with the stk push"</span>,
                        <span class="hljs-attr">error</span> : e
                    })
                } <span class="hljs-keyword">else</span> {
                    res.status(<span class="hljs-number">200</span>).json(body)
                }
            }
        )
    }<span class="hljs-keyword">catch</span> (e) {
        <span class="hljs-built_in">console</span>.error(<span class="hljs-string">"Error while trying to create LipaNaMpesa details"</span>,e)
        res.status(<span class="hljs-number">503</span>).send({
            <span class="hljs-attr">message</span>:<span class="hljs-string">"Something went wrong while trying to create LipaNaMpesa details. Contact admin"</span>,
            <span class="hljs-attr">error</span> : e
        })
    }
}
</code></pre>
<p>The function will initiate the stk push to the end user. The code destructures the request body to extract the amount, phone, and Order_ID. You then send a request to <a target="_blank" href="https://sandbox.safaricom.co.ke/mpesa/stkpush/v1/processrequest">https://sandbox.safaricom.co.ke/mpesa/stkpush/v1/processrequest</a> using the access token as the authorization header and the shortcode + passkey + timestamp as the password. This request requires a callback URL which we create using ngrok. Ngrok exposes your server to the internet. You also initialize a tunnel listener that will log any logs from the callback function to the terminal.</p>
<p>Running this request on post man yields:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1660981906377/8gZ1ZjqlJ.png" alt="mpesa8.png" /></p>
<p>On the terminal, you will see a callback logged</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1660979195705/2SUyYt-G5.JPG" alt="mpesa9.JPG" /></p>
<p>Lastly, the phone number used will receive an stk push as shown</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1660979354269/MySZC5WSj.jpeg" alt="mpesa3.jpeg" class="image--center mx-auto" /></p>
<p>The second function on the controllers.lipanampesa.js :</p>
<pre><code class="lang-javascript">
<span class="hljs-comment">// @desc callback route Safaricom will post transaction status</span>
<span class="hljs-comment">// @method POST</span>
<span class="hljs-comment">// @route /stkPushCallback/:Order_ID</span>
<span class="hljs-comment">// @access public</span>
<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> stkPushCallback = <span class="hljs-keyword">async</span>(req, res) =&gt; {
    <span class="hljs-keyword">try</span>{

    <span class="hljs-comment">//    order id</span>
        <span class="hljs-keyword">const</span> {Order_ID} = req.params

        <span class="hljs-comment">//callback details</span>

        <span class="hljs-keyword">const</span> {
            MerchantRequestID,
            CheckoutRequestID,
            ResultCode,
            ResultDesc,
            CallbackMetadata
                 }   = req.body.Body.stkCallback

    <span class="hljs-comment">//     get the meta data from the meta</span>
        <span class="hljs-keyword">const</span> meta = <span class="hljs-built_in">Object</span>.values(<span class="hljs-keyword">await</span> CallbackMetadata.Item)
        <span class="hljs-keyword">const</span> PhoneNumber = meta.find(<span class="hljs-function"><span class="hljs-params">o</span> =&gt;</span> o.Name === <span class="hljs-string">'PhoneNumber'</span>).Value.toString()
        <span class="hljs-keyword">const</span> Amount = meta.find(<span class="hljs-function"><span class="hljs-params">o</span> =&gt;</span> o.Name === <span class="hljs-string">'Amount'</span>).Value.toString()
        <span class="hljs-keyword">const</span> MpesaReceiptNumber = meta.find(<span class="hljs-function"><span class="hljs-params">o</span> =&gt;</span> o.Name === <span class="hljs-string">'MpesaReceiptNumber'</span>).Value.toString()
        <span class="hljs-keyword">const</span> TransactionDate = meta.find(<span class="hljs-function"><span class="hljs-params">o</span> =&gt;</span> o.Name === <span class="hljs-string">'TransactionDate'</span>).Value.toString()

        <span class="hljs-comment">// do something with the data</span>
        <span class="hljs-built_in">console</span>.log(<span class="hljs-string">"-"</span>.repeat(<span class="hljs-number">20</span>),<span class="hljs-string">" OUTPUT IN THE CALLBACK "</span>, <span class="hljs-string">"-"</span>.repeat(<span class="hljs-number">20</span>))
        <span class="hljs-built_in">console</span>.log(<span class="hljs-string">`
            Order_ID : <span class="hljs-subst">${Order_ID}</span>,
            MerchantRequestID : <span class="hljs-subst">${MerchantRequestID}</span>,
            CheckoutRequestID: <span class="hljs-subst">${CheckoutRequestID}</span>,
            ResultCode: <span class="hljs-subst">${ResultCode}</span>,
            ResultDesc: <span class="hljs-subst">${ResultDesc}</span>,
            PhoneNumber : <span class="hljs-subst">${PhoneNumber}</span>,
            Amount: <span class="hljs-subst">${Amount}</span>, 
            MpesaReceiptNumber: <span class="hljs-subst">${MpesaReceiptNumber}</span>,
            TransactionDate : <span class="hljs-subst">${TransactionDate}</span>
        `</span>)

        res.json(<span class="hljs-literal">true</span>)

    }<span class="hljs-keyword">catch</span> (e) {
        <span class="hljs-built_in">console</span>.error(<span class="hljs-string">"Error while trying to update LipaNaMpesa details from the callback"</span>,e)
        res.status(<span class="hljs-number">503</span>).send({
            <span class="hljs-attr">message</span>:<span class="hljs-string">"Something went wrong with the callback"</span>,
            <span class="hljs-attr">error</span> : e.message
        })
    }
}
</code></pre>
<p>The callback function simply destructures the request body and prints it to the console. At this point, you can save the response to any database you choose.</p>
<p>Rerunning the /stkpush endpoint on postman yields the following on the terminal:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1660981388146/uJGvreMRz.JPG" alt="mpesa10.JPG" /></p>
<p>The last part is confirming payments using the CheckoutRequestID.</p>
<p>The final function in the controller file is as shown</p>
<pre><code class="lang-javascript"><span class="hljs-comment">// @desc Check from safaricom servers the status of a transaction</span>
<span class="hljs-comment">// @method GET</span>
<span class="hljs-comment">// @route /confirmPayment/:CheckoutRequestID</span>
<span class="hljs-comment">// @access public</span>
<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> confirmPayment = <span class="hljs-keyword">async</span>(req, res) =&gt; {
    <span class="hljs-keyword">try</span>{


        <span class="hljs-keyword">const</span> url = <span class="hljs-string">"https://sandbox.safaricom.co.ke/mpesa/stkpushquery/v1/query"</span>
        <span class="hljs-keyword">const</span> auth = <span class="hljs-string">"Bearer "</span> + req.safaricom_access_token

        <span class="hljs-keyword">const</span> timestamp = getTimestamp()
        <span class="hljs-comment">//shortcode + passkey + timestamp</span>
        <span class="hljs-keyword">const</span> password = <span class="hljs-keyword">new</span> Buffer.from(process.env.BUSINESS_SHORT_CODE + process.env.PASS_KEY + timestamp).toString(<span class="hljs-string">'base64'</span>)


        request(
            {
                <span class="hljs-attr">url</span>: url,
                <span class="hljs-attr">method</span>: <span class="hljs-string">"POST"</span>,
                <span class="hljs-attr">headers</span>: {
                    <span class="hljs-string">"Authorization"</span>: auth
                },
                <span class="hljs-attr">json</span>: {
                    <span class="hljs-string">"BusinessShortCode"</span>:process.env.BUSINESS_SHORT_CODE,
                    <span class="hljs-string">"Password"</span>: password,
                    <span class="hljs-string">"Timestamp"</span>: timestamp,
                    <span class="hljs-string">"CheckoutRequestID"</span>: req.params.CheckoutRequestID,

                }
            },
            <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params">error, response, body</span>) </span>{
                <span class="hljs-keyword">if</span> (error) {
                    <span class="hljs-built_in">console</span>.log(error)
                    res.status(<span class="hljs-number">503</span>).send({
                        <span class="hljs-attr">message</span>:<span class="hljs-string">"Something went wrong while trying to create LipaNaMpesa details. Contact admin"</span>,
                        <span class="hljs-attr">error</span> : error
                    })
                } <span class="hljs-keyword">else</span> {
                    res.status(<span class="hljs-number">200</span>).json(body)
                }
            }
        )
    }<span class="hljs-keyword">catch</span> (e) {
        <span class="hljs-built_in">console</span>.error(<span class="hljs-string">"Error while trying to create LipaNaMpesa details"</span>,e)
        res.status(<span class="hljs-number">503</span>).send({
            <span class="hljs-attr">message</span>:<span class="hljs-string">"Something went wrong while trying to create LipaNaMpesa details. Contact admin"</span>,
            <span class="hljs-attr">error</span> : e
        })
    }
}
</code></pre>
<p>The function takes a similar construct as the initiate stkpush function, but on the JSON body, we pass in the CheckoutRequestID, which we get from the request params. Executing the function on post man confirms the payment, as shown below</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1660982096403/AC8iozkSr.png" alt="mpesa11.png" /></p>
<p>You did it!</p>
<iframe src="https://giphy.com/embed/ely3apij36BJhoZ234" width="480" height="480" class="giphy-embed"></iframe>

<p><a target="_blank" href="https://giphy.com/gifs/good-job-congratulations-otter-ely3apij36BJhoZ234">via GIPHY</a></p>
<p>The full code can be found <a target="_blank" href="https://github.com/wamaithaNyamu/Lipa-na-Mpesa-STK-Push-">here</a></p>
]]></content:encoded></item><item><title><![CDATA[Python Lists]]></title><description><![CDATA[Why use type hints in python? Python being a dynamically typed language means that variable types are determined during run time. However, declaring the expected type can be beneficial in very large projects as the code becomes self-documenting and m...]]></description><link>https://wamaithanyamu.com/python-lists</link><guid isPermaLink="true">https://wamaithanyamu.com/python-lists</guid><category><![CDATA[algorithms]]></category><category><![CDATA[data structures]]></category><category><![CDATA[Lists]]></category><dc:creator><![CDATA[Wamaitha ]]></dc:creator><pubDate>Mon, 04 Jul 2022 14:08:19 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1672516425470/6bdb91ae-55d8-4ccb-ae61-57bee84f7c76.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<blockquote>
<p>Why use type hints in python? Python being a dynamically typed language means that variable types are determined during run time. However, declaring the expected type can be beneficial in very large projects as the code becomes self-documenting and make it more readable. FAANG also expect their python developers to know how to use type hints in code. Google has a Python style guide that can be found <a target="_blank" href="https://google.github.io/styleguide/pyguide.html">here</a>.</p>
</blockquote>
<h2 id="heading-initializing-lists">Initializing Lists</h2>
<p>Lists are a built-in data type in Python used to store multiple items in a single variable and are initialized in two ways :</p>
<ol>
<li>Using square brackets :</li>
</ol>
<pre><code class="lang-py"><span class="hljs-comment"># Import the type hint module</span>
<span class="hljs-comment"># Any means the List will take in any type of data type</span>
<span class="hljs-keyword">from</span> typing <span class="hljs-keyword">import</span> List, Any

<span class="hljs-comment"># Initialised using square brackets</span>

my_empty_list: List[Any] = []
print(my_empty_list)

my_list_of_integers: List[int] =  [<span class="hljs-number">1</span>,<span class="hljs-number">2</span>,<span class="hljs-number">3</span>,<span class="hljs-number">4</span>]
print(my_list_of_integers)

my_list_of_floats: List[float]  = [<span class="hljs-number">1.2</span>, <span class="hljs-number">3.4</span>, <span class="hljs-number">5.6</span>]
print(my_list_of_floats)

my_list_of_strings: List[str]  =[<span class="hljs-string">"this"</span>, <span class="hljs-string">"is"</span> ,<span class="hljs-string">"a"</span> ,<span class="hljs-string">"string"</span>]
print(my_list_of_strings)

my_list_of_booleans: List[bool]  =[<span class="hljs-literal">True</span>,<span class="hljs-literal">False</span>,<span class="hljs-literal">False</span>,<span class="hljs-literal">True</span>]
print(my_list_of_booleans)

my_mixed_list: List[Any] = [<span class="hljs-number">1</span>, <span class="hljs-string">"Hello"</span>, <span class="hljs-literal">False</span>, <span class="hljs-number">1.23</span>]
print(my_mixed_list)
</code></pre>
<p>Output:</p>
<pre><code class="lang-shell">[]
[1, 2, 3, 4]
[1.2, 3.4, 5.6]
['this', 'is', 'a', 'string']
[True, False, False, True]
[1, 'Hello', False, 1.23]
</code></pre>
<ol>
<li>Using the list constructor :</li>
</ol>
<pre><code class="lang-py"><span class="hljs-comment"># Import the type hint module</span>
<span class="hljs-comment"># Any means the List will take in any type of data type</span>
<span class="hljs-keyword">from</span> typing <span class="hljs-keyword">import</span> List, Any

<span class="hljs-comment"># Initialized using the list constructor </span>

my_empty_list: List[Any] = list()
print(my_empty_list)

my_list_of_integers: List[int] = list((<span class="hljs-number">1</span>,<span class="hljs-number">2</span>,<span class="hljs-number">3</span>,<span class="hljs-number">4</span>))
print(my_list_of_integers)

my_list_of_floats: List[float] =list ((<span class="hljs-number">1.2</span>, <span class="hljs-number">3.4</span>, <span class="hljs-number">5.6</span>))
print(my_list_of_floats)

my_list_of_strings: List[str]=list ((<span class="hljs-string">"this"</span>, <span class="hljs-string">"is"</span> ,<span class="hljs-string">"a"</span> ,<span class="hljs-string">"string"</span>))
print(my_list_of_strings)

my_list_of_booleans: List[bool] =list((<span class="hljs-literal">True</span>, <span class="hljs-literal">False</span>,<span class="hljs-literal">False</span>,<span class="hljs-literal">True</span>))
print(my_list_of_booleans)

my_mixed_list: List[Any]= list((<span class="hljs-number">1</span>, <span class="hljs-string">"Hello"</span>, <span class="hljs-literal">False</span>, <span class="hljs-number">1.23</span>))
print(my_mixed_list)
</code></pre>
<p>Output:</p>
<pre><code class="lang-shell">[]
[1, 2, 3, 4]
[1.2, 3.4, 5.6]
['this', 'is', 'a', 'string']
[True, False, False, True]
[1, 'Hello', False, 1.23]
</code></pre>
<h2 id="heading-characteristics-of-list-items">Characteristics of list items</h2>
<p>Items in a list have the following characteristics :</p>
<ol>
<li>Order</li>
</ol>
<p>The list items are said to be ordered when they follow each other in a set manner. By default, new items are added to the very end of the list</p>
<ol>
<li>Can hold duplicates</li>
</ol>
<p>Since the list has an order and every item in the list has a unique index, we can duplicate values within the list as much as we want.</p>
<p>For example ```py my_list = [1,2,2,5,6,3,4]</p>
<pre><code class="lang-bash">The two 2<span class="hljs-string">'s are in different positions yet have the same value.

3. Can be changed

We can add, remove and change elements in a list. 

## Accessing items in a list

### Positive indexing

&gt; Positive indexing means we start from the beginning of the list

Every item in the list has a unique index. Index values begin at zero, meaning that the first element in a list is in index zero. We use square brackets containing the index of the list we want. 

For example,

Given the list:
```py
# list of only integers
my_list: List[int] = [1,2,2,5,6,3,4]</span>
</code></pre>
<p>We can access the first element in the list (1) using :</p>
<pre><code class="lang-py"><span class="hljs-comment"># list of only integers</span>
my_list: List[int] = [<span class="hljs-number">1</span>,<span class="hljs-number">2</span>,<span class="hljs-number">2</span>,<span class="hljs-number">5</span>,<span class="hljs-number">6</span>,<span class="hljs-number">3</span>,<span class="hljs-number">4</span>]
<span class="hljs-comment"># item in the list of integers is of type int(integer)</span>
first_item: int= my_list[<span class="hljs-number">0</span>]
print(first_item)
</code></pre>
<p>The general access expression is :</p>
<pre><code class="lang-sh">list_item_you_want: List[<span class="hljs-built_in">type</span> of list items the list will contain] = the_list[list_item_index]
</code></pre>
<h3 id="heading-negative-indexing">Negative indexing</h3>
<blockquote>
<p>Negative indexing means we start from the end of the list.</p>
</blockquote>
<p>We can also access the list items from the last element using negative indexing.</p>
<p>For example,</p>
<p>Given the list :</p>
<pre><code class="lang-py"><span class="hljs-comment"># List of only strings</span>
adele_lyrics: List[str]  = [<span class="hljs-string">"Hello"</span>, <span class="hljs-string">"from"</span> ,<span class="hljs-string">"the"</span> ,<span class="hljs-string">"other"</span>, <span class="hljs-string">"side"</span>]
</code></pre>
<p>To get the last element, we can use negative indexing : ```py</p>
<h1 id="heading-list-of-only-strings">List of only strings</h1>
<p>adele_lyrics: List[str] = ["Hello", "from" ,"the" ,"other", "side"]</p>
<h1 id="heading-the-last-element-of-the-string-which-is-of-type-strstring">The last element of the string which is of type str(string)</h1>
<p>last_word: str = adele_lyrics[-1]</p>
<p>print(last_word) <code>Output :</code>sh side <code>To get the second last element :</code>py</p>
<h1 id="heading-list-of-only-strings-1">List of only strings</h1>
<p>adele_lyrics: List[str] = ["Hello", "from" ,"the" ,"other", "side"]</p>
<h1 id="heading-the-second-last-element-of-the-string-which-is-of-type-strstring">The second last element of the string which is of type str(string)</h1>
<p>second_last_word: str = adele_lyrics[-2]</p>
<p>print(second_last_word) <code>Output :</code>sh other ```</p>
<p><img src="https://media.giphy.com/media/I1Nf4Z7X7YQAgmQeyQ/giphy.gif" alt="You get it" class="image--center mx-auto" /></p>
<h3 id="heading-range-of-indexes">Range of indexes</h3>
<p>A range of indexes helps get a sublist from the list.</p>
<p>For example:</p>
<p>Given the list :</p>
<pre><code class="lang-py"><span class="hljs-comment"># List of fruits</span>

fruits: List[str] = [<span class="hljs-string">"oranges"</span>, <span class="hljs-string">"grapefruits"</span>,<span class="hljs-string">"mandarins "</span>,<span class="hljs-string">"limes"</span>,<span class="hljs-string">"strawberries"</span>,<span class="hljs-string">"raspberries"</span>,<span class="hljs-string">"blueberries"</span>,<span class="hljs-string">"watermelons"</span>,<span class="hljs-string">"rockmelons "</span>,<span class="hljs-string">"passionfruit"</span>,<span class="hljs-string">"banana"</span>,]
</code></pre>
<p>From the list of the fruits we can tell that we have at least three categories of fruits on the list :</p>
<ol>
<li><p>Citrus fruits -&gt; oranges to limes</p>
</li>
<li><p>Berries -&gt; strawberries to blueberries</p>
</li>
<li><p>Melons -&gt; watermelons to rockmelons</p>
</li>
</ol>
<p>Using range indexing we can get the three categories. The range indexing general expression is :</p>
<pre><code class="lang-sh">sublist = the_list[start_index: end_index]
</code></pre>
<blockquote>
<p>Note that the end_index is not inclusive. For example, a start index of 2 and an end index of 5 will only return elements from the second index to the fourth index.</p>
</blockquote>
<p>To get the :</p>
<ul>
<li>Citrus fruits</li>
</ul>
<pre><code class="lang-py"><span class="hljs-comment"># get the citrus fruits oranges(0) to limes(3)</span>
<span class="hljs-comment"># to include limes in the sublist we add one to the limes index</span>
citrus_fruits: List[str] = fruits[<span class="hljs-number">0</span>:<span class="hljs-number">4</span>]
print(citrus_fruits)
</code></pre>
<p>Output:</p>
<pre><code class="lang-sh">[<span class="hljs-string">'oranges'</span>, <span class="hljs-string">'grapefruits'</span>, <span class="hljs-string">'mandarins '</span>, <span class="hljs-string">'limes'</span>]
</code></pre>
<ul>
<li>Berries</li>
</ul>
<pre><code class="lang-py"><span class="hljs-comment"># get the berries strawberries(4) to blueberries(6)</span>
<span class="hljs-comment"># to include blueberries in the sublist we add one to the blueberries index</span>
berries_fruits: List[str] = fruits[<span class="hljs-number">4</span>:<span class="hljs-number">7</span>]
print(berries_fruits)
</code></pre>
<p>Output: <code>['strawberries', 'raspberries', 'blueberries']</code></p>
<ul>
<li>Melons</li>
</ul>
<pre><code class="lang-py"><span class="hljs-comment"># get the melons watermelons to rockmelons</span>
<span class="hljs-comment"># to include rockmelons in the sublist we add one to the rockmelons index</span>

melon_fruits: List[str] = fruits[<span class="hljs-number">7</span>:<span class="hljs-number">9</span>]
print(melon_fruits)
</code></pre>
<p>Output:</p>
<pre><code class="lang-bash">[<span class="hljs-string">'watermelons'</span>, <span class="hljs-string">'rockmelons '</span>]
</code></pre>
<p>We can also use negative indexing to get the melons.</p>
<p>To include rockmelons in the sublist we can index from watermelons to the passionfruit index which can either be 9 or -2</p>
<pre><code class="lang-py"><span class="hljs-comment"># get the melons watermelons to rockmelons using negative indexing</span>

melon_fruits: List[str] = fruits[<span class="hljs-number">7</span>:<span class="hljs-number">-2</span>]
print(melon_fruits)
</code></pre>
<p>Output:</p>
<pre><code class="lang-bash">[<span class="hljs-string">'watermelons'</span>, <span class="hljs-string">'rockmelons '</span>]
</code></pre>
<h2 id="heading-length-of-a-list">Length of a list</h2>
<p>There are instances when we may need to know how many items are present in a list.</p>
<p>Python has an inbuilt function for this :</p>
<pre><code class="lang-bash">numbers_list: List[int] = [0,1,2,3,4,5,6,7,8,9,10]
number_of_list_items: int = len(numbers_list)
<span class="hljs-built_in">print</span>(number_of_list_items)
</code></pre>
<p>Output:</p>
<pre><code class="lang-bash">11
</code></pre>
<p>Example use case:</p>
<p>Given a list get all the values in a list that are in an even index.</p>
<pre><code class="lang-py"><span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">get_all_values_in_even_indexes</span>(<span class="hljs-params">the_list: List[Any]</span>) -&gt; List[Any]:</span>
    <span class="hljs-string">""" Get all values that are in even indexes in a list
    Args:
     the_list: List of values.

    Returns:
      List of all values in even indexes

    """</span>
    <span class="hljs-comment"># initialise empty list for even numbers</span>
    even_list: List[Any] = []
    <span class="hljs-comment"># loop over all the indexes in the list</span>
    <span class="hljs-keyword">for</span> index <span class="hljs-keyword">in</span> range(len(the_list)):
        <span class="hljs-comment"># if the index number when divided by 2 we get 0; ie its even we append the item to the even list</span>
        <span class="hljs-keyword">if</span> index % <span class="hljs-number">2</span> == <span class="hljs-number">0</span>:
            even_list.append(the_list[index])
    print(even_list)
    <span class="hljs-keyword">return</span> even_list


items_in_even_indexes = get_all_values_in_even_indexes([<span class="hljs-string">"Zero"</span>,<span class="hljs-string">"One"</span>, <span class="hljs-string">"Two"</span>, <span class="hljs-string">"Three"</span>, <span class="hljs-string">"Four"</span>, <span class="hljs-string">"Five"</span>, <span class="hljs-string">"Six"</span>])
</code></pre>
<p>Output</p>
<pre><code class="lang-sh">[<span class="hljs-string">'Zero'</span>, <span class="hljs-string">'Two'</span>, <span class="hljs-string">'Four'</span>, <span class="hljs-string">'Six'</span>]
</code></pre>
<h2 id="heading-list-manipulations">List manipulations</h2>
<h3 id="heading-changing-values-in-a-list">Changing values in a list</h3>
<p>To replace the value of a list item, we can refer to its index number and change the value.</p>
<p>Going back to the fruits example, to change limes to lemons we simply do:</p>
<pre><code class="lang-py">fruits: List[str] = [<span class="hljs-string">"oranges"</span>, <span class="hljs-string">"grapefruits"</span>, <span class="hljs-string">"mandarins "</span>, <span class="hljs-string">"limes"</span>, <span class="hljs-string">"strawberries"</span>, <span class="hljs-string">"raspberries"</span>, <span class="hljs-string">"blueberries"</span>,
                     <span class="hljs-string">"watermelons"</span>, <span class="hljs-string">"rockmelons "</span>, <span class="hljs-string">"passionfruit"</span>, <span class="hljs-string">"banana"</span>]
fruits[<span class="hljs-number">3</span>]: str = <span class="hljs-string">'lemons'</span>
</code></pre>
<p>Output:</p>
<pre><code class="lang-sh">[<span class="hljs-string">'oranges'</span>, <span class="hljs-string">'grapefruits'</span>, <span class="hljs-string">'mandarins '</span>, <span class="hljs-string">'lemons'</span>, <span class="hljs-string">'strawberries'</span>, <span class="hljs-string">'raspberries'</span>, <span class="hljs-string">'blueberries'</span>, <span class="hljs-string">'watermelons'</span>, <span class="hljs-string">'rockmelons '</span>, <span class="hljs-string">'passionfruit'</span>, <span class="hljs-string">'banana'</span>]
</code></pre>
<p>Using range indexing we can also change the values on a list. On the fruits list, we can change the berries to leafy greens as follows:</p>
<pre><code class="lang-py">leafy_greens: List[str]= [<span class="hljs-string">"lettuce"</span>,<span class="hljs-string">"spinach"</span>,<span class="hljs-string">"silverbeet"</span>]
fruits[<span class="hljs-number">4</span>:<span class="hljs-number">7</span>] = leafy_greens
print(fruits)
</code></pre>
<p>Output: <code>sh ['oranges', 'grapefruits', 'mandarins ', 'lemons', 'lettuce', 'spinach', 'silverbeet', 'watermelons', 'rockmelons ', 'passionfruit', 'banana']</code></p>
<h3 id="heading-adding-items-in-a-list">Adding items in a list</h3>
<p>We can also insert items in an index without replacing the existing values. In the fruits example, we can add a new citrus fruit after lemons without replacing lettuce.</p>
<pre><code class="lang-py">fruits.insert(<span class="hljs-number">4</span>,<span class="hljs-string">"pomelo"</span>)
print(fruits)
</code></pre>
<p>Output :</p>
<pre><code class="lang-sh">[<span class="hljs-string">'oranges'</span>, <span class="hljs-string">'grapefruits'</span>, <span class="hljs-string">'mandarins '</span>, <span class="hljs-string">'lemons'</span>, <span class="hljs-string">'pomelo'</span>, <span class="hljs-string">'lettuce'</span>, <span class="hljs-string">'spinach'</span>, <span class="hljs-string">'silverbeet'</span>, <span class="hljs-string">'watermelons'</span>, <span class="hljs-string">'rockmelons '</span>, <span class="hljs-string">'passionfruit'</span>, <span class="hljs-string">'banana'</span>]
</code></pre>
<p>To add items at the end of a list we use the append() method:</p>
]]></content:encoded></item><item><title><![CDATA[How to determine packages used in a Python project that has no requirements.txt]]></title><description><![CDATA[Have you ever cloned a python project on GitHub that did not include a requirements.txt and you could not make it run on your local machine due to compatibility issues? What if there was a solution that could crawl the whole project and generate a re...]]></description><link>https://wamaithanyamu.com/how-to-determine-packages-used-in-a-python-project-that-has-no-requirementstxt</link><guid isPermaLink="true">https://wamaithanyamu.com/how-to-determine-packages-used-in-a-python-project-that-has-no-requirementstxt</guid><category><![CDATA[Python]]></category><category><![CDATA[packages]]></category><category><![CDATA[pip]]></category><category><![CDATA[Requirements.txt]]></category><category><![CDATA[package management]]></category><dc:creator><![CDATA[Wamaitha ]]></dc:creator><pubDate>Sun, 30 Jan 2022 06:34:45 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1672516699872/519f5dc9-e517-4852-9573-6ff4af541e3f.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Have you ever cloned a python project on GitHub that did not include a requirements.txt and you could not make it run on your local machine due to compatibility issues? What if there was a solution that could crawl the whole project and generate a requirements.txt file for you for the packages used and their versions? Well, good news there is!</p>
<h2 id="heading-prerequisites">Prerequisites</h2>
<ul>
<li><p>You need to have python 3.5+ installed - Download python <a target="_blank" href="https://www.python.org/downloads/">here</a></p>
</li>
<li><p>Pip package installer- Download pip from <a target="_blank" href="https://pip.pypa.io/en/stable/cli/pip_download/">here</a></p>
</li>
<li><p>Venv for creating virtual environments - Download Venv from <a target="_blank" href="https://docs.python.org/3/library/venv.html">here</a></p>
</li>
</ul>
<h2 id="heading-working-with-projects-that-do-not-have-a-requirementstxt">Working with projects that do not have a requirements.txt</h2>
<p>I came across this issue when I was following machine learning tutorials on Microsft Learn. The GitHub repo only has the code with no requirements.txt. I was particularly interested in the code for extracting text from images and pdfs. The code looks as shown in the image below.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1643524215626/qIkxxSaJE.png" alt="mslearn.png" /></p>
<p>You can see there is no requirements.txt. We have to first clone the whole repo then navigate to the specific folder we want to work on</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1643524226722/ZC4OHN2Om.png" alt="repo.png" /></p>
<p>Start by cloning the whole repository.</p>
<pre><code class="lang-shell">git clone https://github.com/MicrosoftLearning/AI-102-AIEngineer.git
</code></pre>
<p>Then get into the specific folder we are interested in</p>
<pre><code class="lang-shell">cd AI-102-AIEngineer-master\20-ocr\Python\read-text\
</code></pre>
<p>The project structure is as shown below ``` AI-102-AIEngineer-master └───20-ocr | └───Python | | └───read-text | | | read-text.py | | | .env | | └───images | | | | Lincoln.jpg | | | | Note.jpg | | | | Rome.pdf</p>
<pre><code class="lang-bash">
<span class="hljs-comment">## Setting up an environment</span>

Setting up an environment <span class="hljs-keyword">for</span> a project ensures that the installs <span class="hljs-keyword">done</span> <span class="hljs-keyword">for</span> the project are within that environment without affecting global installs. This can also be useful when a specific package is only installed once on a specific project.
To <span class="hljs-built_in">set</span> up a venv environment:

```shell
python -m venv name-of-environment
</code></pre>
<p>Name the environment folder that you wish. I'll name mine venv. My current folder structure now looks like ``` AI-102-AIEngineer-master └───20-ocr | └───Python | | └───read-text | | | read-text.py | | | .env | | | venv | | └───images | | | | Lincoln.jpg | | | | Note.jpg | | | | Rome.pdf</p>
<pre><code class="lang-bash">
On windows activate the environment 
```shell
 <span class="hljs-built_in">cd</span> venv\Scripts\ &amp;&amp; activate &amp;&amp; <span class="hljs-built_in">cd</span> .. &amp;&amp; <span class="hljs-built_in">cd</span> ..
</code></pre>
<p>With the environment set, next, we need to install (Pigar)[https://pypi.org/project/pigar/]. Pigar is a tool used to generate requirements.txt for python projects. Install Pigar with pip</p>
<pre><code class="lang-shell">pip install pigar
</code></pre>
<p>To use pigar on our project simply run</p>
<pre><code class="lang-shell">pigar
</code></pre>
<p>You can find a generated requirements.txt on your project folder. Learn more about pigar from its <a target="_blank" href="https://pypi.org/project/pigar/">documentation</a>. With a requirements.txt, install all the dependencies using</p>
<pre><code class="lang-shell">pip install -r requirements.txt
</code></pre>
<p>Happy hacking!</p>
]]></content:encoded></item><item><title><![CDATA[Building a telegram bot that extracts text from images and documents using Azure Cognitive Services.]]></title><description><![CDATA[Youtube video tutorial
I was recently applying for my passport and was required to provide passport numbers for my parents. Problem was, they both misplaced them and the only option left was to visit a government office to have the numbers manually r...]]></description><link>https://wamaithanyamu.com/building-a-telegram-bot-that-extracts-text-from-images-and-documents-using-azure-cognitive-services</link><guid isPermaLink="true">https://wamaithanyamu.com/building-a-telegram-bot-that-extracts-text-from-images-and-documents-using-azure-cognitive-services</guid><category><![CDATA[Azure]]></category><category><![CDATA[AI]]></category><category><![CDATA[Machine Learning]]></category><category><![CDATA[Data Science]]></category><category><![CDATA[telegram]]></category><dc:creator><![CDATA[Wamaitha ]]></dc:creator><pubDate>Sat, 29 Jan 2022 18:34:41 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1672517538285/b92ee280-e7e8-414d-8492-f3b4e5e1bb5f.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p><a target="_blank" href="https://www.youtube.com/watch?v=sLQkK_jTvwA&amp;t">Youtube video tutorial</a></p>
<p>I was recently applying for my passport and was required to provide passport numbers for my parents. Problem was, they both misplaced them and the only option left was to visit a government office to have the numbers manually retrieved since my parents applied for passports before digitization. I had to wait for an hour before they could trace the numbers. As I sat there waiting, I couldn't help but wonder if the government could leverage current technology to digitize the documents that are still in hard copy. Although the government will not be using telegram, the approach used here has an endpoint that makes it interoperable with any system that accepts REST endpoints.</p>
<blockquote>
<p>For the visual learners, find a video walkthrough of this tutorial <a target="_blank" href="https://www.youtube.com/watch?v=sLQkK_jTvwA&amp;t">here</a></p>
</blockquote>
<h1 id="heading-demo">Demo</h1>
<p>What we will be building 👇🏾</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1643481378893/FzD0Y8wum.gif" alt="DEMO.gif" /></p>
<h1 id="heading-prerequisites">Prerequisites</h1>
<ul>
<li><p>Familiarity with python</p>
</li>
<li><p>Azure account - Create an azure account <a target="_blank" href="https://portal.azure.com/">here</a></p>
</li>
<li><p>Python 3.8+ - Install python from <a target="_blank" href="https://www.python.org/downloads/">here</a></p>
</li>
<li><p>Pip - Install python from here</p>
</li>
<li><p>Telegram account - Install python from here</p>
</li>
</ul>
<h1 id="heading-project-structure">Project structure</h1>
<pre><code class="lang-bash">ProjectFolder
│main.py   
│app.py   
│.env     
│.gitignore
 | requirements.txt
└───images
│   │   abc.png
│   │   chinua.jpg
│   │   story.pdf
│   │   wams.png
</code></pre>
<h1 id="heading-creating-an-azure-cognitive-service">Creating an azure cognitive service</h1>
<blockquote>
<p>Signup for an azure account <a target="_blank" href="https://portal.azure.com/">here</a></p>
</blockquote>
<p>Once logged in, click on the plus <strong>create resource</strong></p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1643479136752/La8YS_-cs.png" alt="one.png" /></p>
<p>Search for cognitive services</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1643479176953/udVWNIjTJ.png" alt="two.png" /></p>
<p>Select cognitive services</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1643479191481/PJSuCaULc.png" alt="three.png" /></p>
<p>Create the resource</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1643479202215/YmOHCRZnx.png" alt="four.png" /></p>
<p>If you do not have a resource group created yet, click on the create new below the resource group input. On the name field, make sure to give a unique URL of your project. Azure will let you know when a name is taken.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1643479226572/5KLkqbWkB.png" alt="five.png" /></p>
<p>I did not make any changes on the Identity or tags tab and went straight to the Review+Create tab and clicked on create at the very bottom.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1643479234712/FqstTUlCp.png" alt="six.png" /></p>
<p>You can check the progress of your deployment on the notification bar that can be accessed using the bell icon on the navbar to the right.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1643479253784/hNeDL5EtU.png" alt="seven.png" /></p>
<p>Viola! Once your deployment is done, click on Go to the resource.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1643479286575/7l4QegUPi.png" alt="eight.png" /></p>
<p>We need two important things that will enable us to connect with azure from python. A key and our API endpoint. Click on manage keys .</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1643479298696/Ol-dIkrUB.png" alt="nine.png" /></p>
<p>This takes us to the page where we can access two keys and an endpoint URL.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1643479306258/PVoIFGWoC.png" alt="10.png" /></p>
<h1 id="heading-environmental-variables">Environmental variables</h1>
<h2 id="heading-azure-keys">Azure Keys</h2>
<p>On your IDE, paste any one of the two keys and the endpoint in your .env. Keep the format as follows:</p>
<pre><code class="lang-bash">COG_SERVICE_ENDPOINT=your_azure_endpoint_goes_here
COG_SERVICE_KEY=your_azure_key_goes_here
</code></pre>
<h2 id="heading-telegram-token">Telegram Token</h2>
<p>Search the BotFather on telegram or access it from <a target="_blank" href="https://t.me/BotFather">here</a> There are very many fake accounts so please make sure the profile looks as below:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1643480489922/Tfa5SMJc6.png" alt="botfather.png" /></p>
<p>To create a new bot send a /newbot message to the botfather.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1643480769489/DmtCR5cXR.png" alt="botprmots.png" /></p>
<p>You might need to keep trying for an available username. Once you do, copy the token to your .env.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1643481199584/IIclVyQwt.png" alt="botname.png" /></p>
<pre><code class="lang-bash">COG_SERVICE_ENDPOINT=your_azure_endpoint_goes_here
COG_SERVICE_KEY=your_azure_key_goes_here
TELEGRAM_TOKEN=your_telegram_token_goes_here
</code></pre>
<blockquote>
<p>If you will push this code to a codebase, add the .env file to the .gitignore to avoid exposing your keys to the public.</p>
</blockquote>
<h1 id="heading-installing-dependencies">Installing dependencies</h1>
<p>Next up, we will install all the dependencies needed to run the project.</p>
<p>Add the following in your requirements.txt</p>
<pre><code class="lang-bash">Pillow == 8.3.1
matplotlib == 3.5.1
python_dotenv == 0.17.1
flask == 2.0.2
python-telegram-bot == 13.10
azure-cognitiveservices-vision-computervision==0.7.0
requests == 2.27.1
</code></pre>
<p>Open a terminal from the project folder and run : <code>sh pip install -r requirements.txt</code> This installs are the packages needed to run this project. A summary of what each package does:</p>
<ul>
<li><p><a target="_blank" href="https://python-pillow.org">Pillow</a>: Imaging Library</p>
</li>
<li><p><a target="_blank" href="https://matplotlib.org">Matplotlib</a>:plotting package</p>
</li>
<li><p><a target="_blank" href="https://github.com/theskumar/python-dotenv">python_dotenv</a>: Read key-value pairs from a .env file and set them as environment variables</p>
</li>
<li><p><a target="_blank" href="https://palletsprojects.com/p/flask">flask</a> :A simple framework for building complex web applications.</p>
</li>
<li><p><a target="_blank" href="https://python-telegram-bot.org/">python-telegram-bot</a>: Python wrapper for the official telegram API</p>
</li>
<li><p><a target="_blank" href="https://github.com/Azure/azure-sdk-for-python">azure-cognitiveservices-vision-computervision</a> :Microsoft Azure Cognitive Services Computer Vision Client Library for Python</p>
</li>
<li><p><a target="_blank" href="https://requests.readthedocs.io">requests</a> :Python HTTP for Humans. Now we are ready to write some code</p>
</li>
</ul>
<iframe src="https://giphy.com/embed/zk0zTXQY5ukCs" width="480" height="200" class="giphy-embed"></iframe>

<p><a target="_blank" href="https://giphy.com/gifs/giphyqa-zk0zTXQY5ukCs">via GIPHY</a></p>
<h1 id="heading-difference-between-read-api-and-ocr-api">Difference between READ API and OCR API</h1>
<p>The OCR API uses an older recognition model, supports only images, and executes synchronously, returning immediately with the detected text. The Read API uses an updated recognition model, takes an image or PDF document as the input, and extracts text asynchronously. But supports fewer languages The READ API also works page by page then line by line then word by word whereas the OCR works region by region then line by line then word by word. In terms of API response, the OCR API's response looks as follows:</p>
<pre><code class="lang-JSON">{
  <span class="hljs-attr">"language"</span>: <span class="hljs-string">"en"</span>,
  <span class="hljs-attr">"textAngle"</span>: <span class="hljs-number">0.00000</span>,
  <span class="hljs-attr">"orientation"</span>: <span class="hljs-string">"Up"</span>,
  <span class="hljs-attr">"regions"</span>: [
    {
      <span class="hljs-attr">"boundingBox"</span>: <span class="hljs-string">"462,379,497,75"</span>,
      <span class="hljs-attr">"lines"</span>: [
        {
          <span class="hljs-attr">"boundingBox"</span>: <span class="hljs-string">"462,379,497,74"</span>,
          <span class="hljs-attr">"words"</span>: [
            {
              <span class="hljs-attr">"boundingBox"</span>: <span class="hljs-string">"462,379,41,73"</span>,
              <span class="hljs-attr">"text"</span>: <span class="hljs-string">"Hello"</span>
            },
            {
              <span class="hljs-attr">"boundingBox"</span>: <span class="hljs-string">"523,379,153,73"</span>,
              <span class="hljs-attr">"text"</span>: <span class="hljs-string">"World!"</span>
            }
          ]
        }
      ]
    }
  ]
}
</code></pre>
<p>The API response for the READ API looks as follows: <code>JSON { "status": "succeeded", "createdDateTime": "2019-10-03T14:32:04Z", "lastUpdatedDateTime": "2019-10-03T14:38:14Z", "analyzeResult": { "version": "v3.0", "readResults": [ { "page": 1, "language": "en", "angle": 49.59, "width": 600, "height": 400, "unit": "pixel", "lines": [ { "boundingBox": [ 20,61,204,64,204,84,20,81], "text": "Hello world!", "words": [ { "boundingBox": [ 20,62,48,62,48,83,20,82], "text": "Hello", "confidence": 0.91 }, { "boundingBox": [ 51,62,105,63,105,83,51,83], "text": "world!", "confidence": 0.164 } ] } ] } ] } }</code> Since both APIs provide bounding box coordinates, they can be used for visualization. For example, performing OCR on the abc.png in our images folders yields this:</p>
<p>Original image :</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1643608933279/cl_j15QSz.png" alt="abc.png" /></p>
<p>Image after OCR:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1643608976003/jBttl6T73.jpeg" alt="abc-ocr_results.jpg" /></p>
<iframe src="https://giphy.com/embed/3o6ozqV6d2phfVYx3O" width="480" height="306" class="giphy-embed"></iframe>

<p><a target="_blank" href="https://giphy.com/gifs/youngertv-cool-tv-land-younger-3o6ozqV6d2phfVYx3O">via GIPHY</a></p>
<p>You can read more from the official <a target="_blank" href="https://docs.microsoft.com/en-us/azure/cognitive-services/computer-vision/overview-ocr">documentation</a> on their differences. Let's get our hands dirty now, shall we?</p>
<h1 id="heading-ask-the-user-for-input">Ask the user for input</h1>
<p>For a start, we will create a function <em>ask_user_for_input()</em> that asks a user to choose whether they want to invoke the Read API or the OCR API. The GetTextRead() function and the GetTextOcr() function only print out a statement but we will build on them as we go. Your main.py should contain: ```py import os</p>
<p>def GetTextOcr(file_path): """ Takes in a file and does OCR on the document and returns a string of the text extracted during OCR. :param file_path: :return: string """ try: print("This function invokes the OCR API on", file_path) except Exception as ex: print(ex)</p>
<p>def GetTextRead(file_path): """ Takes in a file extracts handwritten or printed text on the document and returns a string of the text extracted during OCR. :param file_path: :return: string """ try: print("This function invokes the READ API on", file_path) except Exception as ex: print(ex)</p>
<p>def ask_user_for_input():</p>
<p>""" Asks the user for input from the command line</p>
<p>""" try:</p>
<p># Menu for text reading functions print('1: Use OCR API\n2: Use Read API\n3: Read handwriting\nAny other key to quit') command = input('Enter a number:') if command == '1': image_file = os.path.join('images', 'chinua.png') GetTextOcr(image_file) elif command == '2': image_file = os.path.join('images', 'story.pdf') GetTextRead(image_file) elif command == '3': image_file = os.path.join('images', 'wams.png') GetTextRead(image_file)</p>
<p>except Exception as ex: print(ex)</p>
<p>ask_user_for_input()</p>
<pre><code class="lang-bash">Run main.py using:
```shell
python main.py
</code></pre>
<p>You should get prompts to enter a number</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1643557458326/TjxZiBdwn.png" alt="askuserinput.png" /></p>
<h1 id="heading-authenticating-the-azure-cognitive-client">Authenticating the azure cognitive client</h1>
<p>The library imports and the azure cognitive client authentication code is as follows: ```py import os import time from matplotlib import pyplot as plt from PIL import Image, ImageDraw</p>
<h1 id="heading-import-namespaces">Import namespaces</h1>
<p>from azure.cognitiveservices.vision.computervision import ComputerVisionClient from azure.cognitiveservices.vision.computervision.models import OperationStatusCodes from msrest.authentication import CognitiveServicesCredentials</p>
<p>from dotenv import load_dotenv</p>
<h1 id="heading-get-configuration-settings">Get Configuration Settings</h1>
<p>load_dotenv() cog_endpoint = os.getenv('COG_SERVICE_ENDPOINT') cog_key = os.getenv('COG_SERVICE_KEY')</p>
<h1 id="heading-authenticate-computer-vision-client">Authenticate Computer Vision client</h1>
<p>credential = CognitiveServicesCredentials(cog_key) cv_client = ComputerVisionClient(cog_endpoint, credential)</p>
<p>def GetTextOcr(file_path): """ Takes in a file does OCR on the document and returns a string of the text extracted during OCR.</p>
<p>.... ``` Let's go over it line by line.</p>
<ul>
<li><p>The os enables us to retrieve images from local directories.</p>
</li>
<li><p>We will need our code to sleep for a few seconds at some point. The time module helps with that.</p>
</li>
<li><p>Matplotlib offers a canvas for us to draw on</p>
</li>
<li><p>Pillow (PIL) enables us to read and draw on our images. We will be drawing bounding boxes to show where the text was extracted. This will be especially useful when visualizing what the cognitive service is doing behind the scenes</p>
</li>
<li><p>The azure ComputerVisionClient is responsible for all the OCR computations we will be doing</p>
</li>
<li><p>The azure computer vision models are advanced models trained to not only work on printed text but also handwritten. As we will see later, the OCR API does very poorly on handwritten data while the READ API is competent in decoding printed and handwritten text.</p>
</li>
<li><p>Last but not least, we import the CognitiveServicesCredentials that handles authenticating our code to azure using the key and endpoint we generated from the Azure portal.</p>
</li>
<li><p>Finally, we import load_dotenv. A python package is used to retrieve environmental variables.</p>
</li>
</ul>
<p>Now that we have all the necessary libraries In place, we need to authenticate our computer vision client. The code just before the GetTextOcr() should now be as follows:</p>
<pre><code class="lang-py"><span class="hljs-keyword">import</span> os
<span class="hljs-keyword">import</span> time
<span class="hljs-keyword">from</span> PIL <span class="hljs-keyword">import</span> Image, ImageDraw
<span class="hljs-keyword">from</span> matplotlib <span class="hljs-keyword">import</span> pyplot <span class="hljs-keyword">as</span> plt

<span class="hljs-comment"># Import namespaces</span>
<span class="hljs-keyword">from</span> azure.cognitiveservices.vision.computervision <span class="hljs-keyword">import</span> ComputerVisionClient
<span class="hljs-keyword">from</span> azure.cognitiveservices.vision.computervision.models <span class="hljs-keyword">import</span> OperationStatusCodes
<span class="hljs-keyword">from</span> msrest.authentication <span class="hljs-keyword">import</span> CognitiveServicesCredentials

<span class="hljs-keyword">from</span> dotenv <span class="hljs-keyword">import</span> load_dotenv

<span class="hljs-comment"># Get Configuration Settings</span>
load_dotenv()
cog_endpoint = os.getenv(<span class="hljs-string">'COG_SERVICE_ENDPOINT'</span>)
cog_key = os.getenv(<span class="hljs-string">'COG_SERVICE_KEY'</span>)

<span class="hljs-comment"># Authenticate Computer Vision client</span>
credential = CognitiveServicesCredentials(cog_key)
cv_client = ComputerVisionClient(cog_endpoint, credential)


<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">GetTextOcr</span>(<span class="hljs-params">file_path</span>):</span>
    <span class="hljs-string">"""
    Takes in a file does OCR on the document and returns a string
    of the text extracted during OCR.</span>
</code></pre>
<p>To authenticate the client, we get our environmental variables from the .env.</p>
<blockquote>
<p>Note: The variable name being retrieved from the .env must be the same one passed to os.getenv(). Case sensitivity is observed. For example, the <strong>COG_SERVICE_ENDPOINT</strong> in the env must be the same as what is passed in the <code>py os.getenv('COG_SERVICE_ENDPOINT')</code></p>
</blockquote>
<p>We then authenticate the computer vision by first passing our key to the CognitiveServicesCredentials that validate our key. That is then passed along to the ComputerVisionClient.</p>
<h1 id="heading-ocr-api">OCR API</h1>
<p>We will start with the OCR API.</p>
<p>The full code for the GetTextOcr() looks as follows:</p>
<pre><code class="lang-py">
....
<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">GetTextOcr</span>(<span class="hljs-params">file_path</span>):</span>
    <span class="hljs-string">"""
    Takes in a file and does OCR on the document and returns a string
    of the text extracted during OCR.
    :param file_path:

    """</span>
    <span class="hljs-keyword">try</span>:
        print(<span class="hljs-string">"This function invokes the OCR API on"</span>, file_path)
        <span class="hljs-comment"># Use OCR API to read the text in the image</span>
        <span class="hljs-keyword">with</span> open(file_path, mode=<span class="hljs-string">"rb"</span>) <span class="hljs-keyword">as</span> image_data:
            ocr_results = cv_client.recognize_printed_text_in_stream(image_data)

        <span class="hljs-comment"># Prepare image for drawing</span>
        fig = plt.figure(figsize=(<span class="hljs-number">7</span>, <span class="hljs-number">7</span>))
        img = Image.open(file_path)
        draw = ImageDraw.Draw(img)

        <span class="hljs-comment"># All the words extracted will be stored as a list</span>
        results = []

        <span class="hljs-comment"># Process the text line by line</span>
        <span class="hljs-keyword">for</span> region <span class="hljs-keyword">in</span> ocr_results.regions:
            <span class="hljs-keyword">for</span> line <span class="hljs-keyword">in</span> region.lines:

                <span class="hljs-comment"># Show the position of the line of text</span>
                l, t, w, h = list(map(int, line.bounding_box.split(<span class="hljs-string">','</span>)))
                draw.rectangle(((l, t), (l + w, t + h)), outline=<span class="hljs-string">'magenta'</span>, width=<span class="hljs-number">5</span>)

                <span class="hljs-comment"># Read the words in the line of text</span>
                line_text = <span class="hljs-string">''</span>
                <span class="hljs-keyword">for</span> word <span class="hljs-keyword">in</span> line.words:
                    line_text += word.text + <span class="hljs-string">' '</span>
                print(line_text.rstrip())
                results.append(line_text.rstrip())

        <span class="hljs-comment"># Save the image with the text locations highlighted if the image was ocrd</span>
        <span class="hljs-keyword">if</span> len(results) &gt; <span class="hljs-number">0</span>:
            plt.axis(<span class="hljs-string">'off'</span>)
            plt.imshow(img)
            <span class="hljs-comment"># create output folder if doesnt exist</span>
            <span class="hljs-keyword">if</span> <span class="hljs-keyword">not</span> os.path.exists(<span class="hljs-string">'ocr-results'</span>):
                os.makedirs(<span class="hljs-string">'ocr-results'</span>)
            file_path = file_path.rsplit(<span class="hljs-string">'\\'</span>, <span class="hljs-number">1</span>)[<span class="hljs-number">-1</span>].rsplit(<span class="hljs-string">'.'</span>, <span class="hljs-number">1</span>)[<span class="hljs-number">0</span>]
            outputfile = <span class="hljs-string">f'ocr-results\\<span class="hljs-subst">{file_path}</span>-ocr_results.jpg'</span>
            fig.savefig(outputfile)
            print(<span class="hljs-string">'Results saved in'</span>, outputfile)
            <span class="hljs-comment"># if there was no ocr decoded the results list will be empty</span>
        <span class="hljs-keyword">if</span> len(results) == <span class="hljs-number">0</span>:
            print(<span class="hljs-string">f'<span class="hljs-subst">{file_path}</span> IMAGE WAS NOT OCRD'</span>)


    <span class="hljs-keyword">except</span> Exception <span class="hljs-keyword">as</span> ex:
        print(ex)
....
</code></pre>
<p>Code break down: ```py</p>
<h1 id="heading-use-ocr-api-to-read-the-text-in-the-image">Use OCR API to read the text in the image</h1>
<p>with open(file_path, mode="rb") as image_data: ocr_results = cv_client.recognize_printed_text_in_stream(image_data)</p>
<pre><code class="lang-bash">- The above code opens the image we are currently working on.
- Streams the image to the azure client that recognized the text <span class="hljs-keyword">in</span> the image.
- The results from the stream are stored <span class="hljs-keyword">in</span> ocr_results.

```py
        <span class="hljs-comment"># Prepare image for drawing</span>
        fig = plt.figure(figsize=(7, 7))
        img = Image.open(file_path)
        draw = ImageDraw.Draw(img)
</code></pre>
<p>Next, we initialize a matplotlib canvas (figure) and use the Draw() method from pillow to draw our image on the canvas.</p>
<pre><code class="lang-py">     <span class="hljs-comment"># All the words extracted will be stored as a list</span>
        results = []
</code></pre>
<p>Initialize an empty list that will store all the words extracted from the image by the azure service.</p>
<pre><code class="lang-py">
        <span class="hljs-comment"># Process the text line by line</span>
        <span class="hljs-keyword">for</span> region <span class="hljs-keyword">in</span> ocr_results.regions:
            <span class="hljs-keyword">for</span> line <span class="hljs-keyword">in</span> region.lines:

                <span class="hljs-comment"># Show the position of the line of text</span>
                l, t, w, h = list(map(int, line.bounding_box.split(<span class="hljs-string">','</span>)))
                draw.rectangle(((l, t), (l + w, t + h)), outline=<span class="hljs-string">'magenta'</span>, width=<span class="hljs-number">5</span>)

                <span class="hljs-comment"># Read the words in the line of text</span>
                line_text = <span class="hljs-string">''</span>
                <span class="hljs-keyword">for</span> word <span class="hljs-keyword">in</span> line.words:
                    line_text += word.text + <span class="hljs-string">' '</span>
                print(line_text.rstrip())
                results.append(line_text.rstrip())
</code></pre>
<p>The for loop starts by looping through each region from the ocr_results. Refer to the sample API response we highlighted when comparing the OCR and READ API. Every region has an array of lines in the region. Think of a line like a sentence. Every line comes with bounding boxes that we will be drawing on the image we duplicated on the matplotlib canvas. All the words extracted from each line are stored in the results list we had initialized. This means that if the image did not contain any words or the OCR API was unable to extract any words (remember that OCR performs poorly on handwritten text), the results list remains empty. This logic takes us to the next part of the code.</p>
<pre><code class="lang-py">
       <span class="hljs-comment"># Save the image with the text locations highlighted if the image was ocrd</span>
        <span class="hljs-keyword">if</span> len(results) &gt; <span class="hljs-number">0</span>:
            plt.axis(<span class="hljs-string">'off'</span>)
            plt.imshow(img)
            <span class="hljs-comment"># create output folder if doesnt exist</span>
            <span class="hljs-keyword">if</span> <span class="hljs-keyword">not</span> os.path.exists(<span class="hljs-string">'ocr-results'</span>):
                os.makedirs(<span class="hljs-string">'ocr-results'</span>)
            file_path = file_path.rsplit(<span class="hljs-string">'\\'</span>, <span class="hljs-number">1</span>)[<span class="hljs-number">-1</span>].rsplit(<span class="hljs-string">'.'</span>, <span class="hljs-number">1</span>)[<span class="hljs-number">0</span>]
            outputfile = <span class="hljs-string">f'ocr-results\\<span class="hljs-subst">{file_path}</span>-ocr_results.jpg'</span>
            fig.savefig(outputfile)
            print(<span class="hljs-string">'Results saved in'</span>, outputfile)
            <span class="hljs-comment"># if there was no ocr decoded the results list will be empty</span>
        <span class="hljs-keyword">if</span> len(results) == <span class="hljs-number">0</span>:
            print(<span class="hljs-string">f'<span class="hljs-subst">{file_path}</span> IMAGE WAS NOT OCRD'</span>)
</code></pre>
<p>We first check the length of the results list. If the length is greater than 0, the image processed contained at least one line or one word that the API extracted. If this is the case, we save the image on our local directory. If no word was extracted on the image we simply print that the image did not contain any word the API could recognize. Run main.py and test the function:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1643619515525/YqFE49oRc.png" alt="abcocrterminal.png" /></p>
<p>Results were as we had seen on the abc.png. Try out an image of your liking by changing the file name in the function we created for asking the user for input initially.</p>
<iframe src="https://giphy.com/embed/InvZkV5yZFjCnhDN9Z" width="480" height="300" class="giphy-embed"></iframe>

<p><a target="_blank" href="https://giphy.com/gifs/RoadshowPBS-InvZkV5yZFjCnhDN9Z">via GIPHY</a></p>
<h1 id="heading-read-api">READ API</h1>
<p>The GetTextRead() function implements the READ API. The full code for the function is as follows:</p>
<pre><code class="lang-py"><span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">GetTextRead</span>(<span class="hljs-params">file_path</span>):</span>
    <span class="hljs-string">"""
      Takes in a file extracts handwritten or printed text
       on the document and returns a string of the text extracted during OCR.
      :param file_path:
    """</span>
    <span class="hljs-keyword">try</span>:
        print(<span class="hljs-string">"This function invokes the READ API on"</span>, file_path)
        results = []
        <span class="hljs-comment"># Use Read API to read text in image</span>
        <span class="hljs-keyword">with</span> open(file_path, mode=<span class="hljs-string">"rb"</span>) <span class="hljs-keyword">as</span> image_data:
            read_op = cv_client.read_in_stream(image_data, raw=<span class="hljs-literal">True</span>)

        <span class="hljs-comment"># Get the async operation ID so we can check for the results</span>
        operation_location = read_op.headers[<span class="hljs-string">"Operation-Location"</span>]
        operation_id = operation_location.split(<span class="hljs-string">"/"</span>)[<span class="hljs-number">-1</span>]

        <span class="hljs-comment"># Wait for the asynchronous operation to complete</span>
        <span class="hljs-keyword">while</span> <span class="hljs-literal">True</span>:
            read_results = cv_client.get_read_result(operation_id)
            <span class="hljs-keyword">if</span> read_results.status <span class="hljs-keyword">not</span> <span class="hljs-keyword">in</span> [OperationStatusCodes.running, OperationStatusCodes.not_started]:
                <span class="hljs-keyword">break</span>
            time.sleep(<span class="hljs-number">1</span>)

        <span class="hljs-comment"># If the operation was successfuly, process the text line by line</span>
        <span class="hljs-keyword">if</span> read_results.status == OperationStatusCodes.succeeded:
            <span class="hljs-keyword">for</span> page <span class="hljs-keyword">in</span> read_results.analyze_result.read_results:
                <span class="hljs-keyword">for</span> line <span class="hljs-keyword">in</span> page.lines:
                    print(line.text)
                    results.append(line.text)
        print(<span class="hljs-string">'Reading text in {}\n'</span>.format(file_path))

        <span class="hljs-keyword">return</span> results
    <span class="hljs-keyword">except</span> Exception <span class="hljs-keyword">as</span> ex:
        print(ex)
</code></pre>
<p>Going over the code line by line:</p>
<pre><code class="lang-py">results =[]
</code></pre>
<p>Initialise empty list that will store all the extracted text in the image or document ```py<br /># Use Read API to read text in image with open(file_path, mode="rb") as image_data: read_op = cv_client.read_in_stream(image_data, raw=True)</p>
<p># Get the async operation ID so we can check for the results operation_location = read_op.headers["Operation-Location"] operation_id = operation_location.split("/")[-1]</p>
<p># Wait for the asynchronous operation to complete while True: read_results = cv_client.get_read_result(operation_id) if read_results.status not in [OperationStatusCodes.running, OperationStatusCodes.not_started]: break time.sleep(1) ``` We mentioned that the READ API works asynchronously so we need a mechanism that waits for the whole process to complete. We first pass the image to our computer vision client. In order to make sure the READ API is done, we start a while loop that only exits when the status code is either SUCCEEDED or FAILED. You can learn more about the operational codes <a target="_blank" href="https://docs.microsoft.com/en-us/java/api/com.microsoft.azure.cognitiveservices.vision.computervision.models.operationstatuscodes?view=azure-java-stable">here</a>. Once the code breaks, the results from the READ API will be stored in the read_results variable.</p>
<pre><code class="lang-py"> <span class="hljs-comment"># If the operation was successfuly, process the text line by line</span>
        <span class="hljs-keyword">if</span> read_results.status == OperationStatusCodes.succeeded:
            <span class="hljs-keyword">for</span> page <span class="hljs-keyword">in</span> read_results.analyze_result.read_results:
                <span class="hljs-keyword">for</span> line <span class="hljs-keyword">in</span> page.lines:
                    print(line.text)
                    results.append(line.text)
        print(<span class="hljs-string">'Reading text in {}\n'</span>.format(file_path))

        <span class="hljs-keyword">return</span> results
    <span class="hljs-keyword">except</span> Exception <span class="hljs-keyword">as</span> ex:
        print(ex)
</code></pre>
<p>If the operation is successful, we extract all the words and append them in the results variable. Any errors will be printed to the console as exceptions. Since the READ API works on both documents and images, we will test it out using the story.pdf and the handwritten file wams.py.</p>
<p>Test it out :</p>
<pre><code class="lang-shell">python main.py
</code></pre>
<p>You should get the following results:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1643632700838/ytyaQQVSX.png" alt="READAPITERMINAL.png" /></p>
<p>The full code for main.py:</p>
<pre><code class="lang-py">

<span class="hljs-keyword">import</span> os
<span class="hljs-keyword">import</span> time
<span class="hljs-keyword">from</span> PIL <span class="hljs-keyword">import</span> Image, ImageDraw
<span class="hljs-keyword">from</span> matplotlib <span class="hljs-keyword">import</span> pyplot <span class="hljs-keyword">as</span> plt

<span class="hljs-comment"># Import namespaces</span>
<span class="hljs-keyword">from</span> azure.cognitiveservices.vision.computervision <span class="hljs-keyword">import</span> ComputerVisionClient
<span class="hljs-keyword">from</span> azure.cognitiveservices.vision.computervision.models <span class="hljs-keyword">import</span> OperationStatusCodes
<span class="hljs-keyword">from</span> msrest.authentication <span class="hljs-keyword">import</span> CognitiveServicesCredentials
<span class="hljs-keyword">from</span> dotenv <span class="hljs-keyword">import</span> load_dotenv


<span class="hljs-comment"># Get Configuration Settings</span>
load_dotenv()
cog_endpoint = os.getenv(<span class="hljs-string">'COG_SERVICE_ENDPOINT'</span>)
cog_key = os.getenv(<span class="hljs-string">'COG_SERVICE_KEY'</span>)

<span class="hljs-comment"># Authenticate Computer Vision client</span>
credential = CognitiveServicesCredentials(cog_key)
cv_client = ComputerVisionClient(cog_endpoint, credential)


<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">GetTextOcr</span>(<span class="hljs-params">file_path</span>):</span>
    <span class="hljs-string">"""
    Takes in a file and does OCR on the document and returns a string
    of the text extracted during OCR.
    :param file_path:

    """</span>
    <span class="hljs-keyword">try</span>:
        print(<span class="hljs-string">"This function invokes the OCR API on"</span>, file_path)
        <span class="hljs-comment"># Use OCR API to read text in image</span>
        <span class="hljs-keyword">with</span> open(file_path, mode=<span class="hljs-string">"rb"</span>) <span class="hljs-keyword">as</span> image_data:
            ocr_results = cv_client.recognize_printed_text_in_stream(image_data)

        <span class="hljs-comment"># Prepare image for drawing</span>
        fig = plt.figure(figsize=(<span class="hljs-number">7</span>, <span class="hljs-number">7</span>))
        img = Image.open(file_path)
        draw = ImageDraw.Draw(img)

        <span class="hljs-comment"># All the words extracted will be stored as a list</span>
        results = []
        <span class="hljs-comment"># Process the text line by line</span>
        <span class="hljs-keyword">for</span> region <span class="hljs-keyword">in</span> ocr_results.regions:
            <span class="hljs-keyword">for</span> line <span class="hljs-keyword">in</span> region.lines:

                <span class="hljs-comment"># Show the position of the line of text</span>
                l, t, w, h = list(map(int, line.bounding_box.split(<span class="hljs-string">','</span>)))
                draw.rectangle(((l, t), (l + w, t + h)), outline=<span class="hljs-string">'magenta'</span>, width=<span class="hljs-number">5</span>)

                <span class="hljs-comment"># Read the words in the line of text</span>
                line_text = <span class="hljs-string">''</span>
                <span class="hljs-keyword">for</span> word <span class="hljs-keyword">in</span> line.words:
                    line_text += word.text + <span class="hljs-string">' '</span>
                print(line_text.rstrip())
                results.append(line_text.rstrip())

        <span class="hljs-comment"># Save the image with the text locations highlighted if the image was ocrd</span>
        <span class="hljs-keyword">if</span> len(results) &gt; <span class="hljs-number">0</span>:
            plt.axis(<span class="hljs-string">'off'</span>)
            plt.imshow(img)
            <span class="hljs-comment"># create output folder if doesnt exist</span>
            <span class="hljs-keyword">if</span> <span class="hljs-keyword">not</span> os.path.exists(<span class="hljs-string">'ocr-results'</span>):
                os.makedirs(<span class="hljs-string">'ocr-results'</span>)
            file_path = file_path.rsplit(<span class="hljs-string">'\\'</span>, <span class="hljs-number">1</span>)[<span class="hljs-number">-1</span>].rsplit(<span class="hljs-string">'.'</span>, <span class="hljs-number">1</span>)[<span class="hljs-number">0</span>]
            outputfile = <span class="hljs-string">f'ocr-results\\<span class="hljs-subst">{file_path}</span>-ocr_results.jpg'</span>
            fig.savefig(outputfile)
            print(<span class="hljs-string">'Results saved in'</span>, outputfile)
            <span class="hljs-comment"># if there was no ocr decoded the results list will be empty</span>
        <span class="hljs-keyword">if</span> len(results) == <span class="hljs-number">0</span>:
            print(<span class="hljs-string">f'<span class="hljs-subst">{file_path}</span> IMAGE WAS NOT OCRD'</span>)


    <span class="hljs-keyword">except</span> Exception <span class="hljs-keyword">as</span> ex:
        print(ex)


<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">GetTextRead</span>(<span class="hljs-params">file_path</span>):</span>
    <span class="hljs-string">"""
      Takes in a file and extracts handwritten or printed text
       on the document and returns a string of the text extracted during OCR.
      :param file_path:
    """</span>
    <span class="hljs-keyword">try</span>:
        print(<span class="hljs-string">"This function invokes the READ API on"</span>, file_path)
        results = []
        <span class="hljs-comment"># Use Read API to read text in image</span>
        <span class="hljs-keyword">with</span> open(file_path, mode=<span class="hljs-string">"rb"</span>) <span class="hljs-keyword">as</span> image_data:
            read_op = cv_client.read_in_stream(image_data, raw=<span class="hljs-literal">True</span>)

        <span class="hljs-comment"># Get the async operation ID so we can check for the results</span>
        operation_location = read_op.headers[<span class="hljs-string">"Operation-Location"</span>]
        operation_id = operation_location.split(<span class="hljs-string">"/"</span>)[<span class="hljs-number">-1</span>]

        <span class="hljs-comment"># Wait for the asynchronous operation to complete</span>
        <span class="hljs-keyword">while</span> <span class="hljs-literal">True</span>:
            read_results = cv_client.get_read_result(operation_id)
            <span class="hljs-keyword">if</span> read_results.status <span class="hljs-keyword">not</span> <span class="hljs-keyword">in</span> [OperationStatusCodes.running, OperationStatusCodes.not_started]:
                <span class="hljs-keyword">break</span>
            time.sleep(<span class="hljs-number">1</span>)

        <span class="hljs-comment"># If the operation was successfuly, process the text line by line</span>
        <span class="hljs-keyword">if</span> read_results.status == OperationStatusCodes.succeeded:
            <span class="hljs-keyword">for</span> page <span class="hljs-keyword">in</span> read_results.analyze_result.read_results:
                <span class="hljs-keyword">for</span> line <span class="hljs-keyword">in</span> page.lines:
                    print(line.text)
                    results.append(line.text)
        print(<span class="hljs-string">'Reading text in {}\n'</span>.format(file_path))

        <span class="hljs-keyword">return</span> results
    <span class="hljs-keyword">except</span> Exception <span class="hljs-keyword">as</span> ex:
        print(ex)


<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">ask_user_for_input</span>():</span>
    <span class="hljs-string">"""
    Asks the user for input from the command line

    """</span>
    <span class="hljs-keyword">try</span>:

        <span class="hljs-comment"># Menu for text reading functions</span>
        print(<span class="hljs-string">'1: Use OCR API\n2: Use Read API\n3: Read handwriting\nAny other key to quit'</span>)

        command = input(<span class="hljs-string">'Enter a number:'</span>)
        <span class="hljs-keyword">if</span> command == <span class="hljs-string">'1'</span>:
            file_path = os.path.join(<span class="hljs-string">'images'</span>, <span class="hljs-string">'abc.png'</span>)
            GetTextOcr(file_path)
        <span class="hljs-keyword">elif</span> command == <span class="hljs-string">'2'</span>:
            file_path = os.path.join(<span class="hljs-string">'images'</span>, <span class="hljs-string">'story.pdf'</span>)
            GetTextRead(file_path)
        <span class="hljs-keyword">elif</span> command == <span class="hljs-string">'3'</span>:
            file_path = os.path.join(<span class="hljs-string">'images'</span>, <span class="hljs-string">'wams.png'</span>)
            GetTextRead(file_path)


    <span class="hljs-keyword">except</span> Exception <span class="hljs-keyword">as</span> ex:
        print(ex)


ask_user_for_input()
</code></pre>
<iframe src="https://giphy.com/embed/1jCs6Doz3WRtOPl6bq" width="480" height="258" class="giphy-embed"></iframe>

<p><a target="_blank" href="https://giphy.com/gifs/xmoons-soupapinho-aintmuch-1jCs6Doz3WRtOPl6bq">via GIPHY</a></p>
<p>So far we've been hardcoding the image we are processing, but we need to make the awesome project accessible to others. We will implement a flask API endpoint that will be used by telegram. You can use the endpoint with any frontend framework that accepts REST</p>
<p>Our Flask endpoint will reside in app.py. The bare-bone code on app.py is :</p>
<pre><code class="lang-py"><span class="hljs-keyword">import</span> os
<span class="hljs-keyword">from</span> dotenv <span class="hljs-keyword">import</span> load_dotenv
<span class="hljs-keyword">from</span> flask <span class="hljs-keyword">import</span> Flask
<span class="hljs-keyword">from</span> telegram.ext <span class="hljs-keyword">import</span> Updater, MessageHandler, Filters
<span class="hljs-keyword">from</span> telegram.ext <span class="hljs-keyword">import</span> CommandHandler
<span class="hljs-keyword">from</span> main <span class="hljs-keyword">import</span> GetTextRead
load_dotenv()


TELEGRAM_TOKEN = os.getenv(<span class="hljs-string">'TELEGRAM_TOKEN'</span>)
updater = Updater(token=TELEGRAM_TOKEN, use_context=<span class="hljs-literal">True</span>)
dispatcher = updater.dispatcher

app = Flask(__name__)


<span class="hljs-meta">@app.route('/')</span>
<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">extract_text_from_telegram</span>(<span class="hljs-params">update, context</span>):</span>
    <span class="hljs-keyword">try</span>:
        print(<span class="hljs-string">"Uploading to telegram ..."</span>)
    <span class="hljs-keyword">except</span> Exception <span class="hljs-keyword">as</span> ex:
        update.message.reply_text(<span class="hljs-string">"Upload an image with text"</span>)
        print(ex)


<span class="hljs-comment"># set up the introductory statement for the bot when the /start command is invoked</span>
<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">start</span>(<span class="hljs-params">update, context</span>):</span>
    <span class="hljs-keyword">try</span>:
        chat_id = update.effective_chat.id
        context.bot.send_message(chat_id=chat_id,
                                 text=<span class="hljs-string">"Hello there, Wamaitha here. Thank you for registering for the event. \n"</span>
                                      <span class="hljs-string">"This bot takes in a photo and applies the azure cognitive service to extract "</span>
                                      <span class="hljs-string">"printed or handwritten text from images or pdfs. Have fun while at it! \n "</span>
                                      <span class="hljs-string">"Connect with me  :\n"</span>
                                      <span class="hljs-string">"Linkedin : https://www.linkedin.com/in/wamaithanyamu/\n"</span>
                                      <span class="hljs-string">"Github : https://github.com/wamaithaNyamu \n"</span>
                                      <span class="hljs-string">"Twitter : https://twitter.com/wamaithaNyamu \n"</span>)


    <span class="hljs-keyword">except</span> Exception <span class="hljs-keyword">as</span> ex:
        print(ex)


<span class="hljs-comment"># run the start function when the user invokes the /start command</span>
dispatcher.add_handler(CommandHandler(<span class="hljs-string">"start"</span>, start))

<span class="hljs-comment"># Handle messages from the user</span>
dispatcher.add_handler(MessageHandler(Filters.all, extract_text_from_telegram))
updater.start_polling()


<span class="hljs-keyword">if</span> __name__ == <span class="hljs-string">'__main__'</span>:
    app.run(debug=<span class="hljs-literal">True</span>)
</code></pre>
<p>Let's go over the code line by line :</p>
<ul>
<li><p>We import the os and dotenv that will be used to retrieve our environmental variable for the telegram token</p>
</li>
<li><p>Import flask that will be used to make an endpoint</p>
</li>
<li><p>Import telegram.ext and helper functions that will be used to communicate with python from telegram</p>
</li>
<li><p>We will be using the READ API since its more comprehensive than the OCR API, as such we need to import it from our main.py</p>
</li>
<li><p>The extract_text_from_telegram function will be populated with the logic needed to filter out messages sent by the user whether they uploaded an image or a pdf. More on this next! For now we only print a statement to the console</p>
</li>
<li><p>The start function is what you see when you start the bot</p>
</li>
</ul>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1644152646852/d8E82Z7dV.png" alt="start.png" /></p>
<ul>
<li><p>The dispatcher is what interacts with telegram. the first dispatcher handles the /start command from the user. The other dispatcher will handle any incoming messages from telegram and then invoke the extract_text_from_telegram function.</p>
</li>
<li><p>Lastly we poll the updater to check if there is any message from the user so that we can process it</p>
</li>
<li><p>We then run the flask app</p>
</li>
</ul>
<p>To run the app.py as is :</p>
<p>On Unix Bash (Linux, Mac, etc.):</p>
<pre><code class="lang-shell">export FLASK_APP=app
flask run
</code></pre>
<p>On Windows CMD:</p>
<pre><code class="lang-shell">set FLASK_APP=app
flask run
</code></pre>
<p>On Windows PowerShell:</p>
<pre><code class="lang-shell">$env:FLASK_APP = "app"
flask run
</code></pre>
<p>The full code for our app.py is as follows: ```py import os import pathlib</p>
<p>from dotenv import load_dotenv from flask import Flask import requests from telegram.ext import Updater, MessageHandler, Filters from telegram.ext import CommandHandler</p>
<h1 id="heading-from-our-mainpy">from our main.py</h1>
<p>from main import GetTextRead</p>
<p>load_dotenv()</p>
<p>TELEGRAM_TOKEN = os.getenv('TELEGRAM_TOKEN') updater = Updater(token=TELEGRAM_TOKEN, use_context=True) dispatcher = updater.dispatcher</p>
<p>app = Flask(<strong>name</strong>)</p>
<p>def file_handler(update): try: print(update.message) file_id = '' if len(update.message.photo) == 0 and update.message.document.file_id: print("WE HAVE A FILE", update.message.document.file_id) file_id = update.message.document.file_id elif len(update.message.photo) &gt; 0: print("WE HAVE AN IMAGE", update.message.photo[-1].file_id) file_id = update.message.photo[-1].file_id return file_id</p>
<p>except Exception as e: print("Handler exception",e)</p>
<p>@app.route('/') def extract_text_from_telegram(update, context): """</p>
<p>:param update: checks for updates from telegram :param context: :return: """ try:</p>
<p>file_name ='' print("Uploading to telegram ...", ) file_id = file_handler(update) print("FILE ID", file_id)</p>
<p>if file_id: update.message.reply_text("Processing file...") file_path = f'https://api.telegram.org/bot{TELEGRAM_TOKEN}/getFile?file_id={file_id}' img = requests.get(file_path).json() img = img['result']['file_path'] # img path: photos/file_35.jpg print("img path:", img) file_image = f'https://api.telegram.org/file/bot{TELEGRAM_TOKEN}/{img}' response = requests.get(file_image) # split_img path: file_35.jpg split_img = img.rsplit('/', 1)[-1] print("split_img path:", split_img)</p>
<p># File Extension: .jpg file_extension = pathlib.Path(split_img).suffix print("File Extension: ", file_extension) # file_35.jpg-AgACAgQAAxkBAAIBS2H5FN20IK2ArluFlw_E_MiY2bw.jpg file_name = f'{split_img}-{file_id}.{file_extension}'</p>
<p>file = open(f'{file_name}', "wb") file.write(response.content) file.close()</p>
<p>user_image_text_results = GetTextRead(file_name) print("results:", user_image_text_results) if len(user_image_text_results) &gt; 0: results = ' '.join(str(e) for e in user_image_text_results) print(results) update.message.reply_text("Thank you for your patience. This is what I extracted:") update.message.reply_text(results) else: update.message.reply_text( "Unfortunately, this image does not contain any printed or handwritten text. ")</p>
<p>else: update.message.reply_text("Upload an image with text")</p>
<p>if os.path.exists(file_name): os.remove(file_name) print(file_name, ' deleted!') else: print(f"The file {file_name} does not exist") except Exception as ex: update.message.reply_text("Upload an image with text") print(ex)</p>
<h1 id="heading-set-up-the-introductory-statement-for-the-bot-when-the-start-command-is-invoked">set up the introductory statement for the bot when the /start command is invoked</h1>
<p>def start(update, context): """ /start command on telegram :param update: :param context: :return: """ try: chat_id = update.effective_chat.id context.bot.send_message(chat_id=chat_id, text="Demo time!!!")</p>
<p>except Exception as ex: print(ex)</p>
<h1 id="heading-run-the-start-function-when-the-user-invokes-the-start-command">run the start function when the user invokes the /start command</h1>
<p>dispatcher.add_handler(CommandHandler("start", start))</p>
<p>dispatcher.add_handler(MessageHandler(Filters.all, extract_text_from_telegram)) updater.start_polling()</p>
<h1 id="heading-print">print</h1>
<p>if <strong>name</strong> == '<strong>main</strong>': app.run(debug=True)</p>
<pre><code class="lang-bash">
Going over the app.y, we added code to the extract_text_from_telegram <span class="hljs-keyword">function</span> and also made a utility <span class="hljs-keyword">function</span> file_handler.

The file_handler <span class="hljs-keyword">function</span> takes <span class="hljs-keyword">in</span> the message from the user and determines whether the message has an image or a file and <span class="hljs-keyword">then</span> returns an id of either the image id or the file id. 

In the extract_text_from_telegram <span class="hljs-keyword">function</span>, we call on the file_hanlder <span class="hljs-keyword">function</span> to give us an id of the current message being sent by the user. 

If there is a file id, we request a telegram <span class="hljs-keyword">for</span> that file and store it on our <span class="hljs-built_in">local</span> machine. The image or file is <span class="hljs-keyword">then</span> passed to the Get_Read <span class="hljs-keyword">function</span> that we worked on <span class="hljs-keyword">in</span> the main.py. The extracted text we get from Azure is sent back to the user on telegram. If azure did not find any texts on the provided file or image, we <span class="hljs-built_in">let</span> the user know that we found no text on the supplied file or image.

Lastly, the file or image we saved from telegram is <span class="hljs-keyword">then</span> deleted from our system. 

&lt;iframe src=<span class="hljs-string">"https://giphy.com/embed/3o6Mbbs879ozZ9Yic0"</span> width=<span class="hljs-string">"480"</span> height=<span class="hljs-string">"366"</span> frameBorder=<span class="hljs-string">"0"</span> class=<span class="hljs-string">"giphy-embed"</span> allowFullScreen&gt;&lt;/iframe&gt;&lt;p&gt;&lt;a href=<span class="hljs-string">"https://giphy.com/gifs/season-7-the-simpsons-7x4-3o6Mbbs879ozZ9Yic0"</span>&gt;via GIPHY&lt;/a&gt;&lt;/p&gt;

Results from telegram:

![sample1.png](https://cdn.hashnode.com/res/hashnode/image/upload/v1645532848269/-nTsQEk8m.png)


![sample2.png](https://cdn.hashnode.com/res/hashnode/image/upload/v1645532866846/aN6n3nZhD.png)


![sample3.png](https://cdn.hashnode.com/res/hashnode/image/upload/v1645532891711/DtdRTEFeS.png)


![sample4.png](https://cdn.hashnode.com/res/hashnode/image/upload/v1645532902947/ZhTPipvHT.png)


![sample5.png](https://cdn.hashnode.com/res/hashnode/image/upload/v1645532913522/vfYxBZMY8.png)

<span class="hljs-comment"># HOSTING ON HEROKU</span>

We need to make sure the rest of the world can use our bot even we shut down our computer. We will be hosting on heroku.


You will need to have the heroku CLI and git  installed on your system. If you <span class="hljs-keyword">do</span> not have the CLI you can install it using npm:

```shell
npm install -g heroku
</code></pre>
<p>Installation instructions can be found <a target="_blank" href="https://devcenter.heroku.com/articles/heroku-cli">here</a> incase you get stuck.</p>
<p>Git installation instructions are found <a target="_blank" href="https://git-scm.com/book/en/v2/Getting-Started-Installing-Git">here</a></p>
<iframe src="https://giphy.com/embed/jVStxzak9yk2Q" width="480" height="427" class="giphy-embed"></iframe>

<p><a target="_blank" href="https://giphy.com/gifs/finally-atlast-itsover-jVStxzak9yk2Q">via GIPHY</a></p>
<h3 id="heading-create-the-procfile">Create the Procfile</h3>
<p>Heroku uses a special file that defines the commands we want to run when heroku launches our application. In this case we need a Procfile that tells Heroku to start our flask app. Add the Procfile to your project root folder. The folder structure should now be as follows: <code>ProjectFolder |main.py |app.py |.env |.gitignore | requirements.txt | Procfile └───images │ │ abc.png │ │ chinua.jpg │ │ story.pdf │ │ wams.png</code></p>
<blockquote>
<p>The Procfile does not have a file extension</p>
</blockquote>
<p>Add the following to your Procfile</p>
<pre><code class="lang-shell">web: python app.py
</code></pre>
<h3 id="heading-create-the-heroku-app">Create the Heroku App</h3>
<blockquote>
<p>Ensure you have the CLI and Git installed on your system</p>
</blockquote>
<p>Create a Heroku app using the command</p>
<pre><code class="lang-shell">heroku create unique_name_for_your_bot
</code></pre>
<p>Alternatively, you can let Heroku create a unique name for your bot by running</p>
<pre><code class="lang-shell">heroku create
</code></pre>
<p>Running the command gives two URLs. One is the application URL and the other is a Git URL.</p>
<p>in your .env file add the application URL, we will need it later. Your .env should now look as follows:</p>
<pre><code class="lang-bash">COG_SERVICE_ENDPOINT=your_azure_endpoint_goes_here
COG_SERVICE_KEY=your_azure_key_goes_here
TELEGRAM_TOKEN=your_telegram_token_goes_here
BOT_URL=your_heroku_application_url_goes_here
PORT=5000
</code></pre>
<h3 id="heading-python-buildpack">Python Buildpack</h3>
<p>Since our application runs on Python, we need to set our Heroku environment to use python. Heroku does this using buildpacks. Add the python buildpack using:</p>
<pre><code class="lang-shell">
heroku buildpacks:set heroku/python
</code></pre>
<h3 id="heading-modifying-the-apppy-file">Modifying the app.py file</h3>
<p>Before we can deploy the code to Heroku we need to change a few things in our app.py code</p>
<pre><code class="lang-py">...
dispatcher.add_handler(MessageHandler(Filters.all, extract_text_from_telegram))
<span class="hljs-comment"># updater.start_polling() &lt;--- comment this out</span>

<span class="hljs-comment"># add the webhook code</span>
updater.start_webhook(listen=<span class="hljs-string">"0.0.0.0"</span>,
                      port=int(os.environ.get(<span class="hljs-string">'PORT'</span>, <span class="hljs-number">8080</span>)),
                      url_path=TELEGRAM_TOKEN,
                      webhook_url=os.getenv(<span class="hljs-string">'BOT_URL'</span>) + TELEGRAM_TOKEN
                      )
</code></pre>
<p>We are switching from polling to webhooks. Polling asks telegram if the user has sent any new message every few seconds while a webhook waits until the user sends a message to act on it. Thus we are telling telegram to listen to any new messages from the Heroku application URL we provide.</p>
<h3 id="heading-deploying">Deploying</h3>
<p>With git already installed, you need to first initialize git in the repo then push the code to Heroku. Run the following commands sequentially in your terminal:</p>
<blockquote>
<p>Make sure to add .env file in your .gitignore</p>
</blockquote>
<ul>
<li><p>Initialise git <code>shell git init</code></p>
</li>
<li><p>Add all files except those listed in the .gitignore to git <code>shell git add .</code></p>
</li>
<li><p>Commit <code>shell git commit -m "Done with my telegram azure bot "</code></p>
</li>
<li><p>Deploy</p>
</li>
</ul>
<pre><code class="lang-shell">git push heroku main
</code></pre>
<h3 id="heading-adding-envs-to-heroku">Adding envs to Heroku</h3>
<p>Log in to your Heroku dashboard and go to the application you created. it will be under the same name you gave Heroku or Heroku created for you. On the settings tab, scroll and click on Reveal Config vars.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1645524054197/BI08UrBqI.png" alt="herokuenv.png" /></p>
<p>From there just copy-paste all the vars in your .env.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1645524545429/exmwt42C1.png" alt="vars.png" /></p>
<h1 id="heading-conclusion">Conclusion</h1>
<iframe src="https://giphy.com/embed/3oz8xAFtqoOUUrsh7W" width="480" height="320" class="giphy-embed"></iframe>

<p><a target="_blank" href="https://giphy.com/gifs/studiosoriginals-3oz8xAFtqoOUUrsh7W">via GIPHY</a></p>
<p>Good job! You did it. You should now have the app running on Heroku. Note that the free dynos on Heroku sleep when they are idle for a while thus you may find that the next time you use your telegram bot you may have delays. Worry not! The dynos take a minute to go back up online.</p>
<blockquote>
<p>Full code can be found <a target="_blank" href="https://github.com/wamaithaNyamu/Building-a-telegram-bot-using-azure-cognitive-services">here</a></p>
</blockquote>
]]></content:encoded></item><item><title><![CDATA[Loan App Prediction using machine learning in Python]]></title><description><![CDATA[Angela has some disposable income that she wants to invest in a money lending business. This kind of business has several risk factors associated with it. How can Angela make data driven decisions on whom to lend money to?
Expectations
The end goal i...]]></description><link>https://wamaithanyamu.com/loan-app-prediction-using-machine-learning-in-python</link><guid isPermaLink="true">https://wamaithanyamu.com/loan-app-prediction-using-machine-learning-in-python</guid><category><![CDATA[Python]]></category><category><![CDATA[Machine Learning]]></category><category><![CDATA[data analysis]]></category><category><![CDATA[Dataanalysis]]></category><category><![CDATA[Predictive modeling]]></category><dc:creator><![CDATA[Wamaitha ]]></dc:creator><pubDate>Mon, 24 Jan 2022 17:44:19 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1672517728156/84584172-8bbb-418d-a560-cb414f75446a.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Angela has some disposable income that she wants to invest in a money lending business. This kind of business has several risk factors associated with it. How can Angela make data driven decisions on whom to lend money to?</p>
<p><strong>Expectations</strong></p>
<p>The end goal is to make this a binary classification problem where given a set of inputs, the model either gives a prediction of approved or rejected. The approved prediction means Angela can go ahead and give the loan to the applicant while rejection means Angela will either have to revisit the decision manually or outright trust the model.</p>
<p><strong>Loading the Data</strong></p>
<p>For this problem, we will be using the data set on loan prediction on Kaggle. You can download the data from <a target="_blank" href="https://www.kaggle.com/altruistdelhite04/loan-prediction-problem-dataset">here</a></p>
<blockquote>
<p>You will need to use scikit-learn version 0.23.2 for the project to run smoothly</p>
</blockquote>
<pre><code class="lang-py">pip install -U scikit-learn==<span class="hljs-number">0.23</span><span class="hljs-number">.2</span>
</code></pre>
<p>Import the relevant libraries needed ```py</p>
<h1 id="heading-importing-important-libraries">Importing important libraries</h1>
<p>import pandas as pd import numpy as np import matplotlib.pyplot as plt import seaborn as sns import plotly import plotly.express as px import plotly.figure_factory as ff from sklearn.utils import resample from sklearn.preprocessing import StandardScaler , MinMaxScaler from collections import Counter from scipy import stats</p>
<h1 id="heading-classifiers">Classifiers</h1>
<p>from sklearn.ensemble import AdaBoostClassifier , GradientBoostingClassifier , VotingClassifier , RandomForestClassifier from sklearn.linear_model import LogisticRegression , RidgeClassifier from sklearn.discriminant_analysis import LinearDiscriminantAnalysis from sklearn.model_selection import RepeatedStratifiedKFold from sklearn.neighbors import KNeighborsClassifier from sklearn.model_selection import GridSearchCV from sklearn.tree import DecisionTreeClassifier</p>
<p>from sklearn import tree from sklearn.naive_bayes import GaussianNB from xgboost import plot_importance from xgboost import XGBClassifier from sklearn.svm import SVC</p>
<p>#Model evaluation tools from sklearn.metrics import classification_report , accuracy_score , confusion_matrix from sklearn.metrics import accuracy_score,f1_score from sklearn.model_selection import cross_val_score</p>
<p>#Data processing functions from sklearn.preprocessing import StandardScaler from sklearn.model_selection import train_test_split from sklearn import model_selection from sklearn.preprocessing import LabelEncoder le = LabelEncoder()</p>
<pre><code class="lang-bash">Load the data from the csv. Replace ”./path to csv” with <span class="hljs-built_in">where</span> your data is located.

```py
    data = pd.read_csv(<span class="hljs-string">"./path to csv"</span>)
</code></pre>
<p>We load the dataset as a pandas dataframe and store it in data. We can view our data as follows <code>py data.head(4)</code></p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1643045579487/7_25hb0-D.png" alt="dataframe.png" /></p>
<p><strong>Exploratory data analysis</strong> We first draw a scatter plot of all variables against themselves in a bivariate fashion. <code>py sns.pairplot(data) plt.show()</code></p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1643097775135/kwiqRsPAY.jpeg" alt="pairplot.jpeg" /></p>
<pre><code class="lang-py">data.describe()
</code></pre>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1643098207841/ZMyi9CsfxC.png" alt="describe.png" /></p>
<p>From the above results from the describe function, we can see that some columns have missing values as indicated by the count. The standard deviation(std) also tells us more on how the data is distributed. Low standard deviation means data points are clustered around the mean, and high standard deviation indicates data is more spread out. We might need to investigate further on the data points on the ApplicantIncome and CoapplicantIncome columns as the std is too high which might indicate presence of outliers. Before we can do any manipulations we need to understand our column datatype using the info() function. <code>py data.info()</code></p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1643098251222/3GaT5swAb.png" alt="info.png" /></p>
<p>We have a dataset with 4 distinct datatype. The Loan_ID column does not help with our predictions, so we drop it. You will not qualify for a loan just because your loan id is a certain value.</p>
<pre><code class="lang-py">data = data.drop([<span class="hljs-string">"Loan_ID"</span>],axis=<span class="hljs-number">1</span>)
</code></pre>
<p>Are graduates more eligible for loans than non-graduands?</p>
<pre><code class="lang-py">
                sns.catplot(x=<span class="hljs-string">'Loan_Status'</span>,
                            y=<span class="hljs-string">'LoanAmount'</span>,
                            hue=<span class="hljs-string">"Education"</span>,
                            ci=<span class="hljs-string">"sd"</span>,
                            palette=<span class="hljs-string">"dark"</span>,
                            alpha=<span class="hljs-number">.6</span>,
                            height=<span class="hljs-number">6</span>,
                            data=data,
                            kind=<span class="hljs-string">'bar'</span>,
                           );
</code></pre>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1643098306504/ioq_-sUdc.png" alt="graduatLoanStatus.png" /></p>
<p>Are women more likely to get loans than men?</p>
<pre><code class="lang-py">  sns.catplot(x=<span class="hljs-string">'Loan_Status'</span>,
                            y=<span class="hljs-string">'LoanAmount'</span>,
                            hue=<span class="hljs-string">"Gender"</span>,
                            ci=<span class="hljs-string">"sd"</span>,
                            palette=<span class="hljs-string">"dark"</span>,
                            alpha=<span class="hljs-number">.6</span>,
                            height=<span class="hljs-number">6</span>,
                            data=data,
                            kind=<span class="hljs-string">'bar'</span>,
                    );
</code></pre>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1643098343527/Xa-pfKkgf.png" alt="genderLoanStatus.png" /></p>
<p>Are single people more likely to get loans than married people? ```py sns.catplot(x='Loan_Status', y='LoanAmount', hue="Married", ci="sd", palette="dark", alpha=.6, height=6, data=data, kind='bar', );</p>
<pre><code class="lang-bash">
![maritalLoanStatus.png](https://cdn.hashnode.com/res/hashnode/image/upload/v1643098374749/n7IoRKeoe.png)
From the std, we suspect having outliers <span class="hljs-keyword">in</span> the Applicant Income and coapplicant income. Lets start by first visualizing the distribution of the data <span class="hljs-keyword">in</span> the ApplicantIncome to verify that we indeed have outliers that are accounting <span class="hljs-keyword">for</span> the high std.
```py

fig = px.scatter_matrix(data[<span class="hljs-string">"ApplicantIncome"</span>])
fig.update_layout(width=700,height=400)
fig.show()
</code></pre>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1643098413754/oondQfg6H.png" alt="applicant.png" /></p>
<p>The ApplicantIncome scatter plot visually confirms that the column has outliers. Next we work on the visualization of the CoapplicantIncome column.</p>
<pre><code class="lang-py">
fig = px.scatter_matrix(data[<span class="hljs-string">"CoapplicantIncome"</span>])
fig.update_layout(width=<span class="hljs-number">700</span>,height=<span class="hljs-number">400</span>)
fig.show()
</code></pre>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1643098455097/gYtNDjRVs.png" alt="coapplicant.png" /></p>
<p>As expected, the visual plot of the Coapplicantincome also shows our outliers.</p>
<blockquote>
<p>To make work easier as the notebook grows, I will be writing all our model predictions on an external txt</p>
</blockquote>
<pre><code class="lang-py">
<span class="hljs-keyword">import</span> os
<span class="hljs-keyword">if</span> os.path.exists(<span class="hljs-string">"results.txt"</span>):
   os.remove(<span class="hljs-string">"results.txt"</span>)
<span class="hljs-keyword">else</span>:
   print(<span class="hljs-string">"The file does not exist"</span>)
<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">write_results_to_file</span>(<span class="hljs-params">txt</span>):</span>

  f = open(<span class="hljs-string">"results.txt"</span>,<span class="hljs-string">"a"</span>)
  f.write(txt + <span class="hljs-string">'\n'</span>)
  f.close()

<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">print_line</span>(<span class="hljs-params">name</span>):</span>
    write_results_to_file(<span class="hljs-string">f'------------------------------------------------------------<span class="hljs-subst">{name}</span>----------------------------------'</span>)
    print(<span class="hljs-string">f'------------------------------------------------------------<span class="hljs-subst">{name}</span>----------------------------------'</span>)
</code></pre>
<p>The above code just removes any old files named results.txt on our file system and creates one if it does not exist. The write_results_to_file appends a new line to the results.txt while the print_line adds a divider to the write_results_to_file file. Checking the distribution of the data</p>
<p>Ideally,to get the best possible results our data needs to be in a normal distribution. Data in a normal distribution observes the bell curve.</p>
<pre><code class="lang-py"><span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">check_distribution_of_column</span>(<span class="hljs-params">column_name</span>):</span>
  fig = px.histogram(data[column_name],x =column_name,y = column_name)
  fig.update_layout(title=column_name)
  fig.show()
</code></pre>
<p>The function check_distribution_of_column takes in a column name and plots a histogram showing the distribution of data in that column. We iterate through all the columns in our data-frame calling on our handy function to draw the distribution.</p>
<pre><code class="lang-py">
<span class="hljs-keyword">for</span> col <span class="hljs-keyword">in</span> data.columns:
    print(<span class="hljs-string">"Now showing the distribution of the "</span> + col + <span class="hljs-string">" column:"</span>)
    check_distribution_of_column(col)
</code></pre>
<p>You should be having individual plots of each column. The loan amount plot looks like this</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1643098567741/x_5F6bneC.png" alt="loandistribution.png" /></p>
<p>From the above graphs we can see that we have a mix of categorical variables and discrete variables. Categorical variables contain a finite number of categories or distinct groups. Categorical data might not have a logical order. For example, categorical predictors for our data-set are gender,marital status,self employed etc. Discrete variables are numeric variables that have a countable number of values between any two values for example the applicants income, coapplicantincome and loan amount. With all these, we can now begin some data cleaning.</p>
<p><strong>Data Cleaning</strong></p>
<p>From our EDA we have established we have some problems we need to fix with the data :</p>
<p>Deal with null values to ensure our data is of same length Deal with outliers Find a way of converting categorical variables to discrete variables since our model will expect to use discrete values. Find a way to make the data normally distributed</p>
<ol>
<li>Dealing with null values We have a few options:</li>
</ol>
<p>Drop all rows with at least one null value Replace the null value with either the mode, mean or median of the column. For the first approach, we do not have much data to work with as such I will be exploring the second option. Also dropping the null values doesnt necessarily mean that we will not be dropping more rows as we continue with the data cleaning. We might need to drop more rows in future and thus greatly reduce our dataset. Option 2 is our best bet.</p>
<pre><code class="lang-py">
<span class="hljs-comment"># shows number of missing values by column</span>
data.isnull().sum()
</code></pre>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1643270669741/cBzV1NSqX.png" alt="nullValues.png" /></p>
<p>For the categorical variables, I will use the mode of the column. For example, we have more men in the dataset so the probability of someone with a null value having being a man was higher than it being a woman.</p>
<pre><code class="lang-py">
data[<span class="hljs-string">"Gender"</span>].fillna(data[<span class="hljs-string">"Gender"</span>].mode()[<span class="hljs-number">0</span>],inplace=<span class="hljs-literal">True</span>)
data[<span class="hljs-string">"Married"</span>].fillna(data[<span class="hljs-string">"Married"</span>].mode()[<span class="hljs-number">0</span>],inplace=<span class="hljs-literal">True</span>)
data[<span class="hljs-string">"Self_Employed"</span>].fillna(data[<span class="hljs-string">"Self_Employed"</span>].mode()[<span class="hljs-number">0</span>],inplace=<span class="hljs-literal">True</span>)
data[<span class="hljs-string">"Loan_Amount_Term"</span>].fillna(data[<span class="hljs-string">"Loan_Amount_Term"</span>].mode()[<span class="hljs-number">0</span>],inplace=<span class="hljs-literal">True</span>)
data[<span class="hljs-string">"Dependents"</span>].fillna(data[<span class="hljs-string">"Dependents"</span>].mode()[<span class="hljs-number">0</span>],inplace=<span class="hljs-literal">True</span>)
data[<span class="hljs-string">"Credit_History"</span>].fillna(data[<span class="hljs-string">"Credit_History"</span>].mode()[<span class="hljs-number">0</span>],inplace=<span class="hljs-literal">True</span>)
</code></pre>
<pre><code class="lang-py">
<span class="hljs-comment"># shows number of missing values by column</span>
data.isnull().sum()
</code></pre>
<p>Now checking for null values, we notice only the LoanAmount still has nulls which is a discrete variable.To fix this we will use the median since we had already established an outlier for this column. Thus using the mean might not give an accurate representation of roughly how much might have been borrowed.</p>
<pre><code class="lang-py">data[<span class="hljs-string">"LoanAmount"</span>].fillna(data[<span class="hljs-string">"LoanAmount"</span>].median(),inplace=<span class="hljs-literal">True</span>)
</code></pre>
<p>All done with dealing with null values.</p>
<p><strong>2. Dealing with outliers</strong> We have three discrete variables of which two have outliers. <code>py data["ApplicantIncome"] = np.log(data["ApplicantIncome"]) #As "CoapplicantIncome" columns has some "0" values we will get log values except "0" since log 0 is undefined data["CoapplicantIncome"] = [np.log(i) if i!=0 else 0 for i in data["CoapplicantIncome"]] data["LoanAmount"] = np.log(data["LoanAmount"])</code> We apply the natural logarithm to all our discrete variables to normalize the data. Natural log transformations reduce skewness to our datasets. Compare our loan amount distribution now with what we had above.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1643270852685/DrGtQPmr7.png" alt="normLoan.png" /></p>
]]></content:encoded></item></channel></rss>