<?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>Forem: Manikanta Inugurthi</title>
    <description>The latest articles on Forem by Manikanta Inugurthi (@memanikanta).</description>
    <link>https://forem.com/memanikanta</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F100371%2F18c8ab29-ffb0-47fb-92fb-6554b8de66c5.png</url>
      <title>Forem: Manikanta Inugurthi</title>
      <link>https://forem.com/memanikanta</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/memanikanta"/>
    <language>en</language>
    <item>
      <title>Splitwise Automation to send expense updates</title>
      <dc:creator>Manikanta Inugurthi</dc:creator>
      <pubDate>Wed, 28 Sep 2022 17:17:23 +0000</pubDate>
      <link>https://forem.com/memanikanta/splitwise-automation-to-send-expense-updates-180</link>
      <guid>https://forem.com/memanikanta/splitwise-automation-to-send-expense-updates-180</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;If you are reading this I assume you know about &lt;a href="https://www.splitwise.com" rel="noopener noreferrer"&gt;splitwise&lt;/a&gt; application. A one-line summary in their words is &lt;em&gt;"Splitwise is a free tool for friends and roommates to track bills and other shared expenses so that everyone gets paid back"&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;I use another app called &lt;a href="https://moneymanagerapp.com/" rel="noopener noreferrer"&gt;money manager&lt;/a&gt; for personal expenses, but instead of jumping between applications I always wanted one app to track all my expenses. I was about to send feedback to Splitwise to add a feature where I can add costs only where I am involved, interestingly enough many users needed &lt;a href="http://feedback.splitwise.com/forums/162446-general/suggestions/4084332-track-personal-expenses" rel="noopener noreferrer"&gt;this feature&lt;/a&gt; and thanks to them the Splitwise team have suggested &lt;a href="http://feedback.splitwise.com/forums/162446-general/suggestions/4084332-track-personal-expenses" rel="noopener noreferrer"&gt;a way&lt;/a&gt; to do it.&lt;/p&gt;

&lt;p&gt;All my expenses are in the same app, now what? Although all my expenses are in the same place, Splitwise doesn't have a feature to tell me how much I've spent in the previous month across the application. So the curious person in me wanted to solve this problem, that is how this &lt;a href="https://manicodes.hashnode.dev/series/side-project-sunday" rel="noopener noreferrer"&gt;Side-Project-Sunday&lt;/a&gt; is born.&lt;/p&gt;

&lt;h2&gt;
  
  
  Requirements
&lt;/h2&gt;

&lt;p&gt;Now as the storytime is done, let's list down all the requirements&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Ability to get the total expense

&lt;ul&gt;
&lt;li&gt;For a given date/date range&lt;/li&gt;
&lt;li&gt;For a given friend(s)/group(s)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Output a pie chart for a given date range

&lt;ul&gt;
&lt;li&gt;For Group-wise expenses&lt;/li&gt;
&lt;li&gt;For Category-wise expenses&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Send an email on the first day of the month, with the following template&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn.hashnode.com%2Fres%2Fhashnode%2Fimage%2Fupload%2Fv1662314990904%2FPiNq43eCu.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn.hashnode.com%2Fres%2Fhashnode%2Fimage%2Fupload%2Fv1662314990904%2FPiNq43eCu.png" alt="sample_email.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Research
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Fetching data
&lt;/h3&gt;

&lt;p&gt;With a quick googling about how we can do it, I found that Splitwise is supported in automation apps like &lt;a href="https://zapier.com" rel="noopener noreferrer"&gt;Zapier&lt;/a&gt;, &lt;a href="https://www.make.com/en" rel="noopener noreferrer"&gt;make&lt;/a&gt; etc.., although with a little bit of effort we can use these apps to fulfil my requirements, I thought it would be better if I code it myself so that I'll have more control on the output.&lt;/p&gt;

&lt;p&gt;Further googling landed me on Splitwise &lt;a href="https://dev.splitwise.com" rel="noopener noreferrer"&gt;dev documentation&lt;/a&gt; and I am so happy that there are a few developers who have developed third-party SDKs using the exposed APIs.&lt;/p&gt;

&lt;p&gt;As I was planning to write a python script to implement this, I decided to use &lt;a href="https://github.com/namaggarwal/splitwise" rel="noopener noreferrer"&gt;this python implementation&lt;/a&gt; by &lt;a href="https://github.com/namaggarwal/" rel="noopener noreferrer"&gt;namaggarwal&lt;/a&gt; for my automation.&lt;/p&gt;

&lt;h3&gt;
  
  
  Email notification
&lt;/h3&gt;

&lt;p&gt;For sending out the email, I wanted to use Gmail's SMTP server to send out the email. A quick search through the web helped me understand how I can do it. I've linked all the references that I've taken help from at the end of the article.&lt;/p&gt;

&lt;h3&gt;
  
  
  Scheduling
&lt;/h3&gt;

&lt;p&gt;As I was planning to push my script to GitHub, I was thinking to have a GitHub action to run my script on a regular interval. GitHub supports multiple ways of triggering actions, of which one is &lt;a href="https://en.wikipedia.org/wiki/Cron#CRON_expression" rel="noopener noreferrer"&gt;cron expression&lt;/a&gt;. This should help me in running the script at the end of every month.&lt;/p&gt;

