<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>The Ops Community ⚙️: Akilesh</title>
    <description>The latest articles on The Ops Community ⚙️ by Akilesh (@akilesh).</description>
    <link>https://community.ops.io/akilesh</link>
    <image>
      <url>https://community.ops.io/images/DYidFCz8TltCL5Glx7BY_WxbHyW_0HgkP289ij3xXh0/rs:fill:90:90/g:sm/mb:500000/ar:1/aHR0cHM6Ly9jb21t/dW5pdHkub3BzLmlv/L3JlbW90ZWltYWdl/cy91cGxvYWRzL3Vz/ZXIvcHJvZmlsZV9p/bWFnZS8xNjUvM2Q1/ZTZmZWEtZDNhZi00/NWYwLWE3YzQtYjIz/NmE3N2FhN2IzLnBu/Zw</url>
      <title>The Ops Community ⚙️: Akilesh</title>
      <link>https://community.ops.io/akilesh</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://community.ops.io/feed/akilesh"/>
    <language>en</language>
    <item>
      <title>Digital Ocean DNS Config</title>
      <dc:creator>Akilesh</dc:creator>
      <pubDate>Thu, 02 Jun 2022 17:23:36 +0000</pubDate>
      <link>https://community.ops.io/akilesh/digital-ocean-dnd-config-36io</link>
      <guid>https://community.ops.io/akilesh/digital-ocean-dnd-config-36io</guid>
      <description>&lt;p&gt;We will be creating a droplet in the digital ocean with a docker container running on it and on top of its IP address we will be adding our Domain Name System.&lt;/p&gt;

&lt;h2&gt;
  
  
  Purchase a domain
&lt;/h2&gt;

&lt;p&gt;To set up a domain name, you need to purchase a domain name from a domain name registrar ex : GoDaddy, Namecheap, Google Domains etc…&lt;br&gt;
I did purchase my domain using Namecheap, but other registrars will also offer the same features.&lt;/p&gt;
&lt;h2&gt;
  
  
  Set up DNS
&lt;/h2&gt;

&lt;p&gt;Choose your domain name which needs to be configured and set up custom DNS records to make DigitalOcean accessible be your domain name registrar.&lt;br&gt;
&lt;a href="https://community.ops.io/images/8NpHZOC-uss0m71JLNi4jzGCIxCs-c8RaQc2O0lRdFs/w:880/mb:500000/ar:1/aHR0cHM6Ly9kZXYt/dG8tdXBsb2Fkcy5z/My5hbWF6b25hd3Mu/Y29tL3VwbG9hZHMv/YXJ0aWNsZXMvcWRw/aXNzc2pobHZ6bXl1/MTBrOHMucG5n" class="article-body-image-wrapper"&gt;&lt;img src="https://community.ops.io/images/8NpHZOC-uss0m71JLNi4jzGCIxCs-c8RaQc2O0lRdFs/w:880/mb:500000/ar:1/aHR0cHM6Ly9kZXYt/dG8tdXBsb2Fkcy5z/My5hbWF6b25hd3Mu/Y29tL3VwbG9hZHMv/YXJ0aWNsZXMvcWRw/aXNzc2pobHZ6bXl1/MTBrOHMucG5n" alt="Image description" width="880" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Add this below configuration to your DNS record.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ns1.digitalocean.com
ns2.digitalocean.com
ns3.digitalocean.com 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Save the settings and wait for a minute or so for DigitalOcean to identify your DNS.&lt;/p&gt;

&lt;p&gt;Now we are ready to move on to connecting the domain with your Droplet in the DigitalOcean control panel.&lt;/p&gt;

&lt;h2&gt;
  
  
  control panel
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;Assuming that you have been already created a droplet with some content in it and could be able to preview the content through its IP address on the web.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Moving to our digital ocean control panel we can choose the &lt;code&gt;Add a domain&lt;/code&gt; option from the dropdown&lt;br&gt;
&lt;a href="https://community.ops.io/images/3oVxhZq2grvUt4ETi8sbqj5_z2vj4kyn458HQp0RTro/w:880/mb:500000/ar:1/aHR0cHM6Ly9kZXYt/dG8tdXBsb2Fkcy5z/My5hbWF6b25hd3Mu/Y29tL3VwbG9hZHMv/YXJ0aWNsZXMva3Zs/MXoxZW82ODM1NDF3/cG56eTEucG5n" class="article-body-image-wrapper"&gt;&lt;img src="https://community.ops.io/images/3oVxhZq2grvUt4ETi8sbqj5_z2vj4kyn458HQp0RTro/w:880/mb:500000/ar:1/aHR0cHM6Ly9kZXYt/dG8tdXBsb2Fkcy5z/My5hbWF6b25hd3Mu/Y29tL3VwbG9hZHMv/YXJ0aWNsZXMva3Zs/MXoxZW82ODM1NDF3/cG56eTEucG5n" alt="Image description" width="880" height="130"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Add your domain name in the text area then click the button to create a new record.&lt;br&gt;
&lt;a href="https://community.ops.io/images/RDgBgH6b8LrP3QNwQvMiuIFbm7Kyxo06WkaijdV710w/w:880/mb:500000/ar:1/aHR0cHM6Ly9kZXYt/dG8tdXBsb2Fkcy5z/My5hbWF6b25hd3Mu/Y29tL3VwbG9hZHMv/YXJ0aWNsZXMvMW93/Ynh0dnM0angyNXUz/dzR2OTcucG5n" class="article-body-image-wrapper"&gt;&lt;img src="https://community.ops.io/images/RDgBgH6b8LrP3QNwQvMiuIFbm7Kyxo06WkaijdV710w/w:880/mb:500000/ar:1/aHR0cHM6Ly9kZXYt/dG8tdXBsb2Fkcy5z/My5hbWF6b25hd3Mu/Y29tL3VwbG9hZHMv/YXJ0aWNsZXMvMW93/Ynh0dnM0angyNXUz/dzR2OTcucG5n" alt="Image description" width="880" height="418"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  New record
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://community.ops.io/images/PKQc6-PxBR_rmfb273ggvOGOJkzYWBxfXui1jrzAflE/w:880/mb:500000/ar:1/aHR0cHM6Ly9kZXYt/dG8tdXBsb2Fkcy5z/My5hbWF6b25hd3Mu/Y29tL3VwbG9hZHMv/YXJ0aWNsZXMvdzc5/cXR5dWl6dXVwazEy/bm93dnAucG5n" class="article-body-image-wrapper"&gt;&lt;img src="https://community.ops.io/images/PKQc6-PxBR_rmfb273ggvOGOJkzYWBxfXui1jrzAflE/w:880/mb:500000/ar:1/aHR0cHM6Ly9kZXYt/dG8tdXBsb2Fkcy5z/My5hbWF6b25hd3Mu/Y29tL3VwbG9hZHMv/YXJ0aWNsZXMvdzc5/cXR5dWl6dXVwazEy/bm93dnAucG5n" alt="Image description" width="880" height="347"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  to b continued 🚧
&lt;/h1&gt;

</description>
    </item>
    <item>
      <title>Plausible Manual Setup</title>
      <dc:creator>Akilesh</dc:creator>
      <pubDate>Tue, 31 May 2022 17:31:24 +0000</pubDate>
      <link>https://community.ops.io/akilesh/plausible-manual-setup-26fn</link>
      <guid>https://community.ops.io/akilesh/plausible-manual-setup-26fn</guid>
      <description>&lt;p&gt;From the &lt;a href="https://community.ops.io/akilesh/plausible-in-minutes-1go6"&gt;previous post&lt;/a&gt;, you know that plausible is a simple, lightweight, open-source alternative to Google Analytics.  &lt;/p&gt;

&lt;p&gt;Check out their &lt;a href="https://plausible.io/"&gt;service&lt;/a&gt;, which offers a variety of features and seamless integration in under two minutes.&lt;/p&gt;

&lt;p&gt;We will self-host plausible in a cloud. All the configurations and the services used in this post could be modified according to your needs.&lt;/p&gt;

&lt;h2&gt;
  
  
  Digital Ocean
&lt;/h2&gt;