&lt;h2&gt;
  
  
  Implementation
&lt;/h2&gt;

&lt;p&gt;At first, I started exploring the &lt;a href="https://github.com/namaggarwal/splitwise" rel="noopener noreferrer"&gt;SDK&lt;/a&gt; to understand all the APIs I could use to fulfil my requirements.&lt;/p&gt;

&lt;h3&gt;
  
  
  Setup
&lt;/h3&gt;

&lt;p&gt;First I've created a new application in &lt;a href="https://secure.splitwise.com/apps/new" rel="noopener noreferrer"&gt;Splitwise&lt;/a&gt;, this generates &lt;code&gt;Consumer Key&lt;/code&gt; and &lt;code&gt;Consumer Secret&lt;/code&gt; for us which we'll be using to authenticate our calls.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn.hashnode.com%2Fres%2Fhashnode%2Fimage%2Fupload%2Fv1662575588158%2F7mfG2lJU4.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn.hashnode.com%2Fres%2Fhashnode%2Fimage%2Fupload%2Fv1662575588158%2F7mfG2lJU4.png" alt="image.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As I plan to use this for a personal use case I've generated an API key, instead of going through the hassle of setting up OAuth1/OAuth2.&lt;/p&gt;

&lt;h3&gt;
  
  
  Let's play with the API
&lt;/h3&gt;

&lt;p&gt;Let's go through all the APIs that Splitwise supports and list down the methods in which we are interested.&lt;/p&gt;

&lt;h4&gt;
  
  
  Get all expenses of the current user
&lt;/h4&gt;

&lt;p&gt;This API is so powerful that most of our logic will rely on this, the &lt;code&gt;getExpenses&lt;/code&gt; method supports a wide variety of filters with which we can filter the data based on &lt;code&gt;createdDate&lt;/code&gt;, &lt;code&gt;updatedDate&lt;/code&gt;, &lt;code&gt;groupIds&lt;/code&gt;, &lt;code&gt;friendIds&lt;/code&gt; etc.., As I'll need the data for the past month, I'll just get all the expenses for the previous month date range. Unfortunately, this API doesn't give us all the details in one network call hence we need to get all the data by making multiple network calls.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;s = Splitwise(CONSUMER_KEY, CONSUMER_SECRET, api_key=API_KEY)
expenses = []
current_page_num = 0
page_size = 100

while True:
    curr_page_expenses = s.getExpenses(
        dated_after=start_date,
        dated_before=end_date,
        limit=page_size,
        offset=current_page_num * page_size,
    )
    if len(curr_page_expenses) == 0:
        break
    current_page_num = current_page_num + 1
    expenses = expenses + curr_page_expenses

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

&lt;/div&gt;



&lt;h4&gt;
  
  
  Get all Groups and Friends
&lt;/h4&gt;

&lt;p&gt;The &lt;code&gt;getGroups&lt;/code&gt; and &lt;code&gt;getFriends&lt;/code&gt; method fetches us all the friends and group data to us which we can map with the expense data that we've fetched earlier.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;groups = s.getGroups()
friends = s.getFriends()

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

&lt;/div&gt;



&lt;p&gt;We can traverse through friends' data and filter out friends where we owe them or where we are owed.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;def get_friends_with_outstanding_table(friends):
    friends_with_outstanding_balances = []
    for f in friends:
        balance = sum(map(lambda b: float(b.amount), f.balances))
        if balance != 0:
            color = "red" if balance &amp;lt; 0 else "green"
            friends_with_outstanding_balances.append([f.first_name, balance, color])

    friends_with_outstanding_balances.sort(key=lambda f: f[1], reverse=True)
    return friends_with_outstanding_balances

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

&lt;/div&gt;



&lt;h3&gt;
  
  
  Visualization
&lt;/h3&gt;

&lt;p&gt;When it comes to visualization in python, most of us are familiar with matplotlib. The same is the case with me, so I used matplotlib to generate the charts.&lt;/p&gt;

&lt;h3&gt;
  
  
  Email notification
&lt;/h3&gt;

&lt;p&gt;As I mentioned earlier, I wanted to use Gmail's SMTP server to send out emails. There are plenty of articles available on how to setup and send out emails using Gmail which I'll link at the end of the article.&lt;/p&gt;

&lt;h3&gt;
  
  
  Scheduling the script
&lt;/h3&gt;

&lt;p&gt;Let's first create a Github action to run our script and send out the email. Let the name of the job be send-email&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;jobs:
  send-mail:
    runs-on: ubuntu-latest

    steps:

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

&lt;/div&gt;



&lt;p&gt;The first step of our job is to checkout the repository with the default branch. There is already an action available in GitHub Action Marketplace for this.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;jobs:
  send-mail:
    runs-on: ubuntu-latest

    steps:    
      - name: Checkout Repos
        uses: actions/checkout@v2.4.2

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

&lt;/div&gt;



&lt;p&gt;Next, set up the python environment so that our script can run without any issues. I'll be using setup-python from marketplace to install python and pip.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;jobs:
  send-mail:
    runs-on: ubuntu-latest

    steps:

      - name: Checkout Repos
        uses: actions/checkout@v2.4.2

      - name: Setup Python
        uses: actions/setup-python@v4.2.0
        with:
          python-version: 3.8.0
          cache: pip
          architecture: x64
          update-environment: true

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

&lt;/div&gt;



&lt;p&gt;Finally, let's install all the required libraries using pip and then run our script.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;jobs:
  send-mail:
    runs-on: ubuntu-latest

    steps:

      - name: Checkout Repos
        uses: actions/checkout@v2.4.2

      - name: Setup Python
        uses: actions/setup-python@v4.2.0
        with:
          python-version: 3.8.0
          cache: pip
          architecture: x64
          update-environment: true

      - name: Install required dependencies
        run: python -m pip install -r requirements.txt

      - name: Run Automation
        run: python main.py

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

&lt;/div&gt;



&lt;p&gt;As our action is ready to go, let's now try to schedule the action at regular intervals. GitHub supports cron expressions as &lt;code&gt;schedule&lt;/code&gt; which we'll be using to schedule our script to run on the 1st of every month.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;on:
  push:
    branches: ["main"]
  schedule:
    - cron: 0 10 1 * *

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

&lt;/div&gt;



&lt;p&gt;The complete yml file can be found &lt;a href="https://github.com/me-manikanta/SplitwiseAutomation/blob/main/.github/workflows/main.yml" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  References
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://dev.splitwise.com/" rel="noopener noreferrer"&gt;Splitwise Documentation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://splitwise.readthedocs.io/en/latest/" rel="noopener noreferrer"&gt;Python SDK for Splitwise&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://geekflare.com/send-gmail-in-python/" rel="noopener noreferrer"&gt;Send mails using Gmail Account&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://stackoverflow.com/a/52329759" rel="noopener noreferrer"&gt;Inline images when sending emails in python&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.github.com/en/actions" rel="noopener noreferrer"&gt;Github Actions Documentation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#onschedule" rel="noopener noreferrer"&gt;Schedule Github Action&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>sideprojectsunday</category>
      <category>python</category>
      <category>automation</category>
    </item>
    <item>
      <title>Let's discuss commit messages</title>
      <dc:creator>Manikanta Inugurthi</dc:creator>
      <pubDate>Sun, 10 Jul 2022 04:30:00 +0000</pubDate>
      <link>https://forem.com/memanikanta/lets-discuss-commit-messages-11lo</link>
      <guid>https://forem.com/memanikanta/lets-discuss-commit-messages-11lo</guid>
      <description>&lt;p&gt;We all know what a commit message is, and probably write at least 1 commit message every day. But how confident we are that we'll understand the changes we made in a commit by reading only the message after some time?&lt;/p&gt;

&lt;p&gt;There are multiple reasons why we don't write proper commit messages:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;No proper structure is defined in the team/project&lt;/li&gt;
&lt;li&gt;Laziness&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;There can be a wide variety of other reasons as well.&lt;/p&gt;

&lt;p&gt;But wait!&lt;/p&gt;

&lt;h2&gt;
  
  
  Why should we write better commit messages?
&lt;/h2&gt;

&lt;p&gt;If we browse through the &lt;code&gt;git log&lt;/code&gt; of any of our repositories, there is a high chance that it looks something like this,&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;fixed NPE in test case
CSS fixes
added a button to send mail
updates versions in package.json

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

&lt;/div&gt;



&lt;p&gt;Hmm, so what is the fix in the latest commit? Yeah, even I can't tell until I look at the changes in the commit. Perhaps you have encountered code in a professional environment where you had no idea what it was doing or meant for.&lt;/p&gt;

&lt;p&gt;Similar to having maintainable code and good documentation, having a good commit log is suggested to save yourself and/or co-workers hours of digging around in the repository.&lt;/p&gt;

&lt;h2&gt;
  
  
  Anatomy of a commit message
&lt;/h2&gt;

&lt;p&gt;Most of us write only one line of commit message explaining what the changes are by running the following command.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;git commit -m "&amp;lt;Message&amp;gt;"

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

&lt;/div&gt;



&lt;p&gt;But did you know that multi-line commits are possible?&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;git commit -m "&amp;lt;Message1&amp;gt;" -m "&amp;lt;Message2&amp;gt;"

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

&lt;/div&gt;



&lt;p&gt;Actually, for each &lt;code&gt;-m&lt;/code&gt; param to the &lt;code&gt;git commit&lt;/code&gt; command it adds a new line to the commit message.&lt;/p&gt;

&lt;p&gt;This feature opens a whole new world for us to write commits, as we now know that we aren't blocked by writing about our changes in 1 line, we can have a summary in one line have a detailed explanation in the subsequent lines. The structure of the commitment will come as&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;git commit -m "&amp;lt;Title&amp;gt;" -m "&amp;lt;Message&amp;gt;" -m "&amp;lt;Footer&amp;gt;"

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

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;Pro Tip: Prefer using a text editor when writing commit messages instead of command-line :D&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Let's talk about better commit messages
&lt;/h2&gt;