&lt;p&gt;You need a &lt;a href="https://www.digitalocean.com/"&gt;digital ocean&lt;/a&gt; account to create a cloud server.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;You can feel free to choose your own cloud service provides (&lt;a href="https://aws.amazon.com/"&gt;AWS&lt;/a&gt;, &lt;a href="https://azure.microsoft.com"&gt;Azure&lt;/a&gt;,...) to create a instance.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Droplet
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Create a new droplet aka server by clicking the &lt;br&gt;
&lt;code&gt;Create &amp;gt; Drolets&lt;/code&gt;.&lt;br&gt;
&lt;a href="https://community.ops.io/images/bAHKB1ZifRbYwUbLdERVHyZqoCtAn-KGgo28XEubbC0/w:880/mb:500000/ar:1/aHR0cHM6Ly9kZXYt/dG8tdXBsb2Fkcy5z/My5hbWF6b25hd3Mu/Y29tL3VwbG9hZHMv/YXJ0aWNsZXMvNm1l/ejc4cjRpZnNvM2h4/c2Z5dWwuanBn" class="article-body-image-wrapper"&gt;&lt;img src="https://community.ops.io/images/bAHKB1ZifRbYwUbLdERVHyZqoCtAn-KGgo28XEubbC0/w:880/mb:500000/ar:1/aHR0cHM6Ly9kZXYt/dG8tdXBsb2Fkcy5z/My5hbWF6b25hd3Mu/Y29tL3VwbG9hZHMv/YXJ0aWNsZXMvNm1l/ejc4cjRpZnNvM2h4/c2Z5dWwuanBn" alt="Image description" width="880" height="247"&gt;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Choose a Linux distributor you are good at using. I'll choose Ubuntu.&lt;br&gt;
&lt;a href="https://community.ops.io/images/d0FnH7rekkzDo3jSGK_lFdqzF_Dvd9QydFcEF5N-f2Q/w:880/mb:500000/ar:1/aHR0cHM6Ly9kZXYt/dG8tdXBsb2Fkcy5z/My5hbWF6b25hd3Mu/Y29tL3VwbG9hZHMv/YXJ0aWNsZXMveHY2/Z2JkaG1zZXhweXU4/MGEzaGUuanBn" class="article-body-image-wrapper"&gt;&lt;img src="https://community.ops.io/images/d0FnH7rekkzDo3jSGK_lFdqzF_Dvd9QydFcEF5N-f2Q/w:880/mb:500000/ar:1/aHR0cHM6Ly9kZXYt/dG8tdXBsb2Fkcy5z/My5hbWF6b25hd3Mu/Y29tL3VwbG9hZHMv/YXJ0aWNsZXMveHY2/Z2JkaG1zZXhweXU4/MGEzaGUuanBn" alt="Image description" width="880" height="613"&gt;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Choose a datacenter region located close to your current location.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The authentication process in the cloud is a tricky one. We will use the SSH key to secure our droplet to know more about how to connect using SSH. Refer to this &lt;a href="https://dev.to/akilesh/types-of-ssh-ways-to-connect-2afd-temp-slug-2530675"&gt;blog&lt;/a&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;All the setting up process is done, you can click the create a droplet and wait for a few minutes until the droplet gets ready to run.&lt;br&gt;
&lt;a href="https://community.ops.io/images/RMSZhnYvkstWZ3ODlfq-q9FiO2E9XfUTNaQqr9-a-7E/w:880/mb:500000/ar:1/aHR0cHM6Ly9kZXYt/dG8tdXBsb2Fkcy5z/My5hbWF6b25hd3Mu/Y29tL3VwbG9hZHMv/YXJ0aWNsZXMvYml4/ejEwYW1reDU2aTY0/c2ZycHouanBn" class="article-body-image-wrapper"&gt;&lt;img src="https://community.ops.io/images/RMSZhnYvkstWZ3ODlfq-q9FiO2E9XfUTNaQqr9-a-7E/w:880/mb:500000/ar:1/aHR0cHM6Ly9kZXYt/dG8tdXBsb2Fkcy5z/My5hbWF6b25hd3Mu/Y29tL3VwbG9hZHMv/YXJ0aWNsZXMvYml4/ejEwYW1reDU2aTY0/c2ZycHouanBn" alt="Image description" width="880" height="100"&gt;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Droplet Console
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;For the shake of our covenant, we will use an inbuilt web - based terminal console to access our droplet.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;a href="https://community.ops.io/images/IQXycm5-KaFJ2Z9ydqvKayUKphX7QTAr6SL98SESng4/w:880/mb:500000/ar:1/aHR0cHM6Ly9kZXYt/dG8tdXBsb2Fkcy5z/My5hbWF6b25hd3Mu/Y29tL3VwbG9hZHMv/YXJ0aWNsZXMvY2h0/eXphbTJ5ZWxsNGZh/dXUzNm4uanBn" class="article-body-image-wrapper"&gt;&lt;img src="https://community.ops.io/images/IQXycm5-KaFJ2Z9ydqvKayUKphX7QTAr6SL98SESng4/w:880/mb:500000/ar:1/aHR0cHM6Ly9kZXYt/dG8tdXBsb2Fkcy5z/My5hbWF6b25hd3Mu/Y29tL3VwbG9hZHMv/YXJ0aWNsZXMvY2h0/eXphbTJ5ZWxsNGZh/dXUzNm4uanBn" alt="Image description" width="880" height="373"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;1) Lets first clone the &lt;a href="https://github.com/plausible/hosting"&gt;plausible/hosting&lt;/a&gt; repo.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ git clone https://github.com/plausible/hosting
$ cd hosting
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://community.ops.io/images/f47pcXbOW2mhwBnKLtZv2AahQB6EQCIdV-dwKvcRMqE/w:880/mb:500000/ar:1/aHR0cHM6Ly9kZXYt/dG8tdXBsb2Fkcy5z/My5hbWF6b25hd3Mu/Y29tL3VwbG9hZHMv/YXJ0aWNsZXMveG5w/eHdwNm40dTYxNjZ4/eXZlNTkuanBn" class="article-body-image-wrapper"&gt;&lt;img src="https://community.ops.io/images/f47pcXbOW2mhwBnKLtZv2AahQB6EQCIdV-dwKvcRMqE/w:880/mb:500000/ar:1/aHR0cHM6Ly9kZXYt/dG8tdXBsb2Fkcy5z/My5hbWF6b25hd3Mu/Y29tL3VwbG9hZHMv/YXJ0aWNsZXMveG5w/eHdwNm40dTYxNjZ4/eXZlNTkuanBn" alt="Image description" width="880" height="266"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;2) We have access to mess with two files and explore what they need to offer &lt;code&gt;docker-compose.yml&lt;/code&gt; &amp;amp; &lt;code&gt;plausible-conf.env&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://community.ops.io/images/rQ2kTObAaVTJF987-84cVWwf141DDiL72LnjeN9H01Y/w:880/mb:500000/ar:1/aHR0cHM6Ly9kZXYt/dG8tdXBsb2Fkcy5z/My5hbWF6b25hd3Mu/Y29tL3VwbG9hZHMv/YXJ0aWNsZXMvZ24x/NnZ6cmtza29ucWoz/M3F2cmguanBn" class="article-body-image-wrapper"&gt;&lt;img src="https://community.ops.io/images/rQ2kTObAaVTJF987-84cVWwf141DDiL72LnjeN9H01Y/w:880/mb:500000/ar:1/aHR0cHM6Ly9kZXYt/dG8tdXBsb2Fkcy5z/My5hbWF6b25hd3Mu/Y29tL3VwbG9hZHMv/YXJ0aWNsZXMvZ24x/NnZ6cmtza29ucWoz/M3F2cmguanBn" alt="Image description" width="880" height="41"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In &lt;code&gt;plausible-conf.env&lt;/code&gt; we need to fill the required parameters&lt;br&gt;
for these placeholders.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ADMIN_USER_EMAIL=replace-me
ADMIN_USER_NAME=replace-me
ADMIN_USER_PWD=replace-me
BASE_URL=replace-me
SECRET_KEY_BASE=replace-me
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;SECRET_KEY_BASE&lt;/code&gt;
Now to set the parameters we'll first need a random 64-character secret key which will be used to secure the app. Use the below command to generate one.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ openssl rand -base64 64 | tr -d '\n' ; echo
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now edit &lt;code&gt;plausible-conf.env&lt;/code&gt; and set &lt;code&gt;SECRET_KEY_BASE&lt;/code&gt; to your secret key.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;BASE_URL&lt;/code&gt;
It should be the base URL where this instance is accessible, including the scheme (e.g. http:// or https://), the domain name, and, optionally, a port. If no port is specified the default 8000.
ex: &lt;code&gt;https://1x9.5x.x6.x4:8000&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;ADMIN_USER&lt;/code&gt;
You just fill your details email id, User name: Password (PWD).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;&lt;u&gt;Reference&lt;/u&gt;&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ADMIN_USER_EMAIL=2112akilesh@gmail.com
ADMIN_USER_NAME=akilesh
ADMIN_USER_PWD=xxxxxxxxxx
BASE_URL=https://1x9.5x.x6.x4
SECRET_KEY_BASE=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Start the server
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;Docker installation commend on ubuntu&lt;br&gt;
&lt;/p&gt;
&lt;/blockquote&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ apt install docker-compose
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once you've entered your secret key base, base URL and admin credentials, you're ready to start up the server:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ docker-compose up -d
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This commend will execute the following process:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;   Creates a Postgres database for user data&lt;/li&gt;
&lt;li&gt;   Creates a Clickhouse database for stats&lt;/li&gt;
&lt;li&gt;   Runs migrations on both databases to prepare the schema&lt;/li&gt;
&lt;li&gt;   Creates an admin account (which is just a normal account with a generous 100 years of free trial)&lt;/li&gt;
&lt;li&gt;   Starts the server on port 8000&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;To check your docker status, run this commends in your terminal:&lt;br&gt;
&lt;/p&gt;
&lt;/blockquote&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sudo systemctl status docker
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://community.ops.io/images/AmVx_Yb04smo_k8iNe1e2Q7QLmQsxa9Y502O8bzUy98/w:880/mb:500000/ar:1/aHR0cHM6Ly9kZXYt/dG8tdXBsb2Fkcy5z/My5hbWF6b25hd3Mu/Y29tL3VwbG9hZHMv/YXJ0aWNsZXMvc2Zh/eGI3ejhudDVqNHU3/c2xvc2suanBn" class="article-body-image-wrapper"&gt;&lt;img src="https://community.ops.io/images/AmVx_Yb04smo_k8iNe1e2Q7QLmQsxa9Y502O8bzUy98/w:880/mb:500000/ar:1/aHR0cHM6Ly9kZXYt/dG8tdXBsb2Fkcy5z/My5hbWF6b25hd3Mu/Y29tL3VwbG9hZHMv/YXJ0aWNsZXMvc2Zh/eGI3ejhudDVqNHU3/c2xvc2suanBn" alt="Image description" width="880" height="240"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can now navigate to http://{hostname}:8000 and see the login screen.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://community.ops.io/images/miFVyugSzVDhiVDZmuaEjKHrkH_7G_lV-yRvwN-tQBo/w:880/mb:500000/ar:1/aHR0cHM6Ly9kZXYt/dG8tdXBsb2Fkcy5z/My5hbWF6b25hd3Mu/Y29tL3VwbG9hZHMv/YXJ0aWNsZXMvcG90/dnJkMGh1OHdlYXBl/aWhpanYuanBn" class="article-body-image-wrapper"&gt;&lt;img src="https://community.ops.io/images/miFVyugSzVDhiVDZmuaEjKHrkH_7G_lV-yRvwN-tQBo/w:880/mb:500000/ar:1/aHR0cHM6Ly9kZXYt/dG8tdXBsb2Fkcy5z/My5hbWF6b25hd3Mu/Y29tL3VwbG9hZHMv/YXJ0aWNsZXMvcG90/dnJkMGh1OHdlYXBl/aWhpanYuanBn" alt="Image description" width="880" height="504"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;After logging process, add your domain to plausible and repeat the process that we did in their paid tier.&lt;a href="https://community.ops.io/akilesh/plausible-in-minutes-1go6"&gt;Reference&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://community.ops.io/images/ms7hPJRzqlkot4UZYQ46sH3sjgjQglO0YB2vllUSbVw/w:880/mb:500000/ar:1/aHR0cHM6Ly9kZXYt/dG8tdXBsb2Fkcy5z/My5hbWF6b25hd3Mu/Y29tL3VwbG9hZHMv/YXJ0aWNsZXMvODZw/a2t0cXZhMWp2cHh5/MWVuaDIuanBn" class="article-body-image-wrapper"&gt;&lt;img src="https://community.ops.io/images/ms7hPJRzqlkot4UZYQ46sH3sjgjQglO0YB2vllUSbVw/w:880/mb:500000/ar:1/aHR0cHM6Ly9kZXYt/dG8tdXBsb2Fkcy5z/My5hbWF6b25hd3Mu/Y29tL3VwbG9hZHMv/YXJ0aWNsZXMvODZw/a2t0cXZhMWp2cHh5/MWVuaDIuanBn" alt="Image description" width="880" height="289"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;History Repeats🔄&lt;/p&gt;

</description>
      <category>plausible</category>
      <category>analytics</category>
      <category>cloudops</category>
      <category>open</category>
    </item>
    <item>
      <title>Plausible in minutes</title>
      <dc:creator>Akilesh</dc:creator>
      <pubDate>Mon, 30 May 2022 10:04:31 +0000</pubDate>
      <link>https://community.ops.io/akilesh/plausible-in-minutes-1go6</link>
      <guid>https://community.ops.io/akilesh/plausible-in-minutes-1go6</guid>
      <description>&lt;p&gt;Plausible is a lightweight web analytics tool used to check your web traffic and also pinpoints from where the traffic comes from with clear visual charts and Email/Slack reports.&lt;/p&gt;

&lt;p&gt;In this blog I used next.js for reference to show how plausible works, but plausible will work on any framework.&lt;/p&gt;

&lt;h2&gt;
  
  
  Login/Create
&lt;/h2&gt;

&lt;p&gt;Create a &lt;a href="//ttps://plausible.io/"&gt;plausible account&lt;/a&gt; to get started and click the add website button.&lt;/p&gt;

&lt;h2&gt;
  
  
  Add a website
&lt;/h2&gt;

&lt;p&gt;Enter your domain name in the text area (provide the domain name which you have access to) and location will be automatically set accordingly to your device location.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://community.ops.io/images/_8fJC1HdDZHvZHzhAIdmUnrxgBCRnGMx_2C2YXtZ758/w:880/mb:500000/ar:1/aHR0cHM6Ly9kZXYt/dG8tdXBsb2Fkcy5z/My5hbWF6b25hd3Mu/Y29tL3VwbG9hZHMv/YXJ0aWNsZXMvbHQ3/d3dmMWs2YnNidGgy/bnpmeTQuanBn" class="article-body-image-wrapper"&gt;&lt;img src="https://community.ops.io/images/_8fJC1HdDZHvZHzhAIdmUnrxgBCRnGMx_2C2YXtZ758/w:880/mb:500000/ar:1/aHR0cHM6Ly9kZXYt/dG8tdXBsb2Fkcy5z/My5hbWF6b25hd3Mu/Y29tL3VwbG9hZHMv/YXJ0aWNsZXMvbHQ3/d3dmMWs2YnNidGgy/bnpmeTQuanBn" alt="Image description" width="880" height="792"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Code snippet
&lt;/h2&gt;

&lt;p&gt;A code snippet will be generated from our previous details, and it needs to be pasted in our project's head.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://community.ops.io/images/_V4wyFwHfT36uBxSh-74M_EWbiZLLaKeT8R1tyFcqGg/w:880/mb:500000/ar:1/aHR0cHM6Ly9kZXYt/dG8tdXBsb2Fkcy5z/My5hbWF6b25hd3Mu/Y29tL3VwbG9hZHMv/YXJ0aWNsZXMvZDZv/NHNrenNndWhkeHFu/NWR0dHYuanBn" class="article-body-image-wrapper"&gt;&lt;img src="https://community.ops.io/images/_V4wyFwHfT36uBxSh-74M_EWbiZLLaKeT8R1tyFcqGg/w:880/mb:500000/ar:1/aHR0cHM6Ly9kZXYt/dG8tdXBsb2Fkcy5z/My5hbWF6b25hd3Mu/Y29tL3VwbG9hZHMv/YXJ0aWNsZXMvZDZv/NHNrenNndWhkeHFu/NWR0dHYuanBn" alt="Image description" width="880" height="761"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Let's add this script to our project in our way.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Create a meta.tsx file under the components directive and paste the below snippet.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import Head from 'next/head'

export default function Meta() {
    return (
        &amp;lt;Head&amp;gt;
            &amp;lt;script
                defer
                data-domain="akilesh.io"
                src="https://plausible.io/js/plausible.js"&amp;gt;
            &amp;lt;/script&amp;gt;
        &amp;lt;/Head&amp;gt;
    )
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now you can import this meta component to your layout.tsx component if you have one. If not, you can add it to this component to other components which have access to all the pages.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The ides here is to make the &lt;code&gt;&amp;lt;script&amp;gt;&lt;/code&gt; tag accessible by the whole application.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Domain dashboard
&lt;/h2&gt;

&lt;p&gt;Go to your domain dashboard &lt;a href="https://plausible.io/**your_domain_name**"&gt;https://plausible.io/**your_domain_name**&lt;/a&gt; and stay relaxed. Plausible will handle all the complicated process and in a few seconds you will see your webpage's stats.&lt;/p&gt;

&lt;h2&gt;
  
  
  Screen shot:
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;This will be your dashboard until you get someone to visit your website.&lt;br&gt;
&lt;a href="https://community.ops.io/images/v1z7LoFBVKwWeqYYvbZQpELScthLHkr_fqkraSV8Z_g/w:880/mb:500000/ar:1/aHR0cHM6Ly9kZXYt/dG8tdXBsb2Fkcy5z/My5hbWF6b25hd3Mu/Y29tL3VwbG9hZHMv/YXJ0aWNsZXMvcTc5/NzNoMTQybWx6MHN2/bHJiY2ouanBn" class="article-body-image-wrapper"&gt;&lt;img src="https://community.ops.io/images/v1z7LoFBVKwWeqYYvbZQpELScthLHkr_fqkraSV8Z_g/w:880/mb:500000/ar:1/aHR0cHM6Ly9kZXYt/dG8tdXBsb2Fkcy5z/My5hbWF6b25hd3Mu/Y29tL3VwbG9hZHMv/YXJ0aWNsZXMvcTc5/NzNoMTQybWx6MHN2/bHJiY2ouanBn" alt="Image description" width="880" height="391"&gt;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;This is a line chart represents about the total visitors and also will identify unique visitors from the total traffic then the duration of the visiting period.&lt;br&gt;
&lt;a href="https://community.ops.io/images/R5T3ScU-AXXZLUNsWziA1ll__UxVz08MnhWzHnwF0Kw/w:880/mb:500000/ar:1/aHR0cHM6Ly9kZXYt/dG8tdXBsb2Fkcy5z/My5hbWF6b25hd3Mu/Y29tL3VwbG9hZHMv/YXJ0aWNsZXMvbXN1/dWJwZmpwN2tnaXp2/b3lkM2ouanBn" class="article-body-image-wrapper"&gt;&lt;img src="https://community.ops.io/images/R5T3ScU-AXXZLUNsWziA1ll__UxVz08MnhWzHnwF0Kw/w:880/mb:500000/ar:1/aHR0cHM6Ly9kZXYt/dG8tdXBsb2Fkcy5z/My5hbWF6b25hd3Mu/Y29tL3VwbG9hZHMv/YXJ0aWNsZXMvbXN1/dWJwZmpwN2tnaXp2/b3lkM2ouanBn" alt="Image description" width="880" height="493"&gt;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;This dashboard will give a detail representation&lt;br&gt;
&lt;a href="https://community.ops.io/images/96YSyOae3GoDzWgRQ0UeeezFTarCdGyazZ4kJ8QV5AY/w:880/mb:500000/ar:1/aHR0cHM6Ly9kZXYt/dG8tdXBsb2Fkcy5z/My5hbWF6b25hd3Mu/Y29tL3VwbG9hZHMv/YXJ0aWNsZXMvMjE1/cHE3bTJ3OHUzdmJx/ZG8xM3MucG5n" class="article-body-image-wrapper"&gt;&lt;img src="https://community.ops.io/images/96YSyOae3GoDzWgRQ0UeeezFTarCdGyazZ4kJ8QV5AY/w:880/mb:500000/ar:1/aHR0cHM6Ly9kZXYt/dG8tdXBsb2Fkcy5z/My5hbWF6b25hd3Mu/Y29tL3VwbG9hZHMv/YXJ0aWNsZXMvMjE1/cHE3bTJ3OHUzdmJx/ZG8xM3MucG5n" alt="Image description" width="880" height="495"&gt;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Top Sources (From where the traffic comes).&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Pages (On what page the user entered, exit).&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Location.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Devices, Browser, OS.&lt;br&gt;
&lt;a href="https://community.ops.io/images/YZxXupVzCF2aUpK0LRYL7EjcKRP6kR13n_aNI9nhIg8/w:880/mb:500000/ar:1/aHR0cHM6Ly9kZXYt/dG8tdXBsb2Fkcy5z/My5hbWF6b25hd3Mu/Y29tL3VwbG9hZHMv/YXJ0aWNsZXMvZzNy/ZnRtcnN6NTVsanc5/cmZrM2IuanBn" class="article-body-image-wrapper"&gt;&lt;img src="https://community.ops.io/images/YZxXupVzCF2aUpK0LRYL7EjcKRP6kR13n_aNI9nhIg8/w:880/mb:500000/ar:1/aHR0cHM6Ly9kZXYt/dG8tdXBsb2Fkcy5z/My5hbWF6b25hd3Mu/Y29tL3VwbG9hZHMv/YXJ0aWNsZXMvZzNy/ZnRtcnN6NTVsanc5/cmZrM2IuanBn" alt="Image description" width="880" height="728"&gt;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>plausible</category>
      <category>analytics</category>
      <category>devops</category>
      <category>nextjs</category>
    </item>
    <item>
      <title>Sentry with Next.js</title>
      <dc:creator>Akilesh</dc:creator>
      <pubDate>Sat, 28 May 2022 17:06:59 +0000</pubDate>
      <link>https://community.ops.io/akilesh/sentry-with-nextjs-50nn</link>
      <guid>https://community.ops.io/akilesh/sentry-with-nextjs-50nn</guid>
      <description>&lt;p&gt;In this blog we will see how to integrate sentry into an existing next.js application.&lt;/p&gt;

&lt;h2&gt;
  
  
  Installing
&lt;/h2&gt;

&lt;p&gt;Add Sentry’s Next.js &lt;a href="https://github.com/getsentry/sentry-javascript/tree/master/packages/nextjs"&gt;SDK&lt;/a&gt; to your next.js projects.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;yarn add @sentry/nextjs
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Automatic Configuration
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npx @sentry/wizard -i nextjs
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Choose your project from below listed options on your CLI then the setup wiz will automatically create configuration file's with default values and your API key.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://community.ops.io/images/t7bWUKqjddQt806F93yblI0LGrzF_QpoEvMSAEDiRVU/w:880/mb:500000/ar:1/aHR0cHM6Ly9jb21t/dW5pdHkub3BzLmlv/L3JlbW90ZWltYWdl/cy91cGxvYWRzL2Fy/dGljbGVzLzlyYzdu/ZjFxb2w0ZTlycG5o/dzRiLmpwZw" class="article-body-image-wrapper"&gt;&lt;img src="https://community.ops.io/images/t7bWUKqjddQt806F93yblI0LGrzF_QpoEvMSAEDiRVU/w:880/mb:500000/ar:1/aHR0cHM6Ly9jb21t/dW5pdHkub3BzLmlv/L3JlbW90ZWltYWdl/cy91cGxvYWRzL2Fy/dGljbGVzLzlyYzdu/ZjFxb2w0ZTlycG5o/dzRiLmpwZw" alt="Image description" width="880" height="409"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Generated file's:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;sentry.client.config.js&lt;/li&gt;
&lt;li&gt;sentry.server.config.js&lt;/li&gt;
&lt;li&gt;.sentryclirc (Where the token key will be stored)&lt;/li&gt;
&lt;li&gt;sentry.properties&lt;/li&gt;
&lt;li&gt;next.config.wizardcopy.js (if next.config.js already exists).&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Modify next.config.js
&lt;/h2&gt;

&lt;p&gt;If &lt;code&gt;next.config.js&lt;/code&gt; does already exist in your project, the app will automatically create a &lt;code&gt;next.config.wizardcopy.js&lt;/code&gt; then we'll need to manually copy the snippet into &lt;code&gt;next.config.js&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://community.ops.io/images/QsUnq2L43kVO7QP0KiofAk-YI_MGWnJacBig87YY7HA/w:880/mb:500000/ar:1/aHR0cHM6Ly9kZXYt/dG8tdXBsb2Fkcy5z/My5hbWF6b25hd3Mu/Y29tL3VwbG9hZHMv/YXJ0aWNsZXMvdDJl/ODZheTlvNnVlM2Fp/b3A1dGguanBn" class="article-body-image-wrapper"&gt;&lt;img src="https://community.ops.io/images/QsUnq2L43kVO7QP0KiofAk-YI_MGWnJacBig87YY7HA/w:880/mb:500000/ar:1/aHR0cHM6Ly9kZXYt/dG8tdXBsb2Fkcy5z/My5hbWF6b25hd3Mu/Y29tL3VwbG9hZHMv/YXJ0aWNsZXMvdDJl/ODZheTlvNnVlM2Fp/b3A1dGguanBn" alt="Image description" width="880" height="383"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Paste this snippet in your next.config.js&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/** @type {import('next').NextConfig} */
const { withSentryConfig } = require( '@sentry/nextjs' );

const nextConfig = {
  reactStrictMode: true
}

const sentryWebpackPluginOptions = {
  // Additional config options for the Sentry Webpack plugin. Keep in mind that
  // the following options are set automatically, and overriding them is not
  // recommended:
  //   release, url, org, project, authToken, configFile, stripPrefix,
  //   urlPrefix, include, ignore

  silent: true, // Suppresses all logs
  // For all available options, see:
  // https://github.com/getsentry/sentry-webpack-plugin#options.
};

module.exports = withSentryConfig( nextConfig, sentryWebpackPluginOptions );
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;p&gt;In an application to capture errors and monitor server performance of the API, we will wrap the handlers with a Sentry function.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Syntax:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { withSentry } from '@sentry/nextjs';

const handler = async (req, res) =&amp;gt; {
  // your API calls...
}

export default withSentry(handler);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Reference:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import type { NextApiRequest, NextApiResponse } from "next"
import { withSentry } from "@sentry/nextjs";

const handler = async (req: NextApiRequest, res: NextApiResponse) =&amp;gt; {
  res.status(200).json({ name: "Akilesh" });
};

export default withSentry(handler);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Testing
&lt;/h2&gt;

&lt;p&gt;To check every thing works fine, we will trigger a button click event in a frontend component that throws an error.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;            &amp;lt;button type="button" onClick={() =&amp;gt; {
              throw new Error("Sentry Frontend Error");
            }}&amp;gt;
              Throw error
            &amp;lt;/button&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://community.ops.io/images/EcrCB63zUZf53lYJ7OWfP2ytheH12sQeX0vkaIX-9us/w:880/mb:500000/ar:1/aHR0cHM6Ly9kZXYt/dG8tdXBsb2Fkcy5z/My5hbWF6b25hd3Mu/Y29tL3VwbG9hZHMv/YXJ0aWNsZXMvZ2U1/bXA5NTExYjNwYmx4/ZDRxdGQuanBn" class="article-body-image-wrapper"&gt;&lt;img src="https://community.ops.io/images/EcrCB63zUZf53lYJ7OWfP2ytheH12sQeX0vkaIX-9us/w:880/mb:500000/ar:1/aHR0cHM6Ly9kZXYt/dG8tdXBsb2Fkcy5z/My5hbWF6b25hd3Mu/Y29tL3VwbG9hZHMv/YXJ0aWNsZXMvZ2U1/bXA5NTExYjNwYmx4/ZDRxdGQuanBn" alt="Image description" width="880" height="431"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now check your &lt;a href="https://sentry.io"&gt;sentry dashboard&lt;/a&gt; to know more about the errors and performance metrics.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://community.ops.io/images/xfn17SBT1wAsGNk9dEphMXzUlMC3rKgYOYRkjWGH-RE/w:880/mb:500000/ar:1/aHR0cHM6Ly9kZXYt/dG8tdXBsb2Fkcy5z/My5hbWF6b25hd3Mu/Y29tL3VwbG9hZHMv/YXJ0aWNsZXMvd2Vn/bGtsY2M2eWw1MnZv/ZHJwNGUuanBn" class="article-body-image-wrapper"&gt;&lt;img src="https://community.ops.io/images/xfn17SBT1wAsGNk9dEphMXzUlMC3rKgYOYRkjWGH-RE/w:880/mb:500000/ar:1/aHR0cHM6Ly9kZXYt/dG8tdXBsb2Fkcy5z/My5hbWF6b25hd3Mu/Y29tL3VwbG9hZHMv/YXJ0aWNsZXMvd2Vn/bGtsY2M2eWw1MnZv/ZHJwNGUuanBn" alt="Image description" width="880" height="413"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Deployment (Vercel)
&lt;/h2&gt;

&lt;p&gt;Assuming that your next.js project has deployed on vercel.&lt;br&gt;
Add environment variable to your project in vercel &lt;code&gt;Project Settings &amp;gt; General &amp;gt; Environment Variables&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;SENTRY_AUTH_TOKEN&lt;/code&gt;: in environment variable with your sentry auth token which will be present in your &lt;code&gt;.sentryclirc&lt;/code&gt; file.&lt;/p&gt;

&lt;p&gt;Stay Tuned For Advanced Deployment Configuration.&lt;/p&gt;

</description>
      <category>sentry</category>
      <category>productivity</category>
      <category>nextjs</category>
      <category>performance</category>
    </item>
    <item>
      <title>Reinforcement learning in Super Mario bros Pt.2</title>
      <dc:creator>Akilesh</dc:creator>
      <pubDate>Sat, 28 May 2022 01:35:32 +0000</pubDate>
      <link>https://community.ops.io/akilesh/reinforcement-learning-in-super-mario-bros-pt2-3n9i</link>
      <guid>https://community.ops.io/akilesh/reinforcement-learning-in-super-mario-bros-pt2-3n9i</guid>
      <description>&lt;p&gt;&lt;a href="https://community.ops.io/akilesh/reinforcement-learning-in-super-mario-bros-48a"&gt;Before getting here you should setup &amp;amp; preprocess the environment tap your index or thumb finger hovering on me&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We are building an AI 🤖 to play 🎮 Super Mario Bros by reinforcement learning method and  RL has four key elements.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Agent 🕵️&lt;br&gt;
&lt;a href="https://community.ops.io/images/838RaMRHn0tCXqmTf0zNu2KgG3uuDN2H0QcfdbJRu3k/w:880/mb:500000/ar:1/aHR0cHM6Ly9kZXYt/dG8tdXBsb2Fkcy5z/My5hbWF6b25hd3Mu/Y29tL3VwbG9hZHMv/YXJ0aWNsZXMvY2kw/OG1hZnh6NG9mcnFk/M216emkucG5n" class="article-body-image-wrapper"&gt;&lt;img src="https://community.ops.io/images/838RaMRHn0tCXqmTf0zNu2KgG3uuDN2H0QcfdbJRu3k/w:880/mb:500000/ar:1/aHR0cHM6Ly9kZXYt/dG8tdXBsb2Fkcy5z/My5hbWF6b25hd3Mu/Y29tL3VwbG9hZHMv/YXJ0aWNsZXMvY2kw/OG1hZnh6NG9mcnFk/M216emkucG5n" alt="Image description" width="880" height="502"&gt;&lt;/a&gt;&lt;br&gt;
Agent can take some action in an environment to have some rewards or penalties.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Reward 🏆&lt;br&gt;
&lt;a href="https://community.ops.io/images/A7Wl9hlJ5BI0qIkxv8xpAKufWsBJ-q6afjDdAJUQbKo/w:880/mb:500000/ar:1/aHR0cHM6Ly9kZXYt/dG8tdXBsb2Fkcy5z/My5hbWF6b25hd3Mu/Y29tL3VwbG9hZHMv/YXJ0aWNsZXMvYWRq/Y2MxNmJ3Z2RoODNy/eGdjZTMucG5n" class="article-body-image-wrapper"&gt;&lt;img src="https://community.ops.io/images/A7Wl9hlJ5BI0qIkxv8xpAKufWsBJ-q6afjDdAJUQbKo/w:880/mb:500000/ar:1/aHR0cHM6Ly9kZXYt/dG8tdXBsb2Fkcy5z/My5hbWF6b25hd3Mu/Y29tL3VwbG9hZHMv/YXJ0aWNsZXMvYWRq/Y2MxNmJ3Z2RoODNy/eGdjZTMucG5n" alt="Image description" width="586" height="597"&gt;&lt;/a&gt;&lt;br&gt;
depending upon agent action he will get a reward or a penalty&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Environment 🖼️&lt;br&gt;
&lt;a href="https://community.ops.io/images/QETsB0F04p5-CPlRrPBk6QNmt538UrleoGVn_EluIOw/w:880/mb:500000/ar:1/aHR0cHM6Ly9kZXYt/dG8tdXBsb2Fkcy5z/My5hbWF6b25hd3Mu/Y29tL3VwbG9hZHMv/YXJ0aWNsZXMvZDE1/dWw5d2xnNjl0aGlq/dnE0bnkucG5n" class="article-body-image-wrapper"&gt;&lt;img src="https://community.ops.io/images/QETsB0F04p5-CPlRrPBk6QNmt538UrleoGVn_EluIOw/w:880/mb:500000/ar:1/aHR0cHM6Ly9kZXYt/dG8tdXBsb2Fkcy5z/My5hbWF6b25hd3Mu/Y29tL3VwbG9hZHMv/YXJ0aWNsZXMvZDE1/dWw5d2xnNjl0aGlq/dnE0bnkucG5n" alt="Image description" width="721" height="208"&gt;&lt;/a&gt;&lt;br&gt;
The place where all happens. Agent does specify work according to the AI by analyzing environment and reward or penalty is given by how good or bad does the agent perform in that environment.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Action 🎬&lt;br&gt;
&lt;a href="https://community.ops.io/images/4kowYezrtOfRil0AWRSIwDW_wun3OyZKMBZaF01Nk4A/w:880/mb:500000/ar:1/aHR0cHM6Ly9kZXYt/dG8tdXBsb2Fkcy5z/My5hbWF6b25hd3Mu/Y29tL3VwbG9hZHMv/YXJ0aWNsZXMvd2Zt/anJqZGJib25nc3M2/bmlobGYuZ2lm" class="article-body-image-wrapper"&gt;&lt;img src="https://community.ops.io/images/4kowYezrtOfRil0AWRSIwDW_wun3OyZKMBZaF01Nk4A/w:880/mb:500000/ar:1/aHR0cHM6Ly9kZXYt/dG8tdXBsb2Fkcy5z/My5hbWF6b25hd3Mu/Y29tL3VwbG9hZHMv/YXJ0aWNsZXMvd2Zt/anJqZGJib25nc3M2/bmlobGYuZ2lm" alt="Image description" width="" height=""&gt;&lt;/a&gt;&lt;br&gt;
The task given to the agent do certain tasks.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;blockquote&gt;
&lt;p&gt;The Algorithm we use is PPO &lt;a href="https://stable-baselines3.readthedocs.io/en/master/modules/ppo.html"&gt;Proximal Policy Optimization&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  🚂 Train the model to play
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import os 

from stable_baselines3 import PPO

from stable_baselines3.common.callbacks import BaseCallback
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here we are importing os to work with our file which helps when we save our model every 10000 games or steps so we can have a backup of our progress.&lt;/p&gt;

&lt;p&gt;Imported our main Algorithm PPO that will be used to train our AI model or reinforcement learning model&lt;/p&gt;

&lt;p&gt;then we imported &lt;a href="https://stable-baselines3.readthedocs.io/en/master/guide/callbacks.html"&gt;Base Callback&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;To save the model 💾&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;class TrainAndLoggingCallback(BaseCallback):

    def __init__(self, check_freq, save_path, verbose=1):
        super(TrainAndLoggingCallback, self).__init__(verbose)
        self.check_freq = check_freq
        self.save_path = save_path

    def _init_callback(self):
        if self.save_path is not None:
            os.makedirs(self.save_path, exist_ok=True)

    def _on_step(self):
        if self.n_calls % self.check_freq == 0:
            model_path = os.path.join(self.save_path, 'best_model_{}'.format(self.n_calls))
            self.model.save(model_path)

        return True
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The above step is optional you can skip it if you need. The process happening in the above step is we are saving the trained data at a set of callback intervals. So if we need we don't need to train the model again we can reuse this. Make sure you have enough storage space the model produces a hefty amount of data.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Locate file 📁&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;CHECKPOINT_DIR = './train/'
LOG_DIR = './logs/'
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We are specifying where the produced data is located at &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Setup Callback 📞🔙&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;callback = TrainAndLoggingCallback(check_freq=10000, save_path=CHECKPOINT_DIR)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is just the instance of the TrainAndLoggingCallback(). What we are doing here is to save our model every 1000 steps or every 1000 games &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;We just have it as a backup for future reference else we need to re-run the whole training process.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;We are going to setup our PPO model 💃&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;model = PPO('CnnPolicy', env, verbose=1, tensorboard_log=LOG_DIR, learning_rate=0.000001, 
            n_steps=512) 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;What we have done is create a variable called &lt;code&gt;model&lt;/code&gt; and set that to &lt;code&gt;PPO&lt;/code&gt; which is our model and passing parameters:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;code&gt;CnnPolicy&lt;/code&gt;- It is like a computer-based brain like a Neural Network in deep learning. A bunch of neurons communicate with each other and learn the relationship between different variables. Then there are various policies for different tasks. We used &lt;code&gt;CnnPolicy&lt;/code&gt; because when it comes to image-based problems this model has its upper hand,
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;env&lt;/code&gt; - Our environment which we preprocessed.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;verbose=1&lt;/code&gt; - This gives us the data when we train the model. like setting it to &lt;code&gt;0 no output, 1 info, 2 debug&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;tensorboard_log=LOG_DIR&lt;/code&gt; - This helps us to view the metric of how our training is performing as we are running our model.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;learning_rate=0.000001&lt;/code&gt; - The learning rate, can be a function of the current progress remaining.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;n_steps=512&lt;/code&gt; - The number of steps to run for each environment per update.&lt;/li&gt;
&lt;/ol&gt;

&lt;blockquote&gt;
&lt;p&gt;The hardest thing in any deep learning or machine learning is Getting the data in the right format.&lt;br&gt;
This one-line code created a temporary AI model.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;AI model starts to learn 📖&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;model.learn(total_timesteps=100000, callback=callback)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is where our AI model starts to learn we are passing some parameters:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;code&gt;total_timesteps=100000&lt;/code&gt; - The total number of samples (env steps) to train on.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;callback=callback&lt;/code&gt; - called at every step with the state of the algorithm.&lt;/li&gt;
&lt;/ol&gt;

&lt;blockquote&gt;
&lt;p&gt;You will get some details about the current process while running &lt;code&gt;model.learn&lt;/code&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;a href="https://community.ops.io/images/mTZmcn8BCmUQLUbcsBd4O7d0MF8dSZ1fQVscUxemiOU/w:880/mb:500000/ar:1/aHR0cHM6Ly9kZXYt/dG8tdXBsb2Fkcy5z/My5hbWF6b25hd3Mu/Y29tL3VwbG9hZHMv/YXJ0aWNsZXMvcDZ0/a2VlMnF6ajg4OWVm/NHp1NmEuanBn" class="article-body-image-wrapper"&gt;&lt;img src="https://community.ops.io/images/mTZmcn8BCmUQLUbcsBd4O7d0MF8dSZ1fQVscUxemiOU/w:880/mb:500000/ar:1/aHR0cHM6Ly9kZXYt/dG8tdXBsb2Fkcy5z/My5hbWF6b25hd3Mu/Y29tL3VwbG9hZHMv/YXJ0aWNsZXMvcDZ0/a2VlMnF6ajg4OWVm/NHp1NmEuanBn" alt="Image description" width="486" height="487"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you see the above then you are good to go else check the previous step or do it as a developer does stackoverflow it.&lt;/p&gt;

&lt;p&gt;Let's break down the resulting log by just taking look at some important values to notice.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;code&gt;fps&lt;/code&gt; : Frame Per Second&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;iterations&lt;/code&gt; : Number of times the process repeated.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;time_elapsed&lt;/code&gt; : How long it been training for.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;total_timesteps&lt;/code&gt; : How many frames our model goes through.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Training Metrics :&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;blockquote&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;entropy_loss&lt;/code&gt; (⬇️)  : In reinforcement learning, a similar situation can occur if the agent discovers a strategy that results in a reward that is better than it was receiving when it first started, but very far from a strategy that would result in an optimal reward.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;explained_variance&lt;/code&gt; (⬆️) : The explained variance is used to measure the proportion of the variability of the predictions of a machine learning model. Simply put, it is the difference between the expected value and the predicted value.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;learning_rate&lt;/code&gt; (📚): It is a tuning parameter in an optimization algorithm that determines the step size at each iteration while moving toward a minimum of a loss function.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;loss&lt;/code&gt; (⬇️) : loss is the value of the cost function for our training data.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;value_loss&lt;/code&gt; (⬇️)  : val_loss is the value of cost function for our cross-validation data&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Finally ✨
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Load model ⌛&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;model = PPO.load('./train/best_model_1000000')
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We are loading the trained model to our algorithm and saying the PPO algorithm to use that specific trained model to play Mario in our case it's &lt;code&gt;best_model_1000000&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Ai Plays Mario 🤖&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;state = env.reset()
while True: 

    action, _ = model.predict(state)
    state, reward, done, info = env.step(action)
    env.render()
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then we are starting our game and loop through the game. Previously we used some random actions to move Mario now we are using the model.predict(state) to predict according to the model and give certain actions to Mario.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/Akilesh2112/AI-Mario"&gt;Source Code&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This article is created by referring to Nicholasrenotte works all credit goes to him.&lt;/p&gt;

</description>
      <category>gym</category>
      <category>python</category>
      <category>openaigym</category>
      <category>stablebaselines3</category>
    </item>
    <item>
      <title>Reinforcement learning in Super Mario bros</title>
      <dc:creator>Akilesh</dc:creator>
      <pubDate>Sat, 28 May 2022 01:31:33 +0000</pubDate>
      <link>https://community.ops.io/akilesh/reinforcement-learning-in-super-mario-bros-48a</link>
      <guid>https://community.ops.io/akilesh/reinforcement-learning-in-super-mario-bros-48a</guid>
      <description>&lt;p&gt;The main objective of this blog is to learn reinforcement learning by doing. We are going to make a Mario game to play by itself using &lt;a href="https://towardsdatascience.com/reinforcement-learning-101-e24b50e1d292"&gt;Reinforcement Learning&lt;/a&gt; method.&lt;/p&gt;

&lt;h4&gt;
  
  
  📦&lt;strong&gt;Packages and tools used&lt;/strong&gt;
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://pypi.org/project/gym-super-mario-bros/"&gt;Mario Environment&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://pypi.org/project/nes-py/"&gt;OpenAI Gym interface&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://gym.openai.com/"&gt;OpenAI Gym toolkit&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://pytorch.org/get-started/locally/"&gt;Py Torch&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://stable-baselines3.readthedocs.io/en/master/modules/ppo.html"&gt;PPO Algorithm&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://spinningup.openai.com/en/latest/spinningup/rl_intro3.html"&gt;Policy Optimization&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  ⚙️Setup a mario environment
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;pip install gym_super_mario_bros nes_py
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We are Installing &lt;a href="https://pypi.org/project/gym-super-mario-bros/"&gt;gym_super_mario_bros&lt;/a&gt; &amp;amp; &lt;a href="https://pypi.org/project/nes-py/"&gt;nes_py&lt;/a&gt;. gym_super_mario_bros package is used to set up our gaming environment where our Mario will save the Princess Peach 👸 from Bowser and have you remembered the controls in this game &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Left dpad – Move left, enter pipe to the left of Mario.&lt;/li&gt;
&lt;li&gt;Right dpad – Move right, enter pipe to the right of Mario.&lt;/li&gt;
&lt;li&gt;Up dpad – Enter pipe above Mario or enter door behind Mario.&lt;/li&gt;
&lt;li&gt;Down dpad – Crouch, Ground Pound (In the air), enter pipe below Mario.&lt;/li&gt;
&lt;li&gt;A button – Jump, confirm menu selection.&lt;/li&gt;
&lt;li&gt;B button – Jump, exit menu.&lt;/li&gt;
&lt;li&gt;X button/Y button – Dash (Hold), launch fireball (Fire Mario only).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Haaa good old memories now back to our topic nes-py help us to build a virtual joypad for python for our model to control Mario to do certain tasks.&lt;/p&gt;

&lt;p&gt;⛓️&lt;strong&gt;Importing the required dependence&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import gym_super_mario_bros

from nes_py.wrappers import JoypadSpace

from gym_super_mario_bros.actions import SIMPLE_MOVEMENT
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let's break down what does this three import does &lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;code&gt;import gym_super_mario_bros&lt;/code&gt; - is importing the game itself.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;from nes_py.wrappers import JoypadSpace&lt;/code&gt; -&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;from gym_super_mario_bros.actions import SIMPLE_MOVEMENT&lt;/code&gt; - By default, gym_super_mario_bros environments use the full NES action space of 256 discrete actions. To constrain this, gym_super_mario_bros.actions provide three actions lists (RIGHT_ONLY, SIMPLE_MOVEMENT, and COMPLEX_MOVEMENT) we are using SIMPLE_MOVEMENT which has only 7 actions that help us to reduce the data we are going to process.
actions are the combination of controls in the game.&lt;/li&gt;
&lt;/ol&gt;

&lt;blockquote&gt;
&lt;p&gt;(Bonus: Simplify the environment as much as possible the more complex it is the harder it is going to be for our AI to learn how to play that game. We will also convert the RGB image into a grayscale image which again helps us in reducing the data we need to be processed)&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;🎮&lt;strong&gt;Setting up the game&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;env = gym_super_mario_bros.make('SuperMarioBros-v0')
env = JoypadSpace(env, SIMPLE_MOVEMENT)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;gym_super_mario_bros has various environments where we are going to train our model to learn and play. Feel free to check out different environments to play with. I am going to use &lt;code&gt;SuperMarioBros-v0&lt;/code&gt;as the default classical environment.&lt;br&gt;
&lt;u&gt;SuperMarioBros-v0&lt;/u&gt;&lt;br&gt;
&lt;a href="https://community.ops.io/images/UWtFIIMYdq7kLWf1k8meNcthXkha1HiiBRZKkb5-sMg/w:880/mb:500000/ar:1/aHR0cHM6Ly9kZXYt/dG8tdXBsb2Fkcy5z/My5hbWF6b25hd3Mu/Y29tL3VwbG9hZHMv/YXJ0aWNsZXMvY2V6/cTVkeno3cjd3eDVt/YWJxbzAucG5n" class="article-body-image-wrapper"&gt;&lt;img src="https://community.ops.io/images/UWtFIIMYdq7kLWf1k8meNcthXkha1HiiBRZKkb5-sMg/w:880/mb:500000/ar:1/aHR0cHM6Ly9kZXYt/dG8tdXBsb2Fkcy5z/My5hbWF6b25hd3Mu/Y29tL3VwbG9hZHMv/YXJ0aWNsZXMvY2V6/cTVkeno3cjd3eDVt/YWJxbzAucG5n" alt="Image description" width="256" height="224"&gt;&lt;/a&gt;&lt;br&gt;
&lt;u&gt;SuperMarioBros-v2&lt;/u&gt;&lt;br&gt;
&lt;a href="https://community.ops.io/images/e5UjRDlTYJZEJWRd5p6uY2sXDbV9zZo-n-q8ox5fnBM/w:880/mb:500000/ar:1/aHR0cHM6Ly9kZXYt/dG8tdXBsb2Fkcy5z/My5hbWF6b25hd3Mu/Y29tL3VwbG9hZHMv/YXJ0aWNsZXMveHVu/d210aDlrYzZmb3Yy/MTVrdmUucG5n" class="article-body-image-wrapper"&gt;&lt;img src="https://community.ops.io/images/e5UjRDlTYJZEJWRd5p6uY2sXDbV9zZo-n-q8ox5fnBM/w:880/mb:500000/ar:1/aHR0cHM6Ly9kZXYt/dG8tdXBsb2Fkcy5z/My5hbWF6b25hd3Mu/Y29tL3VwbG9hZHMv/YXJ0aWNsZXMveHVu/d210aDlrYzZmb3Yy/MTVrdmUucG5n" alt="Image description" width="256" height="224"&gt;&lt;/a&gt;&lt;br&gt;
&lt;u&gt;SuperMarioBros-v3&lt;/u&gt;&lt;br&gt;
&lt;a href="https://community.ops.io/images/snBg6tQEstVe9c5TuqvrTdCxeZngzMkDaCxwRgeFMdE/w:880/mb:500000/ar:1/aHR0cHM6Ly9kZXYt/dG8tdXBsb2Fkcy5z/My5hbWF6b25hd3Mu/Y29tL3VwbG9hZHMv/YXJ0aWNsZXMvZ3Bz/YmdrZ3g0dXdsang5/aDgyeWMucG5n" class="article-body-image-wrapper"&gt;&lt;img src="https://community.ops.io/images/snBg6tQEstVe9c5TuqvrTdCxeZngzMkDaCxwRgeFMdE/w:880/mb:500000/ar:1/aHR0cHM6Ly9kZXYt/dG8tdXBsb2Fkcy5z/My5hbWF6b25hd3Mu/Y29tL3VwbG9hZHMv/YXJ0aWNsZXMvZ3Bz/YmdrZ3g0dXdsang5/aDgyeWMucG5n" alt="Image description" width="256" height="224"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Next we wrapper nes_py.wrappers.JoypadSpace with environmental and &lt;a href="https://github.com/Kautenja/gym-super-mario-bros/blob/master/gym_super_mario_bros/actions.py"&gt;actions&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;🏃&lt;strong&gt;Run the game&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;done = True
for step in range(5000):
    if done:
        state = env.reset()
    state, reward, done, info = env.step(env.action_space.sample())
    env.render()

env.close()
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The first thing we had done is &lt;code&gt;done = True&lt;/code&gt; setting the flag to true. Tells whether the game needs to restart or not.&lt;/p&gt;

&lt;p&gt;next looping through every single frame &lt;code&gt;for step in range(5000):&lt;/code&gt; everything when a screen gets updated we say it to do specific actions.&lt;/p&gt;

&lt;p&gt;To start the game &lt;code&gt;env.reset()&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;env.step()&lt;/code&gt; to pass the action to the game like saying it to press a button to move right, left, etc...&lt;br&gt;
&lt;code&gt;env.action_space.sample()&lt;/code&gt; it gives random actions.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;state, reward, done, info&lt;/code&gt; will return us some data to process with.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;env.render()&lt;/code&gt; this allows us to show the game on the screen.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;env.close()&lt;/code&gt; this allows us to close the game or terminate that game.&lt;/p&gt;
&lt;h4&gt;
  
  
  🥁result:
&lt;/h4&gt;

&lt;p&gt;&lt;a href="https://community.ops.io/images/pt7l2rEV_tbAu-lh1LjcOgbCVUtAu6KvdiF2x1gVaAw/w:880/mb:500000/ar:1/aHR0cHM6Ly9kZXYt/dG8tdXBsb2Fkcy5z/My5hbWF6b25hd3Mu/Y29tL3VwbG9hZHMv/YXJ0aWNsZXMvNjJ4/bHZzN3ZpZXM4bGUx/cHRyZXUuZ2lm" class="article-body-image-wrapper"&gt;&lt;img src="https://community.ops.io/images/pt7l2rEV_tbAu-lh1LjcOgbCVUtAu6KvdiF2x1gVaAw/w:880/mb:500000/ar:1/aHR0cHM6Ly9kZXYt/dG8tdXBsb2Fkcy5z/My5hbWF6b25hd3Mu/Y29tL3VwbG9hZHMv/YXJ0aWNsZXMvNjJ4/bHZzN3ZpZXM4bGUx/cHRyZXUuZ2lm" alt="Image description" width="" height=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;code&gt;OSError: exception: access violation reading 0x000000000003C200&lt;/code&gt; if you get any of these access violation errors just restart your Kernel and run the imports again this will do the job.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;
  
  
  🔄Preprocess the game for applied AI
&lt;/h2&gt;
&lt;h2&gt;
  
  
  📦Installing packages for preprocessing
&lt;/h2&gt;

&lt;p&gt;we need to preprocess the Mario game data before we train in our AI model. Two key preprocessing steps are grayscale and frame stacking&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;!pip3 install torch torchvision torchaudio
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Install &lt;a href="https://pytorch.org/get-started/locally/"&gt;Pytorch&lt;/a&gt; &lt;br&gt;
you can customize your installation process according to your hardware if you have CUDA-enabled GPU then follow the steps given in &lt;a href="https://pytorch.org/get-started/locally/"&gt;Pytorch&lt;/a&gt; website.&lt;br&gt;
if you don't have one no worries just run the above command.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;!pip install stable-baselines3[extra]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next, we are going to install stable baselines an open-source project for implementations of reinforcement learning algorithms in PyTorch they provide various RL Algorithm to work with for this Mario game we are using (PPO) Proximal Policy Optimization algorithm to train our AI&lt;/p&gt;

&lt;h2&gt;
  
  
  📦Importing packages for preprocessing
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;from gym.wrappers import GrayScaleObservation

from stable_baselines3.common.vec_env import VecFrameStack, DummyVecEnv

from matplotlib import pyplot as plt
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Our AI is going to take the images of the Mario game to learn &lt;br&gt;
the color images (RGB)  that have three times as many pixels to process so we convert them into grayscale images to process. &lt;br&gt;
frame stacking helps our AI to have an idea of where Mario and enemies are by stacking the following frames.&lt;/p&gt;
&lt;h2&gt;
  
  
  🎁Wrapping the environment
&lt;/h2&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;env = gym_super_mario_bros.make('SuperMarioBros-v0')

env = JoypadSpace(env, SIMPLE_MOVEMENT)

env = GrayScaleObservation(env, keep_dim=True)

env = DummyVecEnv([lambda: env])

env = VecFrameStack(env, 4,channels_order='last')
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;&lt;code&gt;GrayScaleObservation(env, keep_dim=True)&lt;/code&gt; - This commend helps to convert our environment from RGB to grayscale. &lt;code&gt;keep_dim=True&lt;/code&gt; is used to obtain the final channel and it helps in frame stacking.&lt;br&gt;
This process helps to reduce the number of pixels we need to process Let's take a quick look at how the color image contains.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;code&gt;RGB Image (240*256*3)&lt;/code&gt; = &lt;code&gt;184320&lt;/code&gt; pixels to process&lt;br&gt;
&lt;code&gt;Grayscale (240*256*1)&lt;/code&gt; = &lt;code&gt;61440&lt;/code&gt; pixels to process &lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;code&gt;env = DummyVecEnv([lambda: env])&lt;/code&gt; - We wrap all the images in a dummy vectorization environment each time we run this step the shape of the data gets changed.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;env = VecFrameStack(env, 4,channels_order='last')&lt;/code&gt; - We pass our preprocessed environment and how many frames we are going to stack in our case &lt;code&gt;4&lt;/code&gt; you can add more if you need to and finally where our channel order is specified at by &lt;code&gt;channels_order='last'&lt;/code&gt;.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;stack shape is represented at the end.&lt;br&gt;
code:&lt;br&gt;
&lt;code&gt;&lt;br&gt;
state = env.reset()&lt;br&gt;
state.shape&lt;br&gt;
&lt;/code&gt;&lt;br&gt;
output:&lt;br&gt;
&lt;code&gt;(1, 240, 256, 1)&lt;/code&gt; =  one channel&lt;br&gt;
&lt;code&gt;(1, 240, 256, 4)&lt;/code&gt; =  four channels&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;
  
  
  Running the game
&lt;/h2&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;state = env.reset()
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;To get our current state in the game (basically the snapshot of the game at an index)&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;state, reward, done, info = env.step([5])
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;this step is explained in the Mario setup phase you can check above or if you are lazy enough to scroll to the top and find the specific para the same content is repeated below.&lt;br&gt;
env.step() to pass the action to the game like saying it to press a button to move right, left, etc...&lt;br&gt;
env.action_space.sample() it gives random actions.&lt;/p&gt;

&lt;p&gt;In the above code, we give env.step([5]) herein SIMPLE_MOVEMENT array index:&lt;br&gt;
&lt;code&gt;[['NOOP'],&lt;br&gt;
 ['right'],&lt;br&gt;
 ['right', 'A'],&lt;br&gt;
 ['right', 'B'],&lt;br&gt;
 ['right', 'A', 'B'],&lt;br&gt;
 ['A'],&lt;br&gt;
 ['left']]&lt;/code&gt;&lt;br&gt;
5th index action is to jump. just for making sure everything works fine, we are checking it out.&lt;/p&gt;

&lt;p&gt;state, reward, done, info is the reward function in the environment the objective of the game is to move as far right as possible, as fast as possible, without dying.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;code&gt;state&lt;/code&gt; - Our current state in the environment&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;reward&lt;/code&gt; - A positive score gives when our Mario performs well in the game else &lt;strong&gt;NOPE&lt;/strong&gt;.
Our main goal is to maximize the total rewards.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;done&lt;/code&gt; - It says whether Mario is dead or not, the game is over or not...&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;info&lt;/code&gt; - we get some information about the environment like &lt;code&gt;{'coins': 0,
'flag_get': False,
'life': 2,
'score': 0,
'stage': 1,
'status': 'small',
'time': 400,
'world': 1,
'x_pos': 40,
'y_pos': 79}&lt;/code&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  🖼️ Frame Stacking
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;plt.figure(figsize=(20,16))
for idx in range(state.shape[3]):
    plt.subplot(1,4,idx+1)
    plt.imshow(state[0][:,:,idx])
plt.show()
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://community.ops.io/images/63WK66_DVOlxI-Frs1uPgSvLsTR0n-PzCCrPt90LQCI/w:880/mb:500000/ar:1/aHR0cHM6Ly9kZXYt/dG8tdXBsb2Fkcy5z/My5hbWF6b25hd3Mu/Y29tL3VwbG9hZHMv/YXJ0aWNsZXMvbWpm/Njhjbmd6bnNqeDJ1/ZmQ2OHQuanBn" class="article-body-image-wrapper"&gt;&lt;img src="https://community.ops.io/images/63WK66_DVOlxI-Frs1uPgSvLsTR0n-PzCCrPt90LQCI/w:880/mb:500000/ar:1/aHR0cHM6Ly9kZXYt/dG8tdXBsb2Fkcy5z/My5hbWF6b25hd3Mu/Y29tL3VwbG9hZHMv/YXJ0aWNsZXMvbWpm/Njhjbmd6bnNqeDJ1/ZmQ2OHQuanBn" alt="Image description" width="562" height="568"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This process is just to visualize how the stacking process works.&lt;br&gt;
&lt;a href="https://community.ops.io/images/kXzbXe5774Y8SzRx9W8d7cyoFoNwQED0mMGE4UoVR4Y/w:880/mb:500000/ar:1/aHR0cHM6Ly9kZXYt/dG8tdXBsb2Fkcy5z/My5hbWF6b25hd3Mu/Y29tL3VwbG9hZHMv/YXJ0aWNsZXMvdHdh/eGt1NGVhMnNiN3lm/eWloZHEucG5n" class="article-body-image-wrapper"&gt;&lt;img src="https://community.ops.io/images/kXzbXe5774Y8SzRx9W8d7cyoFoNwQED0mMGE4UoVR4Y/w:880/mb:500000/ar:1/aHR0cHM6Ly9kZXYt/dG8tdXBsb2Fkcy5z/My5hbWF6b25hd3Mu/Y29tL3VwbG9hZHMv/YXJ0aWNsZXMvdHdh/eGt1NGVhMnNiN3lm/eWloZHEucG5n" alt="Image description" width="880" height="199"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://dev.to/akilesh/reinforcement-learning-in-super-mario-bros-pt2-1kkm"&gt;Continue Reading&lt;/a&gt;&lt;/p&gt;

</description>
      <category>openai</category>
      <category>python</category>
      <category>mario</category>
      <category>reinforcement</category>
    </item>
    <item>
      <title>Jupyter on Docker</title>
      <dc:creator>Akilesh</dc:creator>
      <pubDate>Sat, 28 May 2022 01:29:12 +0000</pubDate>
      <link>https://community.ops.io/akilesh/jupyter-on-docker-37gk</link>
      <guid>https://community.ops.io/akilesh/jupyter-on-docker-37gk</guid>
      <description>&lt;p&gt;🐬Docker is like an isolated operating system where you can build and run all your development needs in a docker environment and we are going to install and run jupyter notebook from docker. Jupyter starts up a local Python server to serve these apps to your web browser.&lt;/p&gt;




&lt;h3&gt;
  
  
  1 (Get Image) 🖼️:
&lt;/h3&gt;

&lt;p&gt;Get the Docker image of jupyter notebook from &lt;a href="https://jupyter-docker-stacks.readthedocs.io/en/latest/using/selecting.html"&gt;here&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;They have a list of different docker images for various needs &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;jupyter/base-notebook&lt;/li&gt;
&lt;li&gt;jupyter/minimal-notebook&lt;/li&gt;
&lt;li&gt;jupyter/r-notebook&lt;/li&gt;
&lt;li&gt;jupyter/scipy-notebook&lt;/li&gt;
&lt;li&gt;jupyter/tensorflow-notebook&lt;/li&gt;
&lt;li&gt;jupyter/datascience-notebook&lt;/li&gt;
&lt;li&gt;jupyter/pyspark-notebook&lt;/li&gt;
&lt;li&gt;jupyter/all-spark-notebook
&lt;img src="https://community.ops.io/images/pVy6lGGZvyBGpuDYVJ4h_ilFLZXaKVNiXSO0tukF4So/w:880/mb:500000/ar:1/aHR0cHM6Ly9kZXYt/dG8tdXBsb2Fkcy5z/My5hbWF6b25hd3Mu/Y29tL3VwbG9hZHMv/YXJ0aWNsZXMvOTRo/dWh3OHpoMm1qOHpm/bXRtNDkuanBn" alt="Image Relationships" width="640" height="520"&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;If you are just here to test things out you can try &lt;a href="https://hub.docker.com/r/jupyter/minimal-notebook/tags/"&gt;&lt;code&gt;minimal-notebook&lt;/code&gt;&lt;/a&gt; it has the basic jupyter notebook features in small image size.&lt;br&gt;
You can try &lt;a href="https://hub.docker.com/r/jupyter/all-spark-notebook/tags/"&gt;&lt;code&gt;jupyter/all-spark-notebook&lt;/code&gt;&lt;/a&gt; which has access to all the features but in cost to its larger image size. It depends upon your need but the steps are going to be the same for all.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;For this post, I am using &lt;a href="https://hub.docker.com/r/jupyter/scipy-notebook/tags/"&gt;&lt;code&gt;jupyter/scipy-notebook&lt;/code&gt;&lt;/a&gt;&lt;br&gt;
First, we will pull the docker jupyter image to your local machine.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;docker pull jupyter/scipy-notebook:latest
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  2 (Run Container) 🏃:
&lt;/h3&gt;

&lt;p&gt;Next, we will run the image in a docker container.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;docker run -it --name scipy-notebook -p 8080:8888 jupyter/scipy-notebook:latest
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will start a container that contains our jupyter scipy notebook image or whatever image you used.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://community.ops.io/images/lSazbVATEVsugMbU28RmEqd_Mjv4CRwwGtPRgtY833w/w:880/mb:500000/ar:1/aHR0cHM6Ly9kZXYt/dG8tdXBsb2Fkcy5z/My5hbWF6b25hd3Mu/Y29tL3VwbG9hZHMv/YXJ0aWNsZXMvZDJv/bWJzcTJzaHhkOGxk/YzZ0NWEuanBn" class="article-body-image-wrapper"&gt;&lt;img src="https://community.ops.io/images/lSazbVATEVsugMbU28RmEqd_Mjv4CRwwGtPRgtY833w/w:880/mb:500000/ar:1/aHR0cHM6Ly9kZXYt/dG8tdXBsb2Fkcy5z/My5hbWF6b25hd3Mu/Y29tL3VwbG9hZHMv/YXJ0aWNsZXMvZDJv/bWJzcTJzaHhkOGxk/YzZ0NWEuanBn" alt="Image description" width="880" height="468"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h3&gt;
  
  
  3 (Navigate) 🧭:
&lt;/h3&gt;

&lt;p&gt;navigate to your browser localhost port 8080 &lt;a href="http://localhost:8080/"&gt;http://localhost:8080/&lt;/a&gt; to access jupyter notebook in a browser.&lt;/p&gt;




&lt;h3&gt;
  
  
  4 (Bonus) ✨:
&lt;/h3&gt;

&lt;blockquote&gt;
&lt;p&gt;If you see any password or token required page in jupyter follow the step below else you can close this tab and play with jupyter notebook in docker.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;a href="https://community.ops.io/images/Sp2oSOd-7He9JbgczYt9OiftqXi0pb3-J6G4Im2vQ8k/w:880/mb:500000/ar:1/aHR0cHM6Ly9kZXYt/dG8tdXBsb2Fkcy5z/My5hbWF6b25hd3Mu/Y29tL3VwbG9hZHMv/YXJ0aWNsZXMvcTlo/d3NxNndqZHpnaTBj/ZjM3MTguanBn" class="article-body-image-wrapper"&gt;&lt;img src="https://community.ops.io/images/Sp2oSOd-7He9JbgczYt9OiftqXi0pb3-J6G4Im2vQ8k/w:880/mb:500000/ar:1/aHR0cHM6Ly9kZXYt/dG8tdXBsb2Fkcy5z/My5hbWF6b25hd3Mu/Y29tL3VwbG9hZHMv/YXJ0aWNsZXMvcTlo/d3NxNndqZHpnaTBj/ZjM3MTguanBn" alt="Image description" width="880" height="664"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Open your current running docker container which contains the image of the jupyter notebook and type:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;jupyter notebook list
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://community.ops.io/images/uUvNBnvBAU36Jn_JUO2dgwdeJIZ_uOJhV53vK1N_hhU/w:880/mb:500000/ar:1/aHR0cHM6Ly9kZXYt/dG8tdXBsb2Fkcy5z/My5hbWF6b25hd3Mu/Y29tL3VwbG9hZHMv/YXJ0aWNsZXMvNDdk/M3FwNGR1c2U3bHo5/eTBxaGguanBn" class="article-body-image-wrapper"&gt;&lt;img src="https://community.ops.io/images/uUvNBnvBAU36Jn_JUO2dgwdeJIZ_uOJhV53vK1N_hhU/w:880/mb:500000/ar:1/aHR0cHM6Ly9kZXYt/dG8tdXBsb2Fkcy5z/My5hbWF6b25hd3Mu/Y29tL3VwbG9hZHMv/YXJ0aWNsZXMvNDdk/M3FwNGR1c2U3bHo5/eTBxaGguanBn" alt="Image description" width="880" height="157"&gt;&lt;/a&gt;&lt;br&gt;
which will list out the current running notebook servers copy the token listen in the CLI and paste it in the jupyter token required page else scroll below and you can set a password with your token.&lt;/p&gt;

&lt;h3&gt;
  
  
  Finally 🥂 :
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://community.ops.io/images/hs0U7KwoTXUqW748W2_jvdJPUYNvY28qdoVnmtpaU8g/w:880/mb:500000/ar:1/aHR0cHM6Ly9kZXYt/dG8tdXBsb2Fkcy5z/My5hbWF6b25hd3Mu/Y29tL3VwbG9hZHMv/YXJ0aWNsZXMvNXN2/eDM1YWI5aTB3bmFs/bDJ6ZHouanBn" class="article-body-image-wrapper"&gt;&lt;img src="https://community.ops.io/images/hs0U7KwoTXUqW748W2_jvdJPUYNvY28qdoVnmtpaU8g/w:880/mb:500000/ar:1/aHR0cHM6Ly9kZXYt/dG8tdXBsb2Fkcy5z/My5hbWF6b25hd3Mu/Y29tL3VwbG9hZHMv/YXJ0aWNsZXMvNXN2/eDM1YWI5aTB3bmFs/bDJ6ZHouanBn" alt="Image description" width="880" height="432"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>jupyter</category>
      <category>docker</category>
      <category>remote</category>
      <category>python</category>
    </item>
  </channel>
</rss>