&lt;p&gt;As we now have defined a basic structure of the commit message, let's add some rules along with the structure to have consistency across all of our commits.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;These rules work for me, but might not work for you. My goal here is to give you some inspiration so that you can come up with your structure.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Type of Commit
&lt;/h3&gt;

&lt;p&gt;Wait what!!! There are types of commits?&lt;/p&gt;

&lt;p&gt;Technically there are no types, but logically we can have any number of types based on our needs. Following are a few of the widely accepted types of commits:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;feat&lt;/code&gt; Added a new feature&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;fix&lt;/code&gt; Fixed a bug&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;chore&lt;/code&gt; Changes that do not relate to a fix or feature and don't modify src or test files (for example updating dependencies)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;refactor&lt;/code&gt; refactored code that neither fixes a bug nor adds a feature &lt;/li&gt;
&lt;li&gt;
&lt;code&gt;docs&lt;/code&gt; Updates to docs in the repository&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;style&lt;/code&gt; Format code&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;test&lt;/code&gt; Adding/fixing test cases&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;perf&lt;/code&gt; Performance improvements&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;ci&lt;/code&gt; Continuous Integration related&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;build&lt;/code&gt; Changes that affect the build system or external dependencies &lt;/li&gt;
&lt;li&gt;
&lt;code&gt;revert&lt;/code&gt; Reverts a previous commit&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;To specify the type of the commit I use emojis like , and 🐛 for the feat, and bug respectively... more emojis for different types of commits can be found &lt;a href="http://gitmoji.dev"&gt;here&lt;/a&gt;. Specifying the type as text would also work, but I've transitioned to this way of writing commits for 2 reasons:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Git log looks cool 😎&lt;/li&gt;
&lt;li&gt;I save some characters in the subject line&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Scope of Commit
&lt;/h3&gt;

&lt;p&gt;The scope is a noun that describes what section of the codebase is affected by this change/commit.&lt;/p&gt;

&lt;p&gt;Example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;- feat(API) or (API)
- fix(orders) or 🐛(orders)
- docs(workflow) or 📝(workflow)

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

&lt;/div&gt;



&lt;h3&gt;
  
  
  Subject
&lt;/h3&gt;

&lt;p&gt;Summary of the changes in the commit.&lt;/p&gt;

&lt;p&gt;Some key points to consider while writing the subject:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Have a hard limit of 50 characters&lt;/li&gt;
&lt;li&gt;There is no need to add punctuation, be mindful of the hard limit we've set ;)&lt;/li&gt;
&lt;li&gt;Use the imperative mood&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Body
&lt;/h3&gt;

&lt;p&gt;This is an optional section where we can provide a detailed explanation of what the change is, and why we've made the change.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Separate the subject from the body with a blank line&lt;/li&gt;
&lt;li&gt;Try having a hard limit in each line of the body(I prefer to have around 70 to 75)&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Footer
&lt;/h3&gt;

&lt;p&gt;This section can be used as a meta section of the commit. Here we can specify things like the ticket or issue number that the change corresponds to. reviewer of the commit etc..,&lt;/p&gt;

&lt;p&gt;Most of this information is inspired from &lt;a href="https://www.conventionalcommits.org/en/v1.0.0/#specification"&gt;Conventional Commits&lt;/a&gt; and they explain all the specs for writing a good commit in much detail. Do note that there is no one correct way of writing commits but having a team agreement can help maintain the repository easier.&lt;/p&gt;

&lt;h2&gt;
  
  
  Tools
&lt;/h2&gt;

&lt;p&gt;If you've liked the concept of writing conventional commits and want to force them into your workflow, there are a lot of tools available to help you to do so.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;If you prefer using the command line for writing commits, &lt;a href="https://commitizen-tools.github.io/commitizen/"&gt;Commitizen&lt;/a&gt; would be a good choice for you, when you are about to commit your changes it asks a series of questions and forces you to write descriptive commits.&lt;/li&gt;
&lt;li&gt;If you prefer using IDEs like IntelliJ or VS Code there are plugins available. I've used &lt;a href="https://plugins.jetbrains.com/plugin/13389-conventional-commit"&gt;this&lt;/a&gt; plugin in IntelliJ and it worked for me, however, I haven't tried any in VSCode.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  References
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.conventionalcommits.org/en/v1.0.0/#specification"&gt;Conventional Commits&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://gitmoji.dev"&gt;Gitmoji&lt;/a&gt; - An emoji guide for your commit messages&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://commitizen-tools.github.io/commitizen/"&gt;Commitizen&lt;/a&gt; - Command line tool for writing conventional commits&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://plugins.jetbrains.com/plugin/13389-conventional-commit"&gt;Conventional Commit Plugin&lt;/a&gt; - An IntelliJ plugin for writing conventional commits&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>git</category>
      <category>commit</category>
      <category>bestpractices</category>
    </item>
    <item>
      <title>Logging best practices that I follow</title>
      <dc:creator>Manikanta Inugurthi</dc:creator>
      <pubDate>Sun, 10 Apr 2022 04:38:53 +0000</pubDate>
      <link>https://forem.com/memanikanta/logging-best-practices-that-i-follow-nlg</link>
      <guid>https://forem.com/memanikanta/logging-best-practices-that-i-follow-nlg</guid>
      <description>&lt;p&gt;At the beginning of my coding career, I mostly wrote logs (print statements 😜) when debugging an issue and removed them (unsaid rule) after the bug was fixed. But when I became a backend developer, I realised the importance of logging. In this blog, I am listing down all the things that I've learned in the past few years and the rules I follow when writing log statements.&lt;/p&gt;

&lt;h2&gt;
  
  
  What exactly is logging?
&lt;/h2&gt;

&lt;p&gt;In laymen's terms, we all know what logging is; it's printing messages, variables and exceptions to the console or a file. It is partially correct. However, what I feel is logging is a mechanism to record the state of the application and the actions it has taken at a given point in time.&lt;/p&gt;

&lt;p&gt;If we think about it, both definitions are completely different. One suggests that we are indicating where we are in the codebase, and the second indicates the current state of request flow. This change in the thought process when developing an application helps us to write better logging statements and, in turn, helps us to improve the application.&lt;/p&gt;

&lt;h2&gt;
  
  
  Different levels of Logs
&lt;/h2&gt;

&lt;p&gt;The level of logging may vary based on the team you're in, the application you're working on, and, most likely, the framework/language you're using. The most widely used logging levels are &lt;code&gt;DEBUG&lt;/code&gt;, &lt;code&gt;INFO&lt;/code&gt;, &lt;code&gt;WARN&lt;/code&gt; and &lt;code&gt;ERROR&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  DEBUG 👨🏻‍💻
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--xUVnV9_Z--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://cdn.hashnode.com/res/hashnode/image/upload/v1649343223530/meiZpkf88.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--xUVnV9_Z--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://cdn.hashnode.com/res/hashnode/image/upload/v1649343223530/meiZpkf88.gif" alt="debugging.gif" width="220" height="198"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As the name suggests, these logs contain information that is most interesting for developers when trying to debug a problem. They are fine-grained statements concerning the state of the application.&lt;/p&gt;

&lt;p&gt;In our production system, we have a huge number of &lt;code&gt;DEBUG&lt;/code&gt; logs, and we tend to disable this level mostly. We enable this only when the need arises.&lt;/p&gt;

&lt;h3&gt;
  
  
  INFO ℹ️
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--d1qjz4di--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://cdn.hashnode.com/res/hashnode/image/upload/v1649343014674/dCycagJ2a.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--d1qjz4di--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://cdn.hashnode.com/res/hashnode/image/upload/v1649343014674/dCycagJ2a.gif" alt="info-level.gif" width="498" height="280"&gt;&lt;/a&gt;Logs at this level are purely informational messages, as the name suggests and they should not be used to indicate a fault or error state in the application. To make the best use of this log level, consider what general information would be useful for diagnosing an application error when the primary interface is unavailable.&lt;/p&gt;

&lt;h3&gt;
  
  
  WARN ⚠️
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--VrN3vk5x--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://cdn.hashnode.com/res/hashnode/image/upload/v1649342969562/Gn4KrBiLs.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--VrN3vk5x--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://cdn.hashnode.com/res/hashnode/image/upload/v1649342969562/Gn4KrBiLs.gif" alt="darth-vader-this-is-your-last-warning.gif" width="480" height="640"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Warning logs should be used when something happens that could potentially cause some oddities in the application. These logs give us some heads-up, indicating something that might need our attention.&lt;/p&gt;

&lt;h3&gt;
  
  
  ERROR 🚨
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--qYp6FQbf--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://cdn.hashnode.com/res/hashnode/image/upload/v1649342866161/BKZSWVpCu.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--qYp6FQbf--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://cdn.hashnode.com/res/hashnode/image/upload/v1649342866161/BKZSWVpCu.gif" alt="error.gif" width="220" height="164"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Logs at this level contain failures in the application. Use error logs when something that could've gone wrong has gone wrong and action needs to be taken by someone to resolve the issue. In the event there is no action needed, we can use the &lt;code&gt;WARN&lt;/code&gt; level instead.&lt;/p&gt;

&lt;h2&gt;
  
  
  How much should we log?
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Have fewer logs 🔇
&lt;/h3&gt;

&lt;p&gt;There is no harm in actually having very few logs in our system, but this will make figuring out why something happened the way it happened a bit difficult.&lt;/p&gt;

&lt;h3&gt;
  
  
  Log everything 🔊
&lt;/h3&gt;

&lt;p&gt;If logging is this good, why can't we write a log statement after every line of our code?&lt;/p&gt;

&lt;p&gt;Is it too much?&lt;/p&gt;

&lt;p&gt;How much logging is too much logging?&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--xL8cq_Y---/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://cdn.hashnode.com/res/hashnode/image/upload/v1649343245360/yLswYQvicp.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--xL8cq_Y---/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://cdn.hashnode.com/res/hashnode/image/upload/v1649343245360/yLswYQvicp.gif" alt="talk-spongebob.gif" width="498" height="356"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;These types of questions were asked in a lot of forums, including StackOverflow, which led to &lt;a href="https://blog.codinghorror.com/the-problem-with-logging/"&gt;this blog&lt;/a&gt; by &lt;a href="https://blog.codinghorror.com"&gt;Coding Horror&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;A few takeaways, according to me, are as follows:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;The more logs you add, the larger the codebase grows.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;There is a slight chance of your application becoming slower by having too many logs.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;There is no one golden standard in the case of how much we need to log. It all depends on the application and who is writing the code. I suggest starting logging &lt;code&gt;ERROR&lt;/code&gt; and &lt;code&gt;WARN&lt;/code&gt; levels, and then &lt;code&gt;INFO&lt;/code&gt; and &lt;code&gt;DEBUG&lt;/code&gt; level logs as the need arises during development. Be mindful of what is being logged.&lt;/p&gt;

&lt;h2&gt;
  
  
  Other uses of logging
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Having good logs helps us to debug and resolve the issue better and faster.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Structured logs help us visualise logs in viewers like Kibana, Datadog, etc. These visualisations help us derive a few patterns in our application, which in turn can be used to make the application better.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Rules I follow while writing logs 🪵
&lt;/h2&gt;

&lt;p&gt;So we've read about what logging is, the different levels of logs, and the advantages of having good logs. The following are the rules and guidelines that I follow on a day-to-day basis when writing logs. It might not be exhaustive, but it should give a very good idea of the different things to consider while writing logs.&lt;/p&gt;

&lt;h3&gt;
  
  
  Not all exceptions are errors.
&lt;/h3&gt;

&lt;p&gt;I used to have the habit of logging the exception messages and categorising them as errors. Later down the line, I realised that not all exceptions are errors, and some might be expected. These can be logged as either warnings or informational messages.&lt;/p&gt;

&lt;h3&gt;
  
  
  Log after an action is performed, not before.
&lt;/h3&gt;

&lt;p&gt;Logging before an action is taken might give you some contextual information about what action would be taken, but we would never know whether the action was successful or not. Having a log message after the action is taken would inform us that the action was performed and the result of the action, if available. There are scenarios where we log before and after the action is taken. This is done to measure the amount of time it takes to complete the action.&lt;/p&gt;

&lt;h3&gt;
  
  
  Either log or throw exceptions, but not both.
&lt;/h3&gt;

&lt;p&gt;As mentioned in &lt;a href="https://rules.sonarsource.com/java/RSPEC-2139"&gt;RSPEC-2139&lt;/a&gt;, it is not a good idea to log and throw exceptions at the same time. This is done to avoid a large number of identical logs, which would lead to noise in the log files.&lt;/p&gt;

&lt;h3&gt;
  
  
  Define a logging format for your project.
&lt;/h3&gt;

&lt;p&gt;Having a predefined structure for log messages would make reading the logs easier. Additionally, this structure would help to visualise the logs in log viewers like Kibana, Datadog, etc...&lt;/p&gt;

&lt;p&gt;Try not to merge log messages and variables when writing the logs. A good log format that I follow is&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;LOG.level("{Message} {Variables}");

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

&lt;/div&gt;



&lt;p&gt;Some examples:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;LOG.info("Fetched the employee data for a company. [companyId={}]", companyId);
LOG.error("Error occurred while fetching the employee data for the company. [companyId={}]", companyId, e);

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

&lt;/div&gt;



&lt;h2&gt;
  
  
  References
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Coding Horror &lt;a href="https://blog.codinghorror.com/the-problem-with-logging/"&gt;blog on logging&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Code project &lt;a href="https://www.codeproject.com/Articles/42354/The-Art-of-Logging"&gt;blog on The Art of Logging&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>programming</category>
      <category>softwaredevelopment</category>
      <category>logging</category>
    </item>
    <item>
      <title>How-to: Python Paramiko</title>
      <dc:creator>Manikanta Inugurthi</dc:creator>
      <pubDate>Sun, 06 Mar 2022 18:00:19 +0000</pubDate>
      <link>https://forem.com/memanikanta/how-to-python-paramiko-4dj0</link>
      <guid>https://forem.com/memanikanta/how-to-python-paramiko-4dj0</guid>
      <description>&lt;h2&gt;
  
  
  Background
&lt;/h2&gt;

&lt;p&gt;I recently had a task that required me to connect to an SFTP server, download particular files (if they existed), and then process the contents. We were given the option of using any language to fulfil the criteria, so I chose Python to tackle the problem. This blog outlines the problems I encountered when attempting to build the script, as well as a tutorial on how to use the library.&lt;/p&gt;

&lt;h3&gt;
  
  
  Why not an alternative lib something like &lt;code&gt;pysftp&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://stackoverflow.com/questions/48434941/pysftp-vs-paramiko"&gt;This&lt;/a&gt; StackOverflow answer lists the differences between the libraries.&lt;/p&gt;

&lt;h2&gt;
  
  
  Requirements
&lt;/h2&gt;

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

&lt;p&gt;We need to have some version of python installed in our machine to use the library, in my case I am currently using &lt;code&gt;python 3.8&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Install Paramiko
&lt;/h3&gt;

&lt;p&gt;Run the following command in the terminal to install the &lt;code&gt;paramiko&lt;/code&gt; library globally&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 paramiko
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  SFTP Server
&lt;/h3&gt;

&lt;p&gt;It is suggested that we have an SFTP server up and running in order to test the script. Our server can assist us in configuring the setup to our liking.&lt;/p&gt;

&lt;p&gt;If you do not want to set up or have an SFTP server to test on, we can utilise several publicly available servers. A list of servers may be found &lt;a href="https://www.sftp.net/public-online-sftp-servers"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to use the library
&lt;/h2&gt;

&lt;p&gt;Now let's start discussing how to use the library. We'll be talking about the following in upcoming sections.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Connecting to the remote server&lt;/li&gt;
&lt;li&gt;Executing commands on the server&lt;/li&gt;
&lt;li&gt;Transferring files (Upload/Download)&lt;/li&gt;
&lt;li&gt;The final script&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Connecting to the remote server
&lt;/h3&gt;

&lt;p&gt;First let's define the constants for connection properties like hostname, username, password, public key, private key etc.., In a real-world application, these properties should come from a configuration file/system.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# There might be others based on the SFTP configuration
HOST_NAME = "test.rebex.net"
USER_NAME = "demo"
PASSWORD = "password"
PRIVATE_KEY_FILE = '/some/file/path/private_key.txt'
PORT = 22
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To connect to the server we create an instance &lt;code&gt;client&lt;/code&gt; of &lt;code&gt;paramiko.SSHClient()&lt;/code&gt;. &lt;a href="https://docs.paramiko.org/en/stable/api/client.html"&gt;Paramiko.SSHClient&lt;/a&gt; is the primary class used to make connections to the remote server and execute commands. This class wraps &lt;a href="https://docs.paramiko.org/en/stable/api/transport.html"&gt;Transport&lt;/a&gt;, &lt;a href="https://docs.paramiko.org/en/stable/api/channel.html"&gt;Channel&lt;/a&gt;, and &lt;a href="https://docs.paramiko.org/en/stable/api/client.html#paramiko.client.SSHClient"&gt;SFTPClient&lt;/a&gt; to take care of most(not all as discussed below) aspects of authenticating and opening channels. The creation of an SSHClient object allows establishing server connections via the &lt;a href="https://docs.paramiko.org/en/stable/api/client.html#paramiko.client.SSHClient.connect"&gt;&lt;code&gt;connect()&lt;/code&gt;&lt;/a&gt; method.&lt;/p&gt;

&lt;h3&gt;
  
  
  Password-based auth
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;client = paramiko.SSHClient()
client.connect(hostname=HOST_NAME, port=PORT, username=USER_NAME, password=PASSWORD)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://docs.paramiko.org/en/stable/api/client.html#paramiko.client.SSHClient.connect"&gt;&lt;code&gt;connect()&lt;/code&gt;&lt;/a&gt; method has support for a wide variety of ways to authenticate before connecting to the server for a user. The above-mentioned method is for password-based authentication.&lt;/p&gt;

&lt;p&gt;For private key-based auth, instead of passing the password parameter, we can instead pass &lt;code&gt;pkey&lt;/code&gt; an object of type &lt;a href="https://docs.paramiko.org/en/stable/api/keys.html#paramiko.pkey.PKey"&gt;PKey&lt;/a&gt; or &lt;code&gt;key_filename&lt;/code&gt; the list of private key file names.&lt;/p&gt;

&lt;h3&gt;
  
  
  Private key-based auth
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# Passing the PKey object
client = paramiko.SSHClient()
pkey = paramiko.RSAKey.from_private_key_file(PRIVATE_KEY_FILE)
client.connect(hostname=HOST_NAME, port=PORT, username=USER_NAME, pkey=pkey)

# Passing private key file path
client = paramiko.SSHClient()
client.connect(hostname=HOST_NAME, port=PORT, username=USER_NAME, key_filename=PRIVATE_KEY_FILE)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;However, &lt;a href="https://docs.paramiko.org/en/stable/api/client.html#paramiko.client.SSHClient.connect"&gt;&lt;code&gt;connect()&lt;/code&gt;&lt;/a&gt; method doesn't support 2-Factor authentication. Following are a few examples where the above method might not work.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;SFTP server is enabled with both password and private key-based auth.&lt;/li&gt;
&lt;li&gt;SFTP server needs an OTP post entering the password to login into the server.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For use cases like these instead of creating an object of &lt;a href="https://docs.paramiko.org/en/stable/api/client.html"&gt;Paramiko.SSHClient&lt;/a&gt; we can create &lt;a href="https://docs.paramiko.org/en/stable/api/transport.html"&gt;Paramiko.Transport&lt;/a&gt; instead. Transport method has a support to authenticate the connection after successful negotiation by the methods like &lt;a href="https://docs.paramiko.org/en/stable/api/transport.html#paramiko.transport.Transport.auth_password"&gt;auth_password&lt;/a&gt;, &lt;a href="https://docs.paramiko.org/en/stable/api/transport.html#paramiko.transport.Transport.auth_publickey"&gt;auth_publickey&lt;/a&gt;, &lt;a href="https://docs.paramiko.org/en/stable/api/transport.html#paramiko.transport.Transport.auth_interactive"&gt;auth_interactive&lt;/a&gt; etc..,&lt;/p&gt;

&lt;h4&gt;
  
  
  Password and Private key-based auth
&lt;/h4&gt;

&lt;p&gt;Following are a few sample code snippets for the usage of above mentioned methods&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;transport = paramiko.Transport()
transport.start_client()
transport.auth_password(USER_NAME, PASSWORD)
pkey = paramiko.RSAKey.from_private_key_file(PRIVATE_KEY_FILE)
transport.auth_publickey(USER_NAME, pkey)

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

&lt;/div&gt;



&lt;h4&gt;
  
  
  Password and OTP based auth
&lt;/h4&gt;

&lt;p&gt;Here we use &lt;a href="https://docs.paramiko.org/en/stable/api/transport.html#paramiko.transport.Transport.auth_interactive"&gt;&lt;code&gt;auth_interactive&lt;/code&gt;&lt;/a&gt; method which accepts a handler for passing the values to the prompt. The custom handler that we write has the logic to pass the OTP or Password etc.. based on the output in the prompt. If we want to get the input to the handler from &lt;code&gt;stdin&lt;/code&gt; instead then we can use the &lt;a href="https://docs.paramiko.org/en/stable/api/transport.html#paramiko.transport.Transport.auth_interactive_dumb"&gt;&lt;code&gt;auth_interactive_dumb&lt;/code&gt;&lt;/a&gt; method.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;transport = paramiko.Transport()
transport.start_client()
transport.auth_interactive(username, handler)

def handler(self, title, instructions, prompt_list):
        answers = []
        for prompt_, _ in prompt_list:
            prompt = prompt_.strip().lower()
            if prompt.startswith('password'):
                answers.append(PASSWORD)
            elif prompt.startswith('verification'):
                answers.append(OTP)
            else:
                raise ValueError('Unknown prompt: {}'.format(prompt_))
        return answers
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Executing commands on the server
&lt;/h2&gt;

&lt;p&gt;After we have successfully logged into the server by any one of the above-mentioned methods executing the commands is as simple as passing your command to &lt;a href="https://docs.paramiko.org/en/2.9/api/client.html#paramiko.client.SSHClient.exec_command"&gt;exec_command&lt;/a&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;command = "ls -l"
stdin, stdout, stderr = client.exec_command(command)
print(stdout.read())
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;However paramiko has some very useful util methods to run SFTP commands on the server, following are a few of them&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sftp_client = client.open_sftp()
sftp_client.chdir() # Changing the directory
sftp_client.cwd() # Get the current working directory
sftp_client.listdir() # List all the contents of the current working directory
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For other supported commands, we can refer to &lt;a href="https://docs.paramiko.org/en/stable/api/sftp.html"&gt;this documentation&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  File Transfers
&lt;/h2&gt;

&lt;p&gt;Paramiko allows to programmatically send and receive files using the SFTP protocol. To upload/download file we need to create an &lt;a href="https://docs.paramiko.org/en/stable/api/sftp.html#paramiko.sftp_client.SFTPClient"&gt;&lt;code&gt;SFTPClient&lt;/code&gt;&lt;/a&gt; session object by calling &lt;a href="https://docs.paramiko.org/en/stable/api/client.html?highlight=open_sftp#paramiko.client.SSHClient.open_sftp"&gt;&lt;code&gt;open_sftp()&lt;/code&gt;&lt;/a&gt;. This object allows to perform common SFTP operations like &lt;a href="https://docs.paramiko.org/en/stable/api/sftp.html#paramiko.sftp_client.SFTPClient.get"&gt;&lt;code&gt;get()&lt;/code&gt;&lt;/a&gt;, &lt;a href="https://docs.paramiko.org/en/stable/api/sftp.html#paramiko.sftp_client.SFTPClient.put"&gt;&lt;code&gt;put()&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Upload File
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sftp_client= client.open_sftp()
sftp_client.put(uploadlocalfilepath,uploadremotefilepath)
sftp_client.close()
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Download File
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sftp_client= client.open_sftp()
sftp_client.get(downloadremotefilepath,downloadlocalfilepath)
sftp_client.close()
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  The final script
&lt;/h2&gt;

&lt;p&gt;Navigate to &lt;a href="https://gist.github.com/me-manikanta/aa9e0fd11ba2757521b6e1c5265ac29a#file-paramiko_example-py"&gt;this link&lt;/a&gt; for the gist of the following script. Star ⭐️ the gist if it helped you.&lt;/p&gt;

&lt;h2&gt;
  
  
  References
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Official Paramiko &lt;a href="https://docs.paramiko.org/en/stable/#"&gt;documentation&lt;/a&gt; can be a good start for exploring other features of the library&lt;/li&gt;
&lt;li&gt;&lt;a href="https://stackoverflow.com/questions/48434941/pysftp-vs-paramiko"&gt;pysftp vs paramiko&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.toSSH%20using%20Python%20Paramiko"&gt;SSH using Python Paramiko&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>python</category>
      <category>paramiko</category>
      <category>sftp</category>
    </item>
  </channel>
</rss>
